feat(linkedin-studio): RE-R3b — trend lifecycle (re-score on re-capture · status · seen-log) [skip-docs]
The lifecycle layer over the trend store: what happens to a trend AFTER first capture.
- re-score on re-capture (last-wins; addTrend duplicate branch, score the one mutable
field; provenance + lifecycle untouched; no false-merge via JSON compare). Reverses
R3a's first-sight D3 — that R3a test reconciled to the new behaviour.
- status new/acted/skipped (effectiveStatus/setStatus + act/skip/reset CLI verbs);
rankForBrief EXCLUDES handled trends (a work queue, not an archive).
- seen-log surfacedCount/lastSurfacedAt (markSurfaced, per-day idempotent); the brief
CLI records surfacing on the store AFTER the pure render, unless --no-mark.
- render: entry id in backticks (copy-paste for act/skip) + · sett Nx prior-day hint.
- schema v3→v4 (additive lossless); the R3a migration block reconciled to the bump,
the new R3b block committed against SCHEMA_VERSION (breaks the reconcile cycle).
score.ts + item.ts untouched (re-score reuses the R3a capture path). RED-first (two
phase: 16 logic-RED + 4 stub-RED). Gate: Section 16k (6 emitters), TRENDS_TESTS_FLOOR
146→171, ASSERT_BASELINE_FLOOR 99→105. trends 171/171, gate 120/0/0, hook suite 139/139.
Plan: docs/research-engine/{brief,plan}-re-r3b.md (light-Voyage hardened @ c40b937).
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:
parent
c40b937856
commit
b185db9a12
10 changed files with 661 additions and 44 deletions
|
|
@ -39,7 +39,10 @@ interface TrendRecord {
|
|||
publishedAt?: string;// optional source publish date (ISO-8601); distinct from capturedAt, first-sight, never back-filled
|
||||
topics: string[]; // query tags; unioned across re-captures
|
||||
summary?: string; // optional, verbatim
|
||||
score?: TrendScore; // optional persisted relevance (RE-R3a): { mode, dimensions, composite, priority } — first-sight, never re-scored
|
||||
score?: TrendScore; // persisted relevance (RE-R3a): { mode, dimensions, composite, priority } — REFRESHED on re-capture (RE-R3b, last-wins)
|
||||
status?: TrendStatus; // lifecycle (RE-R3b): "new" | "acted" | "skipped"; absent ⇒ "new"; the brief excludes non-new
|
||||
surfacedCount?: number; // seen-log (RE-R3b): distinct days surfaced in a brief; absent ⇒ 0; per-day idempotent
|
||||
lastSurfacedAt?: string; // seen-log (RE-R3b): ISO date of the most recent surfacing
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -47,9 +50,15 @@ interface TrendRecord {
|
|||
agent's **judgment** — `{ mode, dimensions }` (the five 1–10 dimension scores) — and the
|
||||
store turns that into the persisted `TrendScore` `{ mode, dimensions, composite, priority }`,
|
||||
computing the composite + band once via the single scorer owner (`src/score.ts`). It is
|
||||
set **first-sight** (never updated on re-capture); the score-free `add` manual path omits it.
|
||||
The morning brief ranks each bucket on `composite` first (schema v3). Further fields
|
||||
(first-mover timing, status) can still be added in a later slice without breaking the shape.
|
||||
**refreshed on re-capture** (RE-R3b, last-wins — the timing dimension decays, so the newer
|
||||
judgment supersedes the stored one; `score` is the one mutable field, provenance stays
|
||||
first-sight); the score-free `add` manual path omits it. The morning brief ranks each bucket
|
||||
on `composite` first (schema v4).
|
||||
|
||||
The **lifecycle** fields (RE-R3b) are the trend's life after first capture: `status` is set by
|
||||
the `act`/`skip`/`reset` verbs (a freshly-captured trend is implicitly `new`), and the seen-log
|
||||
`surfacedCount`/`lastSurfacedAt` is recorded by `brief` (per-day idempotent) so the loop can avoid
|
||||
re-surfacing handled work.
|
||||
|
||||
## CLI
|
||||
|
||||
|
|
@ -80,10 +89,17 @@ node --import tsx src/cli.ts query --topics "agents,engineering" [--json]
|
|||
# Time-scoped history — newest first, optionally windowed/capped
|
||||
node --import tsx src/cli.ts list [--since 2026-06-01] [--limit 10] [--json]
|
||||
|
||||
# Dated morning brief — rank the store by pillar-overlap then recency, write a dated
|
||||
# Markdown file the SessionStart hook surfaces. Pillars come from the caller (user config).
|
||||
# Dated morning brief — rank the store by composite then pillar-overlap then recency, write a
|
||||
# dated Markdown file the SessionStart hook surfaces. Pillars come from the caller (user config).
|
||||
# The brief EXCLUDES acted/skipped trends and RECORDS surfacing on the store (per-day idempotent)
|
||||
# unless --no-mark. Pillars come from the caller (user config).
|
||||
node --import tsx src/cli.ts brief --pillars "agents,engineering" \
|
||||
[--fresh-days 7] [--out <dir>] [--store <path>] [--json]
|
||||
[--fresh-days 7] [--out <dir>] [--no-mark] [--store <path>] [--json]
|
||||
|
||||
# Lifecycle — mark a trend handled so the brief stops re-surfacing it (id shown in the brief / list --json):
|
||||
node --import tsx src/cli.ts act --id <id> # wrote about it
|
||||
node --import tsx src/cli.ts skip --id <id> # decided to pass on it
|
||||
node --import tsx src/cli.ts reset --id <id> # return it to the queue
|
||||
```
|
||||
|
||||
Both `capture` and `add` dedupe on normalized title+url — re-capturing the same trend
|
||||
|
|
@ -103,8 +119,13 @@ ${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/trends/morning-brief/YYYY
|
|||
The file's YAML frontmatter carries a single-line `summary` the SessionStart hook surfaces
|
||||
verbatim (zero-tsx — it reads the Markdown, never the TS CLI). As of RE-R3a the brief ranks
|
||||
each bucket on the persisted relevance **composite first** (then pillar-overlap, then recency);
|
||||
a scored entry shows `· <priority> (<mode>)` and the summary names the top entry's band. An
|
||||
autonomous nightly trigger and a seen-log freshness model remain later slices.
|
||||
a scored entry shows `· <priority> (<mode>)` and the summary names the top entry's band.
|
||||
|
||||
As of **RE-R3b** the brief is a **work queue**: it **excludes** `acted`/`skipped` trends, shows
|
||||
each entry's `id` in backticks (copy-paste-ready for `act`/`skip --id`), flags a re-surfaced item
|
||||
with `· sett Nx` (prior-day count, ≥2), and — unless `--no-mark` — **records surfacing** on the
|
||||
store (`surfacedCount`/`lastSurfacedAt`, per-day idempotent) after the pure render. An autonomous
|
||||
nightly trigger and a brief-history diff remain later slices.
|
||||
|
||||
## Tests
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue