Skip to content

feat(curator): monitors-based streaming curator (stages 1+2) — #65#92

Draft
ovidb wants to merge 4 commits into
mainfrom
feat/65-monitors-curator
Draft

feat(curator): monitors-based streaming curator (stages 1+2) — #65#92
ovidb wants to merge 4 commits into
mainfrom
feat/65-monitors-curator

Conversation

@ovidb
Copy link
Copy Markdown
Contributor

@ovidb ovidb commented Apr 19, 2026

Summary

Stages 1 + 2 of the migration from per-turn Stop-hook curator to a long-lived monitors-based streaming curator. Full plan: .claude/plans/65-monitors-curator.md. Architectural doc: issue #65.

  • New: rememora curate --stream — long-lived producer that reads session JSONL from stdin, gates + curates transcript deltas incrementally, emits rate-limited notifications via a token bucket.
  • New: plugin/monitors/monitors.json + plugin/scripts/curate-monitor.sh — Claude Code monitors entry wired to the above. Opt-in in this PR via REMEMORA_USE_MONITOR=1.
  • Refactor: curator::Subagent trait (+ DefaultSubagent) lets tests stub claude -p calls without spending tokens. Production path unchanged.
  • Stage 2 bridge: stop-curate.sh short-circuits when a streaming monitor is already running the session, preventing double-curate during dogfood.
  • Stage 2 instrumentation: --notify-log appends <rfc3339> <outcome> per flush for post-hoc volume tallying.
  • Plugin version: 1.2.1 → 1.3.0.

Why staged (what's NOT in this PR)

Stages 3 (flip monitor to always-on, remove Stop hook) and 4 (delete stop-curate.sh) land in follow-up PRs after ≥5 days of dogfood with REMEMORA_USE_MONITOR=1.

Structural wins vs post-#63 state

  • "≤1 curate per session" becomes a language-level invariant (tail | rememora is one pipeline), not a pgrep/filesystem dance.
  • Signal detector runs on new transcript bytes only; no full re-scan per turn.
  • Back-pressure is natural (blocking pipe).
  • Rate-limit moves into the Rust process as an explicit token bucket.

Test plan

  • cargo test --lib (53 passing, includes 9 new stream::tests)
  • cargo clippy --lib --tests clean (pre-existing tests/ dir noise is unrelated)
  • Stream state machine: push-line / flush / token-bucket / watermark persist+resume / clean EOF (unit tests)
  • Manual smoke: one live Claude Code session with REMEMORA_USE_MONITOR=1 → expect "rememora curator online" banner, one notification per curate-worthy delta, ≤1 / 30 s
  • Verify sqlite3 ~/.rememora/rememora.db 'select byte_offset from watermarks where file_path like "stream:%"' advances
  • Stage-2 dogfood: 5+ days with monitor default before stages 3+4 land

Risks / open items

  • Monitor env vars (CLAUDE_SESSION_ID, CLAUDE_CWD) may differ from hook env — the wrapper falls back to newest-JSONL-by-mtime if the explicit vars are missing. To be verified in dogfood (risk Local agent loop: auto-dispatch issues to Claude CLI #8 in plan).
  • monitors.json schema (top-level monitors key) is based on the plan's educated guess; if Claude Code expects a different shape, stage 2 dogfood catches it and we iterate.
  • tail -F on rotated logs is assumed to handle Claude Code's session JSONL writes. Dogfood confirms.

Closes #65 (partial — stages 1 + 2 only; stages 3 + 4 in follow-up PRs).

ovidb added 4 commits April 19, 2026 23:36
…ge 1)

Adds `rememora curate --stream`, a long-lived streaming curator that
incrementally gates + curates transcript deltas from stdin. Intended as
the worker for a future Claude Code `monitors` entry; Stop hook remains
the default in stage 1.

- `src/stream.rs`: `StreamState` state machine (push-line + flush-on-
  bytes/idle), token bucket rate limiter, watermark persistence, reader
  thread driver.
- `src/curator.rs`: `Subagent` trait + `DefaultSubagent` production impl
  so tests inject fakes without calling `claude -p`.
- CLI: new `--stream` / `--session` / `--stream-flush-ms` /
  `--stream-notify-secs` flags with clap `conflicts_with` against the
  existing curate modes.
- Plugin: opt-in `plugin/monitors/monitors.json` + `curate-monitor.sh`
  (gated on `REMEMORA_USE_MONITOR=1`), plugin version bumped to 1.3.0.

8 unit tests cover state machine, token bucket, watermark round-trip,
and clean-EOF reader shutdown. Full implementation plan lives at
`.claude/plans/65-monitors-curator.md`.
- `stop-curate.sh`: pre-flight pgrep for `rememora curate --stream` on
  the current session; short-circuits when the monitor is already
  serving, preventing double-curate during stage-2 dogfood.
- `curate --stream --notify-log <path>`: appends `<rfc3339> <outcome>`
  lines per flush (outcome ∈ no_signal, curated). Stage-2 metric
  plumbing; removed alongside `stop-curate.sh` in stage 4.
Unified version bump across VERSION / Cargo.{toml,lock} /
plugin.json / marketplace.json. Release workflow now:

- marks pre-release tags (anything with `-` in the tag name) as GitHub
  pre-releases via softprops/action-gh-release@v2
- skips the Homebrew tap update on pre-release tags so the tap's single
  stable formula isn't overwritten by an RC
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spike: replace Stop-hook curator with Claude Code monitors-based long-lived curator

1 participant