Commit Graph

2 Commits

Author SHA1 Message Date
drew 1b09a2054f feat(controller): Phase 1i — per-role prompt templates
Replace the JSON-dump prompt stub with structured per-role builders
that render the V1 input contract fields in human-friendly sections.
The agent's system prompt (.opencode/agents/{name}.md) provides the
HOW (tool calls, response shape); this module provides the WHAT (the
per-attempt context drawn from input_payload).

Per-role builders in tools/controller/worker/prompts.py:
- build_implementer_prompt   — branch coords, CI summary (highlights
  failing gates), active reviewer state, new comments, prior attempts,
  optional allowed_files scope, output contract reminder
- build_reviewer_prompt      — PR coords, CI summary, full diff (with
  code fence), implementer's latest claim, prior reviewer outputs
- build_estimator_prompt     — PR/issue context (title, body, diff
  summary, head_sha), tier-pick guidance
- build_conflict_resolver_prompt — coords, conflicted files (or
  porcelain fallback), prior implementer outputs
- build_summarizer_prompt    — aged-out attempt + prior running
  summary; instructs terse condensation
- build_prompt(role, tier, input_payload) — dispatcher

Truncation budgets (in chars):
- full_diff:           12,000
- pr_body / summaries: 4,000
- prior-attempt JSON:  2,000
- comment one-liner:   200

Wired as the default prompt builder in opencode_session via
``_default_prompt_for``. wire_opencode_session() callers that pass
prompt_builder=... override per-test.

Tests (+35 in test_worker_prompts.py, 1 updated adapter test, 0
regressions):
- Per-role: section headers present, required fields rendered,
  missing fields fall back to (unavailable), oversize fields
  truncated
- Dispatcher: routes by role; raises ValueError for missing tier on
  tiered roles + unknown role
- Adapter integration: _default_prompt_for delegates to build_prompt

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 14:36:14 -04:00
drew bb8d872ba7 feat(controller): Phase 1d-4 — OpenCode session adapter
The production wrapper that wires the existing
_opencode_worker.run_session_blocking into the controller's
run_opencode_session protocol that production_agent_runner expects.

tools/controller/worker/opencode_session.py:

- agent_name_for(role, tier): role+tier → OpenCode agent name.
  - implementer + tier {0,1,2} → task-implementor-tier-{0,1,2}
  - reviewer → pr-review-worker
  - estimator → estimator-implementation
  - conflict_resolver → conflict-resolver-worker
  - summarizer → controller-summarizer (new agent name)

- DEFAULT_TIER_TIMEOUT_S: per-tier wallclock budgets via env vars.
  Defaults match plan v9 (tier 0: 600s, tier 1: 1200s, tier 2: 1800s).
  Reviewer/estimator/conflict use the default-agent timeout (600s).

- wire_opencode_session(opencode_server_url, ...) → callable matching
  the production_agent_runner's run_opencode_session contract.
  - Resolves role+tier to agent name + per-tier timeout
  - Builds the prompt (default stub or custom builder; per-role prompt
    templates are a follow-up)
  - Calls run_session_blocking with an on_poll callback that raises
    WorkerLostLock if the controller's lost_lock_check returns True
    mid-session (heartbeat thread reaper detected stolen lock)
  - Routes SessionResult.status:
    - 'completed' → return None (MCP's canonical output is in the
      tempfile; production_agent_runner reads it after we return)
    - 'timeout' → raise WorkerError(worker-internal-error)
    - 'transport-error' → raise WorkerError(worker-internal-error,
      including error_kind for forensics)
    - unknown → raise WorkerError defensively
  - Any unexpected exception from run_session_blocking wrapped as
    WorkerError(worker-internal-error).

- Production wiring: dependency injection. wire_opencode_session()
  lazy-imports the real run_session_blocking when not overridden.
  Tests inject a stub.

22 new tests in test_worker_opencode_session.py:
- agent_name_for: parametrized per role (4 implementer-tier rows +
  4 flat-role rows) + invalid tier + unknown role
- DEFAULT_TIER_TIMEOUT_S: each tier has a timeout + monotonic
- wire_opencode_session: completed/timeout/transport-error/unknown-
  status routing; run_session_blocking raises wrapped as WorkerError;
  on_poll propagates lost_lock; on_poll no-op when lock held;
  custom prompt_builder used; default prompt includes role + tier
  + finalize() instruction + rendered input_payload; tag prefix
  customizable.

Total: 398 controller tests; full auto_agents suite 2760 pass.
2026-05-18 14:09:34 -04:00