Apply Code Changes
Orchestrate apply: execute tasks.md in order (Changelog group last), run the verify-aligned gate and verify-fix loop, then hand off per venue.
Core = steps 0–7. Change adapter = pre-flight path resolution — change-adapters.md.
Verification ref — branch where gate and verify-fix run: ORIGINAL_BRANCH (local/worktree); FEATURE_BRANCH (remote).
Never in apply: archive, spec sync, archive commit, /opsx:archive.
Invariant
ORIGINAL_BRANCH is the durable branch for local and worktree apply — no openspec/<name> feature branch on those venues. FEATURE_BRANCH exists only for remote (PR head). Worktree isolation uses ephemeral apply-<name> (APPLY_REF), squash-merged onto ORIGINAL_BRANCH at handoff then deleted.
Session variables
| Variable | When set | Meaning |
|---|---|---|
CHANGE_ROOT |
Pre-flight (adapter) | Adapter changeRoot / user path — planning read only after bind |
CHANGE_ROOT_REL |
Pre-flight | Repo-relative path from CHANGE_ROOT — used to compute ACTIVE_CHANGE_ROOT |
NAME |
Pre-flight | Basename of the change — used in apply-<name> |
TRACKING_HINT |
Pre-flight (optional) | Non-authoritative adapter-path seed; may be from the wrong checkout |
TRACKING |
Setup + pre-bind merge | Authoritative fields (Issue, Change, Branch, PR, Presets). Branch resolution reads merged TRACKING only |
TRACKING_SETUP |
End of setup | Snapshot after setup — restore before store-adoption restart in pre-bind merge |
STORE_SOURCE |
Pre-flight (OpenSpec) | explicit for user/command --store, hint for TRACKING_HINT → store |
PRESET_OVERRIDES |
Setup | Current-run venue, parallelism, and explicit STORE — override Presets keys during every merge |
ORIGINAL_BRANCH |
Bind (step 2) | Base branch — local/worktree commits integrate here |
FEATURE_BRANCH |
Bind (step 2) | Remote only — PR head (openspec/<name> or feature/<name> default) |
APPLY_REF |
Bind (step 2) | Worktree only — ephemeral apply-<name> |
WORK_CHECKOUT |
Bind (step 2) | Git directory where the orchestrator runs |
ACTIVE_CHANGE_ROOT |
Bind (step 2) | WORK_CHECKOUT + CHANGE_ROOT_REL — all artifact I/O after bind |
Venue matrix (single source of truth)
After bind, follow the row for venue × parallelism. Step 2 implements bind; step 5 implements handoff — do not contradict this table elsewhere.
| Venue | Parallelism | Main repo branch | WORK_CHECKOUT |
Work ref | Handoff |
|---|---|---|---|---|---|
local |
single |
ORIGINAL_BRANCH |
main repo | — | Return control on ORIGINAL_BRANCH |
local |
subagent-per-group |
ORIGINAL_BRANCH |
main repo | group temps optional | Squash merge → ORIGINAL_BRANCH |
worktree |
single |
ORIGINAL_BRANCH |
worktree on APPLY_REF |
apply-<name> |
Squash merge → ORIGINAL_BRANCH; remove worktree; delete APPLY_REF |
worktree |
subagent-per-group |
ORIGINAL_BRANCH |
worktree on APPLY_REF |
apply-<name> (+ group temps) |
Squash merge → ORIGINAL_BRANCH; cleanup |
remote |
agent picks | ORIGINAL_BRANCH (idle) |
runner on FEATURE_BRANCH |
FEATURE_BRANCH |
Push; PR --base ORIGINAL_BRANCH; link issue |
Legacy Presets key workspace → treat as venue when venue is absent.
Inputs
After bind, paths are under ACTIVE_CHANGE_ROOT. Prefer OpenSpec artifactPaths / contextFiles when set.
| Priority | Source | Purpose |
|---|---|---|
| HIGH | tasks.md |
Checkbox progress |
| HIGH | specs/**/*.md |
Scenario → test coverage gate |
| HIGH | design.md |
Design/spec coherence gate |
| MED | proposal.md |
Changelog scope |
| MED | TRACKING / tracking.md |
Issue/PR linkage + resume Presets |
| LOW | docs/agents/issue-tracker.md |
Issue workflow when present |
Trusted hint: TRACKING_HINT loaded and its Change equals CHANGE_ROOT_REL (legacy: equals CHANGE_ROOT). Branch, Issue, PR, and Presets from an untrusted hint are ignored for merge overlay.
Steps
0. Pre-flight (adapter — planning reads only)
- Run a change adapter per change-adapters.md; set
CHANGE_ROOT,CHANGE_ROOT_REL, andNAME. Adapters never create on-disktracking.md. - Read
tasks.md, specs,design.md,proposal.mdfromCHANGE_ROOTfor planning context. - Load
tracking.mdatCHANGE_ROOTintoTRACKING_HINTwhen present; re-probe OpenSpec once when the hint selects a store. Do not treat it as finalTRACKING.
1. Setup
Interactive host — structured-choices venue gate (one message; must run — a trusted Issue or filled Presets does not skip it):
- Initialize
TRACKING: copy non-emptyTRACKING_HINTfields field-by-field, except Branch, Issue, PR, and Presets keys — copy those only on a trusted hint; set Change =CHANGE_ROOT_REL. Initialize emptyPRESET_OVERRIDES. Do not write to disk. - Venue:
local|worktree|remote→ setTRACKINGPresets →venueandPRESET_OVERRIDES→venue. - When the OpenSpec adapter set
STORE, set Presets →store; add toPRESET_OVERRIDESonly whenSTORE_SOURCEisexplicit. - Parallelism (agent — not a user gate): choose
single|subagent-per-groupusing the criteria below; set Presets →parallelismandPRESET_OVERRIDES→parallelism; note one-line rationale in session. - Snapshot
TRACKING_SETUP= copy ofTRACKING.
Done when: PRESET_OVERRIDES contains venue and parallelism; interactive hosts completed step 2 via structured-choices.
Non-interactive host (CI — no user present to answer; not merely a tool you cannot see): skip step 2 gate; set venue from merged Presets → venue (legacy workspace), else remote when shipping intent is clear, else local; choose parallelism per same criteria; lock both in PRESET_OVERRIDES; snapshot TRACKING_SETUP.
Parallelism criteria (all venues, including remote)
Choose subagent-per-group when every row holds; else single:
| Require | Rationale |
|---|---|
Two or more numbered implementation ## groups in tasks.md |
Enough isolation to earn subagents |
| Platform supports Task/subagents | No fake parallelism |
| Venue supports git isolation | local: orchestrator commits on ORIGINAL_BRANCH; worktree: group temps under apply-<name>-<slug>; remote: runner uses FEATURE_BRANCH |
Sequential dispatch only — never concurrent subagents on shared git state.
Optional: invoke a git worktree skill when present for naming/cleanup conventions — never required.
2. Pre-bind merge, branch resolution, and bind
Pre-bind tracking merge — before branch resolution or bind:
- Merge on-disk
tracking.mdat adapterCHANGE_ROOTwhen present (field-by-field; trusted-hint rules for Issue/PR/Branch). - When Presets →
venueisremote: candidateFEATURE_BRANCH=TRACKING→ Branch, else adapter default. When that branch exists locally or onorigin, readCHANGE_ROOT_REL/tracking.mdfrom it (prefer local, elseorigin/<branch>). WhenSTORE_SOURCEishintand path absent, search branch tree for.../NAME/tracking.md. Non-empty on-disk Branch ≠ candidate: STOP. Change mismatch: defer per store-adoption rules below. - Merge remote-branch tracking (Pre-bind tracking merge step 2 only): non-empty Issue, Branch, PR win; Presets keys merge individually. Overlay
PRESET_OVERRIDES; set Change =CHANGE_ROOT_REL. Never replacePresetswholesale. - Store adoption (unchanged intent): when
STORE_SOURCEishintand Change mismatch deferred or branch Presets →storediffers, adopt store, rerun adapter, restoreTRACKINGfromTRACKING_SETUP, restart merge once. - Map legacy Presets →
workspacetovenuewhenvenueempty.
Branch resolution — from merged TRACKING and Presets → venue:
ORIGINAL_BRANCH:TRACKINGPresets →base-branch; else current branch; else repo default (main/origin/HEAD). Note assumption when inferred.FEATURE_BRANCH— remote only:TRACKING→ Branch, else adapter default. When exists onoriginonly,git checkout -b FEATURE_BRANCH origin/FEATURE_BRANCH— never recreate fromORIGINAL_BRANCH. When exists nowhere,git checkout -b FEATURE_BRANCH ORIGINAL_BRANCH.APPLY_REF— worktree only:apply-<NAME>. Create fromORIGINAL_BRANCHif missing; never reuse a staleapply-<NAME>with unrelated commits — delete and recreate when Change differs.- Persist empty
base-branchinTRACKINGwhen inferred.
Bind — requires Presets → venue and parallelism. Set WORK_CHECKOUT, then ACTIVE_CHANGE_ROOT = WORK_CHECKOUT + "/" + CHANGE_ROOT_REL:
| Venue | Bind |
|---|---|
local |
Checkout ORIGINAL_BRANCH on main → WORK_CHECKOUT = main repo |
worktree |
Main on ORIGINAL_BRANCH. git worktree add <path> -b APPLY_REF ORIGINAL_BRANCH (or attach existing clean APPLY_REF) → WORK_CHECKOUT = worktree path |
remote |
Main on ORIGINAL_BRANCH. Resolve FEATURE_BRANCH per Branch resolution step 2. Checkout FEATURE_BRANCH in worktree or runner checkout; push -u origin FEATURE_BRANCH when new. Create Issue when empty and docs/agents/issue-tracker.md exists. Dispatch cloud/CI runner when platform supports; else orchestrate on FEATURE_BRANCH locally |
- Persist
tracking.mdatACTIVE_CHANGE_ROOT(reconcile; never wholesale Presets replace). - Re-read planning artifacts from
ACTIVE_CHANGE_ROOT.
3. Execute tasks
Prerequisite: ACTIVE_CHANGE_ROOT bound. All artifact reads/writes use ACTIVE_CHANGE_ROOT only.
Work through tasks.md in order — numbered ## groups, then Verification, Documentation, Changelog last.
Per implementation task:
- Map related
#### Scenario:blocks; name tests after scenarios. - Invoke tdd when present (non-blocking if absent).
- Implement; mark
- [ ]→- [x]only when tests pass. - Invoke git-commit for logical units on the active work ref (
ORIGINAL_BRANCH,APPLY_REF, orFEATURE_BRANCHper venue matrix). Commit this unit's in-scope paths; changelog-generator runs in the Changelog group. - After each
##group (or end whensingle), invoke code-review when present — fixed point =ORIGINAL_BRANCH.
Parallelism subagent-per-group:
local: subagents readACTIVE_CHANGE_ROOT; no git; orchestrator commits onORIGINAL_BRANCH.worktree: optional group worktrees onapply-<name>-<slug>; orchestrator merges intoAPPLY_REF; merge gate (step 4) squash-merges toORIGINAL_BRANCHbefore verify.remote: subagents on runner checkout; orchestrator commits onFEATURE_BRANCH.
Documentation group: update files from proposal Impact and tasks. Changelog group: invoke changelog-generator with ACTIVE_CHANGE_ROOT as the change root.
4. Merge gate (worktree only — blocking)
When venue is worktree, squash to ORIGINAL_BRANCH before step 5. Do not run verify-aligned or verify-fix on the worktree checkout.
- Checkout main repo →
ORIGINAL_BRANCH - Squash merge
APPLY_REF→ORIGINAL_BRANCH(one commit preferred) git branch --show-currentequalsORIGINAL_BRANCHgit status --porcelainempty on main repo
Done when: implementation commits live on ORIGINAL_BRANCH and the session is on main repo — not the worktree path.
5. Completion gate — verify-aligned (blocking)
Re-run on the verification ref at ACTIVE_CHANGE_ROOT until every row passes:
- All
tasks.mdcheckboxes[x](including Documentation and Changelog) - Canonical test command from
tasks.md— exit 0; every#### Scenario:has a passing named automated test - Lint/format when
tasks.mdor repo docs name commands — zero new warnings from this change - Adapter validator — OpenSpec:
openspec validate --all --jsonfromPLANNING_HOMEwith--storewhen set; Direct: skip unless user requests - Material
design.mddecisions reflected in specs — material drift = FAIL - Documentation tasks reflect actual behavior
- Changelog task complete
git status --porcelainempty on the verification ref. A dirty tree of this change's own work is resolved by committing it. Discarding the diff to turn the gate green is a defect, not a pass.
On FAIL: fix immediately; do not enter verify-fix or hand off.
6. Verify-fix loop (blocking)
Apply owns verification on the verification ref. Repeat until openspec-verify-change (or /opsx:verify) reports ✅ PASS with no unresolved warnings:
- Invoke openspec-verify-change via Skill tool, or run
/opsx:verify - On ❌ FAIL or new warnings: fix immediately; re-run step 5; return to (1)
- Proceed to handoff only on ✅ PASS
Done when: a standalone /opsx:verify after this step would confirm PASS — not surface new FAILs or warnings. Interruption re-runs route failures back here.
Verify-fix checks implementation vs specs, design, and tasks — beyond structural openspec validate. Step 5 owns git housekeeping and lint; verify-fix does not re-check a dirty tree.
7. Handoff
Done when: verify-fix PASS and the venue handoff row below completes.
| Venue | Parallelism | Handoff |
|---|---|---|
local |
single |
Report gate PASS. Main on ORIGINAL_BRANCH. Return control — no squash. PR only when user asks. Update Issue when linked. |
local |
subagent-per-group |
Squash merge integrated commits onto ORIGINAL_BRANCH. Delete ephemeral group refs. Return control. |
worktree |
either | Merge gate (step 4) already squash-merged APPLY_REF → ORIGINAL_BRANCH. git worktree remove; git branch -D APPLY_REF. Return control on ORIGINAL_BRANCH. |
remote |
either | Push FEATURE_BRANCH. gh pr create --base ORIGINAL_BRANCH --head FEATURE_BRANCH. Set TRACKING → PR; write tracking.md; commit and push. Link PR on Issue. |
Delegation
| Concern | Skill | Required |
|---|---|---|
| User gates | structured-choices | Venue gate (interactive) |
| Tests | tdd | Optional; gate requires green |
| Commits | git-commit | Preferred |
| Changelog | changelog-generator | Changelog group |
| Review | code-review | Optional |
| Verify-fix | openspec-verify-change (/opsx:verify) |
Blocking before handoff |
| PR / issue | gh + issue-tracker doc | Remote handoff |
| Cloud dispatch | SDK / Task environment: cloud |
Remote when available |
Narrowing
- No archive or spec sync inside apply.
- No marking tasks
[x]before tests pass. - No PR before verify-fix PASS (remote).
- No handoff before verify-fix PASS.
- No verify-fix or verify-aligned gate on a worktree checkout — merge gate (step 4) first.
- No treating
openspec validatealone as sufficient — verify-fix is mandatory for OpenSpec apply. - No deferring verify FAILs or warnings to the user.
- No durable feature branch on local or worktree — only
ORIGINAL_BRANCH(+ ephemeralapply-<name>). - No skipping the interactive venue gate because Issue or Presets are prefilled.
- No
FEATURE_BRANCHon local/worktree bind. - No recreating
FEATURE_BRANCHfromORIGINAL_BRANCHwhen it exists onorigin. - No skipping Changelog.
- No hardcoded
openspec/changes/<name>/. - No concurrent subagents on shared git state.
- No adapter creating on-disk
tracking.md. - No bind without Presets →
venueandparallelism. - No wholesale
Presetsreplace during tracking merge. - No inheritance of
Changefrom a tracking file — always current adapterCHANGE_ROOT_REL. - No post-bind artifact I/O via pre-bind
CHANGE_ROOT. - No
gh pr createwithout--base ORIGINAL_BRANCH. - No worktree handoff before merge gate (step 4) completes.
- No mixing superpowers-bridge apply with this skill on the same change.