Append the S29c verification record to docs/hardening/log.md (anchored
"### S29c terminology-scrub" entry, matching S29a/S29b format): the 34-edit replacement map
across 15 reference files, the S29e filename lock (thought-leadership-angles.md ->
content-angles.md) with the canonical file's in-file title/headers scrubbed now, the
KEPT-BY-DESIGN items (ironic quote, TL;DW false positive), and the verify block (FORM A in
references/ = only the kept ironic quote; canonical in-file FORM A = NONE; FORM B unchanged 4;
filename pointers unchanged 3; gate 81/0/0; counts 29/19/26/6 unchanged). Documents fix commit
25b356f.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qgzo6rxthw7KuxHjn5vyE
2090 lines
157 KiB
Markdown
2090 lines
157 KiB
Markdown
# 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:<command-name> — <one-line intent>` (coverage greps `^### /linkedin:<name>`,
|
||
> 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/<x>.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 <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 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.
|
||
|
||
---
|
||
|
||
## Session 4 — Create III (`multiplatform`)
|
||
|
||
> S4 status: re-grounded the proposed predicates against the actual `multiplatform.md` lines
|
||
> BEFORE talk (jerndisiplin: assert-after-read). Field-notes inbox **absent** at S4 → graceful
|
||
> no-op (SC-I). All `file:line` below are tool-grounded against the FINAL (post-edit) file.
|
||
> **Class call (operator-agreed):** NOT post-emitting — output is Twitter/slides/YouTube, so the
|
||
> LinkedIn content predicates (hook 110–140, length band, no-body-link, topic→5 pillars) do **not**
|
||
> apply per platform; imposing them here would be the exact category error the Create-III brief warns
|
||
> against. Tested as **guided/cross-format with a routing sub-predicate**.
|
||
|
||
### /linkedin:multiplatform — adapt existing LinkedIn content to short-/cross-format (Twitter thread · slides · YouTube script)
|
||
|
||
**INTENT.** `multiplatform` is the Create journey's **cross-format adapter**: take existing LinkedIn
|
||
content and re-shape it for other platforms (Twitter/X thread, presentation slides, YouTube script) —
|
||
*write once, publish everywhere*. It explicitly does **not** own long-form (newsletters / blog / essays
|
||
→ `/linkedin:newsletter`, the single long-form entry point). It is an *adapter*, not a post-generator:
|
||
the value-add is adaptation fidelity (each platform's own rules) + correct routing of out-of-scope
|
||
(long-form) requests, not LinkedIn-feed quality gating.
|
||
|
||
**SIMULATE.**
|
||
- **Persona (ICP):** the author — a finished LinkedIn post in drafts, wants to reuse it as a Twitter thread.
|
||
- **Invocation:** `/linkedin:multiplatform` → pick "Twitter/X thread".
|
||
- **Walk:** Step 0 (`:21`) load source from `drafts/` + state → Step 1 (`:31`) AskUserQuestion pick
|
||
Twitter → Twitter template (`:53`, hook ≤**280** + 🧵 + one-idea tweets + CTA) → Step 2 (`:115`) save
|
||
`drafts/multiplatform/twitter-[slug].md` (`:118`) + clipboard, "Copied" (`:123`). **Promised artifact
|
||
produced.**
|
||
- **CONCRETE friction (axis d, adapter-specific — anchored, not a generated post):** `grep` for an
|
||
empty-source / fabrication guard in Step 0 → only matched l.23 "Ask the user to provide … or read
|
||
from drafts" — **no fallback** for the case where drafts is empty/absent, state has no recent posts,
|
||
AND the user pasted nothing. Risk: the model **fabricates a LinkedIn post to adapt** rather than
|
||
stopping. (Same failure class as S3's WebFetch-no-extraction gap.)
|
||
- **Second friction (axis c, voice hygiene):** the banned-buzzword list (content rule #4) is
|
||
*platform-agnostic voice hygiene*, not a char rule — and adaptation **rewrites** the source
|
||
(condense the hook to 280, expand to a script), which can introduce buzzwords absent from the
|
||
clean source. Pre-edit the file mentioned buzzwords **nowhere** (full read). Operator-judged in
|
||
scope (light line, not a full gate).
|
||
|
||
**EVALUATE (4 axes).**
|
||
- **(a) intention fidelity — PASS.** Three adapters (Twitter/slides/YouTube), source-loading,
|
||
save + clipboard, long-form correctly delegated — delivers the description's promise.
|
||
- **(b) algorithm/platform bar — PASS.** Per-platform rules correct, **no LinkedIn-rule leak**:
|
||
Twitter "280 chars max" (`:53`); **no** `110`/`140` hook band anywhere in the file (grep NONE —
|
||
the desired result for a non-LinkedIn surface). Long-form routing intact (see (c)).
|
||
- **(c) MECHANICAL predicate (routing sub-predicate) — PASS; voice-hygiene sub-gap fixed.** Every
|
||
emitted `/linkedin:newsletter` (`:6/:34/:36/:132`) resolves to a real `commands/newsletter.md`
|
||
(verified, ls) ✓; both reference files resolve — `references/linkedin-formats.md` (`:128`) +
|
||
`engagement-frameworks.md` (`:129`) (verified, ls) ✓. **Buzzword voice-hygiene line absent** →
|
||
added (`:45`).
|
||
- **(d) agent-wiring + graceful degradation — wiring N/A; degradation GAP → fixed.** No subagents
|
||
invoked (`grep subagent_type` → NONE — nothing to resolve). **Graceful degradation:** no
|
||
empty-source/fabrication guard in Step 0 (grep matched only the weak "ask … or read from drafts")
|
||
→ added a stop-and-ask-never-fabricate fallback (`:28`).
|
||
|
||
**HARDEN (surgical — 2 edits, 1 file, both grep-confirmed landed).**
|
||
- `commands/multiplatform.md:28` — **added** Step-0 empty-source fallback: *"If none of these yields
|
||
source content (empty/absent drafts, no recent posts, and nothing pasted), **stop and ask the user
|
||
to paste the LinkedIn post to adapt** — never fabricate source content to adapt."*
|
||
- `commands/multiplatform.md:45` — **added** an "Adaptation Templates" voice-hygiene blockquote:
|
||
*"adaptation rewrites the source — keep the adapted copy free of the banned corporate buzzwords
|
||
(content rule #4: leverage, synergy, paradigm shift, thought leader, disruptive, value proposition,
|
||
ecosystem, holistic approach) on every platform; don't let the rewrite introduce them."*
|
||
- **Deferrals:** none. **Scope note (flagged, not touched):** Step 2's "Copied to clipboard." (`:123`)
|
||
is unconditional, but `clipboard-helper.mjs` is shared, tested infra used identically by ~9 content
|
||
commands — re-gating it here would be scope-creep into a shared pattern (surgical discipline).
|
||
|
||
**VERIFY.**
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`; counts **29/19/26/6**
|
||
unchanged (`ls commands/*.md|wc -l` == 29, `ls agents/*.md|wc -l` == 19; `git diff --stat` =
|
||
`multiplatform.md` only, +9 insertions, 0 deletions).
|
||
- Before/after delta: axis (d) empty-source now degrades to "paste the post / never fabricate"
|
||
instead of an invented source; axis (c) the rewrite is now bound by the banned-buzzword list on
|
||
every platform. The 2 edits are the cause.
|
||
- Disposition: **HARDENED** (2 edits, axis-c + axis-d) · 0 deferrals · axes a/b PASS · class
|
||
confirmed non-post-emitting (no LinkedIn predicate imposed).
|
||
|
||
---
|
||
|
||
## Session 5 — Create II (`carousel`)
|
||
|
||
> S5 status: re-grounded the proposed predicates against the actual `carousel.md` lines BEFORE
|
||
> talk (jerndisiplin: assert-after-read). Field-notes inbox **absent** at S5 → graceful no-op (SC-I).
|
||
> All `file:line` below are tool-grounded against the FINAL (post-edit) file.
|
||
> **Class call (operator-agreed):** hybrid **guided/stateful + post-emitting on the caption** — the
|
||
> deck/images are the guided artifact, but the caption IS LinkedIn feed text, so the post predicates
|
||
> apply *to the caption* (not to the slides). Slides are bound by mobile-readability rules, not the
|
||
> feed bar.
|
||
|
||
### /linkedin:carousel — template→slide-by-slide deck + caption + optional mcp-image, full deck to clipboard
|
||
|
||
**INTENT.** `carousel` is the Create journey's **document-post path**: from a chosen template
|
||
(How-To / Listicle / Story / Comparison / Framework) + a topic, produce a publish-ready carousel —
|
||
structured slide-by-slide copy (header + body + visual note per slide) + a feed caption + optional
|
||
slide images (mcp-image), all assembled into ONE clipboard payload. Carousels/documents are the
|
||
top organic format (~7%; `references/algorithm-signals-reference.md`). Hybrid class: the deck is the
|
||
guided/stateful artifact; the **caption is post-emitting** and must honor the content bar mechanically
|
||
on the caption (hook **110–140**, no banned buzzword, topic→5 pillars) + graceful mcp-image degradation.
|
||
|
||
**SIMULATE.**
|
||
- **Persona (ICP):** the author — 1048 followers, "Validation", 5 pillars; direct/technical NO/EN voice.
|
||
Builds a **How-To Guide** carousel on a pillar topic; tested both mcp-image-available and -unavailable.
|
||
- **Invocation:** `/linkedin:carousel` → pick template 1 (How-To) → topic + audience.
|
||
- **Walk:** Step 0 (`:24`) load state/voice/recent → Step 1 (`:30`) AskUserQuestion template → Step 2
|
||
(`:50`) topic+audience → Step 3 (`:58`) slide copy → Step 4 (`:94`) caption (hook 110–140 `:97`, band
|
||
300–500 `:103`) → **Step 5 quality + De-AI gate (the gap)** → Step 5.5 (`:127`) mcp-image (graceful)
|
||
→ Step 6 (`:156`) present full deck → clipboard one-payload (`:191–213`) → Step 7 (`:218`) state.
|
||
- **CONCRETE friction (spec-level, anchored — not a generated deck):** at **Step 5** the De-AI /
|
||
Differentiation gate (pre-edit `:121–123`) confirmed *personal substance · original thinking ·
|
||
concrete specifics · genuine voice* and banned mechanical engagement-bait, but had **no buzzword
|
||
line** — `grep -niE "buzzword|leverage|synergy|paradigm"` over the whole file = **NONE**. So a
|
||
caption opening *"How to leverage AI to drive synergy across the public-sector ecosystem…"* would
|
||
pass Step 5 unflagged; content rule #4 (`CLAUDE.md:124`) was never enforced at the caption gate.
|
||
Same failure class S2/S3 fixed in `post`/`react`. (Caption generation in Step 4 is exactly where a
|
||
rewrite can slip buzzwords in.)
|
||
- **No dead-ends:** all 5 referenced files exist (`agents/differentiation-checker.md`,
|
||
`assets/templates/carousel-templates.md`, `references/algorithm-signals-reference.md`,
|
||
`hooks/scripts/clipboard-helper.mjs`, `hooks/scripts/state-updater.mjs` — `[ -f ]` all FINNES).
|
||
|
||
**EVALUATE (4 axes).**
|
||
- **(a) intention fidelity — PASS.** Template→slides→caption→full-deck-clipboard-as-one-payload
|
||
(`:191–213`, explicitly "the whole carousel, not just the caption") + state — delivers the promise.
|
||
- **(b) algorithm bar — PASS.** ~7% (`:37`) matches the ref exactly (`algorithm-signals-reference.md:55`,
|
||
"Documents / carousels | Top organic format (~7%…)"); "document post" framing correct (`:170`, LinkedIn
|
||
removed native carousels Dec 2023); caption hook 110–140 (`:97`).
|
||
- **(c) MECHANICAL predicate (post-emitting, on the caption) — GAP → fixed.** hook band ✓ (`:97`) ·
|
||
caption band 300–500 ✓ (`:103`) · topic→pillars ✓ (`:26/:56`) · subagent resolves ✓ (`:125`) ·
|
||
**buzzword ✗** (grep NONE; De-AI gate had no rule-#4 line). *no-body-link not imposed: a carousel
|
||
caption is not a typical link site and the command never asks for links — minor, deferred (matches
|
||
S2–S4 which left it untouched).*
|
||
- **(d) agent-wiring + graceful degradation — PASS.** `differentiation-checker` resolves, namespaced,
|
||
conditional (`:125`, `subagent_type: linkedin-studio:differentiation-checker`). **mcp-image graceful
|
||
degradation solid:** "If mcp-image is unavailable or fails, skip… degrades to text-only" (`:129`),
|
||
per-call failure logged + continue, all-fail → text-only Step-6 design-guide branch (`:154`, `:176`).
|
||
`state-updater` exports `updatePostTracking` (`:55`) + `writeState` (`:346`) — Step-7 wiring correct.
|
||
|
||
**HARDEN (surgical — 1 edit, 1 file, grep-confirmed landed).**
|
||
- `commands/carousel.md:123` — **extended** the Step-5 De-AI gate with content rule #4:
|
||
`**Strip corporate buzzwords** from the caption (Content Quality Rule #4: leverage, synergy,
|
||
paradigm shift, thought leader, disruptive, value proposition, ecosystem, holistic approach) —
|
||
caption generation can slip them in, and they trip the same low-substance signal.`
|
||
- **Deferrals:** *no-body-link on the caption* (low risk, command never emits links; S2–S4 precedent)
|
||
+ two cosmetic notes recorded, not edited (slide line-count 5 vs 7 between `:73–77` and `:87`; both
|
||
are NICE-only, below the stopping-rule's axis-pass bar).
|
||
- **CONCRETE after:** the "leverage…synergy…ecosystem" caption now trips the Step-5 buzzword line
|
||
before the deck is presented.
|
||
|
||
**VERIFY.**
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`; counts **29/19/26/6**
|
||
unchanged (`ls commands/*.md|wc -l` == 29, `ls agents/*.md|wc -l` == 19; `git diff --stat` =
|
||
`carousel.md` only, 1 line changed). Only `.md` touched → hooks/analytics `node --test` not needed.
|
||
- Before/after delta: axis (c) buzzword sub-check now enforced at the load-bearing Step-5 caption gate.
|
||
The 1 edit is the cause.
|
||
- Disposition: **HARDENED** (1 edit, axis-c) · 2 recorded deferrals (no-body-link + cosmetic) · axes
|
||
a/b/d PASS.
|
||
|
||
---
|
||
|
||
## Session 6 — Create II (`video`)
|
||
|
||
> S6 status: re-grounded the proposed predicates against the actual `video.md` lines BEFORE
|
||
> talk (jerndisiplin: assert-after-read). Field-notes inbox **absent** at S6 → graceful no-op (SC-I).
|
||
> All `file:line` below are tool-grounded against the FINAL (post-edit) file.
|
||
> **Class call (operator-agreed):** hybrid **guided/stateful + post-emitting on the post caption** —
|
||
> the script / captions / thumbnail are the guided artifact (bound by video rules: muted-autoplay,
|
||
> 2.5 wps, completion-rate), but the **POST CAPTION (200–400) IS LinkedIn feed text**, so the post
|
||
> predicates apply *to the caption* (not the script). **Hook 110–140 deliberately NOT imposed on the
|
||
> caption (operator-agreed):** the caption accompanies the video; the primary hook discipline is the
|
||
> muted-autoplay opening (`:100`), not a standalone-post hook — imposing it would be S4's category error.
|
||
|
||
### /linkedin:video — type→length→angle→video-scripter script + captions + 200–400 post caption + first comment
|
||
|
||
**INTENT.** `video` is the Create journey's **video-script path**: from type (talking-head / screen-rec /
|
||
slideshow / convert-a-text-post) + length (30/60/90/120 s) produce a production-ready script via the
|
||
`video-scripter` agent — timing markers, visual/energy cues, captions, thumbnail, a 200–400-char post
|
||
caption, and a first comment — then save to drafts + deterministic state update. Hybrid class: the script
|
||
is the guided/stateful artifact; the **post caption is post-emitting** and must honor the content bar
|
||
mechanically on the caption (no banned buzzword, topic→5 pillars, no body link) + 2.5-wps pacing +
|
||
graceful degradation when voice samples are absent.
|
||
|
||
**SIMULATE.**
|
||
- **Persona (ICP):** the author — 1048 followers, "Validation", 5 pillars; direct/technical NO/EN voice.
|
||
Builds a 60-s talking-head on a pillar topic. **+ fresh-adopter path** (no voice samples) reasoned to
|
||
exercise graceful degradation (relevant here because of the pre-edit "REQUIRED before scripting").
|
||
- **Invocation:** `/linkedin:video` → talking-head → 60 s → pillar topic.
|
||
- **Walk:** Step 0 load state/voice/refs → Step 1 type → Step 2 length (2.5-wps budgets) → Step 3 angle
|
||
(topic→pillars) → Step 4 `video-scripter` generates → **Step 5 quality + De-AI gate (the gap)** →
|
||
Step 6 present + clipboard post caption → Step 7 refine → Step 8 save + state.
|
||
- **CONCRETE friction (spec-level, anchored — not a generated script):**
|
||
- **(axis c)** at **Step 5** the only buzzword line (`:106`, pre-edit) sat under the **"Natural Speech
|
||
Test"** = the *script's spoken language*; the **caption** De-AI gate (`:129`) carried **no rule-#4
|
||
line** (`grep buzzword` → single hit at `:106`). A caption opening *"How to leverage AI to drive
|
||
synergy across the public-sector ecosystem…"* would pass the caption gate unflagged. Same failure
|
||
class S2/S3/S5 fixed in `post`/`react`/`carousel` (the caption is re-shaped from a text idea in
|
||
Step 4 — exactly where a rewrite slips buzzwords in).
|
||
- **(axis d)** `:42` (pre-edit) "voice-samples … **(REQUIRED before scripting)**" + `:90` "Match voice
|
||
against voice-samples/" had **no fallback** for a fresh adopter with an empty/absent samples dir —
|
||
"REQUIRED" reads as a hard block / dead-end. Same failure class as S3 (WebFetch-no-extraction) / S4
|
||
(empty-source).
|
||
|
||
**EVALUATE (4 axes).**
|
||
- **(a) intention fidelity — PASS.** 8-step workflow delivers the description's promise (script + pacing +
|
||
cues + captions + thumbnail + 200–400 caption + first comment + refine + save + state).
|
||
- **(b) algorithm bar — PASS.** Muted-autoplay correctly de-mythologized (`:100`, "three-second hook =
|
||
folklore; LinkedIn's only official 3 s = minimum length"); captions = the enforceable spec (`:120`);
|
||
60 s sweet spot; 2.5-wps budgets exact (`:64/:66`, 75/30 = 150/60 = 225/90 = 300/120 = 2.5). *Honesty
|
||
note (deferred, operator-agreed):* `:69` "LinkedIn requires 30 % completion or **zero distribution**"
|
||
is more absolute than the declared SSOT `algorithm-signals-reference.md:56` (no 30 %, confidence
|
||
*medium*); it is consistent with `linkedin-formats.md` (cited at `:39`, l.58). Inter-reference tension,
|
||
NOT a video-command gap — deferred (brief Non-Goal: no re-litigation of the bar).
|
||
- **(c) MECHANICAL predicate (post-emitting, on the caption) — GAP → fixed.** no-body-link ✓ (`:125`) ·
|
||
topic→pillars ✓ (`:124` + Step 3) · caption band 200–400 ✓ (`:121`) · subagent resolves ✓ (`:83/:131`) ·
|
||
**buzzword ✗** (grep: single hit `:106`, under Natural Speech = script not caption; caption De-AI gate
|
||
had no rule-#4 line). **hook 110–140 deliberately not imposed** on the accompanying caption (class
|
||
boundary, operator-agreed; primary hook = muted-autoplay `:100`).
|
||
- **(d) agent-wiring + graceful degradation — wiring PASS; degradation GAP → fixed.** `video-scripter`
|
||
(`:83`) + `differentiation-checker` (`:131`, conditional) both resolve to real `agents/*.md`, both
|
||
`subagent_type: linkedin-studio:`-namespaced. **Graceful degradation:** the "REQUIRED before scripting"
|
||
voice-samples dead-end closed → fresh-adopter fallback to SKILL.md/defaults. (mcp-image n/a — video
|
||
generates no images.)
|
||
|
||
**HARDEN (surgical — 2 edits, 1 file, both grep-confirmed landed).**
|
||
- `commands/video.md:129` — **extended** the Step-5 De-AI caption gate with Content Quality Rule #4:
|
||
*"**Strip corporate buzzwords** from the post caption (… leverage, synergy, paradigm shift, thought
|
||
leader, disruptive, value proposition, ecosystem, holistic approach) — re-shaping a text idea into a
|
||
video caption can slip them in, and they trip the same low-substance signal."*
|
||
- `commands/video.md:42 + :45` — softened "(REQUIRED before scripting)" → "(read before scripting)" and
|
||
**added** a Step-0 graceful-degradation line: *"if `voice-samples/` is empty or absent, don't block —
|
||
fall back to the voice settings in SKILL.md (or neutral defaults) … the voice-guardian is suppressed
|
||
under 5 samples, so there is no hard gate to satisfy."*
|
||
- **Deferrals (2, recorded):** (1) hook 110–140 on the caption — deliberate class boundary, not a gap
|
||
(operator-agreed); (2) `:69` "zero distribution" honesty — inter-reference tension, brief Non-Goal (no
|
||
re-litigation of the algorithm bar); leave to a future reference-consistency pass if desired.
|
||
- **CONCRETE after:** the "leverage…synergy…ecosystem" caption now trips the Step-5 buzzword line before
|
||
the script is presented; a fresh adopter with no voice samples now degrades to SKILL.md/defaults instead
|
||
of hitting a "REQUIRED" dead-end.
|
||
|
||
**VERIFY.**
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`, exit 0; counts **29/19/26/6**
|
||
unchanged (`ls commands/*.md|wc -l` == 29, `ls agents/*.md|wc -l` == 19; `git diff --stat` =
|
||
`video.md` only, +4/−2). Only `.md` touched → hooks/analytics `node --test` not needed.
|
||
- Before/after delta: axis (c) buzzword sub-check now enforced at the load-bearing Step-5 caption gate;
|
||
axis (d) the voice-samples dead-end now degrades gracefully. The 2 edits are the cause.
|
||
- Disposition: **HARDENED** (2 edits, axis-c + axis-d) · 2 recorded deferrals (caption-hook boundary +
|
||
:69 honesty) · axes a/b PASS.
|
||
|
||
---
|
||
|
||
## Session 7 — Create II (`batch`)
|
||
|
||
> S7 status: re-grounded the proposed predicates against the actual `batch.md` lines BEFORE
|
||
> talk (jerndisiplin: assert-after-read). Field-notes inbox **absent** at S7 → graceful no-op (SC-I).
|
||
> All `file:line` below are tool-grounded against the FINAL (post-edit) file.
|
||
> **Class call (operator-agreed):** hybrid **guided/stateful + post-emitting per post** — each of
|
||
> the 3–5 drafts IS a feed post, so the post predicates apply *per post*; the orchestration around
|
||
> them (state-load, agent-delegation, scheduling, queue, `.ics`) is the guided/stateful layer.
|
||
|
||
### /linkedin:batch — one theme/pillar → 3–5 mixed-format feed posts, scheduled + queued + .ics
|
||
|
||
**INTENT.** `batch` is the Create journey's **volume path**: from one theme or pillar produce a full
|
||
week (3–5 posts) with varied angles + formats, each scheduled, queued (so it shows in
|
||
`/linkedin:calendar` + session-start reminders), and exported to a `.ics`. It is an **orchestrator** —
|
||
delegates angle discovery to `trend-spotter`, the balanced batch plan to `content-planner`, and owns
|
||
scheduling/queue/ical itself. Post-emitting per post, so each draft must honor the content bar
|
||
mechanically (hook **110–140**, format-appropriate length band, no body link, no banned buzzword,
|
||
topic→5 pillars); guided/stateful for the orchestration (subagent targets + node helpers resolve;
|
||
graceful degradation).
|
||
|
||
**SIMULATE.**
|
||
- **Persona (ICP):** the author — 1048 followers, "Validation", 5 pillars; direct/technical NO/EN voice.
|
||
"Sunday prep": one theme → a week of **mixed-format** posts (standard → carousel → quick → video rotation).
|
||
- **Invocation:** `/linkedin:batch` → "One main theme" → 3–5 angles.
|
||
- **Walk:** Step 0 (`:23`) load state/SKILL/plan/templates → Step 1 (`:33`) theme/pillar (+ `trend-spotter`
|
||
for timely angles) → Step 2 (`:45`) `content-planner` plan + schedule (slot template by `weekly_goal`,
|
||
queue-conflict check `:58`) → **Step 3 per post: 3a draft (`:86`) → 3b Quick Quality Check (`:94`, the
|
||
gap) → 3c save (`:101`) → 3d queueAdd (`:124`)** → Step 4 review (`:130`) → Step 5 finalize + `.ics` (`:172`).
|
||
- **CONCRETE friction (spec-level, anchored — not a generated batch):** at **Step 3b** the per-post check
|
||
`:95` read **"Character count in range"** — **no band named** (range of *what?*), so it is unenforceable;
|
||
and the Step 3a "standard structure" (`:88–92`: hook 110–140 + context 200–300 + insight 400–800 +
|
||
implication 200–300 + CTA 50–100) **sums to 960–1640**, whose floor sits **below** the canonical standard
|
||
band **1,200–1,800** (CLAUDE.md rule #2). A "standard" post drafted to the structure can land ~1,000 chars
|
||
and pass `:95` "in range" unflagged. Complication: batch is **mixed-format**, so the band is
|
||
format-dependent — imposing 1,200–1,800 on everything would wrongly fail a quick post (the S4 category
|
||
error in miniature). **Buzzword + no-body-link were NOT gaps here** (`:98` + `:97` already present — the
|
||
hole S2–S6 kept finding is already closed in `batch`).
|
||
|
||
**EVALUATE (4 axes).**
|
||
- **(a) intention fidelity — PASS.** Theme→plan→schedule→per-post draft/check/save/queue→review→finalize+
|
||
`.ics` delivers the week-of-content promise. *Tension (deferred):* Step 3a imposes one "standard structure"
|
||
though Step 2 (`:76`) promises format variety — `batch` is an orchestrator, so carousel/video inherit their
|
||
own formats' rules; recorded, not edited.
|
||
- **(b) algorithm bar — PASS.** No body link (`:97`); buzzword-clean (`:98`); pillar-aligned scheduling
|
||
(`scheduling-strategy.md`); 5x5x5 reminder (`:169`). `batch` uses a deliberately lighter "Quick Quality
|
||
Check" (no per-post `differentiation-checker`) — breadth-over-depth by design for a week-at-once tool; *note,
|
||
not a fix.*
|
||
- **(c) MECHANICAL predicate (post-emitting, per post) — GAP → fixed.** hook 110–140 ✓ (`:88`) · no-body-link
|
||
✓ (`:97`) · buzzword ✓ (`:98`) · topic→pillars ✓ (`:39/:77/:112`) · subagent resolves ✓ (`:43/:47`) ·
|
||
**length band ✗** (`:95` "in range" named no band; 3a structure sums 960–1640, under the 1,200 standard floor).
|
||
- **(d) agent-wiring + graceful degradation — wiring PASS; PASS.** `trend-spotter` (`:43`) + `content-planner`
|
||
(`:47`) both resolve to real `agents/*.md`, both `subagent_type: linkedin-studio:`-namespaced. All 5 node
|
||
helpers resolve with matching signatures: `queueUpcoming(days=7)` / `queueAdd(…8 params)` (batch passes 8 args)
|
||
/ `queueFormatSummary(entries)` (queue-manager.mjs `:52/:63/:112`), `generateIcalFromQueue` / `writeIcalFile`
|
||
(ical-generator.mjs `:183/:201`). Degradation: no-plan→`:31` "create one first"; empty queue→`:181` guard.
|
||
*Minor (deferred):* no fallback if `weekly_goal` is unset (`:52` slot-template lookup) — micro, below axis-pass.
|
||
|
||
**HARDEN (surgical — 1 edit, 1 file, grep-confirmed landed).**
|
||
- `commands/batch.md:95` — `- Character count in range` → `- Character count in the band for this post's
|
||
format (standard 1,200–1,800 · quick 150–500 · carousel/video caption per that format) — not just "in range"`.
|
||
- **Deferrals (3, recorded):** (1) Step 3a single-structure vs mixed-format promise (orchestrator inherits
|
||
per-format rules); (2) no per-post differentiation gate (deliberate breadth-over-depth design); (3)
|
||
`weekly_goal`-unset fallback (micro-degradation). All below the stopping-rule's axis-pass bar.
|
||
- **CONCRETE after:** a ~1,000-char "standard" draft now trips the format band (under the 1,200 floor) instead
|
||
of passing the vague "in range"; a quick post is judged against 150–500, not wrongly against 1,200–1,800.
|
||
|
||
**VERIFY.**
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`; counts **29/19** confirmed
|
||
(`ls commands/*.md|wc -l` == 29, `ls agents/*.md|wc -l` == 19; `git diff --stat` = `batch.md` only,
|
||
1 insertion / 1 deletion). Only `.md` touched → hooks/analytics `node --test` not needed.
|
||
- Anti-check grep: bare vague `^- Character count in range$` → NONE (gone); format-band line present (`:95`).
|
||
- Before/after delta: axis (c) length-band sub-check now enforced + format-aware at the load-bearing Step-3b
|
||
gate. The 1 edit is the cause.
|
||
- Disposition: **HARDENED** (1 edit, axis-c) · 3 recorded deferrals · axes a/b/d PASS.
|
||
|
||
---
|
||
|
||
## Session 8 — Pipeline (`pipeline`)
|
||
|
||
> S8 status: re-grounded the proposed predicates against the actual `pipeline.md` lines BEFORE
|
||
> talk (jerndisiplin: assert-after-read). Field-notes inbox **absent** at S8 → graceful no-op (SC-I).
|
||
> All `file:line` below are tool-grounded against the FINAL (post-edit) file.
|
||
> **Class call (operator-delegated → quality + precedent):** hybrid **guided/stateful + post-emitting
|
||
> on the final post** — the 8-step idea→publish→analysis pipeline produces ONE feed post (Step 2 draft
|
||
> `:57–62` → Step 3 gate `:66–86` → Step 6 publish `:135`), so the post predicates apply to that post;
|
||
> the orchestration around it (Step 0 load · Step 1 agent-deleg · Step 4 schedule/queue · Step 5 5x5x5 ·
|
||
> Step 7 first-hour · Step 8 analysis · state-update `:186–199`) is the guided/stateful layer.
|
||
|
||
### /linkedin:pipeline — full idea→publish→analysis pipeline, one 1,200–1,800-char feed post, 8 steps
|
||
|
||
**INTENT.** `pipeline` is the Create journey's **end-to-end path**: from idea (or `$ARGUMENTS` topic)
|
||
through ideation → draft → optimize → schedule → 5x5x5 pre-engagement → publish → first-hour → 48h
|
||
analysis, with a deterministic state update. Post-emitting on the final post (hook **110–140**, length
|
||
**1,200–1,800**, no body link, no banned buzzword, topic→5 pillars); guided/stateful for the
|
||
orchestration (subagent targets + 5 node helpers resolve; graceful degradation).
|
||
|
||
**SIMULATE.**
|
||
- **Persona (ICP):** the author — 1048 followers, "Validation", 5 pillars; direct/technical NO/EN voice.
|
||
- **Invocation:** `/linkedin:pipeline about AI-kodeassistenter i offentlig sektor` (topic in `$ARGUMENTS`
|
||
→ Step 1 ideation skipped, `:41`).
|
||
- **Walk:** Step 0 load state/SKILL/voice → Step 1 skipped → Step 2 angle + draft to the structure
|
||
(`:57–62`) → **Step 3 optimize gate (`:66–86`, the gap)** → Step 4 schedule (+ Option-3 queue) →
|
||
Step 5 5x5x5 → Step 6 publish+clipboard → Step 7 first-hour → Step 8 analysis → state.
|
||
- **CONCRETE friction (spec-level, anchored — not a generated post):** at **Step 3** the inline quality
|
||
scorecard (`:75–81`) carried the buzzword line (`:79`) but as a **bare** `- [ ] No corporate buzzwords`
|
||
— no inline enumeration; it leaned on the scorecard reference (`:75` → `quality-scorecard.md:132`). A
|
||
draft opening *"How to leverage AI to drive synergy across the public-sector ecosystem…"* relies on the
|
||
operator cross-referencing a long shared asset rather than the inline gate the AI actually executes.
|
||
**Unlike S2/S3/S5/S6 the line was PRESENT** — the gap was that it was not self-contained, making
|
||
pipeline the odd-one-out among post-emitting commands (post `:134` · react `:131` · carousel `:123` ·
|
||
video `:129` all enumerate inline).
|
||
|
||
**EVALUATE (4 axes).**
|
||
- **(a) intention fidelity — PASS.** 8-step idea→publish→analysis delivers the description's promise.
|
||
- **(b) algorithm bar — PASS.** Step 3 signals (`:70–73`) cited to the SSOT (saves rank highest · a
|
||
15+-word comment ≈ 2× a like · dwell >30 s +25 %); no body link (`:78`); 5x5x5 (`:120–133`); first-hour
|
||
(`:158–169`). No 30 %-claim → untouched by the open reference-consistency follow-up.
|
||
- **(c) MECHANICAL predicate (post-emitting, on the final post) — GAP → fixed.** hook 110–140 ✓
|
||
(`:58/:76`) · length band ✓ (`:77` names 1,200–1,800) · no-body-link ✓ (`:78`) · topic→pillars ✓
|
||
(`:80` + `:49` content-planner pillar-situating) · subagent resolves ✓ (`:49`) · **buzzword
|
||
present-but-not-self-contained ✗→fixed** (`:79` bare → enumerated inline).
|
||
- **(d) agent-wiring + graceful degradation — PASS.** `content-planner` + `trend-spotter` (`:49`) resolve
|
||
to real `agents/*.md`, both `subagent_type: linkedin-studio:`-namespaced. All 5 node helpers export with
|
||
matching signatures: `queueUpcoming(days=7)` (qm`:52`) · `queueAdd(…8 params)` (qm`:63`; pipeline passes
|
||
8 args `:116`) · `queueFormatSummary` (qm`:112`) · `writeState` (su`:346`) · `updatePostTracking`
|
||
(su`:55`). Degradation: Step 1 (`:43–47`) handles no-planned-topic/no-input via AskUserQuestion; Step 0
|
||
voice-samples is a soft read (no "REQUIRED" dead-end).
|
||
|
||
**HARDEN (surgical — 1 edit, 1 file, grep-confirmed landed).**
|
||
- `commands/pipeline.md:79` — `- [ ] No corporate buzzwords` → `- [ ] No corporate buzzwords (leverage,
|
||
synergy, paradigm shift, thought leader, disruptive, value proposition, ecosystem, holistic approach)`
|
||
(same banned list as the other post-emitting gates + the scorecard).
|
||
- **Deferral (1, recorded):** the Step-2 component structure (`:58–62`: hook 110–140 + context 200–300 +
|
||
insight 400–800 + implication 200–300 + CTA 50–100) sums **960–1 640**, whose floor sits below the
|
||
Step-3 band floor 1,200 (`:77`) — **identical structure to batch's 3a (`:88–92`)**. NOT edited:
|
||
pipeline's Step-3 gate already NAMES the band (the S7 fix was naming the band on an *unenforceable* gate;
|
||
here it is already named), so a minimum-path draft is caught by the gate, not shipped — and S7 did not
|
||
edit the analogous 3a structure either. Editing Step 2 here would exceed precedent (over-engineering).
|
||
Below the axis-pass bar.
|
||
- **CONCRETE after:** the "leverage…synergy…ecosystem" draft now trips the Step-3 buzzword line inline
|
||
before optimize completes, without the operator cross-referencing the shared scorecard.
|
||
|
||
**VERIFY.**
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`; counts **29/19** confirmed
|
||
(`ls commands/*.md|wc -l` == 29, `ls agents/*.md|wc -l` == 19; `git diff --stat` = `pipeline.md` only,
|
||
1 insertion / 1 deletion). Only `.md` touched → hooks/analytics `node --test` not needed.
|
||
- Before/after delta: axis (c) buzzword sub-check is now self-contained at the load-bearing Step-3 gate
|
||
(was a bare, reference-dependent line). The 1 edit is the cause.
|
||
- Disposition: **HARDENED** (1 edit, axis-c) · 1 recorded deferral (Step-2 component-sum, gate-caught) ·
|
||
axes a/b/d PASS.
|
||
|
||
---
|
||
|
||
## Session 9 — Longform orchestrator (`newsletter`)
|
||
|
||
> S9 status: re-grounded STATE's 7 S9 predicates against the actual `newsletter.md` (1594 lines, 16
|
||
> phases) BEFORE talk (jerndisiplin: assert-after-read). Class agreed S9 steps 1–2: **guided/stateful
|
||
> long-form orchestrator, NOT post-emitting** — the feed-post predicates (hook 110–140, 1,200–1,800
|
||
> band, no-body-link) do NOT apply; imposing them would be the S4/video category error the command
|
||
> itself already rejects (`:38–42`). All `file:line` below are tool-grounded against the FINAL file
|
||
> (no edits this session → final == 52e888a).
|
||
|
||
### /linkedin:newsletter — long-form edition end-to-end, 16-phase stateful pipeline, NOT post-emitting
|
||
|
||
**INTENT.** `newsletter` is the Longform orchestrator: take an edition (newsletter / essay / series
|
||
article) from load → calibration → verified research → skeleton+spine gates BEFORE prose → full draft
|
||
→ consistency/quality+voice-scrub → fact-check → editorial craft gate → persona sweep → headless
|
||
adversarial review → visual assets → LOCK (POST.html) → hook-gate → scheduling. Multi-session with a
|
||
maintained `edition-state.json`. Long-form quality is enforced by the **pipeline phases**, not
|
||
short-form hooks.
|
||
|
||
**SIMULATE (grounded persona-sim, anchored to Step 6.5 headless fan-out).**
|
||
- **Persona (operator):** KTG producing a series edition; primær reader chosen in Step 1.
|
||
- **Walk:** Step 0 load (voice/persona graceful `:258`) → … → Step 6 persona sweep PASS → **Step 6.5
|
||
freeze → fan out 5 cold archetypes** (`:1062–1069`, single message, all `linkedin-studio:`-namespaced,
|
||
foreground per principle 4; background loses Task `:79`) → **degradation gate** (`:1084–1086` "do not
|
||
proceed with a missing reviewer `[GATE]`") → consolidate via `SendUserFile` → Step 7.5 visual assets
|
||
(mcp-image not a hard dependency, external `cover-raw.png` fallback `:1222–1230`) → Step 8 LOCK
|
||
(`build-linkedin.mjs` → `linkedin/NN/POST.html`, exit-code N3 `:1388`) → Step 10 `queueAdd` (`:1486`).
|
||
- **Friction sought (the S2–S8 pattern: a missing buzzword gate / a missing fallback):** **NONE found.**
|
||
The category-correct equivalent of the short-form buzzword gate is the de-AI/voice-scrub + editorial +
|
||
language-reviewer gates, all present and wired. Every fan-out has a structured-return degradation gate.
|
||
|
||
**EVALUATE (4 axes).**
|
||
- **(a) intention fidelity — PASS.** The 16-phase pipeline delivers the description's promise; primær
|
||
artifact `POST.html` is produced end-to-end at Step 8 lock (`:1379–1395`) with exit-code verification.
|
||
- **(b) algorithm/format bar (long-form honesty, NOT feed-post predicates) — PASS.** Native-newsletter §
|
||
(`:1508–1569`) is nøktern: "sells nothing", notification dedup not oversold ("not three guaranteed
|
||
touchpoints"), cold-start not inflated ("0→9K in 7 days were not cold starts"), downsides disclosed
|
||
(non-exportable / no-canonical / no-analytics / decay), sourcing note (`:1564`) separates LinkedIn
|
||
help-docs (high conf.) from practitioner ranges (medium) + flags the contested email-dedup; citation
|
||
`docs/remediation/research/03-coverage-gap-specs.md` §D5 exists (225 lines, §D5 ×8 — not dangling).
|
||
- **(c) quality rules (category-appropriate) — PASS.** Long-form quality is enforced by the pipeline
|
||
gates (skeleton 2.5 · spine 3a · consistency+voice-scrub 4 · fact-check 5 · editorial 5.5 · persona 6
|
||
· headless 6.5 · hook 9), all present + wired. Short-form hooks correctly NOT imposed (`:38–42`); no
|
||
hollow gate found.
|
||
- **(d) agent-wiring + graceful degradation — PASS.** All 8 invoked subagents resolve to real
|
||
`agents/*.md`, all `subagent_type: linkedin-studio:`-namespaced (persona-reviewer ×5, fact-checker ×2,
|
||
voice-scrubber, language-reviewer, fact-reviewer, editorial-reviewer, content-reviewer,
|
||
content-repurposer). Render/queue helpers exist; `queueAdd(id, draftPath, schedDate, schedTime,
|
||
pillar, format, hookPreview, charCount)` 8-arg signature (qm`:63`) matches the call (`:1486`). All 9
|
||
plugin-root ref/template files (`:1575–1594`) exist. Phase strings: `_doc.phases` (16) ↔ resumption
|
||
table (16) `diff` IDENTICAL. Degradation: voice/persona `:258` · mcp-image `:1222–1230` · exit-codes
|
||
×4 (`:443/580/1144/1388`) · optional config+delingstekst `:1373–1377` · fan-out gates ×3
|
||
(`:340/789/1086`).
|
||
|
||
**HARDEN — NO CHANGE (verified clean).**
|
||
- 0 edits. `newsletter` is among the most iterated commands (v2.0→v4.1 hardened the long-form gates
|
||
across multiple production runs: Seres, Del 4 / Endring 9), and it shows: every axis passes with no
|
||
surgical opportunity at or below the axis-pass bar.
|
||
- **Deferrals:** none. **Anti-gold-plating:** no NICE-only polish pursued beyond axis-pass (stopping
|
||
rule, brief §). The native-newsletter follow-up noted in S2 STATE (reference-consistency for the
|
||
separate `video.md`/`linkedin-formats.md` 30 % surface) is NOT this command's surface — left in the
|
||
open-follow-ups list, not touched here.
|
||
|
||
**VERIFY.**
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`; counts **29/19/26** (+6
|
||
skills) unchanged; `git status --porcelain` empty → working tree byte-identical to `52e888a` (S8).
|
||
- Phase-string diff reproduced: `_doc.phases` ids vs resumption-table `currentPhase` ids → IDENTICAL,
|
||
16↔16, in order.
|
||
- Disposition: **VERIFIED — NO CHANGE** (0 edits) · 0 deferrals · axes a/b/c/d all PASS.
|
||
|
||
---
|
||
|
||
## Session 10 — Longform companion (`headless-review`)
|
||
|
||
> S10 status: re-grounded STATE's predicates against the actual `headless-review.md` (248 lines, 13 KB)
|
||
> BEFORE talk (jerndisiplin: assert-after-read). Class agreed S10 steps 1–2: **cold adversarial
|
||
> review-orchestrator, NOT post-emitting** — it emits a consolidated review report + optional
|
||
> `edition-state` persistence, never a feed post and never drafts prose; the feed-post predicates
|
||
> (hook 110–140, 1,200–1,800 band, no-body-link) do NOT apply — imposing them would be the S4/video
|
||
> category error (even further from feed-post than `newsletter`). The four archetype agents are
|
||
> SHARED with `newsletter` Step 6.5 → S9 agent findings reused as baseline. All `file:line` below are
|
||
> tool-grounded against the FINAL file (no edits this session → final == 1c31644).
|
||
|
||
### /linkedin:headless-review — cold adversarial review package on a FROZEN long-form draft, NOT post-emitting
|
||
|
||
**INTENT.** `headless-review` is the standalone surface of `newsletter` Step 6.5: read a frozen,
|
||
publish-ready (or pivoted) long-form draft COLD — with NONE of the drafting session's framing-bias —
|
||
through five independent archetypes and return direction-only flags, consolidated into one
|
||
operator-gated report. Two isolation layers: fresh session (strongest, `:64–68`) + per-archetype
|
||
subagent context (`:69–74`). The adversarial-independence layer the in-session gates structurally
|
||
cannot be (`:30–48`).
|
||
|
||
**SIMULATE (grounded persona-sim, anchored to the Step 3 cold fan-out).**
|
||
- **Persona (operator):** KTG in a FRESH session running `--type all` on `<serie>/NN-utkast.md`.
|
||
- **Walk:** Step 1 resolve-from-disk (draft → edition-state `currentArticle` fallback → ask-once
|
||
`:103–104`; contract 3-tier fallback ending at always-present `longform-quality-rules.md` `:111–114`;
|
||
personas = newsletter Step 1 chain `:116–118`) → Step 2 freeze (`cp` snapshot `:126`, live-draft
|
||
fallback if `cp` unavailable `:130–131`) → **Step 3 fan out 5 archetypes** (single message,
|
||
`:135–136`; 4 `linkedin-studio:`-namespaced types, persona-reviewer ×2 modes `:141–146`) →
|
||
**degradation gate** (`:161–164` "re-run that one archetype — do not paper over a missing reviewer
|
||
`[GATE]`") → Step 4 consolidate (operator gates, never auto-resolve flags `:168–169`/`:196–197`;
|
||
convergence marked `:200–202`) → Step 5 surface via `SendUserFile` (else `file://` `:208–210`) +
|
||
optional persist `articles.NN.headlessReview` (`:211–215`).
|
||
- **Friction sought (the S2–S8 pattern: a missing buzzword gate / a missing fallback):** **NONE found.**
|
||
Not post-emitting → the category-correct quality bar is the reviewers grading against the writing
|
||
contract + the structured-return degradation gate, both present. Every fan-out call has a degradation
|
||
re-run path.
|
||
|
||
**EVALUATE (4 axes).**
|
||
- **(a) intention fidelity — PASS.** The 5-step flow (resolve → freeze → parallel fan-out → consolidate
|
||
→ surface/persist) delivers the description's promise; primær artifact is the consolidated report
|
||
(`:171–198`), surfaced + optionally persisted (`:204–215`). `allowed-tools` (`:18–26`) complete and
|
||
fully used (Read/Glob/Grep · Bash freeze · AskUserQuestion ask-once · Task fan-out · Write report+state).
|
||
- **(b) algorithm/format bar (long-form honesty, NOT feed-post predicates) — PASS.** Cold contract
|
||
(`:50–74`) nøktern: reviewers get a "deliberately starved context", exactly-what-they-get /
|
||
exactly-what-they-don't enumerated; two-layer isolation with honest caveats (Layer 1 "recommended …
|
||
strongest" not absolute; reload caveat for the v3.1.0 agents `:80–82`). Deliberate redundancy
|
||
(`fact-reviewer`↔`fact-checker`, `language-reviewer`↔`editorial` prose axis) disclosed as
|
||
on-purpose, not collapsed (`:234–237`). No feed-post predicate imposed.
|
||
- **(c) quality rules (category-appropriate) — PASS.** Review quality enforced by (1) reviewers judging
|
||
against the writing contract (`:111–114`), (2) the degradation gate requiring structured/populated
|
||
returns, not a hedged paragraph (`:161–164`), (3) operator-gates-the-fold-in — the command recommends,
|
||
never rewrites/gates (`:168–169`/`:196–197`/`:210`), (4) convergence-as-signal (`:200–202`). No hollow
|
||
gate found.
|
||
- **(d) agent-wiring + graceful degradation — PASS.** 4/4 invoked subagent types resolve to real
|
||
`agents/*.md` with matching frontmatter `name:`, all `subagent_type: linkedin-studio:`-namespaced
|
||
(content-reviewer · language-reviewer · fact-reviewer · persona-reviewer ×2 modes → "five
|
||
archetypes" `:5–6`/`:30–31`). 8/8 Reference Files (`:241–248`) exist. `articles.NN.headlessReview`
|
||
schema present + complete in `edition-state.template.json` (`:56–69`: frozenDraft · reviewers
|
||
{content,language,fact,personaResonance,personaConversion} · consolidatedReport · foldedIn · waived ·
|
||
status; ladder pending→run→folded `:29`). Degradation ×5: freeze `cp` fallback `:130` · draft-missing
|
||
ask-once `:104` · persona fallback `:117` · `[GATE]` re-run `:163–164` · output-default `:93` +
|
||
SendUserFile-else-`file://` `:208–210`.
|
||
|
||
**HARDEN — NO CHANGE (verified clean).**
|
||
- 0 edits. Borderline surfaced at talk: the freeze step (`:126`) assumes `review/` exists (no
|
||
`mkdir -p`). **Operator chose Alt A (no change).** Rationale: the freeze block is **byte-identical to
|
||
`newsletter` `:1050`** (S9-verified-clean), and `review/` is created by the render pipeline that
|
||
produces the draft (`build-html.mjs` → `./review/` newsletter `:1136`; skeleton/draft render
|
||
`:444`/`:580`); adding `mkdir -p` only here would drift from its S9-clean twin, and fixing both would
|
||
re-open S9 (out of S10 scope). The `cp`-unavailable fallback (`:130`) covers the degraded path loosely.
|
||
- **Deferrals:** none. **Anti-gold-plating:** the `mkdir -p` robustness improvement is a NICE below the
|
||
axis-pass bar — left as the operator's logged decision (Alt A), not pursued.
|
||
|
||
**VERIFY.**
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`; counts **29/19/26** (+6
|
||
skills) unchanged (count/version-consistency guards inside the lint passed); `git status --porcelain`
|
||
empty → working tree byte-identical to `1c31644` (S9).
|
||
- Predicates re-grounded against the final file: 4 namespaced subagent types (frontmatter `name:`
|
||
match) · 8/8 ref files exist · `headlessReview` schema `:56–69` · 5 degradation points · persona
|
||
chain = newsletter `:179–183` · "five archetypes" (wrapped) = 5 modes from 4 agents.
|
||
- Disposition: **VERIFIED — NO CHANGE** (0 edits) · 0 deferrals · axes a/b/c/d all PASS.
|
||
|
||
---
|
||
|
||
## Session 11 — Longform companion (`pivot`)
|
||
|
||
> S11 status: re-grounded STATE's predicates against the actual `pivot.md` (162 lines) BEFORE talk.
|
||
> Class agreed steps 1–2: **pure state-mutation / orchestration command, NOT post-emitting, invokes NO
|
||
> agents, never drafts prose.** It re-opens a long-form edition after a late substantive change so the
|
||
> cleared gates re-run before lock — mutating `edition-state.json` (append `pivots[]`, reset
|
||
> `currentPhase`, un-lock, invalidate downstream verdicts), writing a next-step line to `<serie>/STATE.md`,
|
||
> and deferring the actual gate-runs to `/linkedin:newsletter` (`:144`). Feed-post predicates
|
||
> (hook 110–140, 1,200–1,800 band, no-body-link) do NOT apply — even further from feed-post than
|
||
> `headless-review` (this one does not even produce a review of prose). The pivot heuristic + `pivots[]`
|
||
> schema + gates-to-rerun chain are SHARED with `newsletter` (Step 8) and `headless-review` →
|
||
> S9/S10 findings reused as baseline. **Unlike S9/S10 this was NOT a no-change session — P4 surfaced a
|
||
> real cross-file off-by-one bug.**
|
||
|
||
### /linkedin:pivot — re-open a cleared edition for re-review, NOT post-emitting
|
||
|
||
**INTENT.** Make the post-lock re-open a NAMED RITUAL, not a manual scramble (the Del 4 Security
|
||
Champions case, `:33–38`): log the pivot, reset `currentPhase` so newsletter's deterministic resumption
|
||
re-runs the invalidated gates (fact-check 5 → editorial 5.5 → persona 6 → headless 6.5), un-lock if
|
||
needed, and invalidate the stale downstream verdicts so a pre-pivot PASS cannot be mistaken for current.
|
||
|
||
**SIMULATE (grounded persona-sim).**
|
||
- **Persona (operator):** KTG having made a late change to a LOCKED edition, running
|
||
`/linkedin:pivot --article NN --reason "<...>"`.
|
||
- **Walk:** Step 1 load state + locate article (missing → stop+report `:75`) → Step 2 measure scope
|
||
(`wc -w` current vs baseline `personaSweep.resonance.wordCount`; baseline-absent → ask/default
|
||
structural `:84–86`; ambiguous sections → confirm `:90`) → **Step 2 classify → default `--to-phase`**
|
||
→ Step 3 append `pivots[]` + reset `currentPhase` + un-lock + invalidate verdicts (`:100–131`) →
|
||
Step 4 write next-step line to `<serie>/STATE.md`, defer gates to newsletter (`:133–155`).
|
||
- **Friction sought (the S2–S8 pattern):** **FOUND — a phase-reset off-by-one (P4).**
|
||
|
||
**EVALUATE (4 axes — re-tolket for a non-post-emitting orchestrator).**
|
||
- **(a) intention fidelity — PASS after fix.** The ritual (log → reset → un-lock → invalidate → point)
|
||
delivers the promise; `allowed-tools` complete and used. The off-by-one (below) made the *moderate*
|
||
branch silently betray the stated intent ("so the new claims get verified") → fixed.
|
||
- **(b) format bar (heuristic-consistency, NOT feed predicates) — PASS.** Three-way SSOT verified
|
||
identical: pivot heuristic (>20 % words OR >2 sections, baseline = `personaSweep.resonance.wordCount`)
|
||
== newsletter Step 8 lock-precondition (`:1348–1362`) == template `_doc.pivots` (`:30`). Newsletter
|
||
Step 6 actually writes the baseline field (`:990–994`, `:1005`) → not a phantom. Cardinal rule
|
||
("never reopen a locked text", newsletter `:1034–1036`) honoured: pivot is the *sanctioned* exception
|
||
— it loudly un-locks + flags POST.html stale (`:124`) + invalidates verdicts (`:126–130`).
|
||
- **(c) quality rules (state-integrity) — PASS.** `gatesToRerun` `:113` = the 4 real gate phase-names;
|
||
invalidation targets (`personaSweep.resonance` · `editorialReview` · `headlessReview.status`) all
|
||
exist in the schema; `factcheckLog` + `pivots[]` left intact (durable history `:130`).
|
||
- **(d) agent-wiring + graceful degradation — PASS.** No agents (orchestrator). 3 degradation points
|
||
enumerated (article-missing `:75` · baseline-absent `:84–86` · ambiguous-sections `:90`). 3/3 ref
|
||
files exist.
|
||
|
||
**THE BUG (P4 — confirmed cross-file off-by-one).** Newsletter's resumption table is explicit
|
||
(`:196–198`, `:203–221`): `currentPhase` = the *last completed* phase, and the pipeline resumes at the
|
||
step **after** it (`draft`→Step 4, `consistency-quality`→Step 5, `factcheck-sweep`→Step 5.5). But
|
||
pivot.md set the **moderate** default to `to-phase: factcheck-sweep` "so the new claims get verified" —
|
||
which resumes at **Step 5.5 (editorial), SKIPPING the Step 5 fact-check** the moderate pivot exists to
|
||
run. Self-contradictory, and a violation of the command's own invariant that "a pivot always invalidates
|
||
the fact-check" (`:116–119`). The **structural** branch (`to-phase: draft`→Step 4→fact-check at 5) was
|
||
functionally safe but mis-labelled "(Step 3b)". Root cause: conflating "the phase to resume AT" with the
|
||
`currentPhase` (last-completed) value to write.
|
||
|
||
**THE FIX (4 surgical edits, confined to command-anatomy + Step 2).**
|
||
1. Moderate default `:96–97`: `factcheck-sweep` → **`consistency-quality`** (resumes Step 5, fact-check
|
||
runs) — the bug.
|
||
2. Structural label `:92–95`: "(Step 3b) … full prose expansion" → "resumes **Step 4
|
||
(consistency-quality)** … then fact-check (5) → editorial (5.5) → persona (6) → headless (6.5)".
|
||
3. Off-by-one guard callout `:102–108`: explains `to-phase` is a `currentPhase` (last-completed) value
|
||
and newsletter resumes at the NEXT step → why a pivot never resets to `factcheck-sweep`.
|
||
4. Anatomy enum `:46`: dropped `factcheck-sweep` from the offered values (it skips fact-check, violating
|
||
the always-invalidate-fact-check invariant) → `draft | consistency-quality`.
|
||
`gatesToRerun` `:113` and the worked example `:64–70` (structural, `draft`) left intact — both correct.
|
||
|
||
**VERIFY.**
|
||
- Re-grepped final file: `factcheck-sweep` now only at `:106` (guard, as the forbidden value) + `:123`
|
||
(gatesToRerun gate-name); to-phase defaults = `draft` (`:93`) / `consistency-quality` (`:97`); enum
|
||
`:46` = two values; guard landed `:102`.
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`; counts **29/19/26** unchanged
|
||
(count/version/model-consistency guards passed; .md-only edit, node/analytics tests not triggered).
|
||
- **One NICE left (anti-gold-plating):** template `:30` says "+~530 words" (sum of the 2 named sections)
|
||
vs worked-example 1400→1992 = +592; both "~", both +42 % — cosmetic, below the axis-pass bar, left.
|
||
- Disposition: **FIXED** (4 edits, all in `commands/pivot.md`) · 0 deferrals · axes a/b/c/d all PASS
|
||
post-fix.
|
||
|
||
---
|
||
|
||
## Session 12 — Hardening (`firsthour`)
|
||
|
||
> S12 status: re-grounded STATE's predicates against the actual `firsthour.md` (119 lines) BEFORE talk.
|
||
> Class agreed steps 1–2: **post-publish engagement orchestrator — first-hour / reply-loop sprint.
|
||
> NOT post-emitting / feed-draft.** It builds a first-hour plan (target list + draft comments + timeline)
|
||
> by DELEGATING construction to `engagement-coach` (Step 2, `Task`), presents + auto-copies the draft
|
||
> comments (Step 3), persists via `recordFirstHourPlan` (Step 4), and hands off to `post-feedback-monitor`
|
||
> (Step 5). It emits COMMENT copy, not a feed post → the feed-format predicates (hook 110–140, 1,200–1,800
|
||
> band, no-body-link) do NOT apply; the inline comment band (25–50 words, voice, no bait, no generic
|
||
> praise) is its own surface. Axis-d heavy: 2 agents + a state-writer + a hand-off chain.
|
||
|
||
**WHAT VERIFIED CLEAN (no change).**
|
||
- Agent wiring (axis-d): both `subagent_type: linkedin-studio:`-namespaced. `engagement-coach` frontmatter
|
||
= name / model:sonnet / color:magenta; `post-feedback-monitor` = name / model:opus / color:lime — both
|
||
match the CLAUDE.md table exactly.
|
||
- State-writer (axis-d): `recordFirstHourPlan` exported (`state-updater.mjs:235`), additive, `$`-safe
|
||
(function-replacements), stamps `last_firsthour_date` + `firsthour_active`, writes `## First-Hour Plans`
|
||
— exactly as Step 4 claims. `writeState` / `clipboard-helper.mjs` / both ref files present.
|
||
- Delegation sound (axis-d): CEA / 5x5x5 / first-hour / target-selection genuinely owned by
|
||
`engagement-coach.md` (whales / inner-circle, velocity table). Hand-off coach→monitor present (Step 5).
|
||
- Axis-c: the draft-comment band is consistent with the SSOT AI-slop suppression + the 15+-word floor.
|
||
- Principle 2 "comments rank above reactions" (`:111`) IS supported — SSOT `:38`
|
||
`saves > shares > quality comments > reactions/likes` + `:160`. (My initial narrow grep was a false
|
||
negative — withdrawn.)
|
||
|
||
**THE FIX (2 findings, 3 surgical edits, confined to prose + footer).**
|
||
1. [REWORK · axis-b] `~70%`-stat MISATTRIBUTED to "the first 60 min". The command (desc `:4`, body `:22`)
|
||
said the first hour / "first 60 minutes" decide ~70% of reach. SSOT `algorithm-signals-reference.md:81`
|
||
pins ~70% to the **first 15–30 min** sub-window (golden window = 60–90 min); the command even cites
|
||
that SSOT (`:118`) → it contradicted its own source. Fix: desc + body re-attributed to "first 15–30
|
||
minutes decide ~70%", framing the hour as the golden window you work. (Operational guidance was already
|
||
front-loaded — the 5/15/30/60 velocity checkpoints were left unchanged.)
|
||
2. [REWORK-low · axis-a] Reference-label: `engagement-frameworks.md` does NOT contain CEA. The footer
|
||
labelled it "hook types, CEA, engagement hierarchy" — but CEA lives in `algorithm-signals-reference.md:162`
|
||
+ `engagement-coach.md`; the ref carries hook types + "Engagement Quality Hierarchy" only. Fix: dropped
|
||
"CEA" from the footer line (CEA already covered by the Step 2 coach-delegation + the SSOT pointer).
|
||
|
||
**VERIFY.**
|
||
- Re-grepped the final file: `~70%` is now attributed to "15–30 minutes" at desc `:4` + body `:22`; no
|
||
"first 60" / "60 minutes set" attribution remains (`:66` "5/15/30/60-minute" is the velocity-checkpoints
|
||
table, unchanged). CEA now only at `:6 / :44 / :54 / :65 / :112` (desc + Step 2 + Step 3 + principle),
|
||
GONE from the footer; footer `:118` = "hook types, engagement hierarchy".
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`; counts **29/19/26** unchanged
|
||
(count / version / model / stat-consistency guards passed; .md-only edit, node/analytics tests not
|
||
triggered).
|
||
- **One NICE left (anti-gold-plating):** no degradation note for an `engagement-coach`-Task failure /
|
||
absent voice-samples (thin orchestrator, fully coach-dependent) — consistent with the suite's posture,
|
||
below the axis-pass bar, left.
|
||
- Disposition: **FIXED** (3 edits, all in `commands/firsthour.md`) · 0 deferrals · axes a/b/c/d all PASS
|
||
post-fix.
|
||
|
||
---
|
||
|
||
## Session 13 — Hardening (`calendar`)
|
||
|
||
> S13 status: read-and-show of every mechanical dependency against the actual `queue-manager.mjs` +
|
||
> `state-updater.mjs` BEFORE talk. Class agreed steps 1–2: **stateful queue / publish command** —
|
||
> view + manage the scheduling queue + run the publish action. **NOT post-emitting / feed-draft.**
|
||
> It loads the queue (`queueToday/Upcoming/Overdue/Count/FormatSummary`), renders a 14-day view,
|
||
> offers actions (mark-published / reschedule / cancel / view-draft), and on publish does
|
||
> `queueUpdateStatus('published')` → `updatePostTracking` (state) → a quick first-hour plan that
|
||
> defers the full sprint to `/linkedin:firsthour` + `post-feedback-monitor`. It DISPLAYS hook previews
|
||
> of already-queued drafts → it emits no feed content → the feed-format predicates (hook 110–140,
|
||
> 1,200–1,800 band, no-body-link, buzzwords, rotation) do NOT apply. Axis-a/d heavy.
|
||
|
||
**WHAT VERIFIED CLEAN (no change).**
|
||
- Queue wiring (axis-a/d): Step 1 (`queueToday/Upcoming/Overdue/Count/FormatSummary`), 3c + Cancel
|
||
(`queueUpdateStatus`) all match exports in `queue-manager.mjs:41–112` exactly.
|
||
- State-writer (axis-d): `updatePostTracking(stateContent, { postDate, postTopic, hookText, charCount,
|
||
format })` (`state-updater.mjs:55`) + `writeState` (`:346`) — signature matches Step 3d's call verbatim.
|
||
- Agent wiring (axis-d): `subagent_type: linkedin-studio:post-feedback-monitor` (`:156`) namespaced,
|
||
agent file present, `Task` in `allowed-tools`. Hand-off to `/linkedin:firsthour` (`:138`) = S12 chain.
|
||
- Graceful degradation (axis-d): missing/corrupt queue → `ensureQueue()` + `readQueue()` try/catch → `[]`
|
||
(`queue-manager.mjs:12–27`); the "No posts scheduled" branch (`:106–111`) covers the empty case.
|
||
- Algorithm bar (axis-b): the quick first-hour plan (`:151` "15+ engagements in first 60 min", check at
|
||
30/60) is deliberately light and delegates the full sprint to `/linkedin:firsthour`. No ~70%-reach
|
||
misattribution (the S12 bug is absent here). SSOT `:81` golden window 60–90 min / first 15–30 min — not
|
||
contradicted. PASS.
|
||
- Content-quality (axis-c): emits no feed content; the hook "preview" is a truncated display of an
|
||
existing draft, not a generated hook → feed predicates N/A → PASS.
|
||
|
||
**THE FIX (1 finding, 1 surgical edit, confined to the Reschedule action).**
|
||
1. [REWORK · axis-a/d] The **Reschedule** action (`:164–170`) was under-determined vs its sibling actions:
|
||
prose-only with NO bash snippet (unlike Cancel `:177–180` and publish 3c `:116–118`), and it referenced
|
||
`queue_update_status + queue_add` — **snake_case that does not match the exports** `queueUpdateStatus` /
|
||
`queueAdd` (`queue-manager.mjs:63,82`). An agent executing it had no concrete command and the names
|
||
would not resolve if copied literally. Fix: replaced step 3 with a concrete `queueAdd` one-liner using
|
||
the **same id** (since `queueAdd` filters out the prior id first, `:64`, re-adding moves the post in
|
||
place — no duplicate), carrying the unchanged fields from the Step-2 entry. Mirrors the Cancel snippet
|
||
pattern.
|
||
|
||
**VERIFY.**
|
||
- Re-grepped the final file: no `queue_update_status` / `queue_add` remains; the `queueAdd` reschedule
|
||
snippet is at `:174`; every `queue*` reference in the file is now camelCase and matches an export.
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`, **exit 0**; counts **29/19**
|
||
unchanged (.md-only edit, node/analytics tests not triggered — unchanged from S12).
|
||
- **One NICE left (anti-gold-plating):** the quick first-hour plan could name the SSOT "first 15–30 min"
|
||
sub-window, but it deliberately defers to `/linkedin:firsthour`; duplicating it would be scope creep —
|
||
left.
|
||
- Disposition: **FIXED** (1 edit in `commands/calendar.md`) · 0 deferrals · axes a/b/c/d all PASS post-fix.
|
||
|
||
---
|
||
|
||
## Session 14 — Hardening (`import`)
|
||
|
||
> S14 status: read-and-show of the analytics CLI (`csv-parser.ts`, `cli.ts handleImport`,
|
||
> `storage.ts`, `alerts.ts`) BEFORE simulate, then a GROUNDED run against a throwaway fixture
|
||
> (`ANALYTICS_ROOT=/private/tmp/…`, 5-row CSV, discarded after). Class agreed steps 1–2:
|
||
> **analytics ingest** — CSV export → structured weekly JSON via `cli.ts import`, delegating all
|
||
> analysis to `/linkedin:report` (Step 6). **NOT post-emitting** → the feed-format predicates
|
||
> (hook 110–140, length band, no-body-link, buzzwords, rotation) do NOT apply. Axis-a/d heavy
|
||
> (path-seam consistency + graceful degradation + the over-promise).
|
||
|
||
### /linkedin:import — CSV export → structured weekly JSON, intra-batch anomaly surface, analysis delegated to report
|
||
|
||
**WHAT VERIFIED CLEAN (no change).**
|
||
- Path-seam (axis-a): `getAnalyticsRoot()` (`storage.ts:67–72`) → `getDataRoot("analytics")`
|
||
(`:54–59`) = `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/analytics` = the exact path
|
||
the prose tells the user to use (`import.md:37,62,91`); identical `LINKEDIN_STUDIO_DATA` override.
|
||
`ANALYTICS_ROOT` back-compat override (`storage.ts:68`) confirmed (validates the throwaway isolation).
|
||
- saves/dwell honesty (axis-c): grounded run → EU `"5.234"`→5234, US `"8,710"`→8710;
|
||
`parseOptionalCount` (`csv-parser.ts:71–85`) kept saves 42/128 on data rows, **blank → no `saves`
|
||
field** (unknown, not 0); `engagementRate` excludes saves (`:203–208`); **0 dwell field** anywhere.
|
||
README boundary blockquote (`import.md:26–30`) intact.
|
||
- Wiring (axis-d): `/linkedin:report` → `commands/report.md` present; **0** `subagent_type`
|
||
(CLI-delegating, correct); state-file Step 6b = `~/.claude/linkedin-studio.local.md`.
|
||
- Degradation (axis-d): `npm install` pre-step (`:114–121`) covers fresh-clone `ERR_MODULE_NOT_FOUND`;
|
||
file-not-found exit 1 (`cli.ts:67–71`) ↔ Error Handling; empty title (file line 5) + bad date
|
||
(line 6) **skipped** in the run (`csv-parser.ts:186–195`).
|
||
|
||
**THE FIX (1 finding, 5 surgical edits, all in `commands/import.md`).**
|
||
1. [REWORK · axis-a/d] **Over-promise: baseline / metadata infrastructure the code never had.**
|
||
import.md repeatedly promised baseline comparison + rolling 4-week averages + a `baselines.json` /
|
||
`metadata.json` the importer "creates" + ">2 sigma from baseline" alerts. Grep over the whole repo
|
||
(`scripts/analytics/src` + all `*.mjs/ts/py/js`) = **NONE writes either file**; `saveBatch` writes
|
||
only `posts/*.json` (`storage.ts:157`); `detectAlerts` (`alerts.ts:18–69`) flags **intra-batch**
|
||
std-dev from the batch's own mean, not a stored baseline. Concrete bug: Step 5b's
|
||
`cat baselines.json 2>/dev/null` is always empty → the command falls forever to "first import —
|
||
baselines will be established", on every import. Fix: rewrote 5 sections to the truth — Step 5
|
||
(real CLI fields + `YYYY-MM-DD-<shortid>.json` filename; dropped "Duplicates skipped" / "340% above
|
||
baseline" / "Viral threshold"), Step 5b (honest intra-batch surfacing; cross-week analysis deferred
|
||
to `/linkedin:report`), Step 7 (Next-Steps conditioned on the report's trend, not a local baseline),
|
||
State Tracking + Reference Files (no `baselines.json` / `metadata.json`; correct batch filename).
|
||
|
||
**VERIFY.**
|
||
- Re-grepped the final file: `metadata.json` / `YYYY-WXX.json` (filename) / "Duplicates skipped" /
|
||
"340%" / "Viral threshold" all GONE; the only `YYYY-WXX` left is `:226` (`last_import_week` ISO-week
|
||
state field — legitimate); the 3 remaining "baseline" hits are the new honest negations ("keeps no
|
||
baseline file", "no persisted baseline").
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`, **exit 0**; counts **29/19**
|
||
unchanged (.md-only edit, node/analytics tests not triggered).
|
||
- Grounded simulation passes the failing axis: the rewritten prose matches the observed CLI output
|
||
verbatim (filename `2026-06-02-batch-17.json`, "No anomalies detected in imported data.",
|
||
"Saves entered: 170 across 2 post(s) (manual)").
|
||
- Disposition: **FIXED** (5 edits, all in `commands/import.md`) · 0 deferrals · axes a/b/c/d all PASS
|
||
post-fix.
|
||
|
||
---
|
||
|
||
### /linkedin:report — imported analytics → weekly/monthly/heatmap report, alerts surfaced, interpretation delegated to analytics-interpreter
|
||
|
||
**WHAT VERIFIED CLEAN (no change).**
|
||
- Path-seam (axis-a): `getAnalyticsRoot()` (`storage.ts:67–72`) → `getDataRoot("analytics")` (`:54–59`)
|
||
= the exact `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/analytics` paths the prose reads;
|
||
`ANALYTICS_ROOT` override confirmed (the grounded sim ran isolated in `/private/tmp`).
|
||
- CLI surface (axis-a): `report --week`/`--month`, `trends --period --metric`, `heatmap` all dispatch
|
||
(`cli.ts:447–459`); grounded sim produced verbatim weekly report + trends + heatmap on a 3-row fixture.
|
||
- Wiring (axis-d): `subagent_type: linkedin-studio:analytics-interpreter` (`report.md:305`) resolves —
|
||
`agents/analytics-interpreter.md` present with report-mode (`:10,40,295`); one namespaced `Task`.
|
||
- Degradation (axis-d): `trends`/`heatmap` "No posts found. Import some data first." exit 1
|
||
(`cli.ts:245–248,397–400`); weekly report try/catch exit 1 (`:191–194`); Step 1 import-nudge;
|
||
npm-install Step 1b for fresh-clone `ERR_MODULE_NOT_FOUND`.
|
||
- saves/dwell honesty (axis-c): `totalSaves` surfaced only when present, labelled "(manual entry — top
|
||
engagement signal)" (`cli.ts:144–146,342–344`; `weekly.ts:172–173`); grounded run — FIXTURE B's blank
|
||
Saves cell produced **no** saves suffix (unknown≠0); never folded into engagement. **dwell** absent,
|
||
explicit "do not fabricate a dwell field" (`types.ts:26–28`).
|
||
|
||
**THE FIX (3 findings, 6 surgical edits, all in `commands/report.md`).**
|
||
1. [REWORK · axis-a/d] **Over-promise: `baselines.json` + `metadata.json` infrastructure the code never
|
||
had** (the S14 import twin). Step 5c told the model to `cat baselines.json` and flag ">2 standard
|
||
deviations above/below mean"; Reference Files listed both files. Grep over `scripts/analytics/src` =
|
||
**NONE writes either**; `storage.ts` writes only `posts/<date>-<shortid>.json`,
|
||
`weekly-reports/<week>.json`, `monthly-reports/<month>.json`; `ensureDirectories` (`:77–90`) makes no
|
||
baselines dir; a full import+report cycle in the fixture created **NEITHER**. The real alerts are
|
||
`detectAlerts` (intra-week) + `detectWeeklyAlerts` (WoW) surfaced in the report's own `alerts[]`
|
||
(`weekly.ts:222–238`). Fix: Step 5c rewritten to read `alerts[]` straight from the generated
|
||
weekly-report JSON; Reference Files dropped both phantoms, fixed the posts filename to
|
||
`YYYY-MM-DD-<shortid>.json`, added the real `monthly-reports/` file.
|
||
2. [REWORK · axis-a] **Broken metric flag `engagement_rate` (snake_case, `:172`).** Grounded:
|
||
`--metric engagement_rate` → exit 1 "Invalid metric"; the CLI accepts only camelCase `engagementRate`
|
||
(`cli.ts:47,202–209`). Fix: `:172` snake→camel (the other call, old `:363`, was already correct).
|
||
3. [REWORK · axis-a] **Step 4 / Step 6 schema field names that don't exist.** Step 4 claimed JSON fields
|
||
`dateRange`, `postCount`, `aggregateMetrics`; the real `WeeklyReport` (`weekly.ts:124–149`) top keys
|
||
are `week, generatedAt, summary, topPerformers, underperformers, trends, alerts` (no dateRange, no
|
||
aggregateMetrics, no top-level postCount — it is `summary.totalPosts`). Fix: Step 4 rewritten to the
|
||
real schema; Step 6 template `({dateRange})` dropped, `{postCount}`→`{summary.totalPosts}`,
|
||
eng-rate→`{summary.avgEngagementRate}`; Deep-Dive post-search fixed from the non-existent
|
||
`posts/<YYYY-WXX>.json` to iterating the real per-batch `posts/*.json`.
|
||
|
||
**VERIFY.**
|
||
- Re-grepped the final file: `engagement_rate` / `baselines.json` / `metadata.json` / `aggregateMetrics`
|
||
/ `({dateRange})` / `{postCount}` / `posts/<YYYY-WXX>.json` all GONE; corrected strings present
|
||
(`metric engagementRate` `:171,366`; `get('alerts', [])` `:227`; `posts/YYYY-MM-DD-<shortid>.json`
|
||
`:414`; `monthly-reports/YYYY-MM.json` `:416`; Step 4 `summary` schema `:143`; Step 6
|
||
`{summary.totalPosts}`/`{summary.avgEngagementRate}` `:271–272`); no residual `dateRange`/`postCount`.
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`, **exit 0**; counts **29/19**
|
||
unchanged (.md-only edit).
|
||
- Grounded simulation (throwaway `ANALYTICS_ROOT=/private/tmp/claude-report-fixture`, discarded): import
|
||
seeded 3 fake W25 posts → `report --week 2026-W25` printed "Total saves: 10 (manual entry…)", FIXTURE B
|
||
(blank saves) showed no suffix, empty alerts (no baseline consulted); `trends --metric engagementRate`
|
||
ok, `--metric engagement_rate` exit 1; heatmap rendered.
|
||
- Disposition: **FIXED** (6 edits, all in `commands/report.md`) · 0 deferrals · axes a/c/d all PASS
|
||
post-fix (b N/A — not feed-emitting).
|
||
|
||
---
|
||
|
||
### /linkedin:analyze — diagnose why content underperforms (reach drop / low traction / low engagement) → recovery plan, analytics grounded via analytics-interpreter
|
||
|
||
**INTENT.** Prose-diagnosis + conditional agent-delegation — **NOT CLI-backed** (`allowed-tools: Read,
|
||
AskUserQuestion, Task`, no Bash, `analyze.md:9–12`). Measure-journey diagnostic. Steps 1–8 are a
|
||
faithful restatement of `references/troubleshooting-guide.md` (drop-% tiers, 14-day recovery protocol,
|
||
timeline table, prevention checklist).
|
||
|
||
**SIMULATE.** ICP author, scenario 1 (reach −50 %, 1 wk ago, 2 off-pillar posts, 1 body-link). Step 1
|
||
picks "Reach suddenly dropped" (`:32`); Step 2 analytics present → `Task
|
||
subagent_type: linkedin-studio:analytics-interpreter` interpret-mode (`:41`); Step 3 → pattern "Good
|
||
Content, Low Reach" (`:95–102`); Step 4 checklist flags external link + topic drift — **and, before fix,
|
||
asked "5+ hashtags?" (`:149`)**; Step 5 → "Algorithmic suppression likely" → 14-day recovery
|
||
(`:167–173`); Step 6 → `/linkedin:profile` (`:189`); Step 7 → "Moderate 50–70 %: 2–3 wks initial"
|
||
(`:231`). Friction: exactly one unsourced checklist item.
|
||
|
||
**EVALUATE (4 axes; analytics-class predicate = graceful degradation + saves/dwell honesty).**
|
||
- **axis-a (intent): PASS** — faithful render of the cited guide; AskUserQuestion → framework → recovery
|
||
flow intact.
|
||
- **axis-b′ (algorithm-claim vs SSOT — feed-bar reframed since not feed-emitting): 1 FUNN.** `:149`
|
||
"Did you use 5+ hashtags in posts?" is a penalty check absent from **both** the SSOT
|
||
(`grep hashtag references/algorithm-signals-reference.md` = NONE — hashtags deliberately omitted) **and**
|
||
the cited `references/troubleshooting-guide.md` (NONE; its own penalty checklist is 6 items `:101–106`).
|
||
Command-only unsourced folklore carrying a concrete number ("5+").
|
||
- **axis-c (saves/dwell honesty): PASS** — no Bash; never claims to read analytics itself; delegates
|
||
honestly; no saves/dwell mention anywhere → no over-promise.
|
||
- **axis-d (wiring + degradation): PASS** — `linkedin-studio:analytics-interpreter` resolves;
|
||
interpret-mode is real/default (`agent:6,39,141`); shared seam `${LINKEDIN_STUDIO_DATA:-…}/analytics/`
|
||
(`analyze:41` ↔ `agent:49`); `/linkedin:profile` resolves (`commands/profile.md`); "if analytics exists
|
||
→ delegate, else self-report" IF-branch (`:41`); all 4 referenced files exist.
|
||
|
||
**HARDEN (1 surgical edit, `commands/analyze.md`).**
|
||
1. [REWORK · axis-b′] Dropped `:149` "Did you use 5+ hashtags in posts?" — unsourced in both the SSOT and
|
||
the cited guide. Step 4 checklist 8 → 7 items; **kept** `:150` "tag unrelated people for reach"
|
||
(defensible — aligns with the SSOT bait/pod-suppression direction, no numeric claim).
|
||
|
||
**Out-of-scope (logged follow-up, NOT touched in S16).** `troubleshooting-guide.md:127` "External link
|
||
overuse — LinkedIn penalizes directing traffic away" contradicts the SSOT (`:69–75`: LinkedIn *denies* an
|
||
intentional penalty; soften to "body links correlate with lower reach"). `analyze.md:102` only echoes the
|
||
milder guide wording verbatim (`guide:15`) — softening the command alone would drift it from its own cited
|
||
source, so the real fix belongs to the reference-consistency pass.
|
||
|
||
**VERIFY.**
|
||
- Re-grepped final file: `grep hashtag commands/analyze.md` → **NONE**; Step 4 checklist now 7 items
|
||
(`:143–149`), `:149` = "tag unrelated people for reach" (survived).
|
||
- `git diff --stat` = 1 file, **1 deletion**.
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`, **exit 0**; counts **29/19**
|
||
unchanged (.md-only edit).
|
||
- Disposition: **FIXED** (1 edit, `commands/analyze.md`) · 0 deferrals · axes a/c/d PASS, b′ PASS post-fix.
|
||
|
||
---
|
||
|
||
### /linkedin:audit — quarterly content-strategy audit (top/bottom posts · topic & format mix · trends · milestone) → report + routes to strategy/profile
|
||
|
||
**INTENT.** Prose-heavy, self-contained quarterly audit — **NOT CLI-backed, NOT agent-delegated**
|
||
(`allowed-tools: Read, Glob, Grep, AskUserQuestion` post-fix, no Bash, no Task, `audit.md:8–12`).
|
||
Measure-journey (reachable via `/linkedin:measure`). The 8 steps (0–8 + 5.5) faithfully cover the
|
||
description scope: top/bottom performers, topic distribution, format mix, engagement trends, milestone
|
||
progress, profile alignment — quarterly cadence. Names the gap; routes the fix to `/linkedin:strategy`
|
||
(trajectory) + `/linkedin:profile` (profile audit).
|
||
|
||
**SIMULATE.** ICP author, scenario A (partial data). Step 0 reads state + `plans/` + checks `analytics/`
|
||
+ audience-insights/examples, asks user for top-5/bottom-5 + screenshot; Steps 1–2 tabulate vs 7 factors
|
||
(`:41–49`); Step 3 topic-mix vs pillars (`:69–81`); Step 4 format mix → emits "Increase carousel content
|
||
(**highest save rate**)" (`:93`, before fix); Step 5 trends; Step 5.5 milestone if `follower_count>0`;
|
||
Step 6 routes to `/linkedin:profile` (`:151`); Step 7 report; Step 8 AskUserQuestion. Scenario B (fresh
|
||
adopter, no data): Step 0 `:26` "Check for any" = soft, audience files absent → degrades to user-provided
|
||
data; Step 5.5 `:141` `follower_count` not >0 → "Skip this step. Add note: …not configured" — no crash.
|
||
Friction: one orphan tool-grant + one unsourced format-claim.
|
||
|
||
**EVALUATE (4 axes; analytics-class predicate = graceful degradation + saves/dwell honesty).**
|
||
- **axis-a (intent): PASS** — 8 steps faithfully cover the description scope; routing-by-delegation (not
|
||
agent delegation) is honest ("names the gap, strategy/profile prescribe the fix", `:145–156`).
|
||
- **axis-b′ (algorithm/format claim vs SSOT): 1 FUNN.** `:93` "(highest save rate)" — the specific
|
||
metric-attribution is absent from the SSOT (which sources "carousels = **top organic format**",
|
||
`algorithm-signals-reference.md:55`, and "**saves** = top engagement signal", `:42`, but never
|
||
"carousels = highest *save rate*") AND from the cited refs (playbook `:248` "Carousels: 1.6x", `:192`
|
||
"60–70 % reduced reach"; no save-rate superlative). Postulated mechanic embedded in an example block.
|
||
- **axis-c (quality rules): PASS** — emits no content; no format/length advice contradicting CLAUDE.md.
|
||
- **axis-d (wiring + degradation): 1 FUNN.** No Task → claims no agent delegation (`grep
|
||
subagent_type|Task` = NONE) ✅; `/linkedin:strategy` + `/linkedin:profile` resolve ✅; graceful
|
||
degradation present (Step 5.5 explicit skip + user-provided-data primary path) ✅; data-seam consistent
|
||
(`${LINKEDIN_STUDIO_DATA:-…}/` data, `~/.claude/linkedin-studio.local.md` state, `${CLAUDE_PLUGIN_ROOT}/`
|
||
refs) ✅; saves/dwell not over-promised (no Bash; never claims auto-track) ✅. **FUNN: WebFetch orphan
|
||
tool-grant** — `:12` granted it; body `:20–234` never fetches (grep = declaration line only).
|
||
|
||
**HARDEN (2 surgical edits, `commands/audit.md`).**
|
||
1. [REWORK · axis-d] Dropped `WebFetch` from allowed-tools — granted but never invoked; audit reads local
|
||
files + asks user + routes, fetches no URL. Tool-surface now matches behavior (5 → 4 tools).
|
||
2. [REWORK · axis-b′] `:93` → `:92` "(highest save rate)" → "(top organic format)" — aligned to the SSOT's
|
||
sourced framing; dropped the unsourced save-rate superlative.
|
||
|
||
**Out-of-scope (logged, NOT touched in S17).** `:61` "off-topic (topic-relevance penalty)" — SSOT frames
|
||
topic-relevance as a positive distribution driver (`:30,89`, high), not a named penalty; but off-topic →
|
||
weaker relevance match → less reach is defensible and "topic-relevance signal" is plugin convention
|
||
(CLAUDE.md rule #5). Left as-is (NICE, directionally correct diagnostic parenthesis).
|
||
|
||
**VERIFY.**
|
||
- Re-grepped final file: `grep WebFetch commands/audit.md` → **NONE** (allowed-tools now
|
||
Read/Glob/Grep/AskUserQuestion, `:9–12`); `grep "highest save rate"` → **NONE**; `:92` = "Increase
|
||
carousel content (top organic format)".
|
||
- `git diff --stat` = 1 file, **+1 / −2**.
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`, **exit 0**; counts **29/19**
|
||
unchanged (.md-only edit).
|
||
- Disposition: **FIXED** (2 edits, `commands/audit.md`) · 0 deferrals · axes a/c PASS, b′ + d PASS post-fix.
|
||
|
||
---
|
||
|
||
### /linkedin:ab-test — design/log/analyze/history/suggest A/B experiments → managed test files, challenger rewrite delegated to content-optimizer
|
||
|
||
**INTENT.** Stateful analytics command — the most composite class in the queue so far. CLI-backed (Bash
|
||
`ls`/`cat`/`mkdir -p` + `Write` of `[test-name].md` test files under the data-dir), agent-delegated
|
||
(`content-optimizer` via `Task subagent_type: linkedin-studio:content-optimizer`, `:119`), AND routing
|
||
(`/linkedin:post` `:448`, `/linkedin:report` `:459`, `/linkedin:analyze` `:467`). Measure-journey
|
||
(reachable via `/linkedin:measure`). Five lifecycle modes (2a–2e) map 1:1 to the description: design ·
|
||
log · analyze · history · suggest. (STATE predicted `analytics-interpreter` delegation; actual is
|
||
`content-optimizer`.)
|
||
|
||
**SIMULATE.** ICP author → analyze an active hook experiment. Throwaway 2-experiment fixture in
|
||
`/private/tmp/claude-abtest-fixture/analytics/ab-tests/` (ACTIVE 6-post + COMPLETED), run via
|
||
`LINKEDIN_STUDIO_DATA=<fixture>` to exercise the real seam. Seam-discovery (`:37`,`:270`) found both
|
||
files; analyze arithmetic computed correct (eng-rate `:236` per-post 3.0%/5.0%; variant avg + %-diff
|
||
`:278–279` → eng +66.7%, comments +200%, impressions/reposts +0%); directional test (`:311`) cleared
|
||
20% AND consistent across all 3 paired posts → "Adopt B (directional)"; significance honesty (`:320–331`)
|
||
correctly flagged 3/variant << ~50 → directional, NOT significant. History-parse targets all present in
|
||
COMPLETED fixture. Empty `weekly-reports/` + `posts/` → graceful degradation (`:485–488`). Fixture
|
||
discarded (`find -delete`).
|
||
|
||
**EVALUATE (8 predicates across 4 axes; class = stateful + analytics + agent-wiring + routing).**
|
||
- **P1 (axis-d wiring): PASS** — `content-optimizer` resolves; `agents/content-optimizer.md` `name:`
|
||
matches the namespaced invocation form.
|
||
- **P2 (routing): PASS** — `post`/`report`/`analyze`.md all resolve.
|
||
- **P3 (intent refs): PASS** — `ab-testing-framework.md` + `algorithm-signals-reference.md` exist.
|
||
- **P4 (axis-b′ SSOT): 1 FUNN.** ② 20%-threshold **verbatim** from framework SSOT (`fw:173` = `ab:311`)
|
||
✅; ③ significance honesty under-claims (a real significance test needs more than ~50/variant → the
|
||
framing can only make the "directional not significant" verdict safer) ✅; **① `:128` "(Tue-Thu, 8-9 AM
|
||
optimal)"** — sourced (cites the ref) but "optimal" over-claims precision against the SSOT's own `:23`
|
||
"trust the *ordering*, test the *number*", and Thu is 9 AM–1 PM in SSOT (`alg:144`), not 8 AM; it is the
|
||
default slot held constant while testing another variable, so "optimal" is the wrong word.
|
||
- **P5 (data-seam): PASS** — all data via `${LINKEDIN_STUDIO_DATA:-…}/`; `:44` = state file (correct), not
|
||
a leak; refs via `${CLAUDE_PLUGIN_ROOT}/`.
|
||
- **P6 (graceful degradation): PASS** — empty weekly-reports/posts → proceed test-only (`:485–488`); 4
|
||
error modes (`:474–493`).
|
||
- **P7 (saves/dwell honesty): PASS** — no saves/dwell mention; eng-rate `:236` excludes saves (S16-consistent).
|
||
- **P8 (stateful artifact trace): PASS** — see SIMULATE; full analyze + history path sound against a
|
||
realistic file.
|
||
|
||
**HARDEN (1 surgical edit, `commands/ab-test.md`).**
|
||
1. [REWORK · axis-b′] `:128` "(Tue-Thu, 8-9 AM optimal)" → "(Tue-Thu mornings)" — dropped the precision
|
||
over-claim ("optimal" + the inaccurate 8 AM window); the line stem already cites
|
||
`algorithm-signals-reference.md`, so source attribution is retained.
|
||
|
||
**Out-of-scope (logged, NOT touched in S18).** `:284` "baseline comparison" cross-ref vs `weekly-reports/`
|
||
— echoes the S15 "baselines/metadata phantom" vocabulary, but here it reads an existing artifact (the
|
||
report command's weekly reports), optional + graceful-degraded; legitimate soft context, not a phantom.
|
||
Left as-is (low, no-change).
|
||
|
||
**VERIFY.**
|
||
- Re-grepped final file: `grep "8-9 AM optimal"` → **NONE**; `:128` = "...based on
|
||
algorithm-signals-reference.md (Tue-Thu mornings)".
|
||
- `git diff --stat` = 1 file, **+1 / −1**.
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`, **exit 0**; counts **29/19**
|
||
unchanged (.md-only edit).
|
||
- Disposition: **FIXED** (1 edit, `commands/ab-test.md`) · 0 deferrals · P1–P3,P5–P8 PASS, P4 PASS post-fix.
|
||
|
||
---
|
||
|
||
### /linkedin:strategy — phase-aware growth + authority-building plan (S19)
|
||
|
||
**INTENT.** Grow-journey front-door (CLAUDE.md elevates `onboarding`/`strategy` as the Start/Grow
|
||
front-doors) **and** atomic command. Prose-heavy, **no Bash** (`allowed-tools`: Read·Glob·Grep·
|
||
WebFetch·AskUserQuestion·Task). Reads state (`follower_count` → phase auto-detect) + 8 local refs,
|
||
delegates the growth recommendation to `strategy-advisor`, routes the profile audit to
|
||
`/linkedin:profile`. Most composite non-CLI class: prose + state-read + agent-delegation + routing +
|
||
axis-b′.
|
||
|
||
**SIMULATE (ICP author, grounded prose-trace — no fixture; touches no real state/data).** "What should
|
||
I focus on at my level?" → Step 0.5 auto-detects phase from `follower_count` (graceful: `:43` falls
|
||
through to Step-1 questions if 0/missing) → AskUserQuestion → Step 3 delegates to
|
||
`linkedin-studio:strategy-advisor` (`:154`) → phase strategy + Step 3.5 trajectory overlay (graceful:
|
||
`:285` "Skip this step. Note: …" when no milestone data) → Step 3.6 authority-building (Phase 2+).
|
||
Friction: none surfaced; phase bands consistent between Step 0.5 (`:38–42`) and the Step 2 headers
|
||
(`:79/:93/:107/:122/:137`).
|
||
|
||
**EVALUATE (4 axes; mechanical predicate per axis).**
|
||
- **(a) intention: PASS** — body delivers exactly the description: phase-specific guidance 0–1K→10K+,
|
||
trajectory overlay, Phase 2+ signature-content compounding.
|
||
- **(b) algorithm/format-bar (axis-b′): PASS** — topic-relevance is the SSOT signal (`:88/:175/:520`);
|
||
growth rates / timelines are roadmap-sourced (command loads `growth-roadmaps.md` +
|
||
`trajectory-strategy-adjustments.md`), not postulated in-command; saves-honesty intact (`:300/:408`
|
||
"read from native LinkedIn analytics — not auto-tracked by this tool").
|
||
- **(c) quality rules: PASS** — no buzzwords, no body-links; prose-only command (no post emission).
|
||
- **(d) agent-wiring + graceful degradation: 1 FINDING** — P-checks: agent `linkedin-studio:strategy-advisor`
|
||
(`:154`, namespaced) → `agents/strategy-advisor.md` **resolves**; routing `/linkedin:profile`
|
||
(`:293/:361`) → `commands/profile.md` **resolves**; 8/8 referenced files exist; graceful degradation
|
||
present (`:43/:285/:408`). **allowed-tools over-grant:** `:16` `WebFetch` is the ONLY occurrence in
|
||
the file (`grep -n WebFetch` = `:16` only) — never used in the body (no URL fetch anywhere; body reads
|
||
local refs + state, delegates, AskUserQuestion); delegate `strategy-advisor` is Read+Glob only, so it
|
||
doesn't need it either. Exact S17 pattern (`62b7081`). Borderline Grep/Glob left as-is (local,
|
||
low-risk, plausibly used for state/analytics reads) — only the external-network WebFetch trimmed.
|
||
|
||
**HARDEN (1 surgical edit, `commands/strategy.md`).**
|
||
1. [REWORK · axis-d] `:16` removed `- WebFetch` from `allowed-tools` — orphan grant (external-network
|
||
capability, zero usage). allowed-tools now Read·Glob·Grep·AskUserQuestion·Task.
|
||
|
||
**VERIFY.**
|
||
- Re-grepped final file: `grep -n WebFetch commands/strategy.md` → **NONE**; allowed-tools block =
|
||
Read/Glob/Grep/AskUserQuestion/Task.
|
||
- `git diff --stat` = 1 file, **−1**.
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`, **exit 0**; counts **29/19**
|
||
unchanged (.md-only edit).
|
||
- Disposition: **FIXED** (1 edit, `commands/strategy.md`) · 0 deferrals · axes a/b/c PASS, axis-d PASS post-fix.
|
||
|
||
---
|
||
|
||
### /linkedin:competitive — niche thought-leader analysis → landscape map · gap matrix · differentiation plan (S20)
|
||
|
||
**INTENT.** Grow-journey atomic command (with `monetize`/`outreach`/`profile`). Prose-heavy guided
|
||
analysis — **not** post-emitting, **not** routing (no `/linkedin:Y` emitted), **not** own-data
|
||
analytics. Six-step flow (Step 0–6) producing prose output (no persisted artifact: no `Write`/`Task`).
|
||
User names 3–5 competitor profiles (or asks for help finding them) → competitive map + gap analysis +
|
||
differentiation plan. Ethics Note (`:155-157`) steers toward inspiration, not copying.
|
||
|
||
**SIMULATE (ICP author, grounded prose-trace — no fixture; touches no real state/data).** Two branches
|
||
proved against Step 0–6 (`:19-153`): **A** (names 3 profiles, choice 1 `:30`) → reads SKILL.md+state
|
||
(`:21-23`) → per-competitor template Step 2 (`:46-76`, all fields are `[placeholders]` the user/observation
|
||
fills — no fabricated data) → landscape map Step 3 → gap matrix Step 4 → differentiation plan Step 5 →
|
||
takeaways Step 6 → Ethics Note. **B** (choice 2 "help me find", `:31`) → triggers WebSearch (`:34`) →
|
||
same analysis loop. **No branch invokes WebFetch.**
|
||
|
||
**EVALUATE (relevant axes; mechanical predicate each).**
|
||
- **(a) intention: PASS** — body delivers exactly the description: posting frequency, content types,
|
||
hooks, engagement strategy, gaps → differentiation.
|
||
- **(b) algorithm/format-bar (axis-b′): PASS** — `grep -nE "[0-9]+ ?(%|x|times)"` = **NONE**; format fields
|
||
(`:54` `text X%/carousel Y%/video Z%`) are letter-placeholders, not postulated stats; folklore grep hit
|
||
only `:8`/`:116` (false positives). No unsourced algorithm claim.
|
||
- **(c) quality rules: PASS** — prose-only command (no post emission), Ethics Note present.
|
||
- **(d) agent-wiring + graceful degradation: 1 FINDING** — no agent delegation (selbständig prose,
|
||
correct for class); 5/5 referenced files resolve (`:21,:161-163` + state PRESENT); graceful degradation
|
||
present (Step 1 AskUserQuestion 3 branches + WebSearch fallback `:34`). **allowed-tools over-grant:**
|
||
`:11` `WebFetch` was the ONLY occurrence (`grep -n WebFetch` = `:11` only) — never used in the body;
|
||
WebSearch (`:12` grant + `:34` real use) covers the entire legitimate external need (finding thought
|
||
leaders), and WebFetch against LinkedIn profile URLs cannot deliver the public feed content the grant
|
||
implies (login/anti-bot). Exact S17/S19 pattern. Borderline Glob (`:10` grant, no body use) left as-is
|
||
(local, low-risk read tool) — only external-network WebFetch trimmed.
|
||
|
||
**HARDEN (1 surgical edit, `commands/competitive.md`).**
|
||
1. [REWORK · axis-d] `:11` removed `- WebFetch` from `allowed-tools` — orphan grant (external-network
|
||
capability, zero usage, not realizable for LinkedIn profiles). allowed-tools now
|
||
Read·Glob·WebSearch·AskUserQuestion.
|
||
|
||
**VERIFY.**
|
||
- Re-grepped final file: `grep -n WebFetch commands/competitive.md` → **NONE**; allowed-tools block =
|
||
Read/Glob/WebSearch/AskUserQuestion.
|
||
- `git diff --stat` = 1 file, **−1**.
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`, **exit 0**; counts **29/19**
|
||
unchanged (.md-only edit).
|
||
- Disposition: **FIXED** (1 edit, `commands/competitive.md`) · 0 deferrals · axes a/b/c PASS, axis-d PASS post-fix.
|
||
|
||
---
|
||
|
||
### /linkedin:monetize — turn thought leadership into revenue (readiness → stage strategy → lead magnet → funnel → tracking)
|
||
|
||
**INTENT.** Prose-heavy guided monetization strategist (Grow-tier atomic, unlocks ~1K followers). 9-step flow
|
||
(Step 0–8 `:22-487`): load refs → readiness scorecard → stage-specific strategy → lead-magnet blueprint →
|
||
funnel content → CTA optimization → Featured section → revenue model worksheet → tracking dashboard. Output is
|
||
prose templates the user fills in; **persists nothing**.
|
||
|
||
**SIMULATE (aspiring monetizer, grounded prose-trace — no fixture; touches no real state/data).** Traced
|
||
Step 0–8 against the file. Step 1 collects follower count / engagement / offers / goals via **AskUserQuestion**
|
||
(`:37`) — does NOT read state, so it degrades gracefully (asks rather than depends). Stage-1 branch (0–1K,
|
||
score 0–30, `:94-112`) handles the sub-unlock case honestly ("Revenue is secondary… DO NOT sell aggressively").
|
||
All scorecards/blueprints/worksheets are `[placeholder]` fill-ins — no fabricated user data. **No branch
|
||
invokes WebFetch, Write, Glob, or Grep.**
|
||
|
||
**EVALUATE (relevant axes; mechanical predicate each).**
|
||
- **(a) intention: PASS** — body delivers the description (readiness scoring, stage strategy, lead magnets,
|
||
CTA A/B, funnel, consulting-inquiry tracking). 7/7 referenced files resolve (Step 0 + Reference Files).
|
||
- **(b) algorithm/conversion-bar (axis-b′): 1 FINDING** — `:344-346` postulated a generic time-decay curve
|
||
("same-day ~half / next-day 20-30% / **3+ days <10%**"). SSOT (`opportunity-generation.md:302-310`) gives
|
||
**per-channel** rates (speaking 30-50% same-day, consulting 20-30% within-24h, podcast 70-80%) + qualitative
|
||
"speed matters", NOT a generic decay curve — and **`<10%` is invented**, no SSOT basis. Same over-precise
|
||
-unsourced pattern as S18. (`:495` external-links claim was already SSOT-sourced + softened — clean.)
|
||
- **(c) quality rules: PASS** — prose-only (no post emission); no buzzwords; `:495` correctly sourced.
|
||
- **(d) agent-wiring + graceful degradation: 2 FINDINGS (allowed-tools over-grant)** — no agent delegation
|
||
(selbständig prose, correct for class). **`:13` `WebFetch`** = orphan grant, ONLY occurrence (`grep -n
|
||
WebFetch` = `:13` only), zero body use, WebSearch not even granted → pure dead external-capability grant,
|
||
exact S17/S19/S20 pattern. **`:15` `Write`** = orphan grant, ONLY occurrence, zero body use — command
|
||
never persists a file; unused **mutation** capability (stronger least-privilege case than read grants).
|
||
Borderline `Glob`/`Grep` (`:11-12`, no body use) left as-is (local, low-risk read tools — S20 precedent).
|
||
|
||
**HARDEN (2 surgical edits, `commands/monetize.md`).**
|
||
1. [REWORK · axis-d] removed `- WebFetch` and `- Write` from `allowed-tools` — orphan grants (zero usage;
|
||
external-fetch never realized, file never written). allowed-tools now Read·Glob·Grep·AskUserQuestion.
|
||
2. [REWORK · axis-b′] `:343-346` replaced the fabricated decay curve (incl. invented `<10%`) with the
|
||
SSOT-supported qualitative claim ("speed compounds conversion — same-day converts far better than
|
||
days-later") + cite to `references/opportunity-generation.md` for the real per-channel benchmarks.
|
||
`:313/:323` "within 2 hours" left as-is (defensible tactical advice, not a fabricated statistic — operator OK).
|
||
|
||
**VERIFY.**
|
||
- Re-grepped final file: `grep -n WebFetch` → **NONE**; `grep -n Write` → **NONE**; `grep -n "roughly
|
||
half|<10%|3+ days|20-30% conversion"` → **NONE**; allowed-tools block = Read/Glob/Grep/AskUserQuestion.
|
||
- `git diff --stat` = 1 file, **+3 / −6**.
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`, **exit 0**; counts **29/19**
|
||
unchanged (.md-only edit).
|
||
- Disposition: **FIXED** (2 edits, `commands/monetize.md`) · 0 deferrals · axes a/c PASS, axes b′/d PASS post-fix.
|
||
|
||
---
|
||
|
||
### /linkedin:outreach — collab + speaking outreach orchestrator (track select → readiness → targets → format/pitch → production → pipeline persist) (S22)
|
||
|
||
**INTENT.** Grow-tier **orchestrator** (unlocks ~1K), prose-heavy **+ agent-delegating + state-persisting**.
|
||
Two tracks (collab + speaking) under one paradigm, 11 steps (Step 0–10 `:65-1252`). Absorbed
|
||
`collab`/`speaking`/`authority` (capability checklist `:36-63` maps each predecessor function). **First in
|
||
the queue to BOTH delegate to an agent AND mutate real state** — shifts the weight onto axis-d.
|
||
|
||
**SIMULATE (adversarial cold quality-reviewer; grounded prose-trace + throwaway state fixture — operator
|
||
chose the thorough path; touches no real state/data-dir).** Traced Step 0–10 against the file; verified every
|
||
mechanical claim (refs resolve, agent resolves, mutation exists, grants match body use, axis-b′ claims trace
|
||
to SSOT) with a tool, never by reasoning.
|
||
|
||
**EVALUATE (relevant axes; mechanical predicate each).**
|
||
- **(a) intention: PASS** — checklist maps both predecessors; all 5 refs + SKILL.md resolve (Step 0 +
|
||
Reference Files); state path `~/.claude/linkedin-studio.local.md` correct post-M0 (per CLAUDE.md).
|
||
- **(b) algorithm/conversion-bar (axis-b′): 2 FINDINGS.** **B1** `:652` "DM amplification can boost
|
||
first-hour engagement **2-3x**" — over-quantified: 2-3x is SSOT for *collab reach*
|
||
(`collaborations-guide.md:32/165/187`), while the DM-loop's first-hour effect is held *qualitative* there
|
||
(`:335`). **B2** `:921-946` Step 7 pod-warning fabricated magnitudes — "**30-55%** reach reduction",
|
||
"shadow suppression", "account credibility score reduction", "penalizes", "**Post-March 2025** Update" +
|
||
"low dwell" detection (dwell unmeasurable per CLAUDE.md). SSOT (`algorithm-signals-reference.md:98`) states
|
||
ONLY: "auto-comments demoted out of 'Most Relevant', scoped to own network; repeat offenders restricted"
|
||
(Sachdeva 2026-02-16, high) + design rule "do not quote a percentage / soften penalty". Same S18/S21 +
|
||
follow-up-#1 pattern.
|
||
- **(c) quality rules: PASS** — prose templates only (no post emission); "thought leader"/"ecosystem"
|
||
appear only in search-queries / internal *goals* (`:230/:813/:820`), not in published post-copy (Rule 4).
|
||
- **(d) agent-wiring + grants + state + graceful: 3 FINDINGS.** **D1 PASS** — `Task subagent_type:
|
||
linkedin-studio:network-builder` (`:172`) resolves (`agents/network-builder.md`). **D2 PASS** —
|
||
`recordOutreachContact` is **NOT fabricated** (`state-updater.mjs:294`, CLI `--record-outreach :394`,
|
||
additive, `$`-safe). **D3 Bash MISSING** — Step 8c (`:1064`) runs `node …state-updater.mjs --record-outreach`
|
||
(needs Bash) but frontmatter didn't grant it; `firsthour.md` grants Bash for the same `node`-mutation
|
||
pattern → functional defect (command promised a mutation it couldn't execute). **D4 WebFetch orphan**
|
||
(`:19`, zero body use). **D5 Write orphan** (`:21`; `:410/:1040` are prose "Write a post", not tool use).
|
||
|
||
**HARDEN (5 edits across 2 files — operator-approved scope extension to also fix the SSOT-diverging guide
|
||
source so the B2 rewrite can't be re-rendered from a stale reference).**
|
||
1. [REWORK · axis-d] `commands/outreach.md` frontmatter: dropped `- WebFetch` + `- Write` (orphans),
|
||
added `- Bash` (Step 8c needs it). allowed-tools now Read·Glob·WebSearch·AskUserQuestion·Task·Bash
|
||
(borderline Glob kept — S20 precedent).
|
||
2. [REWORK · axis-b′] `:652` (B1): re-attributed 2-3x to collab reach (where SSOT places it), kept the
|
||
first-hour effect qualitative + cite to `collaborations-guide.md`.
|
||
3. [REWORK · axis-b′] Step 7 (B2): replaced "Post-March 2025 Update" header + the fabricated
|
||
detection/penalties block with the SSOT crackdown statement (demote out of "Most Relevant" / scope to own
|
||
network / restrict repeat offenders) + cite; softened "penalizes", dropped "low dwell".
|
||
4. [REWORK · axis-b′, scope-extension] `references/collaborations-guide.md:349-370` (B2 propagation): same
|
||
SSOT rewrite of the "March 2025 Crackdown" section — dropped "shadow banning ALL future content / 90+ days
|
||
/ 6+ months / permanent reach penalty", the date, and the speculative detection methods (browser-extension
|
||
tracking, linguistic fingerprinting).
|
||
|
||
**VERIFY.**
|
||
- Re-grepped final files. `commands/outreach.md`: `grep WebFetch` → **NONE**, `^ - Write$` → **NONE**,
|
||
`^ - Bash$` → `:21`; B1 old form → **NONE** (new `:651`); B2 `30-55%|Post-March 2025|penalizes engagement
|
||
pods|credibility score|Low dwell|Shadow suppression` → **NONE** (SSOT text `:928-929`).
|
||
`references/collaborations-guide.md`: `90+ days|6+ months|never fully recover|permanent reach
|
||
penalty|Shadow banning affects ALL|March 2025 Crackdown` → **NONE** (SSOT text `:349/:364`).
|
||
- **D2/D3 throwaway fixture** (`/private/tmp/claude-*`, fictional, deleted after): `recordOutreachContact`
|
||
direct-call (no `writeState`), **10/10 assertions PASS** — additive insert after `last_post_date`,
|
||
`outreach_active` untouched when undeclared, pre-existing fields + custom section intact, newest-first
|
||
append, **`$`-safe** (`@kari$boss` preserved literally — S13 behavioral), section auto-created.
|
||
- `git diff --stat` = 2 files, **+20 / −28**.
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`, **exit 0**; counts **29/19**
|
||
unchanged (.md-only edits — node hook/analytics tests not triggered).
|
||
- Disposition: **FIXED** (5 edits, 2 files) · 0 deferrals · axes a/c PASS, axes b′/d PASS post-fix.
|
||
Note: state-updater HTML-comment lands between newest+older rows on repeat append = cosmetic, logged as
|
||
non-scope follow-up (the command calls the mutation correctly).
|
||
|
||
---
|
||
|
||
### /linkedin:profile — profile/topic-relevance + profile-SEO audit walkthrough (7 sections → prioritized action plan); self-contained, no agent/routing/state (S23)
|
||
|
||
**INTENT.** Grow-tier **atomic execution command**, prose-heavy + guided (`AskUserQuestion`).
|
||
**Self-contained** — no agent delegation, no `/linkedin:Y` routing, no state mutation. Audits the
|
||
profile for the topic-relevance ranking signal + profile search-SEO.
|
||
|
||
**SIMULATE (adversarial cold quality-reviewer; grounded prose-trace — pure prose, NO fixture: command
|
||
reads/mutates no state).** Headline section (`:79-96`) traced against operator input
|
||
`"AI-rådgiver | KI-transformasjon | Innholdsprodusent"` → checklist `:85-88` yields NO audience / NO
|
||
outcome / vague titles → concrete non-generic rewrite direction. Every mechanical claim (grant grep, SSOT
|
||
read, pattern greps) from a tool, never by reasoning.
|
||
|
||
**EVALUATE (relevant axes; mechanical predicate each).**
|
||
- **(a) intention: PASS** — desc + Grow-journey role coherent; 3 refs (`algorithm-signals-reference.md`,
|
||
`troubleshooting-guide.md`, `SKILL.md`) resolve.
|
||
- **(b) algorithm-bar (axis-b′): 1 systemic FINDING (B1) + 1 minor (B2).** **B1 — "profile-as-validated-object"
|
||
overclaim, 9 sites:** the file repeatedly asserts the relevance model **reads & validates your profile**
|
||
and gates distribution on it — `:5` "validates your profile BEFORE distributing content"; `:31` "the model
|
||
**evaluates five criteria** (see SSOT)" (**false SSOT attribution** — SSOT has no such breakdown); `:35-39`
|
||
"Impact if Missing: **HIGH/HIGH/MEDIUM…**" (postulated coefficients SSOT forbids, `:14/:23`); `:99` "first
|
||
signal telling … what you're **qualified** to discuss"; `:161` "relevance **validation**"; `:173` "**checks
|
||
if** you're connected"; `:230` "if LinkedIn's AI **read my profile**, would it **believe**"; `:51/:67` SEO
|
||
conflation ("**tell the relevance model** what you're expert in" / "the **relevance model's expertise
|
||
signal**"). SSOT (`algorithm-signals-reference.md:25-30/:85-90`) confirms ONLY: topic/interest relevance is a
|
||
ranking input — content matched to a **viewer's interests** is distributed incl. beyond your network (Jurka,
|
||
high); **no off-topic magnitude figure; no weighted profile criteria**. File was **internally inconsistent** —
|
||
its own soft, SSOT-true framing already at `:17/:25/:27`. Same class as follow-up #1 (`audit.md:61`) +
|
||
v4.0.0 "reconcile to one sourced statement". **B2** `:24` "Goes to **10% of audience**" — postulated
|
||
old-model number (`:131` "40%" is an illustrative impact-bullet example → PASS).
|
||
- **(c) quality rules: PASS** — no post emission (audit prose only); buzzword list cited correctly (`:73`
|
||
steers away from "thought leader/guru/ninja").
|
||
- **(d) grants + graceful + wiring: PASS.** **Grant-hygiene REN (two-way):** `allowed-tools` = `Read` +
|
||
`AskUserQuestion` (`:11-13`), both used (`:21/:27/:31/:236` Read; `:77` AskUserQuestion); WebFetch / Write /
|
||
Bash / Task / subagent_type / .mjs → **NONE** (no orphans, no missing node/Bash grant). No agent/routing.
|
||
Graceful degradation OK (Q&A runs regardless of ref availability).
|
||
- **P3 — profile-SEO honesty (`:41-73`): largely defensible** (about LinkedIn *search*, not the ranking
|
||
model; correctly does NOT cite SSOT). One practitioner-claim-as-fact: "**highest-weight** search field"
|
||
(`:52` prose + `:61` table) — self-justified mechanically, softened not removed.
|
||
|
||
**HARDEN (12 edits, 1 file — `commands/profile.md`; all surgical re-source to SSOT framing).**
|
||
1. [REWORK · b′] `:5` desc — "validates your profile BEFORE distributing content" → "a coherent, on-topic
|
||
profile reinforces the topic-relevance signal LinkedIn uses to decide how widely your content is distributed".
|
||
2. [REWORK · b′] `:24` (B2) — "Goes to 10% of audience" → "a slice of your network sees it".
|
||
3. [REWORK · b′] `:25/:27` — "content matched to **your demonstrated expertise**" → "**a viewer's interests**"
|
||
(SSOT wording) + added "LinkedIn confirms no off-topic reach-reduction figure — not a quantified penalty".
|
||
4. [REWORK · b′] `:31` + table `:33-39` — dropped "the model evaluates five criteria (see SSOT)" + HIGH/MEDIUM
|
||
"Impact if Missing" weights → "LinkedIn does **not** publish a profile-scoring breakdown … practitioner
|
||
heuristics", column → "Priority (heuristic)", High/Medium relabeled directional.
|
||
5. [REWORK · b′] `:99` — "first signal telling … qualified to discuss" → "the clearest place to state … the
|
||
strongest single contribution to a coherent topic signal".
|
||
6. [REWORK · b′] `:161` — "Critical for profile/topic-relevance validation" → "Your top skills are a strong,
|
||
searchable topic signal".
|
||
7. [REWORK · b′] `:173` — "relevance checks if you're connected" → "a network concentrated in your expertise
|
||
area reinforces your topic signal and social proof (heuristic — LinkedIn doesn't publish network as a
|
||
profile-ranking factor)".
|
||
8. [REWORK · b′] `:230` — "If LinkedIn's AI read my profile, would it believe I'm an expert" → "Does my
|
||
profile make it obvious — to a human and to LinkedIn's topic-matching".
|
||
9. [REWORK · b′] `:51/:67` SEO conflation — "tell the relevance model what you're expert in" → "make your
|
||
topic legible … to topic-relevance distribution"; "the relevance model's expertise signal" → "carry your
|
||
strongest on-topic signal".
|
||
10. [NICE · P3] `:52/:61` — "highest-weight search field" → "highest-leverage" (×2; rest self-justified, kept).
|
||
|
||
**VERIFY.**
|
||
- Re-grepped final file. All overclaim/conflation patterns (`validates your profile before | evaluates five
|
||
criteria | first signal telling | qualified to discuss | checks if you're connected | relevance validation |
|
||
if linkedin's ai read | 10% of audience | highest-weight | demonstrated expertise | Impact if Missing | tell
|
||
the relevance model | relevance model's expertise signal`) → **NONE**. New SSOT text present (`:26/:28/:33/
|
||
:38/:54/:178/:235`).
|
||
- Grant-hygiene unchanged: `allowed-tools` = Read + AskUserQuestion (`:11-13`); WebFetch/Write/Bash/Task/.mjs → **NONE**.
|
||
- `git diff --stat` = 1 file, **+28 / −22**.
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`, **exit 0**; counts **29/19**
|
||
unchanged (.md-only edits — node hook/analytics tests not triggered).
|
||
- Disposition: **FIXED** (12 edits, 1 file) · 0 deferrals · axis-b′ **FIXED**, axes a/c/d **PASS** · no
|
||
fixture needed (pure prose, no state).
|
||
|
||
---
|
||
|
||
### /linkedin:create — content-creation front-door (guided "what to make?" → routes to the owning creation command); delegates only, no drafting (S24)
|
||
|
||
**CLASS: routing front-door** (Create-journey, v4.1). Predicate: every emitted `/linkedin:Y` resolves to
|
||
`commands/Y.md`; bidirectional grant-hygiene; "delegates only" (never drafts).
|
||
|
||
**TEST (tool-grounded).**
|
||
- Routing (main axis): **8/8 targets resolve** — `commands/{post,quick,react,carousel,video,multiplatform,
|
||
batch,newsletter}.md` all present (menu `:38-45` / table `:54-63`).
|
||
- Grant-hygiene (bidirectional): `Read` (`:13`) used at `:26` (fixed-path state read); `AskUserQuestion`
|
||
(`:15`) used at `:36`; **`Glob` (`:14`) ORPHAN** — zero body usage, only file access is the fixed-path
|
||
Read (`:26`; Read handles the "if exists" itself — no pattern-matching). No missing grants (routing =
|
||
narrative handoff, no tool).
|
||
- Delegates-only: all 4 "draft" hits are the no-draft promise (`:7/:22`, long-form lock `:66`) or
|
||
target-description (`:38` = what `/linkedin:post` does); no drafting leak.
|
||
- Persona (ICP author, no format named): Step 0 `:24-28` state glance → Step 1 `:30-45` AskUserQuestion
|
||
(8 opts) → Step 2 `:47-63` route into target workflow, steps not inlined; URL fast-path `:32-34` →
|
||
`/linkedin:react`. ✓
|
||
|
||
**HARDEN (1 edit, 1 file — `commands/create.md`).**
|
||
1. [REWORK · grant-hygiene] dropped orphan `Glob` from `allowed-tools` (`:14`); net = {Read, AskUserQuestion}.
|
||
|
||
**VERIFY.**
|
||
- Re-grep final: `Glob` → **NONE**; `allowed-tools` = Read + AskUserQuestion (`:13-14`).
|
||
- `git diff --stat` = 1 file, **−1**.
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`, **exit 0**; counts **29/19**
|
||
unchanged (.md-only edit).
|
||
- Disposition: **FIXED** (1 edit, 1 file) · 0 deferrals · routing axis **PASS**, delegates-only **PASS**,
|
||
grant-hygiene **FIXED** · no fixture (pure routing prose).
|
||
- Scope note: `Glob`-orphan may be systemic in sibling front-doors (`measure` S25, `linkedin` S26) — not
|
||
expanded (one command per session); each catches its own.
|
||
|
||
---
|
||
|
||
### /linkedin:measure — performance front-door (guided "how am I doing?" → routes to the owning analytics command); delegates only, no number-crunching (S25)
|
||
|
||
**CLASS: routing front-door** (Measure-journey, v4.1). Predicate: every emitted `/linkedin:Y` resolves to
|
||
`commands/Y.md`; bidirectional grant-hygiene; "delegates only" (never crunches numbers).
|
||
|
||
**TEST (tool-grounded).**
|
||
- Routing (main axis): **5/5 targets resolve** — `commands/{import,report,analyze,audit,ab-test}.md` all
|
||
present (menu `:31-35` / table `:44-48` / order-note `:51`).
|
||
- Grant-hygiene (bidirectional): `AskUserQuestion` (`:13`) used at `:29`; `Glob` (`:12`) + `Read` (`:11`) —
|
||
neither named a tool in body, both hung only on the **optional Step 0** filesystem glance (`:22-25`,
|
||
pre-edit). UNLIKE `create` (Glob a pure orphan, no fs-check at all), measure HAS a Step 0 ("if
|
||
`…/analytics/` holds imported data, note last import"), giving `Glob` a real home — but the grant↔use
|
||
link was implicit (body named no tool).
|
||
- Delegates-only: 3 no-do promises (`:7`, `:20`, `:40`); all crunch/analysis hits are negations — no leak.
|
||
- Persona (ICP author, "how am I doing?"): Step 0 optional analytics glance → Step 1 AskUserQuestion (5
|
||
opts) `:29` → Step 2 route into target workflow `:37-48`; order-note `:50-51` (no data → import first). ✓
|
||
- No fixture (pure routing prose).
|
||
|
||
**HARDEN (operator-chosen — explicit `Glob`, drop `Read`; 2 edits, 1 file — `commands/measure.md`).**
|
||
1. [REWORK · grant-hygiene] dropped `Read` from `allowed-tools` (`:11`, pre-edit) — redundant; the Step 0
|
||
import-date need is met by a `Glob` listing of the analytics dir.
|
||
2. [REWORK · grant↔use traceability] made Step 0 explicit: "You may `Glob` `…/analytics/`; if it holds
|
||
imported data, note last import" (`:23`), giving the retained `Glob` grant a named body home.
|
||
Net grants = {Glob, AskUserQuestion}.
|
||
|
||
**VERIFY.**
|
||
- Re-grep final: `Read` as a tool name → **NONE**; `allowed-tools` = Glob + AskUserQuestion (`:11-12`);
|
||
`Glob` now in body `:23`.
|
||
- Routing unchanged: 5 distinct targets, all present.
|
||
- `git diff --stat` = 1 file, **+1/−2**.
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`, **exit 0**; counts **29/19**
|
||
unchanged (.md-only edit).
|
||
- Disposition: **FIXED** (2 edits, 1 file) · 0 deferrals · routing axis **PASS**, delegates-only **PASS**,
|
||
grant-hygiene **FIXED** (Glob retained + made explicit, Read dropped).
|
||
- Scope note: the `Glob`-orphan question is now resolved for `measure` (it had a home via Step 0; `create`
|
||
did not). `linkedin` (S26) still to check independently.
|
||
|
||
---
|
||
|
||
### /linkedin:linkedin — top router (five-journey menu + status line + queue glance); routes to every command/agent, executes nothing (S26)
|
||
|
||
**CLASS: routing** (the master router over the whole surface — the largest routing check: 28 command
|
||
pointers + 10 agent pointers). Predicate: every emitted `/linkedin:Y` resolves to `commands/Y.md`; every
|
||
"Suggest `X` agent" resolves to `agents/X.md`; bidirectional grant-hygiene; "delegates only" (shows
|
||
menu/status, never executes).
|
||
|
||
**TEST (tool-grounded).**
|
||
- Routing (main axis): **28/28 distinct `/linkedin:Y` targets resolve** to `commands/Y.md` (menu `:66-127`,
|
||
ask-menu `:136-140`, direct-routing `:157-198`) — the full sub-command surface (+`linkedin` itself = 29).
|
||
- Agent pointers (axis d): **10/10** "Suggest `X` agent" resolve to `agents/X.md` (analytics-interpreter,
|
||
content-optimizer, content-planner, content-repurposer, differentiation-checker, engagement-coach,
|
||
network-builder, post-feedback-monitor, trend-spotter, voice-trainer).
|
||
- Grant-hygiene (bidirectional): `allowed-tools` = Read/Bash/AskUserQuestion (`:8-11`); each has a body home
|
||
— Read→state status line (`:20`), Bash→node queue-manager block (`:34-42`), AskUserQuestion→sub-choice
|
||
(`:145`); **no ungranted tool referenced, no Glob at all** — third front-door is clean (unlike create's
|
||
Glob-orphan / measure's Glob-via-Step-0).
|
||
- Delegates-only: body routes/suggests; `:146` "invoke it directly" = hand-off to the command, not
|
||
self-execution. No drafting / number-crunch. PASS.
|
||
- Graceful degrade (axis d): no-state fallback (`:28`) + empty-queue fallback (`:57`). PASS.
|
||
- Persona (ICP author types `/linkedin`): graceful-degrade path — no fixture (pure routing prose).
|
||
|
||
**FINDING (terminology — operator rule surfaced this session): "thought leadership" must not appear in the
|
||
plugin (it is named LinkedIn Studio).** linkedin.md used it at `:4` (frontmatter description) + `:16` (body).
|
||
|
||
**HARDEN (2 edits, 1 file — `commands/linkedin.md`).**
|
||
1. [REWORK · terminology] `:4` "Main router for LinkedIn **thought leadership** commands" → "Main router for
|
||
**LinkedIn Studio** commands".
|
||
2. [REWORK · terminology] `:16` "You are a LinkedIn **thought leadership** assistant" → "You are the
|
||
**LinkedIn Studio** assistant".
|
||
|
||
**VERIFY.**
|
||
- Re-grep final: "thought leadership" in linkedin.md → **NONE**; `:4`/`:16` show "LinkedIn Studio".
|
||
- Routing/agent axes unchanged: 28 + 10 targets all resolve.
|
||
- `git diff --stat` = 1 file, **+2/−2**.
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`, **exit 0**; counts **29/19**
|
||
unchanged (.md-only prose edit).
|
||
- Disposition: **FIXED** (2 edits, 1 file) · 0 deferrals · routing **PASS**, agent-wiring **PASS**,
|
||
grant-hygiene **PASS** (clean — no orphan; third front-door has no Glob), delegates-only **PASS**.
|
||
|
||
**SURFACED (out of scope → queued as S29).** "thought leadership" appears plugin-wide on **85 lines across
|
||
~45 files** (commands, agents, references incl. the filename `references/thought-leadership-angles.md`,
|
||
skills, plus historical CHANGELOG/docs). Promoted to a dedicated terminology-scrub pass **S29**
|
||
(`docs/hardening/plan.md`); the user-facing-vs-historical boundary + the file rename are open scoping
|
||
questions for that pass.
|
||
|
||
---
|
||
|
||
### S27 ref-consistency A — "penalty/penalizes" framing → SSOT (external links + topic-relevance)
|
||
|
||
**CLASS: reference-consistency** (NOT re-hardening of ✅ commands — surgical SSOT-reconciliation of a
|
||
documented divergence-class across multiple files; same tool-grounded discipline: read-and-show →
|
||
grep-confirm → re-grep final). Predicate: no surface frames external-link reach-loss or off-topic
|
||
reach-loss as a hard *named penalty* — SSOT (`references/algorithm-signals-reference.md:69-75`, `:89-90`)
|
||
says links **correlate with lower reach** (LinkedIn denies an *intentional* penalty) and topic relevance is
|
||
a **positive ranking input** with **no quantified off-topic figure**.
|
||
|
||
**TEST (tool-grounded).** Plugin-wide `grep -rniE "penaliz|penalty"` over commands/agents/references/skills.
|
||
The 3 plan-cited lines confirmed (line-nr drift ±1 since plan written: `analyze.md:101` not `:102`,
|
||
`audit.md:60` not `:61`). Catalog split into 4 buckets, operator-approved:
|
||
- **Bucket A (fix now):** 10 hard-penalty framings of link/topic-relevance in non-format files.
|
||
- **Bucket B (defer → S28):** 6 link-penalty lines in `references/linkedin-formats.md` (`:52,:91,:194,:198,:200,:432`) — S28 already opens that file for format magnitudes; reconcile link + format in one pass.
|
||
- **Bucket C (leave — already SSOT):** `first-comment-strategy.md:7`, `glossary.md:171` (def cites SSOT); glossary term `### Link Penalty :170` kept (def is correct).
|
||
- **Bucket D (surface → new S30):** other unsourced magnitudes (−68% hashtags, −25% short-post, −15-25% topic-gap, −30-50% bait, 55% AI-comment, −25%/post, poll-overuse) — a *different* class. Engagement-pod + AI-slop "penalized" left intact (officially confirmed, high confidence).
|
||
|
||
**HARDEN (10 edits, 6 files — Bucket A).**
|
||
1. `commands/audit.md:60` `(topic-relevance penalty)` → `(topic relevance is a ranking input — off-topic gets lower reach)`.
|
||
2. `agents/trend-spotter.md:338` `ranking will penalize off-topic content` → `Topic relevance is a ranking input — off-topic content gets lower reach`.
|
||
3. `agents/trend-spotter.md:353` `topic-relevance penalty, damages authority` → `off-topic content gets lower reach, damages authority`.
|
||
4. `references/troubleshooting-guide.md:15` `External link penalizing reach` → `External links correlate with lower reach`.
|
||
5. `references/troubleshooting-guide.md:127` `LinkedIn penalizes directing traffic away` → `body links correlate with lower reach (LinkedIn denies an intentional penalty; lead with value)`.
|
||
6. `commands/analyze.md:101` `External link penalizing reach` → `External links correlate with lower reach`.
|
||
7. `references/first-comment-strategy.md:3` `without triggering algorithm penalties` → `without the lower-reach correlation of body links`.
|
||
8. `references/first-comment-strategy.md:10` `Avoids link penalty` → `Avoids the body-link reach correlation`.
|
||
9. `references/first-comment-strategy.md:147` `Avoid link penalty` → `Avoid body-link reach hit`.
|
||
10. `references/url-processing-templates.md:396` `Reach penalty` → `Correlates with lower reach`.
|
||
|
||
**VERIFY.**
|
||
- Re-grep final: `topic-relevance penalty|will penalize off-topic` in audit/trend-spotter → **NONE**;
|
||
`link penalty|penalizing reach|penalizes directing|triggering algorithm penalties|Reach penalty` in the
|
||
4 link-files → **NONE**. All 10 new SSOT-framed lines present.
|
||
- `git diff --stat` = **6 files, +10/−10**.
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`, **exit 0**; counts **29/19**
|
||
unchanged (.md-only prose edits).
|
||
- Disposition: **FIXED** (10 edits, 6 files) · Bucket A complete · Bucket B → S28 · Bucket D → S30.
|
||
|
||
**SURFACED (out of scope → queued).** Bucket D unsourced-magnitude class promoted to **S30 magnitude-scrub**
|
||
(`docs/hardening/plan.md`). Bucket B link-penalty lines folded into **S28**'s pass over `linkedin-formats.md`.
|
||
|
||
---
|
||
|
||
### S28 ref-consistency B — format magnitudes + folded-in link lines → SSOT (`video.md` + `linkedin-formats.md`)
|
||
|
||
**CLASS: reference-consistency** (NOT re-hardening — surgical SSOT-reconciliation of format-magnitude
|
||
divergences across `commands/video.md` + `references/linkedin-formats.md`, incl. the 6 link-penalty lines
|
||
folded in from S27 Bucket B; one pass over the file). Predicate: no surface states an unsourced hard
|
||
format coefficient as fact — SSOT (`references/algorithm-signals-reference.md:51-63`, the "trust ordering,
|
||
test the number — never hard coefficients" rule `:12-23`) carries **no completion-rate gate**, no video ER,
|
||
no poll/Live multiplier; native video = "captions are the enforceable spec, no hard gate".
|
||
|
||
**TEST (tool-grounded).** Full read of `linkedin-formats.md` (674 l.) + `video.md:69` context. Plugin grep
|
||
`[0-9]+%|zero.distribution|penaliz|penalty|punish|multiplier|aspect ratio`. The "30% zero-distribution"
|
||
gate recurs **7×** (`video.md:69`; `formats:58,:291,:478,:543,:644,:662`). Catalog bucketed,
|
||
operator-approved (run buckets A+B+C+E now; D → S30):
|
||
- **A — "30% zero-distribution" hard-gate (7):** the named S28 divergence.
|
||
- **B — S27-folded link-penalty lines (6):** `formats:52,:91,:194,:198,:200,:432`.
|
||
- **C — other unsourced FORMAT magnitudes (6):** video 5.60% ER (`:56`), Live 12-24x (`:59,:594`), polls 1.64x (`:81`), 3x shares (`:273`), Creator-Mode 35% (`:674`). `:49` 24.42% left (already hedged + reconciled `:50`).
|
||
- **E — internal contradiction (1):** `:356` "3 seconds determine 70% of retention" contradicted the already-reconciled `:62/:100` folklore note → reframed.
|
||
- **D — S30-class in this file (deferred, operator's call):** `:6` 47-50% reach-decline, `:7` 15%→31%, `:176` AI-comment −30%/−55%, `:231/:279` hashtags −68%. **Left intact**; S30 re-scoped to cover them + the other files.
|
||
|
||
**HARDEN (20 edits, 2 files — Buckets A+B+C+E).**
|
||
- **A:** `video.md:69` + `formats:58,:291,:478,:543,:644,:662` → "completion/dwell is a ranking input; shorter completes better; no published hard completion gate".
|
||
- **B:** `formats:52,:91,:198,:200,:432` → "correlate with lower reach" framing; `formats:194` heading `### The External Link Penalty` → `### External Links and Lower Reach`.
|
||
- **C:** `formats:56` `5.60% ER` → "engagement varies (no reliable single rate)"; `:59,:594` `12-24x` → "strong … (multiplier unverified)"; `:81` `1.64x` → "declining; audience research only (no reliable multiplier)"; `:273` `3x more shares` → "tend to earn more shares"; `:674` `35% more reach` → "tend to see more reach (figure unverified)".
|
||
- **E:** `formats:356` `3 seconds determine 70% of retention` → "opening seconds carry the most weight … three-second hook is folklore".
|
||
|
||
**VERIFY.**
|
||
- Re-grep final: assertive 30%-gate forms (`requires 30%|30% minimum completion rate or|video gets zero distribution|…`) → **NONE** (remaining "completion gate" hits are the *negated* SSOT form "no hard completion gate"); link penalty/penalized/punished in `formats` → **NONE**; stray magnitudes (`5.60%|12-24x|1.64x|3x more shares|35% more reach`) → **NONE**; `:356` old → **NONE**.
|
||
- Bucket D preserved: `47-50%`, `15% to 31%`, `−68% reach`, AI-comment `30%/55%` still present (S30).
|
||
- `git diff --stat` = **2 files, +20/−20**.
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`, **exit 0**; counts **29/19** unchanged.
|
||
- Disposition: **FIXED** (20 edits, 2 files) · Buckets A+B+C+E complete · Bucket D → S30.
|
||
|
||
**SURFACED (out of scope → queued).** Bucket D (this file's non-format coefficients) deferred to **S30
|
||
magnitude-scrub**, which is re-scoped to: `linkedin-formats.md` D-lines (`:6,:7,:176,:231,:279`) + the other
|
||
files (`linkedin-growth-playbook`, `glossary`, `engagement-coach`, `firsthour`, `post-feedback-monitor`,
|
||
`ab-testing-framework`, `poll-strategy-guide`). Pod/AI-slop "penalized" stays intact (officially confirmed).
|
||
|
||
---
|
||
|
||
### S29a terminology-scrub — "thought leadership" → neutral (`commands/`)
|
||
|
||
**CLASS: terminology-scrub** (NOT re-hardening — surgical removal of the operator-banned brand phrase
|
||
"thought leadership", FORM A, from the user-facing command surface; memory `no-thought-leadership-phrase`).
|
||
First sub-pass of the plugin-wide S29 scrub. Predicate: no command surface uses the phrase "thought
|
||
leadership"; the plugin is named **LinkedIn Studio**.
|
||
|
||
**SCOPE (operator-locked for ALL S29 sub-passes a–e).**
|
||
- **FORM A only** ("thought leadership"). **FORM B** ("thought leader(s)" as role — 38 plugin-wide,
|
||
incl. the buzzword denylist `post.md:134`) = **untouched**.
|
||
- **Layers = user-facing surface + forward-looking docs.** Pure history (CHANGELOG, `hardening/log.md`+
|
||
`plan.md`, `voyage-build/*`, `remediation/*`) = untouched ("history → git"). Forward docs =
|
||
`docs/integration-test-guide.md` + `docs/expert-review/plan.md`.
|
||
- **Per-directory sub-passes, one per session.** Replacement map approved BEFORE edit.
|
||
- FORM A baseline = **140 in `.md`** (99 surface + 41 history) + 1 test-`.mjs`.
|
||
|
||
**TEST (tool-grounded).** Plugin grep `thought.leadership` (case-insensitive). `commands/` = 26 hits;
|
||
split: **11 filename pointers** to `references/thought-leadership-angles.md` (`post×3, video×2, batch×2,
|
||
react×1, pipeline×2, competitive×1`) → deferred to **S29e** (atomic rename + pointer update);
|
||
`competitive.md`/`pipeline.md` carry only pointers → untouched here. **15 in-file phrase occurrences** in
|
||
9 files = S29a scope. Read-and-show of all 15 in context before proposing the map.
|
||
|
||
**HARDEN (15 edits, 9 files — 1:1).**
|
||
- `post.md` (4): `:21` "LinkedIn thought leadership content creator" → "LinkedIn content creator";
|
||
`:64` "Select Thought Leadership Angle" → "Select Content Angle"; `:98` "Standard Thought Leadership
|
||
Structure" → "Standard Post Structure"; `:136` "thought leadership test" → "the insight test".
|
||
- `video.md` (1): `:146` "8 thought leadership angles" → "8 content angles".
|
||
- `strategy.md` (3): `:67` "thought leadership & authority" → "authority & influence"; `:234` table row
|
||
"Thought leadership pieces" → "Signature pieces"; `:395` "[Thought leadership milestone]" → "[Authority
|
||
milestone]".
|
||
- `batch.md` (1): `:113` "[thought leadership angle]" → "[content angle]".
|
||
- `setup.md` (2): `:256` "Which thought leadership angle" → "Which content angle"; `:323` "thought
|
||
leadership & authority" → "authority & influence".
|
||
- `react.md` (1): `:22` "into thought leadership posts" → "into insightful posts".
|
||
- `outreach.md` (1): `:813` "Demonstrate thought leadership depth" → "Demonstrate subject-matter depth".
|
||
- `onboarding.md` (1): `:18` "a LinkedIn thought leadership onboarding guide" → "the LinkedIn Studio
|
||
onboarding guide".
|
||
- `monetize.md` (1): `:18` "turn their thought leadership into revenue" → "turn their LinkedIn authority
|
||
into revenue".
|
||
|
||
**VERIFY.**
|
||
- Re-grep final, assertive non-filename FORM-A in `commands/` (`grep -v thought-leadership-angles`) →
|
||
**NONE**. Remaining `commands/` hits = 11, all filename pointers (S29e).
|
||
- FORM B in `commands/` count = **15**, unchanged (incl. `post.md:134` denylist).
|
||
- `git diff --stat` = **9 files, +15/−15** (1:1, no collateral).
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`, **exit 0**; counts
|
||
**29/19/26/6** + v0.5.0 unchanged.
|
||
- Disposition: **FIXED** (15 edits, 9 files). Commit `e6db0dd`; pushed 2026-06-19 22:38 (operator OK, in
|
||
window).
|
||
|
||
**SURFACED (queued, not swept).** S29 remaining sub-passes: **S29b** `agents/` ← next · **S29c**
|
||
`references/` (in-file phrase; `thought-leadership-angles.md` body scrubbed but FILE renamed in S29e) ·
|
||
**S29d** `skills/`+`config/`+`assets/`+`README.md`+`CLAUDE.md`+`hooks/prompts/`+forward-docs+test-`.mjs` ·
|
||
**S29e** rename `references/thought-leadership-angles.md` + all 20 pointers atomically (new filename TBD).
|
||
|
||
---
|
||
|
||
### S29b terminology-scrub — "thought leadership" → neutral (`agents/`)
|
||
|
||
**CLASS: terminology-scrub** (NOT re-hardening — surgical removal of the operator-banned brand phrase
|
||
"thought leadership", FORM A, from the agent surface; memory `no-thought-leadership-phrase`). Second
|
||
sub-pass of the plugin-wide S29 scrub. SCOPE inherits the S29a operator-lock verbatim (FORM A only;
|
||
FORM B untouched; per-directory sub-pass, map approved before edit).
|
||
|
||
**TEST (tool-grounded).** `grep -in "thought leadership"` (space) in `agents/` = **15 in-file phrase
|
||
occurrences / 7 files**; the 11 `thought-leadership-angles.md` filename pointers (hyphenated) are invisible
|
||
to the space-grep and deferred to S29e. Read-and-show of all 15 in context before proposing the map.
|
||
**Discovery during the header anchor-check (`grep "Value Test"`):** 2 same-class instances the phrase-grep
|
||
missed because abbreviated — `trend-spotter.md:192/:257` "TL Value Test" (TL = Thought Leadership).
|
||
Surfaced as an addendum (#16–#17), operator-approved BEFORE edit; scope not expanded silently.
|
||
|
||
**HARDEN (17 edits, 6 files — 1:1).** Vocabulary consistent with S29a: "thought leadership angle(s)" →
|
||
"content angle(s)"; "thought leadership" (positioning/practice) → "authority …"; the whole "Value Test"
|
||
family → "Authority Value Test".
|
||
- `strategy-advisor.md` (1): `:219` "Thought leadership and cross-platform visibility" → "Authority
|
||
building and …".
|
||
- `content-planner.md` (1): `:24` "sustainable thought leadership growth" → "sustainable authority
|
||
building".
|
||
- `voice-trainer.md` (1): `:24` "voice for LinkedIn thought leadership" → "… for LinkedIn authority
|
||
building".
|
||
- `network-builder.md` (1): `:26` "compound their thought leadership reach and influence" → "compound
|
||
their authority, reach, and influence".
|
||
- `differentiation-checker.md` (4): `:47` search query `"[topic]" thought leadership 2025 2026` →
|
||
`"[topic]" expert insights 2025 2026` (functional-keyword judgment-call, operator chose option b);
|
||
`:147` "marketing as thought leadership" → "marketing posing as expertise"; `:203` header "## Thought
|
||
Leadership Value Test" → "## Authority Value Test"; `:327` in-file "thought leadership test" → "authority
|
||
value test" (filename pointer `thought-leadership-angles.md` left for S29e).
|
||
- `trend-spotter.md` (9): `:5` frontmatter desc "suggest thought leadership angles" → "suggest content
|
||
angles"; `:24` "establish thought leadership positioning" → "establish authority positioning"; `:33`
|
||
"strongest thought leadership angle per trend" → "strongest content angle …"; `:156` "map to the
|
||
strongest thought leadership angle" → "… content angle"; `:179` "Future Implication for thought
|
||
leadership" → "… for authority positioning"; `:334` "create thought leadership value" → "create
|
||
authority value"; `:344` "is the thought leadership" → "is the expertise"; `:192` header "### TL Value
|
||
Test" → "### Authority Value Test"; `:257` "Run TL Value Test" → "Run Authority Value Test".
|
||
|
||
**VERIFY.**
|
||
- Re-grep final FORM A `thought leadership` (space) in `agents/` → **NONE**; `\bTL\b` → **NONE**.
|
||
- FORM B in `agents/` = **2**, unchanged (`analytics-interpreter.md:419` "(thought leader)",
|
||
`network-builder.md:562` "Thought Leader" search term).
|
||
- Filename pointers `thought-leadership-angles.md` in `agents/` = **11**, unchanged (S29e).
|
||
- 17 new replacement strings present (1:1 with edits).
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`, **exit 0**; counts
|
||
**29/19/26/6** + v0.5.0 unchanged.
|
||
- Disposition: **FIXED** (17 edits, 6 files). Commit `ebe56c5` (local; push parked per policy until operator OK).
|
||
|
||
**SURFACED (queued, not swept).** `references/glossary.md:229` "### Thought Leadership Value Test" → its
|
||
own directory pass **S29c** (temporary cross-directory naming gap until then — inherent to the
|
||
per-directory design). `differentiation-checker.md:278` "### Value Test: [X]/3 passed" is already neutral
|
||
(no FORM A / TL) and stays — consistent with the renamed header. Remaining S29 queue unchanged: **S29c**
|
||
`references/` · **S29d** misc · **S29e** rename + 20 pointers.
|
||
|
||
---
|
||
|
||
### S29c terminology-scrub — "thought leadership" → neutral (`references/`)
|
||
|
||
**CLASS: terminology-scrub** (NOT re-hardening — surgical removal of the operator-banned brand phrase
|
||
"thought leadership", FORM A, from the reference-doc surface; memory `no-thought-leadership-phrase`).
|
||
Third and largest sub-pass of the plugin-wide S29 scrub (~2× the S29a/S29b volume). SCOPE inherits the
|
||
S29a/S29b operator-lock verbatim (FORM A only; FORM B untouched; per-directory sub-pass, map approved
|
||
before edit).
|
||
|
||
**TEST (tool-grounded).** `grep -in "thought leadership"` (space) in `references/` = **35 phrase lines /
|
||
15 files** — 29 single-line across 14 files + the canonical `thought-leadership-angles.md` = 6 lines / 7
|
||
instances (line 7 ×2) ≈ **36 FORM A instances**. The 3 hyphenated `thought-leadership-angles.md` filename
|
||
pointers are invisible to the space-grep and deferred to S29e. Read-and-show of every occurrence in
|
||
context before proposing the 34-row map. Anchor-check (`grep "Value Test"` / `#thought-leadership` /
|
||
`#the-standard-thought`): **no anchor links** to the changed headers anywhere in the plugin, and the only
|
||
"Value Test" / "Standard Thought Leadership Structure" term-hits were the two SSOT spots being edited — no
|
||
hidden same-class instances.
|
||
|
||
**S29e filename locked (operator, this session): `thought-leadership-angles.md` → `content-angles.md`.**
|
||
The canonical file's in-file title/headers were therefore scrubbed now for consistency (H1 →
|
||
"# Content Angles"; "## The Authority Value Test"; "### Step 3: Test For Authority Value"); the file
|
||
rename + all 20 pointers remain S29e (atomic).
|
||
|
||
**HARDEN (34 edits, 15 files — 1:1).** Vocabulary consistent with S29a/S29b: "thought leadership
|
||
angle(s)" → "content angle(s)"; "thought leadership" (positioning/practice) → "authority …"; content-type
|
||
labels (lists/tables) → "authority content / authority posts"; the "Value Test" family → "Authority Value
|
||
Test".
|
||
- Established cross-pass equivalents closed: `glossary.md:229` "### Thought Leadership Value Test" →
|
||
"### Authority Value Test" (closes the S29b cross-directory naming gap); `glossary.md:29` "8 universal
|
||
thought leadership angles" → "content angles"; `engagement-frameworks.md:137` "Standard Thought
|
||
Leadership Structure" → "Standard Post Structure" (matches S29a `post:98`).
|
||
- `thought-leadership-angles.md` (6 edits / 7 instances): H1 → "# Content Angles"; :3 "identifying
|
||
thought leadership angles" → "content angles"; :7 (×2) "Thought leadership isn't about…/can become
|
||
thought leadership" → "Authority isn't about…/can build authority"; :92 "Test For Thought Leadership
|
||
Value" → "Test For Authority Value"; :212 "Marketing disguised as thought leadership" → "expertise";
|
||
:215 "## The Thought Leadership Test" → "## The Authority Value Test".
|
||
- `linkedin-formats.md` (5): :24/:79/:272/:309 content-type labels → "authority content/posts"; :295
|
||
"Text-based thought leadership often outperforms…" → "Text-based content …" (avoids authority…authority
|
||
echo).
|
||
- `linkedin-visual-style.md` (4): :3 "For thought leadership," → "For building authority,"; :7 "thought
|
||
leadership niche" → "authority niche"; :82 label → "authority content"; :123 "myth for thought
|
||
leadership content" → "authority content".
|
||
- `trajectory-strategy-adjustments.md` (3): :167 "Shift to thought leadership" → "authority content";
|
||
:202 "100% thought leadership and signature content" → "100% authority and signature content"; :217
|
||
"thought leadership deepening" → "authority deepening".
|
||
- `linkedin-monetization-strategies.md` (3): :285 "Inbound from thought leadership" → "authority
|
||
building"; :624 "80% thought leadership" → "authority content"; :658 "Breaks the thought leadership
|
||
positioning" → "authority positioning".
|
||
- `ai-content-framework.md` (2): :3 "build thought leadership in the AI space" → "authority"; :380 "main
|
||
LinkedIn thought leadership skill" → "content skill" (judgment-call; avoids awkward "authority skill").
|
||
- Single-edit files (8 edits / 8 files): `ab-testing-framework.md:9` → "authority building";
|
||
`growth-roadmaps.md:222` "Thought leadership pieces" → "Authority pieces";
|
||
`linkedin-growth-playbook-2025-2026.md:822` → "Reflective authority content";
|
||
`algorithm-signals-reference.md:158` (SSOT) "Text for thought leadership" → "Text for authority"
|
||
(phrase only; magnitudes = S30); `scheduling-strategy.md:35` → "Mid-week authority content";
|
||
`poll-strategy-guide.md:179` "thought leadership angle" → "content angle"; `video-strategy-guide.md:3`
|
||
→ "authority content"; `video-strategy-guide.md:580` "8 thought leadership angles" → "8 content
|
||
angles".
|
||
|
||
**KEPT BY DESIGN (operator-approved).** `video-strategy-guide.md:429` ironic quote "Your 'thought
|
||
leadership' content isn't leading anyone's thoughts." — scrubbing destroys the point.
|
||
`video-strategy-guide.md:532` "TL;DW" = false positive (too long, didn't watch).
|
||
|
||
**VERIFY.**
|
||
- Re-grep final FORM A `thought leadership` (space) in `references/` → only the kept ironic quote
|
||
(`video-strategy-guide.md:429`); canonical-file in-file FORM A → **NONE**.
|
||
- FORM B in `references/` = **4**, unchanged (`collaborations-guide.md:211`, `analytics-tools-guide.md:227`,
|
||
`opportunity-generation.md:81`, `linkedin-visual-style.md:134`).
|
||
- Filename pointers `thought-leadership-angles.md` in `references/` = **3**, unchanged (S29e).
|
||
- New terms present: "Authority Value Test" (`glossary.md:229` + `thought-leadership-angles.md:215`,
|
||
consistent), "Standard Post Structure" (`engagement-frameworks.md:137`).
|
||
- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`, **exit 0**; counts
|
||
**29/19/26/6** + v0.5.0 unchanged.
|
||
- Disposition: **FIXED** (34 edits, 15 files). Commit `b9b769c` (local; push parked per policy until
|
||
operator OK).
|
||
|
||
**SURFACED (queued, not swept).** Remaining S29 queue: **S29d** `skills/` + `config/` + `assets/` +
|
||
`README.md` + `CLAUDE.md` + `hooks/prompts/` + forward-looking docs + test `.mjs` · **S29e** rename
|
||
`thought-leadership-angles.md` → `content-angles.md` + all 20 pointers (atomic; filename locked). The
|
||
canonical file's in-file phrase scrub is done, so S29e is now a pure rename + pointer-update with no
|
||
remaining FORM A inside the file.
|
||
|
||
---
|