feat(linkedin-studio): RE-R3a — persist relevance score on the store record + rank the morning brief on it [skip-docs]
R3 slice 1 (research-deepening). Stop discarding the relevance judgment the
trend-spotter already computes: persist a 4-field TrendScore {mode, dimensions,
composite, priority} on TrendRecord (schema v2->v3, additive lossless migrate),
computed by the existing score.ts composite()+band() (one owner, no new arithmetic),
threaded item->store; then rankForBrief sorts each bucket composite-first (sentinel
-1 for unscored) and renderBrief surfaces "· <priority> (<mode>)" per body entry
(briefSummary shows the band only). First-sight only; mode-blind ranking with the mode
shown so the operator can disambiguate instruments.
- score.ts: TrendScore + requiredDimensions(mode) (ordered) + scoreEnvelope (composes
composite+band; throws on bad dim by contract)
- types.ts: SCHEMA_VERSION 2->3; TrendRecord.score?
- store.ts: TrendInput.score?; addTrend persists first-sight (duplicate keeps it);
migrate comment v1->v2->v3 (logic unchanged, JSON.stringify preserves the field)
- item.ts: TrendItem.score?; normalizeItem validates (non-array score/dimensions + the
mode's five dims in [1,10]) -> structured error never throw, carries validated dims;
itemToInput -> scoreEnvelope (no throw on the capture path; direct call throws by contract)
- brief.ts: composite-primary comparator; band+mode render; exact ranking: descriptor
- cli.ts: capture persists score via itemToInput (doc-only); add/score paths unchanged
- agents/trend-spotter.md Step 4.5: capture batch carries the Step-2 dimensions
- gate: TRENDS_TESTS_FLOOR 104->146; new unconditional Section 16j; ASSERT floor 94->99
Tests: trends 146/146 (RED two-phase: logic-RED store/brief/cli; stub-first then
assertion-RED score/item). Gate green (Passed 114 / Failed 0; 113 checks >= 99).
Hook suite 139/139 untouched. Counts 27/19/29 unchanged. No new source file/agent/command.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmHCQjJHUyWwxGAVVjNLgp
This commit is contained in:
parent
4d3b9f4711
commit
e169c78710
14 changed files with 829 additions and 40 deletions
|
|
@ -285,21 +285,36 @@ For every trend that cleared the relevance filter (Step 2) — not only the ones
|
|||
final digest — fold it into the persistent trend store, so the next session reasons over it
|
||||
instead of re-discovering it. Build ONE raw-item batch (the same trends you just scored) and pipe
|
||||
it through `capture`: it normalizes each item, dedupes on normalized title+URL, unions topics on
|
||||
re-capture (so re-capturing an existing trend just enriches the tags), and persists the source's
|
||||
`publishedAt` for later freshness ranking — one call, not one per trend:
|
||||
re-capture (so re-capturing an existing trend just enriches the tags), persists the source's
|
||||
`publishedAt` for later freshness ranking, and — when you carry the score (below) — persists the
|
||||
relevance assessment so the morning brief ranks on it — one call, not one per trend:
|
||||
|
||||
```bash
|
||||
cd "${CLAUDE_PLUGIN_ROOT}/scripts/trends" && \
|
||||
echo '[
|
||||
{"source":"<tavily|websearch|manual|…>","title":"<verbatim headline>","url":"<source url>",
|
||||
"topics":["<pillar-tag1>","<pillar-tag2>"],"publishedAt":"<YYYY-MM-DD if known>",
|
||||
"summary":"<one-line what-happened>"}
|
||||
"summary":"<one-line what-happened>",
|
||||
"score":{"mode":"kortform","dimensions":{"pillar":N,"audience":N,"timing":N,"angle":N,"authority":N}}}
|
||||
]' | node --import tsx src/cli.ts capture
|
||||
```
|
||||
|
||||
`source` is the tool you actually fetched with (**Research Routing**); `publishedAt` is the
|
||||
source's own publish date — omit the key when unknown (the store's `capturedAt` is set
|
||||
automatically and stays distinct from it). One `capture` call folds the whole batch and reports
|
||||
automatically and stays distinct from it).
|
||||
|
||||
**Carry the Step-2 scores — do not discard them.** You already scored each candidate's five
|
||||
dimensions 1–10 in **Relevance Scoring** (Step 2); fold those same numbers into the capture batch
|
||||
as the item's `"score"`, so the store persists the relevance assessment and the morning brief
|
||||
ranks on its composite (the store computes the composite + band itself — supply only the judgment).
|
||||
Use `"mode":"kortform"` by default; use `"mode":"long-form"` with the long-form dimension names
|
||||
(`pillar`, `depth`, `angle`, `authority`, `currency`) when the caller is producing a chronicle /
|
||||
newsletter / series edition (e.g. invoked from `/linkedin:newsletter`). The `"dimensions"` keys are
|
||||
the rubric's, the `"topics"` are the user's pillars — nothing vendor- or sector-specific is baked
|
||||
in. Omit the `"score"` key when you genuinely did not score an item; an out-of-range or malformed
|
||||
score is reported in `errors[]` (the valid items still persist) and never crashes the run.
|
||||
|
||||
One `capture` call folds the whole batch and reports
|
||||
`{added, merged, duplicates, errors}`; content-invalid items land in `errors[]`, never failing the
|
||||
run. Skip this step silently if the store has no deps installed (an adopter without the trends
|
||||
store) — the digest still compiles, just without persistence.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue