mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
feat(gateway): enable 2-devs-per-cell parallelism + split-before-claim sizing
- Intake / Main-PM / Cell-PM prompts: enumerate independently-shippable work units, inherit the breakdown down the chain, and dispatch independents in parallel (dependency order, never one-at-a-time). - Raise the code-spine concurrency cap from 1 to 2 per parent (one per cell developer) so both devs build in parallel; keep the same-assignee guard and the planning/documentation cap at 1, plus the cross-team planning exemption. - Split-before-claim: hard-block an egregiously-bundled code leaf at delegate time so the PM splits it before any dev claims it; nudge the moderate band in the delegate success envelope.
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
"""Split-before-claim: a code leaf must not bundle too many concerns.
|
||||
|
||||
A `code` subtask carrying more acceptance criteria than the hard cap bundles
|
||||
multiple independent concerns into one leaf — QA can't pass a partial and
|
||||
criteria get dropped. The gateway rejects it at delegate time so the PM splits
|
||||
the bundle before any dev can claim it. Moderate bundling is allowed but flagged
|
||||
in the success envelope (the nudge). `planning` briefs are exempt.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from roboco.services.gateway.choreographer._impl import Choreographer, DelegateInputs
|
||||
|
||||
|
||||
def _inputs(*, task_type: str, ac_count: int) -> DelegateInputs:
|
||||
return DelegateInputs(
|
||||
title="t",
|
||||
description="d",
|
||||
assigned_to="be-dev-1",
|
||||
team="backend",
|
||||
task_type=task_type,
|
||||
nature="technical",
|
||||
acceptance_criteria=[f"criterion {i}" for i in range(ac_count)],
|
||||
)
|
||||
|
||||
|
||||
def test_small_code_leaf_is_allowed() -> None:
|
||||
"""A focused code leaf (<= hard cap) passes the sizing guard."""
|
||||
assert (
|
||||
Choreographer._delegate_sizing_guard(_inputs(task_type="code", ac_count=4))
|
||||
is None
|
||||
)
|
||||
|
||||
|
||||
def test_code_leaf_at_hard_cap_is_allowed() -> None:
|
||||
"""Exactly at the hard cap is still allowed; only strictly-above is blocked."""
|
||||
cap = Choreographer._SIZING_HARD_AC_COUNT
|
||||
assert (
|
||||
Choreographer._delegate_sizing_guard(_inputs(task_type="code", ac_count=cap))
|
||||
is None
|
||||
)
|
||||
|
||||
|
||||
def test_egregiously_bundled_code_leaf_is_rejected() -> None:
|
||||
"""A code leaf above the hard cap is rejected with split guidance."""
|
||||
cap = Choreographer._SIZING_HARD_AC_COUNT
|
||||
env = Choreographer._delegate_sizing_guard(
|
||||
_inputs(task_type="code", ac_count=cap + 4)
|
||||
)
|
||||
assert env is not None
|
||||
body = env.as_dict()
|
||||
assert body["error"] == "invalid_state", body
|
||||
assert "Split this into smaller code subtasks" in (env.remediate or "")
|
||||
|
||||
|
||||
def test_planning_brief_is_exempt_from_sizing() -> None:
|
||||
"""planning subtasks (main_pm -> cell_pm) legitimately carry many criteria."""
|
||||
assert (
|
||||
Choreographer._delegate_sizing_guard(_inputs(task_type="planning", ac_count=20))
|
||||
is None
|
||||
)
|
||||
|
||||
|
||||
def test_no_nudge_below_threshold() -> None:
|
||||
assert Choreographer._sizing_hint(_inputs(task_type="code", ac_count=5)) is None
|
||||
|
||||
|
||||
def test_nudge_in_moderate_band() -> None:
|
||||
"""Above the nudge count and below the hard cap: allowed but flagged."""
|
||||
hint = Choreographer._sizing_hint(_inputs(task_type="code", ac_count=7))
|
||||
assert hint is not None
|
||||
assert "7 acceptance criteria" in hint
|
||||
assert "parallel" in hint
|
||||
|
||||
|
||||
def test_no_nudge_for_planning() -> None:
|
||||
assert (
|
||||
Choreographer._sizing_hint(_inputs(task_type="planning", ac_count=20)) is None
|
||||
)
|
||||
Reference in New Issue
Block a user