Commit graph

129 commits

Author SHA1 Message Date
2eb463f6cd fix(linkedin-studio): S18 harden ab-test — soften "(Tue-Thu, 8-9 AM optimal)" precision over-claim to "(Tue-Thu mornings)" 2026-06-19 13:55:41 +02:00
52e39d18a9 fix(linkedin-studio): S17 harden audit — drop orphan WebFetch grant + re-source carousel save-rate claim to SSOT 2026-06-19 13:24:43 +02:00
7362d8e4b1 fix(linkedin-studio): S16 harden analyze — drop unsourced 5+ hashtags penalty
S16 analyze through the v2 interactive quality gate (analytics/diagnostic class —
prose-diagnosis + conditional agent-delegation, NOT CLI-backed: allowed-tools
Read/AskUserQuestion/Task, no Bash). 1 finding, 1 surgical edit in
commands/analyze.md, grep-confirmed + grounded persona sim (scenario 1, reach -50%):

- FUNN (axis-b'): Step 4 penalty checklist asked 'Did you use 5+ hashtags in posts?'
  — a concrete-number penalty absent from BOTH the SSOT
  (algorithm-signals-reference.md: no hashtag claim, deliberately omitted) AND the
  cited troubleshooting-guide.md (its own checklist is 6 items, no hashtag).
  Command-only unsourced folklore. Dropped analyze.md:149; kept :150 'tag unrelated
  people for reach' (defensible — aligns with SSOT bait/pod-suppression direction,
  no numeric claim).

Verified clean: interpret-mode wiring (analytics-interpreter resolves, real default
mode agent:6,39,141), shared data-dir seam (analyze:41 <-> agent:49),
/linkedin:profile cross-ref, graceful-degradation IF, saves/dwell honesty (no Bash,
no over-promise). External-link hard-penalty framing in troubleshooting-guide.md:127
vs SSOT logged as reference-pass follow-up (out of S16 scope — not touched).

Gate: test-runner 81/0/0 exit 0; counts 29/19 unchanged (.md-only).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qgzo6rxthw7KuxHjn5vyE
2026-06-19 06:34:20 +02:00
060ce8a371 fix(linkedin-studio): S15 harden report — drop baselines/metadata phantom, fix engagement_rate flag + JSON schema fields
S15 report through the v2 interactive quality gate (analytics class). 3 findings,
6 surgical edits in commands/report.md, all grep-confirmed + grounded against a
throwaway-root CLI run:

- FUNN 1 (S14 twin): Step 5c baseline/std-dev mechanism + Reference Files referenced
  baselines.json/metadata.json that no code writes (storage.ts writes only
  posts/weekly-reports/monthly-reports). Rewrote Step 5c to read the report's own
  alerts[] (detectAlerts + detectWeeklyAlerts); fixed posts filename to
  YYYY-MM-DD-<shortid>.json; added real monthly-reports file.
- FUNN 2: trends --metric engagement_rate (snake_case) → exit 1 'Invalid metric';
  CLI accepts only engagementRate (cli.ts:47,202-209). Fixed report.md:172.
- FUNN 3: Step 4/Step 6 claimed JSON fields dateRange/postCount/aggregateMetrics that
  don't exist; real schema is summary/topPerformers/underperformers/trends/alerts.
  Rewrote to real fields; fixed Deep-Dive post-search path.

