# LinkedIn Studio — Command Hardening Log > Per-command audit trail for the hardening phase (`docs/hardening/brief.md` + > `plan.md`). One **anchored** entry per command surface. The operator's parallel > live-testing cross-references every change here. > > **Entry contract (SC-A…SC-E).** Each entry begins with the UNIQUE anchored header > `### /linkedin:` (coverage greps `^### /linkedin:`, > never the bare word). Every entry carries: **INTENT** · **SIMULATE** (persona + the > CONCRETE before-output + friction log) · **EVALUATE** (4 axes, with the per-type > **mechanical predicate** — never "N/A → judgment") · **HARDEN** (surgical diff + > concrete after-output, or "no edit — passes") · **VERIFY** (lint `Failed: 0` + counts > + the failing axis now passes). The cold `/trekreview` reviewer adjudicates every > hardened command's before/after — the author does not self-certify. > > **Mechanical-predicate classes:** *post-emitting* (hook 110–140 · length band · > no body link · no banned buzzword · topic→5 pillars) · *routing* (every emitted > target resolves to a real `commands/.md`) · *analytics* (graceful-degradation > present + saves/dwell honesty intact) · *guided/stateful* (primary promised artifact > actually produced; promised `subagent_type` targets resolve). > > **Stopping rule (anti-gold-plating):** harden until every axis returns pass OR a > recorded deferral; no NICE-only polish beyond axis-pass. > > **Method discipline (learned the hard way in the S1 calibration — three stumbles).** > Write the HARDEN / after-output section **only from the applied + re-grepped diff**, and > assert a gap **only after reading the actual file line**. Every number (char counts, > edit counts) must come from a tool, not from memory. The S1 calibration produced three > assert-before-verify errors — (1) a non-existent inline buzzword list in `quick`, (2) a > non-existent "unused Task" in `quick`, (3) `onboarding`/`first-post` edits asserted as > landed when the Edits had failed on wrong strings, plus a false "25 commands → stale" > claim (the file already said 29). All were caught by failed Edits / empty greps / the > git status, and corrected here. This is exactly the failure mode the independent > `/trekreview` oracle exists to catch — verify-before-assert is part of the method, not optional. --- ## Session 1 — Method calibration (`quick`) + Start journey > S1 status: calibration corrected + method locked (operator, 2026-05-31). onboarding / > first-post / setup follow below under the same entry shape. Field-notes inbox: > **absent** at S1 start → graceful no-op (SC-I). ### /linkedin:quick — 5-minute 3-line post from a topic, ≤1 question, clipboard-ready **INTENT.** `quick` is the Create journey's speed path: a topic in → a publishable 150–500-char post out in ~5 minutes, max one question, auto-copied to clipboard. It must honor the content-quality bar mechanically (hook **110–140**, length **150–500**, no body links, no banned buzzword, topic maps to one of the 5 pillars, exactly one CTA) and the algorithm bar (hook decisive before the mobile fold; links to first comment not body; native; a CTA that invites comments without manufactured engagement-bait). Its defining constraint is *speed* — "skip interrogation, generate immediately." **SIMULATE.** - **Persona (ICP):** the author — 1048 followers, "Validation", 5 pillars; voice = direct/technical/low-formality, NO/EN mix, short sentences, contrarian openings (grounded in the state file + the gitignored `authentic-voice-samples.local.md` the command reads at runtime). `recent_posts: []` → no rotation conflict. *Fresh-adopter path (no voice samples) reasoned, not separately re-drafted: `quick` reads voice from SKILL.md and falls back to defaults; the personalization score is hidden < 3 posts and the voice guardian is suppressed < 5 samples — degrades cleanly, no block, no dead-end.* - **Invocation:** `/linkedin:quick AI-kodeassistenter i offentlig sektor` (pillars "AI-rådgivning i offentlig sektor" / "Claude Code / agentisk koding"). - **Walk:** topic from `$ARGUMENTS` (no question) → load voice + pillars → infer type (HOT TAKE — contrarian) → 3-line formula → auto-CTA → quality check → de-AI/differentiation gate (skip unless commodity) → clipboard → present. - **CONCRETE before-output** (produced under `quick.md` **as written**, pre-fix): > Alle vil ha AI-kodeassistenter i staten. Men hvem tar ansvaret når koden tar feil i > produksjon? > > Verktøyet skriver koden på sekunder. Ansvaret for at den er riktig flyttes ikke — det > blir ditt, raskere enn før. > > Hvordan kvalitetssikrer dere AI-generert kode hos dere? *(Pillar: Claude Code / agentisk koding · type: HOT TAKE · **hook = 95 chars** (node-verified) · body has 0 links / 0 buzzwords.)* - **Friction log:** the **95-char hook passes `quick.md`'s own checks** — Step 2 said "Hook (under 140 characters)" and the Step 5 checklist asked "Hook works in 140 chars?", **both upper-bound only** — yet it breaks the canonical **110** floor (the PreToolUse gate: "under 110: wasting prime real estate"). Same one-sidedness on length ("Under 500?" vs the 150–500 band). The Step 5 checklist also had **no buzzword line**, and a quick post is auto-copied to clipboard (no file Write), so the PreToolUse *file* gate may not fire — the checklist is the load-bearing surface. No dead-ends. **EVALUATE (4 axes).** - **(a) intention fidelity — PASS.** One-question speed path, 3-line formula, 8 templates, auto-CTA, clipboard, why-hook/reach tips, `/linkedin:post` upgrade path — delivers the promise. - **(b) algorithm bar — PASS.** No body link (links → first comment); native; CTA invites comments (comments > reactions) without bait; de-AI gate cites the confirmed low-substance down-rank. Consistent with `references/algorithm-signals-reference.md`. - **(c) MECHANICAL predicate (post-emitting) — GAP → fixed.** no body link ✓ · topic→pillar ✓ · **hook band ✗** (spec + checklist enforced ≤140, omitted the 110 floor; the 95-char before-hook is the live proof) · **length band ✗** (checklist enforced <500, omitted the 150 floor) · **buzzword ✗** (no checklist line; clipboard path bypasses the file gate). - **(d) agent-wiring + graceful degradation — PASS (verified).** `quick` **conditionally** delegates to `differentiation-checker` via `Task` (`quick.md:151`, `subagent_type: linkedin-studio:differentiation-checker`) — only when the take is commodity, preserving the 5-minute promise by default. `Task` in `allowed-tools` is therefore *used*, not vestigial. Fresh-adopter degradation works (defaults; guardian suppressed < 5 samples). **HARDEN (surgical — 2 edits, axis-c only — both grep-confirmed landed).** - `commands/quick.md:68` — `**Line 1: Hook (under 140 characters)**` → `**Line 1: Hook (110-140 characters)**`. - `commands/quick.md` Step 5 checklist — hook check `"Hook works in 140 chars?"` → `"Hook in the 110-140 band (not just under 140)?"`; length check `"Under 500 characters?"` → `"In the 150-500 band (not just under 500)?"`; **added** `"No corporate buzzwords?"`; count `**All 6 = Yes?**` → `**All 7 = Yes?**`. - **Deferrals:** none (the earlier "incomplete buzzword list" and "unused Task" findings were file-misreads, struck — see Method discipline). - **CONCRETE after-output** (under the hardened spec — hook expanded into the 110–140 band): > Alle vil ha AI-kodeassistenter i staten. Få spør hvem som tar ansvaret når assistenten > foreslår noe ingen kan forklare etterpå. > > Verktøyet skriver koden på sekunder. Ansvaret for at den er riktig flyttes ikke — det > blir ditt, raskere enn før. > > Hvordan kvalitetssikrer dere AI-generert kode hos dere? *(**hook = 127 chars** (node-verified, in band) · 0 body links · 0 buzzwords · same pillar.)* **VERIFY.** - `bash scripts/test-runner.sh` → `Failed: 0` + exit 0 + counts 29/19/25/6 unchanged (re-run AFTER the real edits; recorded at the session gate). - Before/after delta: axis (c) failing sub-checks (hook 110 floor, length 150 floor, buzzword) now pass — the 95-char before-hook slips `quick.md`'s old checks; the hardened checklist catches it and the after-hook is 127 chars, buzzword-clean. The two edits are the cause. - Disposition: **HARDENED** (2 edits, axis-c) · 0 deferrals · axes a/b/d PASS. --- ### /linkedin:onboarding — zero→published first post as one guided wizard (Start front-door) **INTENT.** Multi-phase wizard taking a brand-new user from zero to a published first post as one cohesive flow (profile → personalization → first-post), so they don't navigate the full command surface alone. Primary artifacts: a saved profile + a drafted first post (the S15-B1 inline-draft). Guided/stateful; inlines rather than spawning subagents. **SIMULATE.** - **Persona (fresh adopter — the right persona here):** just installed, no profile, no voice samples, `recent_posts: []`. - **Invocation:** `/linkedin:onboarding`. - **Walk:** Phase 0 already-onboarded check → Phase 1 profile/topic-relevance checklist → Phase 2 personalization (voice + user profile, or defaults when < 3 posts) → Phase 3 first post (3.1 topic → 3.2 3-line draft → 3.3 quality check → 3.4 present+clipboard → 3.5 record) → Phase 4 summary. **CONCRETE artifact produced:** a refined first-post inline draft (S15-B1 path — **spot-confirmed still delivers:** Phase 3.2 "Draft the post… Line 1/2/3", 3.4 "present and copy"). - **Friction log:** Phase 3.2 said "Line 1 — Hook (**under 140** chars)" and the Phase 3.3 check asked "Hook works in **140** chars?" — **both upper-bound only**, so a 95-char hook would pass while breaking the 110 floor. (Phase 3.2 line 204 already states "150-500 characters" + "No external links in the post body", so length + links were **NOT** gaps — only the hook floor.) The description + Phase-4 tip hardcode "29 commands" — **accurate today, not stale** (an earlier "25 commands" claim was a misread, struck). **EVALUATE (4 axes).** - **(a) intention fidelity — PASS.** Cohesive zero→post wizard; S15-B1 inline draft holds (Phase 3). - **(b) algorithm bar — GAP → fixed.** Phase 3 emitted a post but enforced only the hook upper bound; now the full 110–140 band (length 150–500 + no-body-links were already present). - **(c) MECHANICAL predicate (guided/stateful) — PASS; one post-emitting sub-check fixed.** Primary artifact (first-post draft) produced ✓; the one-sided hook bound in Phase 3.2 + 3.3 closed. - **(d) agent-wiring + graceful degradation — PASS.** No `Task` in `allowed-tools` — onboarding inlines every step (Phase 2 "delegate to setup" = inline its logic; commands aren't subagents, so no broken `subagent_type`). Built for the no-profile path; degrades cleanly. **HARDEN (surgical — 2 edits, hook floor — both grep-confirmed landed).** - `commands/onboarding.md:200` — `**Line 1 — Hook (under 140 chars):**` → `**Line 1 — Hook (110-140 chars):**`. - `commands/onboarding.md:209` (Phase 3.3 check) — `Hook works in 140 chars?` → `Hook in the 110-140 band (not just under 140)?`. - **Deferred (NICE-only, stopping rule):** the hardcoded "29 commands" (description + Phase-4 tip) is correct now; making it count-free is drift-proofing, not an axis fix → recorded, not edited. **VERIFY.** lint `Failed: 0` + counts unchanged (recorded at gate); S15-B1 inline-draft spot-confirmed; hook floor now enforced in Phase 3.2 + 3.3. Disposition: **HARDENED** (2 edits) · 1 recorded deferral. --- ### /linkedin:first-post — zero→published in <10 min, maximum hand-holding **INTENT.** First-post accelerator: from "never posted" to "just published" in <10 min, breaking the blank-page barrier. Produces one published first post. Guided/stateful; inlines. **SIMULATE.** - **Persona (fresh adopter):** no profile (Step 2 offers a voice quick-setup or 5-question calibration; proceeds either way — momentum over completeness). - **Invocation:** `/linkedin:first-post`. - **Walk:** Step 1 welcome → Step 2 voice setup (samples or 5 Qs; graceful if none) → Step 3 topic (5 angles) → Step 4 write (3-line formula) → Step 5 simplified quality check → Step 6 present + clipboard → Step 7 record → Step 8 first-hour engagement guidance. - **CONCRETE before (the gap):** Step 4 "Line 1: Hook (**under 140** characters)" and the Step 5 check "Hook works in **140** chars?" were **both upper-bound only** — a 95-char hook would pass. (Step 4 already states "Target: 150-500 characters" and "No external links in the post body", so length + links were **NOT** gaps — only the hook floor.) - **Friction log:** the hook bound was one-sided in Step 4 + Step 5; length + no-links already covered. **EVALUATE (4 axes).** - **(a) intention fidelity — PASS.** Delivers zero→published with hand-holding; first-hour guidance present. - **(b) algorithm bar — PASS.** First-hour engagement window cited; no-body-links already in Step 4 tips. - **(c) MECHANICAL predicate (post-emitting) — GAP → fixed.** Hook bound was upper-only in Step 4 + Step 5; now the full 110–140 band (length 150–500 + no-body-links already present). - **(d) agent-wiring + graceful degradation — PASS.** No `Task`; inlines; no-profile/no-samples path graceful. Differentiation-checker deliberately skipped (a first post optimizes for momentum). **HARDEN (surgical — 2 edits, hook floor — both grep-confirmed landed).** - `commands/first-post.md:101` — `**Line 1: Hook (under 140 characters)**` → `**Line 1: Hook (110-140 characters)**`. - `commands/first-post.md:125` (Step 5 check) — `Hook works in 140 chars?` → `Hook in the 110-140 band (not just under 140)?`. - **Deferrals:** none. **VERIFY.** lint `Failed: 0` + counts unchanged; Step 4 ↔ Step 5 hook bound now consistent (both 110-140). Disposition: **HARDENED** (2 edits) · 0 deferrals. --- ### /linkedin:setup — guided personalization (5 pillars + voice profile + prefs) **INTENT.** Build the user's voice profile, expertise pillars, and content preferences into the state/asset files so every post sounds like them. Primary artifact: a populated voice profile + populated asset templates (8-category personalization score). Guided/stateful; delegates voice to an agent. **SIMULATE.** - **Persona (fresh adopter):** no existing data (all templates at placeholder). - **Invocation:** `/linkedin:setup`. - **Walk:** Step 0 calculate score → Step 1 dashboard → Step 2 choose what to set up → Step 3a–3f sub-workflows (voice / case study / framework / post analysis / demographics / user profile) → Step 4 recalculate → Step 5 continue or exit. **CONCRETE artifact:** e.g. Step 3a writes a real voice profile to `assets/voice-samples/authentic-voice-samples.md` (placeholder sentinel removed). - **Friction log:** none — no dead-ends, no ambiguous steps. **EVALUATE (4 axes).** - **(a) intention fidelity — PASS.** Delivers the personalization the description promises (score + 6 sub-flows). - **(b) algorithm bar — PASS (n/a-direct).** Emits no post; the expertise/pillar capture is the topic-relevance foundation the bar depends on. - **(c) MECHANICAL predicate (guided/stateful) — PASS.** Primary artifact (populated profile/assets) produced ✓; the promised `subagent_type: linkedin-studio:voice-trainer` (`setup.md:86`) **resolves** to a real `agents/voice-trainer.md` (verified). - **(d) agent-wiring + graceful degradation — PASS.** voice-trainer invoked via `Task` (correct namespaced type); the placeholder-sentinel logic is explicit; samples optional → degrades if none. **HARDEN.** **No edit — passes all four axes.** (Demonstrates a legitimate zero-edit pass; per the plan, "command file modified" is not a coverage predicate — the `log.md` entry + `/trekreview` are.) **VERIFY.** lint `Failed: 0` + counts unchanged; voice-trainer target resolves. Disposition: **PASS, no edit** · 0 deferrals. --- ## Session 2 — Create I (`post`) > S2 status: the v1 autonomous attempt fabricated and was reverted (→ v2 method; see `plan.md` > header). This is the v2 re-run: parked at step 5 across a `/clear`, re-grounded from STATE.md's > two P4 findings against the actual file lines before talking (jerndisiplin: assert-after-read). > Field-notes inbox **absent** at S2 → graceful no-op (SC-I). ### /linkedin:post — interactive full-workflow post (angle→write→quality→refine→publish), 1200–1800 chars **INTENT.** `post` is the Create journey's full path: from raw material (data / article / experience / observation / opinion / question) to a publishable 1,200–1,800-char thought-leadership post via an 8-step workflow — angle → format → structure+write → quality check → present → refine → pre-publish → deterministic state update. Post-emitting, so it must honor the content bar mechanically (hook **110–140**, length **1,200–1,800**, no body links, no banned buzzword, topic→5 pillars) and the algorithm bar (hook decisive before the mobile fold, links→first comment, native, comment-inviting CTA without bait, de-AI substance signals). **SIMULATE.** - **Persona (ICP):** the author — 1048 followers, "Validation", 5 pillars; direct/technical NO/EN voice. - **Invocation:** `/linkedin:post about AI-kodeassistenter i offentlig sektor`. - **Walk:** Step 0 load state/SKILL → Step 1 type=Opinion → Step 2 angle (contrarian) → Step 3 medium text → Step 4 structure (hook 110–140 ✓ internally) → **Step 5 quality check (the gap)** → Step 6 present+clipboard → Step 7 refine → Step 8 pre-publish + state. - **CONCRETE friction (spec-level, the live proof — anchored, not a generated post):** at **Step 5** the quality checklist (`post.md:127–135`, 9 items) had **no buzzword line**, so a draft opening *"How to leverage AI to drive synergy in the public sector…"* would pass Step 5 unflagged — the banned-list (content rule #4, `CLAUDE.md:124`) was never enforced at the gate. Step 5 also routes to `quality-scorecard.md` (`post.md:125`), which **self-contradicted on the hook bound**: l.32 "Works in **140** characters" + l.114 "Hook works in **140** chars?" (upper-only) vs l.128 "**110-140**" — the same one-sided bound S1 fixed in the command files, surviving in the shared asset. - **Friction log:** `post.md` itself is internally consistent on the hook (110–140 at l.100/113/127); the only command-side gap is the missing Step-5 buzzword line. The shared scorecard carried both gaps (buzzword absent + two one-sided hook bounds). Scorecard is shared by 8 surfaces (`pipeline · video · post · react · batch` + 3 skills) — fixing it is high-leverage, operator-approved. **EVALUATE (4 axes).** - **(a) intention fidelity — PASS.** Full 8-step workflow delivers the description's promise. - **(b) algorithm bar — PASS.** No body link (`post.md:133/176`); comment-inviting CTA; de-AI gate cites the confirmed reach-suppression (`post.md:137–141`). Consistent with `algorithm-signals-reference.md`. - **(c) MECHANICAL predicate (post-emitting) — GAP → fixed.** hook band ✓ · length band ✓ · no-body-link ✓ · topic→pillars ✓ · subagent resolves ✓ · **buzzword ✗** (Step-5 checklist had no line; grep NONE). Shared scorecard added two sub-gaps: hook one-sided at l.32 + l.114. - **(d) agent-wiring + graceful degradation — PASS.** `differentiation-checker` (`post.md:141`, conditional) + `content-optimizer` (`post.md:166`, conditional) both resolve, both correctly `subagent_type: linkedin-studio:`-namespaced. Voice→defaults; fresh-adopter path graceful. **HARDEN (surgical — 4 edits across 2 files, all grep-confirmed landed).** - `commands/post.md:134` — **added** Step-5 checklist line: `- [ ] No corporate buzzwords (leverage, synergy, paradigm shift, thought leader, disruptive, value proposition, ecosystem, holistic approach)`. - `assets/checklists/quality-scorecard.md:32` — `Works in 140 characters` → `Works in 110-140 characters` (Notes `Mobile threshold` → `Mobile band (floor + ceiling)`). - `assets/checklists/quality-scorecard.md:114` — `Hook works in 140 chars?` → `Hook works in 110-140 chars?`. - `assets/checklists/quality-scorecard.md:132` — **added** Pre-Publish Basic Checklist line: `- [ ] No corporate buzzwords (…)` (same banned list). - **Deferrals:** none. **Scope note:** the 3 scorecard edits improve all 8 consumers; only `post` is hardened this session — `react/batch/video/pipeline` get their own EVALUATE in S3/S5/S6/S8. **VERIFY.** - `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`; counts **29/19/26/6** unchanged. - Anti-check grep: no one-sided bare `140 char` hook bound remains in the scorecard (`110-140` at l.32/114/128). Buzzword line present in both files (`post.md:134`, `quality-scorecard.md:132`). - Before/after delta: axis (c) buzzword sub-check now enforced at the load-bearing Step-5 gate; the scorecard hook self-contradiction is resolved (all three mentions banded). The 4 edits are the cause. - Disposition: **HARDENED** (4 edits, axis-c + shared-asset consistency) · 0 deferrals · axes a/b/d PASS. --- ## Session 3 — Create I (`react`) > S3 status: re-grounded STATE's S3 predicate against the actual `react.md` lines BEFORE talk > (jerndisiplin: assert-after-read). Field-notes inbox **absent** at S3 → graceful no-op (SC-I). > All `file:line` below are tool-grounded against the FINAL (post-edit) file. ### /linkedin:react — URL-to-post pipeline (single + comparison), your-take-as-hook, 1,200–1,800 chars **INTENT.** `react` is the Create journey's **URL→post path**: fetch external content (article / news / research / YouTube), extract the key points, pick an angle, and draft in the author's voice. Post-emitting (medium **1,200–1,800**), two paths — single-URL (Steps 1–8) + comparison/multi-URL (Steps 1b–8b). Must honor the content bar mechanically (hook **110–140**, length **1,200–1,800**, no body link, no banned buzzword, topic→5 pillars) and the algorithm bar (URL→first comment not body, post stands alone, *your reaction is the hook* not a summary, comment-inviting CTA, de-AI substance). **Distinct from `post`/`quick`:** the value-add is YOUR perspective *extending* the source, not echoing it — the commodity-reaction guard (differentiation-checker). **SIMULATE.** - **Persona (ICP):** the author — 1048 followers, "Validation", 5 pillars; direct/technical NO/EN voice. - **Invocation:** `/linkedin:react `. - **Walk:** Step 0 load → Step 1 single URL → Step 2 WebFetch extract → Step 3 classify (Product Launch) → Step 4 angle (First-Take Analysis) → Step 5 draft (hook 110–140 ✓) → **Step 6 quality check (the gap)** → Step 7 present+clipboard → Step 8 state. - **CONCRETE friction (spec-level, anchored — not a generated post):** at **Step 6** the quality checklist (pre-edit `react.md:122–128`, 7 items) enforced hook/links/standalone/voice/charcount/ topic/CTA but had **no buzzword line**, so a draft opening *"This launch lets agencies leverage AI to unlock synergy across the value proposition…"* passes unflagged — the banned-list (content rule #4, `CLAUDE.md:124`) was never enforced at the gate. **Step 6b** (`:237` "Same as Step 6, plus") inherits the hole. `react` auto-copies to clipboard (`:144–148`, no file Write) → the PreToolUse **file** gate may not fire → Step 6 is the load-bearing surface. **Same gap S2 fixed in `post`.** - **Second friction (axis d, URL-pipeline-specific):** `grep` for fetch-failure/paywall/transcript/ fallback → **NONE**. Step 2 (`:54`) "Use WebFetch to extract" had **no fallback** when extraction yields nothing (paywall / JS-heavy SPA / **YouTube-no-transcript** — an explicitly accepted input, `:37/:71`); Step 2b (`:185`) same. Risk: proceed on a thin/empty extraction or fabricate from the URL string. **EVALUATE (4 axes).** - **(a) intention fidelity — PASS.** Both paths, angle selection, your-take-not-summary, commodity guard, clipboard, URL→first comment — delivers the description's promise. - **(b) algorithm bar — PASS.** No body link (`react.md:115/125/235`); URL→first comment (`:116/:144/:253`); stands alone (`:118/:126`); take-as-hook (`:101/:118`); de-AI gate bans mechanical engagement-bait (`:135–137`). Consistent with `references/algorithm-signals-reference.md`. - **(c) MECHANICAL predicate (post-emitting) — GAP → fixed.** hook band ✓ (`:101/:124/:221`) · length band ✓ (`:119/:128/:239`) · no-body-link ✓ (`:115/:125/:235`) · topic→pillars ✓ (`:129`) · subagent resolves ✓ (`:137` → `agents/differentiation-checker.md`) · **buzzword ✗** (grep NONE; no inline line in Step 6/6b). - **(d) agent-wiring + graceful degradation — wiring PASS; degradation GAP → fixed.** `differentiation-checker` resolves, namespaced, conditional (`:137`). **Graceful degradation:** no WebFetch-failure/empty-extraction fallback on EITHER path (grep NONE) — YouTube-no-transcript the sharpest, since it is a stated accepted input. - **Shared asset:** `quality-scorecard.md` already clean post-S2 (`110-140` at `:32/:114/:128` + buzzword `:132`) — `react`'s referenced asset (`react.md:270`) is good; the gap was `react`'s OWN inline Step 6. **HARDEN (surgical — 3 edits, 1 file, all grep-confirmed landed).** - `commands/react.md:131` — **added** Step-6 checklist line: `- [ ] No corporate buzzwords (leverage, synergy, paradigm shift, thought leader, disruptive, value proposition, ecosystem, holistic approach)`. Covers Step 6b via "Same as Step 6". - `commands/react.md:60` — **added** Step-2 WebFetch fallback: *"If WebFetch can't extract usable content (paywall / JS-heavy / YouTube-no-transcript): say so, ask the user to paste the key points or choose another URL; never fabricate from the URL string or title."* - `commands/react.md:194` — **added** Step-2b pointer: *"If a source can't be extracted: apply the Step 2 fallback — drop it if 2+ usable sources remain, else ask the user; never fabricate."* - **Deferrals:** none. - **CONCRETE after:** the "leverage…synergy…value proposition" draft now trips the Step-6 buzzword line; a YouTube-no-transcript invocation now degrades to "paste the key points / pick another URL" instead of a fabricated reaction. **VERIFY.** - `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`; counts **29/19/26/6** unchanged (`ls commands/*.md | wc -l` == 29; `git diff --stat` = `react.md` only, +5 insertions, 0 deletions). - Before/after delta: axis (c) buzzword sub-check now enforced at the load-bearing Step-6 gate (both paths via "Same as Step 6"); axis (d) WebFetch-failure now has a sensible fallback on both paths. The 3 edits are the cause. - Disposition: **HARDENED** (3 edits, axis-c + axis-d) · 0 deferrals · axes a/b PASS. ---