From 52e39d18a9d16fa868edf91620b181eca7ae667c Mon Sep 17 00:00:00 2001 From: Kjell Tore Guttormsen Date: Fri, 19 Jun 2026 13:24:43 +0200 Subject: [PATCH] =?UTF-8?q?fix(linkedin-studio):=20S17=20harden=20audit=20?= =?UTF-8?q?=E2=80=94=20drop=20orphan=20WebFetch=20grant=20+=20re-source=20?= =?UTF-8?q?carousel=20save-rate=20claim=20to=20SSOT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- commands/audit.md | 3 +-- docs/hardening/log.md | 56 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/commands/audit.md b/commands/audit.md index a0fd1d4..dbc48ea 100644 --- a/commands/audit.md +++ b/commands/audit.md @@ -9,7 +9,6 @@ allowed-tools: - Read - Glob - Grep - - WebFetch - AskUserQuestion --- @@ -90,7 +89,7 @@ Carousels: ████░░░░░░░░░░░░ 15% Video: █░░░░░░░░░░░░░░░ 5% Polls: ░░░░░░░░░░░░░░░░ 0% -Recommendation: Increase carousel content (highest save rate) +Recommendation: Increase carousel content (top organic format) ``` ## Step 5: Engagement Trends diff --git a/docs/hardening/log.md b/docs/hardening/log.md index f5e0980..98c6cd0 100644 --- a/docs/hardening/log.md +++ b/docs/hardening/log.md @@ -1288,3 +1288,59 @@ source, so the real fix belongs to the reference-consistency pass. - 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. + +---