Closes the 2 grep/Read-verified findings from the S12 cold full-brief re-review
(docs/remediation/review.md, WARN 0/1/1/0, 0 dropped) and closes the $-injection
CLASS — not the line — across the whole state-updater.mjs mutation surface.
See docs/remediation/review.md (S13 ALLOW, 0/0/0/0) for the full closure record:
replaceField -> replacement function; the 3 additive-insert sites -> functions
(m === $1, behavior-preserving); a scalar assert.match pins last_post_topic; and a
behavioral, coverage-complete, self-testing Section 12 guard (check-replace-safety.mjs)
that is mutation-proven. Docs three-doc + residuals updated. test-runner.sh 71/0/0,
node --test 98/98.
Closes the 2 grep-verified findings from the S11 cold full-brief re-review
(docs/remediation/review.md, BLOCK 1/0/1/0, 0 dropped). Both were the NEXT
RING of the meta-class S10/S11 converged: a propagation miss — the fix had
landed where the SC named the file, not in the render-source it depends on.
BLOCKER (command->reference propagation): references/ab-testing-framework.md:166
still shipped the banned A/B "Significant? (>20%)" Yes/No verdict column while
commands/ab-test.md (which RENDERS from it, inlined at :30, presented at :69)
had been cleaned to the honest "Directional?" framing. Re-framed the reference
result template to match the command verbatim (header + the directional note)
and retuned :38 "20% significance threshold" -> "minimum-meaningful-difference
threshold". The whole render chain is now significance-verdict-free.
MINOR ($-replacement, class-closed not line-patched): the newest-first section
appends/rewrites in hooks/scripts/state-updater.mjs passed a replacement STRING
embedding untrusted user content to String.replace, so dollar-sequences
($1 / $& / dollar-backtick / dollar-apostrophe / $$) in a topic/hook/partner
(e.g. "$100 budget cut") re-injected the captured heading and dropped
characters, silently corrupting state. Converted all 5 content-bearing sites
(Recent Posts, prune, Milestone Log, First-Hour, Outreach) to replacement
FUNCTIONS; the 3 remaining $1 sites only interpolate date scalars. +4
$-bearing regression tests — incl. the prune fixture, which itself had to
switch to a function (the bug bit the fixture as it was being written).
META (generalize the lint to the propagation-miss class): new test-runner.sh
Section 11 — render-chain propagation guard. Forbids the significance-verdict
column (Significant? adjacent to "(" or a table pipe) across the WHOLE render
chain (commands + every inlined reference + adjacent surfaces), with a
permanent non-vacuity self-test (3 verdict forms caught, 6 legitimate
Significant/significance/Directional? forms ignored) and an e2e mutation-proof.
Generalizes S10/S11's "fix the class, not the line" to command->reference.
Pre-patch render-chain sweep confirmed ab-testing-framework.md was the SOLE
propagation survivor (so a 6th review finds no 3rd). test-runner.sh 70/0/0;
node --test 98/98. CLAUDE.md lint enumeration synced.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cold full-brief re-review (S10) reached a class the S7->S9 algorithm-stat lens
never did:
BLOCKER — post-feedback-monitor published as Haiku in four surfaces
(README:259, skills/linkedin-studio:159 with wrong color Green too,
skills/linkedin-analytics:41, agents-capability-matrix:20) while
agents/post-feedback-monitor.md runs Opus. v4.0.0's Opus promotion never
reached the user-facing tables. Synced all to Opus/Lime. Refreshed
agents-capability-matrix.md (frozen at the v2.0 14-agent era): header 14->19,
+5 missing longform agents, tier counts Opus 2->8 / Haiku 1->0, longform-gate
diagram updated to the real 8-Opus-agent chain.
MAJOR — de-branded docs/plan-fullspektrum-innholdsmotor.md:70 (model brand +
jan-2026 asserted as fact -> no-name/no-month relevance-model phrasing). It was
the only tracked survivor; the rest live in gitignored ROADMAP.md /
.claude/research/ (not shipped, out of honesty scope).
META — added Section 10 model-consistency guard
(scripts/check-model-consistency.mjs): each agents/*.md model: must match every
surface declaration AND the canonical rosters must list all 19 agents.
Permanent non-vacuity self-test + e2e mutation-proven.
Pre-patch sweep confirmed post-feedback-monitor was the sole drifted agent
(89 model rows, 0 other mismatches). test-runner.sh 68/0/0, node --test 94/94.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes the S9 re-review (1 BLOCKER + 2 MAJOR, all grep-verified). The survivor
set converged 8 -> 6 -> 2; this closes the meta-problem behind the convergence,
not just the two lines.
BLOCKER — references/glossary.md:10: drop the fabricated "150-parameter
foundation model" (a garbled 150B that the S9 enumerative grep/lint, requiring a
"B"/"billion", could not match). Reframe to "a real input to LinkedIn's 2026
relevance-ranking model" with no parameter count, citing
algorithm-signals-reference.md inline — which makes the :12 "Used in" provenance
accurate (the reference does state the relevance-ranking framing; it never stated
"150-parameter").
MAJOR — CHANGELOG.md:308: de-brand "360Brew profile optimization (January 2026
algorithm update)" -> "Profile/topic-relevance optimization". Removes the
unpublishable brand + asserted Jan-2026 date, honouring v4.0.0's "removed
everywhere" claim. It was the only STALE_STATS hit in CHANGELOG.
MAJOR — scripts/test-runner.sh: the rebuilt lint was enumerative on surface form.
Generalize it to the PATTERN CLASS so the same grep that defines the SC fails on
any surface form, present or future:
- STALE_STATS model token: "150 ?B param|150 billion param"
-> "[0-9]+[ -]?(B|billion)?[ -]?param"
(covers 150-parameter / 150B param / 150 billion param). This robustifies the
review's literal suggestion "[0-9]+[ -]?(B|billion )?param", which missed the
space form "150B param"; the separator is moved out of the group.
- STAT_HITS grep scope += CHANGELOG.md (the 360Brew survivor lived outside it).
- Permanent non-vacuity SELF-TEST before the real scan: 13 forbidden probes must
match (incl. the exact "150-parameter" survivor), 8 legitimate probes must not
("Language parameter", "parameterized", "different parameters",
"175-milliarders parametermodell", 5x5x5, cadence, pixel dims, "10x your
reach"). S7->S9 each shipped a green lint because the proof was run by hand and
never committed; this makes narrowing STALE_STATS fail the suite.
Verification: test-runner.sh 67/0/0 exit 0 (was 66/0/0; +1 self-test);
node --test 94/94; broadened exhaustive grep across the tree -> zero survivors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes the S8 re-review (BLOCK 3/4/1). The S8 fix patched only the 2 strings S7 named; the re-review found 6 more same-class survivors. Per the systemic read, this is a comprehensive sweep, not a per-line patch.
Reconciled every retired engagement-coefficient + model-fact survivor against the canonical references/algorithm-signals-reference.md (order, not coefficients; comment ≈ 2x a like; no model name/params):
- glossary.md: coefficient table + Save-Signal '10x weight' → canonical ordering (citation now true)
- engagement-frameworks.md, analytics-interpreter.md, content-optimizer.md, pipeline.md, engagement-coach.md: the 10x/8x/7-9x/2.5x/0.2x system (incl. 4 survivors the re-review did not cite) → ordering
- playbook: '15x more algorithmic boost' + video '5x more conversations' → directional, sourced
- profile.md + linkedin-voice/SKILL.md: '150B parameter foundation model' → '2026 relevance-ranking model'
- quality-scorecard.md: '360Brew Validation' → topic-relevance framing
- setup.md: 'thought leadership plugin' → 'LinkedIn Studio plugin'
Lint (MAJOR 4): rebuilt scripts/test-runner.sh STALE_STATS to forbid EVERY retired-class phrasing (not the 2 S7 strings) + widened scope to assets/checklists/. Targets retired phrasings (7-9x, (10x), '10x weight', '5x more conversations'), NOT bare 10x/15x/5x (legit 5x5x5 / cadence / pixel-dims / '10x your reach' hyperbole). Proven non-vacuous: catches all 10 retired strings, ignores all 10 legit uses.
Tests (MAJOR 7): added no-anchor fall-through tests for recordFirstHourPlan + recordOutreachContact (date scalar not written/reported, section still appended). MINOR 8: reflowed newsletter.md content-repurposer wiring onto one line.
test-runner.sh 66/0/0; node --test 94/94 (was 92, +2). NO push until /trekreview re-clears the gate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Step 21 (remediation Wave 4 / S6, SOLO): finalize the audit-remediation as
v4.0.0. Version 3.1.0 -> 4.0.0 across all current-version declarations; counts
reconciled to the ls-derived source of truth (19 agents / 27 commands / 6 skills
/ 9 hooks / 25 reference docs / 16 newsletter phases — Step 20 confirmed NO
TRIM); three-doc sync (plugin README + plugin CLAUDE.md + root README) clears the
[skip-docs] debt accumulated across Wave 1-4; CHANGELOG v4.0.0 entry summarizing
Steps 1-20.
Scope additions beyond the plan's literal Files list, all version-sync or
[skip-docs]-debt in nature (flagged, not feature creep):
- ../../CLAUDE.md (root marketplace): linkedin-studio entry v3.1.0 -> v4.0.0
(the version-sync invariant mandates updating every version reference; leaving
it stale is a real inconsistency).
- scripts/test-runner.sh: added the version-consistency grep the file's own
Step-1 comment promised ("added in Step 21") — plugin.json version must match
the README badge, the CLAUDE.md header, and the CHANGELOG top entry — and
folded the Wave 2 lint gap (plugin.json now covered by the stat-consistency
scan). 66/0/0, exit 0.
- plugin README: added the missing /linkedin:firsthour command row (Step 16
[skip-docs] debt) and the 25th reference doc (longform-quality-rules.md) to
the knowledge-base table; fixed body counts ("All 26 commands" -> 27, "24
reference documents" -> 25); badges + intro 26 -> 27 commands, 24 -> 25 refs.
- root README + marketplace.json: dropped the unpublishable model brand/date
("360Brew" / "January 2026") the algorithm-signal reconciliation already
removed everywhere inside the plugin.
Surviving "3.1.0" strings are intentional history, not stale declarations: the
README version-history table row, the "vX added Y" attributions in
plugin.json/CLAUDE.md, and the headless-review reload caveat are all
changelog-genre. Every current-version declaration (plugin.json version, README
badge, CLAUDE.md header, root README marker, marketplace.json) reads 4.0.0.
The major bump reflects the remediation's scope plus the reinstall/reload the
newly-wired agents need to register, and consolidates — does not repeat — the
v3.0.0 identity break; it is not a fresh breaking API change (locked operator
decision).
Pre-existing and out of scope (flagged, untouched): a duplicate /linkedin:setup
row in the README command tables.
Verify: bash scripts/test-runner.sh exit 0 (66/0/0); plugin.json + marketplace.json
parse; counts consistent README == CLAUDE.md == root README; stale-count sweep
clean. NO push — /trekreview (S7) is the release gate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire orphan agent #11 (engagement-coach) by giving it a command surface, and
add the tracked first-hour state the plan calls for (remediation Step 16).
- commands/firsthour.md (new, 27th command): post-publish first-hour /
reply-loop sprint. Delegates plan construction to engagement-coach via
Task (subagent_type: linkedin-studio:engagement-coach) — returns a grouped
target list (whales/inner-circle/ICPs/new connections), 2-3 seed
self-comments + 3-5 CEA replies in the user's voice, and a minute-by-minute
timeline anchored to publish time. Presents timeline/targets/drafts +
velocity checkpoints, auto-copies the drafts to clipboard, persists the
plan, then hands off to post-feedback-monitor for the 48h window.
- hooks/scripts/state-updater.mjs: new pure mutation recordFirstHourPlan()
mirroring updatePostTracking — additive by contract (inserts
last_firsthour_date after last_post_date when absent, creates the
## First-Hour Plans section when absent, never touches existing fields).
Section name is deliberately non-R-initial so it stays outside
pruneContentHistory's "## Recent Posts ... (?=\n## [^R])" capture window.
+ a --record-firsthour CLI branch for parity with the other mutations.
- config/state-file.template.md: additive scalars (last_firsthour_date,
firsthour_active) + the ## First-Hour Plans section.
- hooks/scripts/__tests__/state-updater.test.mjs: extend (existing file) with
7 recordFirstHourPlan tests — section creation, field insertion vs in-place
update (no duplication), round-trip non-interference, graceful empty
defaults, changes array.
- CLAUDE.md: register the command (## Commands 26 -> 27, table row).
- scripts/test-runner.sh: EXPECT_COMMANDS 26 -> 27 (registration guard).
Verify: grep 'subagent_type: linkedin-studio:engagement-coach' commands/ ->
firsthour.md; node --test state-updater -> 26/26; full hook suite -> 83/83;
bash scripts/test-runner.sh -> exit 0 (62 passed, commands 27/27).
Plan Step 16 (Wave 4 S3).
[skip-docs]: tre-doc + version bump deferred to Step 21 per remediation plan.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
BREAKING CHANGE: the marketplace slug, the agent namespace
(linkedin-studio:<agent>), and the runtime state-file path
(~/.claude/linkedin-studio.local.md) all change. Reinstall required;
existing state migrated in place (post metrics, streak, history preserved).
The /linkedin:* commands are unchanged — the command namespace is set
per-command in frontmatter and was always independent of the plugin slug.
Functionality is byte-identical to v2.4.0; this release is pure identity.
- dir + manifests: plugins/linkedin-studio + plugin.json + root marketplace.json
- agent namespace updated in commands/newsletter.md (only functional invoker)
- state path updated in 4 hook scripts + topic-rotation prompt + state template
- catch-all skill dir renamed skills/linkedin-studio (5 functional skills unchanged)
- docs + version bump to 3.0.0 across README badge, CHANGELOG, root README/CLAUDE.md
- historical records (CHANGELOG past entries, docs/ build artifacts,
config-audit v5.0.0 snapshots) intentionally retain the old slug
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 11:32:02 +02:00
Renamed from plugins/linkedin-thought-leadership/scripts/test-runner.sh (Browse further)