The decomposition floor that pairs with the roll-up gate (spec 4): a PM
cannot finish decomposing a parent while one of its acceptance criteria has
no subtask responsible for it — the "two leaves, half the ACs silently
dropped" pattern. Three parts:
- Gate: i_am_idle is rejected for a cell_pm/main_pm whose owned parent still
has criteria in unclaimed_parent_acceptance_criteria (claimed = referenced
by any live, non-cancelled child). Distinct from the roll-up gate, which
fires at submit_up/complete and demands a *completed* child; this fires
earlier and asks only that every criterion be *claimed*. Safe-by-
construction: inert until a PM declares coverage, so legacy / not-yet-
adopted decompositions are never blocked.
- Visibility: PM-facing briefings (give_me_work, i_will_plan, submit_up) and
every delegate response now carry parent_ac_coverage ({id,text,claimed,
verified} per criterion) + unclaimed_parent_acs, so a PM can map subtasks
to criterion ids via covers_parent_criteria and see what is still
uncovered after each delegate. Off for leaf roles, so a developer's own
criteria never surface as bogus "unclaimed" noise.
- Prompts: cell_pm / main_pm role prompts document covers_parent_criteria and
the new idle enforcement in the existing Coverage discipline.
TaskService.{parent_ac_coverage,unclaimed_parent_acceptance_criteria} added
beside uncovered_parent_acceptance_criteria; all three refactored onto a
shared _parent_ac_ref_sets helper (keeps each under the xenon B ceiling,
preserves the committed roll-up behavior). Verb tables regenerated for the
new delegate param — the regen also syncs pre-existing table drift that was
never regenerated after earlier merges (read_messages, pass_review
ac_verdicts, board pitch). Two brand-new generated tables (prompter,
secretary) are left untracked pending a separate decision.
regenerate_verb_tables.py imported roboco.api.schemas.v2, which no longer
exists (schemas moved to v1), so it raised on import and the generated
verb/tool tables could never be refreshed — leaving _generated/verbs.md
and the per-role prompts stale (e.g. listing submit_for_qa, omitting the
notify_*/channels/progress/pr_update content tools). Repoint the imports
to v1, fix the renamed schema (OpenPrRequest), and regenerate.
Pre-fix, submit_for_qa opened a PR (side effect) and returned OK with
next='call i_am_done' — agents read the verb name, assumed they were
done with QA handoff, never called i_am_done, and PRs ended up
orphaned (PR #12 in the 2026-05-08 trace).
Two changes:
1. Rename submit_for_qa -> open_pr so the verb name matches the
semantic. The PR opens here; the actual QA handoff happens at
i_am_done. Renamed across:
- choreographer/_impl.py (method)
- mcp/flow_server.py (tool registration + _TOOLS dict)
- api/routes/v2/flow_dev.py (route + handler)
- api/schemas/v2/flow.py (OpenPrRequest)
- services/gateway/verb_gates.py (_STATE_VERBS)
- services/gateway/role_config.py (developer flow manifest)
- services/gateway/content_actions.py (commit-success next= hint)
- agent_sdk/server.py (post-tool guidance map)
- runtime/orchestrator.py (developer prompt)
- agents/prompts/{base,roles/developer,_generated/*}.md
- tests/unit/gateway/test_submit_for_qa.py -> test_open_pr.py
- tests/unit/api/routes/v2/test_flow_dev.py
- tests/unit/gateway/test_verb_gates.py
- tests/unit/api/test_correlation_id.py
- tests/unit/mcp_servers/test_flow_server.py
- tests/integration/test_full_lifecycle_real_db.py
2. New regression test (test_open_pr_does_not_create_pr_if_no_commits)
pins the atomic invariant: preconditions (assignee, commits,
no-prior-PR) must be checked BEFORE git.create_pr/push_branch run.
Any future re-ordering breaks the test.
Tests: 3128 passing (3127 + 1 new), 100% coverage, ruff clean.
Note: TaskService.submit_for_qa() (the v1-layer service method) is
INTENTIONALLY not renamed — it's a different layer used by the v1
routes. The rename here is only the gateway verb surface.