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>
141 lines
9.1 KiB
Markdown
141 lines
9.1 KiB
Markdown
# LinkedIn Studio — Finish Plan (S13–S17)
|
||
|
||
> 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 C13–C46
|
||
> (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 (C13–C46 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** (3–4:
|
||
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 (C13–C46)
|
||
- 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 C13–C46 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 S13–S17 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.
|