Skip to content

Commit a2361d0

Browse files
authored
Implementation Plan: T5-P6-A6-WP1 Eliminate the batch_dispatch Phantom (#4126)
## Summary Remove the unimplemented `batch_dispatch` ingredient from `implementation.yaml`, delete the corresponding BATCH DISPATCH MODE section from the fleet dispatcher prompt in `_prompts_kitchen.py` (inserting a tombstone comment), clean up the now-dead `max_issues_per_food_truck` parameter from the fleet prompt function, delete 6 batch_dispatch-asserting contract tests, and regenerate derived artifacts. The `implement-findings` recipe and campaign references are preserved — only the phantom ingredient and its prompt section are removed. ## Implementation Plan Plan file: `/home/talon/projects/autoskillit-runs/impl-20260627-053031-160994/.autoskillit/temp/make-plan/t5_p6_a6_wp1_batch_dispatch_phantom_plan_2026-06-27_054000.md` Closes #4055 🤖 Generated with [Claude Code](https://claude.com/claude-code) via AutoSkillit <!-- autoskillit:pipeline-signature steps=prepare_pr,run_arch_lenses,compose_pr,annotate_pr_diff,review_pr --> ## Token Usage Summary | Step | Model | count | uncached | output | cache_read | peak_ctx | turns | cache_write | time | |------|-------|-------|----------|--------|------------|----------|-------|-------------|------| | plan* | opus[1m] | 1 | 57 | 16.9k | 1.4M | 90.3k | 52 | 75.3k | 13m 29s | | verify* | sonnet | 1 | 1.2k | 8.9k | 383.7k | 54.2k | 26 | 35.7k | 5m 50s | | implement* | MiniMax-M3 | 1 | 112.0k | 19.9k | 7.1M | 0 | 142 | 0 | 7m 57s | | fix* | sonnet | 1 | 214 | 8.6k | 1.7M | 93.4k | 72 | 74.9k | 5m 18s | | audit_impl* | sonnet | 1 | 44 | 12.2k | 163.7k | 42.5k | 15 | 37.5k | 6m 24s | | prepare_pr* | MiniMax-M3 | 1 | 41.2k | 1.5k | 150.2k | 0 | 12 | 0 | 35s | | compose_pr* | MiniMax-M3 | 1 | 36.4k | 1.5k | 140.5k | 0 | 13 | 0 | 24s | | **Total** | | | 191.1k | 69.4k | 11.0M | 93.4k | | 223.4k | 40m 0s | \* *Step used a non-Anthropic provider; caching behavior may differ.* ## Token Efficiency | Step | LoC Changed | cache_read/LoC | cache_write/LoC | output/LoC | |------|-------------|----------------|-----------------|------------| | plan | 0 | — | — | — | | verify | 0 | — | — | — | | implement | 114 | 62466.2 | 0.0 | 174.7 | | fix | 2 | 826046.0 | 37431.5 | 4281.5 | | audit_impl | 0 | — | — | — | | prepare_pr | 0 | — | — | — | | compose_pr | 0 | — | — | — | | **Total** | **116** | 94906.2 | 1925.9 | 598.6 | ## Model Usage Breakdown | Model | steps | uncached | output | cache_read | cache_write | time | |-------|-------|----------|--------|------------|-------------|------| | opus[1m] | 1 | 57 | 16.9k | 1.4M | 75.3k | 13m 29s | | sonnet | 3 | 1.5k | 29.6k | 2.2M | 148.1k | 17m 34s | | MiniMax-M3 | 3 | 189.6k | 22.9k | 7.4M | 0 | 8m 57s |
1 parent 5cccccf commit a2361d0

7 files changed

Lines changed: 9 additions & 116 deletions

File tree

src/autoskillit/cli/_prompts_kitchen.py

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ def _build_open_kitchen_prompt(
9595
def _build_fleet_dispatch_prompt(
9696
mcp_prefix: str,
9797
recipe_table: str | None = None,
98-
max_issues_per_food_truck: int = 3,
9998
max_total_issues: int = 12,
10099
max_concurrent_dispatches: int = 3,
101100
has_unguarded_filesystem_access: bool = False,
@@ -265,38 +264,6 @@ def _build_fleet_dispatch_prompt(
265264
266265
BEM already caps parallel group sizes via max_parallel (default 6 issues per group).
267266
268-
## BATCH DISPATCH MODE — OPTIONAL
269-
270-
When dispatching the `implementation` recipe for multiple issues and the user has set the
271-
`batch_dispatch` ingredient to `"true"`, use batched dispatch instead of per-issue dispatch:
272-
273-
1. After BEM produces the dispatch_plan, for each parallel group (parallel: true):
274-
a. Chunk the group's issues into sub-groups of up to {max_issues_per_food_truck} issues.
275-
b. Dispatch each chunk as a SINGLE `implement-findings` food truck:
276-
{mcp_prefix}dispatch_food_truck(
277-
recipe="implement-findings",
278-
task="Implement issues #X, #Y, #Z — parallel-safe per BEM group {{N}}",
279-
ingredients={{{{
280-
"issue_urls": "<comma-separated issue URLs for this chunk>",
281-
"execution_map": "<captured execution_map from bem-wrapper>",
282-
"base_branch": "<target branch>",
283-
}}}},
284-
dispatch_name="implement-g{{N}}-{{letter}}",
285-
)
286-
c. Name chunks sequentially: `implement-g{{N}}-a`, `-b`, `-c` …
287-
d. Issue ALL chunks for a parallel group in a single response (parallel tool calls).
288-
The fleet semaphore gates actual concurrency — if FLEET_PARALLEL_REFUSED is returned,
289-
wait for a running dispatch to complete and retry.
290-
291-
2. Groups with only 1 issue or sequential groups (parallel: false): dispatch via the
292-
`implementation` recipe as normal — one food truck per issue, not batched.
293-
294-
3. When `batch_dispatch` is `"false"` (the default): use the standard per-issue dispatch
295-
from Step 4 above — one `implementation` food truck per issue. Do NOT use implement-findings.
296-
297-
4. The execution_map captured from bem-wrapper MUST be passed as an ingredient to each
298-
implement-findings food truck so BEM is not re-run inside the food truck.
299-
300267
## DISPATCHER DISCIPLINE
301268
302269
You are a fleet dispatcher — NOT an executor. ALL recipe execution must be delegated \

src/autoskillit/cli/fleet/_fleet_session.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ def _launch_fleet_session(
7171
prompt = _build_fleet_dispatch_prompt(
7272
mcp_prefix,
7373
recipe_table=recipe_table,
74-
max_issues_per_food_truck=cfg.fleet.max_issues_per_food_truck,
7574
max_total_issues=cfg.fleet.max_total_issues,
7675
max_concurrent_dispatches=cfg.fleet.max_concurrent_dispatches,
7776
has_unguarded_filesystem_access=_backend_caps.has_unguarded_filesystem_access,

src/autoskillit/recipes/diagrams/implementation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- autoskillit-recipe-hash: sha256:e5972fabca3a0bb471f4f99688ceaa78127056b33100574f05f355712fe50ffb -->
1+
<!-- autoskillit-recipe-hash: sha256:d3afca55385bea3028a28ec4354299c0fa8c03318976b921db7354eff380be57 -->
22
<!-- autoskillit-diagram-format: v7 -->
33
# implementation
44

src/autoskillit/recipes/implementation.json

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,6 @@
5555
"description": "Automatically merge the PR after required checks pass — via merge queue when available, direct merge otherwise",
5656
"default": "true"
5757
},
58-
"batch_dispatch": {
59-
"description": "Pack multiple parallel-safe issues per food truck. When \"true\", the fleet dispatcher batches parallel-safe issues (up to max_issues_per_food_truck) into implement-findings food trucks with comma-separated issue_urls.",
60-
"default": "false"
61-
},
6258
"review_max_retries": {
6359
"description": "Maximum number of review-resolve retry cycles before proceeding to CI",
6460
"default": "3"

src/autoskillit/recipes/implementation.yaml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,6 @@ ingredients:
4646
description: Automatically merge the PR after required checks pass — via merge
4747
queue when available, direct merge otherwise
4848
default: 'true'
49-
batch_dispatch:
50-
description: Pack multiple parallel-safe issues per food truck. When "true", the
51-
fleet dispatcher batches parallel-safe issues (up to max_issues_per_food_truck)
52-
into implement-findings food trucks with comma-separated issue_urls.
53-
default: 'false'
5449
review_max_retries:
5550
description: Maximum number of review-resolve retry cycles before proceeding to
5651
CI

tests/cli/test_fleet_session.py

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -809,33 +809,7 @@ async def test_build_fleet_campaign_prompt_accepts_prior_dispatch_id_parameter(
809809

810810

811811
class TestLaunchFleetSessionMaxIssuesPerFoodTruck:
812-
"""Contract: _launch_fleet_session threads max_issues_per_food_truck to prompt builders."""
813-
814-
def test_adhoc_dispatch_forwards_max_issues_per_food_truck(
815-
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
816-
) -> None:
817-
"""Ad-hoc threads cfg.fleet.max_issues_per_food_truck to _build_fleet_dispatch_prompt."""
818-
captured: dict = {}
819-
820-
def _fake_build(*args: object, **kwargs: object) -> str:
821-
captured.update(kwargs)
822-
return "fake-prompt"
823-
824-
monkeypatch.setattr("autoskillit.cli._prompts._build_fleet_dispatch_prompt", _fake_build)
825-
monkeypatch.setattr(
826-
"autoskillit.cli.session._session_launch._run_interactive_session",
827-
lambda *a, **kw: None,
828-
)
829-
830-
config_dir = tmp_path / ".autoskillit"
831-
config_dir.mkdir(exist_ok=True)
832-
(config_dir / "config.yaml").write_text("fleet:\n max_issues_per_food_truck: 7\n")
833-
monkeypatch.chdir(tmp_path)
834-
835-
from autoskillit.cli.fleet._fleet_session import _launch_fleet_session
836-
837-
_launch_fleet_session(None, None, None, None, fleet_mode="dispatch")
838-
assert captured.get("max_issues_per_food_truck") == 7
812+
"""Contract: max_issues_per_food_truck flows to campaign builder only, not dispatch builder."""
839813

840814
def test_campaign_dispatch_forwards_max_issues_per_food_truck(
841815
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
@@ -879,3 +853,10 @@ def _fake_build(
879853
fleet_mode="campaign",
880854
)
881855
assert captured.get("max_issues_per_food_truck") == 7
856+
857+
def test_dispatch_prompt_does_not_accept_max_issues_per_food_truck(self) -> None:
858+
"""_build_fleet_dispatch_prompt must not expose max_issues_per_food_truck parameter."""
859+
from autoskillit.cli._prompts_kitchen import _build_fleet_dispatch_prompt
860+
861+
sig = inspect.signature(_build_fleet_dispatch_prompt)
862+
assert "max_issues_per_food_truck" not in sig.parameters

tests/contracts/test_fleet_dispatch_bem_gate.py

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -98,51 +98,6 @@ def test_fleet_prompt_bem_gate_has_never_block(fleet_prompt: str) -> None:
9898
)
9999

100100

101-
def test_fleet_prompt_contains_batch_dispatch_section(fleet_prompt: str) -> None:
102-
"""Fleet dispatcher prompt must contain batch dispatch mode section."""
103-
assert "BATCH DISPATCH" in fleet_prompt
104-
105-
106-
def test_fleet_prompt_batch_section_references_implement_findings(fleet_prompt: str) -> None:
107-
"""Batch dispatch section must reference implement-findings recipe."""
108-
batch_section = fleet_prompt.split("BATCH DISPATCH")[1].split("## DISPATCHER")[0]
109-
assert "implement-findings" in batch_section
110-
111-
112-
def test_fleet_prompt_batch_section_references_execution_map(fleet_prompt: str) -> None:
113-
"""Batch dispatch section must instruct passing execution_map to implement-findings."""
114-
batch_section = fleet_prompt.split("BATCH DISPATCH")[1].split("## DISPATCHER")[0]
115-
assert "execution_map" in batch_section
116-
117-
118-
def test_fleet_prompt_batch_section_contains_max_issues_cap(fleet_prompt: str) -> None:
119-
"""Batch dispatch section must contain the max_issues_per_food_truck cap value."""
120-
batch_section = fleet_prompt.split("BATCH DISPATCH")[1].split("## DISPATCHER")[0]
121-
assert "3" in batch_section
122-
123-
124-
def test_fleet_prompt_batch_section_preserves_single_issue_fallback(fleet_prompt: str) -> None:
125-
"""Batch section must instruct fallback to implementation recipe for single-issue groups."""
126-
batch_section = fleet_prompt.split("BATCH DISPATCH")[1].split("## DISPATCHER")[0]
127-
lower = batch_section.lower()
128-
assert "single" in lower or "1 issue" in lower or "only 1" in lower
129-
130-
131-
def test_implementation_recipe_has_batch_dispatch_ingredient() -> None:
132-
"""implementation.yaml must expose a visible batch_dispatch ingredient."""
133-
from autoskillit.core.io import load_yaml
134-
from autoskillit.core.paths import pkg_root
135-
136-
recipe_yaml = load_yaml(pkg_root() / "recipes" / "implementation.yaml")
137-
ingredients = recipe_yaml["ingredients"]
138-
assert "batch_dispatch" in ingredients, (
139-
"implementation.yaml must have batch_dispatch ingredient"
140-
)
141-
bd = ingredients["batch_dispatch"]
142-
assert bd.get("default") == "false", "batch_dispatch must default to 'false'"
143-
assert bd.get("hidden") is not True, "batch_dispatch must be visible (not hidden)"
144-
145-
146101
def test_fleet_prompt_contains_gated_group_handling(fleet_prompt: str) -> None:
147102
assert "gated" in fleet_prompt.lower(), (
148103
"Fleet dispatcher prompt must instruct handling of gated groups "

0 commit comments

Comments
 (0)