| name | gtm-advisor |
| description | Weekly GTM Advisor. Orchestrator skill that runs every Friday, sweeps the week's GTM evidence across all reachable connectors via four analysis subagents, stitches their findings, stages PROPOSED updates to the GTM repo as reviewable diffs, and produces an HTML end-of-week report plus a mandatory Slack digest. Human-in-the-loop: it suggests, the operator accepts. Portable across users/repos and across Codex / Claude Code via a config manifest. |
GTM Advisor — Orchestrator
GTM Advisor builds on the source-routing and guardrails already present in a
target repo. It rebuilds the orchestration as an operator skill (thin parent)
that dispatches four lane subagents.
The orchestrator does no analysis itself. It loads config, sets the window,
dispatches lanes, stitches results, stages diffs, renders outputs, and stops.
Lanes analyze; only the orchestrator writes.
Install location vs repo config
The skill may live either:
- inside a repo at
skills/gtm-advisor/, or
- in an agent harness / global skill directory such as Codex's root skills
folder.
Those are equivalent. The skill's own files (SKILL.md, lanes/,
report/report-template.html, config.template.yml) are resolved relative to
the skill install directory, but the active GTM configuration is always resolved
from the current repo/workspace:
- Determine
repo_root from the invocation cwd / active workspace, preferring
the nearest Git root when available.
- Look for
<repo_root>/gtm-advisor.config.yml.
- Load project-local environment files from
environment.files, if configured,
before probing CLI/API connectors. Resolve each file relative to repo_root;
load them in order; ignore missing files; never print secret values; and
never write secrets into YAML or committed docs.
- Read and write repo state only through paths in that manifest, resolved
relative to
repo_root.
- Never treat the skill install directory as the target repo unless it is also
the active workspace.
Golden rules (do not violate)
- Suggest, never land. The run stages proposals on a branch. The operator
accepts → it becomes a tracked edit in the doc. Nothing reaches canonical
main automatically.
- Accountable-Owner-Review gate. ICP, positioning, value-prop hierarchy,
customer interpretation, public proof, tone → propose-only, always. Direct
edits are allowed only for
staging.direct_edit_allowed classes.
- Config-driven, never hardcoded. Read every path, connector, channel, and
identity from the manifest. If you find yourself typing
icp/ or
#gtm-... literally, you are doing it wrong.
- Degrade, don't fail. A missing connector skips its part of a lane and is
reported in source-health. The run still completes.
- Inherit the guardrails. Apply
systems/sources.md rules verbatim:
product-line separation (exclude unrelated-product evidence from the active
product context), product-era caveats on campaign copy,
customer-Slack-as-first-party, and the source-priority conflict order.
References
Load these only when the current step needs the detail:
references/approval-policy.md: write tiers, approval commands, direct-edit
boundaries, and accountable-owner-review rules.
references/report-quality.md: required report sections, headline metrics,
copy-math requirements, and quiet-week behavior.
references/source-policy.md: source health, source conflict handling,
product-era caveats, product-line separation, env loading, auth checks, and
connector degradation.
references/lane-output-schemas.md: structured output expectations for each
analysis lane.
Modes
Run init once per repo. Run run every Friday (scheduled). On every
invocation, first resolve repo_root and check for
<repo_root>/gtm-advisor.config.yml. If run is invoked and that file is
missing, refuse the run and tell the operator to run init for this repo —
never guess the wiring or silently use another repo's manifest.
Mode: init (one-time bootstrap)
Goal: produce a complete gtm-advisor.config.yml at the active repo_root,
validated, with a dry-run sample report. Grill the operator for what you cannot
detect (one question at a time, with a recommended answer). This is the
first-run setup path for any repo where <repo_root>/gtm-advisor.config.yml
does not exist.
- Detect runtime + discover connectors (three inputs):
- Seed the roster from the documented inventory in
inherit.sources /
the repo's systems/tools.md (trust cloud connectors exist at run time).
- Probe the live runtime: Codex →
codex mcp list; Claude Code →
inspect the available MCP tool namespaces in the tool roster.
- Ask the operator for sources neither sees: external connectors, API
polling endpoints, CSV/manual drops.
- Reconcile into
connectors: with source, auth, lane, health.
Flag any documented-but-unprobeable connector as not-checked, not
not-available — local probes cannot see cloud app connectors.
- For
source: cli or source: api with auth: api-key, set the expected
env_var and optional read-only auth_check. If project-local env files
are useful, configure environment.files such as .localenv and .env;
ensure those files are ignored by git. Ask for variable names, not secret
values, unless the operator explicitly wants the agent to add a local
secret file.
- Map docs: scan the repo for canonical folders (
icp/, messaging/,
sales/, projects/) and propose doc_map. Ask if it cannot infer.
- Grill the unknowns: approver name + Slack handle, Slack channel, Notion
on/off, timezone, run time (default Fri 13:00 local).
- Validate wiring: confirm the Slack channel resolves (search / test post);
confirm/choose the HTML report dir; confirm the staging branch prefix.
- Write
gtm-advisor.config.yml from config.template.yml; seed an empty
staging.state_ledger.
- Dry-run: one no-write
run pass → render a sample report.html so the
operator sees the shape before scheduling. Make no branch, no Slack post.
- Wire the schedule per runtime (see Scheduling).
Mode: run (weekly)
0. Load + bound
- Resolve
repo_root from the active workspace/cwd, then load
<repo_root>/gtm-advisor.config.yml. Abort to init if absent.
- Load
environment.files from repo_root before probing. Treat missing
required variables in environment.required_vars or connector env_var as
blocked for that connector, with the env var name in source-health. Do not
expose env values in reports, Slack, logs, or commits.
- Compute the window: rolling
schedule.window_days ending at the run, in
schedule.timezone.
- Recompute connector
health (probe). Build the source-health map
(ok / blocked / not-available / not-checked).
For CLI/API connectors, prefer the connector's auth_check when present,
after env loading, and include any read-only auth failure in source-health.
- Load the
state_ledger (prior proposals → status) for anti-repetition.
1. Dispatch lanes (subagents, parallel)
For each lane, spawn an isolated subagent via the runtime dispatch adapter
(Claude Code → Task/Agent tool; Codex → sub-task invocation). Hand each:
- its lane file (
lanes/<lane>.md),
- the week window,
- only its reachable connector subset,
- the relevant
doc_map targets (for context, not to write),
- the prior-week findings for its lane (from the ledger).
| Lane | File | Connectors (from config) |
|---|
| Outbound copy | lanes/analyze-outbound-copy.md | instantly, heyreach, trigify |
| Conversations | lanes/analyze-conversations.md | fathom, gmail, pylon, slack |
| ICP signal | lanes/analyze-icp-signal.md | chartmogul, hubspot, db, posthog |
| Strategy | lanes/analyze-strategy.md | notion, linear, slack |
Each lane is a pure read function: it returns a structured findings file to
a scratch dir and writes nothing to canonical docs. Lanes never write — this
is what prevents subagents racing on the same files.
2. Stitch + synthesize (orchestrator only)
Read the four findings files and combine them intelligently, not by
concatenation:
- Corroborate across lanes. A signal seen in ≥2 lanes (e.g. a copy angle in
Outbound that matches an objection in Conversations) ranks higher and is
tagged
multi-source.
- Resolve conflicts with the
systems/sources.md source-priority order;
record the conflict rather than silently picking a side.
- Dedupe against the ledger. Drop suggestions already pending / accepted /
rejected. Keep genuinely new ones, plus mark
strengthened-since-last-week
where this week's evidence is materially stronger.
- Synthesize the deliverables: Messaging & Positioning Analysis, exact
winning / losing copy variants, positive-reply language, blocker/objection
themes, ICP notes / shaping, CRM movement, and the next experiment hit-list.
- Explain why copy worked without overloading the report. Keep the main
Messaging & Positioning Analysis concise: a light stats line, winning
messaging with inline quotes, and losing / needs-work messaging. Connect exact
copy to reply content and metrics, but avoid long tables unless the operator
asks for an appendix.
- Always pair copy claims with campaign math. Any report claim that a copy
angle "worked", "won", "underperformed", or "needs work" must include the
available numeric context beside it: campaign/source, audience, sent/contacted
denominator, accepts/contacts where relevant, replies, positive/interested
replies, booked meetings/demos, downstream opportunities/deals when available,
and the attribution caveat if any number is missing or only available from a
sampled slice. For HeyReach specifically, include connection requests sent,
connections accepted, total replies, auto-tagged interested count, and the
exact campaign/list names when those fields are available.
- Use operating metrics in the headline cards. The top-line report metrics
should be sends, replies, interested replies, net-new deals, and confirmed
meetings/demos when available. Do not use report-process metrics such as
Slack/Notion writes or generic insight counts in the headline cards.
- List campaigns / plays separately. Under source health, include a weekly
campaign/play table with source, audience/message, sends, accepts/contacts,
replies, interested count, meetings/demos if available, and notes about
partial data or attribution quality.
- Keep report updates doc-oriented. Draft repo update candidates must name
the target update category: Messaging & Framing, ICP notes / shaping, Next
experiments, or CRM evidence process. Do not scatter blocker evidence across
unrelated docs; pricing and stack-fit questions belong in objections unless
the operator explicitly wants copy changes.
- Classify each suggestion by write-tier:
direct (safe class) vs
propose (everything touching ICP/positioning/value-props/proof/tone).
3. Stage (only if there is material signal)
- Create branch
staging.branch_prefix + YYYY-MM-DD.
- Apply
propose-tier suggestions as real diffs against doc_map targets,
each carrying its evidence, source, date, and product-era caveat.
- Apply
direct-tier edits only for direct_edit_allowed classes.
- Update the
state_ledger with the new proposal ids → pending.
4. Outputs
- HTML (always): render
report/report-template.html → report.html into
outputs.html.dir, committed on the branch beside the diffs. The report must
include: Source health, Outbound GTM Summary, Messaging & Positioning
Analysis, Objections / blockers, ICP insights, CRM updates, Draft repo update
candidates, and Since last week. Each staged update links to its diff when a
non-draft branch exists. (See report template for required sections.)
- Slack (mandatory): post a compact digest to
outputs.slack.channel:
per-lane headlines, proposal counts by area, the source-health line, links to
the report + branch, and 1–3 Grill Me questions. The post is the nudge; the
HTML is the detail.
- Notion (optional): if
outputs.notion.enabled, mirror the report.
5. Quiet week
If there is no material signal: do not open a branch or full report. Post a
short Slack no-action note with the source-health line, update the ledger, stop.
Acceptance / human-in-the-loop
Two valid accept paths, both human:
- Repo: the operator edits/merges the staged diff → it becomes a tracked
update in the doc (the iteration is visible in branch history).
- Slack:
@Approved: applies as written · @Correction: operator's wording
wins · @Hold: stays pending · @Question: answer before changing.
A bare emoji, bare tag, or ambiguous "yes" is never approval for
Accountable-Owner-Review context. On accept/reject, update the state_ledger.
Scheduling (portable)
The schedule lives in config; each runtime wires its own trigger to invoke this
skill in run mode:
- Codex: update the existing automation for this workflow to Friday 13:00
operator-tz and point it at this skill. Do not create a duplicate
automation when the repo already has an app or rule for this workflow.
- Claude Code / other: a routine/cron that invokes the skill
run mode.
The skill itself is scheduler-agnostic — it only needs to be invoked Friday.
Built-in fallback (portable repos)
If inherit.* docs are absent, apply this minimal rule set: preserve real buyer
language before interpretation; propose (don't land) anything about ICP /
positioning / value-prop priority / proof / tone; record source + date +
product-era caveat on time-sensitive evidence; never turn one noisy source into
a canonical claim.