Files
cleveragents-core/tools/controller
drew db12f45acb feat(controller): Phase 1m — wire response-builder MCPs into OpenCode (Option A)
The trial-path expedient (legacy_adapter harvesting chat-JSON) is now
the SAFETY NET; the architecture-intended path (agents call the
controller's response-builder MCPs, which validate against V1 +
write canonical JSON) is now wired end-to-end.

Four pieces:

1. MCP `finalize` accepts `output_path` argument
   ``mcp/_builder_base.finalize_and_emit`` adds ``output_path: str | None``
   kwarg. Precedence: explicit arg > ``CONTROLLER_CANONICAL_OUTPUT_PATH``
   env > stdout. This solves the per-attempt path problem that
   blocked opencode.json registration (the env var is static; the
   per-attempt path comes from the prompt, the agent passes it as a
   tool arg). Each role's finalize updated:
   - estimator_finalize(output_path)
   - implementer_finalize(output_path)
   - reviewer_finalize(output_path)
   - conflict_finalize(output_path)
   - summarizer_finalize(output_path)
   Creates parent directory if missing (so the controller doesn't
   need to pre-create). Returns ``wrote_to`` in the ok dict so tests
   can pin the path.

2. opencode.json registers the 5 controller MCPs
   ``.opencode/opencode.json`` adds:
   - implementer-response-builder
   - reviewer-response-builder
   - estimator-response-builder
   - conflict-resolver-response-builder
   - summarizer-response-builder
   Each spawned via ``python -m tools.controller.mcp.{role}_builder``
   with PYTHONPATH=/repo-root so the controller imports resolve.

3. Controller prompt builder injects the EXACT tool sequence
   ``worker/prompts.py`` rewrites each role's "Output contract"
   section. The old "PREFERRED MCP / FALLBACK file-write" instruction
   becomes a single REQUIRED contract: numbered tool calls (``X_start``,
   ``X_set_*``, ``X_finalize(output_path=...)``) with the explicit
   per-attempt path baked in. Explicit "DO NOT emit a JSON object in
   your final chat message" instruction to override the legacy
   contract baked into the agent system prompts.

4. Agent permission whitelists include the new MCPs
   - ``.opencode/agents/task-implementor.md`` (source for tier-{0,1,2,min}
     variants — regenerated via tools/sync_tier_models.py)
   - ``.opencode/agents/pr-review-worker.md``
   - ``.opencode/agents/estimator-implementation.md``
   - ``.opencode/agents/conflict-resolver-worker.md``
   Each adds the matching ``"{role}*": allow`` pattern.

Safety net preserved:
The legacy_adapter (commit bcc59d38a) is KEPT as a fallback path.
If an agent ignores the new instruction + emits chat-JSON anyway,
``opencode_session.py:inline_output_callback`` harvests it +
``legacy_adapter`` normalizes to V1 + writes to the canonical out
path. Both paths produce valid V1 → ``strict_parse`` succeeds. The
MCP path is now the WORKING preferred path; the chat-JSON harvest
is the safety net.

Total: 738 controller tests pass (+0 net — wiring change, no new
behavior tests), 0 regressions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 18:10:48 -04:00
..