fullstackgtm 0.43.0 → 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +193 -0
- package/README.md +18 -7
- package/dist/cli.js +634 -56
- package/dist/connectors/outboxChannel.d.ts +64 -0
- package/dist/connectors/outboxChannel.js +170 -0
- package/dist/connectors/prospectSources.d.ts +22 -0
- package/dist/connectors/prospectSources.js +43 -0
- package/dist/connectors/signalSources.d.ts +105 -0
- package/dist/connectors/signalSources.js +316 -0
- package/dist/connectors/theirstack.d.ts +84 -0
- package/dist/connectors/theirstack.js +125 -0
- package/dist/icp.d.ts +47 -3
- package/dist/icp.js +105 -11
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/init.js +3 -0
- package/dist/judgeEval.d.ts +7 -0
- package/dist/judgeEval.js +8 -1
- package/dist/runReport.d.ts +26 -0
- package/dist/runReport.js +15 -0
- package/dist/schedule.js +18 -4
- package/dist/signals.d.ts +54 -0
- package/dist/signals.js +64 -0
- package/dist/tam.d.ts +225 -0
- package/dist/tam.js +470 -0
- package/docs/api.md +18 -4
- package/docs/outbox-format.md +92 -0
- package/docs/recipes.md +37 -0
- package/docs/roadmap-to-1.0.md +31 -1
- package/docs/signal-spool-format.md +162 -0
- package/docs/tam.md +195 -0
- package/llms.txt +68 -5
- package/package.json +1 -1
- package/skills/fullstackgtm/SKILL.md +3 -2
- package/src/cli.ts +714 -51
- package/src/connectors/outboxChannel.ts +202 -0
- package/src/connectors/prospectSources.ts +57 -0
- package/src/connectors/signalSources.ts +363 -0
- package/src/connectors/theirstack.ts +170 -0
- package/src/icp.ts +113 -11
- package/src/index.ts +32 -0
- package/src/init.ts +3 -0
- package/src/judgeEval.ts +8 -1
- package/src/runReport.ts +39 -0
- package/src/schedule.ts +20 -4
- package/src/signals.ts +90 -0
- package/src/tam.ts +654 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,199 @@ The path to 1.0 is planned in [docs/roadmap-to-1.0.md](./docs/roadmap-to-1.0.md)
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.44.0] — 2026-07-01
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Paired CLI auto-links HubSpot to the hosted deployment.** When a broker
|
|
15
|
+
credential exists (a paired CLI), `audit --provider hubspot` hands the local
|
|
16
|
+
HubSpot private-app token to the deployment over the broker channel
|
|
17
|
+
(best-effort, with a printed notice), so the hosted Integrations page shows
|
|
18
|
+
HubSpot connected and syncing without a separate OAuth setup. Unpaired CLIs
|
|
19
|
+
are unaffected — pairing remains opt-in via `login --via`.
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- **`tam accounts` credit-cost preview + spend guard.** Pulling the list costs ~3
|
|
24
|
+
TheirStack credits/company, so `tam accounts` now prints the cost up front and
|
|
25
|
+
never spends by surprise: `--dry-run [--usd-per-credit <rate>]` prices the pull
|
|
26
|
+
AND the full TAM (e.g. "up to 100 ≈ 300 credits; full TAM ~43,517 ≈ 130,551
|
|
27
|
+
credits (~$5,483)") for **0 credits**, and a pull above `--max-credits` (default
|
|
28
|
+
150 ≈ 50 companies) requires `--confirm`. The dollar figure appears only with an
|
|
29
|
+
explicit rate (no fabricated default; TheirStack is ~$0.04–$0.11/credit by tier).
|
|
30
|
+
`tam populate` also notes where spend lives — the acquire meter governs
|
|
31
|
+
population, separate from the TheirStack list pull. Library:
|
|
32
|
+
`theirStackPullCost` + `THEIRSTACK_CREDITS_PER_COMPANY`.
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- **`tam status` coverage is now classified against the TAM ICP — not a raw
|
|
37
|
+
account count.** Previously it counted *every* domain-bearing CRM account over
|
|
38
|
+
the universe, so accounts loaded from any source (off-ICP or not) inflated
|
|
39
|
+
coverage and could read >100%. Now `tam estimate` stores the ICP filter
|
|
40
|
+
(`model.targeting`), and `status` classifies each account into **in-TAM /
|
|
41
|
+
out-of-TAM / unknown** (checked on size + industry; geo and "uses-CRM" aren't on
|
|
42
|
+
a `CanonicalAccount`, so they need re-enrichment — a `--reverify` pass is
|
|
43
|
+
planned, and the classifier labels what it checked). Only in-TAM counts toward
|
|
44
|
+
coverage; off-ICP junk is bucketed out. **Bottom-up vs top-down reconciliation:**
|
|
45
|
+
the in-TAM count is a floor on the real universe, so when it meets/exceeds the
|
|
46
|
+
estimate, `status` stops reporting a fake 100% and flips to "the estimate was a
|
|
47
|
+
floor — your real market is at least N (`reconciledUniverse`); re-estimate for
|
|
48
|
+
the headroom." ETA now burns on in-TAM accounts/day. New library:
|
|
49
|
+
`classifyAccount`, `classifyCoverage`, `crmCheckableCriteria`, `coveredAccounts`,
|
|
50
|
+
`TamTargeting`/`TamClassified` (and `TamModel.targeting`). Legacy models without
|
|
51
|
+
targeting fall back to counting all accounts, with a "re-estimate to classify"
|
|
52
|
+
note.
|
|
53
|
+
|
|
54
|
+
### Added
|
|
55
|
+
|
|
56
|
+
- **`tam` technographic sourcing via TheirStack — target by CRM-usage, get a real
|
|
57
|
+
list.** The Explorium firmographic count (NAICS/size/geo) is a weak proxy for a
|
|
58
|
+
RevOps/CRM-hygiene tool and can't return the list (it 403s on pull). New
|
|
59
|
+
`--source theirstack` counts companies that actually **use** a CRM/MAP — set
|
|
60
|
+
`icp.firmographics.technologies` (e.g. `["salesforce","hubspot","pipedrive"]`),
|
|
61
|
+
mapped to TheirStack `company_technology_slug_or` + employee bounds + country —
|
|
62
|
+
which is the real buying signal, labeled `provider:theirstack (uses-CRM)`. And a
|
|
63
|
+
new **`tam accounts --source theirstack`** pulls the actual company list (real
|
|
64
|
+
names + domains, `--out <csv>` or `--json`, `--max` caps the credit spend), so the
|
|
65
|
+
TAM is a list you can review and acquire, not just a number. Counting is cheap
|
|
66
|
+
but not free (TheirStack rejects `limit:0`, so a count returns 1 row ≈ 3 credits;
|
|
67
|
+
a list pull is ~3 credits/company). New connector
|
|
68
|
+
`connectors/theirstack.ts` (`theirStackCountCompanies` / `theirStackSearchCompanies`),
|
|
69
|
+
`icpToTheirStackFilters` + `employeeBandsToRange`, and `login theirstack`.
|
|
70
|
+
(Verified live: filters + `metadata.total_results`; `limit` must be ≥ 1.)
|
|
71
|
+
|
|
72
|
+
### Changed
|
|
73
|
+
|
|
74
|
+
- **`tam estimate` requires a confirmed ANNUAL ACV — no fabricated defaults, and
|
|
75
|
+
the CRM is not silently the ACV source.** A TAM dollar figure built on a guessed
|
|
76
|
+
ACV is a false signal, so the `--acv-band smb|mid|enterprise` presets (hardcoded
|
|
77
|
+
$6k/$24k/$90k) are removed and `estimate` refuses to run without a real ACV. Two
|
|
78
|
+
confirmed paths, always labeled on the model (`acv.source`): `--acv <annual-usd>`
|
|
79
|
+
(`"explicit (annual)"`), or `--acv-from-crm --deal-period monthly|quarterly|annual`
|
|
80
|
+
— the median closed-won deal amount **annualized** by the stated period
|
|
81
|
+
(`"crm:closed-won (N deals, median $X/monthly ×12 = annual)"`). The period is
|
|
82
|
+
**required**: a deal amount can be MRR/quarterly/annual and guessing it is a
|
|
83
|
+
4–12× error (a $15k/mo deal is a $180k ACV). A bare `--provider` is the COVERAGE
|
|
84
|
+
source and **no longer auto-sets ACV**. **Buyers/account** likewise:
|
|
85
|
+
`--buyers-per-account <n>`, else the CRM's **average contacts per account**
|
|
86
|
+
(`"crm:avg-contacts/account (N)"`), else a labeled `"assumption:1-buyer"` — never
|
|
87
|
+
a silent default of 3. New library helpers `deriveAcvFromClosedWon` /
|
|
88
|
+
`deriveBuyersPerAccount`; `TamModel.acv.source` and `universe.buyersSource` are
|
|
89
|
+
new fields (and `acv.band` is gone).
|
|
90
|
+
|
|
91
|
+
### Fixed
|
|
92
|
+
|
|
93
|
+
- **`icp eval --golden default` no longer rots with the calendar.** The default
|
|
94
|
+
golden set's `firstSeen` stamps are relative to a pinned instant
|
|
95
|
+
(`DEFAULT_GOLDEN_NOW_ISO`, exported), but grading used wall-clock `new Date()`
|
|
96
|
+
— so freshness decay silently degraded the "fresh → send" rows until the gate
|
|
97
|
+
started failing (~8 days after the set was authored) on every fresh install
|
|
98
|
+
and in CI, with no code change. The default set is now graded on its own
|
|
99
|
+
pinned clock; file-based golden sets still grade against wall time (their
|
|
100
|
+
timestamps are the caller's).
|
|
101
|
+
|
|
102
|
+
- **`enrich acquire` (pipe0/Crustdata) over-narrow ICP filter → zero discovery.**
|
|
103
|
+
Live testing surfaced that a RevOps ICP returned 0 prospects from pipe0/Crustdata
|
|
104
|
+
even though a title-only search returned plenty — the prime suspect is the
|
|
105
|
+
industry vocabulary: LinkedIn renamed its industry taxonomy (v1→v2) and the map
|
|
106
|
+
sent only one generation, so a vendor on the other generation matches nothing.
|
|
107
|
+
`CRUSTDATA_INDUSTRY` now sends BOTH generations per cluster (e.g. "Software
|
|
108
|
+
Development" *and* "Computer Software"), OR-matched within the field. (pipe0
|
|
109
|
+
credits were exhausted mid-investigation, so this is a reasoned fix not yet
|
|
110
|
+
re-confirmed end-to-end — re-run `enrich acquire --source pipe0` once credits
|
|
111
|
+
refill; if still zero, the next suspects are the `current_seniority_levels`
|
|
112
|
+
shape and `locations`. Note `scoreProspectAgainstIcp` backstops persona but NOT
|
|
113
|
+
industry, so the industry filter is load-bearing.)
|
|
114
|
+
- **`enrich acquire` now surfaces a zero-discovery result** instead of emitting a
|
|
115
|
+
silent empty plan: when a provider returns 0 prospects it warns that this is
|
|
116
|
+
usually an over-narrow filter (not an empty market) and points at `icp show`;
|
|
117
|
+
and when all discovered prospects score below the fit threshold it says so.
|
|
118
|
+
|
|
119
|
+
### Added
|
|
120
|
+
|
|
121
|
+
- **`tam` — Total Addressable Market mapping.** Size the reachable market FROM
|
|
122
|
+
the ICP, then iteratively fill it and track coverage to an ETA. `tam estimate`
|
|
123
|
+
computes a transparent model (account universe × ACV = TAM, account/per-logo
|
|
124
|
+
basis by default or `--acv-basis buyer`; contacts = accounts × buyers/account
|
|
125
|
+
is the population target). The account count is `--accounts <n>` (assumption)
|
|
126
|
+
or `--source explorium` — a live count of matching **companies** via Explorium
|
|
127
|
+
`/v1/businesses`, which is the account universe directly. (Verified against the
|
|
128
|
+
live APIs: people/lead endpoints can't size a market — Explorium `/v1/prospects`
|
|
129
|
+
`total_results` equals the page size and pipe0/Crustdata returns only a cursor —
|
|
130
|
+
so `--source pipe0` for estimate is rejected; pipe0 remains a discovery/
|
|
131
|
+
population source. The businesses count omits `size`, which otherwise caps the
|
|
132
|
+
total.) Explorium caps the count at 60,000; a saturated reading is surfaced as a
|
|
133
|
+
floor (`provider:explorium (≥60k cap — floor)` + a warning to narrow the ICP).
|
|
134
|
+
The model always labels `accountsSource` provider-vs-assumption. Optional citable
|
|
135
|
+
cross-checks sit beside the bottom-up number. `tam populate --cron` schedules
|
|
136
|
+
governed `enrich acquire --save` (plan-only; see below). `tam status --save`
|
|
137
|
+
stamps a coverage timeline (CRM accounts-with-domain + contacts vs. the
|
|
138
|
+
universe, `$ covered` proportional, what the campaign added since baseline);
|
|
139
|
+
`tam report` renders a markdown burn-up. `status`/`report` project a linear
|
|
140
|
+
accounts/day ETA and refuse to project (honest "not enough history") below two
|
|
141
|
+
readings or a flat rate. Coverage + ETA are deterministic (no LLM). Library:
|
|
142
|
+
`estimateTam`, `computeCoverage`, `projectEta`, `tamReportToMarkdown`, the
|
|
143
|
+
store, and the count probe (`probeExploriumBusinessCount` +
|
|
144
|
+
`EXPLORIUM_BUSINESS_COUNT_CAP`). See [docs/tam.md](./docs/tam.md).
|
|
145
|
+
- **Source connectors for `signals` (Phase 1).** `signals fetch` gains an
|
|
146
|
+
additive, opt-in `--connector <id>[,<id>] [--connector-opt k=v …]` that pulls
|
|
147
|
+
signals from connected platforms instead of only ATS boards and hand-staged
|
|
148
|
+
`--from` files. Three connectors ship: `file` (local JSON/JSONL — also the
|
|
149
|
+
webhook landing-zone spool, no auth), `serpapi-news` (funding/company news via
|
|
150
|
+
a news REST API, key through the credential ladder), and `hubspot-forms`
|
|
151
|
+
(first-party `demand` from recent form submissions, reusing the existing
|
|
152
|
+
HubSpot login). Secrets resolve through the env → login → broker ladder, never
|
|
153
|
+
argv; `--connector-opt` carries non-secret knobs only. **Default behavior is
|
|
154
|
+
unchanged** when no `--connector` is passed. Connectors are read-only re: the
|
|
155
|
+
CRM and run through the same evidence-gate/dedup/weight pipeline as `--from`
|
|
156
|
+
(the `readStagedSignals` row validator is now the shared `stagedRowToSignal`).
|
|
157
|
+
Library: `connectors/signalSources.ts` (`listSignalSources`, `getSignalSource`,
|
|
158
|
+
`fileSource`, `serpapiNewsSource`, `hubspotFormsSource`), `stagedRowToSignal` +
|
|
159
|
+
`StagedSignalRow` from `signals.ts`. Design:
|
|
160
|
+
[docs/spec-connectors-signals-outbound.md](../../docs/spec-connectors-signals-outbound.md)
|
|
161
|
+
(the connector taxonomy + the Phase 2/3 webhook-spool and governed-channel plan).
|
|
162
|
+
- **Webhook spool format + conventional landing zone (signals Phase 2).**
|
|
163
|
+
`--connector file` with no path now reads the conventional spool directory
|
|
164
|
+
(`<profile home>/signals/spool`, via `signalsSpoolDir`) — every `*.jsonl` in
|
|
165
|
+
it, name-sorted, so a webhook receiver can append one row per event (per
|
|
166
|
+
source: `rb2b.jsonl`, `hubspot.jsonl`, …) and they all land in one fetch. The
|
|
167
|
+
`file` connector reads a directory as well as a single file. New
|
|
168
|
+
[docs/signal-spool-format.md](./docs/signal-spool-format.md) documents the row
|
|
169
|
+
format, the landing zone, re-read/retention semantics, and per-platform
|
|
170
|
+
payload mappings (RB2B, Trigify, HubSpot form webhook). Per the open-core
|
|
171
|
+
boundary the format + reader are open; the always-on receiver is a hosted
|
|
172
|
+
concern — the package ships no receiver. Also fixes a latent filter bug:
|
|
173
|
+
explicitly-provided rows (`--from`, connectors) whose bucket has no configured
|
|
174
|
+
`sources` (notably `demand`) were silently dropped unless `--bucket` was given;
|
|
175
|
+
now only an explicit `--bucket` narrows them, so spool/`hubspot-forms` `demand`
|
|
176
|
+
signals flow by default.
|
|
177
|
+
- **Outbox channel — governed send terminus (signals Phase 3).** `apply
|
|
178
|
+
--channel outbox` renders each APPROVED drafted opener to a local outbox
|
|
179
|
+
(`<profile home>/signals/outbox/<channel>.jsonl`, via `signalsOutboxDir`) for a
|
|
180
|
+
downstream sender to drain — and **transmits nothing**, preserving the "drafts
|
|
181
|
+
everything, transmits nothing" invariant (the send-side mirror of the spool).
|
|
182
|
+
The outbox channel is a `GtmConnector` whose `applyOperation` renders instead
|
|
183
|
+
of writing a CRM, so it reuses the entire governed apply path (approval set,
|
|
184
|
+
integrity verification, idempotency, run recording) with no change to the apply
|
|
185
|
+
engine; `apply` now takes `--provider <crm>` OR `--channel <id>`. It only
|
|
186
|
+
renders `draft:`-policy `create_task` ops (any other op is skipped) and is
|
|
187
|
+
idempotent on the operation id. New
|
|
188
|
+
[docs/outbox-format.md](./docs/outbox-format.md). Per the open-core boundary the
|
|
189
|
+
governed artifact + format are open; the always-on sender that actually
|
|
190
|
+
transmits (ESP/LinkedIn) is a hosted concern — no sender ships in the package.
|
|
191
|
+
Library: `connectors/outboxChannel.ts` (`createOutboxChannelConnector`,
|
|
192
|
+
`createChannelConnector`, `listOutbox`, `OutboxEntry`), `signalsOutboxDir`.
|
|
193
|
+
|
|
194
|
+
### Changed
|
|
195
|
+
|
|
196
|
+
- **`enrich acquire --save` is now schedulable** (for `tam populate`). It joins
|
|
197
|
+
the read/plan-side schedule allowlist in its plan-producing form ONLY — a
|
|
198
|
+
scheduled `enrich acquire` must include `--save` (rejected otherwise), so each
|
|
199
|
+
firing queues a `needs_approval` lead plan and writes nothing. The acquire
|
|
200
|
+
meter is still charged only at apply, and apply stays a separate human gate
|
|
201
|
+
(`apply --plan-id`, re-checked approved). Scheduling never auto-approves.
|
|
202
|
+
|
|
10
203
|
## [0.43.0] — 2026-06-25
|
|
11
204
|
|
|
12
205
|
### Added
|
package/README.md
CHANGED
|
@@ -38,13 +38,17 @@ It installs a compact operating guide ([skills/fullstackgtm/SKILL.md](./skills/f
|
|
|
38
38
|
## Five-minute loop
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
|
-
# 0.
|
|
41
|
+
# 0. Scaffold a workspace: a starter icp.json, an enrich.config.json acquire
|
|
42
|
+
# preset, and a PLAYBOOK.md wired for your provider + discovery source
|
|
43
|
+
npx fullstackgtm init --provider hubspot --source pipe0
|
|
44
|
+
|
|
45
|
+
# 1. No credentials? Try it on a realistic, deliberately messy demo CRM
|
|
42
46
|
npx fullstackgtm audit --demo
|
|
43
47
|
|
|
44
|
-
#
|
|
48
|
+
# 2. Audit your real HubSpot portal (private app token or OAuth access token)
|
|
45
49
|
HUBSPOT_ACCESS_TOKEN=pat-... npx fullstackgtm audit --provider hubspot --out plan.json
|
|
46
50
|
|
|
47
|
-
#
|
|
51
|
+
# 3. Review plan.json, then apply ONLY the operations you approve
|
|
48
52
|
HUBSPOT_ACCESS_TOKEN=pat-... npx fullstackgtm apply \
|
|
49
53
|
--plan plan.json --provider hubspot \
|
|
50
54
|
--approve op_abc123,op_def456 \
|
|
@@ -53,6 +57,8 @@ HUBSPOT_ACCESS_TOKEN=pat-... npx fullstackgtm apply \
|
|
|
53
57
|
|
|
54
58
|
Nothing is ever written without an explicit `--approve`. Operations whose value is a human decision (`requires_human_*` placeholders, e.g. which owner to assign) are refused unless you supply a concrete `--value` override.
|
|
55
59
|
|
|
60
|
+
`init` keeps any files you already have (`--force` overwrites) and works with `--source pipe0|explorium|linkedin` and `--provider hubspot|salesforce`. The PLAYBOOK.md it writes wires the cold-start and outbound-loop recipes to your workspace; the full play set — cold start, trigger-based outbound, scheduling, ABM, hygiene-gating, TAM — is [docs/recipes.md](./docs/recipes.md).
|
|
61
|
+
|
|
56
62
|
## Call workflows: calls become governed evidence
|
|
57
63
|
|
|
58
64
|
Calls are where pipeline truth lives. `call parse` normalizes any transcript dialect — `Speaker: text` lines (Fathom, Gong exports), `[Speaker]:` labels, or raw Granola utterance JSON — into canonical segments, insights, and `GtmEvidence` records.
|
|
@@ -211,8 +217,11 @@ LinkedIn is just another discovery source on the same scored → deduped → met
|
|
|
211
217
|
Cleaning and filling the CRM tells you *who* to reach; it never tells you *when*. The **signal → judge → draft** loop adds timing — and, like everything else, it stays on the dry-run → approve → apply spine and sends nothing.
|
|
212
218
|
|
|
213
219
|
```bash
|
|
214
|
-
# 1. Watch for movement. Free, no-auth public job boards in the box;
|
|
220
|
+
# 1. Watch for movement. Free, no-auth public job boards in the box; pull from
|
|
221
|
+
# connected platforms via source connectors; webhook platforms via the spool.
|
|
215
222
|
fullstackgtm signals fetch --bucket job --source greenhouse,lever,ashby --keywords "revops,growth" --save
|
|
223
|
+
fullstackgtm signals fetch --connector serpapi-news,hubspot-forms --save # news + first-party form demand
|
|
224
|
+
fullstackgtm signals fetch --connector file --save # webhook landing zone (see docs/signal-spool-format.md)
|
|
216
225
|
fullstackgtm signals list --since 7d # ranked triggers, each with a verbatim source quote
|
|
217
226
|
|
|
218
227
|
# 2. Decide who's worth a touch — and who isn't. Scores timing × fit × memory into send/nurture/skip.
|
|
@@ -220,8 +229,10 @@ fullstackgtm icp judge --signals-from latest --with-history --save
|
|
|
220
229
|
fullstackgtm icp eval --golden default # gate: prove the judge is calibrated before any send (exits 2 if not)
|
|
221
230
|
|
|
222
231
|
# 3. Draft the opener from the trigger. A create_task plan — proposed, never transmitted.
|
|
223
|
-
fullstackgtm draft --from-judge latest --min-score 80 --save
|
|
224
|
-
fullstackgtm plans approve <id> --operations all
|
|
232
|
+
fullstackgtm draft --from-judge latest --min-score 80 --channel email --save
|
|
233
|
+
fullstackgtm plans approve <id> --operations all
|
|
234
|
+
fullstackgtm apply --plan-id <id> --provider hubspot # log the touch as a CRM task
|
|
235
|
+
fullstackgtm apply --plan-id <id> --channel outbox # OR render to the outbox for a sender — transmits nothing (docs/outbox-format.md)
|
|
225
236
|
|
|
226
237
|
# 4. Close the loop. Outcomes re-weight which signals earn a touch.
|
|
227
238
|
fullstackgtm signals outcome --account acme.com --result replied
|
|
@@ -289,7 +300,7 @@ fullstackgtm diff --before old.json --after new.json --fail-on-new-findings
|
|
|
289
300
|
- `--demo` (with `--seed`) generates a realistic mid-market CRM with injected real-world failure modes — departed owners, unlinked deals, orphan accounts, stale pipeline — so agents and CI can exercise the full snapshot → audit → apply pipeline with zero credentials.
|
|
290
301
|
- Exit codes: `0` success, `1` error, `2` findings at/above `--fail-on`.
|
|
291
302
|
|
|
292
|
-
"Built for agents" is measured, not asserted: a 1,
|
|
303
|
+
"Built for agents" is measured, not asserted: a 1,892-run benchmark (20 scenarios = 17 synthetic + 3 seeded from an anonymized real portal, × 3 tool-surface arms × up to 4 trials, across nine models from six vendors, deterministic graders over final CRM state, τ-bench-style pass^k) shows the gated CLI surface beating raw CRM-API access on completion-under-policy for every model tested — and the tool-surface effect is monotonic and vendor-independent. Full matrix and methodology: [the leaderboard](./evals/crm/leaderboard/RESULTS.md).
|
|
293
304
|
|
|
294
305
|
The design is **deterministic apply, governed suggest**: the parts that touch your CRM — the audit rules, the plan/apply contract, compare-and-set, the survivor/merge logic — are deterministic and replayable; the parts that read free text (`call parse`/`score`, `market classify`) are LLM-powered but bounded, with every quoted span mechanically verified against the source before it can drive a writeback. Nondeterministic suggestion, deterministic governance.
|
|
295
306
|
|