feat(linkedin-studio): RE-R3f — /linkedin:trends --headless (MR-F5) [skip-docs]

Unattended discovery: the full poll->score->capture->brief loop can
now fire with no operator present (Sunday-morning discovery), gated
behind a new Step 0.5 contract. Fails fast instead of asking when the
profile is missing, skips triage unconditionally (candidates stay
pending), and refuses --demand --headless rather than silently
degrading.

Trigger mechanism is Claude Code's own Desktop Scheduled Tasks, not a
hand-rolled cron wrapper -- verified against Claude Code's headless
docs that a cron + `claude -p` wrapper loses observability/session-
resumption/error-recovery versus the first-party mechanism. The
fallback `claude -p` recipe is documented (README) for portability,
not built as repo code. No new source/test file; no schema change;
counts unchanged (30/20/29).

This opens the research-engine's own 2026-06-24 re-evaluation gate
against v1.0.0 product maturity for slice (e) -- surfaced to the
operator explicitly rather than built around or silently deferred.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y2qHoS4FrkabuD1bCg8Nr2
This commit is contained in:
Kjell Tore Guttormsen 2026-08-10 20:57:34 +02:00
commit 3290e8fb7d
4 changed files with 111 additions and 8 deletions

View file

@ -148,9 +148,56 @@ line `<ISO-ts> exit=<code> <json>` to `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/lin
The nightly run is **deterministic-brief-only (C1)**: it re-renders the brief from the current store
— freshness-aging drops stale trends, `surfacedCount` accumulates day-over-day — but does **not** poll
new sources. A double-fire on the same day is a safe no-op (RE-R3b per-day idempotency: byte-identical
`.md`, `surfacedCount` not double-counted). The autonomous AI capture step (poll → score → capture
before the brief) plugs into the documented seam in `run-daily.sh` as a later slice (e); a
brief-history diff is also a later slice.
`.md`, `surfacedCount` not double-counted). This wrapper is deliberately unaware of the AI capture
step — that is a **different mechanism**, below (RE-R3f).
## Unattended AI discovery — `--headless` (RE-R3f, slice e)
The gap `run-daily.sh` leaves open on purpose: without new captures, the nightly brief is a
near-no-op. Slice (e) closes it by making the **actual discovery pass**`/linkedin:trends`'s
poll → score → capture → brief loop, including the trend-spotter agent's web research — safe to
fire with no operator present, so Sunday discovery can happen unattended.
**This is not a bash/cron wrapper.** `/linkedin:trends --headless` needs a full Claude Code agent
turn (it spawns the `trend-spotter` subagent), which only the `claude` binary itself can run — a
shell script cannot replicate that. Two mechanisms exist for firing it unattended:
- **Claude Code's Desktop Scheduled Tasks (recommended).** A first-party, local, persistent
scheduler purpose-built for exactly this — unlike a hand-rolled cron job, it survives restarts,
keeps session history, and handles auth without a standing `ANTHROPIC_API_KEY`. Set it up to run
`/linkedin:trends --headless [--mode long-form]` on your Sunday cadence.
- **A hand-rolled `claude -p` invocation** (e.g. from cron/launchd directly), for a machine where
Desktop Scheduled Tasks isn't available. The exact recipe, verified against Claude Code's own
headless docs:
```bash
claude -p "/linkedin:trends --headless" \
--bare --plugin-dir "<path to the installed linkedin-studio plugin>" \
--permission-mode dontAsk \
--allowedTools "Read,Bash,Task,WebSearch,WebFetch" \
--output-format json
```
`--bare` skips plugin auto-discovery for a faster/cleaner scripted start, so `--plugin-dir` is
required alongside it. `--bare` also means no OAuth/keychain session — set `ANTHROPIC_API_KEY` in
the environment. `--permission-mode dontAsk` auto-denies anything not in `--allowedTools`
(including `AskUserQuestion`, unconditionally) instead of blocking on a prompt — the command's own
**Step 0.5 contract** (`commands/trends.md`) is what makes that safe rather than a silent abort.
If the research subagent can run long, raise `CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS` (default
10-minute wait ceiling on a background subagent) or set it to `0`.
**Do not trust the exit code or the model's prose as the success signal** — Claude Code's own docs
are explicit that a `-p` run's process exit reflects the CLI process, not the pass's semantic
outcome. Verify the pass actually did something the same way Step 3 of the command already does:
`CLI status --json`'s capture delta and whether today's `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/
linkedin-studio}/trends/morning-brief/<date>.md` exists. `--output-format json` also reports
`total_cost_usd` per run — log it if you care about the standing cost of a weekly unattended
research pass.
**Scope note:** RE-R3f adds no new source/test files here — the entire mechanism is the
`commands/trends.md` `--headless` contract (a prompt-file change) plus this documentation. No
`schedule.ts`/`run-daily.sh` change; that wrapper stays what RE-R3c built (deterministic brief-only,
above), and remains a separate, valid path for a plain daily re-render with no new discovery.
## Temporal overlay (RE-R3d)