fix(linkedin-studio): S3 harden react — Step-6 buzzword gate + WebFetch-failure fallback (both paths)

S3 close-out (3 edits / 1 file · lint 81/0 · counts 29/19/26/6):
- react.md:131 Step 6: add no-buzzword checklist line (was missing; banned list = content rule #4; covers Step 6b via "Same as Step 6")
- react.md:60 Step 2: WebFetch-failure fallback (paywall/JS/YouTube-no-transcript -> ask user to paste or pick another; never fabricate)
- react.md:194 Step 2b: same fallback pointer for the multi-URL comparison path

Axis-c (buzzword) + axis-d (graceful degradation) closed on both paths; axes a/b PASS. Shared scorecard already clean post-S2. Local-only per push policy.
This commit is contained in:
Kjell Tore Guttormsen 2026-06-18 13:44:00 +02:00
commit b8c2155003
2 changed files with 79 additions and 0 deletions

View file

@ -57,6 +57,8 @@ Use WebFetch to extract the content from the URL. Ask WebFetch to extract:
- Data points or statistics mentioned
- The "so what" — why this matters
**If WebFetch can't extract usable content** (paywall, JS-heavy page, or a YouTube video with no accessible transcript — all accepted inputs WebFetch may not resolve): say so plainly, then ask the user to paste the key points (or the transcript) or choose another URL. Never fabricate a reaction from the URL string or title alone — the post must stand on real source content.
## Step 3: Classify Content Type
Determine the content type to select the right template:
@ -126,6 +128,7 @@ Verify against quality rules:
- [ ] Character count: 1,200-1,800
- [ ] Topic aligns with expertise areas
- [ ] CTA invites discussion, not just "What do you think?"
- [ ] No corporate buzzwords (leverage, synergy, paradigm shift, thought leader, disruptive, value proposition, ecosystem, holistic approach)
### De-AI / Differentiation Gate
@ -188,6 +191,8 @@ Use WebFetch on each URL. For each, extract:
- **Stance/argument** — what position does the author take?
- **Data points** — any statistics or evidence cited
**If a source can't be extracted** (paywall / JS-heavy / no transcript): apply the Step 2 fallback — drop it if 2+ usable sources remain, otherwise ask the user to paste it or choose another. Never fabricate from a title or URL alone.
### Step 3b: Synthesis Analysis
Analyze across all sources:

View file

@ -312,3 +312,77 @@ CTA without bait, de-AI substance signals).
- 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,2001,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,2001,800**), two paths — single-URL (Steps 18) +
comparison/multi-URL (Steps 1b8b). Must honor the content bar mechanically (hook **110140**,
length **1,2001,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 <public-sector-AI news URL>`.
- **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 110140 ✓) → **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:122128`, 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 (`:144148`, 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 (`:135137`). 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.
---