Back to Homepage
GTM Engineering

Anatomy of an Agentic GTM System

The first post in this series named the debt. Two kinds — backward (what you’ve accumulated) and forward (what you haven’t adopted) — both compounding while you stand still. The diagnosis was the easy part.

This one is the architecture. What a GTM system looks like when it’s designed not to accumulate either kind of debt. Five layers, one repository per engagement, built on Claude Code instead of bolted together from a dozen workflow wrappers.

The architecture below is mature in places and being built out in others — that’s the nature of a system that runs and improves at the same time.


Three vectors, not three departments

Outbound, Content, ABM Ads. The default shape is three separate services — three teams running parallel campaigns, three retainers billed separately.

That’s the wrong unit of analysis.

Outbound, Content, and Ads are not three departments. They are three ways to land a message in front of the same account.

  • Outbound is direct delivery — the message arrives at one inbox.
  • Content is ambient delivery — the message shows up in the feed where the buyer already lives.
  • Ads are paid delivery — the message reaches accounts that haven’t engaged yet, on a list you control.

The accounts are the same. The narrative is the same. Only the vector changes. Whether you run one layer or all three, the unit of work is the account, not the channel.


Why one repo per engagement

The standard play is to build infrastructure once and rent it to every client. It looks efficient on paper — one stack, many seats — and on the left side of the productivity barbell, where volume and price compression do the work, it actually is.

We sit on the other side. Boutique scale, small client count, deep engagements. At that scale the math inverts. Shared infrastructure becomes shared blast radius — one schema change breaks five clients, one prompt update needs five rounds of regression testing, one offboarding turns into a permissions audit across the entire workspace.

So we don’t share. Every client gets a self-contained repository — client-{slug}/ — with its own context, its own skills snapshot, its own workflows, its own data. Duplication is the explicit choice. It costs more to operate. It costs less to coordinate. At three to five concurrent engagements, the second number matters more than the first.

If I tried to productize a service on the right side of the barbell, I’d end up in the middle — automatically.

The other reason is hand-off. Self-contained means the system is portable by construction. We’ll come back to that.


Anatomy of Our System

What a working client-{slug}/ repo actually looks like. Not every file — a system in motion accumulates helper scripts, dead experiments, noisy logs — but the skeleton you can read top to bottom.

