import { describe, test } from 'node:test'; import assert from 'node:assert/strict'; import { updatePostTracking, pruneContentHistory, updateFollowerCount, recordFirstHourPlan, recordOutreachContact } from '../state-updater.mjs'; const SAMPLE_STATE = `--- last_post_date: "2026-04-05" first_post_date: "2026-01-15" last_post_topic: "AI strategy" posts_this_week: 2 weekly_goal: 3 current_streak: 5 longest_streak: 12 current_week: "2026-W14" last_import_date: "2026-04-01" last_import_week: "2026-W14" follower_count: 850 follower_target: 10000 target_date: "2026-12-31" monthly_growth: [] projected_10k_date: "" growth_rate_needed: 0 --- # LinkedIn Session State ## Recent Posts - [2026-04-05] "AI governance is not about..." (1450) - AI strategy - [2026-04-03] "Three things I learned..." (1200) - leadership - [2026-03-28] "Why most teams fail at..." (1350) - team building ## Session Notes ## Planned Content ## Milestone Log `; // Mirrors config/state-file.template.md: every template-initialized state file // ships the ## First-Hour Plans and ## Outreach Pipeline sections pre-created, // each already containing its two format comments. Real // /linkedin:firsthour and /linkedin:outreach invocations therefore hit the // section-APPEND branch — not the section-CREATE branch that SAMPLE_STATE (which // omits the sections) exercises. These fixtures cover the production code path. const TEMPLATE_STATE = `--- last_post_date: "2026-04-05" first_post_date: "2026-01-15" last_post_topic: "AI strategy" posts_this_week: 2 weekly_goal: 3 current_streak: 5 longest_streak: 12 follower_count: 850 follower_target: 10000 target_date: "2026-12-31" --- # LinkedIn Session State ## Recent Posts - [2026-04-05] "AI governance is not about..." (1450) - AI strategy ## First-Hour Plans ## Outreach Pipeline `; // A hand-corrupted / pre-migration state missing EVERY date anchor // (last_post_date, last_firsthour_date, last_outreach_date). No production // template ships like this — every template carries last_post_date — but it is // the exact else-fall-through the S8 date-scalar gate protects: the scalar must // NOT be inserted and must NOT be reported in `changes`, while the section append // still runs and the function still returns non-null (log-accuracy branch). const NO_ANCHOR_STATE = `--- first_post_date: "2026-01-15" last_post_topic: "AI strategy" posts_this_week: 2 weekly_goal: 3 follower_count: 850 follower_target: 10000 target_date: "2026-12-31" --- # LinkedIn Session State ## Recent Posts - [2026-04-05] "AI governance is not about..." (1450) - AI strategy `; describe('updatePostTracking', () => { test('sets last_post_date to provided date', () => { const result = updatePostTracking(SAMPLE_STATE, { postDate: '2026-04-07', postTopic: 'AI governance', hookText: 'The real problem with AI governance...', charCount: 1500, format: 'post' }); assert.notEqual(result, null); assert.match(result.content, /^last_post_date: "2026-04-07"$/m); }); test('sets last_post_topic', () => { const result = updatePostTracking(SAMPLE_STATE, { postDate: '2026-04-07', postTopic: 'AI governance', hookText: 'The real problem...', charCount: 1500, format: 'post' }); assert.match(result.content, /^last_post_topic: "AI governance"$/m); }); test('increments posts_this_week when same week', () => { // 2026-04-06 is a Monday, ISO W15. current_week is W14. // Use a date that stays in W14: 2026-04-05 is Sunday W14 — but last_post_date is already 04-05. // Let's use a state with current_week matching the post date week. const w15State = SAMPLE_STATE.replace('current_week: "2026-W14"', 'current_week: "2026-W15"'); const result = updatePostTracking(w15State, { postDate: '2026-04-07', // Tuesday W15 postTopic: 'test', hookText: 'Hook', charCount: 1000, format: 'post' }); assert.notEqual(result, null); assert.match(result.content, /^posts_this_week: 3$/m); // was 2, incremented }); test('increments streak when gap <= 2 days', () => { const result = updatePostTracking(SAMPLE_STATE, { postDate: '2026-04-06', // 1 day after last_post_date 2026-04-05 postTopic: 'test', hookText: 'Hook', charCount: 1000, format: 'post' }); assert.notEqual(result, null); assert.match(result.content, /^current_streak: 6$/m); // was 5, incremented }); test('resets streak to 1 when gap > 2 days', () => { const result = updatePostTracking(SAMPLE_STATE, { postDate: '2026-04-09', // 4 days after 2026-04-05 postTopic: 'test', hookText: 'Hook', charCount: 1000, format: 'post' }); assert.notEqual(result, null); assert.match(result.content, /^current_streak: 1$/m); }); test('sets first_post_date when null', () => { const nullFirstPost = SAMPLE_STATE.replace( 'first_post_date: "2026-01-15"', 'first_post_date: null' ); const result = updatePostTracking(nullFirstPost, { postDate: '2026-04-07', postTopic: 'test', hookText: 'Hook', charCount: 1000, format: 'post' }); assert.notEqual(result, null); assert.match(result.content, /^first_post_date: "2026-04-07"$/m); }); test('does NOT overwrite existing first_post_date', () => { const result = updatePostTracking(SAMPLE_STATE, { postDate: '2026-04-07', postTopic: 'test', hookText: 'Hook', charCount: 1000, format: 'post' }); assert.notEqual(result, null); assert.match(result.content, /^first_post_date: "2026-01-15"$/m); }); test('triggers week rollover when ISO week changes', () => { // 2026-04-14 is W16, current_week is W14 const result = updatePostTracking(SAMPLE_STATE, { postDate: '2026-04-14', postTopic: 'test', hookText: 'Hook', charCount: 1000, format: 'post' }); assert.notEqual(result, null); // After rollover, posts_this_week resets to 0 then increments to 1 assert.match(result.content, /^posts_this_week: 1$/m); assert.match(result.content, /^current_week: "2026-W16"$/m); }); test('appends to Recent Posts section', () => { const result = updatePostTracking(SAMPLE_STATE, { postDate: '2026-04-06', postTopic: 'AI governance', hookText: 'The real problem with AI governance today...', charCount: 1500, format: 'post' }); assert.notEqual(result, null); assert.ok(result.content.includes('- [2026-04-06] "The real problem with AI governance today..." (1500) - AI governance')); // Existing entries should still be there assert.ok(result.content.includes('- [2026-04-05] "AI governance is not about..."')); }); test('appends a $-bearing topic/hook verbatim — special replacement patterns are not interpreted', () => { // Regression (S12): the append used a replacement *string*, so `$1`/`$&`/`$\``/ // `$'`/`$$` in user content were interpreted — a "$100 budget" topic re-injected // the captured `## Recent Posts` heading and dropped characters. The replacement // function inserts the content verbatim. const result = updatePostTracking(SAMPLE_STATE, { postDate: '2026-04-09', postTopic: '$100 budget — $& and $1 rule', hookText: 'We cut $1 of $5', charCount: 1200, format: 'post' }); assert.notEqual(result, null); assert.ok(result.content.includes('$100 budget — $& and $1 rule'), 'topic with $-tokens must be inserted verbatim'); assert.ok(result.content.includes('"We cut $1 of $5"'), 'hook with $-tokens must be inserted verbatim'); // S13: the SCALAR path (replaceField → state-updater.mjs:58) must ALSO insert // verbatim. The S12 test only checked the Recent Posts section entry (a function // append), so a `$&`-corrupted `last_post_topic` scalar shipped green: a // replacement *string* expands `$&` to the whole matched line, rewriting the // field to `last_post_topic: "$100 budget — last_post_topic: "AI strategy" …"`. // This assertion fails until replaceField is a replacement function. assert.match(result.content, /^last_post_topic: "\$100 budget — \$& and \$1 rule"$/m, 'last_post_topic scalar must carry the $-bearing topic verbatim (no $&/$1 expansion)'); const headings = result.content.match(/^## Recent Posts$/gm) || []; assert.equal(headings.length, 1, 'heading must not be re-injected by a $1/$& expansion'); }); test('updates longest_streak when current exceeds it', () => { const highStreak = SAMPLE_STATE.replace('current_streak: 5', 'current_streak: 12'); const result = updatePostTracking(highStreak, { postDate: '2026-04-06', // 1 day gap, streak increments to 13 postTopic: 'test', hookText: 'Hook', charCount: 1000, format: 'post' }); assert.notEqual(result, null); assert.match(result.content, /^current_streak: 13$/m); assert.match(result.content, /^longest_streak: 13$/m); }); test('does not update longest_streak when current is lower', () => { const result = updatePostTracking(SAMPLE_STATE, { postDate: '2026-04-06', postTopic: 'test', hookText: 'Hook', charCount: 1000, format: 'post' }); assert.notEqual(result, null); assert.match(result.content, /^current_streak: 6$/m); assert.match(result.content, /^longest_streak: 12$/m); // unchanged }); test('returns changes array describing what changed', () => { const result = updatePostTracking(SAMPLE_STATE, { postDate: '2026-04-06', postTopic: 'AI governance', hookText: 'Hook', charCount: 1500, format: 'post' }); assert.notEqual(result, null); assert.ok(Array.isArray(result.changes)); assert.ok(result.changes.length > 0); }); }); describe('pruneContentHistory', () => { test('removes entries older than 90 days', () => { const today = new Date(); const old = new Date(today); old.setDate(old.getDate() - 100); const oldDate = old.toISOString().slice(0, 10); const recent = new Date(today); recent.setDate(recent.getDate() - 10); const recentDate = recent.toISOString().slice(0, 10); const stateWithOld = SAMPLE_STATE.replace( '## Recent Posts\n\n', `## Recent Posts\n\n- [${oldDate}] "Old post..." (1000) - old topic\n- [${recentDate}] "Recent post..." (1200) - recent topic\n` ); const result = pruneContentHistory(stateWithOld, 90); assert.notEqual(result, null); assert.equal(result.pruned, 1); assert.ok(!result.content.includes(oldDate)); assert.ok(result.content.includes(recentDate)); }); test('preserves entries within 90 days', () => { const today = new Date(); const recent = new Date(today); recent.setDate(recent.getDate() - 30); const recentDate = recent.toISOString().slice(0, 10); const stateWithRecent = SAMPLE_STATE.replace( '## Recent Posts\n\n', `## Recent Posts\n\n- [${recentDate}] "Recent post..." (1200) - topic\n` ); const result = pruneContentHistory(stateWithRecent, 90); assert.equal(result, null); // nothing to prune }); test('returns null when no entries exist', () => { const emptyRecent = SAMPLE_STATE.replace( /## Recent Posts\n\n[\s\S]*?(?=## Session Notes)/, '## Recent Posts\n\n' ); const result = pruneContentHistory(emptyRecent, 90); assert.equal(result, null); }); test('handles custom maxAgeDays', () => { const today = new Date(); const old = new Date(today); old.setDate(old.getDate() - 40); const oldDate = old.toISOString().slice(0, 10); const stateWithOld = SAMPLE_STATE.replace( '## Recent Posts\n\n', `## Recent Posts\n\n- [${oldDate}] "Somewhat old..." (1000) - topic\n` ); const result = pruneContentHistory(stateWithOld, 30); assert.notEqual(result, null); assert.equal(result.pruned, 1); }); test('preserves a $-bearing kept entry verbatim while pruning — special replacement patterns are not interpreted', () => { // Regression (S12): the rewrite used `.replace(section, newSectionString)`; a // string search has no $1 group, but `$&` still expands to the whole matched // section and `$$` collapses to `$`, so a kept post like "$$ and $& budget" // corrupted state. The replacement function inserts newSection verbatim. const today = new Date(); const old = new Date(today); old.setDate(old.getDate() - 100); const oldDate = old.toISOString().slice(0, 10); const recent = new Date(today); recent.setDate(recent.getDate() - 10); const recentDate = recent.toISOString().slice(0, 10); // Build the fixture with a replacement FUNCTION too — a string replacement here // would itself interpret the `$$`/`$&` we are trying to plant (the very bug under // test), corrupting the fixture before pruneContentHistory ever sees it. const stateWithMix = SAMPLE_STATE.replace( '## Recent Posts\n\n', () => `## Recent Posts\n\n- [${oldDate}] "Old..." (1000) - drop me\n- [${recentDate}] "Saved $&100" (1200) - $$ and $& budget\n` ); const result = pruneContentHistory(stateWithMix, 90); assert.notEqual(result, null); assert.equal(result.pruned, 1); assert.ok(!result.content.includes(oldDate), 'old entry pruned'); assert.ok(result.content.includes(`- [${recentDate}] "Saved $&100" (1200) - $$ and $& budget`), 'kept $-bearing entry survives verbatim'); const headings = result.content.match(/^## Recent Posts$/gm) || []; assert.equal(headings.length, 1, 'section must not be duplicated by a $& expansion'); }); }); describe('updateFollowerCount', () => { test('updates follower_count', () => { const result = updateFollowerCount(SAMPLE_STATE, { count: 920, month: '2026-04' }); assert.notEqual(result, null); assert.match(result.content, /^follower_count: 920$/m); }); test('recalculates growth_rate_needed', () => { const result = updateFollowerCount(SAMPLE_STATE, { count: 920, month: '2026-04' }); assert.notEqual(result, null); const match = result.content.match(/^growth_rate_needed: (\d+)$/m); assert.ok(match, 'growth_rate_needed should be present'); const rate = parseInt(match[1], 10); assert.ok(rate > 0, 'growth_rate_needed should be positive'); }); test('appends to Milestone Log section', () => { const result = updateFollowerCount(SAMPLE_STATE, { count: 920, month: '2026-04' }); assert.notEqual(result, null); assert.ok(result.content.includes('[2026-04] 920 (+70)')); }); }); describe('recordFirstHourPlan', () => { const PLAN_OPTS = { planDate: '2026-05-30 09:00', postTopic: 'AI governance', targets: ['Whale: @bigvoice (within 30 min)', 'Inner circle: @peer'], draftComments: ['Your point about model-cards resonates — we found...'], plan: ['09:00 — Post goes live', '09:10 — Add value self-comment', '09:30 — Reply to every comment'] }; test('creates a non-R-initial First-Hour Plans section when absent', () => { // SAMPLE_STATE has no ## First-Hour Plans section → must be created (additive) assert.ok(!SAMPLE_STATE.includes('## First-Hour Plans')); const result = recordFirstHourPlan(SAMPLE_STATE, PLAN_OPTS); assert.notEqual(result, null); assert.ok(result.content.includes('## First-Hour Plans')); }); test('records topic, targets, draft comments and timeline in the entry', () => { const result = recordFirstHourPlan(SAMPLE_STATE, PLAN_OPTS); assert.notEqual(result, null); assert.ok(result.content.includes('[2026-05-30 09:00]')); assert.ok(result.content.includes('AI governance')); assert.ok(result.content.includes('Whale: @bigvoice (within 30 min)')); assert.ok(result.content.includes('Your point about model-cards resonates')); assert.ok(result.content.includes('09:10 — Add value self-comment')); }); test('is additive — inserts last_firsthour_date when the field is absent', () => { assert.ok(!/^last_firsthour_date:/m.test(SAMPLE_STATE)); const result = recordFirstHourPlan(SAMPLE_STATE, PLAN_OPTS); assert.notEqual(result, null); assert.match(result.content, /^last_firsthour_date: "2026-05-30 09:00"$/m); }); test('updates an existing last_firsthour_date without duplicating it', () => { const withField = SAMPLE_STATE.replace( 'last_post_date: "2026-04-05"', 'last_post_date: "2026-04-05"\nlast_firsthour_date: null' ); const result = recordFirstHourPlan(withField, { ...PLAN_OPTS, planDate: '2026-05-30 11:00' }); assert.notEqual(result, null); const hits = result.content.match(/^last_firsthour_date:/gm) || []; assert.equal(hits.length, 1, 'field must not be duplicated'); assert.match(result.content, /^last_firsthour_date: "2026-05-30 11:00"$/m); }); test('leaves existing fields and sections untouched (round-trip)', () => { const result = recordFirstHourPlan(SAMPLE_STATE, PLAN_OPTS); assert.notEqual(result, null); assert.match(result.content, /^last_post_date: "2026-04-05"$/m); assert.match(result.content, /^follower_count: 850$/m); assert.ok(result.content.includes('- [2026-04-05] "AI governance is not about..."')); }); test('gracefully defaults empty targets/comments/plan', () => { const result = recordFirstHourPlan(SAMPLE_STATE, { planDate: '2026-05-30 09:00', postTopic: 'minimal' }); assert.notEqual(result, null); assert.ok(result.content.includes('## First-Hour Plans')); assert.ok(result.content.includes('minimal')); }); test('returns a changes array describing what changed', () => { const result = recordFirstHourPlan(SAMPLE_STATE, PLAN_OPTS); assert.notEqual(result, null); assert.ok(Array.isArray(result.changes)); assert.ok(result.changes.length > 0); }); test('appends into the pre-existing First-Hour Plans section (production path) without duplicating it or dropping its format comments', () => { // TEMPLATE_STATE ships the section + its two comments → the // section-APPEND branch must fire (the path every real invocation takes), // NOT section creation. assert.ok(TEMPLATE_STATE.includes('## First-Hour Plans')); const result = recordFirstHourPlan(TEMPLATE_STATE, PLAN_OPTS); assert.notEqual(result, null); // Heading not duplicated (the create branch did not also run) const headings = result.content.match(/^## First-Hour Plans$/gm) || []; assert.equal(headings.length, 1, 'section must not be duplicated'); // Both template format comments survive the append assert.ok(result.content.includes('')); assert.ok(result.content.includes('')); // Entry lands INSIDE the section (between its heading and the next section) const section = result.content.slice( result.content.indexOf('## First-Hour Plans'), result.content.indexOf('## Outreach Pipeline') ); assert.ok(section.includes('[2026-05-30 09:00]'), 'entry must land inside the First-Hour Plans section'); assert.ok(section.includes('AI governance')); }); test('records $-bearing topic/targets/comments verbatim — special replacement patterns are not interpreted', () => { // Regression (S12): the section append used a replacement *string*; `$&`/`$1`/`$$` // in the topic/targets/comments were interpreted. The function inserts verbatim. const result = recordFirstHourPlan(TEMPLATE_STATE, { planDate: '2026-05-30 09:00', postTopic: '$100 launch & $& spend', targets: ['Whale: @big$voice ($1 ask)'], draftComments: ['Loved the $$ point and the $& follow-up'], plan: ['09:00 — live'] }); assert.notEqual(result, null); assert.ok(result.content.includes('$100 launch & $& spend'), 'topic with $-tokens verbatim'); assert.ok(result.content.includes('Whale: @big$voice ($1 ask)'), 'target with $-tokens verbatim'); assert.ok(result.content.includes('Loved the $$ point and the $& follow-up'), 'comment with $-tokens verbatim'); const headings = result.content.match(/^## First-Hour Plans$/gm) || []; assert.equal(headings.length, 1, 'section must not be re-injected by a $1/$& expansion'); }); test('no-anchor fall-through: neither last_firsthour_date nor last_post_date — scalar not written, not reported; section still appended', () => { // Exercises the else-fall-through of the date-scalar gate // (state-updater.mjs:225-231): with NO anchor field, the scalar cannot be // inserted and must NOT be reported in `changes` (the exact branch the S8 // fix protects) — yet the section append always runs, so result is non-null. assert.ok(!/^last_firsthour_date:/m.test(NO_ANCHOR_STATE)); assert.ok(!/^last_post_date:/m.test(NO_ANCHOR_STATE)); const result = recordFirstHourPlan(NO_ANCHOR_STATE, PLAN_OPTS); assert.notEqual(result, null); // Scalar NOT inserted (no anchor to insert after) assert.ok(!/^last_firsthour_date:/m.test(result.content), 'scalar must not be inserted without an anchor'); // ...and NOT reported as changed assert.ok(!result.changes.some((c) => /last_firsthour_date/.test(c)), 'no date-scalar change entry without an anchor'); // The section entry IS still written assert.ok(result.changes.some((c) => /^First-Hour Plans \+=/.test(c)), 'section entry must still be recorded'); assert.ok(result.content.includes('## First-Hour Plans')); assert.ok(result.content.includes('[2026-05-30 09:00]')); }); }); describe('recordOutreachContact', () => { const CONTACT_OPTS = { contactDate: '2026-05-30 14:00', track: 'collab', partner: '@bigvoice', stage: 'pitched', nextAction: 'follow up if no reply', dueDate: '2026-06-06' }; test('creates a non-R-initial Outreach Pipeline section when absent', () => { // SAMPLE_STATE has no ## Outreach Pipeline section → must be created (additive) assert.ok(!SAMPLE_STATE.includes('## Outreach Pipeline')); const result = recordOutreachContact(SAMPLE_STATE, CONTACT_OPTS); assert.notEqual(result, null); assert.ok(result.content.includes('## Outreach Pipeline')); }); test('records track, partner, stage, next action and due in the entry', () => { const result = recordOutreachContact(SAMPLE_STATE, CONTACT_OPTS); assert.notEqual(result, null); assert.ok(result.content.includes('[2026-05-30 14:00]')); assert.ok(result.content.includes('@bigvoice')); assert.ok(result.content.includes('collab')); assert.ok(result.content.includes('pitched')); assert.ok(result.content.includes('follow up if no reply')); assert.ok(result.content.includes('2026-06-06')); }); test('is additive — inserts last_outreach_date when the field is absent', () => { assert.ok(!/^last_outreach_date:/m.test(SAMPLE_STATE)); const result = recordOutreachContact(SAMPLE_STATE, CONTACT_OPTS); assert.notEqual(result, null); assert.match(result.content, /^last_outreach_date: "2026-05-30 14:00"$/m); }); test('updates an existing last_outreach_date without duplicating it', () => { const withField = SAMPLE_STATE.replace( 'last_post_date: "2026-04-05"', 'last_post_date: "2026-04-05"\nlast_outreach_date: null' ); const result = recordOutreachContact(withField, { ...CONTACT_OPTS, contactDate: '2026-05-31 09:00' }); assert.notEqual(result, null); const hits = result.content.match(/^last_outreach_date:/gm) || []; assert.equal(hits.length, 1, 'field must not be duplicated'); assert.match(result.content, /^last_outreach_date: "2026-05-31 09:00"$/m); }); test('leaves existing fields and sections untouched (round-trip)', () => { const result = recordOutreachContact(SAMPLE_STATE, CONTACT_OPTS); assert.notEqual(result, null); assert.match(result.content, /^last_post_date: "2026-04-05"$/m); assert.match(result.content, /^follower_count: 850$/m); assert.ok(result.content.includes('- [2026-04-05] "AI governance is not about..."')); }); test('gracefully defaults empty optional fields', () => { const result = recordOutreachContact(SAMPLE_STATE, { contactDate: '2026-05-30 14:00', partner: 'minimal-contact' }); assert.notEqual(result, null); assert.ok(result.content.includes('## Outreach Pipeline')); assert.ok(result.content.includes('minimal-contact')); }); test('returns a changes array describing what changed', () => { const result = recordOutreachContact(SAMPLE_STATE, CONTACT_OPTS); assert.notEqual(result, null); assert.ok(Array.isArray(result.changes)); assert.ok(result.changes.length > 0); }); test('appends into the pre-existing Outreach Pipeline section (production path) without duplicating it or dropping its format comments', () => { // TEMPLATE_STATE ships the section + its two comments → the // section-APPEND branch must fire, NOT section creation. assert.ok(TEMPLATE_STATE.includes('## Outreach Pipeline')); const result = recordOutreachContact(TEMPLATE_STATE, CONTACT_OPTS); assert.notEqual(result, null); // Heading not duplicated (the create branch did not also run) const headings = result.content.match(/^## Outreach Pipeline$/gm) || []; assert.equal(headings.length, 1, 'section must not be duplicated'); // Both template format comments survive the append assert.ok(result.content.includes('')); assert.ok(result.content.includes('')); // Entry lands INSIDE the section (it is the last section in the template) const section = result.content.slice(result.content.indexOf('## Outreach Pipeline')); assert.ok(section.includes('[2026-05-30 14:00]'), 'entry must land inside the Outreach Pipeline section'); assert.ok(section.includes('@bigvoice')); }); test('records $-bearing partner/stage/nextAction verbatim — special replacement patterns are not interpreted', () => { // Regression (S12): same class as the other section appends — replacement // function, not string, so `$&`/`$1`/`$$` in user content are inserted verbatim. const result = recordOutreachContact(TEMPLATE_STATE, { contactDate: '2026-05-30 14:00', track: 'collab', partner: '@big$voice & $&co', stage: 'pitched $100 deal', nextAction: 'send $$ quote, ref $1', dueDate: '2026-06-06' }); assert.notEqual(result, null); assert.ok(result.content.includes('@big$voice & $&co'), 'partner with $-tokens verbatim'); assert.ok(result.content.includes('pitched $100 deal'), 'stage with $-tokens verbatim'); assert.ok(result.content.includes('send $$ quote, ref $1'), 'nextAction with $-tokens verbatim'); const headings = result.content.match(/^## Outreach Pipeline$/gm) || []; assert.equal(headings.length, 1, 'section must not be re-injected by a $1/$& expansion'); }); test('no-anchor fall-through: none of last_outreach_date/last_firsthour_date/last_post_date — scalar not written, not reported; section still appended', () => { // Exercises the else-fall-through of the date-scalar gate // (state-updater.mjs:284-293): with NONE of the three anchors, the scalar // cannot be inserted and must NOT be reported — the section append still // runs, so result is non-null. assert.ok(!/^last_outreach_date:/m.test(NO_ANCHOR_STATE)); assert.ok(!/^last_firsthour_date:/m.test(NO_ANCHOR_STATE)); assert.ok(!/^last_post_date:/m.test(NO_ANCHOR_STATE)); const result = recordOutreachContact(NO_ANCHOR_STATE, CONTACT_OPTS); assert.notEqual(result, null); assert.ok(!/^last_outreach_date:/m.test(result.content), 'scalar must not be inserted without an anchor'); assert.ok(!result.changes.some((c) => /last_outreach_date/.test(c)), 'no date-scalar change entry without an anchor'); assert.ok(result.changes.some((c) => /^Outreach Pipeline \+=/.test(c)), 'section entry must still be recorded'); assert.ok(result.content.includes('## Outreach Pipeline')); assert.ok(result.content.includes('[2026-05-30 14:00]')); }); });