fix(linkedin-studio): close v4.0.0 audit review findings (S8)
Close the 5 findings from the S7 /trekreview release gate (review.md, verdict BLOCK):
- BLOCKER: comment-multiplier "5x" reconciled to the canonical order-only framing
(no fixed multiplier) in agents/engagement-coach.md, linkedin-growth-playbook,
linkedin-formats.md — per algorithm-signals-reference.md ("do not quote a comment
multiplier").
- BLOCKER: carousel rate "6.60%/6.6% (highest)" reconciled to "~7% top organic
format" in linkedin-formats.md:42 (was self-contradicting :50) and
assets/templates/carousel-templates.md.
- Lint hardening: test-runner.sh STALE_STATS now matches 6.60% + the 5x comment
folklore and scans agents/ + assets/templates/ — the grep that defines the
Phase-0 criterion now catches both BLOCKERs.
- MAJOR: onboarding.md command count 26 -> 27.
- MAJOR: add section-append-branch (production-path) tests for recordFirstHourPlan
+ recordOutreachContact against a template-layout fixture.
- MINOR: move date-scalar changes.push inside the write branch in state-updater.mjs.
Verify: node --test hooks/scripts/__tests__/*.test.mjs -> 92/92; bash
scripts/test-runner.sh -> 66/0/0. NO push until /trekreview re-confirms ALLOW/WARN.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
1fa2cc945e
commit
18b198f655
8 changed files with 104 additions and 13 deletions
|
|
@ -36,6 +36,42 @@ growth_rate_needed: 0
|
|||
## 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
|
||||
|
||||
<!-- First-hour / reply-loop plans, newest first. Written by /linkedin:firsthour. -->
|
||||
<!-- Format: ### [YYYY-MM-DD HH:MM] topic -->
|
||||
|
||||
## Outreach Pipeline
|
||||
|
||||
<!-- Outreach contacts / pipeline rows, newest first. Written by /linkedin:outreach. -->
|
||||
<!-- Format: ### [YYYY-MM-DD HH:MM] partner — track -->
|
||||
`;
|
||||
|
||||
describe('updatePostTracking', () => {
|
||||
test('sets last_post_date to provided date', () => {
|
||||
const result = updatePostTracking(SAMPLE_STATE, {
|
||||
|
|
@ -364,6 +400,28 @@ describe('recordFirstHourPlan', () => {
|
|||
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('<!-- First-hour / reply-loop plans, newest first. Written by /linkedin:firsthour. -->'));
|
||||
assert.ok(result.content.includes('<!-- Format: ### [YYYY-MM-DD HH:MM] topic -->'));
|
||||
// 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'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('recordOutreachContact', () => {
|
||||
|
|
@ -438,4 +496,22 @@ describe('recordOutreachContact', () => {
|
|||
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('<!-- Outreach contacts / pipeline rows, newest first. Written by /linkedin:outreach. -->'));
|
||||
assert.ok(result.content.includes('<!-- Format: ### [YYYY-MM-DD HH:MM] partner — track -->'));
|
||||
// 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'));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -219,13 +219,16 @@ export function recordFirstHourPlan(stateContent, { planDate, postTopic = '', ta
|
|||
let content = stateContent;
|
||||
const changes = [];
|
||||
|
||||
// 1. last_firsthour_date — replace in place, else insert after last_post_date (additive)
|
||||
// 1. last_firsthour_date — replace in place, else insert after last_post_date (additive).
|
||||
// Report the change only inside the branch that actually writes it: if neither
|
||||
// anchor field exists, the scalar is not inserted and must not be reported as changed.
|
||||
if (/^last_firsthour_date: .*/m.test(content)) {
|
||||
content = replaceField(content, 'last_firsthour_date', `"${planDate}"`);
|
||||
changes.push(`last_firsthour_date → ${planDate}`);
|
||||
} else if (/^last_post_date: .*/m.test(content)) {
|
||||
content = content.replace(/^(last_post_date: .*)$/m, `$1\nlast_firsthour_date: "${planDate}"`);
|
||||
changes.push(`last_firsthour_date → ${planDate}`);
|
||||
}
|
||||
changes.push(`last_firsthour_date → ${planDate}`);
|
||||
|
||||
// 2. firsthour_active flag — only touch if the field is declared (additive)
|
||||
if (/^firsthour_active: .*/m.test(content)) {
|
||||
|
|
@ -276,15 +279,18 @@ export function recordOutreachContact(stateContent, { contactDate, track = '', p
|
|||
const changes = [];
|
||||
|
||||
// 1. last_outreach_date — replace in place, else insert after last_firsthour_date
|
||||
// if present, else after last_post_date (additive — never required up front)
|
||||
// if present, else after last_post_date (additive — never required up front).
|
||||
// Report the change only inside the branch that actually writes it.
|
||||
if (/^last_outreach_date: .*/m.test(content)) {
|
||||
content = replaceField(content, 'last_outreach_date', `"${contactDate}"`);
|
||||
changes.push(`last_outreach_date → ${contactDate}`);
|
||||
} else if (/^last_firsthour_date: .*/m.test(content)) {
|
||||
content = content.replace(/^(last_firsthour_date: .*)$/m, `$1\nlast_outreach_date: "${contactDate}"`);
|
||||
changes.push(`last_outreach_date → ${contactDate}`);
|
||||
} else if (/^last_post_date: .*/m.test(content)) {
|
||||
content = content.replace(/^(last_post_date: .*)$/m, `$1\nlast_outreach_date: "${contactDate}"`);
|
||||
changes.push(`last_outreach_date → ${contactDate}`);
|
||||
}
|
||||
changes.push(`last_outreach_date → ${contactDate}`);
|
||||
|
||||
// 2. outreach_active flag — only touch if the field is declared (additive)
|
||||
if (/^outreach_active: .*/m.test(content)) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue