• Adds supervisor/deploy_cutover.py: stops the old Live process, retires the
    health-check-mode candidate, and relaunches the same already-verified
    candidate checkout bound to LIVE_PORT/PROD_DB_PATH -- http.server has no
    live socket-rebind operation, so "switch to production port/db" is
    implemented as stop-then-relaunch-from-the-same-dir rather than an in-place
    rebind (functionally equivalent, the only mechanism stdlib actually offers).
    
    Adds LIVE_PORT=8420 to 05 §5.9's MVP-fixed parameter table -- the one
    parameter that table was missing (frontend/app.js never needed one, same-
    origin relative fetches don't care, but successive deployments need to keep
    landing on the same port for a human bookmark to keep working).
    
    "在途 work_node_execution 重启后可恢复" is satisfied by relying on the
    existing REQ-06 recovery machinery unchanged -- cutover doesn't add any
    special-case logic, a cutover-triggered restart is just another interruption
    REQ-06 already survives.
    
    A real (not mocked) test caught a real bug: deploy_supervisor.is_alive()'s
    os.kill(pid, 0) check can't distinguish "truly running" from "exited but
    never reaped by its own parent" -- a zombie's PID slot answers kill(pid, 0)
    successfully until waited on. U-036's own subprocess test never caught this
    because that skeleton deletes its own PID file before exiting, sidestepping
    is_alive() entirely; deploy_cutover's test is the first one to actually poll
    for a stopped process to disappear, and it hung until fixed. Added
    reap_if_exited() (real os.waitpid for processes we spawned ourselves, falls
    back to is_alive() for pids from a prior session where ChildProcessError
    means we're not the real parent) and switched deploy_cutover._stop_process,
    deploy_health_check.check_process_alive, and deploy_supervisor.stop() to use
    it.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    bboonnee authored
     
    Browse Dir »
  • Adds supervisor/deploy_health_check.py: run_health_check() runs the three
    pre-cutover checks in order, stopping at the first failure (03-C: "任一检查项
    fail 立即整体判定 health_check_failed,不等待其余检查项跑完") and persisting
    a deploy_health_signal row for every check that actually ran.
    
    - process_alive: polls deploy_supervisor.is_alive() up to HEALTH_CHECK_STARTUP_TIMEOUT.
    - http_health: hits the candidate's /api/health -- added to backend_server.py
      (U-037 deliberately left this for U-038). Not just "process exists" --
      runs a real SELECT 1 to catch a wedged sqlite connection a live-but-stuck
      process would otherwise pass.
    - canary_task: a real (not mocked) project+task creation round-trip against
      the candidate over HTTP -- no real Org Graph/claude calls, since
      c_task_create's synchronous 'queued' landing is already a deterministic,
      always-should-pass result, which is what a canary needs.
    
    Timeouts are the MVP-fixed drafts from 05 §5.9, explicitly flagged there as
    needing real calibration later, not hardcoded as authoritative.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    bboonnee authored
     
    Browse Dir »
  • 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>
    bboonnee authored
     
    Browse Dir »
  • Ports graphloop's test_isolated_coding_workspace.py/test_workspace_security.py/
    test_change_manifest.py/test_selective_apply.py into supervisor/isolation/
    (only the import path changed -- the graphlemon modules were already adapted
    verbatim from graphloop, confirmed API-identical) plus a few extra edge
    cases the originals didn't cover. 52 tests green, replacing what had been a
    single manual smoke test since these modules were staged.
    
    Adds supervisor/deploy_supervisor.py: process lifecycle only (PID file,
    atomic status file, start/stop/is_running), no deploy decision logic yet --
    that's U-037+'s job, injected via the poll_fn hook run_foreground() calls on
    a schedule. PID/status conventions adapted (not imported) from graphloop's
    hermes_watchdog.py atomic-write pattern. Fail-closed: any exit path
    (normal, SIGTERM, or an exception from poll_fn) unconditionally writes
    status='stopped' and removes the PID file, so is_running() never lies.
    RealSubprocessLifecycleTest actually spawns and stops a real
    `python3 -m supervisor.deploy_supervisor` process rather than mocking it,
    per this Unit's own "能独立启停" Done criterion.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    bboonnee authored
     
    Browse Dir »

  • Establishes the first commit for graphlemon before U-001 work begins:
    architecture/SDD docs (including the PH5 self-hosting design already
    folded into 01-M/03-C/04-P/mvcp-contract v1.7.0), the A-1 spike results,
    the graphloop-adapted supervisor/isolation utilities, PH3 wireframe
    prototypes (now cross-referenced from 02-V), and this session's
    development-plan review (U-024 dependency split, U-035 regression
    requirement, new U-004.5 e2e-harness unit, src/ directory convention).
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    bboonnee authored
     
    Browse Dir »