docs(release): 0.27.0 prep — changelog through Wave C, map + rag + CLAUDE.md current (#697)

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
This commit is contained in:
Renzo F
2026-07-24 22:23:20 +02:00
committed by GitHub
co-authored by Renn F
parent 8f01446243
commit 2a9339225c
19 changed files with 170 additions and 59 deletions
+7 -2
View File
@@ -109,9 +109,11 @@ The AgentOrchestrator is the runtime brain of RoboCo: it owns the per-agent Dock
| AgentOrchestrator._notification_spawn_cooled | method | roboco/runtime/orchestrator.py:4738 | Cross-tick cooldown for notification-triggered spawns (escalation/approval/audit/a2a): one spawn per (agent, notification) per `notification_spawn_cooldown_seconds`; `0` disables (legacy every-tick respawn). |
| AgentOrchestrator._notification_spawn_over_cap | method | roboco/runtime/orchestrator.py:4780 | Hard per-(agent, notification) attempt cap (`notification_spawn_max_attempts`, default 5) — the notification-driven analogue of the PM respawn breaker; these dispatchers carry no `task_id` so that breaker never sees them. Past the cap, stop respawning and log once; the count is id-scoped and survives map pruning. |
| AgentOrchestrator._prune_notification_spawn_maps | method | roboco/runtime/orchestrator.py:4811 | Evict `_notification_spawn_at` entries older than a cutoff so the in-memory cooldown map doesn't grow unbounded. |
| AgentOrchestrator._notification_has_live_work | coroutine | roboco/runtime/orchestrator.py:4848 | Live-work check before spawning a notification's recipient: skip when the notification has expired, is stale past `notification_spawn_max_age_seconds` (default 6h — wedged or reloaded from before a restart), or its related task is already terminal. |
| AgentOrchestrator._notification_has_live_work | coroutine | roboco/runtime/orchestrator.py:4848 | Live-work check before spawning a notification's recipient: skip when the notification has expired, is stale past `notification_spawn_max_age_seconds` (default 6h — wedged or reloaded from before a restart), its related task is already terminal, **or (since #685) the related task is HITL-blocked** (`_is_hitl_blocked`) — closes the admin-route escalation side of the oscillation loop the same way the dev/PM dispatchers' own HITL skip does. |
| AgentOrchestrator._is_hitl_blocked | staticmethod | roboco/runtime/orchestrator.py:13796 | `status == "blocked" and blocker_resolver_type == "human"` over the WIRE task dict; consulted by `_dev_dispatch_one`, `_dispatch_claimed_without_agent`, and `_notification_has_live_work`. Only actually fires over the wire since #685 stamped `TaskResponse.blocker_resolver_type` — before that the field never serialized, so every wire-dict HITL check silently no-op'd regardless of the in-process row. Budget-breach (#654) and the task-scoped oscillation breaker (#685) both set this same resolver+status pair, so one skip covers both trip conditions. |
| AgentOrchestrator._dispatch_claimed_without_agent | coroutine | roboco/runtime/orchestrator.py:14559 | Re-dispatches a `claimed` task whose owning agent isn't running; the RESTORED side of an escalate/unblock round trip (no per-(agent,task) respawn counter of its own) — gated by `_is_hitl_blocked` (#685) so an oscillation-tripped or budget-blocked task stops being re-dispatched here too. |
| AgentOrchestrator._fetch_budget_status | coroutine | roboco/runtime/orchestrator.py:8021 | `ROBOCO_TASK_BUDGETS_ENABLED` sweep helper: live-priced spend snapshot for one active task (closed sessions' `estimated_cost_usd` + open sessions priced from token snapshots). |
| AgentOrchestrator._task_budget_breach | coroutine | roboco/runtime/orchestrator.py:8053 | `(spend, cap)` tuple when a task's own spend exceeds its `budget_usd` (falling back to the `TaskType` default via `effective_task_budget_usd`), else `None`. |
| AgentOrchestrator._task_budget_breach | coroutine | roboco/runtime/orchestrator.py:8053 | `(spend, cap)` tuple when a task's own spend exceeds its `budget_usd` via `effective_task_budget_usd`, else `None`. **Explicit-input only since #666**: a null `budget_usd` resolves to `None` (no cap) — the earlier per-`TaskType` default table (which blocked an unbudgeted coordination root one opus planning turn in) was removed. |
| AgentOrchestrator._handle_task_budget_breach | coroutine | roboco/runtime/orchestrator.py:8090 | BLOCKs a breaching task (HUMAN resolver, a budget marker) BEFORE the graceful stop so the ensuing unclaim no-ops and the dispatcher never respawns onto it; notifies the CEO naming both recovery steps (raise the cap or `unblock`). |
| AgentOrchestrator._sweep_budget_exceeded | coroutine | roboco/runtime/orchestrator.py:8152 | Iterates active agents past `_check_budget_for_agent`; gated entirely by `ROBOCO_TASK_BUDGETS_ENABLED` — off, neither cap is ever consulted. |
| AgentOrchestrator._check_budget_for_agent | coroutine | roboco/runtime/orchestrator.py:8187 | Per-agent budget check called from the sweep; resolves the active task, checks breach, and calls `_handle_task_budget_breach`. |
@@ -269,6 +271,9 @@ stateDiagram-v2
> - `3b9fd0e0` (PR #551, Telegram V2, 2026-07-17): adds `_telegram_poll_task` + `_telegram_poll_loop`/`_run_telegram_poll_cycle` (mirrors the `_x_mentions_poll_loop` shape: gated on BOTH `telegram_enabled` AND `telegram_inbound_enabled`, started in `start()`, cancelled in `stop()`) driving the new `TelegramInboundEngine` (`roboco/services/telegram_inbound.py`) — long-polls Telegram `getUpdates` and dispatches `/status` `/queue` `/task` commands plus Approve/Reject button callbacks to the SAME CEO-gated service methods the HTTP routes call.
> - `b91229f4` (#643, "break the notification-driven respawn loop"): adds `_notification_spawn_cooled` (line 4738), `_notification_spawn_over_cap` (line 4780), `_prune_notification_spawn_maps` (line 4811), and `_notification_has_live_work` (line 4848) — both new guards gate AFTER the existing cross-tick cooldown. `_instances.__init__`/`__new__` gain `_notification_spawn_at: dict[tuple[str, str], float]` (existing cooldown map) plus a companion hard-cap counter; every notification-triggered spawn dispatcher (escalation/approval/audit/a2a) is threaded through both new checks.
> - `7c8453e2` (#654, "per-task and per-project cost budgets"): adds `_fetch_budget_status`/`_task_budget_breach`/`_handle_task_budget_breach`/`_sweep_budget_exceeded`/`_check_budget_for_agent` (lines 8021-8214) gated by `ROBOCO_TASK_BUDGETS_ENABLED`. The claim-time project-month-spend guard (`project_budget_exceeded_guard`) lives in `docs/map/gateway-support.md`; this is the sweep-side task-budget half. The same PR bundles `1d5a8e84` (#652's re-escalation backoff, see `docs/map/notification.md`) as a prerequisite commit in its branch history.
> - `a036c979` (#666, "cockpit data correctness", 2026-07-23) `effective_task_budget_usd` drops its per-`TaskType` default fallback — a budget-less task is now genuinely uncapped (`None`), not silently capped at a `TaskType`-keyed dollar figure that blocked an unbudgeted coordination root one opus planning turn in. `_task_budget_breach`'s description above reflects this; the `TaskType` default table + its resolver are deleted outright.
> - `eb0dcb6e` (#685, "task-scoped oscillation breaker", 2026-07-24) A escalate_up→BLOCKED→unblock→respawn→escalate_up round trip had no fuel limit: the RESTORED side dispatches via `_dispatch_claimed_without_agent`, which consulted no respawn counter at all. `unblock()` now counts strikes task-scoped (durable in `orchestration_markers`, no migration) via a cheap progress fingerprint (commit count + `revision_count` + `terminal_children_count`); past threshold the task is BLOCKED with `blocker_resolver_type=HUMAN` (the same posture budget-breach uses) and the CEO is notified naming both agents + the cycle count. `TaskResponse.blocker_resolver_type` now actually serializes over the wire, so `_is_hitl_blocked`'s skip (already used by `_dev_dispatch_one`) now really fires for `_dispatch_claimed_without_agent` and `_notification_has_live_work` too — previously the wire dict never carried the field, so those two HITL checks silently no-op'd.
> - `987eb09c` (#692, "role-aware worktree refresh at every spawn", 2026-07-24) `_ensure_worktree_before_spawn` now passes `can_author` (from the spawning agent's role, `foundation.identity.WORKTREE_AUTHOR_ROLES`) into `ensure_worktree_self_heal`, which refreshes an ALREADY-PRESENT per-task worktree against origin at every respawn instead of treating it as a pure venv+chown no-op — see `docs/map/workspace.md` for the full classification.
## Regression Risks