docs(#2699): record the generalized subagent watchdog as out-of-scope (#2706)

Records the No-go disposition for #2699 in the rejected-capability
knowledge base consulted by /triage-review on every future run.

GSD does not take a generalized, event-driven, heartbeat-augmented
watchdog layer. The artifact-aware spot-check it generalizes already
ships for the executor, and the planner gap that motivated the proposal
has a scoped fix already diagnosed on #2650 that explicitly excludes
this rearchitecture.

The entry carries a "What this does NOT cover" section because its
keyword surface (watchdog, stall, timeout, heartbeat, orphan, recovery)
overlaps request types this decision deliberately does not deny --
notably extending the existing spot-check pattern to another spawn site,
which remains the sanctioned incremental path.


Claude-Session: https://claude.ai/code/session_01QT3ibz5qJuDuGqpTGRYVGf

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-07-27 11:34:48 -04:00
committed by GitHub
parent 27c2279a39
commit a7f521c84e

View File

@@ -0,0 +1,113 @@
# Generalized subagent activity watchdog
**Source:** [#2699](https://github.com/open-gsd/gsd-core/issues/2699)
**Decision:** wontfix — No-go as filed; redirected to [#2650](https://github.com/open-gsd/gsd-core/issues/2650)
**Date:** 2026-07-27
## Proposal summary
Reporter proposed a general, runtime-agnostic watchdog that would continuously classify
every spawned role agent (planner, executor, reviewer, researcher) into one of five
states — `ACTIVE`, `STALLED`, `COMPLETED_WITHOUT_MARKER`, `LOST_WITHOUT_ARTIFACT`,
`FAILED` — and surface recovery checkpoints instead of an indefinite spinner.
The proposed mechanism: a `SubagentWatchState` object per spawned agent, updated from
tool-call start/end events, artifact creation and mtime changes, commits, and an optional
30–60 second heartbeat emitted by the agent or its wrapper; plus per-unit
`expectedArtifacts` declarations, a `watchdog.*` config block, and bounded chunk resume
that preserves already-committed chunks. Explicitly not `ps aux`, explicitly no auto-kill.
## Why GSD does not own this
- **A narrower version already ships, and this proposal skips past it.**
`gsd-core/workflows/execute-phase.md` already implements artifact-aware stall detection
for the executor: a SUMMARY-existence plus `git log --since` spot-check (`:742-744`), a
periodic surveillance loop (`:755-762`) gated on `executor.stall_detect_interval_minutes`
and `executor.stall_threshold_minutes` (read at `:100-101`; both registered in
`SCHEMA_DEFAULTS` at `src/config.cts:89-90`, defaults 5 and 10), and a *continue waiting /
kill and retry / kill and switch to inline* recovery triad (`:759-762`) that never
auto-kills. The proposal's core insight is already the shipped design; what it adds is
generalization.
*(Line numbers are as of the commit that added this entry and will drift; the symbol and
config-key names are the durable anchors.)*
- **The gap it is actually motivated by has a scoped fix already diagnosed.** #2650
carries a confirmed diagnosis and Agent Brief for the real gap — plan-phase's planner
and plan-checker have no equivalent mechanism — sized as mirroring the two executor
config keys into `planner.*` and reusing the existing checkpoint. That brief explicitly
scopes out generalizing beyond plan-phase and any `SubagentWatchState`-style
rearchitecture. Accepting #2699 would mean building the larger system on top of a
foundation that has not landed.
- **The event-driven model assumes an orchestrator GSD does not have.** The proposal
assumes a resident process with an event loop that can subscribe to hook callbacks and
maintain an in-memory `lastActivityAt`. GSD's orchestrator is an LLM interpreting
workflow markdown one turn at a time. Host-fired hooks run out-of-band and do not
return control to the orchestrating prompt mid-spawn. The only mechanism that yields a
turn in which a check can run is a backgrounded dispatch that returns control between
polls. This is a categorical mismatch, not a per-runtime coverage gap — it does not
resolve by picking a different host.
- **The heartbeat has no place to live.** Agents emitting JSON every 30–60 seconds
requires wrapper-level control over agent execution that no GSD-supported runtime
exposes. Absent a wrapper, the spawned agent would have to interleave heartbeat
emission with its actual work, competing for the same turn.
- **The specification is incomplete by the project's own bar.** The issue was not filed
through `feature_request.yml` and omits its required fields — type of addition, user
stories, maintenance burden, alternatives considered, breaking changes. `CONTRIBUTING.md`
states that incomplete specs are closed rather than revised by maintainers.
Note that the proposal's *premise* about runtime portability was sound and is not a
ground for rejection: `hookBus: host` and `stateIO: filesystem` are documented for the
large majority of supported runtimes in the host-integration capability matrix. The
blocker is the dispatch model, not signal availability.
## What this does NOT cover
This entry denies a **generalized, event-driven, heartbeat-augmented watchdog layer**.
Its keyword surface — watchdog, stall, timeout, heartbeat, hung agent, orphan, recovery —
overlaps request types this decision deliberately does not deny. Do not apply this entry to:
- **Extending the existing spot-check pattern to another spawn site.** Mirroring the
executor's config keys and checkpoint into plan-phase, code-review, or any other
workflow is the sanctioned incremental path. That is #2650's shape, and it is welcome.
- **Fixing the shipped executor mechanism.** If the executor's stall detection does not
fire when it should — and there is open evidence it may not, on a single
non-backgrounded spawn — that is a defect report, not this proposal.
- **Making a specific hang observable.** A report that one command hangs with no
diagnostic is a bug about that command.
- **Config or documentation for the existing `executor.stall_*` keys.**
- **A display-only progress or elapsed-time indicator.** Showing "waiting on planner,
last activity 4m ago" while a spawn is outstanding — with no state machine, no
classification, and no recovery action — is a UX improvement, not this proposal. It is
denied by nothing here.
- **Orphaned OS processes, containers, or bench resources.** Leaked test-runner containers
and similar host-level cleanup share the words *orphan* and *recovery* with this entry
but are a different domain entirely — nothing about GSD subagent dispatch. This entry
says nothing about them.
## Re-open criteria
This may be revisited if:
- **#2650's fix has shipped**, and the resubmission **names at least one specific residual
failure observed after it shipped** — a concrete case the shipped mechanism did not
catch — rather than arguing for the full five-state model in the abstract.
- The **dispatch-turn dependency is resolved first**: a demonstrated mechanism by which
the orchestrator reliably receives an execution turn during a single, non-backgrounded
spawn. Until that exists, any watchdog generalization inherits the same unreliability
the executor mechanism is already suspected of.
- The proposal is refiled through `feature_request.yml` with its required fields
completed.
A generalization is accepted here once the narrow pattern it generalizes is **in use at two
or more spawn sites** and a **named, reproduced failure** shows why per-site application is
no longer sufficient — not before.
## Related
- `gsd-core/workflows/execute-phase.md` — the shipped executor stall-detection pattern
- `src/config.cts` — `SCHEMA_DEFAULTS`, where `executor.stall_*` keys are registered
- `docs/reference/host-integration-capability-matrix.md` — per-runtime `hookBus` / `stateIO` surfaces
- [#2650](https://github.com/open-gsd/gsd-core/issues/2650) — the confirmed defect this was redirected to