Adds supervisor/deploy_stage.py: stage_deployment() creates a deployment row,
git-worktree-checks-out the target commit into a fresh directory (--detach,
never in-place git pull), copies the production DB to a one-time candidate
copy, and Popens a real candidate backend process on a free port.
Return value follows 04-P §4.6's state-machine table (status after the call
is 'candidate_starting') rather than 03-C's one-line 'staged' summary --
04-P's own trigger text ("c_deploy_supervisor_stage 完成" moves the row to
candidate_starting) is the more detailed/authoritative source when the two
disagree, same precedent used throughout this project.
Found and closed two real gaps along the way:
- deployment's own field list never included where a candidate's checkout
directory lives -- added candidate_instance_dir to schema.py (also what
the NEXT staging call's previous_instance_dir bootstraps from once a
deployment reaches 'finalized').
- backend_server.py had no CLI entry point at all since U-007 -- create_server()
was only ever instantiated in-process by tests. Added main() (argparse +
serve_forever + SIGTERM->shutdown() from a separate thread, per
http.server's own same-thread-deadlock warning).
RealCandidateLaunchTest spawns and HTTP-probes a genuine
`python3 -m src.backend_server` subprocess against a real git-worktree
checkout of this repo (not mocked) -- caught a real ordering issue doing so:
git worktree only sees committed content, so testing before this Unit's own
commit landed would check out backend_server.py without main() at all.
Fixed by using `git stash create` (builds a commit object from the current
working tree without touching HEAD/index) as the commit-to-test-against,
falling back to plain HEAD once the tree is clean post-commit.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>