feat(gateway): substantive-plan gate — approach >=150 + real sub_task descriptions (#171)

Plans were vague because the gate accepted the bare minimum: PM
approach >=20 chars and title-only sub_tasks. minimax wrote exactly
the minimum.

- IWillPlanRequest.approach min_length 20 -> 150 (kept in sync with
  _PM_APPROACH_MIN_LEN; the gate enforces it at the choreographer
  layer too so direct/MCP callers can't bypass the HTTP boundary).
- New _thin_subtask_hint: every PM sub_task must have a title and a
  description >= _PM_SUBTASK_DESC_MIN_LEN (60) saying what the step
  does — each sub_task is both a delegate target AND a
  progress-checklist item, so a title alone is not a plan.
- cell_pm/main_pm role prompts: explicit "the gate REJECTS thin plans"
  framing + concrete sub_task example + the new minimums.
- Updated all affected test fixtures across the suite to use
  substantive approaches/descriptions; added thin-sub_task rejection
  coverage.

Commit 1 of 3 for the plan/progress quality work (#171/#172/#173).
This commit is contained in:
Renn F
2026-05-16 10:14:45 +02:00
parent e94159dce8
commit ed828a719b
14 changed files with 415 additions and 82 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ You merge what your developers submit (leaf PRs into your cell branch via `compl
| Verb | What it does | Preconditions |
|---|---|---|
| `give_me_work()` | Returns your highest-priority task (your own pending PM task, or a subtask in `awaiting_pm_review` for you to merge). | None. |
| `i_will_plan(task_id, plan, approach?, technical_considerations?, risks?, open_questions?)` | Claim YOUR cell-PM task, record your plan, transition `pending` -> `in_progress`. Always call this before `delegate`. **Fill `approach` (2-4 sentences), `technical_considerations` (list of strings), `risks` (list of `{risk, mitigation}` dicts), `open_questions` (list of `{question, answered}` dicts).** Empty values produce an empty Plan tab — a regression. | Task assigned to you; task in `pending`/`needs_revision`. |
| `i_will_plan(task_id, plan, approach, sub_tasks, technical_considerations?, risks?, open_questions?)` | Claim YOUR cell-PM task, record your plan, transition `pending` -> `in_progress`. Always call this before `delegate`. **The gate REJECTS thin plans:** `approach` must be **≥150 chars** explaining HOW you decompose + route + sequence (not a one-liner); `sub_tasks` is a non-empty list of `{title, description}` where **every `description` is ≥60 chars saying what that step actually does** — each sub_task is both a `delegate` target AND a progress-checklist item, so it must be a real step. Also fill `technical_considerations`, `risks` (`{risk, mitigation}`), `open_questions` (`{question, answered}`). Example sub_task: `{"title": "Add timestamp comment to README", "description": "be-dev-1 edits README.md, prepends an HTML comment <!-- smoke-test: <date> --> above the H1, leaving the rest of the file untouched"}`. Empty/thin values are rejected, not just an empty Plan tab. | Task assigned to you; task in `pending`/`needs_revision`. |
| `delegate(parent_task_id, title, description, assigned_to, team, task_type, nature, acceptance_criteria, estimated_complexity)` | Create a subtask under your cell-PM task and assign it to a dev in your cell. `nature``technical`/`non_technical`. `task_type` for devs must be `code`/`documentation`/`research`. Gateway blocks duplicate sibling delegations (same assignee + same task_type under same parent). | Parent claimed by you and `in_progress`; assignee is a dev slug in your cell. |
| `triage()` | List what your cell needs next (blocked > awaiting_pm_review > pending). | None. |
| `unblock(task_id, restore=True)` | Resolve a dev's blocked subtask and return it to its pre-block state. | Subtask is in your cell. |
+1 -1
View File
@@ -20,7 +20,7 @@ You merge what your Cell PMs submit (cell PRs into your root branch via `complet
| Verb | What it does | Preconditions |
|---|---|---|
| `give_me_work()` | Returns your highest-priority task (your root in `pending`, or a cell-PM task in `awaiting_pm_review` for you to merge). | None. |
| `i_will_plan(task_id, plan, approach?, technical_considerations?, risks?, open_questions?)` | Claim YOUR root task, record your cell-distribution plan, transition `pending` -> `in_progress`. Always call this before `delegate`. **Fill `approach` (2-4 sentences describing your cell distribution), `technical_considerations` (list of strings), `risks` (list of `{risk, mitigation}` dicts), `open_questions` (list of `{question, answered}` dicts).** Empty values produce an empty Plan tab — a regression. | Task assigned to you; task in `pending`/`needs_revision`. |
| `i_will_plan(task_id, plan, approach, sub_tasks, technical_considerations?, risks?, open_questions?)` | Claim YOUR root task, record your cell-distribution plan, transition `pending` -> `in_progress`. Always call this before `delegate`. **The gate REJECTS thin plans:** `approach` must be **≥150 chars** describing HOW you split work across cells + sequencing + dependencies (not a one-liner); `sub_tasks` is a non-empty list of `{title, description}` where **every `description` is ≥60 chars** stating what that cell slice delivers — each sub_task is both a `delegate` target AND a progress-checklist item. Also fill `technical_considerations`, `risks` (`{risk, mitigation}`), `open_questions` (`{question, answered}`). Empty/thin values are rejected, not just an empty Plan tab. | Task assigned to you; task in `pending`/`needs_revision`. |
| `delegate(parent_task_id, title, description, assigned_to, team, task_type, nature, acceptance_criteria, estimated_complexity)` | Create a subtask under your root and assign it to a Cell PM (`be-pm`, `fe-pm`, `ux-pm`). One subtask per cell that needs work. **`task_type` must be `planning`** (Cell PMs decompose; they don't execute). `nature``technical`/`non_technical`. Gateway blocks duplicate sibling delegations (same Cell PM + same task_type under same parent). | Parent claimed by you and `in_progress`; assignee is a Cell PM slug. |
| `triage_all()` | List blockers and reviews across all cells. | None. |
| `unblock(task_id, restore=True)` | Resolve a cell-PM task's blocker and return it to its pre-block state. | None. |