diff --git a/plugins/linkedin-studio/commands/carousel.md b/plugins/linkedin-studio/commands/carousel.md index 7f784f3..1d1376a 100644 --- a/plugins/linkedin-studio/commands/carousel.md +++ b/plugins/linkedin-studio/commands/carousel.md @@ -188,11 +188,29 @@ Create one slide per page using the content above. Export as PDF and upload directly to LinkedIn. ``` -Auto-copy the carousel caption text to clipboard silently: -```bash -printf '%s' '' | node ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/clipboard-helper.mjs +**Assemble the entire deck** — every slide's copy (header + body, plus its visual note) followed by the caption — into ONE clipboard payload, so the whole carousel travels in a single copy, not just the caption. A carousel's deliverable is the slide text you paste into your design tool *and* the caption; copying only the caption left the bulk of the work uncopied. Build the payload like this: + ``` -Then confirm: "Caption copied to clipboard." +SLIDE 1 of [TOTAL] — [purpose] +[HEADER] +[BODY line 1] +[BODY line 2] +... +Visual: [visual note] + +SLIDE 2 of [TOTAL] — [purpose] +... + +— — — +CAPTION +[caption text] +``` + +Then auto-copy the full deck to clipboard silently: +```bash +printf '%s' '' | node ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/clipboard-helper.mjs +``` +Substitute `` with the assembled deck above — all slides' copy + the caption. Then confirm: "Full deck — [N] slides + caption — copied to clipboard." Offer refinement options as text (no interactive prompt): "Want to refine? Options: adjust slide text / change visual style / regenerate specific slide / different hook / ready for publishing." diff --git a/plugins/linkedin-studio/commands/onboarding.md b/plugins/linkedin-studio/commands/onboarding.md index 3116329..39bc712 100644 --- a/plugins/linkedin-studio/commands/onboarding.md +++ b/plugins/linkedin-studio/commands/onboarding.md @@ -166,28 +166,76 @@ After setup, recalculate and show updated score. ╚═══════════════════════════════════════════╝ ``` -Check `first_post_date` in state file: +Check `first_post_date` in state file. -**If null (no first post yet):** -- "You're ready to create your first post! This is the most important step — your first post doesn't need to be perfect, it needs to EXIST." -- Use AskUserQuestion: - 1. **Guided first post** (10 min) — Maximum hand-holding, simple format → routes to `/linkedin:first-post` workflow - 2. **Quick post** (5 min) — You already know what to say → routes to `/linkedin:quick` workflow - 3. **Not now** — I'll post later +**If first_post_date is set (returning user):** +- "You already have your first post (published [date]). Ready for your next one? Use `/linkedin:post` or `/linkedin:quick` whenever you are." Move to Phase 4. -**If first_post_date is set:** -- "You already have your first post (published [date]). Ready to create your next one?" -- Use AskUserQuestion: - 1. **Create a new post** → suggest `/linkedin:post` - 2. **Quick post** → suggest `/linkedin:quick` - 3. **Exit onboarding** +**If null (no first post yet) — draft it inline, right here:** -**If user chooses to post (option 1 or 2):** Don't invoke the sub-command directly — instead, tell them: -"Run `/linkedin:first-post` to start the guided first-post flow." -or -"Run `/linkedin:quick` to create a quick post." +"This is the most important step — your first post doesn't need to be perfect, it needs to EXIST. Let's write it now, together, in this flow." -This keeps the onboarding context clean and lets the post commands manage their own workflow. +Use AskUserQuestion: +1. **Write my first post now** — Draft it inline, ~5 minutes, ready to paste +2. **Not now** — I'll post later (you can use `/linkedin:post` or `/linkedin:quick` anytime) + +**If "Not now":** move to Phase 4 and mark the first post Pending. + +**If "Write my first post now":** walk through these steps **inline** — do NOT hand off to another command. The post is produced right here in the wizard. + +### 3.1 — Pick a topic + +Use AskUserQuestion: +1. **Something I learned recently** — a specific insight from your work +2. **A tool or approach I recommend** — something that made your work better +3. **An observation about my industry** — a pattern or trend you've noticed +4. **A question I'm genuinely curious about** — start a conversation + +Then ask: "Give me a sentence or two about what you have in mind." If expertise areas are set in the state file, steer the topic toward one of their pillars. + +### 3.2 — Write the post (3-line formula) + +Draft the post using the voice profile from Phase 2 (or the existing `assets/voice-samples/` profile): +- **Line 1 — Hook (under 140 chars):** specific to their experience, no generic opening +- **Line 2 — Context (1-3 sentences):** the what and why, kept tight +- **Line 3 — Insight + question:** their takeaway, ending on a genuine question that invites comments + +Target 150-500 characters (short posts perform well for new accounts). One point, not three. No external links in the post body. + +### 3.3 — Quick quality check + +Confirm 4 things before presenting: +- [ ] Hook works in 140 chars? +- [ ] ONE clear point (not three)? +- [ ] Ends with a question or invitation? +- [ ] Sounds like THEM (not corporate/AI)? + +Fix any miss before showing it. + +### 3.4 — Present and copy + +Show the post with its character count, the hook highlighted, and one alternative hook. Auto-copy the post text to clipboard silently: +```bash +printf '%s' '' | node ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/clipboard-helper.mjs +``` +Then say: "Post copied to clipboard. Go to linkedin.com, click 'Start a post', paste it, and hit Post." + +### 3.5 — Record it + +Update state deterministically (this sets `first_post_date` automatically when null): +```bash +node --input-type=module -e " +import { writeState, updatePostTracking } from '${CLAUDE_PLUGIN_ROOT}/hooks/scripts/state-updater.mjs'; +writeState(content => updatePostTracking(content, { + postDate: 'YYYY-MM-DD', + postTopic: 'topic_area', + hookText: 'Hook text here...', + charCount: NNNN, + format: 'post' +})); +" +``` +Replace the placeholders with the actual post data, then continue to Phase 4. ## Phase 4: Summary and Next Steps @@ -201,7 +249,7 @@ Show final status: ``` Profile: [Optimized / Skipped — run /linkedin:profile later] Personalization: [XX]% [↑ from YY% if improved] -First post: [Published DATE / Pending — run /linkedin:first-post] +First post: [Published DATE / Pending — create anytime with /linkedin:post or /linkedin:quick] ``` **What's next — your first week:** diff --git a/plugins/linkedin-studio/docs/remediation/review.md b/plugins/linkedin-studio/docs/remediation/review.md index d8c000a..b64d3a0 100644 --- a/plugins/linkedin-studio/docs/remediation/review.md +++ b/plugins/linkedin-studio/docs/remediation/review.md @@ -1,145 +1,142 @@ --- type: trekreview review_version: "1.0" -task: "S14 — journey layer over the LinkedIn Studio command surface (operator-reframed from merge/cut after 14a found zero redundancy)" +task: "S15 — UX finish §6c on the final 29-command set: B1 onboarding inline-draft + B3 carousel full-deck clipboard (B2 router-tiering already delivered in S14)" slug: remediation project_dir: docs/remediation/ brief_path: docs/remediation/brief.md -scope_sha_start: 431a893 -scope_sha_end: 431a893 -reviewed_files_count: 15 +scope_sha_start: baca30f +scope_sha_end: baca30f +reviewed_files_count: 2 verdict: ALLOW mode: default -effort: high +effort: standard profile: premium findings: [] --- -# Review — linkedin-studio S14 (journey layer; merge/cut → add a layer) +# Review — linkedin-studio S15 (UX finish §6c: B1 onboarding inline + B3 carousel full-deck) ## Executive Summary -**Verdict: ALLOW** (post-remediation) — 0 BLOCKER, 0 MAJOR, 0 MINOR, 0 SUGGESTION open. -Two independent reviewers (brief-conformance, code-correctness) ran COLD, high-effort, -without cross-feeding, on the as-delivered uncommitted working tree (HEAD `431a893` + -the S14 delta). **Each returned 2 MAJOR findings; deduped to 3 distinct issues.** All -three were **remediated in-session** with deterministic re-verification; the final -state that pushes is clean. +**Verdict: ALLOW** for S15's delivered scope — 0 BLOCKER, 0 MAJOR, 0 MINOR, 0 SUGGESTION +**attributable to the S15 diff**. Two independent reviewers (brief-conformance, +code-correctness) ran COLD, without cross-feeding, on the as-delivered uncommitted +working tree (HEAD `baca30f` + the S15 delta = two command files). -S14 was **reframed by operator decision (2026-05-30)**: 14a's cold per-command review -(`command-rationalization.md`) found **zero redundancy** across the 27 commands (no -defensible merge/cut), so instead of cutting, a **journey layer** was added over the -kept atomics. Build contract: `journey-layer-design.md`. Delivered: two new -delegate-only 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; 27 → 29 commands; v4.0.0 → v4.1.0 -(minor/additive). Two of 14a's honesty nits (router lists `firsthour`; `calendar` -cross-links to it) were real and fixed; a third (a `competitive` 1K-gating claim) was -withdrawn as unfounded on verification. +- **brief-conformance-reviewer:** 0 findings. B1 and B3 both trace to delivered code; the + binding §12b scope-guard is honored (no extensibility / provider-seam / progressive- + disclosure / detect-and-offer language entered the diff); no version/count drift + (19/29/25/6 intact, v4.1.0 untouched); no files outside the two in scope. +- **code-correctness-reviewer:** 1 MAJOR — but **pre-existing and outside S15's scope** + (see Findings). The S15 changes themselves are correct: branches all terminate, the + state-updater + clipboard snippets mirror the established `first-post.md` pattern + exactly, `${CLAUDE_PLUGIN_ROOT}` is the established path var, and the larger carousel + clipboard payload introduces no new failure mode over the uniform plugin pattern. -**What both reviewers confirmed conformant + correct** (the bulk of the delivery): -the two front-doors are delegate-only (no `Write`; route to the contracted targets); -the router re-tier preserves **reachability for all 29 commands** (no atomic dropped; -`post-feedback-monitor` still mentioned in prose, not orphaned); `EXPECT_COMMANDS` -27→29 + `## Commands (29)` + `ls`=29 agree; version 4.1.0 is consistent across -plugin.json / README version badge / CLAUDE.md header / CHANGELOG / root docs; SemVer -framing is honest (additive, nothing falsely "breaking", nothing removed/renamed); -out-of-scope discipline held (no atomic folded; `multiplatform`-develop only recorded, -not built; S16/S17/UI-brief-M0 untouched). +S15 = **B1 + B3 only**. B2 (router tiering) was already delivered in S14 (the finish-plan +S15 body still lists B2, but the S14 amendment + the session-state label supersede it); +its absence here is correct, not a gap. ## Coverage -Scope: HEAD `431a893` (S13's commit) + the **uncommitted S14 working-tree delta** +Scope: HEAD `baca30f` (S14's commit) + the **uncommitted S15 working-tree delta** (annotated `[uncommitted]` — a brief-level contract; the brief's Assumptions allow -uncommitted review). 15 files = the operator's own changes; the 3 untracked -not-mine files (`docs/linkedin-studio-persona-brief.md`, `…-ui-brief.md`, -`docs/voyage-build/progress.json`) are explicitly excluded from scope and from the -commit. **No silent skips.** +uncommitted review). 2 files = the operator's own changes. The 3 untracked not-mine files +(`docs/linkedin-studio-persona-brief.md`, `…-ui-brief.md`, `docs/voyage-build/progress.json`) +are explicitly excluded from scope and from the commit. **No silent skips.** | Treatment | Count | Notes | |-----------|-------|-------| -| `deep-review` | 6 | `commands/create.md` `[new]`, `commands/measure.md` `[new]`, `commands/linkedin.md` (router re-tier), `commands/calendar.md`, `scripts/test-runner.sh`, `README.md` | -| `summary-only` | 9 | plugin `CLAUDE.md`/`CHANGELOG.md`/`plugin.json`/`commands/onboarding.md`; root `CLAUDE.md`/`README.md`; `docs/remediation/{finish-plan.md, command-rationalization.md, journey-layer-design.md}` | +| `deep-review` | 0 | neither file is under `hooks/**` / `auth/**` / `crypto/**` / `**/security/**` | +| `summary-only` | 2 | `commands/onboarding.md` (Phase 3 rewrite + 1 Phase-4 summary line), `commands/carousel.md` (Step 6 clipboard) | | `skip` | 0 | no lockfiles / svg / generated / dist | -**Cross-cutting execution criteria (run by orchestrator):** -`scripts/test-runner.sh` → **74 passed / 0 failed / 0 warnings**, exit 0 (was 71; -+2 the two new commands' frontmatter checks, +1 a new README commands-badge guard, -−1 net rounding of the count-loop — all green). `node --test …/*.test.mjs` → **98/98** -(no hook logic changed). `ls commands/*.md` → **29**. +**B1/B3 acceptance checks (orchestrator-run greps):** +- B1: `grep -E "[Rr]un \`?/linkedin:first-post" commands/onboarding.md` → **0 dead-end strings**; + inline draft steps 3.1–3.5 present (topic → 3-line draft → QC → present+clipboard → state-update that sets `first_post_date`). +- B3: full-deck assembly block at `carousel.md:191` precedes the `clipboard-helper.mjs` call at `:211`; payload contains slide text; caption-only `` removed. + +**Cross-cutting execution criteria:** `scripts/test-runner.sh` → **74 passed / 0 failed / +0 warnings**, exit 0. `node --test hooks/scripts/__tests__/*.test.mjs` → **98/98** (no hook +logic changed). `ls commands/*.md` → **29** (no surface-count change; no version bump — +consistent with S11–S13 precedent: within-scope refinements stay at the current version). ## Findings -**3 distinct MAJOR findings were raised by the independent reviewers and ALL closed -in-session.** No finding remains open; the trailing JSON `findings` is empty because -the pushed state carries none. +**0 findings attributable to the S15 diff.** One MAJOR was raised by code-correctness; on +inspection it is **pre-existing and out of S15's scope**, and is routed to the next session +per the finish-plan's locked constraint ("fix-in-next-session for any review finding") and +the operator rule "ekte design-funn → neste sesjon" (in-session fix is reserved for the +session's *own* lockstep misses). It is recorded below and propagated to STATE.md — **not +dropped, not silenced.** -### [MAJOR — RESOLVED] README commands badge stale at `commands-27` -*Raised by both reviewers (`COUNT_DRIFT` / `SUCCESS_CRITERION_UNMET`), `README.md:11`.* -The release reconciled the version badge, intro prose, CLAUDE.md header, `EXPECT_COMMANDS`, -and rosters to 29, but the shields **commands** badge still read `commands-27-green`. It -slipped both the human pass and the gate (the version-consistency grep checks only the -*version* badge; the design-doc verification grep searched `"27 command"` with a space, -which cannot match `commands-27-green`). -**Resolution:** `README.md:11` → `commands-29-green`; **and** a new -`test-runner.sh` Section-2 guard now asserts the `badge/commands-${EXPECT_COMMANDS}-` -shields badge matches the contract (closing the class, not just the line — the -count-badge analogue of the existing version-badge check). Re-verified: lint 74/0/0; -`grep commands-27` → 0 hits. +### [MAJOR — DEFERRED to next session] onboarding Phase 2 file-saves need `Write`, not in `allowed-tools` -### [MAJOR — RESOLVED] Router self-contradicted on `competitive` gating -*Raised by code-correctness (`INTERNAL_CONTRADICTION`), `commands/linkedin.md:140`.* -The Grow journey **table** marked `competitive` "Any phase" and the gating-rule note -said it is **not** gated, but the **"Ask the User"** menu line still rendered -`competitive ⚿` (the file's own ~1K soft-gate marker). An interim erroneous gating -(added then reverted in the same pass) had been reverted in the table + the gating note -+ the design doc, but the menu line was missed — the exact inconsistency the release -claims to fix. -**Resolution:** `commands/linkedin.md` Ask-the-User Grow line → `competitive` (no ⚿); -re-verified `grep "competitive ⚿"` → 0 hits across commands + the design doc. The router -now states competitive ungated consistently in the table, the note, and the menu. +*Raised by code-correctness (`PLAN_EXECUTE_DRIFT`), `commands/onboarding.md:142` (and `:157`).* -### [MAJOR — RESOLVED] STATE.md binding count block stale at 27 / v4.0.0 -*Raised by brief-conformance (`SUCCESS_CRITERION_UNMET`), `STATE.md:46`.* -The design-doc lockstep enumerates the STATE.md count block as a surface to update -(27→29, v4.1.0). `STATE.md` is **gitignored** (not part of the pushed commit, so not a -push-blocking three-doc violation), but it is a contracted lockstep item and would -mislead the next session. -**Resolution:** updated to 29 / v4.1.0 as part of the session-close STATE.md overwrite -(which also advances the pointer to S15). Not in the pushed artifact (gitignored). +`onboarding.md` frontmatter grants `allowed-tools: [Read, Bash, AskUserQuestion]` — no +`Write`. **Phase 2** instructs "Save the responses to `assets/voice-samples/authentic-voice-samples.md`" +(`:142`, with a REPLACE-vs-append rule) and "Save to `config/user-profile.local.md`" (`:157`). +Persisting those profile files is a file write; the plugin's accepted pattern is the `Write` +tool, and the sibling `first-post.md` (same voice-save) declares `Write` (`first-post.md:9-14`). +With only Read/Bash/AskUserQuestion the agent cannot honor the two "Save to …" instructions +(the only scripted Bash writes are the `node -e` state and clipboard snippets). **Real defect.** + +**Why DEFERRED, not fixed in S15:** +- **Pre-existing.** The gap exists identically at HEAD `baca30f` *before* the S15 edits — + `git show HEAD:…/onboarding.md` shows `allowed-tools: [Read, Bash, AskUserQuestion]` already. +- **Out of S15's diff scope.** The S15 hunks are `@@ -166` (Phase 3) and `@@ -201` (Phase 4 + summary line); lines 142/157 (Phase 2) are untouched. S15's *own* inline steps (3.1–3.5) + stay within Read/Bash/AskUserQuestion and need no `Write` (3.4 clipboard = Bash, 3.5 + state-update = Bash `node -e`). +- **Not introduced or worsened by S15.** The reviewer's "now owns the save flow" framing does + not hold on inspection: Phase 2's saves are reached identically before and after S15, and + the removed `/linkedin:first-post` hand-off was about *post creation* (Phase 3), not Phase 2's + profile saves. S15 added no save-flow that needs `Write`. +- **Operator governance.** Fixing a Phase-2 tool-contract bug inside an S15 (B1+B3) push would + be exactly the "rydd opp i pre-existing som del av en bugfix" / scope-expansion the operator's + rules forbid; the documented default for out-of-scope items is **defer** (record, route to + next session) — not silently fix, not silently drop. + +**Recommended action (next session):** add `Write` to `onboarding.md`'s `allowed-tools` +(matching `first-post.md:9-14`), or replace the two "Save to …" instructions with a `node -e` +Bash write so the steps stay within the declared Bash grant. Natural home: fold into S16 (which +already opens the onboarding/saves surface) or the S17 triage pass. ## Remediation Summary -**Gate: ALLOW (post-remediation).** Two independent high-effort reviewers found 3 -distinct MAJORs — all stale-count / incomplete-revert completion-misses of THIS -session's own committed lockstep, none a design flaw. All three were closed in-session -with deterministic re-verification (lint 74/0/0; targeted greps → 0 residual hits); a -new lint guard prevents the badge regression class from recurring. No BLOCKER; no -design or correctness defect in the journey layer itself; reachability for all 29 -commands preserved; SemVer honest. The pushed state is clean. +**Gate: ALLOW** for S15's delivered scope (B1 + B3). Both reviewers confirm the two changes +are conformant (brief) and correct (code); the binding §12b scope-guard held; no version/count +drift; the inline first-post flow and the full-deck clipboard both function within the existing +tool grants and established patterns. The single MAJOR is a **pre-existing, out-of-scope** +tool-contract gap in Phase 2 that S15 neither introduced nor worsened; per the operator's +explicit "ekte design-funn → neste sesjon" rule it is **deferred and recorded**, not fixed +as scope creep. This is a genuine ALLOW of the scoped delivery — **not** a WARN-override (there +is no open finding *against* S15's diff). -Decision rationale for in-session fix (vs `feedback_trekreview_always_last`'s -fix-in-next-session): the three findings are mechanical completions of the current -task's own lockstep — #1 and #2 would have failed the design doc's own Verification -grep, #3 is an incomplete revert — i.e. *unfinished scope of S14*, not deliberation- -needing review findings. Shipping a README badge that reads "27" and a router that -self-contradicts on gating would be dishonest to the release's own claims. Closing them -now reaches a genuine ALLOW (not a WARN-override). - -Per Handover 6, this `review.md` is consumable by `/trekplan --brief …`; with an ALLOW -verdict and no open findings, no follow-up plan is required — S14 may commit + push, and -the finish-plan continues at S15. +Per Handover 6, this `review.md` is consumable by `/trekplan --brief …`. With an ALLOW verdict +on the delivered scope and the one deferred finding routed forward, S15 may commit + push, and +the finish-plan continues at S16 (with the deferred `Write` gap reconciled there or in S17). ```json { "verdict": "ALLOW", - "scope": { "sha_start": "431a893", "sha_end": "431a893", "reviewed_files_count": 15, "uncommitted_delta": true }, + "verdict_scope": "S15 delivered changes (B1 + B3); 2 files", + "scope": { "sha_start": "baca30f", "sha_end": "baca30f", "reviewed_files_count": 2, "uncommitted_delta": true }, "counts": { "BLOCKER": 0, "MAJOR": 0, "MINOR": 0, "SUGGESTION": 0 }, "findings": [], - "resolved_in_session": [ - { "severity": "MAJOR", "title": "README commands badge stale at 27", "file": "README.md", "line": 11, "rule_key": "COUNT_DRIFT", "resolution": "badge -> commands-29-green + new test-runner.sh commands-badge guard" }, - { "severity": "MAJOR", "title": "Router self-contradicts on competitive gating", "file": "commands/linkedin.md", "line": 140, "rule_key": "INTERNAL_CONTRADICTION", "resolution": "Ask-the-User Grow line competitive ⚿ -> competitive; 0 residual hits" }, - { "severity": "MAJOR", "title": "STATE.md binding count block stale at 27 / v4.0.0", "file": "STATE.md", "line": 46, "rule_key": "SUCCESS_CRITERION_UNMET", "resolution": "updated to 29 / v4.1.0 in session-close overwrite (gitignored, not pushed)" } + "deferred_findings": [ + { + "severity": "MAJOR", + "title": "onboarding Phase 2 file-saves need Write, not in allowed-tools", + "file": "commands/onboarding.md", + "line": 142, + "rule_key": "PLAN_EXECUTE_DRIFT", + "status": "pre-existing, out of S15 scope (Phase 2; S15 diff = Phase 3 + Phase 4 line)", + "routed_to": "next session (S16 onboarding/saves surface, or S17 triage)", + "recommended_action": "add Write to onboarding.md allowed-tools (match first-post.md:9-14), or replace the two 'Save to ...' instructions with a node -e Bash write" + } ], "dropped_findings": [] }