@sellable/mcp 0.1.38-rc.1 → 0.1.38-rc.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.38-rc.1",
3
+ "version": "0.1.38-rc.2",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -773,439 +773,16 @@ Follow that prompt verbatim. It contains the full message-validation workflow, g
773
773
 
774
774
  Do NOT proceed to Step 4 (message review gate) without loading and following the generate-message subskill — message-validation.md must prove the full generate-messages workflow ran, and that workflow is defined in the subskill.
775
775
 
776
- ## MANDATORY TOOL ORDER (read this BEFORE any tail step)
776
+ ## Tail (MANDATORY TOOL ORDER + Steps 13-16 + Threshold Trips + Hard Rules)
777
777
 
778
- Every tail run MUST call these tools in this exact order. The tail is
779
- **review-batch cascade-driven**: you kick off Enrich Prospect only for
780
- the imported review batch, and the workflow engine chains DNC Check →
781
- ICP Score → Passes Rubric → Generate Message automatically. Your job is
782
- to START the bounded cascade, WAIT for it to drain, OBSERVE the results,
783
- and stop for review.
784
- Do NOT manually run rubric-check, enrich, or message-generation
785
- tools — the cascade already does them.
778
+ The full tail detail (~17k chars: MANDATORY TOOL ORDER, auto-execute-leads, validate-sample loop, auto-execute-messaging, awaiting-user-greenlight, threshold-trip logging, tail hard rules) lives in a dedicated on-demand subskill to keep this entry prompt fast to load.
786
779
 
