- the is_running() fast-fail fix (v1.3.91) verified cold backend-http-ready
23.5s -> 3.86s and warm 5.0s -> 3.32s on the signed build, no 9Router regression
- strip the [perf] bg entry logs from mcp/skill/settings/service/9router now that
the stall is diagnosed; keep Apps.py per-lifespan timing but only print a
lifespan over 50ms plus the total (cheap regression tripwire)
- document the root cause + fix + before/after in docs/perf/winv2/README.md
- faulthandler on the signed cold build caught the asyncio loop frozen in
socket.create_connection inside is_running() (process.py): a synchronous
httpx.get to "localhost:20128" called ~5x on the boot path before 9Router is up
- on Windows a dead-port connect to "localhost" stalls ~7s each (tries ::1 first,
loopback refusal is slow), freezing the loop ~18s so uvicorn could not answer
the health probe -> cold backend-http-ready was ~23s
- fix: probe 127.0.0.1 with a 0.3s TCP timeout first (measured 306ms vs ~7s), only
HTTP-confirm when the port is open; 9Router binds 0.0.0.0 so reachability is
unchanged. drop the faulthandler diagnostic from main.py
- v1.3.89 cold proved two stacked stalls: ~5s in is_running() (sync httpx) and a
bigger ~13s BEFORE any background task runs, a silent event-loop freeze
- add faulthandler.dump_traceback_later(7s, repeat) to a temp file so a dump lands
inside the 13s window and names the exact synchronous call the loop is stuck in
- add an entry log to settings._boot_router_then_sync (first startup bg task)
- diagnostic only; reverted once the stall is pinned
- instrumented cold v1.3.88 proved the lifespans are 141ms even cold; the ~18s
cold gap is a backgrounded create_task blocking the event loop AFTER lifespan
startup but BEFORE uvicorn reports ready (what the health probe waits on)
- add [perf] entry/segment logs to the post-startup background tasks: mcp refresh,
skill refresh, 9router ensure (+ prelude bisection), and svc._post/_post_or_spool
- logging only, no behavior change; next cold log names the exact blocking call
- service lifespan was awaiting ensure_9router (~7.4s, up to ~18s cold) on the boot path
- dispatch already ensures it lazily; serialize ensure_running so no double-spawn
- cuts warm backend-ready ~9-10s toward ~2-3s
- frontend-only apps no longer need git bash; kills the [WinError 2] preview failure
- windows-only, falls back to bash run.sh for backend apps or when vite is absent
- _link_node_modules falls back symlink -> junction -> copy (no admin needed)
- _ensure_warm_cache prefers a pre-extracted resources tree, zero first-app extract (#9 item 2)
- falls back to tar/npm when absent so mac/older builds are unchanged; add tests