feat(linkedin-studio): N15 — do-next-kontrakt (recordDoNext + skrivere/lesere + forrige-utgave-kalibrering) [skip-docs]

Måling som ikke endrer neste utgave er teater: report/analyze/ab-test/48h-monitor
endte alle i chat (grep do.next = 0 i hele repoet). N15 gjør kanalen til en kontrakt.

- recordDoNext i hooks/scripts/state-updater.mjs (TDD, 17 nye tester): seksjonen
  Do-Next Directives, rader "[dato] (kilde) direktiv — evidence: peker".
  Levetid = erstatt-per-kilde + 60-dagers aldersgulv, maks 3 per skriv, additiv
  scalar last_donext_date, $-sikre replacement-funksjoner. CLI: --record-do-next.
- Skrivere: report Step 7b, analyze Step 6b, ab-test 2c.6 (kun Adopt),
  post-feedback-monitor (kun 48h-sjekken); analytics-interpreter emitterer nå
  anbefalinger på do-next-form (imperativ + evidens).
- Lesere: post/quick/batch/create Step 0 + newsletter Step 1, som i tillegg
  kalibrerer på forrige utgaves MÅLTE tall via queue-id-match (queue.json →
  analytics/posts) med eksplisitt honest-miss — aldri estimert.
- Guards: test-runner Section 16v (15 ubetingede sjekker, floor 198 → 213),
  $-safety-batteriet dekker recordDoNext (coverage-gate fanget den nye eksporten).

Suiter: test-runner 232/0, hooks 191/0, trends 300/0, brain 134/0, editions 72/0,
specifics-bank 45/0, contract-gate 33/0, tests 35/0, render 60/0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QxvWAjte7vPcF79QeSRvRJ
This commit is contained in:
Kjell Tore Guttormsen 2026-07-25 15:23:35 +02:00
commit b45fdad911
16 changed files with 713 additions and 6 deletions

View file

