Skip to content
This repository was archived by the owner on Oct 12, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
77f6922
fix(ops): stop operator copy from doubling the article before offerName
nish3451 Aug 15, 2026
3c27c10
Merge branch 'main' into fix/operator-copy-offername-article-lane1-20…
nish3451 Aug 15, 2026
2bd4a4f
Merge branch 'main' into fix/operator-copy-offername-article-lane1-20…
nish3451 Aug 15, 2026
fc83247
Merge branch 'main' into fix/operator-copy-offername-article-lane1-20…
nish3451 Aug 15, 2026
afde8d0
Merge branch 'main' into fix/operator-copy-offername-article-lane1-20…
nish3451 Aug 15, 2026
a83768c
Merge branch 'main' into fix/operator-copy-offername-article-lane1-20…
nish3451 Aug 15, 2026
7cb32b6
Merge branch 'main' into fix/operator-copy-offername-article-lane1-20…
nish3451 Aug 15, 2026
02f9041
Merge branch 'main' into fix/operator-copy-offername-article-lane1-20…
nish3451 Aug 15, 2026
3136264
Merge branch 'main' into fix/operator-copy-offername-article-lane1-20…
nish3451 Aug 15, 2026
ebeb783
Merge branch 'main' into fix/operator-copy-offername-article-lane1-20…
nish3451 Aug 17, 2026
8f480fd
Merge branch 'main' into fix/operator-copy-offername-article-lane1-20…
nish3451 Aug 17, 2026
2bbb10a
Merge branch 'main' into fix/operator-copy-offername-article-lane1-20…
nish3451 Aug 17, 2026
ba5ec02
Merge branch 'main' into fix/operator-copy-offername-article-lane1-20…
nish3451 Aug 17, 2026
8c3e8c1
Merge branch 'main' into fix/operator-copy-offername-article-lane1-20…
nish3451 Aug 17, 2026
bf4d01c
Merge branch 'main' into fix/operator-copy-offername-article-lane1-20…
nish3451 Aug 17, 2026
2eaff86
Merge branch 'main' into fix/operator-copy-offername-article-lane1-20…
nish3451 Aug 17, 2026
bed2e68
Merge branch 'main' into fix/operator-copy-offername-article-lane1-20…
nish3451 Aug 18, 2026
0eb5086
Merge branch 'main' into fix/operator-copy-offername-article-lane1-20…
nish3451 Aug 19, 2026
a5dfa0b
Merge branch 'main' into fix/operator-copy-offername-article-lane1-20…
nish3451 Aug 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Lane 1 report — operator copy doubled-article offerName fix

## Item

Operator copy interpolates offerName "The Website Correction" after the word "the", producing doubled-article text ("the The Website Correction").

## Investigation

- The canonical offer name is `"The Website Correction"` (`scripts/lib/client-scaffold.mjs`, `growth-brain/ops/agency-config.json`) — it already contains the article.
- Five operator copy sites on current `origin/main` still interpolate it directly after an article:
- `scripts/draft-client-kickoff.mjs` — "Thanks for approving the ${config.offerName}..."
- `scripts/draft-loom-recording-script.mjs` — "...positions the ${config.offerName} as the obvious next step."
- `scripts/draft-recording-sharpness-brief.mjs` — "CTA: Offer the ${config.offerName} at..."
- `scripts/draft-sales-call-prep.mjs` — "The ${FOUNDER_PILOT.offerName} is exactly a..."
- `scripts/draft-prospect-message.mjs` — "...the exact one-page ${config.offerName} scope."
- Prior unattached attempts existed (`fix/operator-copy-offername-article` PR #39, `fix/operator-copy-offername-article-lane1-20260814` PR #144, `fix/operator-copy-offer-article`) — all unmerged. PR #144 is still open and contains the identical fix plus guard; this lane re-landed it fresh from current origin/main so the PR is mergeable today.

## Fix (branch `fix/operator-copy-offername-article-lane1-20260815`)

Dropped the redundant article at the five sites above (kept grammar natural: "the exact one-page scope for ${config.offerName}").

Added a regression guard in `scripts/check-product-truth.mjs` (runs in `npm run ci`) that fails any active operator copy source interpolating `offerName` directly after "the" across the five fixed scripts plus `scripts/lib/canonical-service-copy.mjs` and `scripts/lib/client-scaffold.mjs`.

## Verification (all passed)

- `node --check` on all six changed files — OK
- `node scripts/check-product-truth.mjs` — exit 0 (new guard active)
- Mutation test: reintroducing "the ${config.offerName}" in kickoff makes the truth gate exit 1 with the guard message; restore returns to exit 0
- Full `npm test` — exit 0 (retention-automation check reports its expected environment-only "fail" in this isolated worktree; unrelated)
- `git diff --check` clean
18 changes: 18 additions & 0 deletions scripts/check-product-truth.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,24 @@ for (const path of activeGeneratorSources) {
}
}

