Repository navigation
fix(sales): gate every runtime record/send surface against retired offer copy #112
Changes from 10 commits
4391319
48ebb42
1b3721a
9eba605
cde0dbb
41dbe9f
37c1004
5bb3eb5
6e516c9
a024fe1
b16a33b
baef3fd
8ddf24a
0d056ad
c23b05f
f5900cd
4189e43
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -76,13 +76,23 @@ try { | |
| } | ||
|
|
||
| const { roots, strict } = config; | ||
| const outboundFiles = new Set(["next-message.md", "send-package.md", "recording-notes.md", "outreach.md", "reply-package.md", "call-booked-package.md", "close-package.md"]); | ||
| // Every active record/send surface, so a stale runtime sheet can never sell a | ||
| // retired broad-agency offer without a CI/operator failure. Mirrors the file | ||
| // set the proof-run brief tells the operator to record and send from. | ||
| const outboundFiles = new Set([ | ||
| "next-message.md", "send-package.md", "recording-notes.md", "outreach.md", | ||
| "reply-package.md", "call-booked-package.md", "close-package.md", | ||
| "loom-outline.md", "recording-script.md", "recording-sharpness-brief.md", | ||
| "audit-brief.md", "buyer-room.md", "loom-package.md" | ||
|
Comment on lines
+82
to
+86
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When an existing Useful? React with 👍 / 👎.
Comment on lines
+82
to
+86
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Useful? React with 👍 / 👎. |
||
| ]); | ||
| const optOutPattern = /\b(reply no|do not follow up|unsubscribe|opt out|ignore me)\b/i; | ||
| const placeholderPattern = /\[(?:add Loom link|link|specific fault|Name)\]|Here is the Loom:\s*$/i; | ||
| const salesPlaceholderPattern = /\badd (?:meeting link|payment link|call time)\b/i; | ||
| // Retired broad-agency offers must never project into an outbound send package. | ||
| // Mirrors the canonical retired-ask guard in export-recording-rehearsal-check.mjs. | ||
| const retiredOfferPattern = /7[-\s]day (?:site|website) revenue (?:leak|fault) (?:fix )?sprint|tangible revenue (?:leak|fault) sprint|30[-\s]day action plan|growth desk|three pages|\$\s?500\b/i; | ||
| // Retired broad-agency offers must never project into an outbound send package | ||
| // or any runtime sheet that feeds it (loom outlines, recording scripts, | ||
| // sharpness briefs, audit briefs, buyer rooms). Mirrors the canonical | ||
| // retired-ask guard in export-recording-rehearsal-check.mjs. | ||
| const retiredOfferPattern = /7[-\s]day (?:site|website) revenue (?:leak|fault) (?:fix )?sprint|7[-\s]day sprint|tangible revenue (?:leak|fault) sprint|30[-\s]day action plan|growth desk|three pages|founder sprint|\$\s?500\b/i; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a legacy runtime surface says “seven-day sprint” instead of “7-day sprint,” this new gate passes it unchanged because the added generic branch only accepts the numeral. The repository already classifies that spelling, plus phrases such as “fix in seven days,” as stale delivery promises in Useful? React with 👍 / 👎. |
||
|
|
||
| function walk(path) { | ||
| if (!existsSync(path)) return []; | ||
|
|
@@ -127,7 +137,7 @@ for (const file of files) { | |
| findings.push({ file, rule: "sales package still has meeting/payment placeholders" }); | ||
| } | ||
|
|
||
| if (["send-package.md", "recording-notes.md"].includes(filename) && retiredOfferPattern.test(content)) { | ||
| if (retiredOfferPattern.test(content)) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a prospect's own pricing or page structure is copied into Useful? React with 👍 / 👎. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a prospect has been marked Useful? React with 👍 / 👎. |
||
| findings.push({ file, rule: "outbound package sells a retired offer" }); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -371,7 +371,25 @@ writeFileSync(resolvedOutputPath, markdown); | |
|
|
||
| const loomLinksDir = resolvedLoomLinksPath.split("/").slice(0, -1).join("/"); | ||
| if (loomLinksDir) mkdirSync(loomLinksDir, { recursive: true }); | ||
| const existingLoomRows = read(resolvedLoomLinksPath).split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#")); | ||
| // Retired broad-agency promise language must never persist in the proof-run | ||
| // sheet: it is the single source the brief tells the operator to record and | ||
| // send from. Existing rows keep their operator-captured fault/impact/fix | ||
| // notes and Loom URL, but any ask column that still names a retired offer is | ||
| // refreshed to the canonical ask. Mirrors the retired-offer pattern in | ||
| // check-outbound-send-readiness.mjs and export-recording-rehearsal-check.mjs. | ||
| const retiredPromisePattern = /7[-\s]day (?:site|website) revenue (?:leak|fault) (?:fix )?sprint|7[-\s]day sprint|tangible revenue (?:leak|fault) sprint|30[-\s]day action plan|growth desk|three pages|founder sprint|\$\s?500\b/i; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Add token boundaries to the new generic alternatives.
Use
📍 Affects 4 files
🤖 Prompt for AI Agents |
||
| let refreshedAsks = 0; | ||
| const existingLoomRows = read(resolvedLoomLinksPath) | ||
| .split("\n") | ||
| .map((line) => line.trim()) | ||
| .filter((line) => line && !line.startsWith("#")) | ||
| .map((line) => { | ||
| if (!line.includes("|")) return line; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When an existing Loom sheet row uses the supported comma-delimited format, this early return skips retired-offer replacement even though Useful? React with 👍 / 👎. |
||
| const parts = line.split("|"); | ||
| if (parts.length < 7 || !retiredPromisePattern.test(parts[6])) return line; | ||
| refreshedAsks += 1; | ||
| return [...parts.slice(0, 6), cleanSheetNote(canonicalProspectAsk(), parts[6])].join("|"); | ||
| }); | ||
| // Sheet row paths may be service-root relative or absolute; normalize both | ||
| // sides before deduplicating so regeneration never appends a duplicate row. | ||
| const sheetRowKey = (line) => { | ||
|
|
@@ -394,5 +412,6 @@ console.log(JSON.stringify({ | |
| blockers: parity.blockers.map((blocker) => blocker.area), | ||
| selectedProspects: recordingBatch.map((prospect) => prospect.path), | ||
| directionGate, | ||
| refreshedAsks, | ||
| recommendedChannel: channelGuidance.recommendedChannel | ||
| }, null, 2)); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -82,10 +82,12 @@ function bannedClaims(content) { | |
|
|
||
| const RETIRED_ASK_PATTERNS = [ | ||
| /7[-\s]day (?:site|website) revenue (?:leak|fault) (?:fix )?sprint/i, | ||
| /7[-\s]day sprint/i, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Useful? React with 👍 / 👎. |
||
| /tangible revenue (?:leak|fault) sprint/i, | ||
| /30[-\s]day action plan/i, | ||
| /growth desk/i, | ||
| /three pages/i, | ||
| /founder sprint/i, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the outline contains a canonical ask but Useful? React with 👍 / 👎. |
||
| /\$\s?500\b/i | ||
| ]; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -49,19 +49,38 @@ try { | |
| assert(output(result).findings.some(finding => finding.rule === "send package still has placeholders")) | ||
|
|
||
| // The send package must never sell a retired broad-agency offer, and | ||
| // recording-notes.md is embedded verbatim into the send package. | ||
| for (const [file, content] of [ | ||
| // recording-notes.md is embedded verbatim into the send package. Every | ||
| // active record/send surface must fail the gate when it still sells a | ||
| // retired offer (7-day sprint / 30-day action plan / founder sprint / | ||
| // $500 price), not just send-package.md and recording-notes.md. | ||
| const retiredFixtures = [ | ||
| ["send-package.md", `${packageHeader}7-Day Site Revenue Fault Sprint with a 30-day action plan. ${optOut}`], | ||
| ["recording-notes.md", `# Recording Notes\n\n## Quality Notes\n\n- Clean ask: If useful, I can run a Tangible Revenue Fault Sprint with a 30-day action plan for $500.\n`] | ||
| ]) { | ||
| ["recording-notes.md", `# Recording Notes\n\n## Quality Notes\n\n- Clean ask: If useful, I can run a Tangible Revenue Fault Sprint with a 30-day action plan for $500.\n`], | ||
| ["loom-outline.md", `# Loom Outline\n\n7. Sprint pitch: 7-Day Site Revenue Fault Sprint with a 30-day action plan\n`], | ||
| ["recording-script.md", `# Recording Script\n\n## Talk Track\n\nIf useful, I can run a 7-day sprint where I map this fault for $1,000 founder sprint.\n`], | ||
| ["next-message.md", `${packageHeader}If useful, I can run a 7-day sprint where I map this leak and give you a 30-day action plan. ${optOut}`], | ||
| ["recording-sharpness-brief.md", `# Sharpness Brief\n\n## Positioning Angle\n\nOffer the The Website Correction at $1,000 founder sprint.\n`], | ||
| ["outreach.md", `# Outreach\n\n## First Message\n\nIf useful, I can run a 7-day sprint where I map this fault and give you a 30-day action plan. ${optOut}`] | ||
| ] | ||
| for (const [file, content] of retiredFixtures) { | ||
| writeFileSync(join(prospect, file), content) | ||
| result = run() | ||
| assert.notEqual(result.status, 0, `expected retired-offer failure for ${file}`) | ||
| assert(output(result).findings.some(finding => finding.rule === "outbound package sells a retired offer")) | ||
|
Comment on lines
+65
to
69
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
After the first iteration writes a retired Useful? React with 👍 / 👎. |
||
| } | ||
|
|
||
| writeFileSync(join(prospect, "send-package.md"), `${packageHeader}The Website Correction, one highest-leverage page, 14-day implementation tracking. ${optOut}`) | ||
| writeFileSync(join(prospect, "recording-notes.md"), `# Recording Notes\n\n## Quality Notes\n\n- Clean ask: If useful, I can run a human-reviewed The Website Correction on this one highest-leverage page with a measurement plan.\n`) | ||
| // Canonical copy on every surface must pass the retired-offer rule. | ||
| for (const [file, content] of [ | ||
| ["send-package.md", `${packageHeader}The Website Correction, one highest-leverage page, 14-day implementation tracking. ${optOut}`], | ||
| ["recording-notes.md", `# Recording Notes\n\n## Quality Notes\n\n- Clean ask: If useful, I can run a human-reviewed The Website Correction on this one highest-leverage page with a measurement plan.\n`], | ||
| ["loom-outline.md", `# Loom Outline\n\n7. Ask: If useful, I can run a human-reviewed The Website Correction on this one highest-leverage page with 14-day implementation tracking.\n`], | ||
| ["recording-script.md", `# Recording Script\n\n## Talk Track\n\nIf useful, I can run a human-reviewed The Website Correction on this one highest-leverage page.\n`], | ||
| ["next-message.md", `${packageHeader}If useful, I can run a human-reviewed The Website Correction on this one highest-leverage page. ${optOut}`], | ||
| ["recording-sharpness-brief.md", `# Sharpness Brief\n\n## Positioning Angle\n\nThe Website Correction at the $1,000 founder pilot.\n`], | ||
| ["outreach.md", `# Outreach\n\n## First Message\n\nIf useful, I can run a human-reviewed The Website Correction. ${optOut}`] | ||
| ]) { | ||
| writeFileSync(join(prospect, file), content) | ||
| } | ||
|
Comment on lines
+56
to
+83
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Isolate and complete each outbound fixture case. After the first iteration, Reset all outbound fixtures to canonical content before each retired case. Add a retired case for every filename in 🤖 Prompt for AI Agents |
||
| result = run() | ||
| assert.equal(result.status, 0, "canonical send package must not trigger the retired-offer rule") | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a legacy
sales-call-prep.mdstill contains the retired sprint,send:checkpasses because this active sales-call close packet is omitted fromoutboundFiles, even thoughdraft-sales-call-prep.mjsemits it for operators andcheck-outbound-claim-safety.mjsclassifies it as outbound. Include this filename so the new “every active record/send surface” gate also blocks stale offer copy from being used on calls.Useful? React with 👍 / 👎.