feat(linkedin-studio): RE-R3c — autonomous trigger (scheduler + headless entry) [skip-docs]
Closes research-engine hulls (1) no autonomous trigger + (6) no headless entry. Makes the daily research loop closed + headless: deterministic-brief-only (C1), print-first (C2 — the tool never runs launchctl or the cron table; --install writes only the inert launchd plist file). - NEW scripts/trends/src/schedule.ts — pure string emitters (launchd plist + cron-line + install/uninstall instructions + defaultLabel). No clock/fs/env/AI; byte-deterministic. - NEW scripts/trends/run-daily.sh — bash-3.2 headless wrapper: resolves node, cd's into the package so tsx resolves, logs via the data-path twin seam; runs the deterministic brief and appends one compact cron.log line per fire. The (e) AI-capture seam is documented, not built. - EDIT cli.ts — schedule --pillars <a,b> [--at HH:MM] [--fresh-days N] [--platform auto|launchd|cron] [--install|--uninstall] [--store <p>]; print-first, no new exit code; logPath anchored to dirname(defaultStorePath()) (not the --store override). - WIRE trend-spotter.md (one prose line) + README (scheduler + wrapper + the C1 boundary). - Gate: TRENDS_TESTS_FLOOR 171->192, ASSERT_BASELINE_FLOOR 105->111, new UNCONDITIONAL Section 16l (6 deps-absent greps + non-vacuity self-test), header-enum + floor-history append. TDD two-phase RED -> GREEN. trends 192/192, gate 126/0, hook-suite 139/0 (untouched), plutil -lint OK. No schema change (SCHEMA_VERSION 4 / BRIEF_SCHEMA_VERSION 1). Counts 29/19/27 unchanged. 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
b43757462b
commit
3276e44dbf
9 changed files with 768 additions and 9 deletions
|
|
@ -54,7 +54,11 @@
|
|||
# 'export type TrendStatus' AND carries 'surfacedCount', scripts/trends/src/store.ts owns
|
||||
# 'export function markSurfaced', scripts/trends/src/brief.ts excludes handled via 'effectiveStatus',
|
||||
# AND scripts/trends/src/cli.ts exposes 'command === "act"', with a non-vacuity self-test) in
|
||||
# Section 16k; the assertion-count anti-erosion floor (SC6) in Section 18. All
|
||||
# Section 16k; the trends-scheduler/headless wiring guard (RE-R3c: scripts/trends/src/schedule.ts
|
||||
# emits 'export function launchdPlist' AND 'export function crontabLine', scripts/trends/src/cli.ts
|
||||
# exposes 'command === "schedule"', scripts/trends/run-daily.sh runs 'cli.ts" brief' AND uses
|
||||
# 'LINKEDIN_STUDIO_DATA:-', with a non-vacuity self-test) in Section 16l; the assertion-count
|
||||
# anti-erosion floor (SC6) in Section 18. All
|
||||
# are live below (Sections 8–18).
|
||||
#
|
||||
# Usage: bash scripts/test-runner.sh
|
||||
|
|
@ -706,7 +710,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=171 # 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) + RE-R3b: store +11, brief +8, cli +6 (lifecycle: re-score + status + seen-log)
|
||||
TRENDS_TESTS_FLOOR=192 # 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) + RE-R3b: store +11, brief +8, cli +6 (lifecycle: re-score + status + seen-log) + RE-R3c: schedule +9, cli +8, run-daily +4 (scheduler + headless wrapper)
|
||||
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
|
||||
|
|
@ -1304,6 +1308,71 @@ fi
|
|||
|
||||
echo ""
|
||||
|
||||
# --- Section 16l: Trends Scheduler / Headless Wiring (research-engine RE-R3c) ---
|
||||
echo "--- Trends Scheduler / Headless Wiring ---"
|
||||
|
||||
# RE-R3c adds the autonomous trigger + headless entry: a `schedule` CLI verb emitting a launchd plist /
|
||||
# cron line (print-first), and a bash wrapper that runs the DETERMINISTIC brief headless. Five literals
|
||||
# must hold, grepped EXACT (grep -F), deps-absent-safe (pure grep, no tsx):
|
||||
# (1) schedule.ts emits the launchd plist, by 'export function launchdPlist';
|
||||
# (2) schedule.ts emits the cron line, by 'export function crontabLine';
|
||||
# (3) cli.ts exposes the schedule verb, by 'command === "schedule"';
|
||||
# (4) run-daily.sh runs the deterministic brief, by 'cli.ts" brief' (the wrapper owns the subcommand);
|
||||
# (5) run-daily.sh uses the data-path twin seam, by 'LINKEDIN_STUDIO_DATA:-'.
|
||||
# Non-vacuity self-test mirrors Section 16k: a probe carrying the launchdPlist pointer is accepted and
|
||||
# one without it rejected. Placed after Section 16k / before Section 18 (anti-erosion must run last so it
|
||||
# sees every prior check). UNCONDITIONAL (no tsx) -> counts toward ASSERT_BASELINE_FLOOR.
|
||||
SCHED_PLIST_LIT='export function launchdPlist'
|
||||
SCHED_CRON_LIT='export function crontabLine'
|
||||
SCHED_VERB_LIT='command === "schedule"'
|
||||
SCHED_BRIEF_LIT='cli.ts" brief'
|
||||
SCHED_DATA_LIT='LINKEDIN_STUDIO_DATA:-'
|
||||
|
||||
I16L_SELFTEST_OK=1
|
||||
if ! echo 'a wired scheduler declares: export function launchdPlist(spec)' | grep -qF "$SCHED_PLIST_LIT"; then
|
||||
I16L_SELFTEST_OK=0; echo " non-vacuity FAIL: a wired scheduler-emit probe was not detected"
|
||||
fi
|
||||
if echo 'an unwired module emits no artifact at all' | grep -qF "$SCHED_PLIST_LIT"; then
|
||||
I16L_SELFTEST_OK=0; echo " false-positive FAIL: an unwired probe matched the scheduler-emit pointer"
|
||||
fi
|
||||
if [ "$I16L_SELFTEST_OK" -eq 1 ]; then
|
||||
pass "trends-scheduler self-test: the emit pointer is detected, the no-emit form rejected"
|
||||
else
|
||||
fail "trends-scheduler self-test failed — the scheduler-wiring lint is vacuous or over-eager"
|
||||
fi
|
||||
|
||||
if grep -qF "$SCHED_PLIST_LIT" scripts/trends/src/schedule.ts; then
|
||||
pass "schedule.ts emits the launchd plist ('$SCHED_PLIST_LIT')"
|
||||
else
|
||||
fail "schedule.ts has no launchd emitter — add '$SCHED_PLIST_LIT' (RE-R3c scheduler)"
|
||||
fi
|
||||
|
||||
if grep -qF "$SCHED_CRON_LIT" scripts/trends/src/schedule.ts; then
|
||||
pass "schedule.ts emits the cron line ('$SCHED_CRON_LIT')"
|
||||
else
|
||||
fail "schedule.ts has no cron-line emitter — add '$SCHED_CRON_LIT' (RE-R3c scheduler)"
|
||||
fi
|
||||
|
||||
if grep -qF "$SCHED_VERB_LIT" scripts/trends/src/cli.ts; then
|
||||
pass "cli.ts exposes the schedule verb ('$SCHED_VERB_LIT')"
|
||||
else
|
||||
fail "cli.ts has no schedule verb — add '$SCHED_VERB_LIT' (RE-R3c trigger)"
|
||||
fi
|
||||
|
||||
if grep -qF "$SCHED_BRIEF_LIT" scripts/trends/run-daily.sh; then
|
||||
pass "run-daily.sh runs the deterministic brief ('$SCHED_BRIEF_LIT')"
|
||||
else
|
||||
fail "run-daily.sh does not run the brief — wire '$SCHED_BRIEF_LIT' (RE-R3c headless entry)"
|
||||
fi
|
||||
|
||||
if grep -qF "$SCHED_DATA_LIT" scripts/trends/run-daily.sh; then
|
||||
pass "run-daily.sh uses the data-path twin seam ('$SCHED_DATA_LIT')"
|
||||
else
|
||||
fail "run-daily.sh has no data-path seam — add '$SCHED_DATA_LIT' (RE-R3c fourth twin)"
|
||||
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
|
||||
|
|
@ -1321,12 +1390,17 @@ echo ""
|
|||
# (trends-brief self-test + cli.ts brief-handler grep + trend-spotter brief-pointer grep +
|
||||
# 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.
|
||||
# trend-spotter dimensions grep + brief.ts composite-rank grep) = 99; +6 for RE-R3b's six
|
||||
# UNCONDITIONAL Section-16k checks (lifecycle self-test + types.ts TrendStatus grep + types.ts
|
||||
# surfacedCount grep + store.ts markSurfaced grep + brief.ts effectiveStatus grep + cli.ts
|
||||
# act-verb grep) = 105; +6 for RE-R3c's six UNCONDITIONAL Section-16l checks (scheduler self-test
|
||||
# + schedule.ts launchdPlist grep + schedule.ts crontabLine grep + cli.ts schedule-verb grep +
|
||||
# run-daily.sh brief-invocation grep + run-daily.sh data-twin grep) = 111.
|
||||
# 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=105
|
||||
ASSERT_BASELINE_FLOOR=111
|
||||
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