Skip to content

fix(kanban): in-process child budget exhaustion no longer records against the parent task - #90025

Open
garadice wants to merge 1 commit into
NousResearch:mainfrom
garadice:fix/kanban-child-budget-exhaustion-misattribution
Open

garadice wants to merge 1 commit into
NousResearch:mainfrom
garadice:fix/kanban-child-budget-exhaustion-misattribution

Conversation

@garadice

Copy link
Copy Markdown

Summary

HERMES_KANBAN_TASK stays set in os.environ for in-process children running inside a dispatcher worker: delegate_task subagents, background-review forks, and cron agents fired via the cronjob tool. Those children carry their own much smaller iteration budgets (review-fork cap 16, delegation.max_iterations 50), so when a child exhausted ITS budget, _record_kanban_budget_exhausted recorded a terminal timed_out failure against the WORKER's task — advancing the dispatcher's consecutive_failures circuit breaker and archiving live parent work that was still running fine.

Observed in production: a verifier task archived after a (16/16) exhaustion event (the background-review fork's cap, not the task's 250) and a coder task archived after (50/50) (the delegation cap, not 300). Both parent tasks were healthy; only the child had run out of budget.

Fix

Gate both _record_kanban_budget_exhausted call sites in agent/turn_finalizer.py on a new _should_record_budget_exhaustion(agent) helper:

  • is_dispatcher_owned_worker_context() — the documented single predicate for HERMES_KANBAN_* identity gates (machinery from fix(cron): don't let a cron job inherit a kanban worker's dispatcher identity #79657). False for delegated children and non-dispatcher-owned cron execution.
  • not getattr(agent, "_persist_disabled", False) — the background-review fork's persistence-isolation marker (same idiom as the micro-compact guard in this file).

Zero behavior change for genuine dispatcher-owned workers: a real worker exhausting its real budget still records timed_out exactly as before (verified live: ContextVar token reset restores parent ownership after child scopes exit).

Relationship to other PRs

Test plan

  • New regression test test_budget_exhaustion_recorded_only_for_dispatcher_owned_worker — 5-case matrix: dispatcher-owned fires; delegated child skips; review fork skips; non-dispatcher-owned cron skips; guard truth table
  • Mutation-checked: removing the ownership leg, the _persist_disabled leg, or fully reverting the guard each makes the test fail — both legs independently covered
  • All 9 pre-existing tests in test_turn_finalizer_iteration_limit_exit.py pass unchanged (10/10 file total)
  • Full tests/agent/ suite: failure set byte-identical before/after (zero collateral)

@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cron Cron scheduler and job management tool/delegate Subagent delegation P3 Low — cosmetic, nice to have sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Aug 19, 2026
@Enough1122

Copy link
Copy Markdown

AI code review — automated review; please use your judgment.

The fix targets a genuinely harmful misattribution: an in-process child's small iteration cap (16/16, 50/50) was advancing the dispatcher worker's failure breaker and archiving healthy parent tasks, and the five-case matrix exercises all four contexts (worker fires, delegate child skips, _persist_disabled fork skips, non-dispatcher cron skips) plus a unit truth table — with real incident IDs cited as motivation.

  1. agent/turn_finalizer.py:~108–128, ~230–236, ~246–249 — three comments read "LOCAL PATCH 14 …", narrating deployment patch provenance rather than describing behavior for upstream reviewers — why it matters: same issue as fix(gateway): insert dedup repeat counter inside the code fence #90578's "local patch" hunks; it obscures which repo state this applies to and reads as though a private fork leaked into the PR — suggestion: reword to state the rule ("budget-exhaustion recording is dispatcher-owned only") and drop the patch numbering.

  2. Nit (:~120–124): gating on getattr(agent, "_persist_disabled", False) couples the decision to a persistence flag set by background_review.py; if that attribute is renamed or its meaning broadens, review forks silently start counting against parents again — suggestion: introduce an explicit marker (e.g. agent._budget_attribution_exempt = True) owned by the fork setup, or at least reference the setting site in the docstring so rename tooling can follow it.

— reviewer-a · automated agent review (Hermes week-review)

@garadice
garadice force-pushed the fix/kanban-child-budget-exhaustion-misattribution branch from 965ab84 to b256342 Compare August 25, 2026 11:52
@garadice

Copy link
Copy Markdown
Author

Reworded the four comments flagged in review: they now state the rule itself (budget-exhaustion recording is dispatcher-owned only) instead of 'LOCAL PATCH 14' provenance numbering, so the diff reads as applying to this repo state. Tests unchanged semantically, 10/10 still green.

@garadice

garadice commented Sep 4, 2026

Copy link
Copy Markdown
Author

Rebased onto current main (the Sep 2026 decomposition consolidated the two former call sites into one in finalize_turn, so the gate now lives on that single site — same _should_record_budget_exhaustion predicate, same regression matrix test). Finalizer + delegation test suites green (36 passed).

…inst the parent task

HERMES_KANBAN_TASK stays set in os.environ for in-process children
(delegate_task subagents, background-review forks, cron jobs fired via
the cronjob tool) running inside a dispatcher worker. Those children
carry their own — much smaller — iteration budgets, so a child running
out of iterations was recording a terminal timed_out failure against
the worker's task, advancing the dispatcher's consecutive-failure
circuit breaker and archiving live work that was still running fine at
the parent level.

Gate the budget-exhaustion recording on the dispatcher-ownership
predicate (is_dispatcher_owned_worker_context) plus the review fork's
persistence-isolation marker (_persist_disabled): only the
dispatcher-owned worker agent records.

Rebased onto the Sep 2026 decomposition: the two former call sites are
now one in finalize_turn.
@garadice
garadice force-pushed the fix/kanban-child-budget-exhaustion-misattribution branch from 3a2cbe8 to d935450 Compare September 24, 2026 09:20

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cron Cron scheduler and job management P3 Low — cosmetic, nice to have sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades tool/delegate Subagent delegation type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants