| name | opencode-execute-phase |
| description | OpenCode-optimized executor for a `<harness>-plan-phase` lane plan. Use when the user wants OpenCode to implement a planned phase. Executes lanes with clean git preflight, owned-file boundaries, verification, and optional explicit worker-subagent fanout for disjoint lanes. |
OpenCode Execute Phase
Executes a phase plan produced by <harness>-plan-phase. The default executor is the main OpenCode thread. Worker subagents are optional and only used when the user explicitly asks for subagents, delegation, or parallel execution.
Core Rules
Use phase_loop_runtime.skill_paths resolver helpers for harness skill roots, handoff roots, helper roots, and reflection roots.
- Read the full phase plan before editing.
- Preserve user work. Never revert changes you did not make.
- Use the active session's file-editing tool for manual edits.
- When implementation depends on current external documentation, use PMCP first. Context7 is the preferred path for library docs; Bright Data or other search/scrape tools may be used only when
gateway_catalog_search shows they are available.
- Keep lane ownership boundaries. If implementation requires touching another lane's files, stop and revise the plan or ask the user.
- Do not run destructive git commands such as
git reset --hard or git checkout -- <path> unless the user explicitly requested that operation.
- Do not commit, push, or merge unless the user asked for those git actions.
Runner-Owned Lane Work Units
Injected HARNESSLANE runs may include a HarnessLaneAssignment from
shared/phase-loop/protocol.md. Treat that assignment as the work-unit
contract: execute only the selected lane_id, write only the listed
owned_files, treat consumed_interfaces as read-only, and emit one shared
automation: closeout for that work unit. Installed-skill drift is
warning-only when the repo-injected context is present.
OpenCode lane launches must preserve explicit delivery and permission
metadata: <harness> run, --dir, --agent, provider-qualified --model,
optional --variant, --format json, and the shared context file. Review,
reducer, verify, and closeout prompts are distinct from implementation prompts.
If the selected policy is unsupported by the active harness, stop with a typed
non-human blocker instead of silently downgrading.
Inputs
- Plan path: default latest
plans/phase-plan-*.md.
--dry-run: parse and print the lane schedule without editing.
--parallel: allowed only when the user explicitly requests parallel worker execution.
If no plan path is explicit, first check the current repo and branch handoff from <harness>-plan-phase using <harness>-config/shared/runtime-state.md: read the repo-local handoff resolver target .dev-skills/handoffs/<harness>-plan-phase/latest.md, validate from, repo, repo_root, branch, branch_slug, commit, and artifact, then use the artifact only if it exists under the current repo root. Ignore missing or mismatched handoffs unless the user explicitly asks to reuse cross-branch state.
Preflight
- Resolve repo root and plan path.
- Run
git status --short.
- Run
git status --short -- <plan_path> and warn if the plan is untracked.
- If the tree has unrelated dirty files, leave them alone and scope edits around them.
- Parse:
- interface gates;
- lane DAG;
- owned files;
- task lists;
- verification commands;
- optional
Dispatch Hints from the roadmap or plan using the frozen
vocabulary from shared/phase-loop/protocol.md.
- Validate producer dependencies:
- any lane that consumes another lane's findings, interfaces, or artifacts must list that producer lane in
Depends on;
- any lane that writes a synthesized artifact must be downstream of every producer lane it summarizes;
- if dependencies are missing, stop and require a plan correction before execution.
- For
--dry-run, report the topological lane order and stop.
Execution Workflow
- Execute lanes in topological order.
- For each lane:
- when the selected executor is OpenCode live, keep the launch contract
explicit around
<harness> run, --dir, --agent, provider-qualified
--model, optional --variant, --format json, and the shared
context_file artifact;
- record OpenCode permission posture explicitly and refuse permissive
defaults unless the runner intentionally opts in;
- treat installed-skill drift as warning-only metadata when injected repo
context is present and the child otherwise succeeds;
- read the owned files and related tests;
- write or update tests first when practical;
- implement only lane-scoped changes;
- run lane verification (normalize any phase-plan pytest
-k selector whose prose terms contain spaces โ e.g. remote connect becomes 'remote and connect' โ or quote it; a bare spaced -k term is an executor-side normalization, not a plan blocker);
- run any phase-level checks that cover touched files.
- After each lane:
- inspect
git diff -- <owned files>;
- confirm no peer-owned files were modified;
- record completed gates.
- After all lanes:
- run the full phase verification commands;
- if execution discoveries change downstream work, amend the nearest
downstream roadmap phase that is not already executing;
- treat any older downstream phase plan or handoff as stale after a roadmap
amendment and route the next step back through
<harness>-plan-phase;
- describe the change explicitly as a roadmap amendment in the final
closeout;
- summarize changed files, tests run, and any residual risks.
Optional Worker Fanout
Use worker subagents only when the user explicitly authorizes parallel agent work and the DAG-ready lanes are disjoint.
For every worker brief:
- State that they are not alone in the codebase.
- Assign exact owned files or globs.
- List files they may read but not edit.
- Tell them not to revert unrelated changes.
- Require a final response with changed paths, tests run, failures, and blockers.
- Do not give two workers overlapping write ownership.
The main thread remains responsible for integrating results, reviewing diffs, running final verification, and resolving conflicts.
Failure Policy
- Test failure in a lane: diagnose once, fix within the lane if the cause is local, otherwise stop and report.
- Ownership violation: stop and revise the plan before continuing.
- Missing dependency or unclear interface: stop and route back through planning.
- Verification command unavailable: report the missing tool and run the closest available static check only if it is meaningful.
- Use only the frozen blocker taxonomy from
shared/phase-loop/protocol.md,
including missing_secret, dirty_worktree_conflict,
branch_sync_conflict, and repeated_verification_failure.
Runner Verification Evidence
Before reporting a successful closeout, require the runner-owned verification artifact. The closeout must name verification_artifact_path, quote the artifact summary line, and must not report verification_status=passed unless that artifact exists and supports the executed work. Treat dependency-manifest install refresh and the full suite before closeout as runner-enforced expectations, not optional narrative checks. A blocked gate may be re-verdicted only by rerunning the originally specified runner check; proxy evidence requires a roadmap or plan amendment before the verdict changes. A prior run's terminal-summary is authoritative for reconcile-or-skip only when that closeout was accepted; a rejected or blocked prior closeout โ regardless of any self-reported complete/passed โ must not be reconciled against. On a re-run, re-do and re-verify the phase work from scratch rather than skipping on a stale summary.
Spec Delta Closeout
Before final closeout, choose exactly one spec_delta_closeout.v1 decision: no_spec_delta, roadmap_amendment, canonical_spec_update, governed_pipeline_refresh, mirror_cutover_required, dotfiles_skill_source_update, or human_source_judgment_required. Cite metadata-only evidence paths such as the active plan, lane closeouts, targeted pytest output, and git diff --check output. Preserve the phase plan's target surfaces and redaction_posture=metadata_only; do not include raw specification bodies, raw patch bodies, credentials, provider-supplied payloads, local environment values, or evidence-source contents. Missing or malformed spec-closeout evidence is a repairable automation blocker with blocker_class=contract_bug unless the decision is human_source_judgment_required.
Closeout
Manifest lifecycle
After plan validation and before lane execution, perform a best-effort plan-manifest append lifecycle update through phase_loop_runtime.plan_manifest.update_lifecycle to mark the matching type=phase entry executing with run metadata. During closeout, update the same entry to completed or failed with verification metadata, reflection metadata, produced-gate metadata, if_gates_produced, and dirty-worktree summary fields as available. if_gates_produced must list only the IF gates the active phase produces per its own plan; never carry a prior phase's gate forward into this phase's closeout. Manifest lifecycle failures are non-fatal during the dual-mode window: emit a ledger warning, mention the warning in the mandatory reflection, and preserve the existing bounded phase execution, closeout expectations, and dirty-worktree behavior.
Before final closeout, run git status --short -- <plan_path> <roadmap_path> for every consumed or updated planning artifact. If any planning artifact is untracked or modified and the user did not explicitly forbid staging, run git add <path> for each artifact. Rerun status and report Artifact state: staged|tracked|modified|unstaged|blocked for each artifact. Do not commit unless requested. Repo-local handoff files are operational state: do not git add an ignored handoff alongside the plan artifact unless the plan's owned-files/allowlist explicitly includes the handoff directory; leave ignored handoffs ignored and exclude them from artifact-state reporting.
Determine the next step before final response and handoff:
- If the current phase is incomplete or verification failed, report
Next phase: <current alias> - blocked: <blocker> and Next command: none - <blocker>.
- If another generated phase plan is ready, report
Next phase: <next alias> - execution ready and Next command: <harness>-execute-phase <next_plan_path>.
- If the roadmap has an unplanned ready phase, report
Next phase: <next alias> - planning ready and Next command: <harness>-plan-phase <roadmap_path> <next_alias>.
- If the roadmap needs extension, report
Next phase: none - roadmap extension needed and Next command: <harness>-phase-roadmap-builder <roadmap_path>.
- If all phases are complete, report
Next phase: none - roadmap complete and Next command: none - roadmap complete.
Add a machine-readable automation: handoff with verification_status that agrees with the human-readable next step fields, and use the phrase manual event when standalone compatibility event import applies. Closeout payload shape is defined by EmitPhaseCloseout in phase_loop_runtime/baml_src/emit_phase_closeout.baml (if that path is absent in the checkout, use the operator/prompt-supplied field contract or the installed phase_loop_runtime package โ the missing vendored BAML source is not a blocker); keep skill text focused on value selection and handoff routing, not duplicated field ceremony.
Before final response, write a reflection for every non-trivial run. Write it to resolve_skill_bundle_root("codex")/<harness>-execute-phase/reflections/<repo_hash>/<branch_slug>/<run_id>.md. The reflection must include ## Run context with skill name, ISO timestamp, repo, branch, commit, and artifact path if any, followed by ## What worked, ## What didn't, and ## Improvements to SKILL.md. skip only when no artifact was produced AND no decision was made AND the run was pure inspection.
Report:
- lanes completed;
- files changed;
- planning artifact tracking state;
- next phase and next command;
- verification commands and results;
- commands not run and why;
- follow-up risks or manual checks.
In every report, handoff, and closeout above โ and in any PR/issue body or commit message this run writes โ reference issues and PRs as repo#N (or owner/repo#N), never a bare #N; the fleet is multi-repo, so a lone number is ambiguous.
Resolve closeout writes through the phase_loop_runtime.skill_paths resolver as the primary source โ resolve_handoff_root(repo) for the handoff root and resolve_reflection_root(skill_name) for reflection roots; fall back to the repo-local shared/phase-loop/handoff_path.py resolver only when phase_loop_runtime is not importable. Legacy harness handoff roots are read only for migration. Follow <harness>-config/shared/runtime-state.md and use OpenCode paths only:
- Reflection:
resolve_skill_bundle_root("codex")/<harness>-execute-phase/reflections/<repo_hash>/<branch_slug>/<run_id>.md
- Handoff:
<repo>/.dev-skills/handoffs/<harness>-execute-phase/<run_id>.md
- Latest handoff pointer:
<repo>/.dev-skills/handoffs/<harness>-execute-phase/latest.md
Handoff frontmatter must include from: <harness>-execute-phase, timestamp:, repo:, repo_root:, branch:, branch_slug:, commit:, run_id:, artifact:, artifact_state:, next_skill:, next_command:, and next_phase:. Put open follow-up items in the body, and update latest.md with the same handoff content.
Publication mode
After verified phase work, select exactly one of three modes. Default to (a) unless a
clear interactive signal is present โ a run missing BOTH the adapter prompt prefix AND
PHASE_LOOP_RUN_MODE is AMBIGUOUS and MUST be treated as (a) (fail safe toward the runner).
- (a) Runner-managed closeout (incl. governed mode), OR ambiguous mode. If this is the
phase-loop adapter (the prompt begins with
<harness>-execute-phase <plan> from a
pipeline run dir), or PHASE_LOOP_RUN_MODE is set (autonomous/governed), OR neither
interactive signal is clearly present, the RUNNER owns closeout and commit. Do NOT
independently publish โ defer entirely to runner closeout (awaiting_phase_closeout /
runner commit). Publishing here would bypass the governed pre-merge review panel.
- (b) Interactive orchestrator on a clean, non-protected feature branch (a clear
interactive signal, and the merge target passed the merge-target safety gate). After the
Step-9 clean-tree state, push the merge-target branch and open a PR (
gh pr create,
--draft if dependencies remain or verification was partial/skipped, else ready) instead
of leaving the lane merge only local.
- (c) Merge target is
main or a protected branch. Already STOPPED at the merge-target
safety gate before any lane merge โ never merge lanes onto main/protected. Re-target a
feature branch or take explicit instruction.
This applies only to interactive (non-runner) completion; it never overrides
awaiting_phase_closeout, the runner's deliberate non-complete terminal. Allowed runner
hygiene (forced lane-worktree removal, branch -D, sweep_stale_worktrees.sh) is
unchanged โ the destructive-op ban targets publication branches/worktrees holding
unmerged work.
Draft PR early โ push on first commit (visibility)
Do not let a phase branch accumulate commits only locally โ that is how lanes drift 70โ100 commits ahead of origin and in-flight work stays invisible. On the FIRST commit of a phase, push the branch to origin and open a DRAFT PR (gh pr create --draft); keep pushing as the phase progresses, and flip the PR to ready at closeout once verification is green. The early draft PR is the visibility contract, not a request to merge.
Respect the publication ownership above: in runner-managed / governed mode the RUNNER owns publication, so the runner performs the early push and draft-PR โ do not independently publish or bypass the governed pre-merge review panel. In the interactive-orchestrator path you perform the early push + draft PR yourself on the first commit.
Worktree lifecycle โ prune after merge (standing rule)
Prune a worktree โ and delete its now-dead branch โ as soon as its PR merges. Never leave merged or abandoned worktrees behind. Forced removal of the run's own lane worktrees at merge time is already allowed runner hygiene; this rule additionally covers the sibling worktrees under the shared workspace volume (/mnt/workspace/worktrees/<project>-<branch>, or the repo-sibling ../<project>-<branch> fallback on hosts without it) that outlive a single run. Left unpruned they accumulate without bound โ the shared volume has reached thousands of stale merged worktrees because nothing swept them.
This is a sweep, not "delete the tree you are standing in." At closeout the current run's own branch/worktree is still unmerged, so it is KEPT by the criterion below โ which is exactly right. Run the sweep at closeout (and, cheaply, at phase start).
For each sibling worktree from git worktree list (excluding the primary checkout and this run's own worktree), classify:
- SAFE to prune โ the branch is MERGED and the tree is CLEAN. MERGED means either
git merge-base --is-ancestor <branch> origin/main (fetch first) OR gh pr view <branch> --json state -q .state reports MERGED (catches squash/rebase merges that are not ancestors). CLEAN means git -C <path> status --porcelain is empty. Then git worktree remove --force <path> and git branch -D <branch> โ the ref is dead once its PR merged to origin, so deleting it is correct regardless of the branch's naming.
- KEEP โ unmerged (work not on
origin/main and no merged PR) OR dirty (--porcelain non-empty). Leave it and log one line. This preserves the current run's own worktree and any in-flight peer work.
Permission-locked fallback (gotcha). A worktree whose node_modules (or other build output) was installed under a different uid โ e.g. CI-offload / rootless-docker runs โ is permission-locked. git worktree remove --force and a plain rm -rf will FAIL with Permission denied. When removal fails on permissions, fall back to sudo rm -rf <path> (or re-run the cleanup as the installing uid), then git worktree prune to drop the dangling administrative entry, then git branch -D <branch>. A permission-denied removal is still SAFE โ do not reclassify it as KEEP.