docs(rfc): one observation layer, one controller - #9339
Conversation
Two independently built systems in this repo answer the same question -- do not spend an agent turn on a subject that has not usefully changed -- and neither can be deleted in favour of the other. The structured monitors under monitoring/ own budgets, terminal handling and a typed decision layer with no GitHub knowledge in it. The irq kernel with the pr_watch cron owns named observations, severity, a coalescing window and a time-bounded re-alert, and it is the only one of the two that anything actually runs today. The RFC proposes merging them in one direction: keep one observation layer that emits named entries with severity, and one controller that decides and spends the turn. It records what #9073 already settled (both arming paths now share the directive transport, so only the observation layer and the wake decision remain unmerged), the level-triggered vs edge-triggered axis that separates the two, three gaps neither side covers today (rate-limit classification on the irq side, orphaned-watch reclamation, and batching reachable only from an out-of-session poller), and a dashboard surface that reports "no loop" while a structured monitor runs. Adds the index row and the sequence diagram. Status is draft: this is a proposal, nothing here is built.
Opus 4.8 Review — ✅ no blocking findingsReviewed Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
Design Review (Fable 5) — ✅ PASSDesign-level review of Design-Verdict: PASS A code-verified consolidation RFC with a clear delete list, ordered steps, and named open decisions — the right artifact for this problem; fully reversible. Suggestions
[DESIGN-REVIEWED] df35569 |
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
Problem
Two systems in this repo answer the same question -- do not spend an agent turn on an external subject that has not usefully changed -- and they were built independently.
The structured monitors under
src/kiro_crew/monitoring/(#5180, #5181, #5182, #5183, #5184 merged) own budgets, terminal handling, and a typed decision layer with zero GitHub references in it. The irq kernel with thepr_watchcron (#5273, #5886, #6071, #6279, #7431, #7634, #8122, #8326 merged) owns named observations with severity, a coalescing window, and a time-bounded re-alert.Neither can be deleted in favour of the other, and only one of them is actually running:
~/.kiro/crew/autonudge.jsonhas zero structured monitors ever armed, whilebabysit/SKILL.mdnamesmonitor_starttwenty times againstmonitor_watchonce.#9073 narrowed the problem without closing it. Both arming paths now share the directive transport completely, so what remains unmerged is exactly the observation layer and the wake decision.
Solution
Adds
docs/request-for-change/rfc-consolidated-monitor.md, statusdraft. It proposes one observation layer that emits named entries carryingkey,severityandresets_on_head, and one controller that decides and spends the turn.The merge is one-directional but not one-sided: the observation layer keeps irq's named entries and coalescing window, the controller keeps the structured side's budgets and terminal handling. The document records the axis that actually separates the two designs -- level-triggered per-key re-assertion against edge-triggered fingerprint change -- and why industry practice leans level.
It also records three gaps neither side covers today, each verified against the tree at
ef38f4cbe: rate limiting is classified on the structured side (ProviderErrorKind.RATE_LIMITED) and unclassified on the irq side, so one runaway watch degrades every other session sharing the credential; nothing reclaims an orphaned watch, which runs forever absent a terminal observation; and GraphQL batching is reachable only from the single out-of-session poller, so fifty session-bound monitors cost roughly a hundred and fiftyghcalls where one query would do.One finding is a dated regression rather than a design gap.
api_autonudge_getnulls a structured monitor out before the goal popover sees it, so the moment #5186 routes babysit throughmonitor_watch, the popover will report "no loop" while one is running. That fix is independent of the rest of the RFC and wants to land first.Adds the index row in
docs/request-for-change/README.mdwith a code-verified status column, and a sequence diagram underdocs/request-for-change/assets/.Testing
scripts/docs-lint.shpasses (exit 0, "All documentation checks passed"). The six line-number citations the first run flagged were rewritten as symbol names, per the rule that a name survives the refactor that moves the line.scripts/check_brand_name.pypasses on the new file. The image reference resolves to the committed asset; the document is pure ASCII, carries no local filesystem paths, and every pull request reference is a clickable link except in one heading, where a link would break the anchor.Documentation only. No source file is touched, so there is no behaviour to test.
Blocked features
None.
Other suggestions
Two things a reviewer should weigh in on.
The diagram is committed as a PNG, and no other RFC in this directory embeds an image -- five use ```mermaid blocks instead. The caption describes the diagram completely enough that converting it to mermaid source loses nothing and gains a diffable, non-binary form. Say the word and it becomes mermaid.
The RFC's step 1 touches
monitoring/models.pyandmonitoring/github_pull_request.py, which are the base of three open stacked pull requests (#5185, #5186, #5305). Landing step 1 first costs those a rebase; waiting blocks it. Sincemonitor_watchcarries no traffic today, the tradeoff is rebase cost, not production risk -- but it is a sequencing call for whoever owns that stack, not one this document should make unilaterally.Separately,
docs/system-specs/modules/babysit-pr-watch.mdsays there are "two current monitoring modes", which stops being true when #5186 lands. Not fixed here.