GTM Bud backend atlas
Convex remains the durable data and control plane. This atlas maps the current system and its principal debt.
A Python execution tier is a later learning question. It is not a proposal to replace Convex.
Production scale
Origin: Stable production snapshot
Commit: 9fc01b8234d467b38baa19ee222143c9084993cd
Date: 2026-08-14
Runtime line counts exclude blank lines, Markdown, tests, fixtures, generated files, build output, dependencies, and this atlas. Counts include production TypeScript, JavaScript, and Svelte source.
Runtime and trust boundaries
The browser uses Convex directly for reactive product data. Railway performs server work and provider operations.
Requests, durable state, providers, and telemetry
flowchart LR B["Browser
End User and Admin End User"] CF["Cloudflare
edge delivery and request proxy"] SK["SvelteKit Node on Railway
SSR, API routes, provider gateway, compute"] subgraph CX["Convex: durable data and control plane"] Q["Queries
reactive reads"] M["Mutations
transactional state changes"] A["Actions
external work coordination"] SCH["Scheduler
recurring and delayed work"] DB[("Database
domain state, claims, intents, history")] Q --> DB M --> DB A --> Q A --> M SCH --> A SCH --> M end PG["Provider gateways
authentication, timeouts, request IDs"] WH["Webhook ingress
provider events and confirmations"] U["Unipile
LinkedIn and email delivery"] L["LinkdAPI
LinkedIn sourcing"] LM["LeadMagic
contact enrichment"] OR["OpenRouter
model access"] S["Stripe
billing events"] R["Resend
transactional email"] PH["PostHog
diagnostic telemetry and product analytics"] B -->|"page and API request"| CF --> SK B -->|"direct subscription and command"| Q B --> M SK -->|"server client"| Q SK --> M A -->|"call SvelteKit gateway"| SK SK --> PG PG <--> U PG <--> L PG <--> LM PG <--> OR PG <--> S PG <--> R U --> WH S --> WH R --> WH WH --> SK -->|"validate and reconcile"| M SK --> PH A --> PH M --> PH classDef client fill:#17233a,stroke:#63a4ff,color:#eef5ff classDef durable fill:#102f2a,stroke:#4fd1a1,color:#effff9 classDef gateway fill:#30271a,stroke:#e5ad52,color:#fff6e7 classDef provider fill:#29203b,stroke:#ae82ed,color:#f8efff classDef telemetry fill:#342025,stroke:#ec7184,color:#fff0f2 class B,CF client class Q,M,A,SCH,DB durable class SK,PG,WH gateway class U,L,LM,OR,S,R provider class PH telemetry
Sourcing to Send
This path must find good Leads and use the available Account volume. It contains the highest execution and recovery pressure.
Campaign intent to settled provider result
flowchart TD CFG["Campaign Config
ICP, Value Offers, Sequence, channel policy"] PC["Supply Plan compile
convert intent into bounded sources and quotas"] PA["Supply Plan activation
publish immutable plan and mutable progress"] CD{"Controller decision
start, continue, park, or stop"} SA[("sourcingAttempts
durable attempt history")] SJ[("sourcingJobs
claimable provider work")] PW["Provider work
LinkdAPI, LeadMagic, provider searches"] CAN[("Candidates
raw provider identities")] N["Narrowing
deduplicate, filter, and rank"] EE["Evidence and Contact Enrichment
qualification facts and channel addresses"] CR{"Channel Readiness Gate
qualified and contactable?"} REC[("v2Runs Assembly receipt
idempotent intake record")] LI[("v2LeadItems
Lead execution state")] LIM[("leadItemMessages
planned message content")] CON[("v2Conversations
channel progress and next eligibility")] AA["Account Assignment
select an eligible sending Account"] PIN[("pinnedAccountId
stable Account ownership")] CAP{"Capacity gates
weekly CR, OP InMail, pacing, Account health"} D["Dispatcher
find due work and claim a lease"] W["Walker
choose the next valid Sequence action"] DI[("Durable intent
request ID, action, target, idempotency key")] TR[("Token reservation
reserve scarce send capacity")] GW["SvelteKit gateway
validate, authenticate, set timeout"] UNI["Unipile
execute LinkedIn or email operation"] CONF["Known provider result
action response or accepted webhook"] SET["Settlement
commit result, consume or release token"] RESULT{"Result"} IDLE["Idle
wait until nextEligibleAt"] PARK["Parked
wait for capacity or operator repair"] TERM["Terminal
complete, rejected, or exhausted"] RETRY["Recovery
retry safe work with the same identity"] RECON["Required provider reconciliation
verify an ambiguous result before retry"] DEBT["Current debt
provider verification is not complete"] ADR["Accepted ADR-S0099
verify provider state before a final retry"] LEG["Legend
Green = durable record
Blue = decision or processing
Purple = external boundary
Red = recovery or debt"] CFG --> PC --> PA --> CD CD -->|"start or continue"| SA --> SJ --> PW --> CAN --> N --> EE --> CR CD -->|"park"| PARK CD -->|"stop"| TERM CR -->|"ready"| REC --> LI REC --> LIM LI --> CON --> AA --> PIN --> CAP CR -->|"not ready"| RETRY CAP -->|"capacity available"| D --> W --> DI --> TR --> GW --> UNI --> CONF --> SET --> RESULT CAP -->|"wait"| PARK RESULT -->|"more Sequence work"| IDLE --> D RESULT -->|"blocked"| PARK RESULT -->|"complete or exhausted"| TERM PW -. "timeout, partial page, or expired claim" .-> RETRY --> SJ GW -. "positive proof of failure before provider call" .-> RETRY --> D UNI -. "ambiguous result" .-> RECON --> CONF RECON --> ADR ADR -. "required design" .-> DEBT DEBT -. "gap can permit unsafe retry" .-> PARK classDef durable fill:#102f2a,stroke:#4fd1a1,color:#effff9 classDef process fill:#17233a,stroke:#63a4ff,color:#eef5ff classDef external fill:#29203b,stroke:#ae82ed,color:#f8efff classDef risk fill:#3a1e25,stroke:#f17487,color:#fff0f2 classDef note fill:#30271a,stroke:#e5ad52,color:#fff6e7 class SA,SJ,CAN,REC,LI,LIM,CON,PIN,DI,TR durable class CFG,PC,PA,CD,N,EE,CR,AA,CAP,D,W,CONF,SET,RESULT,IDLE,PARK,TERM process class PW,GW,UNI external class RETRY,RECON,DEBT risk class ADR,LEG note
Durable application data
The database receives debt slowly and releases it slowly. Names, types, indexes, and compatibility paths need explicit ownership.
Stable schema
211 tables contain 512 named indexes. The 33 schema files contain 8,318 nonblank lines.
Compatibility surface
The schema has 2,910 top-level field slots. Of these fields, 1,548 are directly optional.
Optional fields represent 53.2 percent of the top-level field slots.
Migration inventory
Stable registers 100 migration definitions. The production tree has 96 migration or backfill modules.
Branch drift
The checked-out alpha branch has 173 tables and 419 indexes. It has 38 fewer tables than Stable.
Use Stable for production scale. Use alpha only for local code analysis.
Application table families and named indexes
flowchart TB
ROOT[("Stable Convex application schema
211 tables · 512 named indexes
2,910 top-level field slots")]
PRODUCT["Product and delivery state
98 tables"]
SOURCE["Sourcing and economics
93 tables"]
OPS["Platform and operations
20 tables"]
P1["v2 execution, delivery, reporting
51 tables · 107 indexes"]
P2["Campaign design and messaging
14 · 33"]
P3["Leads, enrichment, scrapes
13 · 49"]
P4["Users, Accounts, access
6 · 19"]
P5["Customer email and notifications
6 · 13"]
P6["Lifecycle automation
4 · 10"]
P7["UTM attribution
4 · 8"]
S1["Sourcing and quality
48 tables · 114 indexes"]
S2["Supply planning and generation
33 · 76"]
S3["Provider cost and billing
12 · 34"]
O1["Platform configuration and infrastructure
6 tables · 4 indexes"]
O2["Operational alerts
6 · 15"]
O3["Legacy Outreach execution
5 · 23"]
O4["Observability and audit
3 · 7"]
ROOT --> PRODUCT
ROOT --> SOURCE
ROOT --> OPS
PRODUCT --> P1
PRODUCT --> P2
PRODUCT --> P3
PRODUCT --> P4
PRODUCT --> P5
PRODUCT --> P6
PRODUCT --> P7
SOURCE --> S1
SOURCE --> S2
SOURCE --> S3
OPS --> O1
OPS --> O2
OPS --> O3
OPS --> O4
classDef root fill:#102f2a,stroke:#4fd1a1,color:#effff9
classDef group fill:#30271a,stroke:#e5ad52,color:#fff6e7
classDef family fill:#17233a,stroke:#63a4ff,color:#eef5ff
class ROOT root
class PRODUCT,SOURCE,OPS group
class P1,P2,P3,P4,P5,P6,P7,S1,S2,S3,O1,O2,O3,O4 family
Widen, migrate, and narrow
flowchart LR W["Widen
accept old and new shapes"] C["Compatibility
write both shapes and read either shape"] B["Backfill
move existing documents in bounded batches"] V{"Verify
check coverage, invariants, and read behavior"} CUT["Cutover
read and write only the new shape"] N["Narrow
remove old fields, indexes, and compatibility code"] HOLD["Hold and repair
keep compatibility active"] W --> C --> B --> V V -->|"verified"| CUT --> N V -->|"gap found"| HOLD --> B classDef phase fill:#17233a,stroke:#63a4ff,color:#eef5ff classDef decision fill:#30271a,stroke:#e5ad52,color:#fff6e7 classDef recovery fill:#3a1e25,stroke:#f17487,color:#fff0f2 class W,C,B,CUT,N phase class V decision class HOLD recovery
Table count is not debt count
A table can reduce cost and coupling when it has one stable purpose. Enrichment sibling rows prevent large Lead reads during narrow operations.
Send indexes prevent full scans on dispatch paths. Projections keep read models small. Immutable Supply Plan data separates compiled intent from mutable progress.
Debt starts when ownership, lifetime, invariants, or migration paths are unclear. A high table count is evidence for review, not proof of debt.
Largest Stable table contracts
Field counts include direct top-level fields. They exclude nested object fields and Convex system fields.
appSettings395 optional · 0 indexes81 unipileAccounts70 optional · 4 indexes80 campaigns73 optional · 10 indexes64 supplyPlans29 optional · 8 indexes64 leadItems60 optional · 25 indexes49 sourcingAttempts37 optional · 13 indexes
Architecture record pressure signals
ADR-0008v2Config is the canonical Campaign Config. The legacy stepData shape remains stored.
ADR-0029steps[] is authoritative. The stored channels field remains for compatibility.
ADR-0038v2ConvProjection protects dashboard reads. It also requires projection writers and repair.
ADR-S0138The Plan Space Definition becomes authoritative. Older manifest and payload tables remain declared.
Walker migration grillSome v1 fields remain because live replay can still read historical shapes.
ADR-S0099Provider verification is required before retry. The current orphan-intent path does not implement this verification.
Code and decision inventory
Large modules show concentration. Document counts show how much operating knowledge supports the runtime.
Largest Stable runtime modules
web/src/convex/outreach/v2/leadGenExecutor.ts7,324 nonblank linesweb/src/convex/appSettings.ts4,010 nonblank linesweb/src/convex/lib/posthogEvents.ts3,438 nonblank linesweb/src/lib/admin/controlBoardCatalog.ts3,388 nonblank linesweb/src/convex/outreach/v2/dashboardQueries.ts3,253 nonblank lines
These counts use the same Stable snapshot and nonblank-line rule as the production KPI.
Architecture record
- ADRs
- 242
- Grills
- 32
- Fix records
- 744
ADRs record selected trade-offs. Grills preserve operator decisions. Fix records preserve defect evidence and correction history.
Later learning question
Can a Python execution tier reduce long-running execution complexity while Convex keeps durable state and control?
Answer this question with one bounded workflow and measured operational evidence. Do not make it the current atlas conclusion.