feat(linkedin-studio): N7 — trend->newsletter-bro (sourceTrendId + Step 1-inntak + auto-act) + F7 band-cap-gate [skip-docs]

Del 2 (broen): commands/newsletter.md Step 1 trend-intake pre-fills brief
(angle/targetLevel/key-points/source-URLs) from a /linkedin:trends candidate via
the trends CLI; sourceTrendId persisted at Step 1.5; Step 10 auto-act flips the
source trend to acted (closes the discovery->production loop deterministically);
Step 2 external fact-package intake path (kilde-så-draft with finished research).
edition-state.template.json: per-article sourceTrendId (additive-optional, no
schemaVersion bump). A1-8: grep -ci trend commands/newsletter.md 0 -> 18.

Del 2.5 (F7 gate, TDD): score.ts capForActionability — a candidate whose reader-grip
is explicitly not formulated (actionability.formulated=false) caps to at most High
regardless of composite. Pure; overrides the composite->band derivation downward only
(composite/dimensions/mode preserved, score stays etterprøvbar); absent actionability
left uncapped (legacy-safe). Wired into the item.ts capture path so the PERSISTED
priority is gated; store carries the envelope verbatim (re-capture safe). No KTG values
hardcoded — the N6 field carries the verdict, the gate only enforces it.

Suites (all green): trends 276/0 (was 266, +10), test-runner 139/0, brain 134/0,
hooks 140/0, tests 35/0, render 60/0; tsc --noEmit clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014bE7VbkmR3cqHFEeGfzgwb
This commit is contained in:
Kjell Tore Guttormsen 2026-07-23 20:38:55 +02:00
commit 1adb7f4361
6 changed files with 204 additions and 8 deletions

View file

@ -271,6 +271,33 @@ Establish the edition brief with **at most ~3 calibration questions**. Infer
everything you can from Step 0 (series brief, STATE.md, prior edition); only ask
what genuinely changes the work.
> **Trend intake (optional bridge — N7).** If this edition is being started from a
> `/linkedin:trends` candidate — the operator supplies a **trend id** (shown in the
> trends brief and via `CLI list --json`, where
> `CLI` = `cd "${CLAUDE_PLUGIN_ROOT}/scripts/trends" && node --import tsx src/cli.ts`)
> — read that record and **pre-fill the brief from it** instead of asking from
> scratch. Without a trend id, skip this block entirely: the flow below is
> unchanged (the bridge is an offer, never a requirement).
>
> 1. **Read the candidate.** Run `CLI list --json` (or `CLI query --topics <edition
> topics> --json`) and select the entry whose `id` equals the supplied trend id.
> 2. **Pre-fill from the record's proposal layer (N6 fields)** — each is a *starting
> point the operator confirms or edits* in the dimensions below, not a bypass of
> calibration:
> - `angle` → the edition **Angle**.
> - `targetLevel` → the audience/tone calibration (**Audience** / **Tone**).
> - `summary` + `rationale` + `readerQuestion` / `painPoint` → seed the **Key
> points** and the **Leader-takeaway** (the reader-side framing this bridge
> exists to carry: what the reader would DO with it).
> - `url` (+ any URLs in `rationale`) → seed **source URLs** for Step 2 research /
> the `NN-kilder.md` ledger (they land as `ekstern` slots at Step 1.5).
> - `actionability` / `verdict` (BÆRENDE/STØTTE/NYHET) → frame the takeaway; a
> candidate the operator selected already cleared the F7 reader-grip gate.
> 3. **Bind the provenance.** Note the trend id — it is persisted as
> `articles.NN.sourceTrendId` at the **Step 1.5 checkpoint** (below) and closes the
> discovery→production loop automatically at Step 10 (scheduling flips the trend to
> `acted`).
Settle these dimensions (most should come from context, not questions):
- **Angle** — the one premise this edition argues.
@ -416,6 +443,9 @@ Edition brief
- Persist the resolved **brief** (Step 1) and **personas** (the set + primær)
into `articles.NN` — this is the first durable write of both (Step 2 then adds
the research notes).
- If Step 1 ran the **trend-intake bridge**, persist `articles.NN.sourceTrendId`
(the trend id) here too — the durable provenance link Step 10 reads to auto-`act`
the trend. Omit it (leave `null`) for a manually-started edition.
- Write `articles.NN.livedSpecifics` (the slot-map above, `status: "pending"`).
- Set `currentPhase: "lived-specifics"` in `edition-state.json`.
- Write a "lived-specifics bound → next: research (scoped by the binding)" line
@ -451,6 +481,26 @@ Next: Step 2 — Research (scoped by the binding: fill ekstern/unresolved, verif
against a primary source (regel 6/7). This is what makes research *fill* the
draft instead of re-deriving what the operator already knows.
0.5. **External fact-package intake (optional — kilde-så-draft with finished
research).** If the operator already has a **deep-research report** — a file the
operator supplies by path (e.g. a Gemini Deep Research export, a consultant brief,
a prior research pass; markdown / text / PDF) — ingest it as a research input
instead of researching those slots from zero:
- **Read the file** and extract the same structure the fan-out returns: findings
(each with its source), data points (figures + source + date), and anything the
report itself flags as uncertain.
- **Map onto the binding.** Attribute each finding to the `ekstern`/`unresolved`
slot (or unverified number) it resolves; its sources become the `source` on
those slots and land in `NN-kilder.md` at persist (step 5).
- **Same guilty-until-disproven discipline.** A packaged claim is NOT
pre-verified: label anything you cannot corroborate `UNVERIFIED` and carry it to
the Step 5 fact-check sweep as 🟡 — a polished report is not a primary source.
- **Then scope the fan-out to the GAP only.** Slots the package covers need no
`Task` call; run steps 13 below **only for the slots the package leaves open**.
If the package covers every `ekstern`/`unresolved` slot, the fan-out may reduce
to zero calls — note that explicitly (don't fabricate a fan-out that added
nothing). With no package supplied, this sub-step is a no-op: proceed to step 1.
1. **Decompose** the binding's `ekstern`/`unresolved` slots + unverified numbers
(step 0) into 25 *scoped, orthogonal* research sub-questions. Each sub-question
must be answerable independently so the calls can run in parallel without
@ -1749,7 +1799,22 @@ now a first-class scheduled post.
The function appends to `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/drafts/queue.json` with `status:
"scheduled"` and returns the new entry.
3. **Persist + close the edition.** Set the article's `status: "scheduled"`,
3. **Close the discovery loop (auto-`act` — N7).** If `articles.NN.sourceTrendId`
is set (this edition was started from a `/linkedin:trends` candidate), flip that
trend's store status to `acted` so it drops out of future discovery triage — the
discovery→production loop closes deterministically, not from memory:
```bash
cd "${CLAUDE_PLUGIN_ROOT}/scripts/trends" && node --import tsx src/cli.ts act --id "<sourceTrendId>"
```
A present id flips to `acted` (exit 0). If the id is no longer in the store the
call errors (all-miss → exit 2, store untouched) — that is **not fatal** to
scheduling (the edition is already registered in step 2): note it and proceed.
Skip this step entirely when `sourceTrendId` is `null` (a manually-started
edition has no trend to close).
4. **Persist + close the edition.** Set the article's `status: "scheduled"`,
`scheduled: "<YYYY-MM-DD HH:MM>"`, and `currentPhase: "scheduling"` in
`edition-state.json`. Write a closing "edition scheduled → mark live via
`/linkedin:calendar` on <date>" line to `<serie>/STATE.md` (overwrite). The