client-{slug}/
├── CLAUDE.md                    ← Layer 1: Brain (≤200 lines, scannable in 2 min)
├── README.md
│
├── .claude/                     ← Layer 3: Skills library (full map inline)
│   ├── settings.json            ← permissions (single security layer)
│   ├── settings.local.json      ← personal overrides (gitignored)
│   └── skills/
│       ├── README.md
│       │
│       ├── cold-email/                     # B2B outbound campaigns
│       │   ├── atl-messaging               # VP/C-Level/Director
│       │   ├── btl-messaging               # Managers/ICs
│       │   ├── first-touch                 # opening email
│       │   ├── follow-up                   # Email 2 + 3
│       │   ├── personalization
│       │   ├── re-engagement
│       │   └── subject-lines
│       │
│       ├── linkedin-ads/                   # 3-layer funnel architecture
│       │   ├── audiences
│       │   ├── ads-outbound-sync           # ABM plays
│       │   ├── bidding
│       │   ├── campaign-setup
│       │   ├── copy                        # VoC-driven
│       │   ├── creative                    # Thought Leader Ads
│       │   ├── measurement                 # Insight Tag + CAPI
│       │   └── optimization
│       │
│       ├── linkedin-content/               # Organic, founders/GTM leaders
│       │   ├── hooks                       # 8 hook formulas
│       │   ├── storytelling                # AIDA, PAS, BAB
│       │   ├── formats                     # carousels, video, text
│       │   ├── scheduling                  # Golden Hour
│       │   ├── engagement
│       │   ├── cta
│       │   └── repurposing
│       │
│       ├── list-building/                  # contact + account data
│       │   ├── define-icp                  # 3-layer + 100-point scoring
│       │   ├── source-companies            # Apollo, Sales Nav, Google Maps
│       │   ├── find-contacts               # Boolean search
│       │   ├── qualify-accounts            # ABM tiers + intent
│       │   ├── clean-validate              # email verification
│       │   └── deduplicate
│       │
│       ├── n8n/                            # workflow automation
│       │   ├── workflow-design
│       │   ├── triggers-webhooks
│       │   ├── error-handling              # DLQ, circuit breakers
│       │   ├── crm-automation              # HubSpot, Salesforce, Slack
│       │   └── self-hosting                # Docker + Postgres
│       │
│       └── signal-sourcer/                 # signal-based selling
│           ├── job-changes                 # 14-45 day window post-change
│           ├── funding                     # 2-4 week window post-raise
│           ├── hiring                      # new role signals
│           ├── website-visitors            # RB2B, Warmly
│           ├── company-events              # M&A, IPO, leadership
│           ├── tech-changes                # stack swaps
│           ├── competitor-signals          # G2, review-mining
│           ├── content-engagement          # Trigify
│           └── multi-signal                # scoring + tiers
│
├── context/                     ← Layer 2: Institutional knowledge
│   ├── profile.md
│   ├── icp-definition.md
│   ├── signal-library.md
│   ├── positioning.md
│   ├── competitor-radar.md
│   ├── personas/
│   │   ├── {persona-1}.md
│   │   ├── {persona-2}.md
│   │   └── {persona-3}.md
│   ├── business-assets/
│   └── docs/
│       ├── kickoff-summary.md
│       └── communication-cadence.md
│
├── workflows/                   ← Layer 4: Decision trees for humans (per channel)
│   ├── outbound/
│   │   ├── enrichment.md
│   │   ├── signal-routing.md
│   │   └── campaign-build.md
│   ├── content/
│   │   ├── ideation.md
│   │   ├── campaign-build.md
│   │   └── post-writing.md
│   └── ads/
│       ├── list-building.md
│       ├── campaign-build.md
│       ├── ad-copy.md
│       └── ad-design.md
│
├── playbooks/                   ← Conditional / situational workflows
│   └── new-signal-response.md
│
├── outbound/                    ← Layer 5a: Outbound artifacts
│   ├── strategy.md
│   ├── scoring/
│   ├── campaigns/
│   └── logs.md
│
├── content/                     ← Layer 5b: Content artifacts
│   ├── strategy.md
│   ├── posts/
│   └── logs.md
│
├── ads/                         ← Layer 5c: Ads artifacts
│   ├── strategy.md
│   ├── lists/
│   ├── campaigns/
│   ├── creatives/
│   └── logs.md
│
└── data-sync/                   ← Infrastructure: pull external performance data
    ├── README.md
    ├── .env
    ├── sync-campaign-results.py
    ├── sync-signal-performance.py
    ├── sync-content-performance.py
    └── sync-ad-performance.py

The five layers

The tree shows the shape. The table below shows what each layer does.

LayerPathWhat it isFor whom
1CLAUDE.mdBrain — index, not referenceClaude (always in context)
2context/Institutional knowledge — foundation for skillsClaude (read by skills)
3.claude/skills/Methodology + execution with progressive disclosureClaude (activated on match)
4workflows/Decision trees around skillsHumans (Andrii, Dmytro)
5outbound/, content/, ads/Channel-shaped artifacts — strategy, campaigns, logs per channelInternal review + hand-off

A few things to notice once you’ve read the tree

  1. Channels appear twice — by design. workflows/outbound/ is how humans operate the channel: enrichment paths, signal routing, campaign build. outbound/ is what the channel produced: strategy, scoring, campaigns, logs. Decision trees on one side, artifacts on the other. The mirroring is intentional, not a duplication bug.

  2. data-sync/ is infrastructure, not Layer 6. Real Python scripts that pull performance data — LinkedIn Ads results, ESP metrics, CRM state — back into the repo where the skills can read it. Plumbing at the same conceptual level as .claude/settings.json. Important, but not a layer of the architecture.

  3. logs.md per channel — append-only journal. What was done, when, with what result. The foundation for retrospectives and the reason hand-off doesn’t require a tribal-knowledge transfer.

  4. 48 SKILL.md files, all activated on match, not by default. Six top-level skills, forty-two sub-skills under them. Progressive disclosure is the reason a number that size doesn’t melt the context window — Claude only loads the skill when the work actually matches it.

    Not headcount. Not seats. Skills.

    Skills aren’t one component of the system. They’re the format that fuses methodology and execution into something Claude can activate on demand.


