feat(auto-agents): retire implementation-worker wrapper (R2 wrapper-chain tax)
The depth-0 implementation-worker agent was a pure passthrough whose only LLM-side responsibility was translating the dispatcher-built prompt into tier-dispatcher's input format (top-level params + ``task_prompt:`` fenced body). Deterministic Python now does the same translation in microseconds, removing one full LLM hop (and its summarization-risk warning prose) from every implementer cycle. What changed: 1. **New ``_wrap_for_tier_dispatcher`` helper** in ``tools/dispatch_implementer.py`` — emits the exact format documented in ``.opencode/agents/tier-dispatcher.md``: outer-level ``task_agent: \`task-implementor\``` + ``estimator_agent: \`estimator-implementation\``` + optional ``escalation_tier_hint`` line, then the body verbatim inside a fenced ``task_prompt:`` block. 2. **``_implementation_prompt_dispatch`` refactored** to compute the escalation_tier_hint upfront (start_tier vs estimator-flag logic preserved exactly) and wrap+return once. The three prior return paths (early-return / explicit-hint / final-extras) all funnel through the same wrap helper. 3. **WORK_GROUP defs swapped**: three ``worker_agent="implementation- worker"`` → ``"tier-dispatcher"`` so the dispatcher invokes tier-dispatcher directly as the top-level OpenCode session. 4. **``release_claim_on_exit`` removed** from the emitted prompt. It was an implementation-worker-only directive telling the wrapper to skip its own session-end release; tier-dispatcher has never read it. The dispatcher's ``finally`` block has always owned the actual claim lifecycle and is unchanged. 5. **Test updates**: three test files updated to match the new contract — assert absence of ``release_claim_on_exit`` and ``worker_agent == "tier-dispatcher"``. All other tests pass without modification (the wrapper's .md file remains as historical doc; can be deleted in a follow-up cleanup). Audit confirmed no permission boundary lost (both agents are ``mode: all`` with equivalent bash/write allowlists), no agent- name conditionals in ``_opencode_worker``, no archive-filename grep filtering, tier-dispatcher input format is a 1:1 superset of what the wrapper emits. Live-cycle savings: removes the wrapper's ~1.5–3K tokens of reasoning + tool overhead, removes one full session-creation + session-archive cycle, removes the "verbatim forwarding" summarization risk (deterministic Python can't lose sections). Chain shrinks from 4 levels (impl-worker → tier-dispatcher → estimator/tier-N → task-implementor) to 3 (tier-dispatcher → estimator/tier-N → task-implementor). Full auto_agents suite: 2056 passing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1425,9 +1425,17 @@ class TestEstimatorEnabledFlag:
|
||||
prompt = driver._implementation_prompt_dispatch(cfg, item, group)
|
||||
|
||||
assert self._hint_line(prompt) == "escalation_tier_hint: `0`"
|
||||
assert "release_claim_on_exit: false" in prompt, (
|
||||
"escalation-enabled prompt must still tell the worker to "
|
||||
"skip its session-end claim release"
|
||||
# R2 (2026-05-16): with the implementation-worker wrapper
|
||||
# retired, the dispatcher emits the tier-dispatcher input
|
||||
# format directly. ``release_claim_on_exit`` was a wrapper-
|
||||
# only directive (telling the wrapper to skip its own
|
||||
# session-end claim release) — it has no role in
|
||||
# tier-dispatcher's contract, so the dispatcher OMITS it.
|
||||
# The actual claim release stays in the dispatcher's
|
||||
# ``finally`` block, unchanged.
|
||||
assert "release_claim_on_exit" not in prompt, (
|
||||
"release_claim_on_exit is a retired wrapper directive; "
|
||||
"tier-dispatcher's input format does not include it"
|
||||
)
|
||||
|
||||
def test_escalation_on_estimator_on_first_attempt_omits_hint(
|
||||
@@ -1435,8 +1443,7 @@ class TestEstimatorEnabledFlag:
|
||||
):
|
||||
"""Estimator ON + escalation ON + no resumption label = true
|
||||
first attempt: the dispatcher OMITS the hint so the estimator
|
||||
runs. ``release_claim_on_exit: false`` is still emitted (the
|
||||
dispatcher holds the claim across tier attempts)."""
|
||||
runs."""
|
||||
monkeypatch.setenv("IMPLEMENTER_ESCALATION_ENABLED", "1")
|
||||
monkeypatch.setenv("IMPLEMENTER_ESTIMATOR_ENABLED", "1")
|
||||
monkeypatch.setenv("IMPLEMENTER_DISPATCHER_PREFETCH", "0")
|
||||
@@ -1456,7 +1463,10 @@ class TestEstimatorEnabledFlag:
|
||||
"true-first-attempt with estimator ON must omit the hint; "
|
||||
f"got {self._hint_line(prompt)!r}"
|
||||
)
|
||||
assert "release_claim_on_exit: false" in prompt
|
||||
# R2: ``release_claim_on_exit`` no longer emitted
|
||||
# (implementation-worker wrapper retired; see comment in
|
||||
# ``test_escalation_on_estimator_off_first_attempt_emits_hint_zero``).
|
||||
assert "release_claim_on_exit" not in prompt
|
||||
|
||||
def test_resumption_label_emits_hint_regardless_of_estimator_flag(
|
||||
self, driver, cfg, monkeypatch
|
||||
|
||||
@@ -973,7 +973,10 @@ def test_implementer_issue_group_does_not_claim(runtime):
|
||||
|
||||
assert issue_group.item_kind == "issue"
|
||||
assert issue_group.claim_kind is None
|
||||
assert issue_group.worker_agent == "implementation-worker"
|
||||
# R2 (2026-05-16): the implementation-worker wrapper is retired
|
||||
# and the dispatcher invokes tier-dispatcher directly. Saves one
|
||||
# full LLM hop per cycle.
|
||||
assert issue_group.worker_agent == "tier-dispatcher"
|
||||
|
||||
|
||||
def _make_loop_cfg(runtime, tmp_path, *, budget):
|
||||
|
||||
@@ -2372,7 +2372,13 @@ class TestClaimReleaseDirective:
|
||||
|
||||
prompt = driver._implementation_prompt_dispatch(cfg, item, group)
|
||||
|
||||
assert "release_claim_on_exit: false" in prompt
|
||||
# R2 (2026-05-16): the implementation-worker wrapper was
|
||||
# retired; its ``release_claim_on_exit`` directive is no
|
||||
# longer emitted (tier-dispatcher has never read that flag,
|
||||
# and the dispatcher's own ``finally`` block has always owned
|
||||
# the actual claim release). Asserting absence keeps the
|
||||
# contract pinned now that the directive is dead.
|
||||
assert "release_claim_on_exit" not in prompt
|
||||
|
||||
def test_flag_off_pr_prompt_does_not_carry_directive(
|
||||
self, driver, cfg, monkeypatch
|
||||
|
||||
@@ -1657,6 +1657,45 @@ def _maybe_short_circuit(
|
||||
}
|
||||
|
||||
|
||||
def _wrap_for_tier_dispatcher(
|
||||
body: str, *, escalation_tier_hint: int | None = None,
|
||||
) -> str:
|
||||
"""Wrap a dispatcher-built body for ``tier-dispatcher``'s
|
||||
expected input format.
|
||||
|
||||
Replaces the ``implementation-worker`` wrapper which previously
|
||||
did this translation as a 1.5–3K-token LLM hop (depth-0 of the
|
||||
chain). Deterministic Python produces byte-identical output for
|
||||
every PR and removes one full agent round-trip from each cycle.
|
||||
|
||||
Format documented in ``.opencode/agents/tier-dispatcher.md`` —
|
||||
outer-level ``task_agent`` / ``estimator_agent`` parameter
|
||||
lines, optional ``escalation_tier_hint``, and the body wrapped
|
||||
in a fenced ``task_prompt:`` block. The body MAY itself contain
|
||||
credential lines and ``## Pre-fetched …`` sections; both are
|
||||
forwarded verbatim. tier-dispatcher reads the outer parameters
|
||||
and then embeds ``task_prompt`` into the estimator + tier-N
|
||||
calls verbatim per its documented contract.
|
||||
|
||||
Note ``release_claim_on_exit`` is intentionally OMITTED: that
|
||||
was an implementation-worker directive telling the wrapper to
|
||||
skip its own claim-release at session end. The dispatcher's
|
||||
own ``finally`` block has always owned the actual claim
|
||||
lifecycle; the directive becomes dead prose once the wrapper
|
||||
is gone.
|
||||
"""
|
||||
outer_lines = [
|
||||
"task_agent: `task-implementor`",
|
||||
"estimator_agent: `estimator-implementation`",
|
||||
]
|
||||
if escalation_tier_hint is not None:
|
||||
outer_lines.append(
|
||||
f"escalation_tier_hint: `{int(escalation_tier_hint)}`"
|
||||
)
|
||||
outer = "\n".join(outer_lines)
|
||||
return f"{outer}\n\ntask_prompt:\n```\n{body}\n```\n"
|
||||
|
||||
|
||||
def _implementation_prompt_dispatch(
|
||||
cfg: Any, item: dict[str, Any], group: Any
|
||||
) -> str:
|
||||
@@ -1690,16 +1729,14 @@ def _implementation_prompt_dispatch(
|
||||
# the worker should fall through to ``estimator-implementation``
|
||||
# on the first attempt (G11 harvest 2026-05-15).
|
||||
if not _is_escalation_enabled() or not is_pr_shape:
|
||||
if estimator_enabled:
|
||||
# OMIT the hint so ``tier-dispatcher`` runs the estimator
|
||||
# and chooses an adaptive tier.
|
||||
return base_prompt
|
||||
# Default OFF: emit an explicit hint=0 so the worker passes
|
||||
# it through and ``tier-dispatcher`` short-circuits to
|
||||
# ``tier-0``. Byte-equivalent to today's flag-OFF prompt
|
||||
# after the worker's no-hint→default-0 fallback is removed
|
||||
# (see ``implementation-worker.md`` G11 edit).
|
||||
return f"{base_prompt}\n\nescalation_tier_hint: `0`\n"
|
||||
# Estimator ON → OMIT the hint so ``tier-dispatcher`` runs
|
||||
# the estimator and chooses an adaptive tier. Estimator OFF
|
||||
# → emit hint=0 so ``tier-dispatcher`` short-circuits to
|
||||
# ``tier-0`` directly.
|
||||
hint = None if estimator_enabled else 0
|
||||
return _wrap_for_tier_dispatcher(
|
||||
base_prompt, escalation_tier_hint=hint,
|
||||
)
|
||||
|
||||
pr_number = int(item.get("number") or 0)
|
||||
start_tier = _read_start_tier_from_labels(cfg, pr_number)
|
||||
@@ -1716,28 +1753,26 @@ def _implementation_prompt_dispatch(
|
||||
item["_dispatcher_implementer_context"] = context
|
||||
context["start_tier"] = start_tier
|
||||
|
||||
# Tell the worker to skip its session-end claim release.
|
||||
# When escalation is on, the dispatcher holds the claim across
|
||||
# all tier attempts in this cycle and the dispatch_one finally
|
||||
# block does the actual release. Without this directive, the
|
||||
# worker's release between tiers leaves a 1–3 s window where
|
||||
# another driver could grab the PR. See plan #5.
|
||||
extras = ["release_claim_on_exit: false"]
|
||||
# Resolve the escalation_tier_hint that tier-dispatcher consumes:
|
||||
# - start_tier > 0 → label-driven cross-cycle resumption hint
|
||||
# (the auto/last-attempt-tier-N label names the next tier
|
||||
# explicitly; the estimator cannot override).
|
||||
# - start_tier == 0 AND estimator OFF → emit hint=0 so
|
||||
# tier-dispatcher short-circuits to tier-0 (legacy default).
|
||||
# - start_tier == 0 AND estimator ON → OMIT the hint so
|
||||
# tier-dispatcher falls through to estimator-implementation.
|
||||
#
|
||||
# ``release_claim_on_exit`` is no longer emitted (R2, 2026-05-16
|
||||
# implementation-worker retirement): the dispatcher's outer
|
||||
# ``finally`` block has always owned the actual claim lifecycle;
|
||||
# the directive was a wrapper-only flag telling the (now-retired)
|
||||
# wrapper to skip its own session-end release.
|
||||
if start_tier > 0:
|
||||
# Cross-cycle resumption: ``auto/last-attempt-tier-N`` labels
|
||||
# name the next tier explicitly. The estimator cannot override
|
||||
# label-driven resumption — even when ``estimator_enabled`` is
|
||||
# True the hint is emitted so the worker resumes at the
|
||||
# correct tier.
|
||||
extras.append(f"escalation_tier_hint: `{start_tier}`")
|
||||
hint: int | None = start_tier
|
||||
elif not estimator_enabled:
|
||||
# Estimator OFF on a true first attempt (no resumption labels):
|
||||
# emit the explicit hint=0 fallback so the worker short-circuits
|
||||
# to ``tier-0`` — byte-for-byte the legacy behaviour.
|
||||
extras.append("escalation_tier_hint: `0`")
|
||||
# else: estimator ON, first attempt — OMIT the hint so the worker
|
||||
# falls through to ``estimator-implementation``.
|
||||
final_prompt = f"{base_prompt}\n\n" + "\n".join(extras) + "\n"
|
||||
hint = 0
|
||||
else:
|
||||
hint = None
|
||||
|
||||
# P0 / A: deterministic short-circuit.
|
||||
# If the prefetch + deterministic sections + remote CI state all
|
||||
@@ -1749,7 +1784,7 @@ def _implementation_prompt_dispatch(
|
||||
# for the auto-fix path; the no-op skip case fires whenever
|
||||
# escalation is on and conditions are met).
|
||||
_maybe_short_circuit(cfg, item, context)
|
||||
return final_prompt
|
||||
return _wrap_for_tier_dispatcher(base_prompt, escalation_tier_hint=hint)
|
||||
|
||||
|
||||
# ─── Post-session action: cleanup pre-cloned worktree ───────────────────────
|
||||
@@ -3065,7 +3100,7 @@ WORK_GROUPS = [
|
||||
script_name="list_prs_ci_failing",
|
||||
item_kind="pr",
|
||||
claim_kind=CLAIM_KIND,
|
||||
worker_agent="implementation-worker",
|
||||
worker_agent="tier-dispatcher",
|
||||
tag_prefix="AUTO-IMP",
|
||||
prompt_factory=_implementation_prompt_dispatch,
|
||||
post_session_action=_dispatch_post_session_action,
|
||||
@@ -3075,7 +3110,7 @@ WORK_GROUPS = [
|
||||
script_name="list_prs_changes_requested",
|
||||
item_kind="pr",
|
||||
claim_kind=CLAIM_KIND,
|
||||
worker_agent="implementation-worker",
|
||||
worker_agent="tier-dispatcher",
|
||||
tag_prefix="AUTO-IMP",
|
||||
prompt_factory=_implementation_prompt_dispatch,
|
||||
post_session_action=_dispatch_post_session_action,
|
||||
@@ -3085,7 +3120,7 @@ WORK_GROUPS = [
|
||||
script_name="list_issues",
|
||||
item_kind="issue",
|
||||
claim_kind=None,
|
||||
worker_agent="implementation-worker",
|
||||
worker_agent="tier-dispatcher",
|
||||
tag_prefix="AUTO-IMP",
|
||||
prompt_factory=_implementation_prompt_dispatch,
|
||||
post_session_action=_dispatch_post_session_action,
|
||||
|
||||
Reference in New Issue
Block a user