mustflow 2.117.0 → 2.118.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +1 -0
  2. package/dist/cli/commands/run/args.js +5 -0
  3. package/dist/cli/commands/run/execution.js +7 -6
  4. package/dist/cli/commands/run/preview.js +6 -5
  5. package/dist/cli/commands/run/receipt.js +1 -0
  6. package/dist/cli/commands/run.js +2 -1
  7. package/dist/cli/commands/workspace.js +216 -81
  8. package/dist/cli/i18n/en.js +1 -0
  9. package/dist/cli/i18n/es.js +1 -0
  10. package/dist/cli/i18n/fr.js +1 -0
  11. package/dist/cli/i18n/hi.js +1 -0
  12. package/dist/cli/i18n/ko.js +1 -0
  13. package/dist/cli/i18n/zh.js +1 -0
  14. package/dist/cli/lib/manifest-lock.js +10 -1
  15. package/dist/cli/lib/repo-map.js +5 -0
  16. package/dist/cli/lib/run-context.js +136 -0
  17. package/dist/cli/lib/run-root-trust.js +22 -15
  18. package/dist/cli/lib/validation/index.js +8 -0
  19. package/dist/core/config-loading.js +107 -14
  20. package/dist/core/run-receipt.js +1 -0
  21. package/dist/core/workspace-command-authority.js +98 -0
  22. package/package.json +1 -1
  23. package/schemas/run-receipt.schema.json +10 -0
  24. package/schemas/workspace-command-catalog.schema.json +3 -0
  25. package/schemas/workspace-command-fragments.schema.json +2 -0
  26. package/schemas/workspace-status.schema.json +4 -1
  27. package/schemas/workspace-verification-plan.schema.json +3 -0
  28. package/templates/default/common/.mustflow/config/mustflow.toml +2 -0
  29. package/templates/default/i18n.toml +14 -2
  30. package/templates/default/locales/en/.mustflow/skills/INDEX.md +12 -3
  31. package/templates/default/locales/en/.mustflow/skills/ai-game-asset-production/SKILL.md +209 -0
  32. package/templates/default/locales/en/.mustflow/skills/ai-game-asset-production/references/asset-contract-validation.md +106 -0
  33. package/templates/default/locales/en/.mustflow/skills/ai-game-asset-production/references/raster-alpha-atlas-checklist.md +109 -0
  34. package/templates/default/locales/en/.mustflow/skills/ai-game-asset-production/references/tile-animation-checklist.md +98 -0
  35. package/templates/default/locales/en/.mustflow/skills/connection-lifecycle-integrity-review/SKILL.md +302 -0
  36. package/templates/default/locales/en/.mustflow/skills/connection-lifecycle-integrity-review/references/connection-fault-injection-resource-lifetime-validation.md +277 -0
  37. package/templates/default/locales/en/.mustflow/skills/connection-lifecycle-integrity-review/references/node-stream-transport-lifecycle-checklist.md +381 -0
  38. package/templates/default/locales/en/.mustflow/skills/memory-lifetime-review/SKILL.md +94 -2
  39. package/templates/default/locales/en/.mustflow/skills/memory-lifetime-review/references/cross-runtime-memory-stream-reproduction-harness.md +289 -0
  40. package/templates/default/locales/en/.mustflow/skills/memory-lifetime-review/references/node-memory-resource-backpressure-diagnostics.md +369 -0
  41. package/templates/default/locales/en/.mustflow/skills/routes.toml +23 -0
  42. package/templates/default/manifest.toml +17 -1
@@ -0,0 +1,277 @@
1
+ # Connection Fault-Injection and Resource-Lifetime Validation
2
+
3
+ Use this reference when a connection-lifecycle review must design, execute, or judge repeated
4
+ fault injection, soak tests, resource-growth evidence, regression gates, or root-cause attribution.
5
+ The lifecycle model remains in `node-stream-transport-lifecycle-checklist.md`; this document owns
6
+ the validation campaign and the strength of the resulting claim.
7
+
8
+ The concrete API examples target Node.js HTTP/1.1 `http` and `net`, plus HTTP/2 `http2`. Preserve
9
+ the test structure and evidence model in other runtimes, but replace runtime-specific APIs and
10
+ thresholds only through a predeclared repository or release contract.
11
+
12
+ ## Contents
13
+
14
+ 1. [Evidence boundaries and common misdiagnoses](#1-evidence-boundaries-and-common-misdiagnoses)
15
+ 2. [Harness isolation and request ledger](#2-harness-isolation-and-request-ledger)
16
+ 3. [Common failure gates](#3-common-failure-gates)
17
+ 4. [Repeated fault and soak matrix](#4-repeated-fault-and-soak-matrix)
18
+ 5. [Termination-method side effects](#5-termination-method-side-effects)
19
+ 6. [Compatibility and regression matrix](#6-compatibility-and-regression-matrix)
20
+ 7. [Lifecycle implementation requirements](#7-lifecycle-implementation-requirements)
21
+ 8. [Root-cause attribution](#8-root-cause-attribution)
22
+ 9. [Mitigation and containment decisions](#9-mitigation-and-containment-decisions)
23
+ 10. [Resolution claim gate](#10-resolution-claim-gate)
24
+
25
+ ## 1. Evidence boundaries and common misdiagnoses
26
+
27
+ Do not equate RSS growth with retained application objects. RSS also contains allocator
28
+ fragmentation, native TLS buffers, runtime arenas, and pages that the allocator has not returned to
29
+ the operating system. Pair heap evidence with external memory, array buffers, native handles, file
30
+ descriptors, and protocol ownership.
31
+
32
+ Do not count `TIME_WAIT` as an open application socket. It represents a connection the kernel is
33
+ remembering after close. Sustained `CLOSE_WAIT`, by contrast, is strong evidence that the process
34
+ observed peer FIN but did not complete its local close path. Confirm the owning process and socket
35
+ before assigning cause.
36
+
37
+ Do not treat a `destroy()` call as cleanup completion. It is an abortive action that can discard
38
+ buffered data, fail or strand callbacks, and destroy sibling work on a shared HTTP/2 session.
39
+ Require terminal settlement, resource release, and physical close observation separately.
40
+
41
+ Keep these evidence classes distinct:
42
+
43
+ - configured repeated test;
44
+ - configured controlled benchmark;
45
+ - configured soak or chaos lane;
46
+ - heap or retained-object diagnostic;
47
+ - native handle, file-descriptor, or TCP-state diagnostic;
48
+ - packet or infrastructure-path evidence;
49
+ - static lifecycle review;
50
+ - manual-only evidence; and
51
+ - missing evidence.
52
+
53
+ Never promote a static review or one green reproducer into a soak, production, or root-cause claim.
54
+
55
+ ## 2. Harness isolation and request ledger
56
+
57
+ Separate the system under test, clean-load generator, raw TCP or HTTP fault peer, and network-fault
58
+ injector into different processes or isolation units. Measure only the system under test. On Linux,
59
+ a dedicated cgroup and PID namespace can prevent harness descriptors and timers from contaminating
60
+ the process ledger. Treat namespaces, native RST helpers, packet capture, and network emulation as
61
+ configured or manual-only infrastructure; this reference does not authorize raw commands.
62
+
63
+ Generate FIN through a graceful peer write close. Generate RST with a runtime-supported reset API
64
+ or a narrowly scoped native helper whose behavior is proven. A proxy that disconnects a connection
65
+ tests disconnect handling; it does not prove packet loss, reordering, or blackhole behavior. Use a
66
+ kernel-level network emulator when the configured environment owns those profiles.
67
+
68
+ Assign every logical request:
69
+
70
+ - a monotonic request identifier;
71
+ - deterministic payload bytes and SHA-256;
72
+ - intended and observed byte counts;
73
+ - an idempotency key where side effects are possible;
74
+ - monotonic timestamps for creation, connect, headers, body, completion, cancel, timeout, error,
75
+ and physical close; and
76
+ - exactly one terminal classification: succeeded, expected fault, or unexpected failure.
77
+
78
+ Require the closed-cohort conservation rule:
79
+
80
+ ```text
81
+ started = succeeded + expected_faulted + unexpected_failed
82
+ outstanding = 0
83
+ ```
84
+
85
+ Sample at a declared interval, normally one second for long-running Node campaigns:
86
+
87
+ - RSS, heap used, external memory, and array-buffer bytes;
88
+ - file descriptors or native handles and TCP state counts;
89
+ - active timers and runtime resource categories;
90
+ - HTTP/1 agent busy and free socket counts;
91
+ - HTTP/2 session and stream counts;
92
+ - pending promises and write callbacks; and
93
+ - event-loop delay and clean-request latency.
94
+
95
+ Use `process.getActiveResourcesInfo()` only as a resource-category observation. It does not prove
96
+ ownership. Private runtime APIs such as `process._getActiveHandles()` may support test diagnostics
97
+ when explicitly configured, but must not become production cleanup logic or the sole proof.
98
+
99
+ Establish clean offered load before injecting faults. A useful default is 70% of the maximum clean
100
+ throughput that still satisfies the p99 SLO. Mix faulted and clean requests so the campaign exposes
101
+ cross-request damage. Run fixed campaigns with at least three predeclared independent seeds; never
102
+ rerun until a favorable seed appears.
103
+
104
+ ## 3. Common failure gates
105
+
106
+ Zero-tolerance correctness rules are not environment calibration points:
107
+
108
+ - no crash, OOM kill, assertion, uncaught exception, or unhandled rejection;
109
+ - no successful request with a byte-length or SHA-256 mismatch;
110
+ - no idempotency key committed twice and no cancelled request later committed as success;
111
+ - no duplicate terminal transition, common finalizer, or object `close` observation;
112
+ - every accepted write callback or promise settles exactly once; and
113
+ - every closed-cohort request reaches exactly one terminal outcome.
114
+
115
+ Use the following numeric defaults only when the repository has not declared a stricter gate and
116
+ the harness can measure the named quantity consistently. Record any override before the run.
117
+
118
+ | Surface | Default failure gate |
119
+ | --- | --- |
120
+ | Settlement | After load stops, any outstanding request, promise, or write callback remains after `max(2 * longest timeout, 30s)` |
121
+ | Write callback | Settlement exceeds the operation timeout plus 250ms |
122
+ | Fixed-run heap | After cooldown, heap exceeds clean baseline by 16MiB |
123
+ | Fixed-run RSS | After cooldown, RSS exceeds clean baseline by 64MiB |
124
+ | Fixed-run heap slope | The lower bound of the 95% confidence interval across 500-request quiescent snapshots exceeds 256 bytes per request |
125
+ | Fixed-run handles | After explicitly closing agents and HTTP/2 sessions, descriptors exceed baseline by 3 or active TCP exceeds baseline by 1 |
126
+ | `CLOSE_WAIT` | Any additional connection persists for at least 60 seconds |
127
+ | Soak heap slope | After the first hour, one-minute medians exceed 0.5MiB/hour in two consecutive 30-minute windows |
128
+ | Soak external slope | External plus array buffers exceed 1MiB/hour under the same rule |
129
+ | Soak RSS slope | RSS exceeds 2MiB/hour under the same rule |
130
+ | Soak hard cap | RSS grows by `max(128MiB, 25% of baseline)` or heap grows by 64MiB |
131
+ | Handle slope | FD, TCP connection, or HTTP/2 stream growth exceeds 0.2/hour; streams must return to zero and sessions to the configured pool size after faults stop |
132
+ | Clean-request isolation | Unexpected error rate exceeds 0.01% or p99 exceeds clean baseline by 25% |
133
+ | Post-fix performance | Throughput falls below 95%, p99 rises above 115%, or CPU per request rises above 110% of the pre-fix comparator |
134
+ | Owned buffer | Queued bytes exceed the declared limit plus one maximum chunk and 64KiB |
135
+ | Timeout accuracy | Fires over 50ms early or later than the deadline plus `max(250ms, 2 * event-loop p99)` |
136
+
137
+ Keep two different teardown modes. Leak campaigns explicitly close agents and sessions before the
138
+ final baseline. Pool-reuse campaigns intentionally leave the pool open and judge only its declared
139
+ steady state. Mixing them falsely labels healthy keep-alive sockets as leaks.
140
+
141
+ ## 4. Repeated fault and soak matrix
142
+
143
+ Run each applicable fault for HTTP/1.1 cleartext, HTTP/1.1 TLS, and HTTP/2 TLS, in both client and
144
+ server roles when the product supports them. A release-level claim must include the production TLS
145
+ and proxy path or explicitly state that this evidence is missing.
146
+
147
+ | Fault | Repeated campaign | Soak campaign | Additional oracle |
148
+ | --- | --- | --- | --- |
149
+ | Slow peer | Headers at 1 byte/250ms, body at 1KiB/500ms, with some permanent stalls; 5,000 fault requests and 100 concurrent slow connections | Six hours with 50 slow connections and 5% slow arrivals | Header and body idle deadlines fire within tolerance; inbound queues stay bounded; clean p99 rises no more than 20%; no handles remain after slow peers close |
150
+ | Disconnect by phase | Disconnect after connect, at 50% headers, after headers, at 25/50/75/99% body, after response headers, and at 25/50/75/99% response body; 20,000 evenly distributed attempts | Six hours with 10% phased disconnects | Partial requests never succeed; files, transactions, locks, timers, and listeners release within five seconds of terminal observation |
151
+ | FIN | Complete message, truncated declared body, and mid-response FIN; 10,000 per position | Six hours with 5% random FIN | Complete framing may succeed; truncated framing must fail; `end` plus `close` never finalizes twice |
152
+ | RST | Same positions as FIN; separate TCP RST from HTTP/2 `RST_STREAM(CANCEL)`; 10,000 TCP cases and 20,000 stream resets | Six hours with 5% random resets | No unfinished work succeeds after TCP RST; stream reset does not fail sibling streams or close a healthy session |
153
+ | Reader stops | Never read, or stop after 64KiB and 1MiB, while peer streams 100MiB; 2,000 cases and 50 stuck readers | Six hours with 20 stuck readers | No enqueue after backpressure without drain; callbacks settle; owned buffers and external memory remain bounded |
154
+ | Writer stops | Declare a large body, send 0/1/50/99%, then leave the connection open; 5,000 cases and 100 stalled writers | Six hours with 50 stalled writers | Body idle timeout retires handler, parser, timer, and transport; partial body never becomes success |
155
+ | Timeout boundary | Connect, headers, body idle, response idle, total, keep-alive, HTTP/2 stream, and session timeouts at `T-100`, `T-10`, `T+10`, and `T+100ms`; 2,000 per timeout | Six hours with 1% of requests near each boundary | No early timeout and no stale timer kills the next request on a reused socket |
156
+ | Unconsumed response | No listener, resume, destroy, or cancel for 0-byte, 1KiB, 1MiB, and endless chunked bodies; 10,000 cases and 100 concurrent bodies | Six hours with 5% unconsumed responses | Lease expiry performs bounded discard or cancel; HTTP/1 busy pool does not stick at its maximum; HTTP/2 sibling p99 rises no more than 10% |
157
+ | Mid-stream cancel | Cancel uploads and downloads at chunk boundaries; 20,000 cases | Six hours with 10% random cancellation | Cancelled HTTP/1 sockets are not reused; HTTP/2 cancels only the stream; partial data never succeeds |
158
+ | Server restart | 200 graceful and 200 hard restarts with 200 in-flight requests; use GOAWAY for graceful HTTP/2 | Six hours, restarting every five minutes and alternating graceful and hard | No accepts after drain starts; grace-eligible work reaches 99.9% success; old process exits within grace plus five seconds; hard-restart client operations settle within their own deadlines |
159
+ | Delay and loss | Directional 200ms plus or minus 50ms, 1% random loss, periodic 5% burst loss, 0.1% reorder, 0.1% duplicate, then a mixed profile; 100,000 requests per profile | Six hours per profile and 24 hours mixed | Zero successful-body corruption and duplicate effects; idempotent success at least 99.9% after at most one allowed retry; retry amplification at most 1.2; no automatic retry for non-idempotent work without a safe key; throughput at least 70% of the same-RTT clean control |
160
+
161
+ TCP packet loss does not directly imply the same request failure rate because retransmission hides
162
+ many losses. Use reset, blackhole, and half-open profiles when the goal is request failure rather
163
+ than prolonged latency and resource ownership.
164
+
165
+ ## 5. Termination-method side effects
166
+
167
+ | Change | Risk | Required contract |
168
+ | --- | --- | --- |
169
+ | `destroy()` | Discards writable buffers, can race callbacks, prevents HTTP/1 reuse, and can kill every stream in an HTTP/2 session | Admit abortive termination once; retire only an HTTP/1 connection whose message boundary is untrusted; destroy an HTTP/2 session only for connection-level transport or protocol failure |
170
+ | `cancel()` | A shared signal cancels unrelated requests; retry after partial delivery can duplicate non-idempotent effects; error reasons can be flattened | Use request-scoped cancellation, preserve the reason, forbid unsafe automatic replay, and cancel an HTTP/2 stream rather than its session |
171
+ | `pause()` | A paused stream can retain a socket forever and consume HTTP/2 flow-control credit; a peer-idle timer can contradict application-owned pause | Give pause an owner, high/low-water hysteresis, and a deadline that ends in resume or cancel; charge application pause to a total-processing deadline rather than peer-idle time |
172
+ | `resume()` | Automatic drain can silently discard a body, hold endless chunked responses, and hide trailers or body errors | Auto-drain only under an explicit body-discard contract; cap bytes and time; exclude `HEAD`, `204`, and `304` from body-discard logic |
173
+ | Buffer cap | Truncation can report corrupt data as success; compressed size misses expansion; per-layer high-water marks do not form one process cap | Count compressed and expanded bytes plus every application-owned queue; fail explicitly on overflow; bound overshoot by one chunk plus 64KiB |
174
+ | Timeout | Completion can race a stale timeout; socket-owned timers can kill a later pooled request; stream timeout can destroy an HTTP/2 session | Separate connect, headers, body idle, response idle, total, keep-alive, stream, and session timers; remove request timers before pool return |
175
+ | Forced callback settlement | Runtime callbacks and error events can both complete the same logical write, or neither may do so | Track write identity and settle through one once-only business completion gate; a watchdog diagnoses missing ownership but does not replace it |
176
+
177
+ `finish` proves only that the local writable handed bytes to a lower layer. It does not prove peer
178
+ receipt. Use peer-observed length and digest, or an application-level acknowledgement, for delivery
179
+ integrity.
180
+
181
+ ## 6. Compatibility and regression matrix
182
+
183
+ Preserve documented public contracts and legitimate user behavior, not accidental nanosecond event
184
+ order, infinite sockets, or missing callbacks. Adding automatic cancellation, default timeouts, or
185
+ body-size limits is a compatibility change. Use opt-in behavior, a deprecation period, or a major
186
+ version boundary where the existing public contract permits late consumers or slow streams.
187
+
188
+ Minimum regression coverage:
189
+
190
+ - body sizes 0, 1, 63KiB, 64KiB, 65KiB, 1MiB, and 100MiB over HTTP/1 and HTTP/2, with exact length,
191
+ SHA-256, headers, and trailers;
192
+ - success, peer FIN, peer RST, local cancel, and timeout event contracts, asserting partial order and
193
+ counts rather than one exact event array;
194
+ - 100,000 writes around backpressure, destroy, and timeout, with issued callbacks equal to settled
195
+ callbacks and no pending callback after the deadline;
196
+ - a 100MiB producer against a 1KiB/s consumer, with no enqueue after `write() === false` before drain;
197
+ - 10,000 healthy HTTP/1 requests, failing if socket reuse drops by more than five percentage points;
198
+ - 10,000 damaged HTTP/1 requests, proving zero reuse of parser-unsafe sockets and no response-byte
199
+ contamination of the next request;
200
+ - HTTP/2 rounds with 100 concurrent streams and 10 cancellations, proving zero sibling errors,
201
+ zero session closes, and no more than 10% sibling p99 degradation;
202
+ - no-body `HEAD`, `204`, `304`, and zero-length responses, proving no unnecessary body lease;
203
+ - 200 graceful restart rounds with correct GOAWAY retry and no duplicate side effect;
204
+ - preserved public error class, code, abort reason, and timeout reason; and
205
+ - post-suite quiescence at no more than baseline plus three descriptors, baseline plus one TCP
206
+ connection, zero pending callback, and zero HTTP/2 streams.
207
+
208
+ ## 7. Lifecycle implementation requirements
209
+
210
+ Route timeout, abort, stream error, socket close, and shutdown through one idempotent finalizer. The
211
+ first terminal reason wins; later contenders remain secondary diagnostics. Remove request timers and
212
+ listeners, settle callbacks, finalize body and stream disposition, perform the narrowest
213
+ protocol-appropriate close, and decide pool eligibility last.
214
+
215
+ Reuse HTTP/1 only after a complete message boundary with no parser or framing error. Cancel HTTP/2
216
+ at stream scope unless transport, TLS, protocol, GOAWAY, or connection-level flow-control evidence
217
+ invalidates the session. Give every pause and every body lease a named owner and bounded end state.
218
+
219
+ ## 8. Root-cause attribution
220
+
221
+ Repeat the same deterministic fault schedule through four progressively smaller layers:
222
+
223
+ 1. full application;
224
+ 2. minimal program using the candidate library;
225
+ 3. minimal program using only runtime core APIs; and
226
+ 4. raw transport across both direct and production-like infrastructure paths.
227
+
228
+ Classify only from differential evidence:
229
+
230
+ | Class | Confirmation | Primary action |
231
+ | --- | --- | --- |
232
+ | Application | Full app reproduces, minimal library handler does not, and retainers point to app maps, listeners, timers, closures, bodies, or callbacks | Fix lifecycle ownership and body consumption; temporarily isolate or limit the suspect endpoint |
233
+ | Library | Minimal library program reproduces, core-only program does not, or a library-version bisect identifies the boundary | Upgrade to a fixed release, apply a bounded vendor fix, or exactly pin the last known good version |
234
+ | Runtime | Core-only program reproduces and runtime-version evidence identifies a fix or regression | Upgrade to the fixed runtime or exactly pin the last known good runtime after compatibility tests |
235
+ | Environment | Direct path is clean but a load balancer, mesh, NAT, firewall, kernel, container, or proxy path reproduces with packet or configuration evidence | Correct timeout, keep-alive, conntrack, descriptor, NAT, or proxy-buffering policy; do not hide it with indiscriminate destroy calls |
236
+
237
+ An unconsumed body is an application defect when the library contract requires consume or cancel
238
+ and the caller does neither. It becomes a library defect only when documented cancellation still
239
+ leaves the pool slot or HTTP/2 stream retained.
240
+
241
+ ## 9. Mitigation and containment decisions
242
+
243
+ Treat these as default incident thresholds only when adopted by the owning operational policy:
244
+
245
+ - above 70% of the process resource budget, or under six hours to projected exhaustion, halve
246
+ suspect concurrency and limit new-connection rate and request-body size;
247
+ - above 85%, or under 30 minutes to exhaustion, stop new admission, drain gracefully, then restart;
248
+ - isolate a request when it can retain over 5% of process memory, a paused HTTP/2 stream reduces
249
+ sibling throughput by over 10%, or one tenant holds over 20% of pool capacity for a sustained
250
+ period; and
251
+ - use separate workers, pools, tenant sessions, or bounded queues for the isolation boundary.
252
+
253
+ A restart, traffic limit, or exact-version pin can prevent exhaustion while root cause remains. Mark
254
+ it as mitigation, not permanent resolution. Never leave periodic restarts as the only long-term
255
+ design when retained ownership still grows monotonically.
256
+
257
+ ## 10. Resolution claim gate
258
+
259
+ Call the issue permanently resolved only when all applicable evidence exists:
260
+
261
+ 1. The deterministic reproducer fails 20 of 20 runs before the fix. For a probabilistic defect,
262
+ run at least 30,000 pre-fix trials and require the lower bound of its 95% failure-rate confidence
263
+ interval to exceed the declared failure threshold.
264
+ 2. The same reproducer succeeds 20 of 20 runs after the fix. For a probabilistic lane, require zero
265
+ failures in at least 30,000 post-fix trials. Run the full correctness suite three times with
266
+ independent seeds and preserve throughput, p99, CPU, integrity, and callback gates.
267
+ 3. Each applicable fault passes a six-hour soak and the mixed profile passes 24 hours. If the
268
+ historical median time to failure exceeds eight hours, use the longer of 24 hours or three times
269
+ that median. Accelerating event count does not waive the 24-hour wall-clock floor for this gate.
270
+ 4. Retained objects, open handles, version bisect, or reproducible infrastructure mechanics identify
271
+ the owner and causal path.
272
+
273
+ If only the reproducer is green, report a fix candidate. If soak passes without causal ownership,
274
+ report symptom suppression or partial evidence. If traffic limits or restart flatten the graph,
275
+ report mitigation. Never label unavailable long-running, packet, heap, native-handle, or
276
+ production-path evidence as passed; keep it manual-only or missing under the selected repository's
277
+ command contract.
@@ -0,0 +1,381 @@
1
+ # Node Stream and Transport Lifecycle Checklist
2
+
3
+ Use this reference for Node.js `net.Socket`, HTTP/1.1, HTTP/2, Web Streams, Undici, stream
4
+ adapters, connection pools, and process-shutdown reviews. Re-check version-sensitive details against
5
+ the current official documentation and repository tests before making durable compatibility claims.
6
+
7
+ ## Contents
8
+
9
+ 1. [Layered state model](#1-layered-state-model)
10
+ 2. [Events, actions, and terminal causes](#2-events-actions-and-terminal-causes)
11
+ 3. [Event path contracts](#3-event-path-contracts)
12
+ 4. [Request, body, reader, and reuse rules](#4-request-body-reader-and-reuse-rules)
13
+ 5. [Resource and conservation invariants](#5-resource-and-conservation-invariants)
14
+ 6. [Backpressure and buffer budgets](#6-backpressure-and-buffer-budgets)
15
+ 7. [Retainer and event-loop reference audit](#7-retainer-and-event-loop-reference-audit)
16
+ 8. [Verification and fault injection](#8-verification-and-fault-injection)
17
+ 9. [Server and client shutdown](#9-server-and-client-shutdown)
18
+ 10. [Acceptance gate](#10-acceptance-gate)
19
+ 11. [Official references](#11-official-references)
20
+
21
+ ## 1. Layered state model
22
+
23
+ Do not model the whole lifecycle as `OPEN -> CLOSING -> CLOSED`. TCP read and write sides close
24
+ independently. HTTP request completion, response-body completion, pool return, and physical socket
25
+ close are different facts. HTTP/2 stream cancellation normally must not kill the session.
26
+
27
+ Use orthogonal dimensions when applicable:
28
+
29
+ ```text
30
+ TransportState = CONNECTING | OPEN | ABORTING | CLOSED
31
+ ReadState = OPEN | EOF_PENDING_CONSUME | ENDED | ERRORED
32
+ WriteState = OPEN | END_REQUESTED | FINISHED | ERRORED
33
+ PoolState = UNPOOLED | BUSY | IDLE | QUARANTINED
34
+
35
+ RequestState = QUEUED | WRITING | WAITING_HEADERS | BODY_OPEN | SETTLING | SETTLED
36
+ BodyDisposition = NONE | CONSUMED | DISCARDED | CANCELLED | FAILED
37
+ ReaderState = NONE | LOCKED | CANCEL_PENDING | CANCELLED | RELEASED
38
+ ```
39
+
40
+ Interpret `ReadState=ENDED, WriteState=OPEN` as remote half-close and
41
+ `ReadState=OPEN, WriteState=FINISHED` as local half-close. Do not explode the cross-product into a
42
+ single enum unless a smaller protocol truly forbids independent progress.
43
+
44
+ ## 2. Events, actions, and terminal causes
45
+
46
+ Keep Node stream events separate from local actions:
47
+
48
+ | Symbol | Meaning |
49
+ | --- | --- |
50
+ | `E` | socket `end`; readable EOF became observable after buffered data consumption |
51
+ | `F` | socket `finish`; writable `end()` completed flushing to the underlying system |
52
+ | `X` | socket `error` |
53
+ | `K` | socket `close`; the underlying resource closed |
54
+ | `D` | one state-machine-approved `destroy()` or `resetAndDestroy()` action |
55
+
56
+ `destroy()` is not an event. A graceful FIN path can complete with `D=0`. Require connection-record
57
+ finalization exactly once and abortive destroy admission at most once.
58
+
59
+ Normalize termination causes, for example:
60
+
61
+ ```text
62
+ NORMAL_LOCAL_FIN | NORMAL_REMOTE_FIN | PEER_RESET | TIMEOUT | ABORT_SIGNAL
63
+ READER_CANCEL | WRITER_ERROR | CLIENT_FORCE | SERVER_FORCE | HALF_CLOSE_TIMEOUT
64
+ BODY_ABANDONED | PROTOCOL_ERROR | CONNECT_ERROR
65
+ ```
66
+
67
+ Route all abortive entry points through one `tryTerminate(cause, mode)`-style boundary. Atomically
68
+ store the first cause as primary. Retain later timeout, abort, reset, or writer contenders as
69
+ secondary diagnostics only. Do not let them repeat destroy, callback settlement, registry removal,
70
+ or terminal metrics.
71
+
72
+ Finalize a physical connection from the physical close observer. Do not remove it from the active
73
+ registry immediately after `destroy()`, because the descriptor or handle can still be live. A
74
+ request may settle earlier after request-scoped cleanup.
75
+
76
+ ## 3. Event path contracts
77
+
78
+ The table assumes `E` and `F` have not already happened. Preserve earlier events; after a transport
79
+ error, do not invent later successful `E` or `F` events.
80
+
81
+ | Path | Required relation | `(E,F,X,K,D)` | Reuse |
82
+ | --- | --- | ---: | --- |
83
+ | Graceful FIN, either side first | `E` and `F` once each, both before `K`; their mutual order can vary | `(1,1,0,1,0)` | no |
84
+ | Peer reset | normalize peer-reset error, then `X -> K` | `(0,0,1,1,0)` | no |
85
+ | Idle timeout with local abort | timeout notification, terminal admission, timer cleanup, `D -> X -> K` | `(0,0,1,1,1)` | no |
86
+ | AbortSignal after socket ownership | terminal admission, listener removal, `D -> X -> K` | `(0,0,1,1,1)` | no |
87
+ | Abort before socket allocation | request settles cancelled; no physical socket events | `(0,0,0,0,0)` | n/a |
88
+ | Safe body discard or HTTP/2 stream cancel | settle stream or body, release lock; keep physical connection | `(0,0,0,0,0)` | conditional |
89
+ | HTTP/1.1 body boundary unrecoverable | quarantine, `D -> K` or adapter-defined `D -> X -> K` | transport-dependent | no |
90
+ | Writer error | fail all accepted writes once, `D -> X -> K`; no new `F` | `(0,0,1,1,1)` | no |
91
+ | Local force close without error object | `D -> K` | `(0,0,0,1,1)` | no |
92
+ | Half-close deadline | prior `E`, then timeout `D -> X -> K`; no new `F` | `(1,0,1,1,1)` | no |
93
+ | Paused readable, bounded drain succeeds | consume remaining data, then graceful finish and close | `(1,1,0,1,0)` | no |
94
+ | Paused readable, drain deadline fails | abort before EOF is observable, then `X -> K` | `(0,0,1,1,1)` | no |
95
+ | Pending writes finish gracefully | stop new writes, settle accepted writes, `F`; consume peer EOF, then `K` | `(1,1,0,1,0)` | no |
96
+ | Pending writes aborted | stop new writes, fail accepted writes once, `D -> X -> K`; no `F` | `(0,0,1,1,1)` | no |
97
+ | Unconsumed body before lease deadline | keep request body open and connection busy | `(0,0,0,0,0)` | not yet |
98
+ | Lease discard succeeds | confirm message boundary, settle body, return connection idle | `(0,0,0,0,0)` | yes |
99
+ | Lease discard fails | quarantine and retire the stream or connection | transport-dependent | no |
100
+
101
+ Do not force a total order between `E` and `F` on graceful full close. Local and remote FIN progress
102
+ independently. Require their valid partial order relative to physical close.
103
+
104
+ For Node HTTP objects, do not confuse request or response completion with socket completion:
105
+
106
+ - A successful keep-alive response can emit response `end` and request `close` while the socket
107
+ stays pooled.
108
+ - `ClientRequest` `finish` means request bytes were handed to the operating system, not accepted by
109
+ the server.
110
+ - `ServerResponse` `finish` does not prove client receipt.
111
+ - Premature response close has its own response `aborted`, request `close`, response error, and
112
+ response close surface; confirm exact supported ordering in the target Node version.
113
+
114
+ ## 4. Request, body, reader, and reuse rules
115
+
116
+ Return an HTTP/1.1 connection to an idle pool only when all required facts are true:
117
+
118
+ ```text
119
+ transportState == OPEN
120
+ poolState == BUSY
121
+ parserAtMessageBoundary == true
122
+ bodyDisposition in {CONSUMED, DISCARDED}
123
+ pendingWriteCount == 0
124
+ reservedBufferBytes == 0
125
+ primaryCause == null
126
+ readState == OPEN
127
+ writeState == OPEN
128
+ protocolCorruption == false
129
+ ```
130
+
131
+ Quarantine a connection after premature close, framing or content-length mismatch, parser error,
132
+ reset, or unrecoverable body abandonment. Do not trust `writable`, `readable`, or destroyed flags as
133
+ proof that the next response starts at a clean boundary.
134
+
135
+ Apply HTTP/2 cancellation at stream scope. Escalate to session quarantine only for connection-level
136
+ GOAWAY policy, session error, flow-control corruption, or another session-wide violation.
137
+
138
+ Every response body needs a lease:
139
+
140
+ ```text
141
+ BodyLease = {
142
+ openedAt,
143
+ deadline,
144
+ maxDiscardBytes,
145
+ discardTimeout,
146
+ disposition,
147
+ readerOwner,
148
+ connectionId,
149
+ }
150
+ ```
151
+
152
+ When a body is abandoned, attempt bounded discard only if framing and remaining size make it safe.
153
+ Apply byte and wall-clock limits together. Cancel or retire the narrowest safe protocol scope when
154
+ discard fails.
155
+
156
+ `reader.cancel()` completion and `reader.releaseLock()` are separate facts. Settle pending reads or
157
+ cancel, then release the lock in a finally-like boundary. Give cancel cleanup its own deadline when
158
+ the underlying source can stall.
159
+
160
+ Protocol defaults:
161
+
162
+ | Protocol | Cancellation policy |
163
+ | --- | --- |
164
+ | HTTP/1.1, known small remainder | bounded discard, then parser-boundary confirmation |
165
+ | HTTP/1.1, unknown or excessive remainder | quarantine and retire the socket |
166
+ | HTTP/2 | reset the stream; keep the session if session invariants remain valid |
167
+ | Raw TCP | retire the connection unless the application protocol defines cancel framing |
168
+ | WebSocket | use close handshake or protocol-error handling; do not map reader cancel blindly |
169
+
170
+ ## 5. Resource and conservation invariants
171
+
172
+ Track and assert the following where the implementation owns the evidence:
173
+
174
+ | ID | Invariant |
175
+ | --- | --- |
176
+ | I1 | Every connection record reaches physical close and finalizes at most once. |
177
+ | I2 | `connections_created = connections_active + connections_closed`. |
178
+ | I3 | Graceful paths accept no destroy action; abortive paths accept at most one. |
179
+ | I4 | Each stream `end`, `finish`, `error`, and `close` event is counted at most once. |
180
+ | I5 | After terminal transport error, no new successful end or finish transition is accepted. |
181
+ | I6 | Closed connections remain in neither active registry nor pool indexes. |
182
+ | I7 | Every request has exactly one success, cancellation, or failure result. |
183
+ | I8 | Request settlement leaves no request timer, listener, retry token, or body lease. |
184
+ | I9 | Late callbacks cannot re-enter user code after request settlement or generation change. |
185
+ | I10 | Every accepted write settles once as success or failure; shutdown leaves none pending. |
186
+ | I11 | Application-owned reserved buffers remain within request, connection, and global budgets. |
187
+ | I12 | Every body reaches consumed, discarded, cancelled, or failed within its policy deadline. |
188
+ | I13 | Reader and writer locks do not outlive their operation or request. |
189
+ | I14 | Connections without a confirmed message boundary are never reused. |
190
+ | I15 | Retry queues are bounded by item count, bytes, age, and attempt; unsafe bodies are not replayed. |
191
+ | I16 | Intended shutdown leaves no unexpected refed timer, socket, or native handle. |
192
+ | I17 | Server shutdown progresses only through admission-stop, drain, force, and closed phases. |
193
+
194
+ Useful conservation equations:
195
+
196
+ ```text
197
+ requests_started = requests_active + requests_succeeded + requests_failed + requests_cancelled
198
+ writes_accepted = writes_pending + writes_succeeded + writes_failed
199
+ body_leases_opened = body_leases_active + body_consumed + body_discarded
200
+ + body_cancelled + body_failed
201
+ buffer_bytes_reserved = buffer_bytes_current + buffer_bytes_released
202
+ ```
203
+
204
+ Update registry state and related counters in the same transition boundary. Otherwise exceptions or
205
+ interleaving can leave durable metric divergence.
206
+
207
+ Use bounded metric dimensions such as reason, protocol, role, and mode. Put connection or request
208
+ identifiers in logs and traces, not metric labels.
209
+
210
+ ## 6. Backpressure and buffer budgets
211
+
212
+ A stream high-water mark starts backpressure; it is not a whole-system hard limit. Account for every
213
+ application-owned queue:
214
+
215
+ ```text
216
+ B_owned = B_node_readable + B_node_writable + B_web_readable + B_web_writable
217
+ + B_http_parser + B_transform + B_application_queue + B_retry_body
218
+ + B_discard_buffer + B_observability_capture
219
+ ```
220
+
221
+ Reserve byte budget before enqueue or body replication. On reservation failure, pause the producer,
222
+ reject admission, or apply the declared drop policy. Discovering an overage from metrics after
223
+ enqueue is not enforcement.
224
+
225
+ Count logical reservations consistently when Buffer slices share backing stores. Observe RSS,
226
+ external memory, kernel socket buffers, and TLS native buffers separately; JavaScript accounting
227
+ cannot promise an absolute process RSS cap.
228
+
229
+ After `write()` reports backpressure, wait for drain or abort instead of continuing to allocate.
230
+ Track every accepted write by sequence or ownership token and settle its callback or promise once.
231
+
232
+ ## 7. Retainer and event-loop reference audit
233
+
234
+ Build explicit `ConnectionScope` and `RequestScope` ledgers. Register cleanup at creation time.
235
+
236
+ Connection scope examples:
237
+
238
+ - core socket close and error observers;
239
+ - socket timeout and half-close timer;
240
+ - pool idle timer;
241
+ - native close waiter;
242
+ - connection buffer reservations.
243
+
244
+ Request scope examples:
245
+
246
+ - AbortSignal listener remover;
247
+ - request and response listeners;
248
+ - request deadline timer;
249
+ - retry token;
250
+ - body lease;
251
+ - reader and writer ownership;
252
+ - pending write records;
253
+ - operation buffer reservations.
254
+
255
+ Make disposal idempotent. Request cleanup must not remove connection-core observers needed to record
256
+ physical close.
257
+
258
+ Audit long-lived roots and their capture paths:
259
+
260
+ ```text
261
+ global dispatcher -> origin pool -> client -> socket -> parser/listener -> response body -> reader
262
+ module singleton -> request map -> request scope -> timer/retry/body buffer
263
+ libuv timer -> callback closure -> retry queue -> request body stream -> descriptor
264
+ server -> connection registry -> socket -> listener closure -> request/response
265
+ long-lived AbortSignal -> listener -> request scope -> reader/writer/socket
266
+ AsyncLocalStorage -> store -> request/response -> body buffers
267
+ diagnostics subscriber -> correlation map -> request -> body/socket
268
+ unconsumed tee branch -> queued chunks -> backing buffer -> response
269
+ ```
270
+
271
+ `once` listeners can still live forever if the event never arrives. `Promise.race` does not cancel
272
+ the losing I/O. `unref` permits process exit but does not remove the object, listener, or descriptor.
273
+ Finalization registries are leak canaries, not deterministic cleanup.
274
+
275
+ For stream completion helpers, check whether listeners are retained after completion and use the
276
+ runtime-supported cleanup option or returned cleanup function. Do not reuse failed pipeline streams
277
+ unless the implementation contract proves their listener and error state is clean.
278
+
279
+ Separate heap retention from event-loop liveness. An unresolved promise alone does not keep Node
280
+ alive, but a promise reachable from a native callback, registry, listener, timer, or retry job can
281
+ retain a large request graph.
282
+
283
+ ## 8. Verification and fault injection
284
+
285
+ Use a pre/post active-resource baseline where configured. `process.getActiveResourcesInfo()` can
286
+ show active resource types but not ownership, so it supplements rather than replaces the ledger.
287
+ Normalize version-dependent resource type names into categories such as timeout, TCP, TLS, DNS,
288
+ pipe, and message port.
289
+
290
+ Run natural-exit scenarios in a child process without `process.exit()` when the repository has a
291
+ configured harness. Pair natural exit with open-handle or descriptor evidence so `unref` cannot hide
292
+ an open resource.
293
+
294
+ For heap proof, warm up, repeat a bounded workload, collect only configured snapshot or retention
295
+ evidence, and compare retained request, connection, body lease, reader, and buffer counts with active
296
+ ownership plus fixed instrumentation slack. Pair heap evidence with native handle or descriptor
297
+ evidence when the platform supports it.
298
+
299
+ Fault matrix axes:
300
+
301
+ | Axis | Cases |
302
+ | --- | --- |
303
+ | Time | before connect, after connect, before/after headers, before first body byte, before last byte, around finish |
304
+ | Cause | FIN, RST, timeout, abort, reader cancel, writer error, client kill, server kill |
305
+ | Read | flowing, paused, locked reader, pending read, unconsumed body |
306
+ | Write | none, pending, backpressured, before drain, immediately after end request |
307
+ | Protocol | raw TCP, HTTP/1.1 keep-alive, HTTP/2 multiplexing |
308
+ | Race | abort+timeout, RST+timeout, body end+cancel, shutdown+client reset |
309
+ | Load | one connection, pool limit, many slow consumers, full retry queue |
310
+
311
+ The oracle must include event trace and counts, primary cause, accepted destroy count, request
312
+ result, registry and pool sizes, pending writes, body disposition, lock state, and peak reserved
313
+ bytes. “An error happened” is not a sufficient oracle.
314
+
315
+ ## 9. Server and client shutdown
316
+
317
+ Server sequence:
318
+
319
+ ```text
320
+ ACCEPTING
321
+ -> stop new accepts
322
+ -> DRAINING
323
+ -> retire idle HTTP/1.1 connections
324
+ -> give active responses and pending writes a grace deadline
325
+ -> send HTTP/2 GOAWAY and reject new streams when applicable
326
+ -> FORCING
327
+ -> retire remaining HTTP connections
328
+ -> retire separately tracked WebSocket, CONNECT, HTTP/2, or upgraded transports
329
+ -> wait for physical close observation
330
+ -> CLOSED
331
+ ```
332
+
333
+ Do not assume a generic HTTP close-all helper owns upgraded or HTTP/2 transports. Keep an explicit
334
+ registry for every transport class the application accepts.
335
+
336
+ Client sequence:
337
+
338
+ ```text
339
+ OPEN
340
+ -> stop new request admission and retry scheduling
341
+ -> fail queued requests
342
+ -> give active body consume or cancel a grace deadline
343
+ -> close idle pool members
344
+ -> force remaining active connections after deadline
345
+ -> verify every request and physical connection finalized
346
+ -> CLOSED
347
+ ```
348
+
349
+ ## 10. Acceptance gate
350
+
351
+ Require the applicable subset of these conditions before calling lifecycle handling complete:
352
+
353
+ ```text
354
+ duplicate_finalize_total == 0
355
+ duplicate_destroy_attempt_total == 0
356
+ duplicate_stream_event_total == 0
357
+ post_error_event_total == 0
358
+ connection_conservation_error == 0
359
+ cleanup_residual_total == 0
360
+ late_callback_total == 0
361
+ writes_pending == 0 after shutdown
362
+ reuse_after_error_total == 0
363
+ stream_locks_active == 0 after request settlement
364
+ body_leases_active == 0 after request settlement
365
+ buffer_peak_bytes <= configured global buffer limit
366
+ open handles return to baseline plus intentional pool capacity
367
+ child process exits naturally without process.exit()
368
+ ```
369
+
370
+ Do not require metrics or diagnostics that the implementation does not own. Report the missing
371
+ evidence and preserve the bounded invariant rather than fabricating proof.
372
+
373
+ ## 11. Official references
374
+
375
+ - [Node.js Streams](https://nodejs.org/api/stream.html)
376
+ - [Node.js Net](https://nodejs.org/api/net.html)
377
+ - [Node.js HTTP](https://nodejs.org/api/http.html)
378
+ - [Node.js Web Streams](https://nodejs.org/api/webstreams.html)
379
+ - [Node.js Timers](https://nodejs.org/api/timers.html)
380
+ - [Node.js Process](https://nodejs.org/api/process.html)
381
+ - [Undici Diagnostics Channel](https://undici.nodejs.org/api/DiagnosticsChannel)