db12f45acb
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>
121 lines
3.3 KiB
JSON
121 lines
3.3 KiB
JSON
{
|
|
"$schema": "https://opencode.ai/config.json",
|
|
"mcp": {
|
|
"graphify": {
|
|
"type": "local",
|
|
"enabled": true,
|
|
"command": [
|
|
"/home/drew/repos/cleveragents-core/.venv/bin/python",
|
|
"/home/drew/repos/cleveragents-core/tools/mcp_graphify_server.py"
|
|
],
|
|
"environment": {
|
|
"GRAPHIFY_OUT_DIR": "/home/drew/repos/cleveragents-core/graphify-out",
|
|
"GRAPHIFY_BIN": "/home/drew/.local/bin/graphify"
|
|
}
|
|
},
|
|
"ci": {
|
|
"type": "local",
|
|
"enabled": true,
|
|
"command": [
|
|
"/home/drew/repos/cleveragents-core/.venv/bin/python",
|
|
"/home/drew/repos/cleveragents-core/tools/mcp_ci_server.py"
|
|
],
|
|
"environment": {
|
|
"CI_GATE_SCRIPT": "/home/drew/repos/cleveragents-core/tools/local_ci_gate.sh"
|
|
}
|
|
},
|
|
"forgejo": {
|
|
"type": "local",
|
|
"enabled": true,
|
|
"command": [
|
|
"/home/drew/repos/cleveragents-core/.venv/bin/python",
|
|
"/home/drew/repos/cleveragents-core/tools/mcp_forgejo_server.py"
|
|
]
|
|
},
|
|
"git": {
|
|
"type": "local",
|
|
"enabled": true,
|
|
"command": [
|
|
"/home/drew/repos/cleveragents-core/.venv/bin/python",
|
|
"/home/drew/repos/cleveragents-core/tools/mcp_git_server.py"
|
|
]
|
|
},
|
|
"handoff": {
|
|
"type": "local",
|
|
"enabled": true,
|
|
"command": [
|
|
"/home/drew/repos/cleveragents-core/.venv/bin/python",
|
|
"/home/drew/repos/cleveragents-core/tools/mcp_handoff_server.py"
|
|
]
|
|
},
|
|
"block_store": {
|
|
"type": "local",
|
|
"enabled": true,
|
|
"command": [
|
|
"/home/drew/repos/cleveragents-core/.venv/bin/python",
|
|
"/home/drew/repos/cleveragents-core/tools/mcp_block_store_server.py"
|
|
]
|
|
},
|
|
"implementer-response-builder": {
|
|
"type": "local",
|
|
"enabled": true,
|
|
"command": [
|
|
"/home/drew/repos/cleveragents-core/.venv/bin/python",
|
|
"-m",
|
|
"tools.controller.mcp.implementer_builder"
|
|
],
|
|
"environment": {
|
|
"PYTHONPATH": "/home/drew/repos/cleveragents-core"
|
|
}
|
|
},
|
|
"reviewer-response-builder": {
|
|
"type": "local",
|
|
"enabled": true,
|
|
"command": [
|
|
"/home/drew/repos/cleveragents-core/.venv/bin/python",
|
|
"-m",
|
|
"tools.controller.mcp.reviewer_builder"
|
|
],
|
|
"environment": {
|
|
"PYTHONPATH": "/home/drew/repos/cleveragents-core"
|
|
}
|
|
},
|
|
"estimator-response-builder": {
|
|
"type": "local",
|
|
"enabled": true,
|
|
"command": [
|
|
"/home/drew/repos/cleveragents-core/.venv/bin/python",
|
|
"-m",
|
|
"tools.controller.mcp.estimator_builder"
|
|
],
|
|
"environment": {
|
|
"PYTHONPATH": "/home/drew/repos/cleveragents-core"
|
|
}
|
|
},
|
|
"conflict-resolver-response-builder": {
|
|
"type": "local",
|
|
"enabled": true,
|
|
"command": [
|
|
"/home/drew/repos/cleveragents-core/.venv/bin/python",
|
|
"-m",
|
|
"tools.controller.mcp.conflict_resolver_builder"
|
|
],
|
|
"environment": {
|
|
"PYTHONPATH": "/home/drew/repos/cleveragents-core"
|
|
}
|
|
},
|
|
"summarizer-response-builder": {
|
|
"type": "local",
|
|
"enabled": true,
|
|
"command": [
|
|
"/home/drew/repos/cleveragents-core/.venv/bin/python",
|
|
"-m",
|
|
"tools.controller.mcp.summarizer_builder"
|
|
],
|
|
"environment": {
|
|
"PYTHONPATH": "/home/drew/repos/cleveragents-core"
|
|
}
|
|
}
|
|
}
|
|
}
|