@sellable/mcp 0.1.37 → 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.
|
@@ -761,1265 +761,28 @@ Do not:
|
|
|
761
761
|
|
|
762
762
|
## Step 3: Generate Message
|
|
763
763
|
|
|
764
|
-
|
|
765
|
-
`lead-filter.md`, optional `message-prep.md`, and optional
|
|
766
|
-
`message-candidate-drafts.md`.
|
|
764
|
+
The full Step 3 detail (~45k chars: message-validation workflow, gold-standard rules, proof inventory, candidate generation, finalizer pass, message-review-decision contract, A+B+C subject shape, token-fill rules, etc.) lives in a dedicated on-demand subskill to keep this entry prompt fast to load.
|
|
767
765
|
|
|
768
|
-
|
|
769
|
-
`lead-sample.json` exist. If `lead-filter.md` is not ready yet, draft from the
|
|
770
|
-
lead-review message handoff rows, then reconcile after the filter lands.
|
|
766
|
+
When you reach Step 3, **load the dedicated subskill verbatim**:
|
|
771
767
|
|
|
772
|
-
|
|
768
|
+
```
|
|
769
|
+
mcp__sellable__get_subskill_prompt({ subskillName: "create-campaign-v2-generate-message" })
|
|
770
|
+
```
|
|
773
771
|
|
|
774
|
-
-
|
|
775
|
-
|
|
776
|
-
Run the `generate-messages` skill in caller-declared `DRY MODE`. Its SKILL.md
|
|
777
|
-
holds the full drafting contract (retrieval, proof inventory, candidates,
|
|
778
|
-
finalizer pass, voice rules, safety). This step only covers orchestration.
|
|
779
|
-
This is not optional: before any write to `message-validation.md`,
|
|
780
|
-
`message-review.md`, `approval-packet.md`, or a commit-gate question, the
|
|
781
|
-
current run must load the full `generate-messages` prompt. In hosted/from-
|
|
782
|
-
scratch runs, load it with chunked `get_subskill_prompt({ subskillName:
|
|
783
|
-
"generate-messages", offset, limit })` calls so every tool result stays small
|
|
784
|
-
enough for the streamed harness. Start with `offset: 0`; the MCP will apply a
|
|
785
|
-
portable default limit when omitted. Keep calling with `offset: nextOffset`
|
|
786
|
-
until `hasMore` is false.
|
|
787
|
-
In Codex-hosted runs, this is a quality gate, not just provenance: if the
|
|
788
|
-
model cannot retrieve the complete prompt or cannot follow the required
|
|
789
|
-
gold-standard deliberation flow, stop at `message-review` and ask for
|
|
790
|
-
`revise-messaging`. Do not mint the campaign to compensate for weak copy.
|
|
791
|
-
Do not hand-write `message-validation.md` from `message-prep.md` or
|
|
792
|
-
`message-candidate-drafts.md`. Those files are planning inputs only; the final
|
|
793
|
-
message-validation artifact must come from the actual `generate-messages`
|
|
794
|
-
prompt path (`get_message_prompt` / `generate-messages`).
|
|
795
|
-
|
|
796
|
-
Orchestration requirements:
|
|
797
|
-
|
|
798
|
-
- start once real leads exist: `brief.md`, `lead-review.md`, and
|
|
799
|
-
`lead-sample.json` are enough to run generate-message
|
|
800
|
-
- use only the find-leads message handoff rows as basis examples, e.g.
|
|
801
|
-
"prospects 1, 5, and 6 are solid enough to think from"
|
|
802
|
-
- if `lead-filter.md` is ready, use it; if it arrives after message drafting,
|
|
803
|
-
reconcile before approval and rerun or revise messaging if the selected
|
|
804
|
-
winner depended on rows that fail the final filter
|
|
805
|
-
- never write `message-validation.md`, render a message review, render an
|
|
806
|
-
approval packet, or ask the commit gate until 100% of the real
|
|
807
|
-
`generate-messages` prompt has been read
|
|
808
|
-
- before `message-review` can recommend `approve-message`, verify
|
|
809
|
-
`message-validation.md` contains the full generate-messages shape:
|
|
810
|
-
`Gold Standard Strategy Map`, `Proof Inventory`, `Token Fill Rules`,
|
|
811
|
-
`Token Adherence Table`, `Angle Drafts`, `Kill / Combine Review`,
|
|
812
|
-
`Finalists`, `Finalizer Pass`, `Gold-Standard Quality Gate`,
|
|
813
|
-
`Skeptical Prospect Review`, `Winner Gate`, and a raw sendable
|
|
814
|
-
`Selected Winner`. If any are missing, recommend `revise-messaging` and do
|
|
815
|
-
not continue to approval or mint.
|
|
816
|
-
- pass no `campaignId`
|
|
817
|
-
- read only `brief.md`, `lead-review.md`, `lead-sample.json`, optional
|
|
818
|
-
`lead-filter.md`, and the `gold-standard-*` references. If
|
|
819
|
-
`message-prep.md` exists, use it as a
|
|
820
|
-
planning cache instead of redoing the same element inventory from scratch. If
|
|
821
|
-
`message-candidate-drafts.md` exists, use it as provisional draft input only
|
|
822
|
-
after checking its basis rows against the completed `lead-filter.md`, when
|
|
823
|
-
available.
|
|
824
|
-
- generate 2-3 sample messages inline
|
|
825
|
-
- start output with `Mode: DRY MODE (no DB mutation)`
|
|
826
|
-
- treat the archived examples as the **quality bar**, not a paste source;
|
|
827
|
-
write messages that could plausibly belong in the archive for this motion
|
|
828
|
-
- exact-template preservation only applies when the archived winner is the
|
|
829
|
-
same company as the brief
|
|
830
|
-
- draft 3 internal candidates and run a Finalizer Pass that combines the
|
|
831
|
-
best opener, proof sentence, bridge, and CTA across them into one winner
|
|
832
|
-
- if all finalists use the same first substantive line, treat the opener
|
|
833
|
-
test as failed; compare materially different opener jobs before selecting
|
|
834
|
-
a winner, or route to `revise-message`
|
|
835
|
-
- the Finalizer Pass must block adjacent repeated copy. Do not let a pain
|
|
836
|
-
line and mechanism line reuse the same noun stack unless the second line
|
|
837
|
-
adds plainly new buyer value. Merge, translate, bullet, or cut instead.
|
|
838
|
-
- the selected winner subject/body must not contain semicolons.
|
|
839
|
-
- the selected winner subject must not be generic category copy like
|
|
840
|
-
`first LinkedIn campaign`, `quick question`, `outbound`, or `intro`. Prefer the
|
|
841
|
-
original gold-standard A + B + C subject shape: a concrete row/company token
|
|
842
|
-
plus a concrete workflow/object plus the offer/result, for example
|
|
843
|
-
`{{company}} + LinkedIn outbound + Claude/Codex` or
|
|
844
|
-
`{{source_post_topic}} + active leads + first message test`. Keep it short,
|
|
845
|
-
buyer-native, and free of abstract category nouns.
|
|
846
|
-
- A + B + C is a quality shape, not permission to stuff tokens into the
|
|
847
|
-
subject. The subject must pique buyer interest and name a buyer-relevant
|
|
848
|
-
problem, workflow, or useful artifact. Do not use sender names, founder names,
|
|
849
|
-
"Austin's review", "founder call", "demo", "quick call", or similar
|
|
850
|
-
sender-centered language in the subject. Do not use a row token in the subject
|
|
851
|
-
unless the filled version is natural, lowercase/common-word where appropriate,
|
|
852
|
-
and more interesting than the non-tokenized version.
|
|
853
|
-
- the selected winner must not contain sender-intro fragments like
|
|
854
|
-
`Usama here`, `derm here`, `board-cert derm here`, `[credential] here`,
|
|
855
|
-
or `[role] at [company] here`.
|
|
856
|
-
- the selected winner must not use generic signal tokens such as
|
|
857
|
-
`{{recentSignal}}`, `{{recent_signal}}`, or `{{recent_signal_quote}}`.
|
|
858
|
-
Row personalization must use concrete enriched-row fields such as
|
|
859
|
-
`{{post_context}}`, `{{comment_summary}}`, `{{profile_summary}}`,
|
|
860
|
-
`{{source_post_topic}}`, `{{headline}}`, or `{{row_proof_note}}`, and it
|
|
861
|
-
must avoid source-citation phrases like `caught my eye`.
|
|
862
|
-
- do not default to founder-to-founder, MD-to-MD, doctor-to-doctor,
|
|
863
|
-
peer-call, compare-notes, or similar identity-call CTA framing unless
|
|
864
|
-
the user explicitly selected that route or the brief makes it the
|
|
865
|
-
approved offer. Even then, the CTA must name the useful return artifact,
|
|
866
|
-
preview, teardown, or working session.
|
|
867
|
-
- if the message is selling or introducing a product, the selected winner
|
|
868
|
-
must make the product plain before asking for time, but the opener must
|
|
869
|
-
not sound like homepage copy. A line shaped like `[Product] is a
|
|
870
|
-
[category] platform -- feature, feature, feature` is blocked when it reads
|
|
871
|
-
like product copy instead of a human note. For command-native products,
|
|
872
|
-
describe the buyer job (`launch a LinkedIn campaign from Claude/Codex`)
|
|
873
|
-
before category nouns like `platform` or `MCP`.
|
|
874
|
-
- pass the Thomas revision filters before writing findings
|
|
875
|
-
- immediately after `message-validation.md` is confirmed and reconciled with
|
|
876
|
-
`lead-filter.md`, write `message-review.md` and render the slim chat version
|
|
877
|
-
inline starting with `Status: message-review` as the first visible line. Do
|
|
878
|
-
not put a markdown heading, preface, or summary before that status line.
|
|
879
|
-
This is a customer checkpoint optimized for fast approve / revise judgment.
|
|
880
|
-
- Chat-vs-file split (this is the rule the user pinned):
|
|
881
|
-
- `message-review.md` (the file) keeps the full detail: `Status:
|
|
882
|
-
message-review`, `Subject:`, `Tokenized template:`, `Sample prospect fill:` + `Token fill basis:`, `Rendered examples:`, `Good token fill:`,
|
|
883
|
-
`Good omit:`, `Bad token fill:`, `Why bad:`, `Fallback if missing:`,
|
|
884
|
-
`Token notes:`, `My take:`, `Suggested adjustment:`, `Question:
|
|
885
|
-
approve-message or revise-messaging?`, `Recommendation:
|
|
886
|
-
approve-message|revise-messaging`. The detailed gold-standard/rule audit
|
|
887
|
-
stays in `message-validation.md`. Both files are unchanged in scope and
|
|
888
|
-
are still required.
|
|
889
|
-
- The chat rendering shows ONLY: `Status: message-review`, `Subject:`,
|
|
890
|
-
`Tokenized template:`, `Sample prospect fill:` (with `Token fill basis:`),
|
|
891
|
-
`My take:`, `Suggested adjustment:`, `Question: approve-message or
|
|
892
|
-
revise-messaging?`, `Recommendation:`. Then `Open artifacts:` links to
|
|
893
|
-
`message-review.md` and `message-validation.md` so the user can read the
|
|
894
|
-
full token guidance, good/omit/bad examples, and fallbacks if they want
|
|
895
|
-
to before deciding.
|
|
896
|
-
- Do NOT inline `Rendered examples:`, `Good token fill:`, `Good omit:`,
|
|
897
|
-
`Bad token fill:`, `Why bad:`, `Fallback if missing:`, or `Token notes:`
|
|
898
|
-
in chat. Those exist in the file and are linked. Keeping them out of
|
|
899
|
-
chat is what makes the gate easy to approve or revise on.
|
|
900
|
-
- Never ask the message approval question until the slim chat review is fully
|
|
901
|
-
visible. A summary like `Message review is ready` or `the draft avoids a
|
|
902
|
-
generic pitch` is not enough. The user must see the actual subject, the
|
|
903
|
-
actual tokenized message template, one filled sample prospect version with
|
|
904
|
-
its token fill basis, your take, and the suggested adjustment before any
|
|
905
|
-
`approve-message` / `revise-messaging` question appears. If you catch
|
|
906
|
-
yourself wanting to offer `show me message` as a choice, stop: render the
|
|
907
|
-
slim chat review first, then ask only `approve-message` or
|
|
908
|
-
`revise-messaging`. Include `Open artifacts:` links to `message-review.md`
|
|
909
|
-
and `message-validation.md` before the approval question.
|
|
910
|
-
- `My take:` and `Suggested adjustment:` are mandatory customer-facing decision
|
|
911
|
-
fields, not optional summary text. They must appear after `Token notes:` and
|
|
912
|
-
before the question in every rendered message review. They must each be
|
|
913
|
-
non-empty, specific to the current message, and useful for one last manual
|
|
914
|
-
feedback pass. Never render a message review that jumps straight from token
|
|
915
|
-
notes to `Question:` or `Recommendation:`.
|
|
916
|
-
- `Tokenized template:` in `message-review.md` must be a tokenized template with supported
|
|
917
|
-
enriched-row tokens such as `{{first_name}}`, `{{company}}`,
|
|
918
|
-
`{{workflow_context}}`, `{{headline}}`, `{{source_post_topic}}`, or
|
|
919
|
-
`{{row_proof_note}}`. It must not be a one-row-only raw sample. It must
|
|
920
|
-
include at least one literal `{{...}}` token. Keep only tokens that future
|
|
921
|
-
enriched rows can actually fill or safely omit. If you cannot produce a
|
|
922
|
-
tokenized template from supported enriched-row fields, stop with
|
|
923
|
-
`Recommendation: revise-messaging`.
|
|
924
|
-
- `Sample prospect fill:` must render the same template for one named sample
|
|
925
|
-
prospect from `lead-sample.json` or the selected row used in
|
|
926
|
-
`message-validation.md`. It must show the complete subject and body the
|
|
927
|
-
prospect would receive, plus a one-line `Token fill basis:` naming which row
|
|
928
|
-
fields filled each token. This is the user-facing proof that the template
|
|
929
|
-
works. Do not ask for approval if the user cannot compare the template to a
|
|
930
|
-
concrete filled message.
|
|
931
|
-
- `{{profile_signal}}` is never a supported customer-facing token. It is an
|
|
932
|
-
internal enrichment label and must not appear in `Subject:`, `Tokenized
|
|
933
|
-
template:`, `Sample prospect fill:`, rendered examples, `Token notes:`,
|
|
934
|
-
`message-validation.md` selected copy, or `approval-packet.md`. If a
|
|
935
|
-
profile-derived signal matters, translate it into a buyer-readable derived
|
|
936
|
-
token with a clear fill rule, such as `{{workflow_context}}` or
|
|
937
|
-
`{{row_proof_note}}`; otherwise omit the line.
|
|
938
|
-
- `Rendered examples:` must include at least one `Good token fill:` rendered
|
|
939
|
-
message where the row has a clean signal and one `Good omit:` rendered message
|
|
940
|
-
where an optional token line is omitted instead of forced. Each example must
|
|
941
|
-
be a complete rendered subject + body, not a bullet list of token names or a
|
|
942
|
-
single bridge-line fragment. Do not use bracketed instructions, deferred row
|
|
943
|
-
notes, or phrases like `approve the selected winner above`.
|
|
944
|
-
- `Bad token fill:` must show at least one realistic wrong fill for the current
|
|
945
|
-
campaign, followed by `Why bad:`. This teaches the user and the live campaign
|
|
946
|
-
runtime what not to do. Examples: overclaiming signal intent (`you are
|
|
947
|
-
evaluating Clay` when the only evidence is a post reaction), using forbidden
|
|
948
|
-
phrasing (`you engaged with content...` when the approved voice is `raised
|
|
949
|
-
your hand in a conversation around...`), forcing a weak optional token,
|
|
950
|
-
lowercasing proper nouns, or making row signal sound like AI mail merge.
|
|
951
|
-
- `Fallback if missing:` is required for every token in the template. Each
|
|
952
|
-
fallback must say exactly what to do when the row lacks clean data: use a
|
|
953
|
-
safe segment-level phrase, omit the sentence/line, or route to
|
|
954
|
-
`revise-messaging` if omission would break the message. It must include a
|
|
955
|
-
concrete example of the fallback behavior, such as `omit the noticed... line`,
|
|
956
|
-
`use "operators"`, or `use "your outbound workflow"`. Never leave a token
|
|
957
|
-
with "N/A", "unknown", empty braces, bracketed instructions, or a generic
|
|
958
|
-
fill that sounds robotic or creepy.
|
|
959
|
-
- rendered examples may only use token values that exist in `lead-sample.json`,
|
|
960
|
-
`lead-review.md`, `lead-filter.md`, or the selected winner's documented
|
|
961
|
-
supported-token plan. Do not invent example values to make the template look
|
|
962
|
-
better. If a token is absent, weak, or not a supported enriched-row field,
|
|
963
|
-
show the `Good omit:` version instead of filling it.
|
|
964
|
-
- token casing must look intentional. Keep proper nouns, recipient names,
|
|
965
|
-
company names, product names, acronyms, and the pronoun `I` capitalized in
|
|
966
|
-
both the template examples and rendered output. Casual lowercase is allowed
|
|
967
|
-
only for static common words when the chosen message voice uses it. Do not
|
|
968
|
-
lowercase token values or invent transform syntax like `{{company_lower}}`,
|
|
969
|
-
`{{first_name | lower}}`, or bracketed casing instructions.
|
|
970
|
-
- Token usage must be intelligent, not decorative. If a tokenized opener reads
|
|
971
|
-
like mail merge (`Reaching out because {{reporting_context}} sits in...`,
|
|
972
|
-
`your {{role_context}} work`, `saw your {{topic}}`, or similar), rewrite the
|
|
973
|
-
base line so the token either sounds like a natural noun phrase or is omitted.
|
|
974
|
-
Prefer row-derived tokens that change the reply reason; otherwise use a clean
|
|
975
|
-
segment-level line and document why the row token is omitted.
|
|
976
|
-
- founder/sender names should not be used as the hook. Mention a founder name
|
|
977
|
-
only when the buyer already has a reason to care about that person or when the
|
|
978
|
-
user explicitly wants named-founder branding. In normal first-touch copy,
|
|
979
|
-
frame the CTA around the useful return artifact (`map one reporting problem`,
|
|
980
|
-
`review one dashboard`, `pressure-test one workflow`) instead of "call with
|
|
981
|
-
Austin" or another founder-name CTA.
|
|
982
|
-
- message-review hard-fail preflight: before rendering `Status: message-review`,
|
|
983
|
-
scan the subject, tokenized template, good-fill example, good-omit example,
|
|
984
|
-
`My take`, `Suggested adjustment`, and recommendation. If any of them contain
|
|
985
|
-
`felt close enough to send`, `close enough to send`,
|
|
986
|
-
`thought this was relevant enough`, `seemed close
|
|
987
|
-
enough`, `{{profile_signal}}`, `profile signal`, `is why I thought this might
|
|
988
|
-
be relevant`, `Austin's`, a sender/founder name in the subject, a one-row raw
|
|
989
|
-
sample under `Tokenized template:`, rendered examples without full copy, a
|
|
990
|
-
mail-merge-sounding tokenized opener, missing or empty `My take:`, missing or
|
|
991
|
-
empty `Suggested adjustment:`, `My take:` that only says `looks good`,
|
|
992
|
-
`strong`, or `approved`, `Suggested adjustment:` that only says `none`, or no
|
|
993
|
-
literal `{{...}}` token in `Tokenized template:`, revise the message before
|
|
994
|
-
showing it. Do not output
|
|
995
|
-
`Recommendation: approve-message` when any hard-fail preflight item is
|
|
996
|
-
present.
|
|
997
|
-
- `My take` must be 1-3 short bullets or sentences, focused on what a founder
|
|
998
|
-
needs to decide: approve as-is, revise once, make it shorter, make it more
|
|
999
|
-
specific, change proof, change CTA, or preserve the current length because
|
|
1000
|
-
cutting would remove reply reason. If a shorter version is recommended,
|
|
1001
|
-
`Suggested adjustment` must say exactly what to remove or compress. Do not
|
|
1002
|
-
include a long `What works`, `Gold-standard/rule check`, or internal checklist
|
|
1003
|
-
in this checkpoint.
|
|
1004
|
-
- `Suggested adjustment` must give the founder a concrete final-edit option even
|
|
1005
|
-
when the recommendation is `approve-message`. Use a useful shape like
|
|
1006
|
-
`Approve as-is, or revise once to [specific edit] if you want [effect]`. It
|
|
1007
|
-
must invite the user to give manual feedback before minting, for example:
|
|
1008
|
-
`Reply revise-messaging with the exact line change, proof change, CTA change,
|
|
1009
|
-
or tone feedback you want before I build the approval packet.` Do not write
|
|
1010
|
-
`none`.
|
|
1011
|
-
- if the message is merely plausible, generic, template-shaped, or weaker than
|
|
1012
|
-
the loaded gold-standard examples, set `Recommendation: revise-messaging`.
|
|
1013
|
-
The next Codex UAT goal is to prove message quality before mint, so no
|
|
1014
|
-
approval packet or campaign mutation should happen just because the mechanics
|
|
1015
|
-
are working.
|
|
1016
|
-
- immediately after rendering `message-review.md`, use the structured question
|
|
1017
|
-
gate with exactly two choices, in this order: `approve-message`,
|
|
1018
|
-
`revise-messaging`. Stop after the question. Do not write `approval-packet.md`,
|
|
1019
|
-
`customer-roleplay.md`, or a commit-gate question until
|
|
1020
|
-
`message-review-decision.md` contains `approve-message`.
|
|
1021
|
-
- if the selected winner or PS uses named customer/logo proof, quantified proof,
|
|
1022
|
-
team credentials, or investor/accelerator proof that is not explicitly
|
|
1023
|
-
supported by `brief.md`, `lead-review.md`, `lead-sample.json`, or loaded
|
|
1024
|
-
reference material, the review must set `Recommendation: revise-messaging`.
|
|
1025
|
-
Do not approve speculative credibility claims.
|
|
1026
|
-
- if the selected winner or PS uses vague proof wrappers like `spoken publicly
|
|
1027
|
-
about`, `publicly talked about`, `trusted by`, `worked with`, `used by`, or
|
|
1028
|
-
bare customer-logo lists without naming a concrete buyer-relevant result,
|
|
1029
|
-
mechanism, or risk reducer, the review must set `Recommendation:
|
|
1030
|
-
revise-messaging`. Supported logos are not automatically body-worthy proof.
|
|
1031
|
-
Translate to a concrete proof line or omit the proof from the message.
|
|
1032
|
-
- if customer/logo proof is useful mainly for credibility, prefer a concise PS
|
|
1033
|
-
over interrupting the body. The PS must name what the customers used the
|
|
1034
|
-
product/service for, not just list logos. If the body already carries buyer
|
|
1035
|
-
pain, mechanism, and CTA clearly, adding concrete social proof in the PS is
|
|
1036
|
-
allowed when it lowers vendor risk.
|
|
1037
|
-
- the selected winner or PS must not use internal labels like `p.s. relevant
|
|
1038
|
-
proof:`, `p.s. useful proof:`, `p.s. proof:`, or `p.s. social proof:`.
|
|
1039
|
-
Rewrite as a natural aside before asking the user to approve.
|
|
1040
|
-
- if the user chooses `revise-messaging`, write `message-review-decision.md`
|
|
1041
|
-
with `revise-messaging`, preserve upstream artifacts, delete/regenerate
|
|
1042
|
-
only `message-validation.md`, `message-review.md`,
|
|
1043
|
-
`message-review-decision.md`, `customer-roleplay.md`, and
|
|
1044
|
-
`approval-packet.md`, then rerun this message step.
|
|
1045
|
-
- after `message-review-decision.md` contains `approve-message`, write
|
|
1046
|
-
`approval-packet.md` in customer-facing order and render the same approval
|
|
1047
|
-
packet inline before the commit gate. The approval packet and six-choice
|
|
1048
|
-
commit question must appear in the same customer-visible turn. Do not emit a
|
|
1049
|
-
standalone commit-gate question. Include a clear `## Message Review` section
|
|
1050
|
-
inside the approval packet before `## Approved Message Template`. It should
|
|
1051
|
-
summarize the customer checkpoint from `message-review.md` and preserve the
|
|
1052
|
-
same literal `My take:`, `Suggested adjustment:`, `Good token fill:`,
|
|
1053
|
-
`Good omit:`, `Bad token fill:`, `Why bad:`, `Fallback if missing:`,
|
|
1054
|
-
`Token fill basis:`, and `Recommendation:` labels.
|
|
1055
|
-
Include `Open artifacts:` links to `approval-packet.md`,
|
|
1056
|
-
`message-review.md`, `lead-review.md`, and `brief.md` before the commit-gate
|
|
1057
|
-
question.
|
|
1058
|
-
- if `message-validation.md` contains an extractable `Selected Winner`, use
|
|
1059
|
-
that exact winner as the `## Approved Message Template` in
|
|
1060
|
-
`approval-packet.md` and in the campaign brief passed to `create_campaign`.
|
|
1061
|
-
Only substitute concrete enriched-prospect-row `{{tokens}}`; do not rewrite
|
|
1062
|
-
the copy inline at approval time. Do not use abstract slot tokens like
|
|
1063
|
-
`{{hookLine}}`, `{{painLine}}`, `{{productLine}}`, `{{closeLine}}`, or
|
|
1064
|
-
`{{psLine}}`, and do not use `{{recent_signal_quote}}`; use fields that exist
|
|
1065
|
-
on the enriched prospect row instead.
|
|
1066
|
-
- the live body under `## Approved Message Template` must be sender-ready copy.
|
|
1067
|
-
Bracketed text in the body is allowed only when it is a fully-specified
|
|
1068
|
-
AI-native token (`[ALL_CAPS_NAME — Intent. DO: ... DON'T: ... FALLBACK:
|
|
1069
|
-
omit the line.]` — see
|
|
1070
|
-
`mcp/sellable/skills/create-campaign/references/ai-native-tokens.md` for the
|
|
1071
|
-
contract). Bracketed instruction placeholders that lack a contract are
|
|
1072
|
-
BLOCKED — including `[ROW BRIDGE ...]`, `[insert ...]`, `[generated ...]`,
|
|
1073
|
-
and any prose that defers per-row composition to a later step without
|
|
1074
|
-
giving the model the rules. Either upgrade the placeholder to a full
|
|
1075
|
-
AI-native token (Intent / DO / DON'T / FALLBACK), put the per-row rules in
|
|
1076
|
-
`## Token Fill Rules` with concrete enriched-row fields, or route to
|
|
1077
|
-
`revise-messaging`.
|
|
1078
|
-
- `approval-packet.md` and the live campaign brief passed to `create_campaign`
|
|
1079
|
-
must include `## Approved Message Template`, `## Token Fill Rules`, and
|
|
1080
|
-
`## Token Fill Examples`. `## Token Fill Examples` must copy the approved
|
|
1081
|
-
examples from `message-review.md` / `message-validation.md`, including
|
|
1082
|
-
`Good token fill:`, `Good omit:`, `Bad token fill:`, `Why bad:`,
|
|
1083
|
-
`Fallback if missing:`, and `Token fill basis:`. Do not call
|
|
1084
|
-
`create_campaign` if these sections are missing from the campaign brief.
|
|
1085
|
-
- `message-validation.md` must not call the winner a canonical template or
|
|
1086
|
-
hide per-row generation in bracketed body text. `## Selected Winner` must
|
|
1087
|
-
be a real sendable message that could be approved as-is.
|
|
1088
|
-
- block awkward bridge phrasing like `felt close enough to send this`,
|
|
1089
|
-
`thought this was relevant enough`, or `seemed close enough`. Replace with a
|
|
1090
|
-
crisp row-backed bridge using a buyer-readable row-derived token, such as
|
|
1091
|
-
`That {{workflow_context}} work is where teams usually need cleaner reporting
|
|
1092
|
-
ownership.` If the signal is weak or absent, omit the bridge line entirely.
|
|
1093
|
-
- if subject, tokenized template, or rendered examples do not meet the bar,
|
|
1094
|
-
`message-review.md` must recommend `revise-messaging`, not `approve-message`.
|
|
772
|
+
Follow that prompt verbatim. It contains the full message-validation workflow, gold-standard rules, proof inventory, candidate generation, finalizer pass, message-review-decision contract, and all token-fill rules (A+B+C subject shape, Good/Bad token-fill examples, fallback-if-missing logic, tokenized templates).
|
|
1095
773
|
|
|
1096
|
-
Do
|
|
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.
|
|
1097
775
|
|
|
1098
|
-
-
|
|
1099
|
-
- call `get_rows`
|
|
1100
|
-
- call `update_cell`
|
|
1101
|
-
- call `update_campaign_brief`
|
|
1102
|
-
- fetch fresh web or LinkedIn research
|
|
1103
|
-
- mutate DB-backed campaign state
|
|
776
|
+
## Tail (MANDATORY TOOL ORDER + Steps 13-16 + Threshold Trips + Hard Rules)
|
|
1104
777
|
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
- `Status`
|
|
1108
|
-
- `Mode`
|
|
1109
|
-
- `Template Used`
|
|
1110
|
-
- `Primary Example`
|
|
1111
|
-
- `Secondary Influence`
|
|
1112
|
-
- `Lead Sample Basis`
|
|
1113
|
-
- `Strongest Reply Reason`
|
|
1114
|
-
- `Pre-Draft Buyer-Role Analysis`
|
|
1115
|
-
- `Campaign Element Pool`
|
|
1116
|
-
- `Gold Standard Strategy Map`
|
|
1117
|
-
- `Current Campaign Translation`
|
|
1118
|
-
- `Element Scoring`
|
|
1119
|
-
- `Proof Inventory`
|
|
1120
|
-
- `Token Fill Rules`
|
|
1121
|
-
- `Token Adherence Table`
|
|
1122
|
-
- `Angle Drafts`
|
|
1123
|
-
- `Kill / Combine Review`
|
|
1124
|
-
- `Finalists`
|
|
1125
|
-
- `Candidate Messages`
|
|
1126
|
-
- `Finalizer Pass`
|
|
1127
|
-
- `Gold-Standard Quality Gate`
|
|
1128
|
-
- `Skeptical Prospect Review`
|
|
1129
|
-
- `Winner Gate`
|
|
1130
|
-
- `Selected Winner`
|
|
1131
|
-
- `Findings`
|
|
1132
|
-
- `Recommendation`
|
|
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.
|
|
1133
779
|
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
- campaign direction
|
|
1137
|
-
- lead source and sample
|
|
1138
|
-
- filters and rubrics
|
|
1139
|
-
- selected message(s)
|
|
1140
|
-
- `## Message Review` with literal `My take:`, `Suggested adjustment:`, and
|
|
1141
|
-
`Recommendation:` (`approve-message` or `revise-messaging`). If the message
|
|
1142
|
-
is only okay, say so plainly so the user can choose `revise-messaging` at the
|
|
1143
|
-
commit gate.
|
|
1144
|
-
- `## Approved Message Template`; when `message-validation.md` has a selected
|
|
1145
|
-
winner, this section must preserve that selected winner rather than a newly
|
|
1146
|
-
drafted approval-template variant
|
|
1147
|
-
- risks / caveats
|
|
1148
|
-
- next action: the six-choice commit gate
|
|
1149
|
-
|
|
1150
|
-
`message-review.md` must contain:
|
|
1151
|
-
|
|
1152
|
-
- `Status: message-review`
|
|
1153
|
-
- `Subject:` using the approved tokenized A + B + C subject shape
|
|
1154
|
-
- `Tokenized template:` followed by the tokenized approved message template
|
|
1155
|
-
- `Sample prospect fill:` with one concrete sample prospect's complete rendered
|
|
1156
|
-
subject + body and a `Token fill basis:` line
|
|
1157
|
-
- `Rendered examples:`
|
|
1158
|
-
- `Good token fill:` with a complete concrete rendered subject + body
|
|
1159
|
-
- `Good omit:` with a complete concrete rendered subject + body where optional
|
|
1160
|
-
row signal is omitted cleanly
|
|
1161
|
-
- `Bad token fill:` with a realistic wrong fill for the current campaign
|
|
1162
|
-
- `Why bad:` explaining the exact overclaim, unsupported token, weak signal,
|
|
1163
|
-
casing mistake, or mail-merge phrasing that makes the bad fill unacceptable
|
|
1164
|
-
- `Fallback if missing:` with one concrete fallback or omit example for every
|
|
1165
|
-
token in the template
|
|
1166
|
-
- `Token notes:` naming which tokens are safe to fill, which optional token line
|
|
1167
|
-
should be omitted when absent, and whether the message uses standard sentence
|
|
1168
|
-
case or casual lowercase static words
|
|
1169
|
-
- `My take:`
|
|
1170
|
-
- `Suggested adjustment:` with a concrete final-edit option and explicit
|
|
1171
|
-
invitation to give manual feedback before approval
|
|
1172
|
-
- `Question: approve-message or revise-messaging?`
|
|
1173
|
-
- `Recommendation:` exactly `approve-message` or `revise-messaging`
|
|
1174
|
-
- The two-choice structured question gate immediately after the rendered review
|
|
1175
|
-
|
|
1176
|
-
`message-review-decision.md` must contain exactly one selected route:
|
|
1177
|
-
`approve-message` or `revise-messaging`. It does not authorize DB mutation.
|
|
1178
|
-
|
|
1179
|
-
If token sourcing is weak, revise token fill rules or route back to
|
|
1180
|
-
`revise-filter` or `revise-message` instead of guessing.
|
|
1181
|
-
|
|
1182
|
-
</step_contracts>
|
|
1183
|
-
|
|
1184
|
-
<resume_rules>
|
|
1185
|
-
|
|
1186
|
-
Phase 84 validation resume:
|
|
1187
|
-
|
|
1188
|
-
- No `brief-v1.md` / `brief.md` present -> run `create-campaign-brief`
|
|
1189
|
-
- `brief-v1.md` present but no `brief.md` -> copy `brief-v1.md` to `brief.md`,
|
|
1190
|
-
then run `find leads`
|
|
1191
|
-
- Only `brief.md` present -> compatibility resume: run `find leads`
|
|
1192
|
-
- `lead-review.md` + `lead-sample.json` present, but no `lead-filter.md` and
|
|
1193
|
-
no `message-validation.md` -> run `filter leads` and `generate message`
|
|
1194
|
-
from the same find-leads basis; do not ask the user between them when
|
|
1195
|
-
lead-review is confirmed
|
|
1196
|
-
- `lead-review.md` + `lead-sample.json` present, but no `lead-filter.md` -> run
|
|
1197
|
-
`filter leads`
|
|
1198
|
-
- `lead-review.md` + `lead-sample.json` present, but no
|
|
1199
|
-
`message-validation.md` -> run `generate message`
|
|
1200
|
-
- `lead-filter.md` + `message-validation.md` present -> reconcile message
|
|
1201
|
-
basis rows against the final filter, then write/render `message-review.md`
|
|
1202
|
-
and ask the two-choice message review gate
|
|
1203
|
-
- `message-validation.md` present but no `message-review-decision.md` -> write
|
|
1204
|
-
`message-review.md`, render it inline starting with `Status:
|
|
1205
|
-
message-review`, then ask `approve-message` vs `revise-messaging`
|
|
1206
|
-
- `message-review-decision.md` contains `revise-messaging` -> preserve
|
|
1207
|
-
upstream artifacts; delete only `message-validation.md`,
|
|
1208
|
-
`message-review.md`, `message-review-decision.md`,
|
|
1209
|
-
`customer-roleplay.md`, and `approval-packet.md`; rerun `generate message`
|
|
1210
|
-
- `message-review-decision.md` contains `approve-message` but no
|
|
1211
|
-
`approval-packet.md` -> write `approval-packet.md`, render that packet inline
|
|
1212
|
-
starting with `Status: approval-packet`, then ask the six-choice commit gate
|
|
1213
|
-
in the same turn
|
|
1214
|
-
- `message-validation.md` + `message-review-decision.md` +
|
|
1215
|
-
`approval-packet.md` present -> validation complete,
|
|
1216
|
-
render `approval-packet.md` inline starting with `Status: approval-packet`,
|
|
1217
|
-
then ask the six-choice commit gate in the same turn
|
|
1218
|
-
- `lead-review.md` without `lead-sample.json`, or vice versa -> stop with a
|
|
1219
|
-
contract violation
|
|
1220
|
-
- `lead-filter.md` without upstream lead artifacts -> stop with a contract violation
|
|
1221
|
-
|
|
1222
|
-
Phase 85 commit-gate + atomic-mint resume:
|
|
1223
|
-
|
|
1224
|
-
- All validation artifacts present and `message-review-decision.md` contains
|
|
1225
|
-
`approve-message` but no `approval-packet.md` yet -> write the approval
|
|
1226
|
-
packet, render it inline, then show the commit gate in the same turn (see
|
|
1227
|
-
`<commit_gate>` below)
|
|
1228
|
-
- `approval-packet.md` present but no `commit-gate-decision.md` -> show the
|
|
1229
|
-
approval packet inline and then the commit gate in the same turn; an existing
|
|
1230
|
-
positive `customer-roleplay.md` is advisory only
|
|
1231
|
-
- `commit-gate-decision.md` is a revision choice or `abort` -> route without
|
|
1232
|
-
DB mutation
|
|
1233
|
-
- `commit-gate-decision.md` contains `approve` and no `CampaignOffer` minted
|
|
1234
|
-
yet -> run atomic mint
|
|
1235
|
-
- `CampaignOffer` exists and `currentStep = "auto-execute-leads"` -> resume
|
|
1236
|
-
via `create_campaign({ campaignId })` to re-fetch state + `watchUrl`,
|
|
1237
|
-
re-surface the watch link using `references/watch-link-handoff.md`, and
|
|
1238
|
-
continue into the autonomous tail at Step 13
|
|
1239
|
-
|
|
1240
|
-
Phase 85 autonomous-tail resume (Plan 85-02):
|
|
1241
|
-
|
|
1242
|
-
- `CampaignOffer.currentStep === "auto-execute-leads"` -> run Step 13
|
|
1243
|
-
(import) then advance to `validate-sample`
|
|
1244
|
-
- `CampaignOffer.currentStep === "validate-sample"` -> run Step 14 sample
|
|
1245
|
-
validation loop. Read the persisted `revisionRound` counter and do NOT
|
|
1246
|
-
reset it to 0 on resume
|
|
1247
|
-
- `CampaignOffer.currentStep === "auto-execute-messaging"` -> run Step 15
|
|
1248
|
-
messaging scale-up
|
|
1249
|
-
- `CampaignOffer.currentStep === "awaiting-user-greenlight"` -> Step 16:
|
|
1250
|
-
re-surface `watchUrl` + `handoff.orientation` and STOP. Do not call
|
|
1251
|
-
`start_campaign` on resume. Wait for UI start or a Claude greenlight
|
|
1252
|
-
turn
|
|
1253
|
-
- `CampaignOffer.currentStep === "running"` -> campaign is live. Surface
|
|
1254
|
-
a "campaign is live" confirmation + `watchUrl`. Do not re-start
|
|
1255
|
-
|
|
1256
|
-
On every tail resume, re-surface the `watchUrl` and the current step's
|
|
1257
|
-
orientation string (v1 create-campaign watch-mode pattern). Load the
|
|
1258
|
-
relevant reference for the active step:
|
|
1259
|
-
|
|
1260
|
-
- Step 14 -> `references/sample-validation-loop.md`
|
|
1261
|
-
- any escalation path -> `references/escalation-ladder.md`
|
|
1262
|
-
- Step 16 -> `references/final-handoff-contract.md`
|
|
1263
|
-
|
|
1264
|
-
</resume_rules>
|
|
1265
|
-
|
|
1266
|
-
<commit_gate>
|
|
1267
|
-
|
|
1268
|
-
After Phase 84 artifacts are complete, show the user a commit gate before any
|
|
1269
|
-
DB mutation. Load `references/approval-gate-framing.md` for the full
|
|
1270
|
-
framing — especially the approval packet ordering and per-choice draft
|
|
1271
|
-
directory effects.
|
|
1272
|
-
|
|
1273
|
-
Preconditions (block the gate when missing):
|
|
1274
|
-
|
|
1275
|
-
- `brief-v1.md` should exist for from-scratch runs
|
|
1276
|
-
- `brief.md` must exist
|
|
1277
|
-
- `lead-review.md` must exist
|
|
1278
|
-
- `lead-sample.json` must exist
|
|
1279
|
-
- `lead-filter.md` must exist
|
|
1280
|
-
- `message-validation.md` must exist
|
|
1281
|
-
- `message-review.md` must exist
|
|
1282
|
-
- `message-review-decision.md` must exist and contain `approve-message`
|
|
1283
|
-
- `approval-packet.md` must be written before asking for a commit decision
|
|
1284
|
-
- `rubric.json` is optional but strongly preferred
|
|
1285
|
-
|
|
1286
|
-
Anchor validation (block `approve` when empty):
|
|
1287
|
-
|
|
1288
|
-
- `brief.md` has a non-empty `## Message Thesis`
|
|
1289
|
-
- `lead-review.md` status is `confirmed`
|
|
1290
|
-
- `lead-sample.json` parses and contains at least one row
|
|
1291
|
-
- `lead-filter.md` has `Decision`, `Who We'll Keep`, `Who We'll Exclude`,
|
|
1292
|
-
`Pass Rate`, `Recommendation`, and `Implementation Details`
|
|
1293
|
-
- `message-validation.md` has `Status: confirmed`, `Mode: DRY MODE (no DB
|
|
1294
|
-
mutation)`, a `Selected Winner`, and a non-empty `Token Adherence Table`
|
|
1295
|
-
- `message-review.md` has `Status: message-review`, `Subject:`,
|
|
1296
|
-
`Tokenized template:` with at least one literal `{{...}}` token,
|
|
1297
|
-
`Sample prospect fill:` with a complete rendered subject + body and
|
|
1298
|
-
`Token fill basis:`, `Rendered examples:`, `Good token fill:` with a complete
|
|
1299
|
-
rendered subject + body, `Good omit:` with a complete rendered subject + body,
|
|
1300
|
-
`Bad token fill:`, `Why bad:`, `Fallback if missing:`, `Token notes:`,
|
|
1301
|
-
`My take:`, `Suggested adjustment:`, `Question:
|
|
1302
|
-
approve-message or revise-messaging?`, and
|
|
1303
|
-
`Recommendation: approve-message`
|
|
1304
|
-
- `message-review-decision.md` is exactly `approve-message`
|
|
1305
|
-
|
|
1306
|
-
Present the approval packet in user-facing order: brief, lead sample, lead
|
|
1307
|
-
filter, message validation. Do not dump raw JSON or opaque validation
|
|
1308
|
-
anchors at the user.
|
|
1309
|
-
|
|
1310
|
-
When rendering the approval packet inline, start it with exactly
|
|
1311
|
-
`Status: approval-packet`. Write the same content to `approval-packet.md`
|
|
1312
|
-
before showing the gate. The same assistant turn must then use the structured
|
|
1313
|
-
question gate with the six commit choices below. In Claude Code, call
|
|
1314
|
-
`AskUserQuestion`; in Codex, call `request_user_input` when available. A
|
|
1315
|
-
commit-gate question without the inline `Status: approval-packet` packet is
|
|
1316
|
-
invalid UX. If a
|
|
1317
|
-
customer roleplay is performed, write `customer-roleplay.md` before the final
|
|
1318
|
-
choice. A roleplay result that says "looks good", "approve", or equivalent is
|
|
1319
|
-
only critique; it does not satisfy the gate. Write `commit-gate-decision.md`
|
|
1320
|
-
only after the user/operator makes one explicit choice.
|
|
1321
|
-
|
|
1322
|
-
Use the structured question gate with exactly these 6 choices, in this order:
|
|
780
|
+
Before any auto-execute or validate-sample step, **load the dedicated tail subskill verbatim**:
|
|
1323
781
|
|
|
1324
|
-
```text
|
|
1325
|
-
approve
|
|
1326
|
-
revise-brief
|
|
1327
|
-
revise-leads
|
|
1328
|
-
revise-rubric
|
|
1329
|
-
revise-messaging
|
|
1330
|
-
abort
|
|
1331
782
|
```
|
|
1332
|
-
|
|
1333
|
-
Do not collapse, reorder, or add choices. Do not auto-select.
|
|
1334
|
-
|
|
1335
|
-
If the host UI caps options per question, preserve the same six routes with a
|
|
1336
|
-
two-question gate: Q1 = `approve`, `revise`, `abort`; Q2 =
|
|
1337
|
-
`revise-brief`, `revise-leads`, `revise-rubric`, `revise-messaging`. Do not
|
|
1338
|
-
tell the customer this is because of schema/tool limits. In the split form,
|
|
1339
|
-
`revise` is only the parent choice for the second question, not a new route.
|
|
1340
|
-
|
|
1341
|
-
</commit_gate>
|
|
1342
|
-
|
|
1343
|
-
<sender_resolution>
|
|
1344
|
-
|
|
1345
|
-
Before atomic mint, the skill needs a concrete sender identity. Resolve
|
|
1346
|
-
it in this order — stop at the first step that yields a confirmed
|
|
1347
|
-
sender, and NEVER invent / guess / brute-force LinkedIn URLs:
|
|
1348
|
-
|
|
1349
|
-
1. **If the fixture/caller provides `senderIdentity.senderId`**, use it
|
|
1350
|
-
directly. Call `get_sender({ senderId })` to confirm it exists in
|
|
1351
|
-
the active workspace, then call `enrich_sender({ senderId })` ONCE
|
|
1352
|
-
to populate `companySnapshot` + `proofDigest`. Do not call
|
|
1353
|
-
`list_senders`, do not guess alternate URLs.
|
|
1354
|
-
2. **Else if the fixture/caller provides a confirmed
|
|
1355
|
-
`senderIdentity.linkedinUrl`**, call `enrich_sender({ linkedinUrl
|
|
1356
|
-
})` ONCE. If it errors, DO NOT retry with a variation of the URL.
|
|
1357
|
-
Fall through to step 3.
|
|
1358
|
-
3. **Else if the caller provides `senderIdentity.name` +
|
|
1359
|
-
`companyDomain` (or workspace context), do ONE `WebSearch` pass**
|
|
1360
|
-
for the authoritative LinkedIn profile URL ("`{name}` `{company}`
|
|
1361
|
-
linkedin"). Use ONLY a URL that appears verbatim in a web search
|
|
1362
|
-
result. Never synthesize a URL from the name. If WebSearch returns
|
|
1363
|
-
no clean match, escalate to step 4.
|
|
1364
|
-
4. **Else escalate.** Print the available sender context and ask the
|
|
1365
|
-
user to resolve (senderId or confirmed linkedinUrl). Do not proceed
|
|
1366
|
-
with atomic mint.
|
|
1367
|
-
|
|
1368
|
-
Hard rules:
|
|
1369
|
-
|
|
1370
|
-
- At most ONE `enrich_sender` retry per run, only when the first
|
|
1371
|
-
response was a transient (5xx) error. Do not retry on 400/404 by
|
|
1372
|
-
changing the URL.
|
|
1373
|
-
- At most ONE `WebSearch` pass per run for sender resolution.
|
|
1374
|
-
- NEVER call `enrich_sender` on a lead's LinkedIn URL. `enrich_sender`
|
|
1375
|
-
is for the single sender entity only. Lead enrichment happens via
|
|
1376
|
-
the Enrich Prospect column in the workflow table.
|
|
1377
|
-
- If the fixture's `mustMatchWorkspaceSender` flag is true and the
|
|
1378
|
-
resolved sender isn't in the active workspace, ABORT the run with an
|
|
1379
|
-
explicit mismatch error.
|
|
1380
|
-
|
|
1381
|
-
</sender_resolution>
|
|
1382
|
-
|
|
1383
|
-
<atomic_mint>
|
|
1384
|
-
|
|
1385
|
-
Only run this sequence when the user picks `approve`. Load
|
|
1386
|
-
`references/watch-link-handoff.md` before starting; it owns the watch-link
|
|
1387
|
-
surfacing protocol and the partial-mint recovery rules.
|
|
1388
|
-
|
|
1389
|
-
Exact sequence:
|
|
1390
|
-
|
|
1391
|
-
1. Read `brief.md` and `rubric.json` from the draft directory.
|
|
1392
|
-
2. Validate anchors one more time. An empty anchor blocks the approve path.
|
|
1393
|
-
The brief must include an `## Approved Message Template` section with
|
|
1394
|
-
the exact live message template and at least one supported `{{token}}`
|
|
1395
|
-
such as `{{first_name}}`. Rendered examples are not enough. The
|
|
1396
|
-
Generate Message column switches to template mode only when
|
|
1397
|
-
`campaignBrief.content` contains `{{...}}`; without this marker it will
|
|
1398
|
-
use full-generation mode and may rewrite the approved message.
|
|
1399
|
-
The live template body must contain only sender-ready copy plus supported
|
|
1400
|
-
tokens. Two token shapes are allowed:
|
|
1401
|
-
- `{{snake_case}}` field-substitution tokens for atomic values that drop
|
|
1402
|
-
directly into a fixed slot (greeting, subject, company name).
|
|
1403
|
-
- `[ALL_CAPS_NAME — Intent. DO: ... DON'T: ... FALLBACK: omit the line.]`
|
|
1404
|
-
AI-native tokens for personalization sentences. The bracket carries
|
|
1405
|
-
the contract inline; the model writes a sentence (or omits the line)
|
|
1406
|
-
per the inline rules. Full spec:
|
|
1407
|
-
`mcp/sellable/skills/create-campaign/references/ai-native-tokens.md`.
|
|
1408
|
-
No other bracketed placeholders are allowed — bracketed prose without an
|
|
1409
|
-
explicit Intent / DO / DON'T / FALLBACK contract (e.g., `[ROW BRIDGE]`,
|
|
1410
|
-
`[insert ...]`, `[generated ...]`) is BLOCKED because it leaves the
|
|
1411
|
-
per-row generation under-specified.
|
|
1412
|
-
Include `## Token Fill Rules` for every token in the template. Field
|
|
1413
|
-
tokens may be row fields such as `{{first_name}}`, `{{company}}`,
|
|
1414
|
-
`{{role}}`, `{{headline}}`, `{{profile_summary}}`, `{{post_context}}`,
|
|
1415
|
-
`{{comment_summary}}`, `{{source_post_topic}}`, or `{{row_proof_note}}`
|
|
1416
|
-
when those exact fields are supported by the row/enrichment data. AI-native
|
|
1417
|
-
tokens are listed in the rules table by name and type with a pointer back
|
|
1418
|
-
to the inline contract (the rules themselves live inside the bracket — do
|
|
1419
|
-
not duplicate them in the table). Do not document static sender identity,
|
|
1420
|
-
product name, proof points, casing style, `{{recent_signal_quote}}`, or
|
|
1421
|
-
abstract slot tokens such as `{{hookLine}}`, `{{painLine}}`,
|
|
1422
|
-
`{{productLine}}`, `{{closeLine}}`, or `{{psLine}}` — those are abstract
|
|
1423
|
-
slots without a contract; convert them to AI-native bracket tokens with
|
|
1424
|
-
real Intent / DO / DON'T / FALLBACK clauses, or cut them.
|
|
1425
|
-
Include `## Token Fill Examples` copied from the approved message review and
|
|
1426
|
-
validation artifacts. It must preserve `Good token fill:`, `Good omit:`,
|
|
1427
|
-
`Bad token fill:`, `Why bad:`, `Fallback if missing:`, and
|
|
1428
|
-
`Token fill basis:` so the minted campaign brief teaches future row
|
|
1429
|
-
generation how to fill tokens, what fills are blocked, and what to do when
|
|
1430
|
-
row data is missing. If the brief does not contain `## Token Fill Rules` and
|
|
1431
|
-
`## Token Fill Examples`, do not call `create_campaign`; route back to
|
|
1432
|
-
message review or approval packet generation.
|
|
1433
|
-
3. Call `bootstrap_create_campaign({ flowVersion: "v2" })`. Respect
|
|
1434
|
-
`safeToProceed`; on blocking errors, surface and stop.
|
|
1435
|
-
4. Call
|
|
1436
|
-
`create_campaign({ campaignBrief, currentStep: "auto-execute-leads" })`.
|
|
1437
|
-
The `campaignBrief` argument must preserve the approved message
|
|
1438
|
-
template section verbatim, including the `{{tokens}}`.
|
|
1439
|
-
If the brief has no supported `{{token}}`, do not call
|
|
1440
|
-
`create_campaign`. Fail the approve path and ask for a corrected
|
|
1441
|
-
template, unless the user explicitly approved freeform AI-generated
|
|
1442
|
-
messages and the tool call sets `messageGenerationMode:
|
|
1443
|
-
"ai-generated"`.
|
|
1444
|
-
Include `senderIds` when a workspace sender is available. If the
|
|
1445
|
-
customer-named sender is not configured in the current UAT workspace,
|
|
1446
|
-
use an available workspace sender for the rehearsal and state that
|
|
1447
|
-
mismatch in the final runtime notes. Do not silently mint without a
|
|
1448
|
-
sender because sequence selection depends on sender tier/settings.
|
|
1449
|
-
Capture `{ campaignId, watchUrl }` from the response.
|
|
1450
|
-
- If the call errors, surface the error and stop. No rollback needed; no
|
|
1451
|
-
campaign exists.
|
|
1452
|
-
- If the response is missing `watchUrl`, treat it as a recoverable
|
|
1453
|
-
failure. Stop before `save_rubrics`. Do not print a watch link.
|
|
1454
|
-
Surface the contract violation explicitly — a missing `watchUrl` is a
|
|
1455
|
-
plumbing bug, not a silent skip.
|
|
1456
|
-
5. Call `save_rubrics({ campaignOfferId: campaignId, rubric })` using the
|
|
1457
|
-
payload derived from `rubric.json` (or from `lead-filter.md` if
|
|
1458
|
-
`rubric.json` is absent).
|
|
1459
|
-
- If this call fails after `create_campaign` succeeded, either rollback
|
|
1460
|
-
the `CampaignOffer` row, or mark an explicit recoverable failure state
|
|
1461
|
-
so the user can retry. Never leave a silent partial campaign. Do NOT
|
|
1462
|
-
print the watch link during this recovery window.
|
|
1463
|
-
6. Flip the brief status in the draft to `committed`. Move the draft
|
|
1464
|
-
directory from
|
|
1465
|
-
`.sellable/create-campaign-v2/drafts/{workspace-slug}/{campaign-slug}/`
|
|
1466
|
-
to
|
|
1467
|
-
`.sellable/create-campaign-v2/drafts/{workspace-slug}/.committed/{campaign-slug}/`.
|
|
1468
|
-
7. Surface the `watchUrl` using the exact block defined in
|
|
1469
|
-
`references/watch-link-handoff.md`. Only print after both
|
|
1470
|
-
`create_campaign` AND `save_rubrics` have succeeded.
|
|
1471
|
-
8. Hand off to the autonomous tail (Plan 85-02). Do not run Step 13 lead
|
|
1472
|
-
import here.
|
|
1473
|
-
|
|
1474
|
-
If the approved lead source exists only as a provider lane in
|
|
1475
|
-
`lead-review.md` (for example a Sales Navigator filter/search URL) and no
|
|
1476
|
-
live `searchId` or `sourceLeadListId` is present, recreate the approved
|
|
1477
|
-
source by calling the matching provider search tool first, then import from
|
|
1478
|
-
the returned id. This is replaying the approved lane, not inventing a new
|
|
1479
|
-
source. Do not skip import/messages on that basis.
|
|
1480
|
-
|
|
1481
|
-
Do NOT call `import_leads` during atomic mint. Full lead sourcing and import
|
|
1482
|
-
happen in Plan 85-02 Step 13 after mint.
|
|
1483
|
-
|
|
1484
|
-
</atomic_mint>
|
|
1485
|
-
|
|
1486
|
-
<revision_routing>
|
|
1487
|
-
|
|
1488
|
-
When the user picks a revision choice, do not mint a campaign. Do not print
|
|
1489
|
-
a watch link. Route back to the relevant upstream step and reset the draft
|
|
1490
|
-
directory per `references/approval-gate-framing.md`:
|
|
1491
|
-
|
|
1492
|
-
- `revise-brief` -> back to Phase 83 or Phase 84 depending on what changed.
|
|
1493
|
-
Preserve `brief.md`; delete all downstream artifacts.
|
|
1494
|
-
- `revise-leads` -> back to Phase 84 `find leads`. Preserve `brief.md`;
|
|
1495
|
-
delete lead and downstream artifacts.
|
|
1496
|
-
- `revise-rubric` -> back to Phase 84 `filter leads`. Preserve `brief.md`,
|
|
1497
|
-
`lead-review.md`, `lead-sample.json`; delete `lead-filter.md`,
|
|
1498
|
-
`message-validation.md`, and `rubric.json`.
|
|
1499
|
-
- `revise-messaging` -> back to Phase 84 `generate message`. Preserve
|
|
1500
|
-
upstream artifacts; delete only `message-validation.md`,
|
|
1501
|
-
`message-review.md`, `message-review-decision.md`,
|
|
1502
|
-
`customer-roleplay.md`, and `approval-packet.md`.
|
|
1503
|
-
- `abort` -> move the draft directory to
|
|
1504
|
-
`.sellable/create-campaign-v2/drafts/{workspace-slug}/.aborted/{campaign-slug}-{timestamp}/`.
|
|
1505
|
-
No DB rows touched.
|
|
1506
|
-
|
|
1507
|
-
Revisions must produce zero DB rows.
|
|
1508
|
-
|
|
1509
|
-
</revision_routing>
|
|
1510
|
-
|
|
1511
|
-
<boundaries>
|
|
1512
|
-
|
|
1513
|
-
Phase 84 (pre-commit-gate) boundary:
|
|
1514
|
-
|
|
1515
|
-
Do not create live campaign state during validation. Specifically, do not:
|
|
1516
|
-
|
|
1517
|
-
- mint a campaign
|
|
1518
|
-
- import leads
|
|
1519
|
-
- persist selected lead lists
|
|
1520
|
-
- attach downstream assets
|
|
1521
|
-
- save remote rubric state
|
|
1522
|
-
- move the user into a live execution step
|
|
1523
|
-
|
|
1524
|
-
Phase 85 (commit-gate and beyond) boundary:
|
|
1525
|
-
|
|
1526
|
-
- The commit gate is the only place in this skill where DB mutation is
|
|
1527
|
-
authorized. Do not call `create_campaign`, `save_rubrics`,
|
|
1528
|
-
`update_campaign`, or any other mutating tool before the user picks
|
|
1529
|
-
`approve`.
|
|
1530
|
-
- Revision choices and `abort` must never call a mutating tool.
|
|
1531
|
-
- `import_leads` is not part of atomic mint. Defer it to Plan 85-02 Step 13.
|
|
1532
|
-
- Never print the `watchUrl` until both `create_campaign` and `save_rubrics`
|
|
1533
|
-
have succeeded.
|
|
1534
|
-
- Never fabricate or reconstruct the `watchUrl` — capture it from the
|
|
1535
|
-
`create_campaign` response.
|
|
1536
|
-
- Once a campaign has been minted, every provider search and every
|
|
1537
|
-
provider-import call from this skill MUST include the minted
|
|
1538
|
-
`campaignOfferId`. This is universal — Prospeo, Sales Nav, Apollo,
|
|
1539
|
-
Signal Discovery, and any future provider. The only place a
|
|
1540
|
-
campaignless search is correct is the pre-mint Phase 84 `find leads`
|
|
1541
|
-
discovery sample. Post-mint expansion, scale-up imports, alternate
|
|
1542
|
-
lane explorations, account-based reruns, signal-monitor lanes, and
|
|
1543
|
-
operator-driven follow-up searches all persist to the campaign and
|
|
1544
|
-
must carry `campaignOfferId`. A search without `campaignOfferId`
|
|
1545
|
-
after mint orphans the search from the campaign UI's Contact
|
|
1546
|
-
Search panel and the campaign's `Searches` history, even when the
|
|
1547
|
-
search produced rows that later get imported. If the search
|
|
1548
|
-
helper enforces a `confirmed: true` gate, pass that with
|
|
1549
|
-
`campaignOfferId` rather than dropping it.
|
|
1550
|
-
|
|
1551
|
-
</boundaries>
|
|
1552
|
-
|
|
1553
|
-
<autonomous_tail>
|
|
1554
|
-
|
|
1555
|
-
The review-batch tail runs AFTER the atomic mint succeeds. It is driven by
|
|
1556
|
-
`core/auto-execute.yaml` and progresses through four steps — Step 13
|
|
1557
|
-
(import review batch), Step 14 (validate-sample), Step 15
|
|
1558
|
-
(auto-execute-messaging for the review batch), Step 16
|
|
1559
|
-
(awaiting-user-greenlight) — before stopping and waiting for a user
|
|
1560
|
-
greenlight.
|
|
1561
|
-
|
|
1562
|
-
Cost boundary: the commit gate approves creating a campaign and producing a
|
|
1563
|
-
real review batch. It does NOT approve spending enrichment/message credits on
|
|
1564
|
-
hundreds of leads. Two separate scales apply post-mint and they MUST be kept
|
|
1565
|
-
distinct in your head:
|
|
1566
|
-
|
|
1567
|
-
- **Import scale** (how many rows land in the campaign table). Defaults to
|
|
1568
|
-
`import.importLimit` (25). The user MAY explicitly raise this at or before
|
|
1569
|
-
mint with phrasing like "import all of them", "pull the full search", "all
|
|
1570
|
-
~2,000 from this lane", or a numeric target. When the user has stated the
|
|
1571
|
-
full-scale import intent, pass that count as `targetLeadCount` so the table
|
|
1572
|
-
carries the full pool. Importing rows is cheap (no enrichment runs yet);
|
|
1573
|
-
storing them up front lets the user kick remaining enrichment manually
|
|
1574
|
-
without re-importing later.
|
|
1575
|
-
- **Cascade scale** (how many rows get enriched/scored/messaged before user
|
|
1576
|
-
approval). ALWAYS stays at the configured review batch (`sample.sampleSize`
|
|
1577
|
-
or `import.importLimit` — default 25) until the user explicitly approves the
|
|
1578
|
-
sample. This is the cost gate. Even when the user said "import all", you
|
|
1579
|
-
queue Enrich Prospect cells for only the first review-batch rows. The
|
|
1580
|
-
remaining rows sit `pending` in the table for the user to kick manually
|
|
1581
|
-
after they've eyeballed the sample.
|
|
1582
|
-
|
|
1583
|
-
A pre-stated full-import intent is NOT a pre-approval to cascade everything.
|
|
1584
|
-
Re-confirm before queueing enrichment beyond the review batch.
|
|
1585
|
-
|
|
1586
|
-
If the source lane contains hundreds of leads and the user has not stated a
|
|
1587
|
-
full-import intent, leave the rest untouched in the source — do not import
|
|
1588
|
-
beyond `importLimit` until the user asks to expand.
|
|
1589
|
-
|
|
1590
|
-
## MANDATORY TOOL ORDER (read this BEFORE any tail step)
|
|
1591
|
-
|
|
1592
|
-
Every tail run MUST call these tools in this exact order. The tail is
|
|
1593
|
-
**review-batch cascade-driven**: you kick off Enrich Prospect only for
|
|
1594
|
-
the imported review batch, and the workflow engine chains DNC Check →
|
|
1595
|
-
ICP Score → Passes Rubric → Generate Message automatically. Your job is
|
|
1596
|
-
to START the bounded cascade, WAIT for it to drain, OBSERVE the results,
|
|
1597
|
-
and stop for review.
|
|
1598
|
-
Do NOT manually run rubric-check, enrich, or message-generation
|
|
1599
|
-
tools — the cascade already does them.
|
|
1600
|
-
|
|
1601
|
-
```text
|
|
1602
|
-
Step 13 — import review batch + kick bounded cascade
|
|
1603
|
-
import_leads(targetLeadCount=importLimit)
|
|
1604
|
-
wait_for_lead_list_ready
|
|
1605
|
-
confirm_lead_list
|
|
1606
|
-
get_rows_minimal # capture enrichCellIds
|
|
1607
|
-
queue_cells(cellIds=<review-batch Enrich Prospect cells only>) <-- starts bounded chain
|
|
1608
|
-
wait_for_campaign_table_ready # review-batch cascade drains here
|
|
1609
|
-
update_campaign(currentStep=validate-sample)
|
|
1610
|
-
|
|
1611
|
-
Step 14 — observe sample
|
|
1612
|
-
get_rows_minimal # read passesRubric + message cell status per row
|
|
1613
|
-
compute projectedPass
|
|
1614
|
-
if OK: update_campaign(currentStep=auto-execute-messaging)
|
|
1615
|
-
else: diagnose brief-vs-list; if brief: update_campaign_brief + re-queue + wait
|
|
1616
|
-
(check_rubric / bulk_enrich_with_prospeo are NOT called here —
|
|
1617
|
-
cascade already did them. wait_for_rubric_results is OK as a
|
|
1618
|
-
read-only observation helper if you need to block on completion.)
|
|
1619
|
-
|
|
1620
|
-
Step 15 — observe messaging
|
|
1621
|
-
get_rows_minimal # confirm passing rows have completed Generate Message cells
|
|
1622
|
-
(rare) queue_cells on any pending Generate Message cells
|
|
1623
|
-
token-contract spot check via get_rows
|
|
1624
|
-
update_campaign(currentStep=awaiting-user-greenlight)
|
|
1625
|
-
(generate_messages is NOT an MCP tool; messages come from the cascade)
|
|
1626
|
-
|
|
1627
|
-
Step 16 — awaiting-user-greenlight
|
|
1628
|
-
attach_recommended_sequence({ campaignId }) # tier-aware: premium/SN -> If Open Profile->INMAIL_OPEN, else INVITE->accepted->DM
|
|
1629
|
-
re-surface watchUrl + review-batch orientation
|
|
1630
|
-
STOP. DO NOT call start_campaign. DO NOT auto-advance currentStep.
|
|
783
|
+
mcp__sellable__get_subskill_prompt({ subskillName: "create-campaign-v2-tail" })
|
|
1631
784
|
```
|
|
1632
785
|
|
|
1633
|
-
|
|
1634
|
-
autonomous tail.** The recommended tool takes only `campaignId` and
|
|
1635
|
-
delegates template selection to the backend's tier-aware selector
|
|
1636
|
-
(`selectTemplateForTiers`). `attach_sequence` requires you to
|
|
1637
|
-
hand-author a `version: 2` template with nodes, branches, and
|
|
1638
|
-
entryNodeId — that's error-prone mid-long-context (galley-off UAT
|
|
1639
|
-
`20260420T195732Z` failed because Claude hit "Invalid node type" and
|
|
1640
|
-
tried to debug via forbidden Bash/Glob calls). Use `attach_sequence`
|
|
1641
|
-
only when the caller explicitly needs a custom non-recommended cadence.
|
|
1642
|
-
|
|
1643
|
-
Hard gates — if you find yourself about to violate any of these, stop
|
|
1644
|
-
first:
|
|
1645
|
-
|
|
1646
|
-
- Do NOT use `update_cell` to write message bodies. The `Generate
|
|
1647
|
-
Message` column's http_request writes those cells via the cascade.
|
|
1648
|
-
`update_cell` remains reachable for legitimate operator overrides
|
|
1649
|
-
AFTER the tail hands off.
|
|
1650
|
-
- Do NOT call `check_rubric`, `enrich_with_prospeo`, or
|
|
1651
|
-
`bulk_enrich_with_prospeo` in the tail. Those are direct-API
|
|
1652
|
-
mutation tools that fetch data to the caller without writing to
|
|
1653
|
-
the workflow table cells. Step 13's `queue_cells` already
|
|
1654
|
-
triggers the column-level enrichment that populates those cells.
|
|
1655
|
-
Running these tools in the tail produces duplicate cost with no
|
|
1656
|
-
cell side effect.
|
|
1657
|
-
- `wait_for_rubric_results` is read-only and OK to use as an
|
|
1658
|
-
observation helper when you need to block on rubric completion —
|
|
1659
|
-
it does not mutate cells. Prefer `get_rows_minimal` +
|
|
1660
|
-
`wait_for_campaign_table_ready` for the primary cascade-drain
|
|
1661
|
-
path, but use `wait_for_rubric_results({ targetCount: cohortSize })`
|
|
1662
|
-
when the table-level wait returns before rubric cells finish.
|
|
1663
|
-
- `start_campaign` is FORBIDDEN in the autonomous tail. It belongs only
|
|
1664
|
-
in the Claude-greenlight path, AFTER the user signals "start". See
|
|
1665
|
-
`references/final-handoff-contract.md`.
|
|
1666
|
-
- You MAY NOT call `start_campaign` without a prior successful
|
|
1667
|
-
`attach_recommended_sequence` (or `attach_sequence` if a custom
|
|
1668
|
-
cadence is explicitly required) in the same run.
|
|
1669
|
-
- You MAY NOT call `start_campaign` while ANY passing row has an
|
|
1670
|
-
empty `Generate Message` cell. If you discover empty message cells in
|
|
1671
|
-
the greenlight turn, ABORT greenlight and return to Step 15 first.
|
|
1672
|
-
- You MAY NOT call `attach_sequence` while ANY passing row has an
|
|
1673
|
-
empty `Generate Message` cell. Before `attach_sequence`, call
|
|
1674
|
-
`get_rows_minimal` and confirm every row where `passesRubric=true`
|
|
1675
|
-
has a `completed` Generate Message cell with non-empty `result`. If
|
|
1676
|
-
any are pending, call `queue_cells` on those generateMessageCellIds
|
|
1677
|
-
and wait. If rows truly won't message, ESCALATE.
|
|
1678
|
-
- You MAY NOT advance past Step 13 without calling `queue_cells` on
|
|
1679
|
-
the review-batch Enrich Prospect cells. Without it, every downstream
|
|
1680
|
-
cell stays `pending` and the campaign ships empty.
|
|
1681
|
-
- You MAY NOT queue enrichment for rows outside the configured review
|
|
1682
|
-
batch before the user approves expansion. Full-list enrichment/message
|
|
1683
|
-
generation is a credit-spend decision and must happen after the user
|
|
1684
|
-
has reviewed the sample.
|
|
1685
|
-
|
|
1686
|
-
Load `core/auto-execute.yaml` exactly once at the start of Step 13. All
|
|
1687
|
-
subsequent steps read the already-parsed config. Do not re-load mid-run.
|
|
1688
|
-
Load each subskill prompt (`create-campaign-v2`, `research-sender`,
|
|
1689
|
-
`generate-messages`) at most once per run. A multi-call chunk sequence counts
|
|
1690
|
-
as one load. If a tool result already told you to load a prompt, load all
|
|
1691
|
-
chunks once and remember; do not restart the same prompt from offset 0 later.
|
|
1692
|
-
|
|
1693
|
-
After every `update_campaign({ currentStep: ... })` in the tail, narrate
|
|
1694
|
-
what changed and orient the user to what the watch link will show next —
|
|
1695
|
-
reuse the v1 `create-campaign` watch-mode pattern verbatim.
|
|
1696
|
-
|
|
1697
|
-
## Step 13: auto-execute-leads
|
|
1698
|
-
|
|
1699
|
-
Entered on `CampaignOffer.currentStep === "auto-execute-leads"` (set by
|
|
1700
|
-
atomic mint).
|
|
1701
|
-
|
|
1702
|
-
> Reminder: every provider search you run from this point forward — the
|
|
1703
|
-
> review-batch source rerun in Step 13, any expansion search after the
|
|
1704
|
-
> review batch, alternate-lane probes, account-based reruns, and operator
|
|
1705
|
-
> follow-ups — MUST include `campaignOfferId`. This applies to
|
|
1706
|
-
> `search_prospeo`, `search_sales_nav`, `search_apollo`,
|
|
1707
|
-
> `search_signals`, and any other provider search tool added later.
|
|
1708
|
-
> Campaignless searches after mint orphan results from the campaign UI.
|
|
1709
|
-
>
|
|
1710
|
-
> **Lead-list vs campaign-table identity rule (do not confuse them):**
|
|
1711
|
-
> after the first `confirm_lead_list`, two workflow tables exist:
|
|
1712
|
-
>
|
|
1713
|
-
> - the **lead-list** table (the original `sourceLeadListId` returned by
|
|
1714
|
-
> `import_leads`), and
|
|
1715
|
-
> - the **campaign workflow table** (`CampaignOffer.workflowTableId`,
|
|
1716
|
-
> also returned as `campaignTableId` from `confirm_lead_list`).
|
|
1717
|
-
>
|
|
1718
|
-
> `confirm_lead_list` overwrites `CampaignOffer.selectedLeadListId` with
|
|
1719
|
-
> the campaign-table id. That means after the first confirm, the
|
|
1720
|
-
> campaign-table id is NOT a valid `sourceLeadListId` for any future
|
|
1721
|
-
> `import_leads` call.
|
|
1722
|
-
>
|
|
1723
|
-
> When you scale up the import, `mode: "add"` requires the ORIGINAL
|
|
1724
|
-
> lead-list id (the one returned by the first `import_leads`), never the
|
|
1725
|
-
> campaign-table id. Passing the campaign-table id silently writes
|
|
1726
|
-
> straight into the campaign workflow table, bypassing the lead list
|
|
1727
|
-
> entirely; the lead list stays at its original size and the
|
|
1728
|
-
> campaign-builder `/leads` UI throws "Lead list not found" because it
|
|
1729
|
-
> expects `selectedLeadListId` to resolve as a lead list.
|
|
1730
|
-
>
|
|
1731
|
-
> Capture the original `leadListId` from the FIRST `import_leads`
|
|
1732
|
-
> response and reuse it for every scale-up `import_leads(mode: "add")`
|
|
1733
|
-
> call on the same campaign. If the original id is unknown, query
|
|
1734
|
-
> for the lead-list table by name or skip the `mode: "add"` path; do
|
|
1735
|
-
> not pass the campaign-table id as `sourceLeadListId`.
|
|
1736
|
-
|
|
1737
|
-
1. Load `core/auto-execute.yaml`. Capture `import.importLimit`,
|
|
1738
|
-
`sample.sampleSize`, `sample.minProjectedPass`,
|
|
1739
|
-
`sample.maxRevisionRounds`, `messaging.tokenContract`,
|
|
1740
|
-
`messaging.critique.enabled`, `handoff.autoStart`,
|
|
1741
|
-
`handoff.orientation`, `retry.sameToolSameError`,
|
|
1742
|
-
`logging.logEveryThresholdTrip`.
|
|
1743
|
-
2. Resolve the approved source before importing. If there is no
|
|
1744
|
-
`lead-source-intake.json`, keep the existing discovered-provider path:
|
|
1745
|
-
`import_leads({ campaignOfferId, targetLeadCount: importLimit })`.
|
|
1746
|
-
If a manifest exists, branch by `sourceType`:
|
|
1747
|
-
- `supplied-linkedin-profiles`: revalidate file metadata and confirmation
|
|
1748
|
-
token, confirm `load_csv_linkedin_leads` only after approval to batch the
|
|
1749
|
-
supplied CSV into a Sellable lead list, persist the returned `leadListId`,
|
|
1750
|
-
then call `confirm_lead_list({ sourceLeadListId: leadListId,
|
|
1751
|
-
targetLeadCount: importLimit })`. Do not call `import_leads` for this
|
|
1752
|
-
branch; the lead list is the source, and `confirm_lead_list` imports the
|
|
1753
|
-
bounded review batch into the campaign table.
|
|
1754
|
-
- `existing-lead-list`: revalidate that the lead list still exists in the
|
|
1755
|
-
same workspace, reuse it as `sourceLeadListId`, then call
|
|
1756
|
-
`confirm_lead_list({ sourceLeadListId, targetLeadCount: importLimit })`.
|
|
1757
|
-
- `supplied-domains`: reuse or confirm `load_csv_domains`, preserving
|
|
1758
|
-
`domainFilterId`; run a campaign-associated Prospeo people search with
|
|
1759
|
-
`campaignOfferId`, provider prompt preflight, and that `domainFilterId`;
|
|
1760
|
-
then import with `targetLeadCount: importLimit`.
|
|
1761
|
-
Never exceed `importLimit`. This is a review batch, not a full
|
|
1762
|
-
campaign-scale import. If a source search/list contains more rows than
|
|
1763
|
-
the cap, import only the first `importLimit` rows and leave the rest for
|
|
1764
|
-
a later expansion step.
|
|
1765
|
-
Persist or recover materialized IDs on resume: `leadListId`,
|
|
1766
|
-
`domainFilterId`, `searchId`, `campaignTableId`, imported row IDs, and queued
|
|
1767
|
-
cell IDs. If the source file changed after preview, or an existing lead list
|
|
1768
|
-
is missing/wrong-workspace, escalate before materialization instead of
|
|
1769
|
-
silently applying stale preview data. Retrying Step 13 must not duplicate
|
|
1770
|
-
lead lists, searches, campaign rows, or queued cells.
|
|
1771
|
-
3. `wait_for_lead_list_ready` and `confirm_lead_list`.
|
|
1772
|
-
4. **Verify the table is set up properly before queuing.** Call
|
|
1773
|
-
`get_rows_minimal({ tableId })` and confirm:
|
|
1774
|
-
- `Enrich Prospect` column exists on the table and every imported
|
|
1775
|
-
row has a corresponding cell with `status === "pending"`.
|
|
1776
|
-
- `DNC Check`, `ICP Score`, `Passes Rubric`, and `Generate Message`
|
|
1777
|
-
columns all exist too (the cascade depends on all of them).
|
|
1778
|
-
- If any are missing, or the cell is in a non-`pending` terminal
|
|
1779
|
-
state from an earlier run, ESCALATE with an explicit "table not
|
|
1780
|
-
configured for autonomous tail" diagnosis. Do NOT attempt to
|
|
1781
|
-
patch the table shape from the tail.
|
|
1782
|
-
5. **Kick off the bounded review-batch cascade.** `queue_cells({ tableId,
|
|
1783
|
-
cellIds: <pending enrichCellIds for the imported review batch only> })`.
|
|
1784
|
-
You queue ONLY the review-batch `Enrich Prospect` cells; the cascade
|
|
1785
|
-
auto-dispatches `DNC Check`, `ICP Score`, `Passes Rubric`, and (for rows
|
|
1786
|
-
where `Passes Rubric === true`) `Generate Message` as each upstream
|
|
1787
|
-
completes. Do not queue cells for any rows beyond `importLimit` before
|
|
1788
|
-
user expansion approval.
|
|
1789
|
-
6. `wait_for_campaign_table_ready` — waits for the review-batch cascade to
|
|
1790
|
-
drain.
|
|
1791
|
-
If it times out with most cells still `pending`, ESCALATE: the
|
|
1792
|
-
engine is blocked.
|
|
1793
|
-
7. If the import returns zero usable leads, ESCALATE per
|
|
1794
|
-
`references/escalation-ladder.md` (hard fail).
|
|
1795
|
-
8. `update_campaign({ campaignId, currentStep: "validate-sample" })`.
|
|
1796
|
-
9. Orient user on the watch link (same pattern as every other step
|
|
1797
|
-
change).
|
|
1798
|
-
|
|
1799
|
-
**Do NOT call `check_rubric`, `wait_for_rubric_results`,
|
|
1800
|
-
`enrich_with_prospeo`, or `bulk_enrich_with_prospeo` in Step 13.**
|
|
1801
|
-
Those are direct-API tools that fetch enrichment/scoring data to the
|
|
1802
|
-
caller; they do not write to the workflow table cells. The cascade
|
|
1803
|
-
triggered by `queue_cells` runs the `Enrich Prospect` column's
|
|
1804
|
-
http_request, which writes the enrichment into the cell, which
|
|
1805
|
-
triggers the next column, and so on. Running the direct-API tools
|
|
1806
|
-
is duplicate work with no cell-population side effect.
|
|
1807
|
-
|
|
1808
|
-
## Step 14: validate-sample (loop)
|
|
1809
|
-
|
|
1810
|
-
Entered on `CampaignOffer.currentStep === "validate-sample"`. Full
|
|
1811
|
-
decision tree lives in `references/sample-validation-loop.md`.
|
|
1812
|
-
|
|
1813
|
-
**Step 14 is pure observation.** The bounded review-batch cascade that Step
|
|
1814
|
-
13 kicked off (via `queue_cells` on Enrich Prospect) has already written ICP
|
|
1815
|
-
scores and Passes Rubric values back into the workflow table. Step 14 reads
|
|
1816
|
-
those review-batch cell results — it does NOT call `check_rubric`,
|
|
1817
|
-
`wait_for_rubric_results`, `bulk_enrich_with_prospeo`, or any other
|
|
1818
|
-
direct enrichment/scoring tool. All of that already happened in the
|
|
1819
|
-
cascade; running them again is wasted work and can overwrite the
|
|
1820
|
-
real cell results with unrelated data.
|
|
1821
|
-
|
|
1822
|
-
Shape:
|
|
1823
|
-
|
|
1824
|
-
```text
|
|
1825
|
-
get_rows_minimal({ tableId })
|
|
1826
|
-
passInSample = count of first sampleSize review-batch rows with passesRubric === true
|
|
1827
|
-
projectedPass = round(passInSample / sampleSize * importLimit)
|
|
1828
|
-
|
|
1829
|
-
if projectedPass >= minProjectedPass:
|
|
1830
|
-
advance to Step 15
|
|
1831
|
-
else:
|
|
1832
|
-
diagnose brief-vs-list per sample-validation-loop.md
|
|
1833
|
-
- brief: autonomous update_campaign_brief, then re-kick cascade
|
|
1834
|
-
(queue_cells on enrichCellIds) and wait for the new
|
|
1835
|
-
results
|
|
1836
|
-
- list: ESCALATE (no auto-revision of leads)
|
|
1837
|
-
- unknown: ESCALATE
|
|
1838
|
-
revisionRound += 1
|
|
1839
|
-
if revisionRound > maxRevisionRounds:
|
|
1840
|
-
ESCALATE
|
|
1841
|
-
```
|
|
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.
|
|
1842
787
|
|
|
1843
|
-
|
|
1844
|
-
Do NOT reset to 0 on resume — a stale resume must still respect the
|
|
1845
|
-
`maxRevisionRounds` cap.
|
|
1846
|
-
|
|
1847
|
-
A "list problem" diagnosis NEVER triggers autonomous
|
|
1848
|
-
`update_campaign_brief`. Escalate instead.
|
|
1849
|
-
|
|
1850
|
-
When the sample passes the projected-pass floor, call
|
|
1851
|
-
`update_campaign({ campaignId, currentStep: "auto-execute-messaging" })`
|
|
1852
|
-
and orient the user that messaging will complete for the review batch only.
|
|
1853
|
-
|
|
1854
|
-
## Step 15: auto-execute-messaging
|
|
1855
|
-
|
|
1856
|
-
Entered on `CampaignOffer.currentStep === "auto-execute-messaging"`.
|
|
1857
|
-
|
|
1858
|
-
**Messages are produced by the `Generate Message` column cascade, not
|
|
1859
|
-
by a separate tool.** Do NOT call a `generate_messages` MCP tool —
|
|
1860
|
-
that tool does not exist; `generate-messages` is a subskill prompt
|
|
1861
|
-
loaded during live messaging drafts. In the autonomous tail, messages
|
|
1862
|
-
flow through the column pipeline: `Enrich Prospect` →
|
|
1863
|
-
`DNC Check` → `ICP Score` → `Passes Rubric` → `Generate Message`.
|
|
1864
|
-
Each column's http_request auto-fires when its upstream dependency
|
|
1865
|
-
completes AND passes gate (e.g. `Passes Rubric === true` before
|
|
1866
|
-
`Generate Message`). Your job in Step 15 is to WAIT for the review-batch
|
|
1867
|
-
cascade to reach `Generate Message` for rows that passed ICP, and verify
|
|
1868
|
-
the output — not to generate messages manually.
|
|
1869
|
-
|
|
1870
|
-
**What Step 15 does:**
|
|
1871
|
-
|
|
1872
|
-
1. `get_rows_minimal` to read `passesRubric` + `generateMessageCellId`
|
|
1873
|
-
- the Generate Message cell's `status` and `result` per row.
|
|
1874
|
-
Before queueing or waiting on Generate Message cells, confirm the
|
|
1875
|
-
minted campaign brief still contains `{{...}}` in `## Approved Message
|
|
1876
|
-
Template`. If it does not, fail before the cascade runs. Do not repair
|
|
1877
|
-
after mint; the template must be present during mint so Step 15 never
|
|
1878
|
-
starts in freeform generation mode.
|
|
1879
|
-
2. For every row where `passesRubric === true`:
|
|
1880
|
-
- The Generate Message cell should be `running` or `completed`
|
|
1881
|
-
already (cascade auto-fired it). If it is still `pending`, queue
|
|
1882
|
-
it explicitly: `queue_cells({ tableId, cellIds:
|
|
1883
|
-
<generateMessageCellIds> })`.
|
|
1884
|
-
3. `wait_for_campaign_table_ready` (or poll) until every passing
|
|
1885
|
-
row's Generate Message cell is `completed`.
|
|
1886
|
-
4. Read the results back via `get_rows` (full) and sanity-check a
|
|
1887
|
-
sample against the Phase 84 token contract: no unresolved
|
|
1888
|
-
`{{tokens}}`, no invented proof, one sentence per line, etc.
|
|
1889
|
-
5. If the sample fails the token contract, diagnose brief-vs-list
|
|
1890
|
-
(same revision loop as Step 14) and escalate if over
|
|
1891
|
-
`maxRevisionRounds`.
|
|
1892
|
-
6. On success, `update_campaign({ campaignId, currentStep:
|
|
1893
|
-
"awaiting-user-greenlight" })`.
|
|
1894
|
-
|
|
1895
|
-
**Do NOT hand-write message bodies via `update_cell`.** `update_cell`
|
|
1896
|
-
is reachable for legitimate operator overrides AFTER the tail hands
|
|
1897
|
-
off, but during autonomous tail the cascade owns the writes. If you
|
|
1898
|
-
find yourself drafting a message body and about to push it through
|
|
1899
|
-
`update_cell`, you have lost state — stop, read the cells, and let
|
|
1900
|
-
the pipeline finish.
|
|
1901
|
-
|
|
1902
|
-
**What `attach_sequence` does (Step 16, not here):** binds the
|
|
1903
|
-
DM/InMail cadence template to the workflow table. Completely different
|
|
1904
|
-
job from messaging. Does NOT generate or write message text. A
|
|
1905
|
-
sequence with no `Generate Message` outputs would try to send empty
|
|
1906
|
-
strings, which is why Step 16 requires Step 15 to be complete.
|
|
1907
|
-
|
|
1908
|
-
1. Observe the review-batch messages on the same sample that passed
|
|
1909
|
-
validation.
|
|
1910
|
-
2. If `messaging.critique.enabled` is true (Plan 85-03), run the
|
|
1911
|
-
bounded critique pass on the sample output per
|
|
1912
|
-
`references/parallel-critique-protocol.md`. The pass runs on at
|
|
1913
|
-
most `messaging.critique.sampleSize` rows, sends each row through
|
|
1914
|
-
three parallel critics (targeting / copy / voice) that return
|
|
1915
|
-
structured JSON, and merges the voices in a synthesis step that
|
|
1916
|
-
enforces the Phase 84 token contract verbatim and re-runs the
|
|
1917
|
-
finalizer pass. Any rewrite that invents proof or uses an
|
|
1918
|
-
unsupported token falls back to the plain generated message. A
|
|
1919
|
-
trip of `messaging.critique.budgetUsdCap` HALTS critique for the
|
|
1920
|
-
remaining sample and continues the plain tail. Plan 85-02 reads
|
|
1921
|
-
the flag but never flips it; critique stays off until the flag is
|
|
1922
|
-
deliberately enabled.
|
|
1923
|
-
3. If `messaging.critique.opus.enabled` is also true, route the
|
|
1924
|
-
highest-value subset through the Opus / craft-message path per
|
|
1925
|
-
`references/thomas-variant-selection.md`. Opus is capped by
|
|
1926
|
-
`messaging.critique.opus.maxMessagesPerPass` and
|
|
1927
|
-
`messaging.critique.opus.budgetUsdCap`; tripping either cap keeps
|
|
1928
|
-
the remaining rows on the non-Opus synthesizer. Opus is bound by
|
|
1929
|
-
the same token contract as every other rewrite path.
|
|
1930
|
-
4. Check `messaging.tokenContract`. When `strict`, reject any sample
|
|
1931
|
-
message that contains unresolved or unsupported tokens (including
|
|
1932
|
-
any critique rewrite that tried to introduce one).
|
|
1933
|
-
5. If the sample passes the token contract (and critique when enabled),
|
|
1934
|
-
stop at the review-batch handoff. Do NOT scale to the full source list
|
|
1935
|
-
before explicit user expansion approval.
|
|
1936
|
-
6. If the sample fails the token contract or critique, diagnose +
|
|
1937
|
-
loop the same way Step 14 does (brief-vs-list), subject to the same
|
|
1938
|
-
`maxRevisionRounds` cap.
|
|
1939
|
-
7. On success, `update_campaign({ campaignId, currentStep:
|
|
1940
|
-
"awaiting-user-greenlight" })` and orient the user.
|
|
1941
|
-
|
|
1942
|
-
Critique failure modes NEVER escalate. A critic timeout, a total
|
|
1943
|
-
timeout, a budget trip, a fake-proof rejection, or an unsupported-
|
|
1944
|
-
token rejection all fall back to the plain generated message for that
|
|
1945
|
-
row. Step 15 does not stall on critique.
|
|
1946
|
-
|
|
1947
|
-
## Step 16: awaiting-user-greenlight
|
|
1948
|
-
|
|
1949
|
-
Entered on `CampaignOffer.currentStep === "awaiting-user-greenlight"`.
|
|
1950
|
-
Full contract lives in `references/final-handoff-contract.md`.
|
|
1951
|
-
|
|
1952
|
-
Shape:
|
|
1953
|
-
|
|
1954
|
-
1. `attach_sequence` (bind the committed sequence to the campaign).
|
|
1955
|
-
2. Re-surface the `watchUrl` using `references/watch-link-handoff.md`.
|
|
1956
|
-
3. Surface the `handoff.orientation` string from `auto-execute.yaml`
|
|
1957
|
-
alongside the link.
|
|
1958
|
-
4. STOP. Do NOT call `start_campaign`. Do NOT auto-advance
|
|
1959
|
-
`currentStep`. The autonomous tail ends here.
|
|
1960
|
-
5. Make the credit boundary explicit in the handoff: only the first review
|
|
1961
|
-
batch has been enriched/messaged; expanding to more leads requires a
|
|
1962
|
-
separate user instruction.
|
|
1963
|
-
|
|
1964
|
-
Two equally valid greenlight channels take Step 16 into a running
|
|
1965
|
-
campaign:
|
|
1966
|
-
|
|
1967
|
-
- **UI path** — user clicks "Approve all" and "Start Campaign" in the
|
|
1968
|
-
watch-link UI. The skill stays idle. On the next resume, it observes
|
|
1969
|
-
the running state and shifts to a "campaign is live" confirmation.
|
|
1970
|
-
- **Claude greenlight path** — user replies with an affirmative ("yeah
|
|
1971
|
-
start" / "looks good, start" / "ship it"). The skill then performs,
|
|
1972
|
-
in order: (a) bulk-approve queued messages via the EXISTING endpoint
|
|
1973
|
-
`POST /api/v3/workflow-tables/cells/approve-batch` (do NOT invent a
|
|
1974
|
-
new tool/endpoint), (b) `start_campaign({ campaignId })`, (c)
|
|
1975
|
-
`update_campaign({ campaignId, currentStep: "running" })`, (d)
|
|
1976
|
-
re-surface `watchUrl` plus a "campaign is live" confirmation.
|
|
1977
|
-
|
|
1978
|
-
A Claude greenlight on an already-running campaign is a no-op
|
|
1979
|
-
confirmation, NOT a duplicate start. Detect via `get_campaign` before
|
|
1980
|
-
calling any mutating tool; if the campaign is already running, skip
|
|
1981
|
-
approve-batch / start_campaign / update_campaign and just confirm.
|
|
1982
|
-
|
|
1983
|
-
Workspace/sender mismatch at greenlight time aborts the start with the
|
|
1984
|
-
same invariant Phase 84 enforces.
|
|
1985
|
-
|
|
1986
|
-
## Threshold Trips and Logging
|
|
1987
|
-
|
|
1988
|
-
When `logging.logEveryThresholdTrip === true` (default), log every
|
|
1989
|
-
threshold trip: `projectedPass` computation, retry cap, revision cap,
|
|
1990
|
-
hard-fail. These logs drive `auto-execute.yaml` tuning after 3-5 real
|
|
1991
|
-
runs.
|
|
1992
|
-
|
|
1993
|
-
## Tail Hard Rules
|
|
1994
|
-
|
|
1995
|
-
- Review-batch sourcing/import happens in Step 13, not during atomic mint.
|
|
1996
|
-
- Full-list expansion is not part of the default tail. It requires user
|
|
1997
|
-
approval after the review batch.
|
|
1998
|
-
Import must never exceed `importLimit`.
|
|
1999
|
-
- The tail NEVER calls `start_campaign` on its own.
|
|
2000
|
-
- The tail NEVER auto-revises leads. Brief revision is autonomous; lead
|
|
2001
|
-
revision is always operator-gated.
|
|
2002
|
-
- `revisionRound` persists across resume — no silent reset.
|
|
2003
|
-
- The bulk-approve action in the Claude greenlight path uses the
|
|
2004
|
-
existing `POST /api/v3/workflow-tables/cells/approve-batch` endpoint
|
|
2005
|
-
— do NOT create a new tool or endpoint for this.
|
|
2006
|
-
- Threshold trips are logged for calibration.
|
|
2007
|
-
|
|
2008
|
-
</autonomous_tail>
|
|
2009
|
-
|
|
2010
|
-
<references_index>
|
|
2011
|
-
|
|
2012
|
-
| File | Load when |
|
|
2013
|
-
| ------------------------------------------ | ---------------------------------------------------------------------- |
|
|
2014
|
-
| `references/approval-gate-framing.md` | Phase 85 commit gate, before showing the approval packet |
|
|
2015
|
-
| `references/watch-link-handoff.md` | Atomic mint + every tail resume, before surfacing watchUrl |
|
|
2016
|
-
| `references/sample-validation-loop.md` | Step 14, before enriching + scoring the sample |
|
|
2017
|
-
| `references/escalation-ladder.md` | Any tail step that needs to decide retry / revise / escalate |
|
|
2018
|
-
| `references/final-handoff-contract.md` | Step 16, and every Claude greenlight turn |
|
|
2019
|
-
| `references/parallel-critique-protocol.md` | Step 15 when `messaging.critique.enabled` is true |
|
|
2020
|
-
| `references/thomas-variant-selection.md` | Step 15 when `messaging.critique.opus.enabled` is true |
|
|
2021
|
-
| `references/thomas-revision-filters.md` | Any critique rewrite, before persisting |
|
|
2022
|
-
| `core/auto-execute.yaml` | Start of Step 13, load once; all tail steps read parsed values |
|
|
2023
|
-
| `core/auto-execute.README.md` | When tuning `auto-execute.yaml` knobs or reviewing threshold-trip logs |
|
|
2024
|
-
|
|
2025
|
-
</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.
|