Skip to content

fix(dev): Effect-native boot probes + unit process-death harness - #631

Merged
elliotBraem merged 3 commits into
mainfrom
dev-session-death-contract
Oct 26, 2026
Merged

elliotBraem merged 3 commits into
mainfrom
dev-session-death-contract

Conversation

@elliotBraem

Copy link
Copy Markdown
Contributor

What

Two things from the process-death regression investigation:

1. Boot-probe wedge fix (probeHttpOk)

The dev session's readiness probe awaited fetchRaw — a promise bridge around a nested Effect.runPromise — from inside the probe fiber. The nested runtime intermittently wedged (repro'd 5/5 clean, 4/4 pass with a single stderr write shifting timing), stalling boots until the ~90s probe deadline self-healed. This is a plausible contributor to slow CI boots in the framework regression stats.

  • probeHttpOk is now Effect-native (fetchWithRetryEff + orElseSucceed(false)) — same deadline, same all-failures→false semantics, no nested runtime.
  • The host-manifest fetch (same nested pattern, remote-host boot path) got the same treatment with fail-loud status/validation preserved.
  • The graceful-overrun force backstop (armForce/disarmForce) is now a forked Effect.sleep fiber via Effect.forkDetach (a scoped fork inside a finalizer attaches to the closing scope and dies with it) instead of raw setTimeout — the last non-Effect resource management on the teardown path.

2. Unit process-death harness

tests/unit/dev-session-processes.test.ts drives the real runDevSession against real detached group-leader children and watcher grandchildren (30 MB fixture node servers instead of the 5–6 GB full stack):

  • graceful shutdown kills children + grandchildren, frees ports, cleans the registry
  • a child ignoring SIGTERM is escalated to SIGKILL by teardown
  • double-graceful escalates straight to force
  • orphan watchdog force-exits on reparenting (the deps.ppid seam)
  • startup adoption reaps a dead session's live orphans and prunes the registry

This complements the existing mock-based dev-session.test.ts (session semantics) and process-kill.test.ts (kill primitives) with the real-topology layer between them and the slow Go suite.

Verification

  • New suite + all affected suites: 30/30 green locally (~35s)
  • pnpm run lint and pnpm run typecheck pass
  • CI runs the new file automatically via framework-tests (pnpm --filter everything-dev test, gated on packages/everything-dev/ changes)

Follow-ups (next PRs)

  • Confined stacks in the Go framework suite: most tests boot a fixture bos.app.ts with the fake services instead of all 9 real workspaces; keep one full-stack boot (real rspack watchers) as the safety net. Then CI slots can go from 1 back to 2–3 and the 35-min timeout drops (the RAM-saturation rationale is gone).

probeHttpOk awaited fetchRaw's nested Effect.runPromise from inside the
probe fiber; the nested runtime intermittently wedged and stalled boots
until the 90s deadline. The probe and the host-manifest fetch now run
Effect-native (same deadline, fail-loud semantics preserved). The
session's graceful-overrun force backstop becomes a forkDetach
Effect.sleep fiber instead of raw setTimeout.

New unit suite drives the real runDevSession against real detached
children and watcher grandchildren: graceful teardown, SIGKILL
escalation, double-graceful force, orphan watchdog, and startup
adoption — the process-death contract at unit speed.
Every test but one boots the death-contract fixture — a minimal authored
config (tests/regression/fixtures/death-contract) whose five services are
fake node servers — instead of the full 9-child stack. The full
CLI/planning/codegen/registry/TUI path is real; boots reach a registry
entry in ~4s instead of 60-120s (the orphan test: 213s -> 4.6s), and the
RAM-saturation class that forced serial CI slots is gone.

TestSignalQuitKillsEverything/sigterm stays full-stack: the safety net
proving the real rspack/rsbuild watcher grandchildren die. The survivor
scan also names leaked fake-services now. CI: two stack slots instead of
serial, timeout 35 -> 12 minutes.
@elliotBraem

Copy link
Copy Markdown
Contributor Author

Stacked Phase 2 (310e1e0): every framework test but one now boots the confined death-contract fixture (minimal authored config, five fake node services) instead of the full 9-child stack. Orphan test: 213s → 4.6s. One full-stack boot (sigterm) remains as the real-rspack-watcher safety net. CI: 2 stack slots instead of serial, timeout 35 → 12 min. Lint/typecheck green; Go vet clean.

…tion

Addresses the effecttsgo flat-map-conditional-to-filter-or-fail lint
warning — the res.ok and manifest-shape checks become Effect.filterOrFail
with a terminal Effect.map, no identity succeed branches.
@elliotBraem
elliotBraem merged commit 1e43560 into main Oct 26, 2026
20 checks passed
@elliotBraem
elliotBraem deleted the dev-session-death-contract branch October 26, 2026 15:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant