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

141 lines
9.1 KiB
Markdown
Raw 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 — 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_topic` at `: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 `replaceField` was 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 ·
`/trekreview` → **ALLOW** → 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 · `/trekreview` → **ALLOW** → 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 ·
`/trekreview` → **ALLOW** → 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 · `/trekreview` → **ALLOW** → 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 · `/trekreview` → **ALLOW** → 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.