// The canonical offer name already starts with "The"; copy that interpolates it
// directly after an article produces doubled-article text ("the The Website
// Correction") in operator-facing output.
for (const path of [
"scripts/draft-sales-call-prep.mjs",
"scripts/draft-recording-sharpness-brief.mjs",
"scripts/draft-loom-recording-script.mjs",
"scripts/draft-client-kickoff.mjs",
"scripts/draft-prospect-message.mjs",
"scripts/lib/canonical-service-copy.mjs",
"scripts/lib/client-scaffold.mjs"
]) {
const content = existsSync(path) ? readFileSync(path, "utf8") : ""
if (/\b(?:the|The)\s*\$\{(?:config|FOUNDER_PILOT|day0)\.offerName\}/.test(content)) {
failures.push(`${path}: offer name is interpolated directly after the article 'the'`)
}
}

for (const [path, requiredPatterns] of [
[
"scripts/draft-sales-call-prep.mjs",
Expand Down
2 changes: 1 addition & 1 deletion scripts/draft-client-kickoff.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const approvalOwner = day0.approvalOwner;
const highestLeveragePage = field(sprintPlan, "Highest-leverage page:", "choose the highest-leverage page");
const sprint = `**${day0.offerName}**\n\nIncluded: ${config.includedDeliverables.join(", ")}.`;
const timingAndPrice = `- Day 0: ${day0.day0StartedAt}\n- Working-day deadline: ${day0.deadlineAt}\n- Clock rule: ${config.dayZeroRule}\n- Founder pilot ${pilotPosition} of ${FOUNDER_PILOT.capacity}: **${founderPilotPrice}**.`;
const approvalOpening = `Thanks for approving the ${config.offerName}. Day 0 is recorded and the service is paid.`;
const approvalOpening = `Thanks for approving ${config.offerName}. Day 0 is recorded and the service is paid.`;

const kickoff = `# ${name} Kickoff Message

Expand Down
2 changes: 1 addition & 1 deletion scripts/draft-loom-recording-script.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ const script = `# ${metadata.name} Loom Recording Script

## Goal

Record a 2-3 minute cold audit that names one clear fault and positions the ${config.offerName} as the obvious next step.
Record a 2-3 minute cold audit that names one clear fault and positions ${config.offerName} as the obvious next step.

## Before Recording

Expand Down
2 changes: 1 addition & 1 deletion scripts/draft-prospect-message.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ if (stage === "sent" || stage.startsWith("followup-")) {
body = appendEmailComplianceFooter(`Worth sending the exact ${mainPage} structure I would use for this?`);
} else if (pending?.step === "day-5") {
subject = `Re: Quick audit for ${name}`;
body = appendEmailComplianceFooter(`Quick bump. The main thing I would fix first is ${leakFragment}. If useful, I can send the exact one-page ${config.offerName} scope.`);
body = appendEmailComplianceFooter(`Quick bump. The main thing I would fix first is ${leakFragment}. If useful, I can send the exact one-page scope for ${config.offerName}.`);
} else if (pending?.step === "day-10") {
subject = `Re: Quick audit for ${name}`;
body = appendEmailComplianceFooter(`Closing the loop here. I still think ${mainPage} is leaving clarity on the table. Happy to revisit if this becomes a priority.`);
Expand Down
2 changes: 1 addition & 1 deletion scripts/draft-recording-sharpness-brief.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ Use this before recording. The job is not to sound clever. The job is to make on
4. Credibility: ${proof[0] || "use only visible proof from the live page"}
5. Solution: ${firstFix}
6. Proof: ${proof[1] || contrast}
7. CTA: Offer the ${config.offerName} at ${price} without promising revenue, rankings, ROAS, or lift.
7. CTA: Offer ${config.offerName} at ${price} without promising revenue, rankings, ROAS, or lift.

## So-What Chain

Expand Down
2 changes: 1 addition & 1 deletion scripts/draft-sales-call-prep.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ ${payback}

## Close

"The ${FOUNDER_PILOT.offerName} is exactly a ${founderPilotPrice()} for one highest-leverage page. Human reviewers gate the claims and client-facing work. You receive the fault map, rewrite or redesign, one implementation pass or dev-ready handoff, search-trust basics, before/after proof, Loom, measurement plan, one client revision, and 14-day implementation tracking. Day 0 starts after payment, context, and named approval and implementation owners."
"${FOUNDER_PILOT.offerName} is exactly a ${founderPilotPrice()} for one highest-leverage page. Human reviewers gate the claims and client-facing work. You receive the fault map, rewrite or redesign, one implementation pass or dev-ready handoff, search-trust basics, before/after proof, Loom, measurement plan, one client revision, and 14-day implementation tracking. Day 0 starts after payment, context, and named approval and implementation owners."

## Guardrails

Expand Down