Root cause: aiui fuses three components with different lifecycles into one binary whose lifetime hangs on a fragile proxy (mcp-stdio child-count + 60s grace) instead of the real signal (is Claude Desktop alive). The remote path additionally lacks the resurrection / cold-start retry the local path has. Independent code analysis + Codex diagnosis converged; three pivotal facts externally validated.
Target: v0.5.0 — a clean break from the 0.4.x lifecycle whack-a-mole line. Wire protocol extended but kept backward-compatible with 0.4.x Python bridges (companion still serves the blocking POST /render for old remotes). 1.0.0 reserved for "proven stable in real use".
Full spec (authoritative, guards against drift): docs/architecture/stabilization-plan.md.
Steps — each independently shippable, sequential 1→4
Cross-cutting
Invariants (I1–I8) and per-step file lists: see the stabilization plan doc.
Root cause: aiui fuses three components with different lifecycles into one binary whose lifetime hangs on a fragile proxy (mcp-stdio child-count + 60s grace) instead of the real signal (is Claude Desktop alive). The remote path additionally lacks the resurrection / cold-start retry the local path has. Independent code analysis + Codex diagnosis converged; three pivotal facts externally validated.
Target: v0.5.0 — a clean break from the 0.4.x lifecycle whack-a-mole line. Wire protocol extended but kept backward-compatible with 0.4.x Python bridges (companion still serves the blocking
POST /renderfor old remotes).1.0.0reserved for "proven stable in real use".Full spec (authoritative, guards against drift):
docs/architecture/stabilization-plan.md.Steps — each independently shippable, sequential 1→4
host_should_exit() = uninstall/update || !is_claude_desktop_running(). Gate the last-child grace edge on Claude.app liveness (no objc bridge, no poll tick). Window-X = hide (not exit).ExitRequesteddefault-deny. Child-counter demoted to telemetry + start-trigger. → branchfix/host-lifetime-invariantpkill -f aiui-mcp(mid-call crash + cross-session blast radius). Pin applies at next natural spawn; cooperativewire_versionfloor instead of external kill./render+ bridge parity.POST /renderreturns{id}immediately; bounded long-pollGET /render/{id}. Both bridges identical (cold-start poll, progress notifications, error classification: TCP-refused vs connected-but-no-HTTP vs 401). Closes the ReadError class.RemoteForward? multi-session-per-remote needed?). Drop single-occupancy 409; one window per render, each carrying a human-legible session identifier (sessionfield set by caller; remote bridge auto-injects its hostname assession_origin).Cross-cutting
Invariants (I1–I8) and per-step file lists: see the stabilization plan doc.