ktg-plugin-marketplace/plugins/linkedin-studio/docs/remediation/finish-plan.md
Kjell Tore Guttormsen baca30feb1 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

9.1 KiB
Raw Permalink Blame History

LinkedIn Studio — Finish Plan (S13S17)

Closes every remaining hole: the open S12 review findings + the gaps surfaced in the 2026-05-30 verified assessment. Brief-amendment to docs/remediation/brief.md (same project). Each session is one STATE.md-driven Voyage session, gated by test-runner.sh

  • node --test + /trekreview → push only on ALLOW (no more WARN-overrides).

Operator decisions (2026-05-30): build manual saves entry (S16); triage C13C46 (S17); fold into this project as a brief-amendment. Dependency: S14 (command set) precedes S15 (router tiering) — the router can't be tiered before the command set is final.

Sequence & dependency

S13 (close S12 WARN + $-class)  → ALLOW  [finishes the ORIGINAL brief]
        │
S14 (command rationalization)   → ALLOW  [sets the final command set]
        │
S15 (UX §6c — router on final set) → ALLOW
        │
S16 (saves manual entry)        → ALLOW
        │
S17 (C13C46 triage)            → ALLOW  [process complete]

S13 — Close the open S12 findings + the $-replacement class

Finishes the original brief; brings the existing scope to a clean ALLOW.

  • A1 (MINOR): hooks/scripts/state-updater.mjs:14-18 — convert replaceField to a replacement function ((_m) => \${field}: ${value}`) so the untrusted last_post_topicat:58` is inserted literally.
  • A2 (MAJOR): add assert.match(result.content, /^last_post_topic: "\$100 budget — \$& and \$1 rule"$/m) to the existing $-bearing test in state-updater.test.mjs (fails today, passes after A1).
  • A3 (systemic): audit every String.replace in hooks/scripts/*.mjs whose replacement is a string built from a function parameter that can carry user content (grep -nE '\.replace\([^,]+, *\'). Confirm replaceFieldwas the last such site. Add a **structural$-safety lint** (test-runner.sh` Section 12) that flags string-replacement sites whose value derives from an untrusted parameter — non-vacuity self-test + e2e mutation-proof, mirroring Sections 8/10/11.
  • Engine: inline (small, surgical).
  • Verify: A2 assert FAILS pre-A1, PASSES post-A1 · node --test green · test-runner.sh green (incl. new Section 12 self-test) · audit-grep returns 0 unsafe sites · /trekreviewALLOW → commit (review.md + S13) → push.

S14 — Command rationalization (re-opens the original command-surface Non-Goal)

Analysis → operator decision → execute. Nothing deleted without explicit per-command yes.

AMENDMENT — S14 reframed by operator decision (2026-05-30): merge/cut → journey layer. 14a's cold per-command review (command-rationalization.md) found zero redundancy — no command is a defensible merge/cut candidate (the two prior consolidations already removed the genuine overlaps). So instead of cutting, the operator chose to add a journey layer over the kept atomics. The build contract is journey-layer-design.md (this supersedes the 14b/14c "execute merges/cuts" bullets below — there are none to execute). Delivered: two new guided front-doors (/linkedin:create, /linkedin:measure) + the router re-tiered into five journeys (Start · Create · Engage · Measure · Grow) + onboarding/strategy elevated as the Start/Grow front-doors; the 27 atomic commands kept; 27 → 29 commands; v4.0.0 → v4.1.0 (minor/additive). The two 14a honesty nits real on verification were fixed (router lists firsthour; calendar cross-links to it); a third (a competitive 1K-gating claim) was withdrawn as unfounded. The gate is unchanged: test-runner.sh + node --test green → /trekreview ALLOW (no WARN-override) → commit own files → push.

  • 14a Analysis (no edits): cold per-command review of all 27 → docs/remediation/command-rationalization.md. Per command: purpose · overlap with siblings · invocation leverage (algorithmic + likely use) · recommendation keep / develop / merge→X / cut + rationale. Delegate the cold read to an Agent (Opus) for independence.
  • 14b Operator decision: present the doc; operator decides per command (AskUserQuestion batched). No mechanical deletion until approved.
  • 14c Execute approved: apply merges/cuts; for a merge, fold the source command's unique surface into the target and delete the source; update EXPECT_COMMANDS in test-runner.sh, all rosters (CLAUDE.md/README/SKILL.md/router), CHANGELOG, version bump if the surface count changes (breaking → minor/major per SemVer judgment).
  • Engine: Agent (14a) → inline (14c).
  • Verify: ls commands/*.md | wc -l == every declared count · lint count-guard green · three-doc synced · grep old count → 0 stale · /trekreviewALLOW → push.

S15 — UX finish (§6c), on the FINAL command set

  • B1 Onboarding inline: commands/onboarding.md — replace the "Run /linkedin:first-post" hand-off with the first-post steps embedded in the wizard, so the flow produces a draft post inline (no dead-end). Verify: a walkthrough yields a draft inside onboarding; 0 Run /linkedin:first-post dead-end strings. Scope guard (UI brief §12b): fix the dead-end ONLY — do NOT add extensibility/provider "seams" or progressive- disclosure config to onboarding; those are unresolved UI-brief decisions (keep onboarding lean per the persona "first value without forking").
  • B2 Router tiering: commands/linkedin.md — restructure into Primary (34: post/quick/newsletter/firsthour), Secondary (the rest of the final set), Locked ~1K (monetize/outreach/competitive, marked "unlocks later"). Verify: tier sections present; primary ≤4; locked commands flagged, not inline with primaries.
  • B3 Carousel full-deck clipboard: commands/carousel.md — assemble the entire deck (every slide's copy + the caption) into the clipboard payload, not just the caption. Verify: clipboard payload contains slide text; grep shows full-deck assembly before the clipboard-helper.mjs call.
  • Engine: inline.
  • Verify: all three grep/observation checks pass · test-runner.sh green · /trekreviewALLOW → push.

S16 — Saves manual-entry surface (operator-requested; lifts the original Non-Goal)

⚠️ CONFLICT — reconcile before building (UI brief §9b/M0). The UI brief makes it binding that all mutable personal data (assets/analytics/*, queue.json, *.local.md) moves OUT of the plugin tree into a stable per-user data dir, "in the v4.0.0 remediation or immediately after." S16 extends the analytics data model — if built against the current in-tree assets/analytics/ it gets reworked by M0. Decision needed: (a) do M0 first (insert as S15.5), then build S16 in the final location; or (b) defer S16 to ride along with M0/the UI build. Do NOT build S16 blind to M0.

  • Add a manual-entry path for saves (visible in native LinkedIn post analytics, count-only, ~Sept 2025) to the analytics data model (scripts/analytics/ types + import/report path), additive and backward-compatible. Re-rank the actionable-signal output to include saves where the CSV/manual data now contains it. Dwell stays explicitly unmeasurable (internal-only) — do not fabricate a dwell surface.
  • Engine: inline (+ analytics CLI knowledge; may need tsx types touch).
  • Verify: a report run with a manual saves value surfaces it without crashing; existing CSV-only data still works (backward-compat); honesty wording retained for dwell · node --test / analytics tests green · /trekreviewALLOW → push.

S17 — Triage the uncalibrated audit findings (C13C46)

  • Read the ~34 findings the audit never put through a second hostile pass (§10); for each: classify still-real / already-fixed / outdated-drop; close every still-real one; record the disposition in docs/remediation/c13-c46-triage.md. Delegate the cold read to an Agent (Opus).
  • Engine: Agent (triage) → inline (fixes).
  • Verify: every C13C46 finding has a recorded disposition; still-real ones grep-verify closed · test-runner.sh + node --test green · /trekreviewALLOW → push. Process complete.

Verification (whole plan)

  • Per session: bash scripts/test-runner.sh exit 0 · node --test hooks/scripts/__tests__/*.test.mjs all pass · /trekreview --project docs/remediation/ALLOW (not WARN) before push.
  • Plan-complete signal: all of S13S17 pushed on ALLOW; command-rationalization.md + c13-c46-triage.md committed; no open /trekreview finding; STATE.md "Aktiv oppgave" reads "remediering FERDIG — ren ALLOW".
  • Counts contract stays live: the lint's count-guard (19/27/25/6 today; 27 may change in S14) is updated in lockstep with any command merge/cut; grep for the prior count returns 0 stale hits.

Locked constraints (inherited from brief)

  • Opus on everything · no hidden costs (cost-warn /trekcontinue·/trekreview, standing yes) · three-doc rule · version-sync · bash 3.2 + Node-only hooks · push only to Forgejo · stage own files only · fix-in-next-session for any review finding.