3.7 KiB
| id | date | status | supersedes | superseded-by | affected-paths | affected-components | |||
|---|---|---|---|---|---|---|---|---|---|
| 0035 | 2026-07-13 | Accepted |
|
|
0035 — Issue-triggered project AI wakeup via polling, not webhooks
Context
Issue #28 (design spike, blocked-by #27/ADR-0034): when an issue is filed with a project — often via cross-project file-don't-fix — that project's AI should be awakened to triage or act, instead of waiting for the human to open a session. The spike must pick a trigger mechanism, autonomy guardrails, credential scope, and context handoff. The human pre-chose the direction: polling trigger + tmux execution surface (ADR-0036 records the tmux convention).
Decision
Trigger is a poller, not webhooks: a cron/heartbeat script (pilot: plugins/os-backlog/bin/wakeup-poll, one pass per invocation, no cron installed yet) scans opted-in projects for NEW open issues. (1) Scope: rows of the global project index ~/.cc-os/projects.json (ADR-0034) whose tracker is forgejo: or github:, AND whose per-project .cc-os/config carries the explicit opt-in marker wakeup=true (the index row path locates the config; a later config-write slice may mirror the flag into the row — the config file remains the authority). Planka/repo-tracked projects are never polled. (2) New-ness: last-seen state persisted at ~/.cc-os/wakeup-state.json (per-repo highest issue index seen); first scan of a repo baselines without waking anything. (3) Autonomy, in ADR-0029 vocabulary via issue labels: afk-ready -> spawn an acting session; semi -> spawn a triage-only session (label + comment, stop at proposing — never implement); anything else (hitl or unlabeled) -> notify-only. Notification policy v2 (pull-only) applies: the scan appends to a summary the human pulls (~/.cc-os/wakeup-summary.md) — no push notifications. (4) Credentials: the poller lists issues through tea's existing login only; it NEVER reads ~/.credentials contents; spawned sessions get Planka creds the established way (source in a subshell). (5) Context handoff: the spawned session's initial prompt carries the issue number, title, URL, and the Discoverer block if present (ADR-0034), plus its autonomy tier. (6) Execution surface is a tmux session per ADR-0036.
Consequences
Easier: host-agnostic (works for Forgejo and GitHub identically through tea/gh), no listener endpoint, no inbound network surface, trivially disabled (remove the cron line or the wakeup flag). Harder: latency is the poll interval, and the poller is per-machine state — two machines polling would double-wake (accepted for the single-operator pilot; state file is per-host). Non-goals, explicitly: no webhooks; no auto-merge/auto-close; no acting on hitl/unlabeled issues; wakened sessions still obey file-don't-fix (ADR-0034) — they never edit other projects; no bulk backfill of pre-existing open issues (baseline-then-watch).
Alternatives rejected
- Forgejo webhooks -> local listener — rejected for the first cut: needs an always-up endpoint reachable from the Forgejo host, doesn't cover GitHub without a second mechanism, and adds an inbound attack surface; polling via tea reuses existing credentials and works everywhere. Webhooks remain the natural upgrade if poll latency ever matters. 2) Opt-in as a field only in the global index row — rejected: the index is derived and rebuildable (ADR-0034); a rebuild would silently drop opt-ins. Authority lives in the per-project .cc-os/config (ADR-0027). 3) Push notifications per new issue (Apprise, ADR-0024) — rejected here: notification policy v2 is pull-only for ambient status; push stays reserved for the human explicitly asking.