@ -342,6 +342,32 @@ handful of posts per variant a creator realistically gathers (well under the
Update the test file status from ACTIVE to COMPLETED. Add the conclusion and recommended actions to the file.
### 2c.6: Persist an Adopt verdict as a do-next directive
An **Adopt** verdict (and only Adopt — "Keep A", "Inconclusive" and "Extend test" change nothing
about the next draft) is a decision the next post must inherit. Write it to the same do-next
channel `/linkedin:report` and `/linkedin:analyze` use, so every create surface picks it up at
its Step 0 instead of the test result sitting unread in a file:
```bash
node --input-type=module -e "
import { writeState, recordDoNext } from '${CLAUDE_PLUGIN_ROOT}/hooks/scripts/state-updater.mjs';
writeState(content => recordDoNext(content, {
recordDate: 'YYYY-MM-DD',
source: 'ab-test',
directives: [
{ directive: 'Open with a question hook by default', evidence: 'test hook-form (6 posts): B +38% comments, directional-strong' }
]
}));
"
```
Carry the **directional** framing into the evidence pointer (see 2c.4 — organic volume rarely
reaches significance); a directive that reads as proven when the signal was weak is how one
outlier becomes a permanent rule. `recordDoNext` replaces this source's previous adopt row, so
a newer verdict supersedes an older one automatically — and the `report`/`analyze` slots are
untouched.
---
## Step 2d: Review Test History

View file

@ -220,6 +220,31 @@ Quick triage if a full audit can wait:
- [ ] Monitor metrics closely
- [ ] Continue high engagement activity
## Step 6b: Persist the plan as do-next directives (the measurement→creation contract)
The recovery plan above is worth nothing if the next drafting session never sees it. Turn the
**23 load-bearing corrections** into do-next directives — the same channel `/linkedin:report`,
`/linkedin:ab-test` and the 48h monitor write to, and the one every create surface reads at its
Step 0:
```bash
node --input-type=module -e "
import { writeState, recordDoNext } from '${CLAUDE_PLUGIN_ROOT}/hooks/scripts/state-updater.mjs';
writeState(content => recordDoNext(content, {
recordDate: 'YYYY-MM-DD',
source: 'analyze',
directives: [
{ directive: 'Keep every post inside the 3 core pillars for 14 days', evidence: 'diagnosis: profile-content mismatch, reach -55% since off-topic run' },
{ directive: 'No external links in the post body until reach recovers', evidence: 'diagnosis: link-in-body correlates with the drop window' }
]
}));
"
```
Write the corrections that change the NEXT draft — not the whole checklist. `recordDoNext`
replaces this source's previous rows and expires anything older than 60 days, so a superseded
diagnosis cannot keep steering drafts. Confirm in one line what was persisted.
## Step 7: Timeline Expectations
Set realistic expectations:

View file

@ -24,6 +24,10 @@ You are a LinkedIn batch content creator. Help the user create an entire week's
Load state and personalization:
- Read `~/.claude/linkedin-studio.local.md` for recent topics and weekly goals
- **Read `## Do-Next Directives` in the same file** — the last measurement pass's directives
(report / analyze / A/B Adopt / 48h monitor) with their evidence pointers. A batch is where
they bite hardest: apply them across the week's posts and name which post carries which
directive in the plan. Never edit or clear the section (lifetime is managed per source)
- Read `${CLAUDE_PLUGIN_ROOT}/skills/linkedin-studio/SKILL.md` for profile and preferences
- Check `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/plans/` for existing weekly plan
- Read `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/templates/my-post-templates.md` for proven templates — vary templates across the batch for format diversity

View file

@ -26,6 +26,10 @@ If `~/.claude/linkedin-studio.local.md` exists, you may glance at the planned ne
topic and recent pillars to make a smarter suggestion — but keep it to one line and
do not block on it.
If `## Do-Next Directives` in that file is non-empty, mention the top directive in that same
line ("last report says: lead with the number") — it often decides which format to route to.
You do not apply it here; the command you route to reads the section itself at its Step 0.
## Step 1: Identify what they want to create
If the user's message already names a format (e.g. "a carousel about X", "react to

View file

@ -371,6 +371,39 @@ what genuinely changes the work.
> discovery→production loop automatically at Step 10 (scheduling flips the trend to
> `acted`).
> **Calibration from measurement (A2-F14 — the do-next contract).** Two reads, both
> cheap, both BEFORE the dimensions below. Neither asks the operator anything.
>
> 1. **`## Do-Next Directives` in `~/.claude/linkedin-studio.local.md`.** What the last
> measurement pass (`/linkedin:report`, `/linkedin:analyze`, an A/B Adopt verdict, the
> 48h monitor) decided the next piece should do differently, each row carrying its
> evidence pointer. Apply the ones that apply to long-form and **name them in the brief**
> — a directive that shaped this edition must be visible in the brief, not silently
> absorbed. Never edit or clear the section here (each source manages its own rows).
> 2. **The previous edition's MEASURED numbers — queue-id match.** The prior edition of
> this series was registered at its Step 10 under the stable queue id
> `<series-slug>-<NN1>`. Resolve its numbers:
> - read `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/drafts/queue.json`,
> take the entry whose `id` is `<series-slug>-<NN1>` → its `scheduled_date`,
> `hook_preview` and `status`;
> - read the analytics batches in
> `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/analytics/posts/*.json` and
> select the `.posts[]` row whose `publishedDate` equals that `scheduled_date`
> (a rescheduled edition may be a day or two off — widen to ±2 days and disambiguate
> on a `title` that matches `hook_preview`);
> - carry forward `impressions`, `engagementRate`, `comments` and `saves` (when present).
>
> Read them as calibration, not as a verdict: **low impressions** points at hook /
> topic-relevance (a Step 9 problem for this edition), **decent impressions with weak
> comments** points at resonance (a Step 6 persona problem), and a strong prior edition
> says keep the structure that worked. State the comparison in one line in the brief.
>
> **Honest miss.** No queue entry, no analytics match, or the edition simply is not measured
> yet (`status: scheduled`, or the import has not been run) → say so in one line — *"forrige
> utgave er ikke målt ennå; kalibrerer på skjelett og persona alene"* — and continue.
> **Never estimate** the previous edition's numbers, and never let a missing measurement
> block the brief. This is edition NN=1 of a new series' normal state.
Settle these dimensions (most should come from context, not questions):
- **Angle** — the one premise this edition argues.

View file

@ -26,6 +26,14 @@ First, load persistent state and personalization:
- Read `~/.claude/linkedin-studio.local.md` for posting state (streak, weekly progress, recent topics)
- Read `skills/linkedin-studio/SKILL.md` for user profile, voice settings, and preferences
**Read `## Do-Next Directives` in the state file and apply them to this draft.** This is what the
last measurement pass (`/linkedin:report`, `/linkedin:analyze`, an A/B Adopt verdict, the 48h
monitor) decided the next post should do differently — each row carries its evidence pointer.
Apply the ones that fit this post, and **name the applied directive in one line** when you
present the draft ("applied: lead with the number — report 2026-W22"). A directive that does not
fit this topic is skipped silently, not argued with. Directives are already lifetime-managed
(each source replaces its own; >60 days expire) — never edit or clear the section here.
Check state for topic planning:
- Compare intended topic against "Recent Posts" in state file
- If a similar topic was posted in the last 7 days, suggest a different angle or topic

View file

@ -27,6 +27,10 @@ Read `~/.claude/linkedin-studio.local.md` for:
- Weekly posting progress (show "X/Y posts this week")
- Recent topics (avoid repetition within 7 days)
- Current streak status
- **`## Do-Next Directives`** — what the last measurement pass (report / analyze / A/B Adopt /
48h monitor) decided the next post should do differently, each with its evidence pointer.
Apply the ones that fit and name the applied directive in one line with the draft. Never edit
or clear the section (each source manages its own rows; rows older than 60 days expire).
Read `skills/linkedin-studio/SKILL.md` for:
- User's voice profile and tone preferences

View file

@ -336,6 +336,40 @@ Delegate interpretation to the `analytics-interpreter` agent (report mode) — i
3. **Test carousel format**: Your one carousel got 3x more shares than text posts. Create a carousel for your top-performing topic this week.
```
## Step 7b: Persist them as do-next directives (the measurement→creation contract)
A recommendation that only lands in chat dies there — the next post is drafted in a fresh
session that never saw it. **Persist the 23 recommendations above as do-next directives**, so
`/linkedin:post`, `/linkedin:quick`, `/linkedin:batch`, `/linkedin:create` and
`/linkedin:newsletter` read them at their Step 0 and the next piece is actually shaped by this
report. This step is not optional when a report produced recommendations.
Each directive is an **imperative the next draft can act on** plus an **evidence pointer**
the measured number it came from, so the create surface can tell a directive backed by 12 posts
from one backed by 2:
```bash
node --input-type=module -e "
import { writeState, recordDoNext } from '${CLAUDE_PLUGIN_ROOT}/hooks/scripts/state-updater.mjs';
writeState(content => recordDoNext(content, {
recordDate: 'YYYY-MM-DD',
source: 'report',
directives: [
{ directive: 'Plan 2 AI-agent posts this week', evidence: 'weekly 2026-W22: 3 AI posts avg 2400 impressions vs 1200 baseline (n=12)' },
{ directive: 'Move Tuesday posts to 08:00', evidence: 'weekly 2026-W22: Tuesday -40% impressions' }
]
}));
"
```
**Lifetime (do not hand-manage it):** `recordDoNext` replaces this source's own previous rows on
every write and drops any row older than 60 days, so the section stays a live steering signal
rather than a growing backlog. Directives from other sources (`ab-test`, `analyze`,
`48h-monitor`) are left alone — never clear the section by hand.
Then confirm in one line what was persisted, e.g. "2 do-next directives written to state — the
next post/newsletter will read them at Step 0."
## Step 8: Offer Deep Dive Options
After presenting the report, ask: