Skip to content

fix(aep): autopilot worktree guard + Phase 12 merge-completion contract (v2.5.0)#18

Merged
memorysaver merged 4 commits into
mainfrom
fix/autopilot-worktree-merge-contract
Jun 27, 2026
Merged

fix(aep): autopilot worktree guard + Phase 12 merge-completion contract (v2.5.0)#18
memorysaver merged 4 commits into
mainfrom
fix/autopilot-worktree-merge-contract

Conversation

@memorysaver

Copy link
Copy Markdown
Owner

Why

An autopilot worker (MITS-036 / PR #28, Codex backend, AEP 2.4.0) built a story, opened a CLEAN PR with no required checks, and stopped at "PR ready" instead of completing Phase 12 merge + wrap. Upstream of that, the user found the worker had never created a worktree — it created feat/<name> directly in the main checkout.

These are one bug, not two. The worktree binding and the autopilot-mode merge decision were both unenforced soft contracts inferred from cwd, /aep-build had no entry guard, and a global "always confirm before merging" guardrail contradicted Phase 12's autopilot exception. No worktree → cwd not under .feature-workspaces/ → Phase 12 detects "interactive mode" → stop at ready. On Codex codex-subagent (spawn_agent has no cwd parameter) the failure is near-deterministic.

Not a 2.4.0 regression — latent since v1.6.0

git blame + git tag --contains: the contradicting guardrail (f6222ba, 2026-03-26), the autopilot "merge immediately" + cwd detection (108d4c1/4225bf2, v1.6.0), and the entire Codex backend (108d4c1, v1.6.0) all predate 2.4.0. v2.4.0's build/SKILL.md change was journey-authoring (lines 512–650); Phase 12 / the guardrail were untouched.

What changed

Defect Fix
D0 /aep-build had no worktree guard; binding was a soft contract Phase 0 worktree entry guard + onboarding: verify show-toplevel under .feature-workspaces/ on feat/*; self-heal (enter/create), never build in main; escalate if it can't
D2 Mode inferred from cwd (soft on Codex) /aep-launch writes .dev-workflow/signals/**mode** (autopilot); Phase 12 reads the marker, cwd is fallback-only
D1 "Always confirm before merging" contradicted Phase 12 (3 copies) Guardrail + onboarding Key Rule now carry the interactive-only caveat; Phase 12 gains a "PR ready is NOT a stop condition" allowlist
D4 "CI checks green" undefined with zero required checks "required checks green or no required checks configured"; maps mergeStateStatus=CLEAN
D3 "main NEVER merges" loud; worker obligation quiet → leaks on shared-session Codex Paired positive worker MUST merge statement; nudges forbid "stop at ready"; Codex aep-builder role verifies its worktree
D5 No regression fixture merge-decision-cases.md pins the two scenarios

Plus a .gitignore fix: the unanchored build pattern silently ignored new files inside the build/ skill dir (it hid this PR's new fixture) → anchored to /build/.

Version: minor bump 2.5.0 — additive mode signal + guard, backward-compatible (cwd fallback preserved).

Verification

  • grep across build / autopilot / tick-protocol / launch / signals-spec / onboarding: every remaining "ask before merge" is now interactive-only; the only "do not merge"/"stop at ready" strings are guidance forbidding them; the stop-condition allowlist is identical across files.
  • Phase 0 + Phase 12 + Guardrails + onboarding re-read together: no mutually-unsatisfiable instruction pair; no path lets build proceed in the main checkout.
  • merge-decision-cases.md encodes the regression cases (clean PR + no checks ⇒ merge/wrap; build outside worktree ⇒ self-heal).

🤖 Generated with Claude Code

memorysaver and others added 4 commits June 27, 2026 00:09
…ct (v2.5.0)

Fix the autopilot "PR-ready stop" + "feat branch in the main checkout" failure
mode (latent since v1.6.0, surfaced on the Codex backend, MITS-036/PR#28).

Root cause: the worktree binding and the autopilot-mode merge decision were both
unenforced soft contracts inferred from cwd; /aep-build had no entry guard; and a
global "always confirm before merging" guardrail contradicted Phase 12's autopilot
exception. On Codex codex-subagent (spawn_agent has no cwd parameter) a worker
could build in the main checkout and then park a CLEAN, no-required-checks PR at
"ready" forever.

- D0: worktree entry guard in /aep-build Phase 0 + worktree-onboarding — verifies
  the worktree and self-heals; never builds/branches in the main checkout.
- D2: explicit .dev-workflow/signals/mode autonomy marker written by /aep-launch,
  read by Phase 12 (cwd is now only a fallback hint); documented in signals-spec.
- D1: resolve the merge contradiction — guardrail + onboarding Key Rule now carry
  the interactive-only caveat; Phase 12 gains a "PR ready is NOT a stop condition"
  allowlist.
- D4: "no required checks configured" + mergeStateStatus=CLEAN ⇒ proceed.
- D3: pair "main NEVER merges" with the positive worker obligation; strengthen
  merge nudges + the Codex aep-builder role.
- D5: merge-decision-cases.md regression fixture.
- chore: anchor the unanchored `build` .gitignore pattern to /build/ (it silently
  ignored new files inside the build/ skill dir).

Not a 2.4.0 regression; minor bump for the additive `mode` signal + guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WFAeDemRdH8KzcYhiDTrto
…ntract

Self-review of the PR surfaced a regression I introduced plus several
half-applied edges. Fixes (no behavior beyond the original intent):

- mode marker is now the SOLE autopilot authority; dropped the Phase 12 cwd
  fallback. The Phase 0 guard relocates EVERY build (incl. interactive) into a
  worktree, so "cwd under .feature-workspaces/" no longer distinguishes
  autonomous from interactive — the old fallback auto-merged interactive
  (Case C) builds without asking. Ambiguous signal now defaults to interactive.
  Aligns build/SKILL.md, worktree-onboarding, signals-spec, and the Guardrail.
- readiness keys on mergeStateStatus (CLEAN/UNSTABLE proceed; BLOCKED/DIRTY
  stop) instead of counting raw statusCheckRollup (which conflated required vs
  optional and "none configured" vs "not yet reported").
- merge nudges now enumerate the full 6-item stop list (added the two safety
  stops: human-approval gate + policy pause) — no half-applied subset.
- worker no longer told to run /aep-wrap (boundary: /opsx:archive runs on the
  integration branch); it ends at merge + status.json completed.
- Phase 0 guard hardened: anchor to the main repo root (correct from any cwd),
  and handle "feat/<name> already checked out in main" (the resume case) so the
  advertised self-heal actually self-heals; dropped the dead $BRANCH==$BASE term.
- .gitignore: /build/ alone un-ignored apps/*/build and packages/*/build in this
  turbo monorepo; added per-workspace anchors that still keep the skill dir tracked.
- merge-decision-cases.md no longer over-claims the taxonomy is identical across
  six files; names build Phase 12 as the canonical source.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WFAeDemRdH8KzcYhiDTrto
…ate, fix nudge/readiness drift

The full-coverage re-review showed my round-1 "self-heal by creating worktrees"
was the source of most findings. Reverting to the original AEP architecture
(/aep-launch owns worktree creation; build verifies + enters) eliminates the whole
cluster at once:

- Phase 0 guard is now enter-or-escalate and <name>-specific: it enters the
  worktree /aep-launch already made (anchored to the main repo root) and ESCALATES
  if absent — never creates a branch/worktree, never `git switch`es the main
  checkout (which could strand uncommitted WIP), never builds in a sibling worktree.
  Removes the dangerous recovery paths and the wrong-base/prune/origin edges.
- mode marker read is now anchored to the worktree root (was cwd-relative — a build
  phase that cd'd into a subdir would miss it and wrongly park as interactive).
- Phase 12 readiness: UNSTABLE no longer blanket-merges (stop if a check is failing,
  even with no required checks configured); UNKNOWN re-reads (the normal state right
  after the force-push) instead of having no rule.
- stuck merge nudges (autopilot/SKILL.md, tick-protocol.md) still said "Verify CI
  green" (the retired gate) and omitted the do-not-wrap guard — both fixed; the
  primary tick nudge said "rebase on main" (wrong on develop repos) → integration branch.
- merge-decision-cases: Case A clarifies worker-merges/orchestrator-wraps; Case B
  is enter-or-escalate; dropped the stale "cwd fallback" reference.
- CHANGELOG updated to match; documented the stale-marker edge in signals-spec.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WFAeDemRdH8KzcYhiDTrto
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WFAeDemRdH8KzcYhiDTrto
@memorysaver memorysaver merged commit 9414b24 into main Jun 27, 2026
1 check passed
@memorysaver memorysaver deleted the fix/autopilot-worktree-merge-contract branch June 27, 2026 16:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant