← board

DECISION 2026-08-02 — poll, never webhooks

User's call, given verbally at the xeon box, written back here per two-box-protocol.md ("a verbal decision must be written back into its ticket, or the peer never sees it").

No webhooks. A 60 second poll interval is sane. We might be adaptive — if nothing new for a while, poll somewhat less frequently.

So: every return path in Track T is a poll. No inbound HTTP, no callback URL, no service that must be reachable. This covers the daemon's view of origin, the agent-side verdict watch, and any host-local alerting.

Why polling wins here, concretely

Not a general preference — it follows from what this fleet is:

The daemon must NOT grow a time-based backoff

Recorded because "be adaptive" reads like an invitation to add one, and it would make things worse.

twatch's main loop already sleeps interval only when did_work is false — and the idle chain below a new sha is: full-matrix backfill → opt differential sweep → bench → bisect step → fuzz. With idle_fuzz on (default, and on in xeon's config, which is only {"autoticket": true}) the fuzzer never finishes, so the daemon reaches that time.sleep(interval) essentially never.

That is work-gated polling, which is strictly better than time-based backoff: it already fetches only when there is nothing else to do, and it never delays a fetch that had work waiting behind it. Adding "poll less often when quiet" on top would be a no-op at best, and at worst would insert a delay in front of the one cycle that had something to do.

The adaptive half of the decision therefore applies to future pollers that do not have this structure — the host-local health check, anything new — not to the daemon.

Numbers as they stand

loop interval note
daemon → origin (twatch --interval) 60s + 20s debounce work-gated; the sleep is rarely reached
twatch --follow <sha> 30s agent-facing; only runs while someone waits, exits on verdict
host-local health check 5-10 min (not built — [[task-t-xeon-host-local-health-alerting]]) poll trackt health, deliver on non-zero

--follow stays at 30s rather than being unified to 60. It is not a background loop: it exists only while an agent is blocked on a verdict, it terminates when the verdict lands, and its cost is one git fetch of an already-local-network origin. Halving its responsiveness would tax exactly the case the whole offload model was built to serve. 60s there would be harmless, just slightly worse; if uniformity is later preferred, change the default and note it here.

What actually governs verdict latency

Worth stating so nobody tunes the wrong number: the poll interval is not the bottleneck. A push preempts the full backfill, the opt sweep and the fuzzer (abort_check, ~30s granularity), but the bench phase is deliberately not preemptible (~2-3 min). So worst-case time-to-notice a push is dominated by bench plus the 20s debounce — an order of magnitude above any interval anyone would pick. Tracked as [[feature-t-bench-idle-must-be-preemptible]].

decide: how do Track T findings reach anyone?

The question this resolves: with the dev loop cut to ~15s and breadth offloaded to the watcher ([[meta-t-dev-throughput-and-track-a-t-integration]]), the return path became the product. Two shapes were available — push (webhook/callback into the waiting session) or pull (poll origin). The above picks pull, permanently, and records why, so it is not relitigated every time a new notifier is wired.