Gate: test-runner 81/0/0 exit 0; counts 29/19 unchanged (.md-only).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qgzo6rxthw7KuxHjn5vyE
2026-06-19 06:18:44 +02:00
a413279c58 fix(linkedin-studio): S14 harden import — drop baseline/metadata over-promise, prose now matches CLI
import.md repeatedly promised baseline comparison, rolling 4-week averages, and
baselines.json/metadata.json the importer "creates" — but no code anywhere writes
either file; detectAlerts flags intra-batch std-dev from the batch's own mean, not
a stored baseline. Step 5b's `cat baselines.json` was always empty, so the command
fell forever to "first import — baselines will be established" on every import.
Rewrote 5 sections (Step 5, 5b, 7, State Tracking, Reference Files) to the truth:
real CLI fields + YYYY-MM-DD-<shortid> filename, honest intra-batch surfacing,
cross-week analysis deferred to /linkedin:report. Verified with a grounded run
against a throwaway fixture.
Axes a/b/c/d all PASS post-fix; lint 81/0/0, counts 29/19 unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qgzo6rxthw7KuxHjn5vyE
2026-06-18 20:56:47 +02:00
270e99d002 fix(linkedin-studio): S13 harden calendar — concrete Reschedule snippet + correct camelCase exports
Reschedule action was under-determined vs its sibling actions: prose-only with
no bash snippet, referencing snake_case queue_update_status/queue_add that don't
match the exports (queueUpdateStatus/queueAdd). Replaced with a concrete queueAdd
one-liner using the same id (re-add moves the post in place, no duplicate).
Axes a/b/c/d all PASS post-fix; lint 81/0/0, counts 29/19 unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qgzo6rxthw7KuxHjn5vyE
2026-06-18 20:19:31 +02:00
bc325f0243 fix(linkedin-studio): S12 harden firsthour — re-source ~70% stat to first 15-30 min + drop CEA mislabel
Axis-b: command attributed ~70% of reach to the first 60 min (desc + body);
SSOT algorithm-signals-reference.md:81 pins ~70% to the first 15-30 min
sub-window (golden window 60-90 min). The command even cited that SSOT, so it
contradicted its own source. Re-attributed to '15-30 minutes decide ~70%',
framing the hour as the golden window worked.

Axis-a: footer labelled engagement-frameworks.md as carrying CEA, but CEA lives
in the SSOT + engagement-coach. Dropped 'CEA' from the footer label.

Verified clean: both agents namespaced + frontmatter name/model/color match
CLAUDE.md; recordFirstHourPlan exported/additive/$-safe; delegation + hand-off
sound. test-runner.sh 81/0/0; counts 29/19/26 unchanged. FIXED, 0 deferrals.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qgzo6rxthw7KuxHjn5vyE
2026-06-18 18:48:19 +02:00
4fad655a66 fix(linkedin-studio): S11 harden pivot — moderate to-phase reset skipped fact-check (off-by-one)
S11 hardening of /linkedin:pivot. Class: pure state-mutation/orchestration
command, NOT post-emitting, invokes no agents — re-opens a long-form edition so
cleared gates re-run before lock. Feed-post predicates N/A.

BUG (P4, cross-file off-by-one): newsletter's resumption table treats
currentPhase as the LAST COMPLETED phase and resumes at the NEXT step
(draft->Step 4, consistency-quality->Step 5, factcheck-sweep->Step 5.5). The
moderate pivot default set to-phase: factcheck-sweep "so the new claims get
verified" — which resumes at Step 5.5 and SKIPS the Step 5 fact-check the
moderate pivot exists to run, violating the command's own invariant that a pivot
always invalidates the fact-check.

FIX (4 surgical edits, commands/pivot.md only):
1. moderate default factcheck-sweep -> consistency-quality (resumes Step 5)
2. structural label "(Step 3b)" -> "Step 4 (consistency)"
3. off-by-one guard callout explaining to-phase = currentPhase (last-completed)
4. anatomy enum drops factcheck-sweep -> draft | consistency-quality
gatesToRerun list + worked example unchanged (both correct).

4-axis verdict: a/b/c/d all PASS post-fix, 0 deferrals -> FIXED.
Gate: test-runner 81/0/0 · counts 29/19/26 unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qgzo6rxthw7KuxHjn5vyE
2026-06-18 18:34:37 +02:00
adb8c5bde3 docs(linkedin-studio): S10 harden headless-review — verified clean, no change (4 axes PASS)
headless-review is the standalone surface of newsletter Step 6.5: a cold
adversarial review-orchestrator, NOT post-emitting — it emits a consolidated
review report + optional edition-state persist, never drafts prose, never
publishes a feed post, so the feed-post predicates (hook 110-140, 1,200-1,800
band, no-body-link) do not apply (even further from feed-post than newsletter).
The 4 archetype agents are shared with newsletter Step 6.5 -> S9 baseline reused.
Ran the 7 predicates tool-grounded against the final file (248 lines):

