linkedin-studio/docs/hardening/plan.md
Kjell Tore Guttormsen ed835e9024 fix(linkedin-studio): S28 ref-consistency B — format magnitudes → SSOT
Reconcile commands/video.md + references/linkedin-formats.md to the algorithm-signals SSOT
in one pass (20 edits/2 files). The unsourced "30% completion rate or zero distribution"
gate (7x) → "completion/dwell is a ranking input; shorter completes better; no published
hard completion gate". The 6 S27-folded link-penalty lines → "correlate with lower reach"
(incl. heading "The External Link Penalty" → "External Links and Lower Reach"). Six other
unsourced format magnitudes softened to direction-without-number (video 5.60% ER, Live
12-24x, polls 1.64x, 3x shares, Creator-Mode 35%). One internal contradiction fixed (3-sec
= 70% retention contradicted the already-reconciled three-second-folklore note).

Bucket D (this file's non-format coefficients: 47-50% reach-decline, AI-comment -30%/-55%,
hashtag -68%) left intact and deferred to S30, which is re-scoped to cover them + the other
files. Gate 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-19 22:17:54 +02:00

166 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# LinkedIn Studio — Command Hardening Plan v2 (interactive quality gate)
> **Supersedes the v1 autonomous plan** (recoverable in git history: commit `2f90880`).
> Motivated by the 2026-05-31 S2 fabrication incident: the v1 method (autonomous, prose-heavy
> 5-step per command, per-command Opus reviewer swarm, `/trekreview` gate) produced **confident
> fabrications** (Claude wrote SIMULATE/EVALUATE/HARDEN narratives — char counts, line numbers,
> before/after — for files it had not actually observed) and **burned context without value**
> (full-file re-reads + long logs + reviewer agents for what are usually 01-line edits). A large
> parallel tool-batch also destabilized the tool-result stream (out-of-order / duplicated /
> truncated returns). Nothing false shipped — the failed edits + empty greps + real reads caught
> it, and it was reverted — but the method had to change.
>
> **v2 is a slow, dialogic, human-in-the-loop gate: ONE command per session, the operator as the
> truth source, every mechanical claim tool-grounded.**
## Intent
Harden each of the 29 commands to its own intent — **one command per session** — with the
operator in the loop. Last quality gate before GUI/M0. Brief: `brief.md`.
## The locked interactive method (identical every session)
Per command, 8 steps. **Claude STOPS and waits at each "agree" point — no autonomous run-ahead:**
1. **Agree intention** — show the command's own description + its journey role; agree in one line.
2. **Agree test-method** — the class predicates (below) + what the simulation must prove.
3. **Agree persona** — who invokes + with what input.
4. **Run the test** — tool-grounded mechanical facts (each with `file:line`) + a GROUNDED persona
simulation (a real invocation → concrete output, anchored to the shown file region).
5. **Talk** — the operator's judgment is the truth source.
6. **Agree improvements** — surgical, or "no change".
7. **Implement** — Edit → grep-confirm landed → show the confirmed diff.
8. **Move on** — run the end-of-session ritual; the next command is its own session.
### Two guards (non-negotiable)
- **Read-and-show BEFORE simulate** — the simulation is anchored to observed text, shown to the operator.
- **Every mechanical claim from a tool** — char count, ✓/✗, line number: shown from a tool before stated.
### Anti-drift through dialogue
The per-command dialogue IS the drift-reduction mechanism: Claude **proposes** at each agree-point
(intention / test-method / persona / improvement) and **waits** for the operator to confirm or
correct before proceeding. Frequent grounding checkpoints are what keep Claude from confabulating —
never race past an agree-point.
### Execution discipline (the S1+S2 incident lessons)
- **ONE command per session.** Never hold multiple command files in context at once.
- **Never batch dependent tool calls in parallel.** Read→Edit→grep→log is strictly sequential.
Large parallel batches destabilize the tool-result stream.
- **Guard greps:** `grep`/`grep -c` exit non-zero on zero matches → append `|| echo NONE` so a
batch sibling isn't torn down.
- **Never write a HARDEN/after claim before the edit is grep-confirmed landed.**
- **If the tool layer degrades** (slow / truncated / empty / replayed returns): PAUSE and resume
in a fresh session. Do not push through a degraded window.
## Command classes & mechanical predicates (the testmethod per class)
- **post-emitting** — hook 110140 present · length band present · no-body-link present ·
buzzword check present · topic→5 pillars present · every `subagent_type: linkedin-studio:X`
resolves to `agents/X.md`.
- **routing** — every emitted `/linkedin:Y` resolves to `commands/Y.md`.
- **guided/stateful** — primary promised artifact actually produced · subagent targets resolve ·
graceful degradation present.
- **analytics** — graceful degradation present · saves/dwell honesty intact.
## Synthetic test data (fixtures) — for data-dependent commands
Some commands cannot be meaningfully tested without input data — analytics (`import`, `report`,
`analyze`, `audit`, `ab-test`) need a CSV/JSON; stateful commands (`calendar`, `firsthour`) need a
queue/state. For these, **step 2 (agree test-method) includes agreeing a small fictitious dataset**
that exercises the command's real path:
- **Isolated + throwaway** — built in a temp/scratch location (e.g. `/tmp/…` or a gitignored path),
**NEVER** the real analytics/state dirs, **NEVER** committed.
- **Minimal but realistic** — just enough rows/fields to trigger the behavior under test (e.g. a
35-row CSV with the real column names; a 2-experiment ab-test file).
- **Clearly fictitious** — obviously-fake values, impossible to mistake for real data.
- **Discarded after the test** — the fixture proves the command works; it is not a deliverable.
## Session queue (ONE command each)
`S1 ✅` quick · onboarding · first-post · setup — **DONE under v1; do NOT re-harden.**
| | | | |
|---|---|---|---|
| S2 post | S9 newsletter* | S16 analyze | S23 profile |
| S3 react | S10 headless-review | S17 audit | S24 create |
| S4 multiplatform | S11 pivot | S18 ab-test | S25 measure |
| S5 carousel | S12 firsthour | S19 strategy | S26 linkedin |
| S6 video | S13 calendar | S20 competitive | S27 ref-consistency A |
| S7 batch | S14 import | S21 monetize | S28 ref-consistency B |
| S8 pipeline | S15 report | S22 outreach | S29 terminology-scrub |
| | | | S30 magnitude-scrub |
*S9 newsletter (16-phase) may split into S9a/S9b. Otherwise one command = one session.
**S27/S28 — reference-consistency pass (added 2026-06-19, operator-approved; promotes open
follow-up #1 to scheduled work).** NOT full re-hardening of already-✅ commands — surgical
SSOT-reconciliation of the documented divergences, same tool-grounded discipline
(read-and-show → grep-confirm → re-grep final):
- **S27 (A) — "penalty/penalizes" framing → SSOT ✅ DONE (2026-06-19, 10 edits/6 files; see `log.md`).**
The 3 cited lines (`troubleshooting-guide.md:127`, `analyze.md:101`, `audit.md:60` — nr drifted ±1) +
7 same-class siblings (trend-spotter topic-relevance ×2, troubleshooting-guide :15, first-comment-strategy
×3, url-processing-templates :396) reconciled to "correlate with lower reach" / "topic relevance is a
ranking input — off-topic gets lower reach". Bucket B (link-penalty lines in `linkedin-formats.md`) folded
into S28; Bucket D (other unsourced magnitudes) promoted to S30.
- **S28 (B) — format magnitudes → SSOT ✅ DONE (2026-06-19, 20 edits/2 files; see `log.md`).**
`commands/video.md:69` + `references/linkedin-formats.md` reconciled in one pass: the "30% zero-distribution"
gate (7×) → "completion/dwell is a ranking input, no hard gate"; the 6 S27-folded link-penalty lines →
"correlate with lower reach"; 6 other unsourced format magnitudes (video 5.60% ER, Live 12-24x, polls
1.64x, 3x shares, Creator-Mode 35%); 1 internal contradiction (`:356` 3-sec = 70% retention). Bucket D
(this file's non-format coefficients) deferred to S30.
**S29 — terminology scrub: "thought leadership" → "LinkedIn Studio" / neutral (added 2026-06-19,
operator-approved during S26).** The phrase must not appear anywhere in the plugin (it is named LinkedIn
Studio; see auto-memory `no-thought-leadership-phrase`). S26 fixed the 2 occurrences in
`commands/linkedin.md`; **~85 lines across ~45 files remain** (commands, agents, references — incl. the
filename `references/thought-leadership-angles.md` — skills, plus historical CHANGELOG/docs). Open scoping
questions for this pass: (a) user-facing surface only, or include historical CHANGELOG/docs too? (b) the
`thought-leadership-angles.md` rename is its own sub-step — every pointer to the filename must update with
it. Same tool-grounded discipline (read-and-show → grep-confirm → re-grep final); hardening-class.
**S30 — magnitude scrub: unsourced reach-coefficients → SSOT (surfaced during S27, re-scoped during S28,
2026-06-19).** A *different* class from S27's link/topic-relevance framing: discrete percentage penalties
with **no primary source in the SSOT**. **Incl. `linkedin-formats.md` Bucket-D lines left by S28**
(`:6` 47-50% reach-decline, `:7` 15%→31%, `:176` AI-comment 30%/55%, `:231`+`:279` hashtags 68%), plus
the other files — `68%` (5+ hashtags: `linkedin-growth-playbook:166`, `ab-testing-framework:66`),
`25%` (<1000 chars: `linkedin-growth-playbook:224`), `15-25%` (topic-gap: `glossary:235`), `30-50%`
(engagement-bait: `glossary:91`), `55%`/`45%` (AI-comment: `engagement-coach:195`, `firsthour:112` — SSOT
carries ~45% *correlational engagement gap* at medium confidence, not a 55% reach penalty), `25%/post`
(multiple posts/3h: `post-feedback-monitor:330`), poll-overuse (`poll-strategy-guide:20,:205`). **Leave
intact (officially confirmed, high confidence): engagement-pod + AI-slop "penalized" framing.** Full grep
catalog in `log.md` S27 entry, Bucket D. Same discipline; hardening-class.
Run after S26; order adjustable (S27 ✅ → S28 → S29 → S30). These edit already-hardened files surgically and
are hardening-class (commit local, no push).
## End-of-session ritual (every session — STATE.md handoff baked in)
1. `bash scripts/test-runner.sh``Failed: 0` + counts 29/19/25/6.
2. Terse, tool-grounded `log.md` line for the command (ONLY after verify): `PASS` / edit + `file:line`.
3. Commit own file(s) only, **LOCALLY** (`fix:` for command edits; explicit `git add <path>`, never `-u`/`-A`). **Do NOT push hardening work** — see Push policy.
4. **Overwrite STATE.md:** mark this command ✅; point at the NEXT single command + its class;
carry the method + discipline rules → ready for the next "Les STATE.md og følg instruksjonene".
5. (Optional) update Voyage `.session-state.local.json` next-label for `/trekcontinue` coherence.
## Defaults (adjustable between sessions)
1. **Log:** keep, terse, tool-grounded, after-verify only.
2. **Persona:** ICP author default; fresh-adopter for onboarding/first-post/setup; deviate explicitly elsewhere.
3. **Gate/commit:** once per session = per command.
## Push policy (operator rule, 2026-05-31)
Hardening work — *"det vi GJØR"*: command edits + `log.md` entries — is committed **locally
only, never pushed**. Only **method / process / tooling improvements** (this plan, a future
checker, STATE conventions) may be pushed, and only on **explicit operator OK**
(*"eventuelle forbedringer"*).
## Amendment policy
Improve the method/plan **between sessions** if a session surfaces a gap (operator-approved).
This plan is a living contract, not frozen.