linkedin-studio/docs/hardening/review-R5.md
Kjell Tore Guttormsen 4fd038ad1d docs(linkedin-studio): cold-review R5 (Grow+Router) — independent kald-review of 6 surfaces -> 29/29 coverage
Final cold-review round: strategy, competitive, monetize, outreach, profile, linkedin.
2 independent cold Opus reviewers (intent + correctness), no cross-feed, every claim
tool-grounded; divergences re-grounded by main before registration.

Result: verdict MINOR, 0 MAJOR (cleanest batch of the sweep). Resolution integrity
PASS across all 6 (2/2 subagent_type, 28/28 routes, 11/11 router-suggested agents,
helper-script exports all resolve; 0 under-declared tools; 0 dead executable refs).

Findings (all advisory, no REWORK): thought-leader terminology cluster (5 surfaces,
9 instances; profile :79/:101 are correct negative examples, NOT violations);
monetize :6 description<->body scope self-contradiction; bare ref-paths in
strategy+profile (folds into systemic #3); monetize Audience-Size scorecard +40 vs
/25 cap (suggestion).

Cold-review sweep COMPLETE: 29/29 coverage (S1 + R2a + R2b + R3 + R4 + R5).
v1.0.0 review-blocker lifted. test-runner 138/0 unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012GqEHp4uDiivfrAUjw4BdE
2026-06-30 10:28:57 +02:00

258 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
type: cold-review
batch: R5
journey: "Grow + Router — growth/authority surfaces + the command router (FINAL round)"
scope: "FROZEN committed files at HEAD 4109fe7 (clean tree; post-hardening cold pass)"
method: "2 independent cold Opus reviewers for the round (intent + correctness), each covering all 6 surfaces, no cross-feed; every mechanical claim tool-grounded (anti-fabrication mandate); reviewers carry NO drafting-session context. Divergences re-grounded by main before registration."
surfaces: [strategy, competitive, monetize, outreach, profile, linkedin]
reviewers:
- "intent-lens (conformance: intent delivery + cross-ref resolution + class predicates + graceful degradation + thought-leadership terminology ban)"
- "correctness-lens (internal consistency + bound-vs-canonical + step/phase arithmetic + allowed-tools completeness + dead-ref / executable-path checks)"
class: "guided/stateful (strategy·competitive·monetize·outreach·profile — primary artifact produced · subagent targets resolve · graceful degradation) + routing (linkedin — every emitted /linkedin:Y resolves)"
status: "COMPLETE — all 6 surfaces reviewed (strategy, competitive, monetize, outreach, profile, linkedin). R5 completes 29/29 cold-review coverage."
verdict: MINOR
counts: { BLOCKER: 0, MAJOR: 0, MINOR: 3, SUGGESTION: 2 }
---
# Cold review — R5 (Grow + Router) · FINAL round → 29/29 coverage
Independent post-hoc cold review of the 6 remaining surfaces — the five Grow-journey
guided/stateful commands (`strategy`, `competitive`, `monetize`, `outreach`, `profile`) plus the
`linkedin` router — on the FROZEN committed files (HEAD `4109fe7`). Mirrors the S1 `review.md` +
R2a + R2b + R3 + R4 model (the cold-review method that did **not** fabricate): read-and-show before
assert, every `file:line` tool-confirmed, reviewers carry no drafting-session context. The
per-command interactive gate (`log.md`) already passed these; this pass adds the **independent**
axis that gate never had. **R5 completes the sweep: 23/29 + 6 = 29/29 cold-review coverage.**
**Resolution integrity — PASS across all 6 surfaces (the headline R5 result).** Both blind lenses
independently confirmed, target-by-target against their own `ls`/`test -f`:
- **2/2** `subagent_type: linkedin-studio:X` refs resolve — `strategy-advisor` (`strategy.md:153`),
`network-builder` (`outreach.md:171`).
- **28/28** unique `/linkedin:Y` route tokens resolve to `commands/Y.md` (router + cross-command
suggestions).
- **11/11** router-suggested agents (named in `linkedin.md`) resolve to `agents/*.md`.
- **2/2** helper-script invocations resolve: `outreach.md`'s `state-updater.mjs --record-outreach`
(flags `--date/--track/--partner/--stage/--next/--due` match `state-updater.mjs:394-411`
byte-for-byte) and `linkedin.md`'s `queue-manager.mjs` import (`queueUpcoming`/`queueOverdue`/
`queueFormatSummary` exist at `queue-manager.mjs:52,94,112`).
- **0 under-declared tools** — every body-invoked tool (Task/Read/Write/Edit/Bash/WebSearch/
AskUserQuestion) is in the surface's `allowed-tools`; only harmless `Glob`/`Grep` over-declarations.
- **0 dead executable Read/Bash targets** — every `references/*`, `${CLAUDE_PLUGIN_ROOT}/skills/…`,
and script path on an executable path verified present.
**No broken invocation, no missing primary artifact, no failing gate, no runtime-breaking
contradiction anywhere in R5 → 0 MAJOR, verdict MINOR (advisory, not REWORK).** This is the
cleanest batch of the sweep.
**Independence cross-check — both outcomes recurred (the case for two lenses, again):**
1. **Convergence:** both blind lenses independently surfaced the same `monetize.md` description↔body
scope mismatch and the same `thought leader` terminology cluster — high confidence these are real.
2. **Divergence (re-grounded by main):**
- **Terminology severity** — intent-lens called the `thought leader` hits MINOR (it owns the
conformity predicate; memory `no-thought-leadership-phrase` makes this a *standing plugin rule*,
not a nicety), correctness-lens called them SUGGESTION (no runtime break). **Main ruling:
MINOR** — a direct violation of an explicit plugin terminology rule in user-facing strings is a
conformity defect; it is not MAJOR (no runtime break).
- **`monetize` scope mismatch severity** — intent-lens SUGGESTION ("description under-claims
body"), correctness-lens MINOR ("description contradicts its own 0-1K Stage 1"). **Main ruling:
MINOR** — it is a genuine self-contradiction about the command's scope, not a mere under-claim.
- **Lens-unique:** correctness-lens alone caught the `monetize` Audience-Size scorecard
arithmetic; intent-lens alone caught the bare-relative-path robustness gap. Both re-grounded by
main below and kept.
---
## strategy.md — VERDICT: MINOR
- **Intent delivered:** yes. Phase auto-detect from state (`:32-44`), phase strategy + delegation
to `strategy-advisor` (`:153`), trajectory overlay (`:250-284`), authority building Phase 2+
(`:286-419`), stall points (Step 4), 90-day plan (Step 5), metrics (Step 6).
- **Resolution:** all resolve — `subagent_type: linkedin-studio:strategy-advisor` (`:153`) →
`agents/strategy-advisor.md` ✓; routes `/linkedin:profile` (`:292,:360`) ✓; `Task` declared.
- **Class predicate (guided/stateful):** growth-plan artifact produced ✓; subagent resolves ✓;
graceful degradation present ✓ (`follower_count` 0/missing handled `:42`; "If no milestone data:
Skip this step" `:284`; authority skipped in Phase 0-1 `:290`).
- **Arithmetic (correctness-lens, grounded):** 5 phases (04) consistent between Step 0.5
auto-detect ranges and Step 2 headers; step numbering monotonic (0.5,1,2,3,3.5,3.6,4,5,6), no
gaps/dupes. PASS.
- **allowed-tools:** declared {Read, Glob, Grep, AskUserQuestion, Task}; under-declared {} ✓;
over {Glob, Grep} (harmless).
- **Findings:**
1. **[MINOR]** `strategy.md:371` "Engaging with other thought leaders" — `thought leader`
terminology-ban hit (off-primary checklist label). Part of the cross-cutting cluster.
2. **[MINOR]** bare relative paths for all file loads; **0×** `${CLAUDE_PLUGIN_ROOT}` (grounded
`grep -c` = 0, vs 110× in competitive/monetize/outreach/linkedin) → `Read` resolves against
cwd, not plugin root. Has `Glob` in allowed-tools as a fallback so it degrades, not fatal.
Folds into systemic finding #3 (bare ref-paths).
## competitive.md — VERDICT: MINOR
- **Intent delivered:** yes. Competitor analysis template (Step 2), landscape map (Step 3),
gap/opportunity matrix (Step 4), differentiation plan (Step 5), inspired takeaways (Step 6),
ethics note.
- **Resolution:** fully self-contained — **no** `subagent_type`, **no** `/linkedin:` routes;
`allowed-tools` (Read/Glob/WebSearch/AskUserQuestion) correctly omits `Task`. ✓
- **Class predicate (guided/stateful):** competitive-analysis artifact produced ✓; no subagents to
resolve ✓; graceful degradation ✓ (Step 1 user-input/WebSearch-driven, runs with no state).
- **Arithmetic:** Steps 06 linear/monotonic; no stated totals to miscount. PASS.
- **allowed-tools:** declared {Read, Glob, WebSearch, AskUserQuestion}; under {} ✓; over {Glob}.
- **Findings:**
1. **[MINOR]** `thought leader(s)` appears **4×** — including the frontmatter `description`
(`:4`, **user-facing**, shows in command listings) and the opening promise (`:17`), plus
`:31,:34`. **Worst terminology offender of the six.** Conformity defect, no runtime break.
## monetize.md — VERDICT: MINOR
- **Intent delivered:** yes. All 8 steps present: scorecard (1), stage strategy (2), lead-magnet
blueprint (3), funnel calendar (4), CTA + A/B variants (5), Featured optimization (6), revenue
model (7), tracking dashboard (8).
- **Resolution:** all resolve — no `subagent_type`; routes `/linkedin:post` + `/linkedin:pipeline`
(`:367`) both exist. ✓
- **Class predicate (guided/stateful):** monetization-plan artifact produced ✓; no subagents ✓;
graceful degradation ✓ ("Stage 1: Visibility (0-1K followers)" `:92` serves brand-new users).
- **Gating honesty:** does NOT hard-gate; serves 0-1K. The real state-read gate is the router's
soft prepend (`linkedin.md:175`, `<1000` → prepend, continue anyway) — consistent with outreach.
- **Findings:**
1. **[MINOR]** **description↔body scope self-contradiction**`monetize.md:6` "Works from 1K+
followers" contradicts its own Stage 1 "Visibility (**0-1K** followers, score 0-30)" (`:92`,
`:81`) and the router's "they work at any follower count" (`linkedin.md:126`). The body
genuinely covers sub-1K; the description misstates scope. Off-primary (a frontmatter string)
but user-facing. **Strongest non-terminology R5 finding.** Fix: align `:6` to "any follower
count (compounds at 1K+)".
2. **[MINOR]** `thought leaders` in the frontmatter `description` (`:4`, user-facing). Part of the
cross-cutting cluster.
3. **[SUGGESTION]** Audience-Size scorecard arithmetic (`:51-56`): sub-items +5/+10/+15/+5/+5 sum
to **+40** against a **`/25`** cap if read additively, whereas the other three categories each
sum to exactly 25 (e.g. Engagement Quality 5+5+10+3+2=25). The follower tiers (1K/5K/10K) are
clearly intended mutually-exclusive (one tier) but aren't marked as such → a literal additive
read overflows. AI-interpreted (not machine-summed) so intent is recoverable; cosmetic, but it
feeds the band that selects the stage. Fix: mark the three follower tiers "(pick one)".
## outreach.md — VERDICT: PASS
- **Intent delivered:** yes, thoroughly. Two-track (collab + speaking) orchestrator with a
Capability Checklist mapping every predecessor function to a step (`:31-62`) + 10 steps + state
persistence.
- **Resolution:** all resolve — `subagent_type: linkedin-studio:network-builder` (`:171`) ✓;
routes `/linkedin:strategy` (`:112`), `/linkedin:firsthour`+`/linkedin:outreach` (`:1078`),
`/linkedin:calendar` (`:1097`) all exist ✓; **Bash executable path sound** — Step 8c's
`state-updater.mjs --record-outreach` (`:1084-1092`) matches the script's handler
(`state-updater.mjs:294,:394-411`) byte-for-byte. ✓
- **Class predicate (guided/stateful):** outreach-plan + persisted pipeline produced ✓; subagent +
script resolve ✓; graceful degradation ✓ (Step 2a "Not ready: <3 met → build foundation first" +
recommend `/linkedin:strategy` `:112`).
- **Arithmetic (correctness-lens, grounded):** "12 collab formats" → 12 (`FORMAT 112`); "4 talk
templates" → AD; "5 phases" → PHASE 15; scorecards 4×/25=/100 and 5×/5=/25 check out; step
numbering monotonic. PASS.
- **allowed-tools:** declared {Read, Glob, WebSearch, AskUserQuestion, Task, Bash}; under {} ✓;
over {Glob}.
- **Gating honesty:** "1K+ followers" self-report (`:100,:143`) consistent with router's `~1K` soft
gate (`linkedin.md:120,:176`) and monetize's 1K. ✓
- **Findings:**
1. **[SUGGESTION]** `thought leader` inside a WebSearch query template (`:229`,
`"[your niche] linkedin thought leader"`). It is a *search string* targeting how others
self-label (intentional — to find such profiles), so the **lowest-priority** instance of the
terminology cluster — but the literal string is still in the plugin.
## profile.md — VERDICT: PASS
- **Intent delivered:** yes. Relevance-model context (`:20-44`), Profile SEO + per-section keyword
targets (`:46-79`), 7-section audit walkthrough (`:82-199`), profile-content alignment check
(`:200-212`), prioritized action plan (`:214-231`), alignment test (`:232-238`).
- **Resolution:** self-contained — no `subagent_type`, no `/linkedin:` routes; `allowed-tools`
Read/AskUserQuestion (tightest frontmatter of the six). ✓
- **Class predicate (guided/stateful + topic-relevance-audit):** the topic-relevance audit **is**
actually performed (the body *is* that audit) ✓; artifact (audit + action plan) produced ✓;
graceful degradation ✓ (every step AskUserQuestion-driven, runs with zero state). Notable
verification discipline: explicitly refuses to fabricate a scoring breakdown (`:28,:32-36,:179`).
- **Arithmetic:** Sections 17 monotonic; profile-field limits (headline 220, About 2,600) are
LinkedIn field limits, NOT post hook/length bounds — no canonical contradiction. PASS.
- **Terminology — IMPORTANT NON-VIOLATION:** the two `thought leader` hits (`:79,:101`) are
**legitimate negative examples** — the command explicitly tells the user to AVOID the phrase
(`:79` lists it with "guru"/"ninja" as keyword-wasters; `:101` is a "Weak example"). Both lenses
agree; main confirmed by reading both lines. **profile.md models the correct behavior.**
- **Findings:** none of defect class.
1. **[Note — systemic #3]** bare relative paths; **0×** `${CLAUDE_PLUGIN_ROOT}` and no `Glob`
fallback → most-exposed instance of the bare-path robustness item. BUT correctness-lens
verified all three referenced files exist (`test -f` OK), so this is a cwd-robustness concern,
not a dead ref. Folds into systemic #3; profile.md is otherwise the cleanest surface of R5.
## linkedin.md (router) — VERDICT: MINOR
- **Intent delivered:** yes. Status line (`:19-29`), upcoming/overdue posts via queue (`:31-57`),
five-journey menu with front-doors (`:59-127`), gating rule (`:122-127`), interactive menu
(`:129-146`), direct-routing table (`:156-198`).
- **Resolution (routing class):** **all 28 unique route tokens resolve** to `commands/*.md`
(verified token-by-token vs `ls commands/`); all 11 suggested agents resolve; `queue-manager.mjs`
node call (`:35-42`) uses exports that all exist (`:52,:94,:112`). **The router advertises nothing
that doesn't exist.** ✓ Correctly has no `Task` (delegate-only).
- **Class predicate (routing):** every route resolves ✓; graceful degradation ✓ (missing state
"No LinkedIn state tracked yet" `:28`; empty queue `:57`; follower segment only if
`follower_count > 0` `:30`).
- **Gating honesty/consistency:** `:175-176` give monetize + outreach the **same** soft state-read
gate (`<1000` → prepend, continue anyway); `:122-127` document the soft-gate design honestly
("they work at any follower count… competitive is **not** gated"). Consistent with both command
bodies. ✓
- **Findings:**
1. **[MINOR]** `linkedin.md:118` "Competitive analysis of other thought leaders" — `thought
leader` terminology-ban hit in the **user-facing** routing-menu table. Part of the cluster.
---
## Summary table
| Surface | Verdict | MAJOR | MINOR | SUGGESTION |
|---|---|---|---|---|
| strategy.md | MINOR | 0 | 2 | 0 |
| competitive.md | MINOR | 0 | 1 | 0 |
| monetize.md | MINOR | 0 | 2 | 1 |
| outreach.md | PASS | 0 | 0 | 1 |
| profile.md | PASS | 0 | 0 | 0 (1 systemic-#3 note) |
| linkedin.md | MINOR | 0 | 1 | 0 |
| **R5 total** | **MINOR** | **0** | **3 distinct** | **2 distinct** |
(Per-surface MINOR counts include shared cross-cutting findings; the 3 *distinct* R5 MINOR findings
are: the terminology cluster, the monetize scope contradiction, and the bare-path robustness item.)
## Cross-cutting findings (for the consolidated fix-pass)
1. **[MINOR · NEW R5 systemic] `thought leader` terminology cluster — 5 surfaces, 9 instances.**
competitive `:4`(description, user-facing)/`:17`/`:31`/`:34` · monetize `:4`(description,
user-facing) · linkedin `:118`(menu, user-facing) · strategy `:371`(checklist) · outreach
`:229`(WebSearch string, lowest priority). **profile `:79,:101` are NON-violations** (correct
negative examples — do not "fix" them). Memory `no-thought-leadership-phrase` confirms this is a
standing plugin terminology rule. **Recommended fix:** one terminology sweep replacing the
user-facing instances first ("thought leaders" → "creators"/"experts"/"voices in your niche"),
leaving profile's avoid-list intact and the outreach search-string as lowest priority. No runtime
risk — the buzzword gate scopes to post content, not command markdown.
2. **[MINOR · per-flate] `monetize.md:6` description↔body scope self-contradiction.** "Works from
1K+ followers" vs the body's own 0-1K Stage 1 (`:92`) and the router's "any follower count"
(`linkedin.md:126`). Fix: align the description to the body's actual sub-1K-onward range.
3. **[MINOR · folds into existing systemic #3] bare ref-paths — strategy + profile (0×
`${CLAUDE_PLUGIN_ROOT}`).** profile most exposed (no `Glob` fallback). Refs verified to exist, so
cwd-robustness not a dead ref. Reconcile in the repo-wide path-style pass already scoped by
systemic #3 (R3/R4 found the same pattern in batch/pipeline/firsthour/analyze/report).
4. **[SUGGESTION · per-flate] `monetize.md:51-56` Audience-Size scorecard sums to +40 vs `/25`
cap.** Follower tiers intended mutually-exclusive but unmarked. Fix: annotate "(pick one)".
**R5 adds NO new ★ systemic finding to the existing three** (clipboard, scaffold-band, bare-paths) —
the only genuinely new cross-cutting item is the terminology cluster, which is a conformity sweep,
not a code-behavior defect.
## Verification
- Spot-checks re-grounded by main (this file): `grep -rniE 'thought.?leader'` over the 6 (9 hits,
classifications above); `sed -n '1,8p'`/`'92p'`/`'48,64p' monetize.md` (scope + scorecard);
`grep -c CLAUDE_PLUGIN_ROOT` per surface (strategy 0, competitive 4, monetize 10, outreach 10,
profile 0, linkedin 1).
- `bash scripts/test-runner.sh` → see STATE telling (expected 138 passed / 0 failed, floor 123) —
unchanged by this review (review is read-only; no command edits).
## Sweep status after R5
**29/29 cold-review coverage reached** (S1 `review.md` + R2a + R2b + R3 + R4 + R5). The independent
cold-review phase is COMPLETE; the v1.0.0 review blocker is lifted. Remaining v1.0.0 work: the
consolidated fix-pass (operator-gated — review FINDS, fix is a separate decision) and the GUI.