- 4 namespaced subagent types resolve (frontmatter name: match)
- 8/8 Reference Files exist (:241-248)
- always-present fallback longform-quality-rules.md exists
- articles.NN.headlessReview schema complete in edition-state.template.json
  (:56-69, ladder pending->run->folded)
- 5 degradation points (freeze cp :130 / draft ask-once :104 / persona :117 /
  [GATE] re-run :163-164 / output-default :93 + SendUserFile-else-file:// :208-210)
- persona resolution chain = newsletter Step 1 (:179-183)
- "five archetypes" (line-wrapped :5-6/:30-31) = 5 modes from 4 agents

Borderline at talk: freeze (:126) assumes review/ exists (no mkdir -p).
Operator chose Alt A (no change) — byte-identical to newsletter :1050 (S9-clean);
review/ is created by the render pipeline that produces the draft. Logged as a
NICE below the axis-pass bar, not pursued.

4-axis verdict: a/b/c/d all PASS, 0 gaps, 0 deferrals -> NO CHANGE.

Gate: test-runner 81/0/0 · counts 29/19/26 unchanged · headless-review.md
byte-identical to 3cd174c (S9); only docs/hardening/log.md added.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qgzo6rxthw7KuxHjn5vyE
2026-06-18 18:11:35 +02:00
3cd174c616 docs(linkedin-studio): S9 harden newsletter — verified clean, no change (4 axes PASS)
newsletter is a guided/stateful long-form orchestrator, NOT post-emitting,
so the feed-post predicates (hook 110-140, 1,200-1,800 band, no-body-link)
do not apply — imposing them would be the S4/video category error the
command itself rejects (:38-42). Ran the 7 agreed S9 predicates
tool-grounded against the final file (1594 lines, 16 phases):

- 8 invoked subagents all resolve, all linkedin-studio:-namespaced
- render/queue helpers exist; queueAdd 8-arg signature matches the call (:1486)
- 9 plugin-root ref/template files exist
- phase strings: _doc.phases (16) <-> resumption table (16) diff IDENTICAL
- graceful degradation present everywhere (voice/persona, mcp-image,
  exit-codes x4, optional config, fan-out gates x3)
- native-newsletter section honest; sourcing note + cited file both present

4-axis verdict: a/b/c/d all PASS, 0 gaps, 0 deferrals -> NO CHANGE.

Gate: test-runner 81/0/0 · counts 29/19/26 unchanged · working tree
byte-identical to 8babcf7 (S8); only docs/hardening/log.md added.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qgzo6rxthw7KuxHjn5vyE
2026-06-18 17:54:14 +02:00
8babcf77ab fix(linkedin-studio): S8 harden pipeline — Step-3 buzzword gate enumerated inline
Step-3 optimize gate (:79) carried the buzzword line but as a bare
'- [ ] No corporate buzzwords' — not self-contained, leaning on the
quality-scorecard.md reference rather than the inline gate the AI executes.
Enumerated the banned list inline to match every other post-emitting gate
(post :134 / react :131 / carousel :123 / video :129 / scorecard :132),
removing pipeline as the odd-one-out.

Recorded deferral: Step-2 component structure (:58-62) sums 960-1640, floor
below the Step-3 band floor 1,200 — identical to batch's untouched 3a; the
gate (:77) already names the band and catches a minimum-path undershoot, so
no edit (editing Step 2 would exceed S7 precedent).

Gate: test-runner 81/0/0 · counts 29/19 unchanged · only .md touched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qgzo6rxthw7KuxHjn5vyE
2026-06-18 17:15:46 +02:00
8a3e109b35 fix(linkedin-studio): S7 harden batch — format-aware length band on per-post quality check
S7 close-out (1 edit / 1 command-file · lint 81/0 · counts 29/19/26/6):
- batch.md:95 Step-3b Quick Quality Check: replace vague "Character count in range" with a format-aware band (standard 1,200-1,800 · quick 150-500 · carousel/video caption per that format) — the old line named no band, and the 3a structure (:88-92) sums to 960-1640, under the 1,200 standard floor, so a ~1,000-char standard post passed "in range" unflagged

Axis-c (length band) closed; axes a/b/d PASS. Class: hybrid guided/stateful + post-emitting per post (each of 3-5 drafts is a feed post). Buzzword (:98) + no-body-link (:97) already present — the gap S2-S6 found is already closed in batch. Wiring verified: trend-spotter + content-planner resolve; all 5 node helpers (queueUpcoming/queueAdd 8-arg/queueFormatSummary/generateIcalFromQueue/writeIcalFile) export with matching signatures. 3 deferrals (3a single-structure vs mixed-format promise · no per-post differentiation gate by design · weekly_goal-unset micro-fallback). Local-only per push policy.
2026-06-18 15:29:31 +02:00
1524b8438f fix(linkedin-studio): S6 harden video — caption buzzword gate + voice-samples graceful degradation
S6 close-out (2 edits / 1 file · lint 81/0 · counts 29/19/26/6):
- video.md:129 Step-5 De-AI gate: add content rule #4 buzzword strip to the post-caption gate (only buzzword line was :106 under "Natural Speech Test" = script not caption; caption is post-emitting feed text)
- video.md:42/:45 Step-0: soften "(REQUIRED before scripting)" → "(read before scripting)" + add fresh-adopter graceful-degradation fallback (empty/absent voice-samples → SKILL.md/defaults, no dead-end)

Axis-c (buzzword on caption) + axis-d (voice-samples dead-end) closed; axes a/b PASS. Class: hybrid guided/stateful + post-emitting on the caption (hook 110-140 deliberately not imposed on the accompanying caption — operator-agreed). video-scripter + differentiation-checker wiring verified; 2.5-wps budgets exact. 2 deferrals (caption-hook class boundary + :69 "zero distribution" inter-reference honesty). Local-only per push policy.
2026-06-18 15:20:05 +02:00
d3efe326b4 fix(linkedin-studio): S5 harden carousel — Step-5 caption buzzword gate
S5 close-out (1 edit / 1 file · lint 81/0 · counts 29/19/26/6):
- carousel.md:123 Step 5 De-AI gate: add content rule #4 buzzword line to the caption gate (was missing; grep NONE pre-edit; caption is post-emitting feed text)

Axis-c (buzzword on the caption) closed; axes a/b/d PASS. Class: hybrid guided/stateful + post-emitting on the caption. mcp-image graceful degradation + differentiation-checker wiring verified. 2 deferrals (no-body-link on caption + cosmetic slide line-count). Local-only per push policy.
2026-06-18 14:28:07 +02:00
a3d26b223c fix(linkedin-studio): S4 harden multiplatform — Step-0 empty-source fallback + cross-platform buzzword line
S4 close-out (2 edits / 1 command file · lint 81/0 · counts 29/19/26/6):
- multiplatform.md:28 Step 0: empty-source fallback (drafts empty + no recent posts + nothing pasted -> stop, ask user to paste; never fabricate source)
- multiplatform.md:45 Adaptation Templates: cross-platform voice-hygiene line (banned buzzwords = content rule #4; adaptation rewrites can introduce them)

Class call: NOT post-emitting (output is Twitter/slides/YouTube) -> no LinkedIn predicate imposed; tested as guided/cross-format + routing sub-predicate. Routing (/linkedin:newsletter) + both reference files resolve; no subagents. Axis-c (buzzword) + axis-d (degradation) closed; axes a/b PASS. Local-only per push policy.
2026-06-18 14:10:17 +02:00
b8c2155003 fix(linkedin-studio): S3 harden react — Step-6 buzzword gate + WebFetch-failure fallback (both paths)
S3 close-out (3 edits / 1 file · lint 81/0 · counts 29/19/26/6):
- react.md:131 Step 6: add no-buzzword checklist line (was missing; banned list = content rule #4; covers Step 6b via "Same as Step 6")
- react.md:60 Step 2: WebFetch-failure fallback (paywall/JS/YouTube-no-transcript -> ask user to paste or pick another; never fabricate)
- react.md:194 Step 2b: same fallback pointer for the multi-URL comparison path

Axis-c (buzzword) + axis-d (graceful degradation) closed on both paths; axes a/b PASS. Shared scorecard already clean post-S2. Local-only per push policy.
2026-06-18 13:44:00 +02:00
69840e7a94 fix(linkedin-studio): S2 harden post — Step-5 buzzword gate + scorecard hook-band consistency
P4 close-out (4 edits / 2 files · lint 81/0 · counts 29/19/26/6):
- post.md Step 5: add no-buzzword checklist line (was missing; banned list = content rule #4)
- quality-scorecard.md l.32/114: one-sided "140" hook bound -> "110-140" (matches l.128, resolves self-contradiction)
- quality-scorecard.md: add no-buzzword line to Pre-Publish Basic Checklist

Shared scorecard fix benefits 8 consumers; only post hardened this session (react/batch/video/pipeline get own EVALUATE in S3/S5/S6/S8). Local-only per push policy.
2026-06-18 13:32:56 +02:00
68f7701d74 feat(linkedin-studio): M0 — per-user external data dir (v0.5.0) 2026-06-18 13:17:35 +02:00
ef3fa939d1 test(linkedin-studio): M0-17 — full green gate (SC1–SC7 verified): lint 81/0 · hooks 128/0 · analytics 119/0 + tsc clean 2026-06-18 13:13:06 +02:00
8275159aad test(linkedin-studio): M0-16 — lint: EXPECT_REFS=26 + no-bare-path + no-pin + SC2 dry-run (R1) + drop 5 ANALYTICS_ROOT pins it caught 2026-06-18 13:11:50 +02:00
6bd263144f docs(linkedin-studio): M0-15 — repoint remaining prose families + route ab-tests/plans 2026-06-18 13:04:18 +02:00
c0abb82d9c docs(linkedin-studio): M0-14 — D3 path-convention + voice-readers prototype 2026-06-18 12:38:17 +02:00
1d985991d1 chore(linkedin-studio): M0-13 — 4 D2 templates + scrub leak + scaffold fallback 2026-06-18 12:19:48 +02:00
5c1e1800f0 refactor(linkedin-studio): M0-12 — wire + run migrateData (data externalized before scrub) 2026-06-18 12:11:11 +02:00
33aa3c4ca9 docs(linkedin-studio): M0-11 — drop in-plugin ANALYTICS_ROOT pins + repoint content-history (B1/B2) 2026-06-18 12:05:15 +02:00
c35e9fc9d3 refactor(linkedin-studio): M0-10 — content-filter recognizes external drafts (after negatives, before infraDirs) 2026-06-18 11:50:04 +02:00
629ff7ff58 refactor(linkedin-studio): M0-9 — REMEMBER.md materializes external (anti-pattern fix) 2026-06-18 11:48:24 +02:00
289eab498e refactor(linkedin-studio): M0-8 — voice read via getDataRoot (SC7) 2026-06-18 11:45:14 +02:00
3ddf9c5ffa refactor(linkedin-studio): M0-7 — scorer reads external instance (mirrors MOVE/COPY_FILES), plugin fallback (HIGH #3) 2026-06-18 11:42:50 +02:00
abc19b5641 refactor(linkedin-studio): M0-6 — quick-import.mjs via getDataRoot + fix printed path 2026-06-18 11:35:14 +02:00
7f386943c6 refactor(linkedin-studio): M0-5 — queue-manager.mjs via getDataRoot('drafts') 2026-06-18 11:31:56 +02:00
9e059f8a7f refactor(linkedin-studio): M0-4 — migrate-data.mjs atomic+locked+idempotent (created, not wired) 2026-06-18 11:21:00 +02:00
e5806b3890 test(linkedin-studio): M0-3 — resolver twin-consistency test (R2) 2026-06-18 11:12:50 +02:00
760d4c9101 refactor(linkedin-studio): M0-2 — storage.ts getDataRoot + external default (ANALYTICS_ROOT alias kept) 2026-06-18 11:12:11 +02:00
dc294495d6 refactor(linkedin-studio): M0-1 — data-root.mjs resolver twin + test 2026-06-18 11:08:52 +02:00
49eb346812 chore(gitignore): add session/local-state baseline (polyrepo split) 2026-06-18 10:21:12 +02:00
4a60abec7f docs(linkedin-studio): M0 implementation plan (18 steps, adversarially reviewed + revised)
/trekplan on docs/m0/brief.md -> docs/m0/plan.md. 8 exploration agents.
plan-critic REPLAN on v1 (3 blockers + 5 majors + 5 minors); all 13 closed.
scope-guardian ALIGNED (SC1-SC7 + D1-D6 covered). Validator 18 steps / 0 errors.
Local only - not pushed (planning push-policy).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 09:46:28 +02:00
a9d86005f5 docs(linkedin-studio): M0 per-user data-dir migration brief (ratified)
Grounds the architecture-first M0 workstream (v0.4.0 -> v1.0.0). Three
read-only mapping passes corrected the surface from STATE.md's stale 108
to 195 references (~138 in commands/), of which ~186 are .md prose and
only ~3 executable seams do real I/O. Six decisions (D1-D6) ratified.

Brief only; M0 is not implemented. Next step is /trekplan in a fresh
session. Local commit, not pushed (operator-decided).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 14:34:55 +02:00
d7c5cdc487 docs(linkedin-studio): simplify README 663 → 260 lines
Trim the front-door README to essentials: value prop, two engines, quick start, full command overview, compact 19-agent roster (lint-required model surface), merged boundaries, quality rules, 2 examples, license. Deep reference (knowledge base, hooks/skills/personalization/config/analytics internals, agent pipeline) now points to references/ and CLAUDE.md; full version history points to CHANGELOG. The pre-1.0 note is gap-framed (what is not done yet), not a promised roadmap. No functionality changed; lint green (74/0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 19:40:25 +02:00
3a7b0a1872 chore(linkedin-studio): re-baseline 4.1.0 → 0.4.0 (honest pre-1.0 maturity)
Reset version declarations to v0.4.0 across plugin.json, README badge + version-history, CLAUDE.md header, and the root marketplace README entry. The 1.0.0–4.1.0 numbering reflected ambition, not maturity: user data still lives inside the plugin tree (.gitignore-defended), no command has passed the hardening quality-gate, command testing is incomplete, and there is no GUI. A deliberate marketplace downgrade (existing installs will not auto-pull a lower number). Path to v1.0.0 = architecture (M0 per-user data-dir migration) + hardening + command testing + GUI. No functionality changed — only version declarations; all prior changelog/history preserved. Lint green (Passed 74 / Failed 0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 19:13:35 +02:00
c3ea883833 docs(linkedin-studio): replace v1 autonomous hardening plan with v2 interactive
v1 (autonomous 5-step + reviewer swarm + /trekreview gate) produced confident
fabrications and burned context for what are usually 0-1 line edits. v2 is a
slow, dialogic, one-command-per-session gate with the operator as truth source
and every mechanical claim tool-grounded. Method, command-class predicates,
session queue, and STATE.md end-of-session handoff are documented in plan.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 07:47:17 +02:00
94760cde90 fix(linkedin-studio): S1 hardening — method calibration (quick) + Start journey
Hardening phase S1. Tightened the hook character bound from one-sided
(under 140) to the full canonical 110-140 band across the Start-journey
creation surfaces, matching hooks/prompts/content-quality-gate.md:
- quick.md: hook band on Step 2 + Step 5 checklist; added a buzzword check
  and a 150-500 length-band check; checklist tally 6 -> 7.
- onboarding.md: hook band in Phase 3.2 + 3.3 (length + no-links already present).
- first-post.md: hook band in Step 4 + Step 5 (length + no-links already present).
- setup.md: zero-edit pass (all four axes already satisfied).

Adds docs/hardening/log.md (per-command audit trail, 5-step method) and
docs/hardening/review.md (cold /trekreview: ALLOW, 0 BLOCKER/0 MAJOR/1 MINOR).
Lint Failed:0, counts 29/19/25/6 unchanged. No structural/version churn.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 06:46:11 +02:00
78409306ca docs(linkedin-studio): hardening-phase foundation (brief + adversarially-reviewed plan)
New phase after the baseline-audit remediation (S1-S17, bf9d432, complete):
a command-hardening pass that simulates each of the 29 commands and tightens
it to its stated intention (intention-fidelity + prompt-quality only — no
structural redesign, no new features, no GUI/M0). Runs over ~8 journey-grouped
Voyage sessions, gated by lint + /trekreview ALLOW before push.

Foundation laid this session (execution starts next session):
- docs/hardening/brief.md (valid; 3 locked forks: hybrid simulation,
  intention-fidelity+prompt-quality, per-journey cadence; research skipped —
  the 2026 bar is frozen in algorithm-signals-reference.md)
- docs/hardening/plan.md (8 sessions, Grade B+ 87)

Adversarial review: brief-reviewer PROCEED_WITH_RISKS (3 findings folded:
self-graded quality axis, SC-H deferral contradiction, no stopping rule);
plan-critic REVISE (2 blockers + 10 major — all addressed: anchored coverage
predicate kills substring false-positives, full-session cold-reviewer oracle
on concrete before/after output, per-type mechanical predicate, Failed:0 gate
not literal-74, S1 HALT circuit-breaker); scope-guardian ALIGNED.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 05:31:52 +02:00
bf9d4327bd docs(linkedin-studio): S17 — C13–C46 triage (0 still-real) closes audit remediation
Cold-read triage of the ~34 uncalibrated baseline-audit findings (C13–C46)
that never got a second hostile pass. An independent Opus reader classified
each against the current code; every disposition was grep-verified in the
main session.

Result: 0 still-real, 23 already-fixed, 1 outdated-drop (24 grouped/sub-claim
entries). No inline code fix needed — the v4.0.0 + S13–S16 remediation had
already closed every still-real item (dead lint, 11 orphan agents, carousel
full-deck clipboard, router tiering, onboarding inline, de-AI gate, video gate,
post-feedback-monitor->Opus, series-path parameterization, SKILL roster).

Deliverable: docs/remediation/c13-c46-triage.md (disposition record) +
docs/remediation/review.md (S17 review, ALLOW). /trekreview: brief-conformance
0 findings; code-correctness 2 MAJOR in the triage doc's own prose (one
overclaim, one line-pointer) FIXED in-session — no false-green disposition.

Gate: test-runner.sh 74/0/0, hooks node --test 98/98, analytics 116/116.
M0 (per-user data-dir migration) deferred to the UI track. Remediation COMPLETE.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 04:45:11 +02:00
69fec21f43 feat(linkedin-studio): S16 — optional manual saves in analytics + close deferred onboarding Write MAJOR
Lifts the original v4.0.0 Non-Goal: an optional, manually-entered `saves`
metric through the analytics layer, built location-agnostic (option c) so
UI-brief §9b/M0 relocates the data dir in one place later.

- types: PostMetrics.saves? + Weekly/Monthly summary.totalSaves? (optional);
  new RankableMetric type for the always-numeric index-access whitelist
- parser: dedicated parseOptionalCount() — blank/non-numeric/negative -> undefined
  ("unknown != 0"), genuine 0 kept; saves NOT folded into engagementRate
- reports: totalSaves set only when >=1 post carries saves (backward-compat)
- cli: saves surfaced in import summary + weekly/monthly totals + per-post
- S16-pre: onboarding.md allowed-tools gains Write (closes S15-deferred MAJOR)
- docs (three-doc rule): plugin README boundary + analytics README + root README
  + plugin CLAUDE.md + CHANGELOG; dwell stays explicitly unmeasurable

Independent /trekreview: brief-conformance 0 findings; code-correctness 2 MAJOR
(own lockstep misses) FIXED in-session (parseOptionalCount + edge tests). Gate:
tsc clean, analytics 116/116, lint 74/0/0, hooks 98/98. Within-v4.1.0 refinement
(no surface/count/version change).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 22:23:12 +02:00
e36efe9944 fix(linkedin-studio): S15 — UX finish §6c (B1 onboarding inline-draft + B3 carousel full-deck clipboard)
B2 (router tiering) was already delivered in S14, so S15 = B1 + B3 only.
No surface/count/version change -> within-v4.1.0 refinement (S11-S13 precedent).

- B1 (commands/onboarding.md): replace the "Run /linkedin:first-post" dead-end
  hand-off in Phase 3 with the first-post drafting steps embedded inline (3.1 topic
  -> 3.2 3-line draft -> 3.3 QC -> 3.4 present+clipboard -> 3.5 state-update that sets
  first_post_date). Wizard now yields a draft in-flow; 0 dead-end strings. Stays within
  the existing allowed-tools (Read/Bash/AskUserQuestion); UI-brief §12b scope-guard
  honored (no provider seams / progressive-disclosure added).
- B3 (commands/carousel.md): Step 6 now assembles the ENTIRE deck (every slide's copy
  + the caption) into the clipboard payload, not just the caption; full-deck assembly
  precedes the clipboard-helper.mjs call.

Independent /trekreview (2 Opus reviewers): brief-conformance 0 findings; code-correctness
1 MAJOR that is PRE-EXISTING and out of S15 scope (onboarding Phase 2 saves need Write in
allowed-tools; lines 142/157, untouched by the S15 diff) -> DEFERRED to next session per
"ekte design-funn -> neste sesjon". Verdict ALLOW for the delivered scope (not a WARN-override).

Gate: test-runner.sh 74/0/0; node --test 98/98; commands=29; v4.1.0 unchanged.
See docs/remediation/review.md for the full record (ALLOW + 1 deferred MAJOR).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 21:44:34 +02:00
52df229dfc feat(linkedin-studio): S14 — journey layer (create/measure front-doors + 5-journey router), v4.1.0
14a's cold command-rationalization found ZERO redundancy across the 27 commands
(no defensible merge/cut), so the operator reframed S14 from "merge/cut" to
"add a journey layer over the kept atomics".

- Add /linkedin:create + /linkedin:measure — delegate-only guided front-doors
  (Read/Glob/AskUserQuestion only; route to the command that owns the work)
- Re-tier commands/linkedin.md into 5 journeys (Start/Create/Engage/Measure/Grow);
  onboarding/strategy elevated as Start/Grow front-doors; Engage = calendar+firsthour tier
- 14a honesty nits: router now lists firsthour; calendar cross-links to firsthour;
  competitive confirmed UNGATED (the claimed 1K-gating inconsistency was unfounded)
- Lockstep: EXPECT_COMMANDS 27->29, v4.0.0->4.1.0 across plugin.json / README badges /
  plugin+root CLAUDE.md / README / CHANGELOG; new README commands-badge lint guard
- 14a deliverable corrected: multiplatform entry added (26/27 -> 27/27), header counts,
  competitive nit withdrawn
- Independent /trekreview (2 Opus reviewers) raised 3 MAJORs (stale commands badge,
  router competitive self-contradiction, STATE.md count) — ALL remediated in-session;
  verdict ALLOW

Gate: test-runner.sh 74/0/0; node --test 98/98; commands=29; v4.1.0 consistent.
Additive/minor — no command removed/renamed/behavior-changed; reload registers create+measure.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 21:27:06 +02:00
3383abd3b6 fix(linkedin-studio): S13 — close S12 WARN ($-scalar + false-green test) + $-safety lint guard
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.
2026-05-30 19:12:45 +02:00
90cf98d287 fix(linkedin-studio): S12 — close S11 re-review + render-chain-propagation lint guard
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>
2026-05-30 15:42:25 +02:00
be8c9b01fb fix(linkedin-studio): S11 — model-name declaration drift + model-consistency lint guard
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>
2026-05-30 15:05:37 +02:00