First-PR scope: daemon skeleton, queue + dispatch loop, single-CC-session integration, basic logging, ntfy notifications, conversation history with summarization. Get one full loop working end-to-end against a real Anthropic API call.
Architecture
Single-process Python daemon. Main loop polls:
queue/*.json — CC session output envelopes; oldest-first.
state/jc_input.txt — JC override; treated as next chat-side turn (with optional @session-N: prefix for direct routing).
For each picked-up turn:
Append content to state/conversation.json history.
Call Anthropic API with full history + new content. Use prompt caching on the system prompt + early turns.
Parse response:
First 200 characters contain [NEEDS-JC] → set status to needs_jc, send ntfy notification, do NOT dispatch. Daemon waits on jc_input.txt.
Otherwise → write response to dispatch/<session_id>/input.txt (only when prior file is consumed/deleted by the session). CC sessions poll their own input.txt.
If history exceeds HISTORY_CHAR_CAP (default 400k chars), send a summarization turn, replace history with summary + last 10 turns verbatim.
Trust model
Daemon is pure transport between CC and chat-Claude. No merge decisions, no overriding CC's existing rules.
JC override (state/jc_input.txt) is treated as authoritative. With prefix @session-N: … it dispatches directly without an API call. Without prefix it goes through the API as if JC were responding from chat.
Generates a random ~16-char topic on first run, writes back to .env. Daemon prints https://ntfy.sh/<topic> prominently on startup. Subscribe from phone/laptop. Notifies on:
needs_jc flagged on a chat-side response
daemon error (uncaught exception, API failure after retry)
queue stuck >10min (stale entry)
Out of scope (deferred to follow-up PRs)
Status web UI (Flask endpoint to render queue depth, recent activity, current conversation length, last needs_jc time)
Multi-session config (more than one CC session at a time)
Cost tracking dashboard (basic per-call cost in logs ships in this PR; aggregation later)
AI tier
Tier 2. Touches secrets (API key), sends data to a third-party API, runs as a long-lived process. Octopus security-auditor + database-architect (for state-file integrity) reviews before PR.
Done when
Daemon starts, prints ntfy URL, picks up a queue entry, calls the Anthropic API with conversation history, dispatches the response to the right session's input.txt.
[NEEDS-JC] token detection pauses dispatch and notifies via ntfy.
Conversation summarization triggers at HISTORY_CHAR_CAP and reduces the in-memory + on-disk size.
JC override via state/jc_input.txt resumes from needs_jc or routes to a session via @session-N: prefix.
All unit tests pass; the gated real-API smoke test passes when run with a valid key.
README + .env.example sufficient for someone to clone and run.
## Goal
First-PR scope: daemon skeleton, queue + dispatch loop, single-CC-session integration, basic logging, ntfy notifications, conversation history with summarization. Get one full loop working end-to-end against a real Anthropic API call.
## Architecture
Single-process Python daemon. Main loop polls:
1. `queue/*.json` — CC session output envelopes; oldest-first.
2. `state/jc_input.txt` — JC override; treated as next chat-side turn (with optional `@session-N:` prefix for direct routing).
For each picked-up turn:
1. Append content to `state/conversation.json` history.
2. Call Anthropic API with full history + new content. Use prompt caching on the system prompt + early turns.
3. Parse response:
- First 200 characters contain `[NEEDS-JC]` → set status to `needs_jc`, send ntfy notification, do NOT dispatch. Daemon waits on `jc_input.txt`.
- Otherwise → write response to `dispatch/<session_id>/input.txt` (only when prior file is consumed/deleted by the session). CC sessions poll their own `input.txt`.
4. If history exceeds `HISTORY_CHAR_CAP` (default 400k chars), send a summarization turn, replace history with summary + last 10 turns verbatim.
## Trust model
- Daemon is pure transport between CC and chat-Claude. No merge decisions, no overriding CC's existing rules.
- JC override (`state/jc_input.txt`) is treated as authoritative. With prefix `@session-N: …` it dispatches directly without an API call. Without prefix it goes through the API as if JC were responding from chat.
## Files / structure
```
relay/
├── __init__.py
├── __main__.py # CLI: relay run
├── config.py # .env + config.yaml loader
├── daemon.py # main loop
├── queue.py # queue intake
├── dispatch.py # dispatch writer with one-input-at-a-time per session
├── conversation.py # history mgmt + summarization
├── anthropic_client.py # SDK wrapper, prompt caching, cost calc
├── ntfy.py # notifications
├── state.py # atomic file I/O, instance lock
└── logs.py # logging setup
tests/
├── test_config.py
├── test_state.py
├── test_queue.py
├── test_dispatch.py
├── test_conversation.py
├── test_anthropic_client.py # mocked SDK
├── test_daemon_loop.py # integrated, mocked SDK
└── test_real_api.py # gated on ANTHROPIC_API_KEY env var; one round-trip
config.yaml # sessions registry (one default for first PR)
```
## ntfy
Generates a random ~16-char topic on first run, writes back to `.env`. Daemon prints `https://ntfy.sh/<topic>` prominently on startup. Subscribe from phone/laptop. Notifies on:
- `needs_jc` flagged on a chat-side response
- daemon error (uncaught exception, API failure after retry)
- queue stuck >10min (stale entry)
## Out of scope (deferred to follow-up PRs)
- Status web UI (Flask endpoint to render queue depth, recent activity, current conversation length, last `needs_jc` time)
- Multi-session config (more than one CC session at a time)
- systemd unit
- Rich error recovery (exponential backoff, dead-letter queue)
- Cost tracking dashboard (basic per-call cost in logs ships in this PR; aggregation later)
## AI tier
Tier 2. Touches secrets (API key), sends data to a third-party API, runs as a long-lived process. Octopus security-auditor + database-architect (for state-file integrity) reviews before PR.
## Done when
- Daemon starts, prints ntfy URL, picks up a queue entry, calls the Anthropic API with conversation history, dispatches the response to the right session's `input.txt`.
- `[NEEDS-JC]` token detection pauses dispatch and notifies via ntfy.
- Conversation summarization triggers at `HISTORY_CHAR_CAP` and reduces the in-memory + on-disk size.
- JC override via `state/jc_input.txt` resumes from `needs_jc` or routes to a session via `@session-N:` prefix.
- All unit tests pass; the gated real-API smoke test passes when run with a valid key.
- README + .env.example sufficient for someone to clone and run.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Goal
First-PR scope: daemon skeleton, queue + dispatch loop, single-CC-session integration, basic logging, ntfy notifications, conversation history with summarization. Get one full loop working end-to-end against a real Anthropic API call.
Architecture
Single-process Python daemon. Main loop polls:
queue/*.json— CC session output envelopes; oldest-first.state/jc_input.txt— JC override; treated as next chat-side turn (with optional@session-N:prefix for direct routing).For each picked-up turn:
state/conversation.jsonhistory.[NEEDS-JC]→ set status toneeds_jc, send ntfy notification, do NOT dispatch. Daemon waits onjc_input.txt.dispatch/<session_id>/input.txt(only when prior file is consumed/deleted by the session). CC sessions poll their owninput.txt.HISTORY_CHAR_CAP(default 400k chars), send a summarization turn, replace history with summary + last 10 turns verbatim.Trust model
state/jc_input.txt) is treated as authoritative. With prefix@session-N: …it dispatches directly without an API call. Without prefix it goes through the API as if JC were responding from chat.Files / structure
ntfy
Generates a random ~16-char topic on first run, writes back to
.env. Daemon printshttps://ntfy.sh/<topic>prominently on startup. Subscribe from phone/laptop. Notifies on:needs_jcflagged on a chat-side responseOut of scope (deferred to follow-up PRs)
needs_jctime)AI tier
Tier 2. Touches secrets (API key), sends data to a third-party API, runs as a long-lived process. Octopus security-auditor + database-architect (for state-file integrity) reviews before PR.
Done when
input.txt.[NEEDS-JC]token detection pauses dispatch and notifies via ntfy.HISTORY_CHAR_CAPand reduces the in-memory + on-disk size.state/jc_input.txtresumes fromneeds_jcor routes to a session via@session-N:prefix.