Skip to content

feat: add crg-daemon multi-repo watch daemon#167

Open
imkarrer wants to merge 6 commits intotirth8205:mainfrom
imkarrer:feature/crg-daemon
Open

feat: add crg-daemon multi-repo watch daemon#167
imkarrer wants to merge 6 commits intotirth8205:mainfrom
imkarrer:feature/crg-daemon

Conversation

@imkarrer
Copy link
Copy Markdown

@imkarrer imkarrer commented Apr 9, 2026

Summary

Adds a multi-repo watch daemon (crg-daemon) that spawns child processes (subprocess.Popen) each running code-review-graph watch for a configured repository. The daemon reads its configuration from a TOML file, monitors it for live changes, health-checks running watchers, and auto-restarts dead ones. No external dependencies (no tmux, no screen) — pure Python stdlib process management.

Motivation

The graph can go stale when changes occur outside agent-driven updates — manual edits, branch switches, rebases, or pulls from teammates. The daemon orchestrates watchers automatically across configured repos so the graph stays current without relying on hooks or manual rebuilds.

Related: #36, #59.

Changes

New files

File Lines Description
code_review_graph/daemon.py ~870 Core daemon: DaemonConfig/WatchRepo dataclasses, TOML load/save, ConfigWatcher, WatchDaemon (start/stop/reconcile/daemonize/health-check/run_forever), PID management, subprocess.Popen child process management with threading.Lock, persisted state file (daemon-state.json) for cross-process status queries
code_review_graph/daemon_cli.py ~330 Standalone crg-daemon argparse CLI with 7 subcommands: start, stop, restart, status, logs, add, remove; status reads persisted state to report live/dead watchers
tests/test_daemon.py ~790 39 tests covering config parsing, PID management, WatchDaemon lifecycle, health checking with auto-restart, reconciliation, state persistence, cross-process status reporting, and CLI handlers

Modified files

File Description
code_review_graph/cli.py Added daemon subparser group with 7 subcommands delegating to daemon_cli handlers; updated module docstring
pyproject.toml Added crg-daemon entry point in [project.scripts]; added tomli conditional dependency for Python < 3.11
README.md Added "Multi-repo daemon" to Features table, CLI reference, and new expandable details section
docs/COMMANDS.md Added daemon CLI commands and standalone crg-daemon reference with config example
docs/ROADMAP.md Added v2.2.0 section under Shipped

How it works

  1. User defines repos in ~/.code-review-graph/watch.toml
  2. crg-daemon start spawns one subprocess.Popen child per repo, each running code-review-graph watch with stdout/stderr redirected to per-repo log files
  3. A background thread monitors watch.toml for changes and reconciles child processes (starts new repos, terminates removed ones)
  4. Health checks run every 30 seconds — dead watchers (detected via proc.poll()) are automatically restarted
  5. crg-daemon start (without --foreground) double-forks to daemonize, writing a PID file for lifecycle management
  6. _terminate_child() sends SIGTERM, waits up to 5 seconds, then escalates to SIGKILL
  7. Thread-safe access to _children: dict[str, Popen] via threading.Lock
  8. Cross-process status: daemon persists child PIDs to daemon-state.json on every mutation; crg-daemon status reads this file and probes PIDs with os.kill(pid, 0) to report alive/dead without needing the daemon object in-memory

Usage

# Add repos
crg-daemon add ~/project-a --alias proj-a
crg-daemon add ~/project-b

# Start daemon
crg-daemon start

# Check status
crg-daemon status

# View logs
crg-daemon logs --repo proj-a -f

# Stop
crg-daemon stop

Also available as code-review-graph daemon start|stop|status|....

Testing

  • 39 tests in tests/test_daemon.py
  • All tests pass: uv run pytest tests/test_daemon.py -v
  • Ruff: clean
  • Mypy: no new errors (3 pre-existing unrelated issues in cli.py)

Checklist

  • Feature branch from main
  • Tests added for new functionality (39 tests)
  • All tests pass (uv run pytest)
  • Linting passes (uv run ruff check code_review_graph/)
  • Documentation updated (README, COMMANDS.md, ROADMAP.md)
  • No breaking changes to existing CLI or MCP tools
  • No external dependencies (pure stdlib subprocess management)
  • Code style: 100-char lines, Python 3.10+, from __future__ import annotations, type hints, logging.getLogger(__name__), no shell=True, parameterized SQL
  • Cross-process status reporting via persisted state file

Isaac Karrer and others added 6 commits April 8, 2026 19:18
…ild process management

Add a daemon that reads a TOML config listing repos to watch, spawns
child processes (subprocess.Popen) running 'code-review-graph watch'
per repo, monitors config for changes with auto-reconciliation,
health-checks children with auto-restart, and exposes CLI commands
for start/stop/restart/status/logs/add/remove.

New files:
- code_review_graph/daemon.py: DaemonConfig, WatchDaemon, ConfigWatcher,
  PID management, daemonization (double-fork), signal handling
- code_review_graph/daemon_cli.py: CLI entry point for crg-daemon

Modified:
- cli.py: add 'daemon' subcommand to main CLI
- pyproject.toml: add crg-daemon entry point and tomli dependency
Cover config parsing, PID management, WatchDaemon lifecycle,
health checking with auto-restart, reconciliation, and CLI handlers.
All tests use unittest.mock to mock subprocess.Popen.
…P.md

Document daemon CLI commands, configuration format, and usage examples.
Update ROADMAP.md v2.2.0 feature description.
Clarify the daemon's value prop for editors without hooks (Cursor,
OpenCode), add numbered quick-setup steps, inline a TOML config
example, and link to COMMANDS.md for full reference.
The status CLI was instantiating a fresh WatchDaemon with an empty
_children dict, so all repos appeared dead even when watcher processes
were confirmed running via ps.

Fix: the daemon now persists child PIDs to daemon-state.json whenever
children are started, stopped, or restarted. The status command (and
WatchDaemon.status()) reads this state file and checks liveness via
os.kill(pid, 0) when running cross-process.

Added 4 regression tests covering state persistence on start, health
check state updates, cross-process status reads, and the CLI handler.
@imkarrer imkarrer changed the title Feature/crg daemon feat: add crg-daemon multi-repo watch daemon Apr 9, 2026
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.

1 participant