P0.4.2 Graceful Shutdown — Verification

1. Path chosen

Path (c) — Augment inline + barrel. All implementation logic lives in src/startup.ts alongside the existing transport + DB lifecycle machinery (Wave A inline lock). src/shutdown.ts is a re-export-only barrel providing a stable public surface.

Rationale documented in docs/audits/p0-4-2-graceful-shutdown-audit.md §4.


2. Gate evidence

2.1. Test run

Test Suites: 14 passed, 14 total
Tests:       691 passed, 691 total
Snapshots:   0 total
Time:        ~30s

2.2. New shutdown tests (14 tests)

# Test Describe block
1 registers a handler that is called on shutdown registration
2 returns a callable deregister function registration
3 deregister removes the handler — not called after deregister registration
4 registering the same fn twice calls it twice registration
5 two handlers: B (registered last) runs before A LIFO ordering
6 three handlers [A, B, C]: shutdown calls in order C, B, A LIFO ordering
7 async handlers are awaited sequentially preserving LIFO order LIFO ordering
8 throwing handler does not abort remaining handlers error isolation
9 throwing handler logs [Shutdown] handler[i] failed: error isolation
10 shutdown() never throws even when a handler throws error isolation
11 SIGTERM triggers registered handler + exit(0) signal integration
12 SIGINT triggers registered handler + exit(0) signal integration
13 idempotency: handler called exactly once when shutdown() called twice signal integration
14 clearShutdownHandlers resets the list — no handler invoked signal integration

All 14 tests: PASS

2.3. Existing startup.test.ts tests

The 950-line src/__tests__/startup.test.ts was not modified. Its 37 tests continue to pass in the full suite run. No regressions.

2.4. Coverage

  startup.ts   |  100  |  93.22  |  100  |  100  | 193,357,434-435
  • Uncovered branches (193, 357, 434-435): pre-existing null-coalescing fallbacks (options = {}, loadServerModule().stop default, opts?.exit default). These branches were uncovered before P0.4.2 and are not introduced by this task.
  • src/shutdown.ts: pure re-export barrel — no executable logic, 100% trivially.

2.5. Lint

npm run lint  →  exit 0  (no warnings, no errors)

2.6. Build

npm run build (tsc)  →  exit 0  (no TypeScript errors)

3. Acceptance criteria check

AC Status
registerShutdownHandler(fn) registers a cleanup function DONE — src/startup.ts exports registerShutdownHandler; re-exported from src/shutdown.ts
SIGINT/SIGTERM: handlers called in reverse registration order DONE — LIFO loop in shutdown() step 4; tests 5–7 verify
DB connection closed before process exit DONE (pre-existing P0.2.3 feature) — closeDbFn() in step 2 of shutdown()
In-flight MCP requests allowed to complete (max 5s timeout) DONE (pre-existing P0.2.3 feature) — Promise.race in step 1 of shutdown()
Exit code 0 clean shutdown, 1 on error DONE (pre-existing P0.2.3 feature) — gracefulSignalExit returns exit(0) / exit(1)
src/shutdown.ts module exists DONE — created as re-export barrel
Test: mock SIGTERM → verify DB close + handler called DONE — tests 11, 12 cover signal → handler; DB close covered by startup.test.ts

All 7 acceptance criteria: SATISFIED


4. Files changed

File Action Lines
src/startup.ts Modified +74 lines (handler list, registerShutdownHandler, clearShutdownHandlers, step 4 in shutdown(), clearShutdownHandlers() call in __resetForTests)
src/shutdown.ts Created 29 lines (barrel re-export)
src/__tests__/shutdown.test.ts Created ~250 lines (14 tests, 4 describe blocks)
docs/audits/p0-4-2-graceful-shutdown-audit.md Created 182 lines
docs/contracts/p0-4-2-graceful-shutdown-contract.md Created 186 lines
docs/packets/p0-4-2-graceful-shutdown-packet.md Created 190 lines
docs/verification/p0-4-2-graceful-shutdown-verification.md Created this file

5. Commit history

SHA Step Message
a3866373 1. Audit audit(p0-4-2-graceful-shutdown): inventory existing inline shutdown + decide path
84e5c8a9 2. Contract contract(p0-4-2-graceful-shutdown): handler API + signal semantics + exit codes
9295ede9 3. Packet packet(p0-4-2-graceful-shutdown): execution plan
1a3c6685 4. Implement feat(p0-4-2-graceful-shutdown): shutdown handlers + 5s timeout + exit codes
(this commit) 5. Verify verify(p0-4-2-graceful-shutdown): tests + gate evidence

6. Residual risks

Risk Assessment
Handler list is module-scoped Acceptable: signal handlers are inherently process-scoped; test hermeticism ensured by clearShutdownHandlers() in __resetForTests()
Late-registered handlers during shutdown not invoked By design (snapshot before LIFO loop); documented in contract §3.1
Barrel re-export is .startup.js runtime path ESM .js extension is correct for TypeScript ESM (resolved by ts path mapping)
node_modules symlink in worktree not tracked by git Symlink is in .gitignore via node_modules/ pattern; correct behavior

Back to top

Colibri — documentation-first MCP runtime. Apache 2.0 + Commons Clause.

This site uses Just the Docs, a documentation theme for Jekyll.