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
|
|
@ -46,7 +46,11 @@
|
|||
# trends-brief wiring guard (RE-R2b: scripts/trends/src/cli.ts dispatches `brief`,
|
||||
# agents/trend-spotter.md references the brief CLI 'src/cli.ts brief', AND
|
||||
# hooks/scripts/session-start.mjs surfaces it via 'latestMorningBrief', with a non-vacuity
|
||||
# self-test) in Section 16i; the assertion-count anti-erosion floor (SC6) in Section 18. All
|
||||
# self-test) in Section 16i; the trends-score wiring guard (RE-R3a: scripts/trends/src/score.ts
|
||||
# exports the 'export interface TrendScore' persist envelope, scripts/trends/src/types.ts carries
|
||||
# 'score?: TrendScore' on the record, agents/trend-spotter.md carries the judgment via '"dimensions"',
|
||||
# AND scripts/trends/src/brief.ts ranks on 'score?.composite', with a non-vacuity self-test) in
|
||||
# Section 16j; the assertion-count anti-erosion floor (SC6) in Section 18. All
|
||||
# are live below (Sections 8–18).
|
||||
#
|
||||
# Usage: bash scripts/test-runner.sh
|
||||
|
|
@ -698,7 +702,7 @@ if [ -x "$TR_DIR/node_modules/.bin/tsx" ]; then
|
|||
TR_OUT=$( set +e; (cd "$TR_DIR" && npm test) 2>&1; echo "TR_EXIT:$?" )
|
||||
TR_EXIT=$(echo "$TR_OUT" | grep -oE 'TR_EXIT:[0-9]+' | grep -oE '[0-9]+' | head -1)
|
||||
TR_TESTS=$(echo "$TR_OUT" | grep -oE 'tests [0-9]+' | grep -oE '[0-9]+' | tail -1)
|
||||
TRENDS_TESTS_FLOOR=104 # store 24 + RE-R1: item 18 + score 16 + cli 4 + RE-R2a: store +9 + item +4 + cli +4 (capture bridge + publishedAt) + RE-R2b: brief +21 + cli +4 (morning-brief)
|
||||
TRENDS_TESTS_FLOOR=146 # store 24 + RE-R1: item 18 + score 16 + cli 4 + RE-R2a: store +9 + item +4 + cli +4 (capture bridge + publishedAt) + RE-R2b: brief +21 + cli +4 (morning-brief) + RE-R3a: score +6, item +12, store +6, brief +16, cli +2 (relevance score persist + rank)
|
||||
if [ "$TR_EXIT" = "0" ] && [ -n "$TR_TESTS" ] && [ "$TR_TESTS" -ge "$TRENDS_TESTS_FLOOR" ]; then
|
||||
pass "trends-store suite green: $TR_TESTS tests pass (floor $TRENDS_TESTS_FLOOR)"
|
||||
else
|
||||
|
|
@ -1170,6 +1174,66 @@ fi
|
|||
|
||||
echo ""
|
||||
|
||||
# --- Section 16j: Trends Score Wiring (research-engine RE-R3a) ---
|
||||
echo "--- Trends Score Wiring ---"
|
||||
|
||||
# RE-R3a persists the relevance score the trend-spotter agent already computes and ranks the
|
||||
# morning brief on its composite. Four literals must hold, grepped EXACT (grep -F),
|
||||
# deps-absent-safe (pure grep, no tsx):
|
||||
# (1) score.ts exports the persist envelope, by the literal 'export interface TrendScore';
|
||||
# (2) types.ts carries it on the record, by the literal 'score?: TrendScore';
|
||||
# (3) agents/trend-spotter.md carries the judgment in the capture batch, by 'dimensions'
|
||||
# (verified absent pre-R3a -> the grep is non-vacuous);
|
||||
# (4) brief.ts ranks on the composite, by the literal 'score?.composite' (the payoff is wired,
|
||||
# not merely doc'd).
|
||||
# Non-vacuity self-test mirrors Section 16i: the rank predicate must accept a probe carrying the
|
||||
# composite-rank literal and reject one without it. Placed after Section 16i / before Section 18
|
||||
# (anti-erosion must run last so it sees every prior check). UNCONDITIONAL (no tsx) -> counts
|
||||
# toward ASSERT_BASELINE_FLOOR.
|
||||
SCORE_IFACE_LIT='export interface TrendScore'
|
||||
SCORE_TYPE_LIT='score?: TrendScore'
|
||||
SCORE_DIMS_LIT='"dimensions"'
|
||||
SCORE_RANK_LIT='score?.composite'
|
||||
|
||||
I16J_SELFTEST_OK=1
|
||||
if ! echo 'rankForBrief sorts on (b.trend.score?.composite ?? -1) first' | grep -qF "$SCORE_RANK_LIT"; then
|
||||
I16J_SELFTEST_OK=0; echo " non-vacuity FAIL: a wired composite-rank probe was not detected"
|
||||
fi
|
||||
if echo 'the brief ranks on pillar overlap only' | grep -qF "$SCORE_RANK_LIT"; then
|
||||
I16J_SELFTEST_OK=0; echo " false-positive FAIL: an unwired probe matched the composite-rank pointer"
|
||||
fi
|
||||
if [ "$I16J_SELFTEST_OK" -eq 1 ]; then
|
||||
pass "trends-score self-test: composite-rank predicate detects wiring, rejects the under-wired form"
|
||||
else
|
||||
fail "trends-score self-test failed — the score-wiring lint is vacuous or over-eager"
|
||||
fi
|
||||
|
||||
if grep -qF "$SCORE_IFACE_LIT" scripts/trends/src/score.ts; then
|
||||
pass "score.ts exports the persist envelope ('$SCORE_IFACE_LIT')"
|
||||
else
|
||||
fail "score.ts has no TrendScore envelope — add '$SCORE_IFACE_LIT' (RE-R3a persist)"
|
||||
fi
|
||||
|
||||
if grep -qF "$SCORE_TYPE_LIT" scripts/trends/src/types.ts; then
|
||||
pass "types.ts carries the score on the record ('$SCORE_TYPE_LIT')"
|
||||
else
|
||||
fail "types.ts does not carry the score — add '$SCORE_TYPE_LIT' to TrendRecord (RE-R3a schema v3)"
|
||||
fi
|
||||
|
||||
if grep -qF "$SCORE_DIMS_LIT" agents/trend-spotter.md; then
|
||||
pass "trend-spotter.md carries the judgment in the capture batch ($SCORE_DIMS_LIT)"
|
||||
else
|
||||
fail "trend-spotter.md does not carry the judgment — add the per-item $SCORE_DIMS_LIT to Step 4.5 (RE-R3a wiring)"
|
||||
fi
|
||||
|
||||
if grep -qF "$SCORE_RANK_LIT" scripts/trends/src/brief.ts; then
|
||||
pass "brief.ts ranks on the composite ('$SCORE_RANK_LIT')"
|
||||
else
|
||||
fail "brief.ts does not rank on the composite — add the '$SCORE_RANK_LIT' comparator term (RE-R3a payoff)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# --- Section 18: Assertion-Count Anti-Erosion (SC6) ---
|
||||
# The lint self-modifies its own checks, so a green run could mask a silently dropped
|
||||
# assertion. Pin the total pass()+fail() invocations as a monotonic floor; the count
|
||||
|
|
@ -1185,12 +1249,14 @@ echo ""
|
|||
# UNCONDITIONAL Section-16h checks (trends-capture self-test + cli.ts capture-handler grep +
|
||||
# trend-spotter capture-pointer grep) = 90; +4 for RE-R2b's four UNCONDITIONAL Section-16i checks
|
||||
# (trends-brief self-test + cli.ts brief-handler grep + trend-spotter brief-pointer grep +
|
||||
# session-start surfacing grep) = 94.
|
||||
# session-start surfacing grep) = 94; +5 for RE-R3a's five UNCONDITIONAL Section-16j checks
|
||||
# (trends-score self-test + score.ts TrendScore-iface grep + types.ts score-field grep +
|
||||
# trend-spotter dimensions grep + brief.ts composite-rank grep) = 99.
|
||||
# NB: the floor tracks the deps-absent MINIMUM (conditional TS suites warn-skip and drop
|
||||
# the count), so it is bumped only by UNCONDITIONAL new checks — NOT pinned to the
|
||||
# deps-present TOTAL_CHECKS (that would zero the warn-skip margin and false-fail a fresh
|
||||
# clone). Runs last so TOTAL_CHECKS sees every prior check.
|
||||
ASSERT_BASELINE_FLOOR=94
|
||||
ASSERT_BASELINE_FLOOR=99
|
||||
TOTAL_CHECKS=$((PASS + FAIL))
|
||||
if [ "$TOTAL_CHECKS" -ge "$ASSERT_BASELINE_FLOOR" ]; then
|
||||
pass "assertion-count anti-erosion: $TOTAL_CHECKS checks >= baseline floor $ASSERT_BASELINE_FLOOR"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue