redweb 0.12.0 → 0.13.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.
- package/CHANGELOG.md +166 -9
- package/README.md +177 -523
- package/bin/redweb.js +11 -20
- package/client.d.ts +7 -2
- package/config/tsconfig.json +14 -14
- package/contract.d.ts +45 -0
- package/contract.js +5 -0
- package/docs/ACTION_INPUT_VERIFICATION.md +96 -0
- package/docs/ADMISSION_TIMEOUT_VERIFICATION.md +69 -0
- package/docs/AGENT_ACCESS.md +35 -0
- package/docs/AGENT_EVALUATION.md +58 -0
- package/docs/AGENT_READY_ACCEPTANCE.md +763 -0
- package/docs/APPLICATION_RECORDER_VERIFICATION.md +50 -0
- package/docs/BENCHMARK_VERIFICATION.md +307 -0
- package/docs/BROWSER_OWNER_VERIFICATION.md +191 -0
- package/docs/CLI.md +116 -0
- package/docs/CLIENT_DEVELOPMENT.md +152 -0
- package/docs/CLIENT_POLISH_VERIFICATION.md +282 -0
- package/docs/COVERAGE_COUNTER_VALIDATION.md +109 -0
- package/docs/COVERAGE_SCOPE_AUDIT.md +1183 -0
- package/docs/DEVELOPMENT.md +79 -0
- package/docs/DIAGNOSTIC_COMPATIBILITY.md +76 -0
- package/docs/DOCUMENTATION.md +37 -0
- package/docs/FEEDBACK_COMMAND_VERIFICATION.md +228 -0
- package/docs/GETTING_STARTED.md +58 -0
- package/docs/JSX_PERFORMANCE_VERIFICATION.md +59 -0
- package/docs/LIVE_HTML.md +169 -21
- package/docs/LIVE_HTML_LOAD_VERIFICATION.md +98 -0
- package/docs/MIGRATION.md +28 -0
- package/docs/MULTIPLAYER_OPERATIONS.md +26 -4
- package/docs/ORIGINAL_RECOVERY_VERIFICATION.md +100 -0
- package/docs/PACKAGED_EXAMPLE_VERIFICATION.md +126 -0
- package/docs/POLISH_RELEASE_CHECKPOINT.md +91 -0
- package/docs/PROCESS_CLEANUP_OBSERVATION.md +61 -0
- package/docs/PROCESS_REAPING_VERIFICATION.md +30 -0
- package/docs/PRODUCTION_READINESS.md +11 -2
- package/docs/RECOVERY_CLIENT_HEAP.md +201 -0
- package/docs/RECOVERY_CODE_ATTRIBUTION.md +174 -0
- package/docs/RECOVERY_CODE_CENSUS.md +158 -0
- package/docs/RECOVERY_COMPARISON.md +103 -0
- package/docs/RECOVERY_DEOPTIMIZATION.md +169 -0
- package/docs/RECOVERY_FOLLOWUP_SPIKE.md +147 -0
- package/docs/RECOVERY_INVESTIGATION.md +229 -0
- package/docs/RECOVERY_RUNTIME_CONTROLS.md +181 -0
- package/docs/RELEASE_TRUST.md +58 -0
- package/docs/ROOM_AUTHORIZATION.md +49 -0
- package/docs/RUNTIME_DIAGNOSTICS.md +78 -0
- package/docs/SERVER_RECOVERY_CANDIDATE.md +185 -0
- package/docs/SOAK_ROTATION_OBSERVATION.md +160 -0
- package/docs/SOAK_VERIFICATION.md +154 -0
- package/docs/SOCKET_CONTRACTS.md +39 -0
- package/docs/SPLIT_RECOVERY_COVERAGE.md +83 -0
- package/docs/SPLIT_RECOVERY_ERROR_HANDLING.md +67 -0
- package/docs/STARTER_COORDINATOR_VERIFICATION.md +112 -0
- package/docs/STARTER_LIFECYCLE_VERIFICATION.md +75 -0
- package/docs/STARTER_REPORT_RETENTION.md +73 -0
- package/docs/VERIFICATION_EVIDENCE.md +2 -0
- package/docs/generated.json +2154 -0
- package/docs/guides/chatroom.md +27 -0
- package/docs/guides/http-websocket.md +28 -0
- package/docs/guides/jsx-without-react.md +26 -0
- package/docs/guides/realtime-dashboard.md +29 -0
- package/docs/guides/typed-websockets.md +26 -0
- package/docs/reference.json +1207 -0
- package/docs/snippets/room-access.tsx +51 -0
- package/docs/topics.json +21 -0
- package/examples/live-html/chatroom.js +207 -268
- package/examples/live-html/chatroom.tsx +167 -0
- package/examples/live-html/jsx-page.js +1 -1
- package/examples/live-html/jsx-page.tsx +1 -1
- package/examples/live-html/tsconfig.json +8 -7
- package/index.d.ts +170 -45
- package/index.js +2 -0
- package/jsx-dev-runtime.js +2 -2
- package/jsx-runtime.d.ts +7 -2
- package/package.json +88 -7
- package/recipes/add/artifact.test.cjs +57 -0
- package/recipes/add/live.tsx +18 -0
- package/recipes/add/socket-route.ts +24 -0
- package/recipes/chat/README.md +22 -0
- package/recipes/chat/app.test.cjs +105 -0
- package/recipes/chat/app.tsx +9 -0
- package/recipes/dashboard/README.md +43 -0
- package/recipes/dashboard/admin.ts +21 -0
- package/recipes/dashboard/app.css +16 -0
- package/recipes/dashboard/app.test.cjs +450 -0
- package/recipes/dashboard/app.tsx +86 -0
- package/recipes/dashboard/auth.ts +80 -0
- package/recipes/dashboard/cards.tsx +102 -0
- package/recipes/dashboard/rate-window.test.cjs +17 -0
- package/recipes/dashboard/store.ts +120 -0
- package/recipes/http-ws/README.md +11 -0
- package/recipes/http-ws/app.test.cjs +92 -0
- package/recipes/http-ws/app.tsx +36 -0
- package/recipes/realtime/README.md +8 -0
- package/recipes/realtime/app.test.cjs +15 -0
- package/recipes/realtime/app.tsx +28 -0
- package/recipes/shared/README.md +40 -0
- package/recipes/shared/app.css +8 -0
- package/recipes/shared/copy-assets.cjs +8 -0
- package/recipes/shared/network.cjs +59 -0
- package/recipes/shared/run-app.test.cjs +158 -0
- package/recipes/shared/run-app.ts +50 -0
- package/recipes/site/README.md +4 -0
- package/recipes/site/app.test.cjs +19 -0
- package/recipes/site/app.tsx +25 -0
- package/recipes/socket/README.md +39 -0
- package/recipes/socket/app.test.cjs +85 -0
- package/recipes/socket/app.tsx +30 -0
- package/recipes/socket/contract.ts +12 -0
- package/recipes/socket/handlers.ts +40 -0
- package/src/OwnedServerLifecycle.js +66 -0
- package/src/access/AccessPolicy.js +37 -0
- package/src/access/AuthenticationFailure.js +13 -0
- package/src/access/RequestFailure.js +33 -0
- package/src/access/failure-codes.json +25 -0
- package/src/async/BoundedOperation.js +62 -0
- package/src/cli/ActionReferences.js +193 -0
- package/src/cli/AdditionLayout.js +140 -0
- package/src/cli/FilePlan.js +94 -0
- package/src/cli/ProjectAddition.js +60 -0
- package/src/cli/ProjectConfig.js +26 -0
- package/src/cli/ProjectDoctor.js +112 -0
- package/src/cli/ProjectInitializer.js +20 -30
- package/src/cli/SourceInspector.js +207 -0
- package/src/cli/StaticSource.js +192 -0
- package/src/cli/arguments.js +62 -0
- package/src/cli/formatCommand.js +10 -0
- package/src/cli/run.js +57 -0
- package/src/cli/templates.js +86 -87
- package/src/context/RequestSnapshot.js +41 -0
- package/src/dataProperty.js +11 -0
- package/src/development/DevelopmentPageManager.js +48 -0
- package/src/development/Inspection.js +104 -0
- package/src/development/ObservedRenderer.js +42 -0
- package/src/development/description.js +35 -0
- package/src/development/loopbackRequest.js +27 -0
- package/src/development/refreshBrowser.js +96 -0
- package/src/development/refreshStyles.js +9 -0
- package/src/development/settings.js +17 -0
- package/src/docs/Documentation.js +182 -0
- package/src/htmx/ActionDefinition.js +44 -0
- package/src/htmx/Jsx.js +24 -8
- package/src/htmx/LiveHtmlServer.js +41 -19
- package/src/htmx/LivePage.js +63 -13
- package/src/htmx/PageIdentity.js +32 -0
- package/src/htmx/PageLifetime.js +37 -0
- package/src/htmx/PageManager.js +203 -74
- package/src/htmx/ReactiveRenderer.js +241 -0
- package/src/htmx/StaticExporter.js +1 -1
- package/src/htmx/TemplateRenderer.js +13 -7
- package/src/htmx/browserRuntime.js +2 -93
- package/src/htmx/metadata.js +19 -7
- package/src/validation/ActionInputError.js +12 -0
- package/src/validation/SchemaValidator.js +38 -0
- package/src/ws/AdmissionPolicy.js +24 -23
- package/src/ws/BaseSocketServer.js +53 -38
- package/src/ws/ContractValidationError.js +12 -0
- package/src/ws/HeartbeatMonitor.js +19 -7
- package/src/ws/ProtocolPolicy.js +1 -1
- package/src/ws/RoomAccess.js +82 -0
- package/src/ws/RoomRegistry.js +56 -6
- package/src/ws/RouteRuntime.js +56 -10
- package/src/ws/SocketContract.js +112 -0
- package/src/ws/SocketRoute.js +18 -0
- package/src/ws/protocol-schema.json +6 -1
- package/examples/live-html/chatroom.ts +0 -217
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Offline client code attribution
|
|
2
|
+
|
|
3
|
+
The existing private snapshots show a broader set of compiled code attached to
|
|
4
|
+
**preexisting functions and closures**, not an accumulating collection of detached
|
|
5
|
+
optimized versions. This is a narrower, supported conclusion about this diagnostic
|
|
6
|
+
client; it does not establish the entire cause of the original CI recovery failure.
|
|
7
|
+
|
|
8
|
+
An independent research agent began without inherited conversation or a proposed
|
|
9
|
+
cause. It subsequently encountered earlier investigation notes in the repository
|
|
10
|
+
and disclosed that exposure. Its structural findings were reproduced by the
|
|
11
|
+
tested offline analyzer and submitted to a separate senior critic.
|
|
12
|
+
|
|
13
|
+
No new measured workload or capture was performed for this milestone. Both inputs
|
|
14
|
+
are the unchanged Windows/Node 22.21.0, V8 12.4.254.21-node.33 snapshots documented
|
|
15
|
+
in [the capture report](RECOVERY_CLIENT_HEAP.md). The original capture command
|
|
16
|
+
failed its detailed-output size limit; its report and failure remain unchanged.
|
|
17
|
+
The new offline result is not a successful replacement recovery run.
|
|
18
|
+
|
|
19
|
+
## Function identities, not names
|
|
20
|
+
|
|
21
|
+
| Observed structure | Warm | Final |
|
|
22
|
+
| --- | ---: | ---: |
|
|
23
|
+
| Code objects with deoptimization data | 96 | 261 |
|
|
24
|
+
| Associated SharedFunctionInfo identities | 95 | 260 |
|
|
25
|
+
| Identities with one such Code object | 94 | 259 |
|
|
26
|
+
| Identities with two such Code objects | 1 | 1 |
|
|
27
|
+
| Code objects referenced by an owning closure's current code field | 96 | 261 |
|
|
28
|
+
| Feedback vectors | 487 | 532 |
|
|
29
|
+
|
|
30
|
+
The final 261 Code objects comprise 57 surviving and 204 added identities; 39 warm
|
|
31
|
+
Code identities disappeared. All 261 have an owning function identity and a
|
|
32
|
+
matching owning closure already present in the warm snapshot. The only two-code
|
|
33
|
+
group contains exactly the same two Code identities in both snapshots, both
|
|
34
|
+
referenced by current closure fields. There were no unresolved, ambiguous or
|
|
35
|
+
conflicting code/feedback associations in this pair.
|
|
36
|
+
|
|
37
|
+
"Current" means a `closure.code` reference, **not proof that the code remains valid
|
|
38
|
+
or is executing**. V8 permits attached code to be marked for deoptimization; that
|
|
39
|
+
primitive status is not available in these snapshots. See the pinned
|
|
40
|
+
[JSFunction implementation](https://github.com/nodejs/node/blob/v22.21.0/deps/v8/src/objects/js-function.cc).
|
|
41
|
+
This analysis cannot match the exact invalidated versions in the separate earlier
|
|
42
|
+
logging run to these objects.
|
|
43
|
+
|
|
44
|
+
| Source of added Code objects | Objects | Code plus instruction-stream shallow bytes |
|
|
45
|
+
| --- | ---: | ---: |
|
|
46
|
+
| Node built-ins | 180 | 299,360 |
|
|
47
|
+
| ws | 22 | 57,296 |
|
|
48
|
+
| Realtime test harness | 2 | 8,112 |
|
|
49
|
+
|
|
50
|
+
These byte totals describe added Code objects plus their attached streams, not
|
|
51
|
+
exclusively newly allocated nodes: one added Node Code references a surviving
|
|
52
|
+
2,752-byte instruction stream. There are 204 added Code objects but 203 added
|
|
53
|
+
instruction-stream identities.
|
|
54
|
+
|
|
55
|
+
Of 45 added feedback vectors, 44 belong to Node built-ins (9,744 shallow bytes)
|
|
56
|
+
and one to the snapshot observer (184 bytes). Observer allocations are explicitly
|
|
57
|
+
included, not silently attributed to workload code.
|
|
58
|
+
|
|
59
|
+
The deduplicated union of Code, instruction streams and immediate deoptimization,
|
|
60
|
+
relocation and source-position metadata grows from 201,752 to 861,664 shallow
|
|
61
|
+
bytes (+659,912). Feedback vectors are excluded from this union. These are
|
|
62
|
+
selected graph-node self-sizes, not exclusive retained sizes, all code-related
|
|
63
|
+
memory, or bytes that can be subtracted from the recovery gate. Per-function
|
|
64
|
+
metadata can overlap; adding those rows would double-count shared objects.
|
|
65
|
+
|
|
66
|
+
## A concrete persistent path
|
|
67
|
+
|
|
68
|
+
`ws.initAsClient` has the same single closure and SharedFunctionInfo identity in
|
|
69
|
+
both snapshots. Its baseline code remains in `function_data`; one new
|
|
70
|
+
deoptimization-bearing Code occupies the closure's code field at the final
|
|
71
|
+
capture. That new Code and instruction stream occupy 20,056 shallow bytes, with
|
|
72
|
+
15,304 bytes of associated immediate deoptimization data. There is no collection
|
|
73
|
+
of old detached optimized versions for this function in the final snapshot.
|
|
74
|
+
|
|
75
|
+
The analyzer finds this unique chain in both snapshots, with all eight node
|
|
76
|
+
identities unchanged:
|
|
77
|
+
|
|
78
|
+
```text
|
|
79
|
+
process
|
|
80
|
+
-- property/_events --> events object
|
|
81
|
+
-- property/message --> worker IPC listener
|
|
82
|
+
-- internal/context --> listener context
|
|
83
|
+
-- context/WebSocket --> ws WebSocket constructor
|
|
84
|
+
-- internal/context --> constructor context
|
|
85
|
+
-- internal/previous --> enclosing context
|
|
86
|
+
-- context/initAsClient --> initAsClient closure
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The listener is sourced from `recovery-split-worker.cjs`; its context also holds
|
|
90
|
+
the harness's `closeClient` and `waitFor`. Two root-reachable module objects export
|
|
91
|
+
the same WebSocket constructor in each snapshot (`ws/index.js` and
|
|
92
|
+
`ws/lib/websocket.js`). This establishes persistent worker/module reachability,
|
|
93
|
+
not a unique owner or a leftover per-connection closure.
|
|
94
|
+
|
|
95
|
+
The three unchanged objects named `Socket` are also resolved structurally:
|
|
96
|
+
|
|
97
|
+
| Role | Shallow bytes | Identifying reference |
|
|
98
|
+
| --- | ---: | --- |
|
|
99
|
+
| Cached stderr stream | 320 | `getStderr` context's `stderr` binding |
|
|
100
|
+
| TLS socket prototype | 56 | `TLSSocket.prototype` |
|
|
101
|
+
| JS stream socket prototype | 24 | `JSStreamSocket.prototype` |
|
|
102
|
+
|
|
103
|
+
Classification requires the appropriate built-in source and edge structure, not
|
|
104
|
+
the displayed size or constructor name alone. All three retain the same identity
|
|
105
|
+
and role; none is identified as a leftover workload connection. Independent
|
|
106
|
+
inspection also found 50 of the 51 unchanged HTTPParser objects reachable through
|
|
107
|
+
the parser FreeList. One remaining parser's semantic role is unresolved despite
|
|
108
|
+
a path through global handles and a native context. The reusable analyzer does
|
|
109
|
+
not classify HTTPParser ownership.
|
|
110
|
+
|
|
111
|
+
## Method and limits
|
|
112
|
+
|
|
113
|
+
`CodeAttribution.cjs` composes the existing validated graph reader. It identifies
|
|
114
|
+
SharedFunctionInfo structurally, follows Code/deoptimization-data associations,
|
|
115
|
+
cross-checks closure ownership and requires reciprocal instruction-stream links.
|
|
116
|
+
Identity groups remain separate even when function names coincide.
|
|
117
|
+
|
|
118
|
+
It does not assume that a snapshot's numerical hidden edge is a V8 field offset.
|
|
119
|
+
The [pinned snapshot generator](https://github.com/nodejs/node/blob/v22.21.0/deps/v8/src/profiler/heap-snapshot-generator.cc)
|
|
120
|
+
compacts hidden visitor indices. The outer function association is the unique
|
|
121
|
+
direct strong structural SFI reference, not nested inlined-function references
|
|
122
|
+
in the [deoptimization data](https://github.com/nodejs/node/blob/v22.21.0/deps/v8/src/objects/deoptimization-data.h).
|
|
123
|
+
Missing, conflicting, ambiguous and unsupported structures remain explicit.
|
|
124
|
+
|
|
125
|
+
Inputs retain the paired-process identity, exact runtime, phase, size and SHA-256
|
|
126
|
+
checks. Graphs are capped at 250,000 nodes and 1,500,000 edges; metadata association
|
|
127
|
+
work is capped at 2,000,000. Exceeding a bound fails instead of reporting partial
|
|
128
|
+
success. Detailed output contains at most 128 function groups (135 of 263 groups
|
|
129
|
+
are explicitly omitted here); global counts include every group. Output stays
|
|
130
|
+
within 1 MiB, uses narrow fixed source/function labels, and contains no raw object
|
|
131
|
+
IDs, arbitrary values, absolute source paths or code addresses.
|
|
132
|
+
|
|
133
|
+
To analyze this pinned capture format locally, without starting the workload:
|
|
134
|
+
|
|
135
|
+
```powershell
|
|
136
|
+
node scripts/diagnostics/HeapCodeComparison.cjs <original-report.json> <private-snapshot-directory> <new-report.json> attribution
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Output creation is exclusive; an existing output is not overwritten. Raw
|
|
140
|
+
snapshots remain private and local.
|
|
141
|
+
|
|
142
|
+
## Evidence and verification
|
|
143
|
+
|
|
144
|
+
- Implementation: `8a15569` on `codex/agent-ready`.
|
|
145
|
+
- Original report: `coverage/recovery-split-f7owK8/report.json`; SHA-256 `1b894777de9ec50f53faa7e66f144bfe4feb89793ae1bfe068365000833acbe7`.
|
|
146
|
+
- Separate offline attribution: `coverage/recovery-split-f7owK8/code-attribution.json`, 67,985 bytes; SHA-256 `af299ace748edc2c01eb02d18ab93cb256fc28a5883adb853b03e146e2ef3971`.
|
|
147
|
+
- The attribution report embeds original-report and analyzer hashes. Snapshot identities/hashes remain those in the capture report.
|
|
148
|
+
- Native verification: 29 unit and real-process/socket/snapshot tests passed in 8.304 seconds. All six snapshot modules have 100% statements, branches, functions and lines, with unchanged thresholds and no exclusions added. Integration tests use actual native snapshots, CLI processes and network traffic, without mocks.
|
|
149
|
+
- Native coverage: `coverage/code-attribution-native/coverage-final.json`; SHA-256 `fd9c8b997f5d40e7367d79c4ca793435f93e1594a8f4f91f58c3b09af47e535d`.
|
|
150
|
+
- Full regression at implementation `8a15569`: **853 tests in 82 suites passed** in 434.002 seconds, with a normal exit, pretest/generated-documentation checks and all three TypeScript configurations. The subsequently edited evidence prose is checked separately; no runtime or test inputs changed during the suite.
|
|
151
|
+
- Instrumented-library coverage: 100% across 5,445 statements, 4,044 branches, 978 functions and 4,464 lines. The owned-HTTP-shutdown test passed, without resolving its historical timeout.
|
|
152
|
+
- Full-suite JSON: `coverage/code-attribution-full-suite.json`; SHA-256 `7274abbb5915e7865f5e3082afaa32d9a08a624732e24664f916600876c961bc`.
|
|
153
|
+
- After updating the evidence prose and generated catalogue, pretest/type checks and 30 focused documentation/attribution/graph tests in four suites passed (1.241 seconds for tests).
|
|
154
|
+
|
|
155
|
+
The senior critic approved the scoped analyzer after a reciprocal-stream
|
|
156
|
+
double-counting risk was fixed and regression-tested, then independently reproduced
|
|
157
|
+
the saved attribution and approved this report's interpretation. This is not approval of
|
|
158
|
+
release acceptance or a claim that broader coordinator/worker coverage is 100%.
|
|
159
|
+
|
|
160
|
+
## Remaining decision boundary
|
|
161
|
+
|
|
162
|
+
Existing snapshots answer which functions hold this code and disfavor detached
|
|
163
|
+
version accumulation as the explanation for this captured client. They cannot
|
|
164
|
+
answer whether the attached code is marked for invalidation, prove every retained
|
|
165
|
+
byte benign, or reproduce the original Ubuntu/Node 22.23.2 shared-process failure.
|
|
166
|
+
That original fourth-storm 110.218742% result remains above the unchanged 110%
|
|
167
|
+
limit even though the final storm declined. The historical shutdown timeout also
|
|
168
|
+
remains unexplained.
|
|
169
|
+
|
|
170
|
+
There is no supported Redweb production fix to apply from these findings alone.
|
|
171
|
+
Closing the original failure requires evidence under its comparable environment
|
|
172
|
+
and protocol, not another aggregate snapshot, code-byte subtraction or repeated
|
|
173
|
+
runs selected for a pass. No production runtime, dependency, npm link, workload,
|
|
174
|
+
acceptance limit, publication, deployment or merge changed in this milestone.
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Client code-creation census — 2026-08-30
|
|
2
|
+
|
|
3
|
+
Status: concrete compilation candidates identified; **original recovery failure
|
|
4
|
+
not resolved**. This follows the [JIT control](RECOVERY_RUNTIME_CONTROLS.md).
|
|
5
|
+
|
|
6
|
+
## Method and boundaries
|
|
7
|
+
|
|
8
|
+
One predeclared run, executed once after unit/real-network tests and critic review:
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
node scripts/diagnostics/recovery-split.cjs client-code
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The server retains normal flags. Only the client adds `--log-code`,
|
|
15
|
+
`--no-log-source-code`, `--no-log-source-position`, `--no-logfile-per-isolate`
|
|
16
|
+
and `--logfile=-` to `--expose-gc`. Inherited Node options/coverage do not enter
|
|
17
|
+
measured workers. No tests ran concurrently with measurement. Runtime is unchanged:
|
|
18
|
+
Windows, Node 22.21.0, V8 12.4.254.21-node.33, ws 8.21.3.
|
|
19
|
+
|
|
20
|
+
The pinned V8 [flag definitions](https://github.com/nodejs/node/blob/v22.21.0/deps/v8/src/flags/flag-definitions.h)
|
|
21
|
+
show code logging normally enables source logging; the explicit negative flags
|
|
22
|
+
prevent that. V8's [logger implementation](https://github.com/nodejs/node/blob/v22.21.0/deps/v8/src/logging/log.cc)
|
|
23
|
+
records instruction size, code kind, logger-relative microseconds, function name
|
|
24
|
+
and location. Numeric kinds are interpreted using the exact pinned
|
|
25
|
+
[code-kind enum](https://github.com/nodejs/node/blob/v22.21.0/deps/v8/src/objects/code-kind.h).
|
|
26
|
+
Other V8 versions are rejected by the parser, not silently decoded with this enum.
|
|
27
|
+
|
|
28
|
+
Two tiny diagnostic functions are first invoked **after** the warm and final
|
|
29
|
+
samples. Their native code-creation records bracket the interval in the same V8
|
|
30
|
+
stream; JavaScript stdout markers are not mixed into it. Both are interpreted
|
|
31
|
+
functions, two instruction bytes each. The parser requires exactly one of each,
|
|
32
|
+
in order, and monotonic code-event timestamps. It rejects missing/repeated/reversed
|
|
33
|
+
boundaries, malformed records, unknown kinds, private detail records and oversized
|
|
34
|
+
or incomplete input. The existing coordinator preserves bounded logs and hashes;
|
|
35
|
+
census failure invalidates the result. Summary names/locations are limited to
|
|
36
|
+
known code sources, with other entries explicitly unclassified.
|
|
37
|
+
|
|
38
|
+
Logging, native markers and their allocations perturb this run. The census window
|
|
39
|
+
is after-warm-sample to after-final-sample, not precisely the two heap-read
|
|
40
|
+
instants. This is not acceptance-equivalent. Creation events/instruction bytes
|
|
41
|
+
are **not retained objects, retained size, total heap size, or a deoptimization
|
|
42
|
+
diagnosis**. Raw local logs still include addresses, paths and code labels; they
|
|
43
|
+
are not published as sanitized reports. No heap snapshots were taken.
|
|
44
|
+
|
|
45
|
+
## Results
|
|
46
|
+
|
|
47
|
+
All 7,400 exact reply IDs were acknowledged. Client tracked connections and server
|
|
48
|
+
client/room/session registries were empty at every sample. Both workers exited.
|
|
49
|
+
All source fingerprints remained unchanged through measurement and were rechecked
|
|
50
|
+
afterward. Every raw log's byte length, completeness and SHA-256 was independently
|
|
51
|
+
verified; reparsing its serialized census reproduced the report exactly.
|
|
52
|
+
|
|
53
|
+
The log has 4,360 code-creation records: 3,502 through the warm boundary,
|
|
54
|
+
**851 between boundaries**, and seven at/after the final boundary. Native boundary
|
|
55
|
+
timestamps are 2,873,133 and 12,657,842 microseconds. Of the interval events, 843
|
|
56
|
+
are TurboFan (1,620,872 instruction bytes) and eight baseline-tier (9,004 bytes).
|
|
57
|
+
These are cumulative creation sizes, not a memory-retention total.
|
|
58
|
+
|
|
59
|
+
| Source category | Interval creation events | Cumulative instruction bytes |
|
|
60
|
+
| --- | ---: | ---: |
|
|
61
|
+
| Node core | 653 | 1,170,660 |
|
|
62
|
+
| `ws` | 106 | 293,460 |
|
|
63
|
+
| Load-test harness and worker | 41 | 144,184 |
|
|
64
|
+
| Unclassified | 51 | 21,572 |
|
|
65
|
+
|
|
66
|
+
Largest named TurboFan groups:
|
|
67
|
+
|
|
68
|
+
| Function | Source | Events | Cumulative instruction bytes |
|
|
69
|
+
| --- | --- | ---: | ---: |
|
|
70
|
+
| `initAsClient` | `ws/lib/websocket.js:667` | 4 | 71,176 |
|
|
71
|
+
| `ClientRequest` | `node:_http_client:190` | 4 | 67,520 |
|
|
72
|
+
| `Socket._writeGeneric` | `node:net:935` | 5 | 29,520 |
|
|
73
|
+
| `onEvent` | `scripts/realtime-harness.js:13` | 5 | 28,004 |
|
|
74
|
+
| `Socket._destroy` | `node:net:808` | 4 | 27,888 |
|
|
75
|
+
| `parserOnHeadersComplete` | `node:_http_common:74` | 4 | 24,208 |
|
|
76
|
+
|
|
77
|
+
For `initAsClient`, all four events share one SharedFunctionInfo address but have
|
|
78
|
+
four distinct code addresses and instruction sizes of 16,144 / 18,860 / 17,220 /
|
|
79
|
+
18,952 bytes, at 5,508,294 / 7,818,047 / 9,753,664 / 11,684,671 microseconds.
|
|
80
|
+
That is evidence of distinct optimized versions associated with the same function
|
|
81
|
+
in this run, not just four identical records. It does not say why new versions
|
|
82
|
+
were generated, whether earlier versions survived, or which objects retained them.
|
|
83
|
+
|
|
84
|
+
### Memory observations (not a gate)
|
|
85
|
+
|
|
86
|
+
| Phase | Server heap bytes | Client heap bytes | Client code-and-metadata bytes |
|
|
87
|
+
| --- | ---: | ---: | ---: |
|
|
88
|
+
| Preconditioning | 10,190,376 | 7,030,952 | 2,860,536 |
|
|
89
|
+
| Warm | 10,190,592 | 7,005,648 | 2,834,870 |
|
|
90
|
+
| Storm 1 | 10,462,648 | 7,242,200 | 2,986,582 |
|
|
91
|
+
| Storm 2 | 10,791,136 | 7,693,424 | 3,283,972 |
|
|
92
|
+
| Storm 3 | 11,087,200 | 7,816,488 | 3,346,328 |
|
|
93
|
+
| Storm 4 | 10,964,832 | 7,853,856 | 3,355,785 |
|
|
94
|
+
| Storm 5 | 9,910,160 | 7,844,472 | 3,330,512 |
|
|
95
|
+
|
|
96
|
+
Client warm-to-final heap growth is 838,824 bytes; code-and-metadata growth is
|
|
97
|
+
495,642 bytes. Peak/final heap ratios are 112.10748812957773% and
|
|
98
|
+
111.97353906448055%. Client bytecode-and-metadata rises from 756,440 at warm to
|
|
99
|
+
756,544 at storm 1 and stays there; marker overhead is not subtracted. External
|
|
100
|
+
memory (2,331,012) and array buffers (35,458) remain constant after warm-up.
|
|
101
|
+
Server peak/final ratios are 108.79838973044941% and 97.24812846986711%; its
|
|
102
|
+
bytecode-and-metadata again drops late, from 908,728 to 205,328 bytes. None of these
|
|
103
|
+
overlapping statistics is added to/subtracted from heap to manufacture a pass.
|
|
104
|
+
|
|
105
|
+
## Evidence and verification
|
|
106
|
+
|
|
107
|
+
- Tooling commit: `200fb30` on `codex/agent-ready`.
|
|
108
|
+
- UTC run: 2026-08-30 19:46:02.966–19:46:15.721.
|
|
109
|
+
- Local directory: `coverage/recovery-split-4EG5qI` (report, samples and four logs).
|
|
110
|
+
- Report SHA-256: `8cc597cf22bdaef4b6af90943b36a4a541e49af03ec8bc43ef76fc95390c8cdb`.
|
|
111
|
+
- Client stdout: 419,734 bytes; SHA-256 `55a02d595bf57b26e39aac6cc609db49a4062df24f2876b5c2fd9766204709c4`.
|
|
112
|
+
- Other three logs: empty, complete, hashes verified.
|
|
113
|
+
- Parser source SHA-256: `759961f48151f8e74cbc6f9490bec640f9e42508023fa5e4082b866eb9c7acec`.
|
|
114
|
+
|
|
115
|
+
Fifty-nine focused unit/real-child/real-HTTP-WebSocket tests passed. The new parser
|
|
116
|
+
has **100% statements, branches, functions and lines**, in both Jest and native V8
|
|
117
|
+
coverage. The real-network test exercises logging isolation, boundaries, complete
|
|
118
|
+
hashes, source-text exclusion and listener cleanup. On other V8 versions it checks
|
|
119
|
+
explicit parser rejection, while still exercising traffic and capture; it does
|
|
120
|
+
not claim schema support for those versions.
|
|
121
|
+
|
|
122
|
+
The final-tree combined three diagnostic scripts remain below the unchanged native
|
|
123
|
+
100% gate: **80% statements/lines, 97.02% branches and 93.54% functions**. That
|
|
124
|
+
coverage command exited nonzero despite all 59 behavior tests passing. The worker
|
|
125
|
+
is 100% statements/lines/functions but 98.14% branches (cleanup-barrier timeout
|
|
126
|
+
uncovered); coordinator CLI/orchestration/error paths remain uncovered. These
|
|
127
|
+
numbers supersede the earlier pre-finalization coverage check. No thresholds or
|
|
128
|
+
exclusions changed.
|
|
129
|
+
|
|
130
|
+
Full-suite verification passed **813 tests / 77 suites** in 411.353 seconds,
|
|
131
|
+
including pretest/generated-documentation checks and all three TypeScript
|
|
132
|
+
configurations. Instrumented-library coverage is 100% for statements, branches,
|
|
133
|
+
functions and lines (5,445 statements, 4,044 branches and 978 functions, none
|
|
134
|
+
uncovered). This excludes the separately measured diagnostic scripts above.
|
|
135
|
+
Result: `coverage/recovery-code-full-suite.json`, SHA-256
|
|
136
|
+
`562e3d65c12ae5073fa51a5f8cedc25a3e0adf705fb91b22235c0e54b7603999`.
|
|
137
|
+
The previous milestone's 777-pass/one-shutdown-timeout result remains recorded;
|
|
138
|
+
the same test passing now does not explain that failure or waive it as harmless.
|
|
139
|
+
|
|
140
|
+
The senior critic independently verified source/report/log hashes, serialized
|
|
141
|
+
census reproduction, all phase measurements, grouped events and function identity.
|
|
142
|
+
Both the preflight and findings write-up were approved with no outstanding finding.
|
|
143
|
+
After updating the evidence, generated-documentation and TypeScript checks passed
|
|
144
|
+
again, followed by 63 focused documentation/diagnostic tests across four suites.
|
|
145
|
+
|
|
146
|
+
## Next causal question
|
|
147
|
+
|
|
148
|
+
The shortlist is now specific: the HTTP/WebSocket handshake and stream lifecycle,
|
|
149
|
+
plus the load-test event helpers. Investigate why these functions acquire multiple
|
|
150
|
+
optimized versions (deoptimization reasons and dependency invalidation), then
|
|
151
|
+
verify surviving code/feedback objects and retaining paths if needed. Do not
|
|
152
|
+
rewrite Redweb or its client based only on these creation counts: this native
|
|
153
|
+
load generator does not import either runtime. No acceptance workload, warm-up,
|
|
154
|
+
memory limit, production source, dependency, frozen helper, npm link, publication,
|
|
155
|
+
deployment or merge changed. Release gates remain open.
|
|
156
|
+
|
|
157
|
+
Follow-up: [client deoptimization investigation](RECOVERY_DEOPTIMIZATION.md)
|
|
158
|
+
identifies the observed invalidation mechanism without claiming retained-size proof.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Bounded Ubuntu recovery comparison
|
|
2
|
+
|
|
3
|
+
Status: the declared pair is complete; acceptance decision pending. No retries,
|
|
4
|
+
new tracing, threshold changes or replacement of the original gate followed.
|
|
5
|
+
|
|
6
|
+
## Exact execution
|
|
7
|
+
|
|
8
|
+
[Run 33346305798](https://github.com/lakam99/redweb/actions/runs/33346305798)
|
|
9
|
+
executed revision `41915b0c0ef5906eca851aaef64d80b9c198cd08` once on Ubuntu
|
|
10
|
+
24.04.4, image `20260823.283.1`, kernel `6.17.0-1022-azure`, Node 22.23.2,
|
|
11
|
+
V8 `12.4.254.21-node.56`, systemd `255.4-1ubuntu8.17`. The OS/image match the
|
|
12
|
+
historical failing runner; its kernel was not independently recorded. Hardware
|
|
13
|
+
identity or exclusive underlying hardware is not asserted.
|
|
14
|
+
|
|
15
|
+
The temporary workflow's real service probes returned exactly 0 and 7 with
|
|
16
|
+
confirmed cleanup before either measured command. It then ran, sequentially:
|
|
17
|
+
|
|
18
|
+
1. `node --expose-gc scripts/verify-recovery.js` — exit 0, 8.002-second service lifetime.
|
|
19
|
+
2. `node scripts/diagnostics/recovery-split.cjs baseline` — exit 0, 8.068-second service lifetime.
|
|
20
|
+
|
|
21
|
+
Both services became inactive with zero main/control PIDs and removed cgroups.
|
|
22
|
+
Source fingerprints before and after match, including the diagnostic report's
|
|
23
|
+
own fingerprint. All four installed client bundles match published client 0.2.0.
|
|
24
|
+
No diagnostic/workload/coverage overrides entered the measured environments.
|
|
25
|
+
The comparison used systemd descendant containment and a minimal environment,
|
|
26
|
+
unlike the historical direct-shell runner. Those differences are not concealed.
|
|
27
|
+
|
|
28
|
+
The temporary trigger was removed after collection. Its implementation remains
|
|
29
|
+
auditable in commit `41915b0`; do not restore it merely to obtain another pass.
|
|
30
|
+
|
|
31
|
+
## Heap results
|
|
32
|
+
|
|
33
|
+
Bytes measured after each phase's unchanged settling/collection protocol:
|
|
34
|
+
|
|
35
|
+
| Phase | Original combined process | Split server | Split load generator |
|
|
36
|
+
| --- | ---: | ---: | ---: |
|
|
37
|
+
| Preconditioning | 10,388,616 | 9,991,120 | 6,410,728 |
|
|
38
|
+
| Warm | 10,379,216 | 10,023,960 | 6,360,512 |
|
|
39
|
+
| Storm 1 | 10,823,136 | 10,303,776 | 6,611,032 |
|
|
40
|
+
| Storm 2 | 11,207,736 | 10,627,552 | 7,000,048 |
|
|
41
|
+
| Storm 3 | 11,390,320 | 10,801,856 | 7,119,072 |
|
|
42
|
+
| Storm 4 | 11,330,136 | 10,875,592 | 7,156,480 |
|
|
43
|
+
| Storm 5 | 10,118,552 | 9,654,040 | 7,194,432 |
|
|
44
|
+
|
|
45
|
+
The original peaked at **109.741622%** of its fixed warm baseline and finished
|
|
46
|
+
at 97.488596%, satisfying its existing 110% bound in this one run. The isolated
|
|
47
|
+
server peaked at **108.495964%** and finished at 96.309642%. The split load
|
|
48
|
+
generator reached **113.110894%**; this is not a claim that every process satisfied
|
|
49
|
+
110%. Systemd's service-accounting numbers are not substituted for Node's heap
|
|
50
|
+
samples or treated as equivalent process-memory measurements.
|
|
51
|
+
|
|
52
|
+
Both original and split retained empty server client/room/session registries.
|
|
53
|
+
The original waits for a reply per connection but does not assert each reply's
|
|
54
|
+
payload identity. The split verifier additionally checked **7,400 exact replies**:
|
|
55
|
+
client sent, client received and server received agree at every phase, client
|
|
56
|
+
registries are empty, and all recorded worker output is complete and hash-verified.
|
|
57
|
+
Its successful exit proves its stated delivery/cleanup conditions; the heap
|
|
58
|
+
percentages above were independently calculated from the actual samples.
|
|
59
|
+
|
|
60
|
+
## Contrary CI evidence remains a failure
|
|
61
|
+
|
|
62
|
+
The ordinary Node 22 job at prior source head `69ea1fb` also ran during this
|
|
63
|
+
collection, on a separate hosted VM. Its 83 suites passed (854 tests, five
|
|
64
|
+
platform-specific skips), but [job 99349863069](https://github.com/lakam99/redweb/actions/runs/33345905870/job/99349863069)
|
|
65
|
+
failed the original recovery gate: warm 10,395,344 bytes, fourth storm 11,540,472
|
|
66
|
+
bytes (**111.015778%**), final 10,069,496 bytes (96.865443%). Final recovery does
|
|
67
|
+
not erase the failed intermediate limit. The measured production runtime and
|
|
68
|
+
original recovery script did not change between those heads.
|
|
69
|
+
|
|
70
|
+
The same run's Node 20 core job separately failed the trace-mode diagnostic
|
|
71
|
+
integration test with `write EPIPE` during worker startup. Its cause is not yet
|
|
72
|
+
established; it is neither a demonstrated production socket defect nor a passing
|
|
73
|
+
test. Node 24 and lifecycle/browser/package jobs passed. These results must not
|
|
74
|
+
be condensed into a green cross-runtime release claim.
|
|
75
|
+
|
|
76
|
+
## Decision and preserved evidence
|
|
77
|
+
|
|
78
|
+
The bounded comparison supports reviewing a server-focused acceptance protocol,
|
|
79
|
+
not automatically adopting one. The proposed next step is a separately reviewed
|
|
80
|
+
server gate with explicit delivery, cleanup, shutdown and fixed memory budgets,
|
|
81
|
+
while retaining the original combined-process result as visible diagnostic
|
|
82
|
+
evidence. Maintainer approval is required before replacing any existing CI gate.
|
|
83
|
+
Until then the current gate and release blockers remain unchanged, and no merge
|
|
84
|
+
or publication is authorized by this report.
|
|
85
|
+
|
|
86
|
+
The hosted artifact `bounded-recovery-33346305798` is retained for 30 days. A local
|
|
87
|
+
copy remains under `coverage/bounded-recovery-33346305798`; no snapshots were
|
|
88
|
+
collected or uploaded. Key SHA-256 receipts:
|
|
89
|
+
|
|
90
|
+
- `recovery-comparison/inputs-before.json` (identical to `inputs-after.json`):
|
|
91
|
+
`26354cecf712c22a66a91e759b45c939a3a27f456ec9f3e72349eb11b3aeecb4`.
|
|
92
|
+
- `recovery-comparison/original.log`:
|
|
93
|
+
`af8b438e00ef0e901b4dcb598f942e50ce49c9a3fab021b5f82353ee517fc16f`.
|
|
94
|
+
- `recovery-split-8bVO1w/report.json`:
|
|
95
|
+
`106dee62dbc0dc8667e57bc07dd653034516ba7dd8927c499fedff5cabc6411b`.
|
|
96
|
+
|
|
97
|
+
The senior critic independently reproduced the ratios, delivery reconciliation,
|
|
98
|
+
sample/log hashes, input identity and recorded cleanup checks without rerunning
|
|
99
|
+
either workload. That is scoped evidence approval, not merge approval.
|
|
100
|
+
|
|
101
|
+
The original historical failure and deferred research remain described in
|
|
102
|
+
[the follow-up spike](RECOVERY_FOLLOWUP_SPIKE.md). One successful comparison is
|
|
103
|
+
not repeatability evidence and does not explain the underlying V8 cause.
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Client deoptimization investigation — 2026-08-30
|
|
2
|
+
|
|
3
|
+
Status: GC-dependent code invalidation identified in the measured client;
|
|
4
|
+
**original recovery acceptance remains open**.
|
|
5
|
+
This continues the [client compilation census](RECOVERY_CODE_CENSUS.md).
|
|
6
|
+
|
|
7
|
+
## Predeclared method
|
|
8
|
+
|
|
9
|
+
One measured run, after tests and senior critic preflight:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
node scripts/diagnostics/recovery-split.cjs client-deopt
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This adds only `--log-deopt` to the earlier client-code mode. The worker source,
|
|
16
|
+
seven phases, 7,400 connections, batch size, settling, explicit collections,
|
|
17
|
+
server policies and original recovery limits remain unchanged. Only the client
|
|
18
|
+
is logged; the server retains normal flags. No concurrent tests or coverage
|
|
19
|
+
instrumentation enter measurement. Source-text logging remains disabled.
|
|
20
|
+
|
|
21
|
+
The pinned [V8 logger](https://github.com/nodejs/node/blob/v22.21.0/deps/v8/src/logging/log.cc)
|
|
22
|
+
distinguishes `dependency-change` from `deopt-eager` and `deopt-lazy`. A dependency
|
|
23
|
+
invalidation record does not prove an active stack bailed out. The logged code
|
|
24
|
+
address is correlated with earlier creations, following native `code-move` events.
|
|
25
|
+
Unknown moves clear stale destination associations; unmatched events stay counted
|
|
26
|
+
as unmatched. Raw deoptimization locations are not published. Reasons outside a
|
|
27
|
+
small known-label allowlist stay counted as unclassified. Native warm/final
|
|
28
|
+
boundaries determine the interval by log order, not by adjacent JavaScript output.
|
|
29
|
+
Zero record counts in earlier `client-code` logs cannot establish absence of
|
|
30
|
+
deoptimization: those runs did not enable `--log-deopt`. This run cannot
|
|
31
|
+
retroactively identify why every compiled version changed in earlier runs.
|
|
32
|
+
|
|
33
|
+
The [collector implementation](https://github.com/nodejs/node/blob/v22.21.0/deps/v8/src/heap/mark-compact.cc)
|
|
34
|
+
marks code with the reason `weak objects` when an embedded weak object is unmarked,
|
|
35
|
+
then clears embedded objects from that code. A separate
|
|
36
|
+
[dependency mechanism](https://github.com/nodejs/node/blob/v22.21.0/deps/v8/src/objects/dependent-code.cc)
|
|
37
|
+
uses `code dependencies`. Neither reason identifies a particular retained user
|
|
38
|
+
object. The [deoptimizer](https://github.com/nodejs/node/blob/v22.21.0/deps/v8/src/deoptimizer/deoptimizer.cc)
|
|
39
|
+
routes these invalidation records to the native logger. These details apply to
|
|
40
|
+
the pinned Node 22.21.0 / V8 12.4.254.21-node.33 runtime, not every Node release.
|
|
41
|
+
|
|
42
|
+
The census does not model code deletion, prove simultaneous code survival, or
|
|
43
|
+
calculate retained size. Deoptimization records report instruction-stream object
|
|
44
|
+
size; creation records report instruction size. Those values are intentionally
|
|
45
|
+
not added, equated or subtracted from the heap measurements.
|
|
46
|
+
|
|
47
|
+
## Results
|
|
48
|
+
|
|
49
|
+
The single run acknowledged all 7,400 exact reply IDs and left client tracked
|
|
50
|
+
connections and server clients/rooms/sessions empty at every sample. Both workers
|
|
51
|
+
exited. Source fingerprints, output lengths and hashes were independently
|
|
52
|
+
rechecked; reparsing the serialized census exactly reproduced the report.
|
|
53
|
+
|
|
54
|
+
Within the native after-warm/after-final window (2,950,836–12,789,228 microseconds),
|
|
55
|
+
V8 logged 856 code creations and **703 deoptimization/invalidation records**:
|
|
56
|
+
|
|
57
|
+
| Record kind | Reason | Records |
|
|
58
|
+
| --- | --- | ---: |
|
|
59
|
+
| Dependency invalidation | `weak objects` | 698 |
|
|
60
|
+
| Eager bailout | `wrong map` | 4 |
|
|
61
|
+
| Eager bailout | `not a Smi` | 1 |
|
|
62
|
+
|
|
63
|
+
There were 813 such records over the whole log: 109 before the warm boundary,
|
|
64
|
+
703 in the window and one at/after the final boundary. All interval code addresses
|
|
65
|
+
matched known optimized creations, including through 116 logged code moves;
|
|
66
|
+
there were no unmatched moves or unclassified reason labels. Fifty-three interval
|
|
67
|
+
records have unclassified source/function labels: matching a code address is not
|
|
68
|
+
the same as classifying its printable name. No records were dropped to make the
|
|
69
|
+
named-source totals look complete.
|
|
70
|
+
|
|
71
|
+
### The handshake function's replacement sequence
|
|
72
|
+
|
|
73
|
+
`ws.initAsClient` has four optimized code creations for one SharedFunctionInfo
|
|
74
|
+
identity. Each specific code address is subsequently invalidated with the reason
|
|
75
|
+
`weak objects`, before the next version is created:
|
|
76
|
+
|
|
77
|
+
| Version | Created at (V8 microseconds) | Instruction bytes created | Invalidated at | Reason |
|
|
78
|
+
| --- | ---: | ---: | ---: | --- |
|
|
79
|
+
| 1 | 5,581,865 | 16,212 | 6,867,589 | `weak objects` |
|
|
80
|
+
| 2 | 7,886,535 | 15,612 | 8,876,052 | `weak objects` |
|
|
81
|
+
| 3 | 9,846,472 | 14,576 | 10,821,681 | `weak objects` |
|
|
82
|
+
| 4 | 11,807,985 | 15,228 | 12,784,300 | `weak objects` |
|
|
83
|
+
|
|
84
|
+
Together with the pinned collector code above, this identifies garbage-collector
|
|
85
|
+
dependency invalidation as the mechanism behind these observed optimized-version
|
|
86
|
+
replacements. It is materially more specific than merely noticing code growth.
|
|
87
|
+
It does **not** identify the precise weak referents, prove which collection was
|
|
88
|
+
forced versus natural, prove simultaneous survival of the versions, or establish
|
|
89
|
+
that all heap growth comes from code. No new GC trace or heap snapshot was taken.
|
|
90
|
+
|
|
91
|
+
### Heap observations, not an acceptance result
|
|
92
|
+
|
|
93
|
+
| Phase | Server heap bytes | Client heap bytes | Client code-and-metadata bytes |
|
|
94
|
+
| --- | ---: | ---: | ---: |
|
|
95
|
+
| Preconditioning | 10,174,352 | 7,030,968 | 2,860,489 |
|
|
96
|
+
| Warm | 10,193,960 | 7,005,152 | 2,834,367 |
|
|
97
|
+
| Storm 1 | 10,475,080 | 7,241,416 | 2,985,665 |
|
|
98
|
+
| Storm 2 | 10,859,504 | 7,685,000 | 3,276,065 |
|
|
99
|
+
| Storm 3 | 10,854,912 | 7,800,448 | 3,336,345 |
|
|
100
|
+
| Storm 4 | 11,058,200 | 7,840,416 | 3,346,105 |
|
|
101
|
+
| Storm 5 | 9,818,424 | 7,843,832 | 3,330,883 |
|
|
102
|
+
|
|
103
|
+
Client heap grew 838,680 bytes and code-and-metadata grew 496,516 bytes after
|
|
104
|
+
warm-up. Peak/final client heap was 111.97233122136393% of warm. Client bytecode
|
|
105
|
+
was 756,440 at warm and 756,544 thereafter; external memory (2,331,012 bytes) and
|
|
106
|
+
array buffers (35,458 bytes) stayed constant after warm-up. Server peak/final
|
|
107
|
+
ratios were 108.47796145953093% and 96.31609305902711%. Logging and the existing
|
|
108
|
+
native boundaries perturb execution: these observations do not replace the
|
|
109
|
+
original shared-process acceptance gate. No statistics were subtracted or limits
|
|
110
|
+
changed to produce a pass.
|
|
111
|
+
|
|
112
|
+
### Retained evidence
|
|
113
|
+
|
|
114
|
+
- Tooling commit: `8b4694c`, branch `codex/agent-ready`.
|
|
115
|
+
- UTC run: 2026-08-30 20:15:13.595–20:15:26.483, Windows/Node 22.21.0/ws 8.21.3.
|
|
116
|
+
- Local directory: `coverage/recovery-split-xeBAYE` (report, samples and four logs).
|
|
117
|
+
- Report SHA-256: `fdb0626a8fb87425eb0d9384f74066fe3fbbf045c67a4b02a94bc2012262f6e2`.
|
|
118
|
+
- Client stdout: 521,311 bytes; SHA-256 `bbba488633697b70c7ac35e7c0b1821d1008f542af63005710947244f1732bf8`.
|
|
119
|
+
- Other three logs: empty and complete, hashes verified.
|
|
120
|
+
- Code parser SHA-256: `44dae45083bc6a55eed71cb5840dd6ecca4e2c5a2582b0b90bb35e2908af1d5b`.
|
|
121
|
+
- Deoptimization parser SHA-256: `a244196245fb32c3c4ff30a9d70e15fc20b946910e7012334de0d68af99b465b`.
|
|
122
|
+
|
|
123
|
+
The raw local logs contain addresses, paths and code labels; they are not public
|
|
124
|
+
sanitized artifacts. The version-controlled report contains selected known-code
|
|
125
|
+
labels and numeric evidence instead.
|
|
126
|
+
|
|
127
|
+
## Verification
|
|
128
|
+
|
|
129
|
+
Seventy-nine focused tests across five suites passed before measurement, including
|
|
130
|
+
unit tests and real HTTP/WebSocket/process integration. A separate actual-V8
|
|
131
|
+
fixture deterministically produces a field-dependency invalidation and a wrong-map
|
|
132
|
+
eager bailout. Native intrinsics and forced optimization are **test-only**, never
|
|
133
|
+
part of the measured workload. Other runtime versions still exercise real traffic
|
|
134
|
+
and capture, but explicitly reject the unsupported parser schema.
|
|
135
|
+
|
|
136
|
+
Both parser modules have 100% statement/branch/function/line coverage in Jest and
|
|
137
|
+
native V8 coverage. Combined diagnostic-tool coverage remains below its unchanged
|
|
138
|
+
100% gate on the final tree: 83.01% statements/lines, 97.64% branches and 94.87% functions. All 79
|
|
139
|
+
behavior tests pass, but that aggregate coverage command correctly exits nonzero.
|
|
140
|
+
The old coordinator CLI/error paths and worker cleanup-barrier timeout remain
|
|
141
|
+
uncovered. No exclusions or weakened thresholds were introduced.
|
|
142
|
+
|
|
143
|
+
Full-suite verification passed **833 tests / 79 suites** in 418.458 seconds,
|
|
144
|
+
including generated-documentation and all three TypeScript pretest configurations.
|
|
145
|
+
Instrumented-library coverage is 100% statements/branches/functions/lines: 5,445
|
|
146
|
+
statements, 4,044 branches and 978 functions, none uncovered. This library
|
|
147
|
+
denominator does not include the diagnostic-tool gaps above. Saved result:
|
|
148
|
+
`coverage/recovery-deopt-full-suite.json`, SHA-256
|
|
149
|
+
`96ab9e8b4ee219b8682e04229e3843edbd3d133ffadfadccd089f1d1f716411b`.
|
|
150
|
+
The earlier shutdown timeout did not recur, but its historical failure remains
|
|
151
|
+
unexplained; this pass is not proof that it was harmless.
|
|
152
|
+
|
|
153
|
+
The senior critic approved preflight, independently verified every source/output
|
|
154
|
+
hash, census counts and all four code-address sequences, and approved the report's
|
|
155
|
+
wording and inference limits. There were no outstanding findings. No additional
|
|
156
|
+
measured run or acceptance retry was made.
|
|
157
|
+
After the evidence update, generated-documentation and TypeScript checks passed
|
|
158
|
+
again, followed by 83 focused documentation/diagnostic tests across six suites.
|
|
159
|
+
|
|
160
|
+
## Remaining causal boundary
|
|
161
|
+
|
|
162
|
+
We now know why the observed handshake versions are replaced. The next distinct
|
|
163
|
+
question is what remains live after those invalidations: surviving code, feedback
|
|
164
|
+
structures and their retaining paths. That requires retention evidence, not more
|
|
165
|
+
counts of creation or invalidation events. Nothing here justifies a speculative
|
|
166
|
+
Redweb/runtime rewrite, suppressing GC, relaxing the recovery budget, or claiming
|
|
167
|
+
that no leak exists. The client load generator does not import Redweb or
|
|
168
|
+
`redweb-client`. Production/library source, dependencies, npm links, frozen
|
|
169
|
+
helpers, publication, deployment and merge state remain unchanged.
|