feat(linkedin-studio): RE-R3d — temporal overlay (first-mover + saturation) [skip-docs]

R3 slice (b): the rest of hull #3. The morning brief now reads the temporal axis
the R3b seen-log records but the ranking ignored. Two DERIVED signals, computed at
brief time from already-persisted fields (publishedAt/capturedAt -> ageDays,
surfacedCount), never stored:

- first-mover: recent (ageDays <= --first-mover-days, default 2) AND never surfaced
  on a prior day -> ranked up, badge "first ute". Future-dated (ageDays<0) excluded.
- saturation: surfaced on >= --saturation-at (default 3) prior days -> ranked down,
  badge "mettet (Nx)". Self-surfacing (our seen-log), not market coverage.
- warming (1..at-1) keeps the R3b "sett Nx" badge but only at >=2 (contract intact);
  neutral carries no badge.

SB1 derived (no schema bump: SCHEMA_VERSION 4 / BRIEF_SCHEMA_VERSION 1 untouched).
SB2 the R3a relevance composite stays the PRIMARY sort key; the temporal rank is a
new cmp key after pillar-overlap, before effectiveDate -> re-orders only WITHIN a
(composite, overlap) tier. temporalSignal is pure (saturationAt clamped >=1).

Prior-day surfacings exclude today (via lastSurfacedAt), so a same-day re-render is
byte-identical (caught by the R3c run-daily SC7 regression; fixes a latent R3b
prior-day imprecision too). brief CLI gains --first-mover-days / --saturation-at;
schedule untouched (nightly uses defaults).

Wiring: trend-spotter.md (prose), trend-scoring-modes.md (one-line consumer note),
README (## Temporal overlay), gate Section 16m (+6 unconditional -> ASSERT floor
111->117), TRENDS_TESTS_FLOOR 192->216. Counts 29/19/27 unchanged. Zero new files.

Gate: Passed 132 / Failed 0; trends 216/216; hook suite 139/139 untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011vmzxpsFpc8q19LaogAWLD
This commit is contained in:
Kjell Tore Guttormsen 2026-06-26 12:10:42 +02:00
commit 2a8459c674
8 changed files with 474 additions and 43 deletions

View file

@ -343,6 +343,11 @@ written to `<data-dir>/trends/morning-brief/YYYY-MM-DD.md` and ranks only on per
(pillar overlap + `publishedAt`/`capturedAt` freshness, default 7-day window — tune with
`--fresh-days N`). Skip silently if the store has no deps installed — same escape hatch as Step 4.5.
The brief also applies a **derived temporal overlay** (RE-R3d): within a relevance tier, a fresh,
not-yet-surfaced trend is ranked up as a **first-mover** (`· 🥇 først ute`) and a repeatedly-surfaced
one is ranked down as **saturated** (`· 🔁 mettet`) — computed at render time from the publish/capture
dates + the seen-log, with no new capture step. Tune with `--first-mover-days N` / `--saturation-at N`.
The morning brief can also be **scheduled** to regenerate autonomously each morning — deterministic,
from the current store — via `src/cli.ts schedule` (print-first: it emits a launchd/cron entry firing
the `run-daily.sh` headless wrapper). That nightly run re-renders the brief only; your polling above