Add scripts/test-evolve-cycle-smoke.sh that runs one bounded `ao evolve`
cycle and asserts a real commit lands AND no new orphaned commits appear.
Wire it into pre-push-gate.sh as opt-in `--smoke-evolve` (off by default
because a single bounded cycle takes 15-30 min).
This converts the end-to-end invariants for the cycle lifecycle from
narrative description into a runnable check. The 2026-05-06 overnight run
exposed exactly the failure mode this script catches: 4 cycles ran for 9
hours, 0 commits landed on main, 3 commits orphaned in detached worktrees.
None of the unit/integration tests in the existing pyramid would have
caught that — only an end-to-end run can.
Pass criteria (both required):
1. `git rev-parse HEAD` advances by at least one commit during the run.
2. `git fsck --unreachable | grep -c '^unreachable commit'` does not grow.
Behavior:
- Refuses to run on detached HEAD (would lose track of what landed).
- Configurable via --max-cycles, --timeout, --goal flags + AO_BIN env.
- Logs to .agents/evolve/smoke/<timestamp>-cycle.log; deletes on PASS,
keeps on FAIL or when SMOKE_KEEP_LOG=1.
- WARN (not FAIL) when ao evolve exits non-zero but invariants hold —
the supervisor commonly exits non-zero on queue-empty / kill-switch.
The script is a precondition for M3.5 going live: once the daemon executes
cycles via `ao daemon jobs submit --type=rpi.run`, a CI smoke that proves
the cycle lifecycle still satisfies these invariants is the floor.
Currently exercises the in-process evolve path (--dispatch=daemon comes
with M3.5 itself).
Implements: soc-k3fa ([mc-m3.5-pre4])
Refs: .agents/research/2026-05-06-evolve-lifecycle-mid-session-discovery.md
.agents/plans/2026-05-06-evolve-lifecycle-preconditions-to-m3.5.md