Adds supervisor/deploy_rollback.py: rollback_path_a (pre-cutover, health
check failed -- terminate candidate, delete its one-time DB copy, git
revert, record a bug_record(core_engine) audit trail) and rollback_path_b
(post-cutover, anomaly detected -- stop the bad Live, revive
previous_instance_dir, re-run the same three health-check signals directly
against it, git revert only once re-verification passes).
Design choices follow established conventions rather than inventing new
ones: no unified rollback() entry point (deployment has no PID columns,
same reason deploy_stage/deploy_cutover already require callers to pass
pids explicitly); ROLLBACK_FAILED is the raised error name while the
persisted deployment.status is 'blocked_pending_human' (same pattern
CheckoutFailedError/CutoverFailedError already use). git revert conflicts
fold into the same rollback-itself-failed fallback both paths share, per
design doc §10 open question 3's own recommendation, with `git revert
--abort` run afterward so a failed attempt doesn't leave the repo mid-conflict.
This is PH5's highest-rework-risk Unit per development-plan.md's own flag,
so its own "rollback itself fails" fallback is tested for real, not assumed:
a genuinely broken previous_instance_dir (a real empty directory with no
Python package in it) that really fails to bind its port, and a genuinely
conflicting git history (a later commit touching the same line the
self-modification changed) that really fails `git revert` -- both correctly
land on blocked_pending_human without retrying forever or silently
succeeding, and the conflict case leaves the repo clean afterward.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>