-
New src/new_goal_dispatch.py (find_queued_tasks() + dispatch_once(), mirroring task_reconciliation.py's shape but deliberately a separate module/loop -- scheduler-task-reconciliation-design.md §7 already established the "don't merge into one mega scheduler loop" principle, this follows it). Wired into backend_server.py::main() as its own daemon thread with --new-goal-dispatch-interval-seconds (default 45s, shorter than reconciliation's 300s to satisfy the near-real-time acceptance bar) and --disable-new-goal-dispatch-loop. Also added the matching --disable-new-goal-dispatch-loop flag to supervisor/deploy_stage.py::_launch_candidate() -- same reasoning as the existing --disable-reconciliation-loop flag there: a health-checking deploy candidate runs against a copy of the production DB and must stay passive until Deploy Supervisor decides it's live, otherwise it would start autonomously dispatching real Goals off stale/copied state. User-confirmed 2026-08-13: full auto-dispatch, not a conservative default-disabled rollout -- this was previously flagged as a standalone "changes the system's own autonomy boundary" decision requiring separate review (docs/done/scheduler-new-goal-dispatch-requirements.md), now resolved. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
_launch_candidate() starts backend_server against a COPY of the prod db for health-checking only; without --disable-reconciliation-loop it would find resumable tasks in that copy and dispatch real claude -p calls before Deploy Supervisor decides whether the candidate is even good. Adds the flag plus a mocked-Popen regression test (LaunchCandidateArgsTest) so a future regression here fails fast instead of surfacing as a hang or a real incident. Also folds this into docs/todo/scheduler-production-polling-loop-design.md (§6.1) as part of the FR-2 design record. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
The third real self-hosting attempt (first to actually reach stage_deployment itself, past the earlier bootstrap/commit bugs) hit an uncaught FileNotFoundError: _copy_prod_db() assumed prod_db_path always exists and shutil.copy2()'d it unconditionally. graphlemon has never actually run as a persistent service, so ~/.graphlemon/graphlemon.db never existed. stage_deployment() already handles this exact class of "no prior state" bootstrap case for previous_commit_hash/previous_instance_dir -- prod_db_path never got the same treatment, and every existing test seeds a prod db in setUp so this was never exercised. Fix: when prod_db_path doesn't exist, create a fresh schema'd db instead of copying -- semantically correct, since a genuinely-first deployment has no production data to inherit. New regression test (BootstrapProdDbTest) covers it. 555 tests green (435 src + 120 supervisor). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
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>