What we didn’t build

Architecture is as much about what’s not in the repo as what is. Here’s what we deliberately didn’t build — and why each one would have cost more than it returned at our stage.

What we didn’t buildWhy
An agency-os repo with shared skillsThe operational center lives in Notion. There’s nothing to execute in code there.
A virtual monorepo workspaceContext noise and cognitive load outweigh any cross-reference value.
Per-client ~/.claude-configs/ machinerySeparate VS Code windows give isolation natively. A configuration layer would add complexity without proportional value.
Automated skills sync from Notion → client reposBreaks self-containedness. Each client decides when (and whether) to absorb an updated skill.
A six-layer security modelOne layer (.claude/settings.json deny list) is sufficient because there’s no workspace level above the repo.
Custom MCP servers (Anthropic’s tool-integration protocol)Off-the-shelf is enough until a specific bottleneck proves otherwise.
Multi-agent orchestration on day onePremature complexity. Skills with progressive disclosure cover 90% of what orchestration promises.
Cross-client analytics infrastructureAd-hoc once per quarter is enough at this scale. A persistent dashboard would optimize for a problem we don’t have yet.

Cross-reference is, honestly, the last thing I’d add.


The canonical skills library lives in Notion

The skills you saw in .claude/skills/ are snapshots, not sources. The canonical library lives in Notion — written, debated, refined there — and gets copied into a client repo when an engagement starts.

That split is deliberate. Strategic thinking is not executable code. The reasoning behind a skill, the alternatives we rejected, the edge cases that made us split one skill into two — none of that needs to ship to the client. What ships is the skill that survived the argument.

We don’t auto-sync. When a Notion skill evolves, each client repo decides whether and when to absorb the new version. Self-containment beats freshness; nobody wants a silent prompt change to land mid-campaign.

The internal operational center lives in Notion. There’s nothing to execute in code there.


Hand-off is structural, not aspirational

Every architectural decision in client-{slug}/ answers to one question: when this engagement ends, what does the client take with them?

Under one year — data only.

  • outbound/, content/, ads/ — all channel artifacts (strategy, campaigns, scoring, posts, creatives, logs).
  • context/ — ICP, signal library, positioning, personas, business assets, kickoff docs.
  • Synced performance data (output of data-sync/).
  • Delivered as a ZIP; full repo access revoked.

One year and beyond — full system.

Everything above, plus:

  • The entire client-{slug}/ repository — gh repo transfer.
  • All skills (.claude/skills/), workflows, playbooks.
  • data-sync/ scripts and configuration.
  • System hand-off package at context/docs/system-handoff/.
  • A 1–2 day team onboarding session and a 30-day support window.

Hand-off is a promise, not aspirational. It’s on the site. It’s in the FAQ. It’s a personal standard.

Self-contained architecture turns hand-off into a git push and an access revoke. That’s the entire mechanism.


Operational principles

1. Account is the unit, not channel

Outbound, content, ads — three vectors into the same account. Channels have separate feedback loops; the unit of work doesn’t change.

2. One repo per engagement

No shared infrastructure between clients. Duplication is cheaper than coordination at boutique scale — and it makes hand-off architectural, not procedural.

3. Skills are a format, not a feature

Methodology, execution, and progressive disclosure in a single markdown document. Claude activates them on match, not by default.

4. Strategic thinking lives in Notion. Code is execution.

ICP debates, narrative, decisions — not in git. The canonical skills library lives in Notion; client repos hold snapshots that evolve on their own clock.

5. Hand-off is a structural promise

Built so the entire system transfers as git push + access revoke. Under a year: your data. After a year: the full system.

6. Build it once. Use it daily. Update when patterns emerge.

No skill for every task. No template for every situation. The system grows by addition only when repetition demands it.


Outro

Each layer addresses a specific way debt accumulates. One repo per engagement prevents shared-state debt. Notion-as-source prevents prompt-drift debt. Self-contained hand-off prevents lock-in debt. The architecture isn’t complex — it’s specifically anti-debt.

Build it once. Use it daily. Update Notion skills when patterns emerge. Hand off cleanly when the relationship justifies it.