787
- ```text
788
- Step 13 — import review batch + kick bounded cascade
789
- import_leads(targetLeadCount=importLimit)
790
- wait_for_lead_list_ready
791
- confirm_lead_list
792
- get_rows_minimal # capture enrichCellIds
793
- queue_cells(cellIds=<review-batch Enrich Prospect cells only>) <-- starts bounded chain
794
- wait_for_campaign_table_ready # review-batch cascade drains here
795
- update_campaign(currentStep=validate-sample)
796
-
797
- Step 14 — observe sample
798
- get_rows_minimal # read passesRubric + message cell status per row
799
- compute projectedPass
800
- if OK: update_campaign(currentStep=auto-execute-messaging)
801
- else: diagnose brief-vs-list; if brief: update_campaign_brief + re-queue + wait
802
- (check_rubric / bulk_enrich_with_prospeo are NOT called here —
803
- cascade already did them. wait_for_rubric_results is OK as a
804
- read-only observation helper if you need to block on completion.)
805
-
806
- Step 15 — observe messaging
807
- get_rows_minimal # confirm passing rows have completed Generate Message cells
808
- (rare) queue_cells on any pending Generate Message cells
809
- token-contract spot check via get_rows
810
- update_campaign(currentStep=awaiting-user-greenlight)
811
- (generate_messages is NOT an MCP tool; messages come from the cascade)
812
-
813
- Step 16 — awaiting-user-greenlight
814
- attach_recommended_sequence({ campaignId }) # tier-aware: premium/SN -> If Open Profile->INMAIL_OPEN, else INVITE->accepted->DM
815
- re-surface watchUrl + review-batch orientation
816
- STOP. DO NOT call start_campaign. DO NOT auto-advance currentStep.
817
- ```
818
-
819
- **Prefer `attach_recommended_sequence` over `attach_sequence` in the
820
- autonomous tail.** The recommended tool takes only `campaignId` and
821
- delegates template selection to the backend's tier-aware selector
822
- (`selectTemplateForTiers`). `attach_sequence` requires you to
823
- hand-author a `version: 2` template with nodes, branches, and
824
- entryNodeId — that's error-prone mid-long-context (galley-off UAT
825
- `20260420T195732Z` failed because Claude hit "Invalid node type" and
826
- tried to debug via forbidden Bash/Glob calls). Use `attach_sequence`
827
- only when the caller explicitly needs a custom non-recommended cadence.
828
-
829
- Hard gates — if you find yourself about to violate any of these, stop
830
- first:
831
-
832
- - Do NOT use `update_cell` to write message bodies. The `Generate
833
- Message` column's http_request writes those cells via the cascade.
834
- `update_cell` remains reachable for legitimate operator overrides
835
- AFTER the tail hands off.
836
- - Do NOT call `check_rubric`, `enrich_with_prospeo`, or
837
- `bulk_enrich_with_prospeo` in the tail. Those are direct-API
838
- mutation tools that fetch data to the caller without writing to
839
- the workflow table cells. Step 13's `queue_cells` already
840
- triggers the column-level enrichment that populates those cells.
841
- Running these tools in the tail produces duplicate cost with no
842
- cell side effect.
843
- - `wait_for_rubric_results` is read-only and OK to use as an
844
- observation helper when you need to block on rubric completion —
845
- it does not mutate cells. Prefer `get_rows_minimal` +
846
- `wait_for_campaign_table_ready` for the primary cascade-drain
847
- path, but use `wait_for_rubric_results({ targetCount: cohortSize })`
848
- when the table-level wait returns before rubric cells finish.
849
- - `start_campaign` is FORBIDDEN in the autonomous tail. It belongs only
850
- in the Claude-greenlight path, AFTER the user signals "start". See
851
- `references/final-handoff-contract.md`.
852
- - You MAY NOT call `start_campaign` without a prior successful
853
- `attach_recommended_sequence` (or `attach_sequence` if a custom
854
- cadence is explicitly required) in the same run.
855
- - You MAY NOT call `start_campaign` while ANY passing row has an
856
- empty `Generate Message` cell. If you discover empty message cells in
857
- the greenlight turn, ABORT greenlight and return to Step 15 first.
858
- - You MAY NOT call `attach_sequence` while ANY passing row has an
859
- empty `Generate Message` cell. Before `attach_sequence`, call
860
- `get_rows_minimal` and confirm every row where `passesRubric=true`
861
- has a `completed` Generate Message cell with non-empty `result`. If
862
- any are pending, call `queue_cells` on those generateMessageCellIds
863
- and wait. If rows truly won't message, ESCALATE.
864
- - You MAY NOT advance past Step 13 without calling `queue_cells` on
865
- the review-batch Enrich Prospect cells. Without it, every downstream
866
- cell stays `pending` and the campaign ships empty.
867
- - You MAY NOT queue enrichment for rows outside the configured review
868
- batch before the user approves expansion. Full-list enrichment/message
869
- generation is a credit-spend decision and must happen after the user
870
- has reviewed the sample.
871
-
872
- Load `core/auto-execute.yaml` exactly once at the start of Step 13. All
873
- subsequent steps read the already-parsed config. Do not re-load mid-run.
874
- Load each subskill prompt (`create-campaign-v2`, `research-sender`,
875
- `generate-messages`) at most once per run. A multi-call chunk sequence counts
876
- as one load. If a tool result already told you to load a prompt, load all
877
- chunks once and remember; do not restart the same prompt from offset 0 later.
878
-
879
- After every `update_campaign({ currentStep: ... })` in the tail, narrate
880
- what changed and orient the user to what the watch link will show next —
881
- reuse the v1 `create-campaign` watch-mode pattern verbatim.
882
-
883
- ## Step 13: auto-execute-leads
884
-
885
- Entered on `CampaignOffer.currentStep === "auto-execute-leads"` (set by
886
- atomic mint).
887
-
888
- > Reminder: every provider search you run from this point forward — the
889
- > review-batch source rerun in Step 13, any expansion search after the
890
- > review batch, alternate-lane probes, account-based reruns, and operator
891
- > follow-ups — MUST include `campaignOfferId`. This applies to
892
- > `search_prospeo`, `search_sales_nav`, `search_apollo`,
893
- > `search_signals`, and any other provider search tool added later.
894
- > Campaignless searches after mint orphan results from the campaign UI.
895
- >
896
- > **Lead-list vs campaign-table identity rule (do not confuse them):**
897
- > after the first `confirm_lead_list`, two workflow tables exist:
898
- >
899
- > - the **lead-list** table (the original `sourceLeadListId` returned by
900
- > `import_leads`), and
901
- > - the **campaign workflow table** (`CampaignOffer.workflowTableId`,
902
- > also returned as `campaignTableId` from `confirm_lead_list`).
903
- >
904
- > `confirm_lead_list` overwrites `CampaignOffer.selectedLeadListId` with
905
- > the campaign-table id. That means after the first confirm, the
906
- > campaign-table id is NOT a valid `sourceLeadListId` for any future
907
- > `import_leads` call.
908
- >
909
- > When you scale up the import, `mode: "add"` requires the ORIGINAL
910
- > lead-list id (the one returned by the first `import_leads`), never the
911
- > campaign-table id. Passing the campaign-table id silently writes
912
- > straight into the campaign workflow table, bypassing the lead list
913
- > entirely; the lead list stays at its original size and the
914
- > campaign-builder `/leads` UI throws "Lead list not found" because it
915
- > expects `selectedLeadListId` to resolve as a lead list.
916
- >
917
- > Capture the original `leadListId` from the FIRST `import_leads`
918
- > response and reuse it for every scale-up `import_leads(mode: "add")`
919
- > call on the same campaign. If the original id is unknown, query
920
- > for the lead-list table by name or skip the `mode: "add"` path; do
921
- > not pass the campaign-table id as `sourceLeadListId`.
922
-
923
- 1. Load `core/auto-execute.yaml`. Capture `import.importLimit`,
924
- `sample.sampleSize`, `sample.minProjectedPass`,
925
- `sample.maxRevisionRounds`, `messaging.tokenContract`,
926
- `messaging.critique.enabled`, `handoff.autoStart`,
927
- `handoff.orientation`, `retry.sameToolSameError`,
928
- `logging.logEveryThresholdTrip`.
929
- 2. Resolve the approved source before importing. If there is no
930
- `lead-source-intake.json`, keep the existing discovered-provider path:
931
- `import_leads({ campaignOfferId, targetLeadCount: importLimit })`.
932
- If a manifest exists, branch by `sourceType`:
933
- - `supplied-linkedin-profiles`: revalidate file metadata and confirmation
934
- token, confirm `load_csv_linkedin_leads` only after approval to batch the
935
- supplied CSV into a Sellable lead list, persist the returned `leadListId`,
936
- then call `confirm_lead_list({ sourceLeadListId: leadListId,
937
- targetLeadCount: importLimit })`. Do not call `import_leads` for this
938
- branch; the lead list is the source, and `confirm_lead_list` imports the
939
- bounded review batch into the campaign table.
940
- - `existing-lead-list`: revalidate that the lead list still exists in the
941
- same workspace, reuse it as `sourceLeadListId`, then call
942
- `confirm_lead_list({ sourceLeadListId, targetLeadCount: importLimit })`.
943
- - `supplied-domains`: reuse or confirm `load_csv_domains`, preserving
944
- `domainFilterId`; run a campaign-associated Prospeo people search with
945
- `campaignOfferId`, provider prompt preflight, and that `domainFilterId`;
946
- then import with `targetLeadCount: importLimit`.
947
- Never exceed `importLimit`. This is a review batch, not a full
948
- campaign-scale import. If a source search/list contains more rows than
949
- the cap, import only the first `importLimit` rows and leave the rest for
950
- a later expansion step.
951
- Persist or recover materialized IDs on resume: `leadListId`,
952
- `domainFilterId`, `searchId`, `campaignTableId`, imported row IDs, and queued
953
- cell IDs. If the source file changed after preview, or an existing lead list
954
- is missing/wrong-workspace, escalate before materialization instead of
955
- silently applying stale preview data. Retrying Step 13 must not duplicate
956
- lead lists, searches, campaign rows, or queued cells.
957
- 3. `wait_for_lead_list_ready` and `confirm_lead_list`.
958
- 4. **Verify the table is set up properly before queuing.** Call
959
- `get_rows_minimal({ tableId })` and confirm:
960
- - `Enrich Prospect` column exists on the table and every imported
961
- row has a corresponding cell with `status === "pending"`.
962
- - `DNC Check`, `ICP Score`, `Passes Rubric`, and `Generate Message`
963
- columns all exist too (the cascade depends on all of them).
964
- - If any are missing, or the cell is in a non-`pending` terminal
965
- state from an earlier run, ESCALATE with an explicit "table not
966
- configured for autonomous tail" diagnosis. Do NOT attempt to
967
- patch the table shape from the tail.
968
- 5. **Kick off the bounded review-batch cascade.** `queue_cells({ tableId,
969
- cellIds: <pending enrichCellIds for the imported review batch only> })`.
970
- You queue ONLY the review-batch `Enrich Prospect` cells; the cascade
971
- auto-dispatches `DNC Check`, `ICP Score`, `Passes Rubric`, and (for rows
972
- where `Passes Rubric === true`) `Generate Message` as each upstream
973
- completes. Do not queue cells for any rows beyond `importLimit` before
974
- user expansion approval.
975
- 6. `wait_for_campaign_table_ready` — waits for the review-batch cascade to
976
- drain.
977
- If it times out with most cells still `pending`, ESCALATE: the
978
- engine is blocked.
979
- 7. If the import returns zero usable leads, ESCALATE per
980
- `references/escalation-ladder.md` (hard fail).
981
- 8. `update_campaign({ campaignId, currentStep: "validate-sample" })`.
982
- 9. Orient user on the watch link (same pattern as every other step
983
- change).
984
-
985
- **Do NOT call `check_rubric`, `wait_for_rubric_results`,
986
- `enrich_with_prospeo`, or `bulk_enrich_with_prospeo` in Step 13.**
987
- Those are direct-API tools that fetch enrichment/scoring data to the
988
- caller; they do not write to the workflow table cells. The cascade
989
- triggered by `queue_cells` runs the `Enrich Prospect` column's
990
- http_request, which writes the enrichment into the cell, which
991
- triggers the next column, and so on. Running the direct-API tools
992
- is duplicate work with no cell-population side effect.
993
-
994
- ## Step 14: validate-sample (loop)
995
-
996
- Entered on `CampaignOffer.currentStep === "validate-sample"`. Full
997
- decision tree lives in `references/sample-validation-loop.md`.
998
-
999
- **Step 14 is pure observation.** The bounded review-batch cascade that Step
1000
- 13 kicked off (via `queue_cells` on Enrich Prospect) has already written ICP
1001
- scores and Passes Rubric values back into the workflow table. Step 14 reads
1002
- those review-batch cell results — it does NOT call `check_rubric`,
1003
- `wait_for_rubric_results`, `bulk_enrich_with_prospeo`, or any other
1004
- direct enrichment/scoring tool. All of that already happened in the
1005
- cascade; running them again is wasted work and can overwrite the
1006
- real cell results with unrelated data.
1007
-
1008
- Shape:
780
+ Before any auto-execute or validate-sample step, **load the dedicated tail subskill verbatim**:
1009
781
 
