@@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
- **Per-role compute policy — model tier + reasoning effort.** Each role can carry both a model tier (`ROLE_MODEL_MAP`) and a per-role reasoning-effort level (`ROLE_EFFORT_MAP` → `CLAUDE_CODE_EFFORT_LEVEL` injected into the agent container). The effort map ships **empty (inert)** — populating a role is opt-in, only after verifying on one spawn that the level actually moves token usage (the CLI config key is version-dependent, and a wrong value is a silent no-op). Roles routed to Haiku ignore effort (Haiku has no effort control).
- **Spawn preflight (default-off, `ROBOCO_SPAWN_PREFLIGHT_ENABLED`).** A flag-gated guard that refuses to spawn a non-human delivery role absent from `GATEWAY_ENABLED_ROLES`: such a role gets no gateway manifest and can never claim its work, so the dispatcher would respawn it on the same task forever. Instead of burning the full system prompt on each futile retry, the spawn is refused (`AgentReadinessError`) and the overseer is alerted once. Inert in practice — every real delivery role is gateway-enabled — so it's a misconfiguration guardrail. Armed on the NAS composes, off in the published registry compose.
- **`GET /api/tasks/summary` — a trimmed task list for panel views.** The panel fetched `/api/tasks` unbounded and full-fat (~2MB per refresh measured live, ~21KB/task); the new route returns exactly the fields list views render (~50× lighter, including `completed_at` + `board_review_complete` for the CEO queue), and the eleven previously-unbounded task list routes (`/my`, `/pending`, `/blocked`, the awaiting-* queues, …) now take a capped `limit` param.
- **Notification-spawn cooldown — the missing loop-breaker on task-less dispatches.** The escalation / approval / audit / a2a dispatchers spawn agents from unacknowledged notifications with no task attached, so neither the readiness gate nor the PM respawn circuit breaker (both task-keyed) ever saw them: an unacked notification respawned its recipient on **every dispatcher tick**, unbounded. A cross-tick damper (`ROBOCO_NOTIFICATION_SPAWN_COOLDOWN_SECONDS`, default 600) now allows one spawn per (agent, notification) per window — the notification stays pending, so the next window retries if it is still unacknowledged; `0` restores the legacy every-tick behavior.
- **`request_changes` — the PM's merge-level reject at `awaiting_pm_review`.** A PM that caught a genuine AC/scope violation at merge review previously had no in-band way to send the work back (its only verbs there were `complete` and escalate), so it looped `i_am_blocked` → escalate → unblock → re-block — the live fe-pm loop from the S6 MegaTask run. The new PM verb (cell_pm + main_pm) transitions `awaiting_pm_review` → `needs_revision` with at least one concrete issue, appends the issues to the dev's notes, routes the revision like a QA fail (original developer for a leaf, the revision PM for an assembled task), and a2a-delivers the reject reason to the new owner so it is never stranded.
@@ -24,13 +25,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
- **LLM-facing payloads are capped, with explicit truncation markers.** Embedded diffs (QA `claim_review` evidence, the `evidence` verb, `roboco_git_diff`) cap at 20K chars with a pointer to the full diff (the PR / file-scoped diff — the panel's HTTP diff route stays uncapped); notification bodies, handoff journal content, and the company-charter north star clip to briefing-sized excerpts (full texts stay readable via `notify_get` and the panel); kb/error/learning search results, mentor sources, and RAG citations cap per-item content. Median payloads are untouched — these bound the tail (observed p90s of 34–37KB per single tool result).
- **The optimal MCP server is role-scoped, like flow/do already were.** Agents no longer carry all 20 optimal tool schemas (~3.7K tokens of every turn's context): error tools go to developer/QA, standards/review to the delivery+review roles, decisions to PMs/Board, doc indexing to the documenter; search/mentor/learnings stay universal. Destructive index management (`clear_index`, `reindex_all`) no longer registers for any agent — dev/test only via `ROBOCO_ALLOW_FULL_TOOLSET`. Unknown roles fail open to the full set.
- **Agent Bash output is explicitly capped** (`BASH_MAX_OUTPUT_LENGTH=20000` in the generated agent settings) so a giant gate/test dump can't flood the session context, and **the spawn-waste metric counts Anthropic sessions only** (`basis: anthropic_sessions`) — non-Anthropic transcripts don't reliably populate output tokens and were reporting phantom waste.
- **Panel: request flood and fat payloads killed.** Prefetch is disabled on every `Link` (hovering the sidebar no longer fires a page-data fetch per item), the task list and CEO approval queue consume the trimmed `/api/tasks/summary` route, the logo/icon images are slimmed (446KB → 7KB logo), and React Query DevTools loads in dev builds only.
- **Model-tier routing rebalanced for cost.** **QA** now routes to Haiku (mechanical gate work whose cost is cache-dominated) and **Main PM** moves off Opus to **Sonnet 5** — coordination cost is dominated by cache read/write, and Sonnet 5's cache-write is ~12× cheaper than Opus. The Main PM move is a watched experiment, revertible via `ROLE_MODEL_MAP` or a per-slug model override without a code change. (Builds on the earlier `sonnet`→Sonnet 5 alias adoption and the PR-reviewer→Opus tiering.)
### Fixed
- **Declared dependencies become real edges (MegaTask + delegate).** The live S6 out-of-order break, both halves. Batch intake: each draft's `depends_on` (the CEO's declared "Depends on" list, batch indices) is now wired verbatim into the sequencing DAG — `SequencingService.analyze` unions declared edges with the derived collision rules (self/out-of-range references rejected, cycles caught by the existing toposort); previously only analyzer-derived file-overlap edges were wired and a declared wave could be silently dropped. Delegate: a `code` subtask now REQUIRES a non-empty `intends_to_touch` collision surface (new `TASK_AT_DELEGATE` completeness spec) — a no-surface code sibling is "parallel to everything" by analyzer design, which is how two devs ran explicitly-sequenced work out of order on divergent branches. Non-code delegations and REST/manual creation are unchanged.
- **Respawn circuit breaker now guards every task-keyed spawn path.** The progress-aware breaker (strike counting with status-advance reset, tracing-gap budget, DB durability, one-shot CEO notification) was consulted by only 3 dispatch paths; doc/QA/dev/PR-review/PR-gate/revision/board spawns ran unguarded at fixed cadence — a documenter with no valid verb respawned 26× in ~100 min on one task. The gate is now consulted at all 14 task-keyed spawn sites.
- **Assembled-PR freshness + integrity at submit_up / submit_root.** Freshness: the assembled cell/root branch is auto-rebased onto its base when behind (children are terminal at submit time; master is never written); a rebase conflict is a clean rejection naming the files — ends the needs_revision ↔ awaiting_pr_review ping-pong of re-reviewing a stale head. Integrity: every completed child's commits must be patch-present (`git cherry`, rebase-safe) in the assembled branch before review — a completed revert whose merge was lost re-spawned the exact violation it fixed.
- **Declared dependencies become real edges (MegaTask + delegate).** The live S6 out-of-order break, both halves. Batch intake: each draft's `depends_on` (the CEO's declared "Depends on" list, batch indices) is now wired verbatim into the sequencing DAG — `SequencingService.analyze` unions declared edges with the derived collision rules (self/out-of-range references rejected, cycles caught by the existing toposort); previously only analyzer-derived file-overlap edges were wired and a declared wave could be silently dropped. Delegate: a `code` subtask now REQUIRES a non-empty `intends_to_touch` collision surface (new `TASK_AT_DELEGATE` completeness spec) — a no-surface code sibling is "parallel to everything" by analyzer design, which is how two devs ran explicitly-sequenced work out of order on divergent branches. Non-code delegations and REST/manual creation are unchanged. The MCP `delegate` tool now actually carries `intends_to_touch` / `adds_migration` / `touches_shared` / `depends_on` and forwards them to the gateway — the gate demanded a field the tool could not send, so every code delegation was rejected `incomplete_input` with no way to comply (live fleet-wide delegation wall); a parity test locks plan-gate fields to tool parameters.
- **Respawn circuit breaker now guards every task-keyed spawn path.** The progress-aware breaker (strike counting with status-advance reset, tracing-gap budget, DB durability, one-shot CEO notification) was consulted by only 3 dispatch paths; doc/QA/dev/PR-review/PR-gate/revision/board spawns ran unguarded at fixed cadence — a documenter with no valid verb respawned 26× in ~100 min on one task. The gate is now consulted at all 14 task-keyed spawn sites. It also catches status ping-pong: any status change used to fully reset the strike counter, so a `blocked` ↔ `in_progress` oscillation — which changes status on every spawn while advancing nothing — never tripped the gate (8 spawns over two hours, live). A never-seen status still fully resets; a REVISITED status gets a bounded reset budget (`pm_respawn_max_revisit_resets`, default 2), after which strikes accrue and the gate fires.
- **Assembled-PR freshness + integrity at submit_up / submit_root.** Freshness: the assembled cell/root branch is auto-rebased onto its base when behind (children are terminal at submit time; master is never written); a rebase conflict is a clean rejection naming the files — ends the needs_revision ↔ awaiting_pr_review ping-pong of re-reviewing a stale head. Integrity: every completed child's commits must be patch-present (`git cherry`, rebase-safe) in the assembled branch before review — a completed revert whose merge was lost re-spawned the exact violation it fixed. The guard now also recognizes **squash-merged** children: `git cherry` can't patch-match N child commits against the one squashed commit, but every commit carries the `[taskid8]` prefix, so a parent commit bearing the child's marker proves the child landed (three squash-merged children read as "work missing" and every legitimate `submit_up` was refused, live). Markerless children stay flagged — the original incident the guard exists for.
- **Review-evidence diffs no longer read a stale local ref.** An assembled branch advances on ORIGIN as child PRs squash-merge on GitHub, but the diff-head resolver preferred the inspecting clone's parked local ref — the PR-gate reviewer's evidence diff was built from a pre-merge snapshot and re-flagged work that had already landed (two false `pr_fail` verdicts on one cell PR, live). When both refs exist and the local ref is strictly behind origin, the diff now resolves to `origin/<branch>`; a local ref that is ahead (unpushed) or diverged keeps priority.
- **Team-match enforcement armed.** The spec-gate team check sat in its permissive fallback since shipping (no caller supplied the agent's team) and three PM verbs opted out entirely — a misrouted frontend cell PM blocked, escalated, and held a backend task through exactly that gap (live). Cell-scoped roles (developer, QA, documenter, cell PM) are now rejected `not_authorized` on cross-team verbs, `resume`/`unblock`/`activate` are team-matched, org-wide roles (Main PM, Board, CEO, PR reviewer) stay exempt so escalation handling and root-PR gating keep working, and the gateway threads the agent's team through all 27 lifecycle `Context` sites so the gate actually sees it.
- **Spawn manifest `workspace_path` follows the task's project.** The manifest hardcoded the agent's roboco-project workspace for every spawn, so an agent working another project's task was told one directory while its shell sat in the task worktree. The manifest now uses the same resolver as the container `-w` — both surfaces agree by construction.
- **Unassigned-QA dispatch no longer pre-claims.** The dispatcher claimed the `awaiting_qa` task *for* the QA agent before it existed, moving it to `claimed` — but the spawned agent's own `claim_review`/`pass_review` demand `awaiting_qa`, so it bounced twice and unclaimed. It now matches the assigned-QA and external-PR-reviewer dispatches: spawn without claiming, the agent claims itself via `claim_review`.
- **Documenter revision-pass dead end.** A task re-entering `awaiting_documentation` with docs already written left the documenter no move it recognized: `i_am_blocked`/`unclaim` are invalid there, and the generic rejection never named the exit. Both rejections now point at the actual exit (`i_documented` re-affirming the existing docs) and the documenter prompt gains an explicit revision-pass rule.
- **Dispatcher heartbeat.** The dispatch loop can die silently (a 4h25m fleet-wide outage left no log line or audit row — its stdout died with the container). A `dispatcher.alive` audit row every 5 minutes makes a dead loop detectable from the DB and distinguishable from "no work".
Nine live-run fixes, all merged to `master` the same day (commits `81f448bb`, `011158db`, `9d10217c`, `7dff3237`, `1cf24ff1`, `569a6157`, `c1acbd5b`, `298751e6`, `53bb0420`, `4de81d92`, `a260f903`, `caecb816`, `fe9e5589`, `8e5f84c4`):
1.**Delegate MCP tool carries the collision surface** (`81f448bb`, `7dff3237`) — `roboco/mcp/flow_server.py``delegate` gains `intends_to_touch` / `adds_migration` / `touches_shared` / `depends_on` and forwards them. The `TASK_AT_DELEGATE` gate (`roboco/foundation/policy/task_completeness.py`) required `intends_to_touch` on code delegations but the tool could not send it — fleet-wide `incomplete_input` delegation wall. Parity test locks plan-gate fields ⊆ tool params (`tests/unit/mcp/test_flow_server_delegate_surface_parity.py`).
2.**Assembly-integrity guard accepts squash-merged children** (`011158db`) — `roboco/services/git.py` (~L4035): when `git cherry` reports a child unmerged, a parent-branch commit carrying the child's `[taskid8]` prefix now proves it landed (squash = N patches → one commit, new patch-id). Markerless children stay flagged.
3.**Diff head prefers origin when local is behind** (`9d10217c`) — `roboco/services/git.py``_resolve_head_ref` (~L4359): both refs exist + local strictly behind (`merge-base --is-ancestor`) → resolve `origin/<branch>`; local-ahead/diverged keep priority. Kills the stale-evidence false `pr_fail` on assembled PRs that advanced on origin.
4.**`GET /api/tasks/summary` + bounded list routes** (`1cf24ff1`, `c1acbd5b`, `298751e6`) — `roboco/api/routes/tasks.py` wires the previously-dead `TaskSummaryResponse` (`roboco/api/schemas/tasks.py`, now + `completed_at` / `board_review_complete`) into a trimmed route (default limit 500, cap 1000); eleven unbounded task list routes gain `limit` params; the `/tasks` status-only branch honors its limit.
5.**Panel request-flood/fat-payload fixes** (`569a6157`, `298751e6`) — `prefetch={false}` on all Links (sidebar, cards, queues), task list + CEO queue consume `/api/tasks/summary` (`panel/src/lib/api/tasks.ts``TaskSummary`), logo/icon PNGs slimmed (446KB → 7KB), ReactQueryDevtools dev-only (`panel/src/components/providers.tsx`).
6.**Spawn manifest `workspace_path` follows the task's project** (`53bb0420`) — `roboco/runtime/orchestrator.py``_build_manifest_for_agent(workspace_path=)` + new `_resolve_workspace_cwd`: manifest and container `-w` share one resolver (was hardcoded to the roboco-project workspace for every spawn).
7.**Respawn breaker catches status ping-pong** (`4de81d92`) — `roboco/foundation/policy/agent_loop.py``pm_respawn_max_revisit_resets: int = 2` + orchestrator `_respawn_status_change_resets`: a never-seen status fully resets strikes; a REVISITED status gets a bounded reset budget (mirrors tracing_resets), after which strikes accrue. `seen_statuses` is in-memory only (rebuilds post-restart, can only under-gate).
8.**Unassigned-QA dispatch no longer pre-claims** (`a260f903`, `caecb816`) — orchestrator (~L10968): the transitioning pre-claim moved `awaiting_qa` → `claimed` before the agent existed, stranding `claim_review`/`pass_review` (both demand `awaiting_qa`). Now matches `_spawn_assigned_qa` / external-PR dispatch: spawn unclaimed, agent self-claims via `claim_review`.
9.**Team-match enforcement armed** (`fe9e5589`, `8e5f84c4`) — `roboco/foundation/policy/lifecycle.py`: `resume`/`unblock`/`activate` flip `needs_team_match=True`; new `_ORG_WIDE_ROLES` exemption (main_pm, CEO, PO, head_marketing, auditor, pr_reviewer) in `_check_team_match(…, role)`. `8e5f84c4` threads `agent_team` through all 27 gateway `Context` construction sites (`choreographer/_impl.py`, `doc.py`, `pr_gate.py`, `pr_review.py`, `qa.py`) so the gate actually receives the team — it had sat in its permissive fallback since shipping.
Slices touched: worksession-git (2, 3), choreographer/gateway-support (9), orchestrator (6, 7, 8), foundation-lifecycle (7, 9), mcp-servers (1), api-routes-schemas (4), panel (5). `docs/map/_complete_map.md` is the pre-delta concatenation — not regenerated.
@@ -160,7 +160,7 @@ You merge your own cell→root PR — the Main PM does **not** merge your cell b
### Sequencing dev-task collisions
When you `delegate` a dev subtask you may pass the collision surface so the sequencing DAG orders siblings that touch the same files:
When you `delegate` a dev subtask, declare the collision surface so the sequencing DAG orders siblings that touch the same files. For `task_type="code"` a non-empty `intends_to_touch` is **required** — the gate rejects a surfaceless code delegation with `incomplete_input` (a code subtask with no declared surface is treated as parallel to every sibling):
Siblings whose `intends_to_touch` globs overlap are serialized (more-important first); migration-adders chain serially; a shared-surface edit runs after each non-shared task it overlaps. Omit these and only the weak assignee-keyed spawn barrier orders your dev tasks (the 2026-06-27 out-of-order break).
Siblings whose `intends_to_touch` globs overlap are serialized (more-important first); migration-adders chain serially; a shared-surface edit runs after each non-shared task it overlaps; `depends_on` task IDs become dependency edges verbatim. Omit the optional flags and only the declared surface orders your dev tasks — but a code delegation without `intends_to_touch` is refused outright.
**Error**: "team 'X' may not act on a 'Y' task (team-based restriction)" (`not_authorized`)
**Cause**: You are a cell-scoped role (developer, QA, documenter, cell PM) and the task belongs to another team. Claim, resume, unblock, and activate are all team-matched.
**Solutions**:
- Follow the envelope's `remediate`: call `give_me_work()` to find a task in your own team
- If the task was misrouted to you, leave it — the pool re-routes it to the right team
Org-wide roles (Main PM, Board, CEO, PR reviewer) are exempt — they act across cells by design.
@@ -40,6 +40,8 @@ note(text="Verified AC #1 (429 on 101st req), #2 (TTL match), #3 "
There is no `roboco_task_claim / _start / _qa_pass / _qa_fail` and no `roboco_git_checkout`. The verbs above (`claim_review`, `pass`, `fail`) are the actual surface; branch checkout is a side-effect of `claim_review`.
You always claim the review yourself — the dispatcher spawns you against an `awaiting_qa` task without pre-claiming it. `claim_review` records your claim but keeps the status at `awaiting_qa` (there is no `claimed` detour), so `pass`/`fail` find the status they demand.
@@ -44,6 +44,7 @@ Exactly one active WorkSession exists per task at a time (enforced in the servic
## Claiming Rules
- **One at a time (workers only)**: Developers, QA, and documenters can't hold multiple in-progress tasks at once. A **blocked** task still counts as active — a blocked dev cannot `claim` a second task; unblock or `unclaim` first. **PM coordinators are exempt** — a Main / Cell PM plans and delegates many roots in parallel, so it may hold several at once; only a real upstream **sequence dependency** (an unfinished task it depends on) holds one of its roots back.
- **Team match**: cell-scoped roles (developer, QA, documenter, cell PM) are rejected `not_authorized` on another team's tasks — claim, resume, unblock, and activate are all team-matched. The `remediate` hint says it: call `give_me_work()` to find a task in your own team. Org-wide roles (Main PM, Board, CEO, PR reviewer) are exempt.
- **Self-review prevention**: QA cannot `claim_review` tasks they developed
- **Self-documentation prevention**: Documenter cannot claim tasks they developed
- **Branch requirement**: Branch auto-created on `i_will_work_on`
@@ -75,7 +76,7 @@ unclaim(task_id)
```
pending → claimed (Developer via i_will_work_on / PM)
needs_revision → claimed (Developer via i_will_work_on)
awaiting_qa → claimed (QA via claim_review)
awaiting_qa → awaiting_qa (QA via claim_review — claim recorded, status stays put so pass/fail match)
awaiting_documentation → claimed (Documenter via claim_doc_task)
awaiting_qa → claim_review (claims; status stays awaiting_qa) → pass/fail
↓
pass: awaiting_documentation
fail: needs_revision
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.