feat(linkedin-studio): N6 — forslags-lag i trends (angle/targetLevel/rationale/relatedIds + selected + --ids + F7/F9-felt actionability/verdict/readerQuestion/painPoint/saturation) [skip-docs]

Artikkelforslaget blir en persistert entitet (steg 2, A1-4) og godkjenningen får
et hjem (steg 3, A1-7) — før dette genererte agenten vinkel/begrunnelse som ble kastet.

Åtte additivt-valgfrie felt på TrendRecord/TrendInput/TrendItem:
- Forslag (A1-4/A1-5): angle · targetLevel (fritekst, brukerdefinert spenn — aldri enum) ·
  rationale · relatedIds[] (flerkilde).
- F7 leser-side: actionability {formulated, note?} (N7-bånd-cap-gaten leser `formulated`) ·
  verdict BÆRENDE/STØTTE/NYHET (lukket mekanisme-vokab).
- F9 leser-side: readerQuestion · painPoint · saturation (N7.5-sveipet fyller dem).

Ingen schema-bump: feltene er additivt-valgfrie, ingen record trenger migrering →
SCHEMA_VERSION forblir 4 (loadStore Math.max håndterer det). First-sight-persistering
(re-capture unionerer topics + re-scorer kun; klobrer aldri en triagert vinkel).

Validering: typede felt (verdict/actionability) feiler hardt ved malformert input;
fritekst/id-liste normaliseres lempelig (summary/topics-idiomet).

Livssyklus: TrendStatus += "selected" → new→selected→acted|skipped. Ny select-verb +
--ids-batch (act/skip/reset/select); partiell suksess = exit 0 + miss-rapport, all-miss = exit 2.
setStatusMany(): ren batch-mutasjon, per-id found/notFound.

Brief: forslagsfeltene rendres per kandidat (detaljert i topp-treff, kompakt token i bullets) +
ny «🚧 I produksjon»-seksjon (selected=valgt + acted=skrevet), pillar-uavhengig, deterministisk
sortert. selected/acted forlater arbeidskøen men vises i produksjons-boardet.

commands/trends.md Step 5 oppgradert: triage → Velg (select) / Skip, batchet per verb (--ids).

Trends-suite 245→266 (ny floor, +21 N6-tester). To eksisterende tester oppdatert for den
endrede brief-kontrakten (acted vises nå i I produksjon; ranking-descriptoren ekskluderer selected).
tsc rent. Roundtrip bevist: capture m/ alle felt → query → brief rendrer feltene → select → I produksjon.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S7SQpXJpBSNvpWaTNq1kWZ
This commit is contained in:
Kjell Tore Guttormsen 2026-07-21 09:09:51 +02:00
commit 65b60337fe
9 changed files with 732 additions and 33 deletions

View file

@ -401,7 +401,7 @@ describe("trends CLI — lifecycle: act/skip/reset + brief surfacing (RE-R3b)",
}
});
test("RED: the brief .md omits an acted record", () => {
test("RED: an acted record leaves the work queue and moves to the 'I produksjon' board (N6)", () => {
const { dir, store, out } = fixture();
try {
seedScored(store, "Handled", "https://e/handled");
@ -409,7 +409,12 @@ describe("trends CLI — lifecycle: act/skip/reset + brief surfacing (RE-R3b)",
run(["act", "--id", id, "--store", store], "");
const o = JSON.parse(run(["brief", "--pillars", "ai,gov", "--out", out, "--store", store, "--json"], "").stdout);
const md = readFileSync(o.path, "utf8");
assert.ok(!md.includes("Handled"), "an acted record must not appear in the brief");
// N6: acted no longer vanishes from the whole brief — it leaves the QUEUE and shows in "I produksjon".
const idx = md.indexOf("## 🚧 I produksjon");
assert.ok(idx >= 0, "the production section is present");
assert.ok(!md.slice(0, idx).includes("Handled"), "an acted record must not appear in the work queue");
assert.ok(md.slice(idx).includes("Handled"), "the acted record appears in I produksjon");
assert.ok(md.slice(idx).includes("skrevet"), "tagged skrevet (acted)");
} finally {
rmSync(dir, { recursive: true, force: true });
}