• Attempts 7 and 11 both failed at exactly the same spot: process_alive read
    'ok' within milliseconds of Popen, then http_health hit ECONNREFUSED in the
    same second and gave up. Real cause: process_alive only confirms the
    process EXISTS, not that backend_server.py has finished importing modules
    and actually bound its listen socket -- a real, non-zero startup gap. The
    old check_http_health did one urlopen(timeout=X) call; that timeout only
    bounds how long to wait for a response once connected, not how long to
    wait for the port to start accepting connections at all -- ECONNREFUSED
    fires essentially instantly when nothing's listening yet, regardless of
    timeout.
    
    This touches an explicit 03-C design statement ("不重试——重试属于给坏版本
    第二次机会"), so confirmed with the user before changing it rather than
    just reinterpreting the policy unilaterally. Resolution: tolerating normal
    startup latency within ONE health-check pass isn't "retrying a failed
    check" -- check_process_alive already does exactly this (polls up to 30s
    for the pid to exist). http_health now does the same, polling within its
    own timeout for connection-refused-class errors specifically. A genuine
    non-2xx HTTP response is still a definitive answer from a live server and
    still fails immediately, no polling -- that boundary is unchanged.
    
    Two new real regression tests: a real server that starts listening 0.3s
    late is correctly waited for and passes; a real 500 response still fails
    immediately without being mistaken for "not up yet".
    
    564 tests green (441 src + 123 supervisor).
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    bboonnee authored
     
    Browse File »

  • 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 File »