1010
- ```text
1011
- get_rows_minimal({ tableId })
1012
- passInSample = count of first sampleSize review-batch rows with passesRubric === true
1013
- projectedPass = round(passInSample / sampleSize * importLimit)
1014
-
1015
- if projectedPass >= minProjectedPass:
1016
- advance to Step 15
1017
- else:
1018
- diagnose brief-vs-list per sample-validation-loop.md
1019
- - brief: autonomous update_campaign_brief, then re-kick cascade
1020
- (queue_cells on enrichCellIds) and wait for the new
1021
- results
1022
- - list: ESCALATE (no auto-revision of leads)
1023
- - unknown: ESCALATE
1024
- revisionRound += 1
1025
- if revisionRound > maxRevisionRounds:
1026
- ESCALATE
1027
782
  ```
783
+ mcp__sellable__get_subskill_prompt({ subskillName: "create-campaign-v2-tail" })
784
+ ```
785
+
786
+ Follow that prompt verbatim. It contains the canonical MANDATORY TOOL ORDER (read this BEFORE any tail step), all Step 13-16 contracts, threshold-trip logging rules, and tail hard rules.
1028
787
 
1029
- Persisted counter rule: `revisionRound` persists across skill resume.
1030
- Do NOT reset to 0 on resume — a stale resume must still respect the
1031
- `maxRevisionRounds` cap.
1032
-
1033
- A "list problem" diagnosis NEVER triggers autonomous
1034
- `update_campaign_brief`. Escalate instead.
1035
-
1036
- When the sample passes the projected-pass floor, call
1037
- `update_campaign({ campaignId, currentStep: "auto-execute-messaging" })`
1038
- and orient the user that messaging will complete for the review batch only.
1039
-
1040
- ## Step 15: auto-execute-messaging
1041
-
1042
- Entered on `CampaignOffer.currentStep === "auto-execute-messaging"`.
1043
-
1044
- **Messages are produced by the `Generate Message` column cascade, not
1045
- by a separate tool.** Do NOT call a `generate_messages` MCP tool —
1046
- that tool does not exist; `generate-messages` is a subskill prompt
1047
- loaded during live messaging drafts. In the autonomous tail, messages
1048
- flow through the column pipeline: `Enrich Prospect` →
1049
- `DNC Check` → `ICP Score` → `Passes Rubric` → `Generate Message`.
1050
- Each column's http_request auto-fires when its upstream dependency
1051
- completes AND passes gate (e.g. `Passes Rubric === true` before
1052
- `Generate Message`). Your job in Step 15 is to WAIT for the review-batch
1053
- cascade to reach `Generate Message` for rows that passed ICP, and verify
1054
- the output — not to generate messages manually.
1055
-
1056
- **What Step 15 does:**
1057
-
1058
- 1. `get_rows_minimal` to read `passesRubric` + `generateMessageCellId`
1059
- - the Generate Message cell's `status` and `result` per row.
1060
- Before queueing or waiting on Generate Message cells, confirm the
1061
- minted campaign brief still contains `{{...}}` in `## Approved Message
1062
- Template`. If it does not, fail before the cascade runs. Do not repair
1063
- after mint; the template must be present during mint so Step 15 never
1064
- starts in freeform generation mode.
1065
- 2. For every row where `passesRubric === true`:
1066
- - The Generate Message cell should be `running` or `completed`
1067
- already (cascade auto-fired it). If it is still `pending`, queue
1068
- it explicitly: `queue_cells({ tableId, cellIds:
1069
- <generateMessageCellIds> })`.
1070
- 3. `wait_for_campaign_table_ready` (or poll) until every passing
1071
- row's Generate Message cell is `completed`.
1072
- 4. Read the results back via `get_rows` (full) and sanity-check a
1073
- sample against the Phase 84 token contract: no unresolved
1074
- `{{tokens}}`, no invented proof, one sentence per line, etc.
1075
- 5. If the sample fails the token contract, diagnose brief-vs-list
1076
- (same revision loop as Step 14) and escalate if over
1077
- `maxRevisionRounds`.
1078
- 6. On success, `update_campaign({ campaignId, currentStep:
1079
- "awaiting-user-greenlight" })`.
1080
-
1081
- **Do NOT hand-write message bodies via `update_cell`.** `update_cell`
1082
- is reachable for legitimate operator overrides AFTER the tail hands
1083
- off, but during autonomous tail the cascade owns the writes. If you
1084
- find yourself drafting a message body and about to push it through
1085
- `update_cell`, you have lost state — stop, read the cells, and let
1086
- the pipeline finish.
1087
-
1088
- **What `attach_sequence` does (Step 16, not here):** binds the
1089
- DM/InMail cadence template to the workflow table. Completely different
1090
- job from messaging. Does NOT generate or write message text. A
1091
- sequence with no `Generate Message` outputs would try to send empty
1092
- strings, which is why Step 16 requires Step 15 to be complete.
1093
-
1094
- 1. Observe the review-batch messages on the same sample that passed
1095
- validation.
1096
- 2. If `messaging.critique.enabled` is true (Plan 85-03), run the
1097
- bounded critique pass on the sample output per
1098
- `references/parallel-critique-protocol.md`. The pass runs on at
1099
- most `messaging.critique.sampleSize` rows, sends each row through
1100
- three parallel critics (targeting / copy / voice) that return
1101
- structured JSON, and merges the voices in a synthesis step that
1102
- enforces the Phase 84 token contract verbatim and re-runs the
1103
- finalizer pass. Any rewrite that invents proof or uses an
1104
- unsupported token falls back to the plain generated message. A
1105
- trip of `messaging.critique.budgetUsdCap` HALTS critique for the
1106
- remaining sample and continues the plain tail. Plan 85-02 reads
1107
- the flag but never flips it; critique stays off until the flag is
1108
- deliberately enabled.
1109
- 3. If `messaging.critique.opus.enabled` is also true, route the
1110
- highest-value subset through the Opus / craft-message path per
1111
- `references/thomas-variant-selection.md`. Opus is capped by
1112
- `messaging.critique.opus.maxMessagesPerPass` and
1113
- `messaging.critique.opus.budgetUsdCap`; tripping either cap keeps
1114
- the remaining rows on the non-Opus synthesizer. Opus is bound by
1115
- the same token contract as every other rewrite path.
1116
- 4. Check `messaging.tokenContract`. When `strict`, reject any sample
1117
- message that contains unresolved or unsupported tokens (including
1118
- any critique rewrite that tried to introduce one).
1119
- 5. If the sample passes the token contract (and critique when enabled),
1120
- stop at the review-batch handoff. Do NOT scale to the full source list
1121
- before explicit user expansion approval.
1122
- 6. If the sample fails the token contract or critique, diagnose +
1123
- loop the same way Step 14 does (brief-vs-list), subject to the same
1124
- `maxRevisionRounds` cap.
1125
- 7. On success, `update_campaign({ campaignId, currentStep:
1126
- "awaiting-user-greenlight" })` and orient the user.
1127
-
1128
- Critique failure modes NEVER escalate. A critic timeout, a total
1129
- timeout, a budget trip, a fake-proof rejection, or an unsupported-
1130
- token rejection all fall back to the plain generated message for that
1131
- row. Step 15 does not stall on critique.
1132
-
1133
- ## Step 16: awaiting-user-greenlight
1134
-
1135
- Entered on `CampaignOffer.currentStep === "awaiting-user-greenlight"`.
1136
- Full contract lives in `references/final-handoff-contract.md`.
1137
-
1138
- Shape:
1139
-
1140
- 1. `attach_sequence` (bind the committed sequence to the campaign).
1141
- 2. Re-surface the `watchUrl` using `references/watch-link-handoff.md`.
1142
- 3. Surface the `handoff.orientation` string from `auto-execute.yaml`
1143
- alongside the link.
1144
- 4. STOP. Do NOT call `start_campaign`. Do NOT auto-advance
1145
- `currentStep`. The autonomous tail ends here.
1146
- 5. Make the credit boundary explicit in the handoff: only the first review
1147
- batch has been enriched/messaged; expanding to more leads requires a
1148
- separate user instruction.
1149
-
1150
- Two equally valid greenlight channels take Step 16 into a running
1151
- campaign:
1152
-
1153
- - **UI path** — user clicks "Approve all" and "Start Campaign" in the
1154
- watch-link UI. The skill stays idle. On the next resume, it observes
1155
- the running state and shifts to a "campaign is live" confirmation.
1156
- - **Claude greenlight path** — user replies with an affirmative ("yeah
1157
- start" / "looks good, start" / "ship it"). The skill then performs,
1158
- in order: (a) bulk-approve queued messages via the EXISTING endpoint
1159
- `POST /api/v3/workflow-tables/cells/approve-batch` (do NOT invent a
1160
- new tool/endpoint), (b) `start_campaign({ campaignId })`, (c)
1161
- `update_campaign({ campaignId, currentStep: "running" })`, (d)
1162
- re-surface `watchUrl` plus a "campaign is live" confirmation.
1163
-
1164
- A Claude greenlight on an already-running campaign is a no-op
1165
- confirmation, NOT a duplicate start. Detect via `get_campaign` before
1166
- calling any mutating tool; if the campaign is already running, skip
1167
- approve-batch / start_campaign / update_campaign and just confirm.
1168
-
1169
- Workspace/sender mismatch at greenlight time aborts the start with the
1170
- same invariant Phase 84 enforces.
1171
-
1172
- ## Threshold Trips and Logging
1173
-
1174
- When `logging.logEveryThresholdTrip === true` (default), log every
1175
- threshold trip: `projectedPass` computation, retry cap, revision cap,
1176
- hard-fail. These logs drive `auto-execute.yaml` tuning after 3-5 real
1177
- runs.
1178
-
1179
- ## Tail Hard Rules
1180
-
1181
- - Review-batch sourcing/import happens in Step 13, not during atomic mint.
1182
- - Full-list expansion is not part of the default tail. It requires user
1183
- approval after the review batch.
1184
- Import must never exceed `importLimit`.
1185
- - The tail NEVER calls `start_campaign` on its own.
1186
- - The tail NEVER auto-revises leads. Brief revision is autonomous; lead
1187
- revision is always operator-gated.
1188
- - `revisionRound` persists across resume — no silent reset.
1189
- - The bulk-approve action in the Claude greenlight path uses the
1190
- existing `POST /api/v3/workflow-tables/cells/approve-batch` endpoint
1191
- — do NOT create a new tool or endpoint for this.
1192
- - Threshold trips are logged for calibration.
1193
-
1194
- </autonomous_tail>
1195
-
1196
- <references_index>
1197
-
1198
- | File | Load when |
1199
- | ------------------------------------------ | ---------------------------------------------------------------------- |
1200
- | `references/approval-gate-framing.md` | Phase 85 commit gate, before showing the approval packet |
1201
- | `references/watch-link-handoff.md` | Atomic mint + every tail resume, before surfacing watchUrl |
1202
- | `references/sample-validation-loop.md` | Step 14, before enriching + scoring the sample |
1203
- | `references/escalation-ladder.md` | Any tail step that needs to decide retry / revise / escalate |
1204
- | `references/final-handoff-contract.md` | Step 16, and every Claude greenlight turn |
1205
- | `references/parallel-critique-protocol.md` | Step 15 when `messaging.critique.enabled` is true |
1206
- | `references/thomas-variant-selection.md` | Step 15 when `messaging.critique.opus.enabled` is true |
1207
- | `references/thomas-revision-filters.md` | Any critique rewrite, before persisting |
1208
- | `core/auto-execute.yaml` | Start of Step 13, load once; all tail steps read parsed values |
1209
- | `core/auto-execute.README.md` | When tuning `auto-execute.yaml` knobs or reviewing threshold-trip logs |
1210
-
1211
- </references_index>
788
+ Do NOT invoke any tail tool (auto_execute_leads, validate_sample, auto_execute_messaging, etc.) without first loading this subskill — the tool order and the threshold-trip checks live there.
@@ -0,0 +1,446 @@
1
+ ---
2
+ name: create-campaign-v2-tail
3
+ description: Tail steps (13-16) + MANDATORY TOOL ORDER + Threshold Trips + Tail Hard Rules for create-campaign-v2. Loaded on-demand via get_subskill_prompt before any auto-execute or validate-sample step. Internal subskill.
4
+ visibility: internal
5
+ ---
6
+
7
+ # create-campaign-v2 Tail (Steps 13-16 + Tool Ordering + Hard Rules)
8
+
9
+ This is the tail detail extracted from the main create-campaign-v2 SKILL.md to keep the entry prompt slim. The agent loads this on-demand BEFORE entering the auto-execute or validate-sample steps. Follow this verbatim.
10
+
11
+ ## MANDATORY TOOL ORDER (read this BEFORE any tail step)
12
+
13
+ Every tail run MUST call these tools in this exact order. The tail is
14
+ **review-batch cascade-driven**: you kick off Enrich Prospect only for
15
+ the imported review batch, and the workflow engine chains DNC Check →
16
+ ICP Score → Passes Rubric → Generate Message automatically. Your job is
17
+ to START the bounded cascade, WAIT for it to drain, OBSERVE the results,
18
+ and stop for review.
19
+ Do NOT manually run rubric-check, enrich, or message-generation
20
+ tools — the cascade already does them.
21
+
22
+ ```text
23
+ Step 13 — import review batch + kick bounded cascade
24
+ import_leads(targetLeadCount=importLimit)
25
+ wait_for_lead_list_ready
26
+ confirm_lead_list
27
+ get_rows_minimal # capture enrichCellIds
28
+ queue_cells(cellIds=<review-batch Enrich Prospect cells only>) <-- starts bounded chain
29
+ wait_for_campaign_table_ready # review-batch cascade drains here
30
+ update_campaign(currentStep=validate-sample)
31
+
32
+ Step 14 — observe sample
33
+ get_rows_minimal # read passesRubric + message cell status per row
34
+ compute projectedPass
35
+ if OK: update_campaign(currentStep=auto-execute-messaging)
36
+ else: diagnose brief-vs-list; if brief: update_campaign_brief + re-queue + wait
37
+ (check_rubric / bulk_enrich_with_prospeo are NOT called here —
38
+ cascade already did them. wait_for_rubric_results is OK as a
39
+ read-only observation helper if you need to block on completion.)
40
+
41
+ Step 15 — observe messaging
42
+ get_rows_minimal # confirm passing rows have completed Generate Message cells
43
+ (rare) queue_cells on any pending Generate Message cells
44
+ token-contract spot check via get_rows
45
+ update_campaign(currentStep=awaiting-user-greenlight)
46
+ (generate_messages is NOT an MCP tool; messages come from the cascade)
47
+
48
+ Step 16 — awaiting-user-greenlight
49
+ attach_recommended_sequence({ campaignId }) # tier-aware: premium/SN -> If Open Profile->INMAIL_OPEN, else INVITE->accepted->DM
50
+ re-surface watchUrl + review-batch orientation
51
+ STOP. DO NOT call start_campaign. DO NOT auto-advance currentStep.
52
+ ```
53
+
54
+ **Prefer `attach_recommended_sequence` over `attach_sequence` in the
55
+ autonomous tail.** The recommended tool takes only `campaignId` and
56
+ delegates template selection to the backend's tier-aware selector
57
+ (`selectTemplateForTiers`). `attach_sequence` requires you to
58
+ hand-author a `version: 2` template with nodes, branches, and
59
+ entryNodeId — that's error-prone mid-long-context (galley-off UAT
60
+ `20260420T195732Z` failed because Claude hit "Invalid node type" and
61
+ tried to debug via forbidden Bash/Glob calls). Use `attach_sequence`
62
+ only when the caller explicitly needs a custom non-recommended cadence.
63
+
64
+ Hard gates — if you find yourself about to violate any of these, stop
65
+ first:
66
+
67
+ - Do NOT use `update_cell` to write message bodies. The `Generate
68
+ Message` column's http_request writes those cells via the cascade.
69
+ `update_cell` remains reachable for legitimate operator overrides
70
+ AFTER the tail hands off.
71
+ - Do NOT call `check_rubric`, `enrich_with_prospeo`, or
72
+ `bulk_enrich_with_prospeo` in the tail. Those are direct-API
73
+ mutation tools that fetch data to the caller without writing to
74
+ the workflow table cells. Step 13's `queue_cells` already
75
+ triggers the column-level enrichment that populates those cells.
76
+ Running these tools in the tail produces duplicate cost with no
77
+ cell side effect.
78
+ - `wait_for_rubric_results` is read-only and OK to use as an
79
+ observation helper when you need to block on rubric completion —
80
+ it does not mutate cells. Prefer `get_rows_minimal` +
81
+ `wait_for_campaign_table_ready` for the primary cascade-drain
82
+ path, but use `wait_for_rubric_results({ targetCount: cohortSize })`
83
+ when the table-level wait returns before rubric cells finish.
84
+ - `start_campaign` is FORBIDDEN in the autonomous tail. It belongs only
85
+ in the Claude-greenlight path, AFTER the user signals "start". See
86
+ `references/final-handoff-contract.md`.
87
+ - You MAY NOT call `start_campaign` without a prior successful
88
+ `attach_recommended_sequence` (or `attach_sequence` if a custom
89
+ cadence is explicitly required) in the same run.
90
+ - You MAY NOT call `start_campaign` while ANY passing row has an
91
+ empty `Generate Message` cell. If you discover empty message cells in
92
+ the greenlight turn, ABORT greenlight and return to Step 15 first.
93
+ - You MAY NOT call `attach_sequence` while ANY passing row has an
94
+ empty `Generate Message` cell. Before `attach_sequence`, call
95
+ `get_rows_minimal` and confirm every row where `passesRubric=true`
96
+ has a `completed` Generate Message cell with non-empty `result`. If
97
+ any are pending, call `queue_cells` on those generateMessageCellIds
98
+ and wait. If rows truly won't message, ESCALATE.
99
+ - You MAY NOT advance past Step 13 without calling `queue_cells` on
100
+ the review-batch Enrich Prospect cells. Without it, every downstream
101
+ cell stays `pending` and the campaign ships empty.
102
+ - You MAY NOT queue enrichment for rows outside the configured review
103
+ batch before the user approves expansion. Full-list enrichment/message
104
+ generation is a credit-spend decision and must happen after the user
105
+ has reviewed the sample.
106
+
107
+ Load `core/auto-execute.yaml` exactly once at the start of Step 13. All
108
+ subsequent steps read the already-parsed config. Do not re-load mid-run.
109
+ Load each subskill prompt (`create-campaign-v2`, `research-sender`,
110
+ `generate-messages`) at most once per run. A multi-call chunk sequence counts
111
+ as one load. If a tool result already told you to load a prompt, load all
112
+ chunks once and remember; do not restart the same prompt from offset 0 later.
113
+
114
+ After every `update_campaign({ currentStep: ... })` in the tail, narrate
115
+ what changed and orient the user to what the watch link will show next —
116
+ reuse the v1 `create-campaign` watch-mode pattern verbatim.
117
+
118
+ ## Step 13: auto-execute-leads
119
+
120
+ Entered on `CampaignOffer.currentStep === "auto-execute-leads"` (set by
121
+ atomic mint).
122
+
123
+ > Reminder: every provider search you run from this point forward — the
124
+ > review-batch source rerun in Step 13, any expansion search after the
125
+ > review batch, alternate-lane probes, account-based reruns, and operator
126
+ > follow-ups — MUST include `campaignOfferId`. This applies to
127
+ > `search_prospeo`, `search_sales_nav`, `search_apollo`,
128
+ > `search_signals`, and any other provider search tool added later.
129
+ > Campaignless searches after mint orphan results from the campaign UI.
130
+ >
131
+ > **Lead-list vs campaign-table identity rule (do not confuse them):**
132
+ > after the first `confirm_lead_list`, two workflow tables exist:
133
+ >
134
+ > - the **lead-list** table (the original `sourceLeadListId` returned by
135
+ > `import_leads`), and
136
+ > - the **campaign workflow table** (`CampaignOffer.workflowTableId`,
137
+ > also returned as `campaignTableId` from `confirm_lead_list`).
138
+ >
139
+ > `confirm_lead_list` overwrites `CampaignOffer.selectedLeadListId` with
140
+ > the campaign-table id. That means after the first confirm, the
141
+ > campaign-table id is NOT a valid `sourceLeadListId` for any future
142
+ > `import_leads` call.
143
+ >
144
+ > When you scale up the import, `mode: "add"` requires the ORIGINAL
145
+ > lead-list id (the one returned by the first `import_leads`), never the
146
+ > campaign-table id. Passing the campaign-table id silently writes
147
+ > straight into the campaign workflow table, bypassing the lead list
148
+ > entirely; the lead list stays at its original size and the
149
+ > campaign-builder `/leads` UI throws "Lead list not found" because it
150
+ > expects `selectedLeadListId` to resolve as a lead list.
151
+ >
152
+ > Capture the original `leadListId` from the FIRST `import_leads`
153
+ > response and reuse it for every scale-up `import_leads(mode: "add")`
154
+ > call on the same campaign. If the original id is unknown, query
155
+ > for the lead-list table by name or skip the `mode: "add"` path; do
156
+ > not pass the campaign-table id as `sourceLeadListId`.
157
+
158
+ 1. Load `core/auto-execute.yaml`. Capture `import.importLimit`,
159
+ `sample.sampleSize`, `sample.minProjectedPass`,
160
+ `sample.maxRevisionRounds`, `messaging.tokenContract`,
161
+ `messaging.critique.enabled`, `handoff.autoStart`,
162
+ `handoff.orientation`, `retry.sameToolSameError`,
163
+ `logging.logEveryThresholdTrip`.
164
+ 2. Resolve the approved source before importing. If there is no
165
+ `lead-source-intake.json`, keep the existing discovered-provider path:
166
+ `import_leads({ campaignOfferId, targetLeadCount: importLimit })`.
167
+ If a manifest exists, branch by `sourceType`:
168
+ - `supplied-linkedin-profiles`: revalidate file metadata and confirmation
169
+ token, confirm `load_csv_linkedin_leads` only after approval to batch the
170
+ supplied CSV into a Sellable lead list, persist the returned `leadListId`,
171
+ then call `confirm_lead_list({ sourceLeadListId: leadListId,
172
+ targetLeadCount: importLimit })`. Do not call `import_leads` for this
173
+ branch; the lead list is the source, and `confirm_lead_list` imports the
174
+ bounded review batch into the campaign table.
175
+ - `existing-lead-list`: revalidate that the lead list still exists in the
176
+ same workspace, reuse it as `sourceLeadListId`, then call
177
+ `confirm_lead_list({ sourceLeadListId, targetLeadCount: importLimit })`.
178
+ - `supplied-domains`: reuse or confirm `load_csv_domains`, preserving
179
+ `domainFilterId`; run a campaign-associated Prospeo people search with
180
+ `campaignOfferId`, provider prompt preflight, and that `domainFilterId`;
181
+ then import with `targetLeadCount: importLimit`.
182
+ Never exceed `importLimit`. This is a review batch, not a full
183
+ campaign-scale import. If a source search/list contains more rows than
184
+ the cap, import only the first `importLimit` rows and leave the rest for
185
+ a later expansion step.
186
+ Persist or recover materialized IDs on resume: `leadListId`,
187
+ `domainFilterId`, `searchId`, `campaignTableId`, imported row IDs, and queued
188
+ cell IDs. If the source file changed after preview, or an existing lead list
189
+ is missing/wrong-workspace, escalate before materialization instead of
190
+ silently applying stale preview data. Retrying Step 13 must not duplicate
191
+ lead lists, searches, campaign rows, or queued cells.
192
+ 3. `wait_for_lead_list_ready` and `confirm_lead_list`.
193
+ 4. **Verify the table is set up properly before queuing.** Call
194
+ `get_rows_minimal({ tableId })` and confirm:
195
+ - `Enrich Prospect` column exists on the table and every imported
196
+ row has a corresponding cell with `status === "pending"`.
197
+ - `DNC Check`, `ICP Score`, `Passes Rubric`, and `Generate Message`
198
+ columns all exist too (the cascade depends on all of them).
199
+ - If any are missing, or the cell is in a non-`pending` terminal
200
+ state from an earlier run, ESCALATE with an explicit "table not
201
+ configured for autonomous tail" diagnosis. Do NOT attempt to
202
+ patch the table shape from the tail.
203
+ 5. **Kick off the bounded review-batch cascade.** `queue_cells({ tableId,
204
+ cellIds: <pending enrichCellIds for the imported review batch only> })`.
205
+ You queue ONLY the review-batch `Enrich Prospect` cells; the cascade
206
+ auto-dispatches `DNC Check`, `ICP Score`, `Passes Rubric`, and (for rows
207
+ where `Passes Rubric === true`) `Generate Message` as each upstream
208
+ completes. Do not queue cells for any rows beyond `importLimit` before
209
+ user expansion approval.
210
+ 6. `wait_for_campaign_table_ready` — waits for the review-batch cascade to
211
+ drain.
212
+ If it times out with most cells still `pending`, ESCALATE: the
213
+ engine is blocked.
214
+ 7. If the import returns zero usable leads, ESCALATE per
215
+ `references/escalation-ladder.md` (hard fail).
216
+ 8. `update_campaign({ campaignId, currentStep: "validate-sample" })`.
217
+ 9. Orient user on the watch link (same pattern as every other step
218
+ change).
219
+
220
+ **Do NOT call `check_rubric`, `wait_for_rubric_results`,
221
+ `enrich_with_prospeo`, or `bulk_enrich_with_prospeo` in Step 13.**
222
+ Those are direct-API tools that fetch enrichment/scoring data to the
223
+ caller; they do not write to the workflow table cells. The cascade
224
+ triggered by `queue_cells` runs the `Enrich Prospect` column's
225
+ http_request, which writes the enrichment into the cell, which
226
+ triggers the next column, and so on. Running the direct-API tools
227
+ is duplicate work with no cell-population side effect.
228
+
229
+ ## Step 14: validate-sample (loop)
230
+
231
+ Entered on `CampaignOffer.currentStep === "validate-sample"`. Full
232
+ decision tree lives in `references/sample-validation-loop.md`.
233
+
234
+ **Step 14 is pure observation.** The bounded review-batch cascade that Step
235
+ 13 kicked off (via `queue_cells` on Enrich Prospect) has already written ICP
236
+ scores and Passes Rubric values back into the workflow table. Step 14 reads
237
+ those review-batch cell results — it does NOT call `check_rubric`,
238
+ `wait_for_rubric_results`, `bulk_enrich_with_prospeo`, or any other
239
+ direct enrichment/scoring tool. All of that already happened in the
240
+ cascade; running them again is wasted work and can overwrite the
241
+ real cell results with unrelated data.
242
+
243
+ Shape:
244
+
245
+ ```text
246
+ get_rows_minimal({ tableId })
247
+ passInSample = count of first sampleSize review-batch rows with passesRubric === true
248
+ projectedPass = round(passInSample / sampleSize * importLimit)
249
+
250
+ if projectedPass >= minProjectedPass:
251
+ advance to Step 15
252
+ else:
253
+ diagnose brief-vs-list per sample-validation-loop.md
254
+ - brief: autonomous update_campaign_brief, then re-kick cascade
255
+ (queue_cells on enrichCellIds) and wait for the new
256
+ results
257
+ - list: ESCALATE (no auto-revision of leads)
258
+ - unknown: ESCALATE
259
+ revisionRound += 1
260
+ if revisionRound > maxRevisionRounds:
261
+ ESCALATE
262
+ ```
263
+
264
+ Persisted counter rule: `revisionRound` persists across skill resume.
265
+ Do NOT reset to 0 on resume — a stale resume must still respect the
266
+ `maxRevisionRounds` cap.
267
+
268
+ A "list problem" diagnosis NEVER triggers autonomous
269
+ `update_campaign_brief`. Escalate instead.
270
+
271
+ When the sample passes the projected-pass floor, call
272
+ `update_campaign({ campaignId, currentStep: "auto-execute-messaging" })`
273
+ and orient the user that messaging will complete for the review batch only.
274
+
275
+ ## Step 15: auto-execute-messaging
276
+
277
+ Entered on `CampaignOffer.currentStep === "auto-execute-messaging"`.
278
+
279
+ **Messages are produced by the `Generate Message` column cascade, not
280
+ by a separate tool.** Do NOT call a `generate_messages` MCP tool —
281
+ that tool does not exist; `generate-messages` is a subskill prompt
282
+ loaded during live messaging drafts. In the autonomous tail, messages
283
+ flow through the column pipeline: `Enrich Prospect` →
284
+ `DNC Check` → `ICP Score` → `Passes Rubric` → `Generate Message`.
285
+ Each column's http_request auto-fires when its upstream dependency
286
+ completes AND passes gate (e.g. `Passes Rubric === true` before
287
+ `Generate Message`). Your job in Step 15 is to WAIT for the review-batch
288
+ cascade to reach `Generate Message` for rows that passed ICP, and verify
289
+ the output — not to generate messages manually.
290
+
291
+ **What Step 15 does:**
292
+
293
+ 1. `get_rows_minimal` to read `passesRubric` + `generateMessageCellId`
294
+ - the Generate Message cell's `status` and `result` per row.
295
+ Before queueing or waiting on Generate Message cells, confirm the
296
+ minted campaign brief still contains `{{...}}` in `## Approved Message
297
+ Template`. If it does not, fail before the cascade runs. Do not repair
298
+ after mint; the template must be present during mint so Step 15 never
299
+ starts in freeform generation mode.
300
+ 2. For every row where `passesRubric === true`:
301
+ - The Generate Message cell should be `running` or `completed`
302
+ already (cascade auto-fired it). If it is still `pending`, queue
303
+ it explicitly: `queue_cells({ tableId, cellIds:
304
+ <generateMessageCellIds> })`.
305
+ 3. `wait_for_campaign_table_ready` (or poll) until every passing
306
+ row's Generate Message cell is `completed`.
307
+ 4. Read the results back via `get_rows` (full) and sanity-check a
308
+ sample against the Phase 84 token contract: no unresolved
309
+ `{{tokens}}`, no invented proof, one sentence per line, etc.
310
+ 5. If the sample fails the token contract, diagnose brief-vs-list
311
+ (same revision loop as Step 14) and escalate if over
312
+ `maxRevisionRounds`.
313
+ 6. On success, `update_campaign({ campaignId, currentStep:
314
+ "awaiting-user-greenlight" })`.
315
+
316
+ **Do NOT hand-write message bodies via `update_cell`.** `update_cell`
317
+ is reachable for legitimate operator overrides AFTER the tail hands
318
+ off, but during autonomous tail the cascade owns the writes. If you
319
+ find yourself drafting a message body and about to push it through
320
+ `update_cell`, you have lost state — stop, read the cells, and let
321
+ the pipeline finish.
322
+
323
+ **What `attach_sequence` does (Step 16, not here):** binds the
324
+ DM/InMail cadence template to the workflow table. Completely different
325
+ job from messaging. Does NOT generate or write message text. A
326
+ sequence with no `Generate Message` outputs would try to send empty
327
+ strings, which is why Step 16 requires Step 15 to be complete.
328
+
329
+ 1. Observe the review-batch messages on the same sample that passed
330
+ validation.
331
+ 2. If `messaging.critique.enabled` is true (Plan 85-03), run the
332
+ bounded critique pass on the sample output per
333
+ `references/parallel-critique-protocol.md`. The pass runs on at
334
+ most `messaging.critique.sampleSize` rows, sends each row through
335
+ three parallel critics (targeting / copy / voice) that return
336
+ structured JSON, and merges the voices in a synthesis step that
337
+ enforces the Phase 84 token contract verbatim and re-runs the
338
+ finalizer pass. Any rewrite that invents proof or uses an
339
+ unsupported token falls back to the plain generated message. A
340
+ trip of `messaging.critique.budgetUsdCap` HALTS critique for the
341
+ remaining sample and continues the plain tail. Plan 85-02 reads
342
+ the flag but never flips it; critique stays off until the flag is
343
+ deliberately enabled.
344
+ 3. If `messaging.critique.opus.enabled` is also true, route the
345
+ highest-value subset through the Opus / craft-message path per
346
+ `references/thomas-variant-selection.md`. Opus is capped by
347
+ `messaging.critique.opus.maxMessagesPerPass` and
348
+ `messaging.critique.opus.budgetUsdCap`; tripping either cap keeps
349
+ the remaining rows on the non-Opus synthesizer. Opus is bound by
350
+ the same token contract as every other rewrite path.
351
+ 4. Check `messaging.tokenContract`. When `strict`, reject any sample
352
+ message that contains unresolved or unsupported tokens (including
353
+ any critique rewrite that tried to introduce one).
354
+ 5. If the sample passes the token contract (and critique when enabled),
355
+ stop at the review-batch handoff. Do NOT scale to the full source list
356
+ before explicit user expansion approval.
357
+ 6. If the sample fails the token contract or critique, diagnose +
358
+ loop the same way Step 14 does (brief-vs-list), subject to the same
359
+ `maxRevisionRounds` cap.
360
+ 7. On success, `update_campaign({ campaignId, currentStep:
361
+ "awaiting-user-greenlight" })` and orient the user.
362
+
363
+ Critique failure modes NEVER escalate. A critic timeout, a total
364
+ timeout, a budget trip, a fake-proof rejection, or an unsupported-
365
+ token rejection all fall back to the plain generated message for that
366
+ row. Step 15 does not stall on critique.
367
+
368
+ ## Step 16: awaiting-user-greenlight
369
+
370
+ Entered on `CampaignOffer.currentStep === "awaiting-user-greenlight"`.
371
+ Full contract lives in `references/final-handoff-contract.md`.
372
+
373
+ Shape:
374
+
375
+ 1. `attach_sequence` (bind the committed sequence to the campaign).
376
+ 2. Re-surface the `watchUrl` using `references/watch-link-handoff.md`.
377
+ 3. Surface the `handoff.orientation` string from `auto-execute.yaml`
378
+ alongside the link.
379
+ 4. STOP. Do NOT call `start_campaign`. Do NOT auto-advance
380
+ `currentStep`. The autonomous tail ends here.
381
+ 5. Make the credit boundary explicit in the handoff: only the first review
382
+ batch has been enriched/messaged; expanding to more leads requires a
383
+ separate user instruction.
384
+
385
+ Two equally valid greenlight channels take Step 16 into a running
386
+ campaign:
387
+
388
+ - **UI path** — user clicks "Approve all" and "Start Campaign" in the
389
+ watch-link UI. The skill stays idle. On the next resume, it observes
390
+ the running state and shifts to a "campaign is live" confirmation.
391
+ - **Claude greenlight path** — user replies with an affirmative ("yeah
392
+ start" / "looks good, start" / "ship it"). The skill then performs,
393
+ in order: (a) bulk-approve queued messages via the EXISTING endpoint
394
+ `POST /api/v3/workflow-tables/cells/approve-batch` (do NOT invent a
395
+ new tool/endpoint), (b) `start_campaign({ campaignId })`, (c)
396
+ `update_campaign({ campaignId, currentStep: "running" })`, (d)
397
+ re-surface `watchUrl` plus a "campaign is live" confirmation.
398
+
399
+ A Claude greenlight on an already-running campaign is a no-op
400
+ confirmation, NOT a duplicate start. Detect via `get_campaign` before
401
+ calling any mutating tool; if the campaign is already running, skip
402
+ approve-batch / start_campaign / update_campaign and just confirm.
403
+
404
+ Workspace/sender mismatch at greenlight time aborts the start with the
405
+ same invariant Phase 84 enforces.
406
+
407
+ ## Threshold Trips and Logging
408
+
409
+ When `logging.logEveryThresholdTrip === true` (default), log every
410
+ threshold trip: `projectedPass` computation, retry cap, revision cap,
411
+ hard-fail. These logs drive `auto-execute.yaml` tuning after 3-5 real
412
+ runs.
413
+
414
+ ## Tail Hard Rules
415
+
416
+ - Review-batch sourcing/import happens in Step 13, not during atomic mint.
417
+ - Full-list expansion is not part of the default tail. It requires user
418
+ approval after the review batch.
419
+ Import must never exceed `importLimit`.
420
+ - The tail NEVER calls `start_campaign` on its own.
421
+ - The tail NEVER auto-revises leads. Brief revision is autonomous; lead
422
+ revision is always operator-gated.
423
+ - `revisionRound` persists across resume — no silent reset.
424
+ - The bulk-approve action in the Claude greenlight path uses the
425
+ existing `POST /api/v3/workflow-tables/cells/approve-batch` endpoint
426
+ — do NOT create a new tool or endpoint for this.
427
+ - Threshold trips are logged for calibration.
428
+
429
+ </autonomous_tail>
430
+
431
+ <references_index>
432
+
433
+ | File | Load when |
434
+ | ------------------------------------------ | ---------------------------------------------------------------------- |
435
+ | `references/approval-gate-framing.md` | Phase 85 commit gate, before showing the approval packet |
436
+ | `references/watch-link-handoff.md` | Atomic mint + every tail resume, before surfacing watchUrl |
437
+ | `references/sample-validation-loop.md` | Step 14, before enriching + scoring the sample |
438
+ | `references/escalation-ladder.md` | Any tail step that needs to decide retry / revise / escalate |
439
+ | `references/final-handoff-contract.md` | Step 16, and every Claude greenlight turn |
440
+ | `references/parallel-critique-protocol.md` | Step 15 when `messaging.critique.enabled` is true |
441
+ | `references/thomas-variant-selection.md` | Step 15 when `messaging.critique.opus.enabled` is true |
442
+ | `references/thomas-revision-filters.md` | Any critique rewrite, before persisting |
443
+ | `core/auto-execute.yaml` | Start of Step 13, load once; all tail steps read parsed values |
444
+ | `core/auto-execute.README.md` | When tuning `auto-execute.yaml` knobs or reviewing threshold-trip logs |
445
+
446
+ </references_index>