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:
parent
bace49f8c2
commit
1adb7f4361
6 changed files with 204 additions and 8 deletions
|
|
@ -337,4 +337,37 @@ describe("trends item normalizer (RE-R1 / B1)", () => {
|
|||
assert.throws(() => itemToInput(item, "2026-06-24"));
|
||||
});
|
||||
});
|
||||
|
||||
// The F7 gate (score.ts capForActionability) is applied on the capture path so the PERSISTED
|
||||
// priority reflects the reader-side verdict — an Immediate composite with no formulated grip
|
||||
// is stored as High, not Immediate. Proven end-to-end through itemToInput (unit-tested purely
|
||||
// in score.test.ts; here it must actually bite the store input).
|
||||
describe("itemToInput — F7 band-cap gate (MR-F7, N7)", () => {
|
||||
const immediateDims = { pillar: 9, audience: 8, timing: 9, angle: 7, authority: 6 }; // composite 8.15 -> Immediate
|
||||
const mkScored = (extra: Partial<TrendItem>): TrendItem => ({
|
||||
source: "tavily",
|
||||
title: "T",
|
||||
url: "https://example.com/t",
|
||||
topics: ["ai"],
|
||||
score: { mode: "kortform", dimensions: immediateDims },
|
||||
...extra,
|
||||
});
|
||||
|
||||
test("Immediate score + actionability.formulated=false -> persisted priority capped to High", () => {
|
||||
assert.equal(scoreEnvelope("kortform", immediateDims).priority, "Immediate"); // precondition
|
||||
const input = itemToInput(mkScored({ actionability: { formulated: false } }), "2026-06-24");
|
||||
assert.equal(input.score?.priority, "High");
|
||||
assert.equal(input.score?.composite, scoreEnvelope("kortform", immediateDims).composite); // real score unchanged
|
||||
});
|
||||
|
||||
test("Immediate score + actionability.formulated=true -> priority stays Immediate", () => {
|
||||
const input = itemToInput(mkScored({ actionability: { formulated: true } }), "2026-06-24");
|
||||
assert.equal(input.score?.priority, "Immediate");
|
||||
});
|
||||
|
||||
test("Immediate score + no actionability -> uncapped (equals scoreEnvelope, legacy-safe)", () => {
|
||||
const input = itemToInput(mkScored({}), "2026-06-24");
|
||||
assert.deepEqual(input.score, scoreEnvelope("kortform", immediateDims));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue