@cavelang/solver-z3 0.34.0 → 0.36.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 (44) hide show
  1. package/BENCHMARK.md +56 -3
  2. package/README.md +267 -9
  3. package/dist/scripts/benchmark.js +35 -13
  4. package/dist/scripts/benchmark.js.map +1 -1
  5. package/dist/scripts/memory-stress.d.ts +2 -0
  6. package/dist/scripts/memory-stress.d.ts.map +1 -0
  7. package/dist/scripts/memory-stress.js +74 -0
  8. package/dist/scripts/memory-stress.js.map +1 -0
  9. package/dist/src/cleanup.d.ts +8 -0
  10. package/dist/src/cleanup.d.ts.map +1 -0
  11. package/dist/src/cleanup.js +65 -0
  12. package/dist/src/cleanup.js.map +1 -0
  13. package/dist/src/close-workers.d.ts +12 -0
  14. package/dist/src/close-workers.d.ts.map +1 -0
  15. package/dist/src/close-workers.js +27 -0
  16. package/dist/src/close-workers.js.map +1 -0
  17. package/dist/src/deadline.d.ts +7 -0
  18. package/dist/src/deadline.d.ts.map +1 -0
  19. package/dist/src/deadline.js +46 -0
  20. package/dist/src/deadline.js.map +1 -0
  21. package/dist/src/error-message.d.ts +3 -0
  22. package/dist/src/error-message.d.ts.map +1 -0
  23. package/dist/src/error-message.js +10 -0
  24. package/dist/src/error-message.js.map +1 -0
  25. package/dist/src/runtime.d.ts.map +1 -1
  26. package/dist/src/runtime.js +137 -72
  27. package/dist/src/runtime.js.map +1 -1
  28. package/dist/src/with-release.d.ts +3 -0
  29. package/dist/src/with-release.d.ts.map +1 -0
  30. package/dist/src/with-release.js +25 -0
  31. package/dist/src/with-release.js.map +1 -0
  32. package/dist/src/workflow-fixture.d.ts.map +1 -1
  33. package/dist/src/workflow-fixture.js +27 -10
  34. package/dist/src/workflow-fixture.js.map +1 -1
  35. package/package.json +4 -3
  36. package/scripts/benchmark.ts +35 -12
  37. package/scripts/memory-stress.ts +72 -0
  38. package/src/cleanup.ts +55 -0
  39. package/src/close-workers.ts +28 -0
  40. package/src/deadline.ts +40 -0
  41. package/src/error-message.ts +5 -0
  42. package/src/runtime.ts +132 -70
  43. package/src/with-release.ts +21 -0
  44. package/src/workflow-fixture.ts +30 -11
package/BENCHMARK.md CHANGED
@@ -1,8 +1,43 @@
1
1
  # Z3 feasibility benchmark
2
2
 
3
+ ## Current macOS measurements
4
+
5
+ Measured on 2026-09-08 on macOS arm64 with `z3-solver` 5.2.0, whose embedded
6
+ backend reports `Z3 5.1.0.0`. Each Node version ran in a separate process,
7
+ sequentially, against the current worktree after the solver input-capture and
8
+ explanation-ownership changes. The fixture uses `Solve.run`; it does not measure
9
+ explanation construction. Each run completed one first optimized check and 25
10
+ warm checks, all returning `optimal`, then closed the runtime and exited.
11
+
12
+ | Measurement | Node 24.16.0 | Node 26.5.0 |
13
+ |---|---:|---:|
14
+ | Cold Wasm initialization | 69 ms | 75 ms |
15
+ | First optimized check | 85.69 ms | 95.06 ms |
16
+ | Warm mean | 5.16 ms | 5.15 ms |
17
+ | Warm p50 | 5 ms | 4.92 ms |
18
+ | Warm p95 | 6.09 ms | 5.97 ms |
19
+ | RSS before initialization | 149,045,248 bytes | 151,977,984 bytes |
20
+ | RSS after 26 checks | 284,917,760 bytes | 248,299,520 bytes |
21
+ | Peak process RSS | 295,780,352 bytes | 256,851,968 bytes |
22
+
23
+ Both runs measured 25 installed dependency files totaling 35,820,846 bytes,
24
+ including 34,938,413 bytes of Wasm. The sum of individually gzip-compressed files
25
+ was 8,066,680 bytes. This measures installed package contents, not an npm tarball.
26
+
27
+ These are single-process observations with 25 warm samples each, not a latency
28
+ budget or a controlled comparison against the earlier implementation. Platform,
29
+ dependency version, cache state and concurrent machine activity can affect the
30
+ results; the Linux measurements below are not a performance baseline for this
31
+ Mac. Use `pnpm --filter @cavelang/solver-z3 benchmark` to reproduce the fixture.
32
+
33
+ ## Earlier Linux measurements
34
+
3
35
  Measured on 2026-07-15 on Linux x64 with `z3-solver` 4.16.0, both at
4
- the project's minimum Node.js 22.18 runtime and the available Node.js 24.14
5
- runtime. CI runs the full adapter and lifecycle suite on Node.js 22.
36
+ the former minimum Node.js 22.18 runtime and the available Node.js 24.14
37
+ runtime. These figures describe that measured dependency version, not the
38
+ current adapter's performance. The runtime CI matrix now runs portable solver
39
+ and adapter suites plus the large forced-GC cleanup diagnostic on its pinned
40
+ Node 24 and 26 / Linux, macOS, and Windows entries.
6
41
 
7
42
  The fixture optimizes an exact architecture-choice model with a finite enum,
8
43
  bounded integer, exact-real cost, conditional constraint, and lexicographic
@@ -30,6 +65,24 @@ with its published dependencies in a clean consumer, solved a model, called
30
65
  `close()`, and exited without a worker hang. `scripts/benchmark.ts` reproduces
31
66
  the local measurements.
32
67
 
68
+ Each successful JSON summary identifies the installed `z3-solver` package
69
+ version separately from its embedded backend version, the canonical fixture
70
+ model digest, and the number of verified checks. Keep these fields with saved
71
+ measurements so results can be matched to the dependency and workload used.
72
+
73
+ The current benchmark verifies every answer, not only its status: optimality
74
+ must be proved, the selected architecture must be `monolith`, team size must be
75
+ 12, and both assigned monthly cost and the sole objective value must equal the
76
+ exact rational `321/4` (80.25). A wrong answer fails without publishing timing
77
+ statistics. Verification runs after each timed solve interval. The measurements
78
+ above predate this stronger answer assertion and retain their original status-only
79
+ verification scope.
80
+
81
+ The benchmark closes its runtime in a `finally` block after initialization,
82
+ including when the first check or a warm check returns an unexpected status,
83
+ or a solve throws. Failed runs retain their diagnostic and do not emit a
84
+ successful measurement summary.
85
+
33
86
  ## Decision
34
87
 
35
88
  Accept Z3 as an optional Node.js backend. It produces satisfying assignments,
@@ -38,7 +91,7 @@ exact decimals round-trip as rationals; actual deadlines return `unknown`;
38
91
  simultaneous calls are queued; and explicit shutdown terminates workers.
39
92
 
40
93
  Do not add it to `@cavelang/solver`, the CLI, MCP, or website dependency graph
41
- by default. Cold start, roughly 34.5 MB installed size, and process-wide
94
+ by default. Cold start, roughly 35.8 MB installed size in the current measurement, and process-wide
42
95
  threaded-Wasm state are material costs. A consumer should opt into
43
96
  `@cavelang/solver-z3`, initialize once, reuse it, and close it only during
44
97
  process shutdown. Browser shipping remains a separate decision gate.
package/README.md CHANGED
@@ -5,6 +5,18 @@ Optional Node.js adapter from CAVE's solver-neutral model to the official
5
5
  Wasm module is dynamically imported and initialized only when `create()` is
6
6
  called.
7
7
 
8
+ | Task | Start here |
9
+ | --- | --- |
10
+ | Run a first model and close its runtime | [Quick start](#quick-start) |
11
+ | Manage shared runtime ownership, failures and limits | [Runtime contract](#runtime-contract) |
12
+ | Understand supported models and proof requirements | [Compilation semantics](#compilation-semantics) |
13
+ | Check the browser support boundary | [Browser delivery decision](#browser-delivery-decision) |
14
+ | Run the named workflow from the CLI | [Named workflow CLI fixture](#named-workflow-cli-fixture) |
15
+ | Investigate timeout and interrupt recovery | [Deadline responsiveness diagnostic](#deadline-responsiveness-diagnostic) |
16
+ | Investigate memory pressure and runtime recovery | [Memory recovery diagnostic](#memory-recovery-diagnostic) |
17
+
18
+ ## Quick start
19
+
8
20
  ```ts
9
21
  import { Model, Solve } from '@cavelang/solver'
10
22
  import { create } from '@cavelang/solver-z3'
@@ -28,12 +40,28 @@ const model: Model.t = {
28
40
  }
29
41
 
30
42
  const z3 = await create()
31
- const result = await Solve.run(z3, model, {
32
- limits: { timeoutMs: 2_000 }
33
- })
34
- await z3.close()
43
+ try {
44
+ const result = await Solve.run(z3, model, {
45
+ limits: { timeoutMs: 2_000 }
46
+ })
47
+ console.log(result)
48
+ } finally {
49
+ await z3.close()
50
+ }
35
51
  ```
36
52
 
53
+ This example owns its runtime and closes it even when model validation or solving
54
+ throws. In a shared service, keep runtime ownership at the service lifecycle: an
55
+ individual request must not close the runtime used by other requests.
56
+
57
+ Use `Solve.run` or `Solve.runWithExplanation` for application requests. These
58
+ entry points validate options and limits, capture and validate the model, freeze
59
+ the submitted graph, negotiate capabilities and capture the adapter result.
60
+ The runtime's `solve` method is the low-level adapter interface: it queues the
61
+ supplied model for compilation and assumes that preparation has already happened.
62
+ Its targeted timeout and cycle checks do not replace model validation or protect
63
+ a directly supplied mutable model from changes while queued.
64
+
37
65
  ## Runtime contract
38
66
 
39
67
  - `create()` initializes once and returns the same process runtime until it is
@@ -41,10 +69,55 @@ await z3.close()
41
69
  - Solve requests enter an explicit FIFO queue. Z3's TypeScript/Wasm binding is
42
70
  not thread-safe, so simultaneous requests never run checks concurrently or
43
71
  share solver state accidentally.
44
- - `close()` waits for queued checks, terminates all Emscripten workers, and is
72
+ Feasibility, optimization and unsatisfiable-core checks release their owned
73
+ solver/optimizer only after the operation settles. If both the operation and
74
+ release fail, both errors survive in order, with the operation error as cause;
75
+ neither an unprintable value nor a release error hides the original failure.
76
+ A release failure alone is propagated unchanged. Each release is attempted
77
+ once; this does not guarantee recovery from damaged native state.
78
+ - `close()` waits for queued checks, requests worker termination and awaits
79
+ Node's worker exit acknowledgments before clearing Emscripten's pool. It is
45
80
  idempotent. Short-lived commands must call it before exit.
81
+ Closing marks the runtime unavailable to new solves immediately, but existing
82
+ queued work settles before worker termination begins. `close()` does not
83
+ forcibly terminate an active native check; if that check never settles, close
84
+ remains pending as well. The check timeout and shutdown therefore share the
85
+ cooperative-cancellation boundary.
86
+ If worker termination fails, shutdown still attempts every worker in that
87
+ batch and awaits all attempts before rejecting. It retains the pool's message
88
+ handlers; one failure is rethrown unchanged and multiple failures are grouped
89
+ in an `AggregateError` whose message includes each failure diagnostic. A failed runtime shutdown remains failed on subsequent
90
+ `close()` or `create()` calls.
91
+ Pending cleanup messages retain their handlers until worker exit; shutdown
92
+ does not assume they arrive within one timer turn. The pool can contain
93
+ persistent workers, so shutdown does not wait for the running pool to become
94
+ empty on its own.
95
+ Calls to `create()` during shutdown wait for it to finish, then share one
96
+ new runtime. They never receive the closing instance. Repeated calls to the
97
+ old runtime's `close()` cannot clear the replacement.
46
98
  - Every check receives Z3's internal timeout plus an independent wall-clock
47
- interrupt. The portable memory limit is applied through Z3's process-wide
99
+ interrupt. After expiry, CAVE repeats the interrupt every 50 ms while the
100
+ check remains pending and stops the timer when the check resolves or rejects.
101
+ An exception from an interrupt request stays within the solve operation instead
102
+ of escaping the timer callback. Cancellation retries continue, retaining the
103
+ first interrupt failure without accumulating repeated errors. The helper waits
104
+ for the native check to settle before reporting failure, so solver/optimizer
105
+ cleanup cannot race pending work. If the check also rejects, its error and the
106
+ first interrupt error are retained together, with the check error as cause.
107
+ Their combined message includes both underlying diagnostics, so the public
108
+ backend-error reason retains them after error conversion. Unprintable thrown
109
+ values use a safe placeholder; later interrupt failures do not replace the first.
110
+ Cancellation is cooperative: the timer does not impose a hard process deadline,
111
+ and initialization, queue wait and model compilation are outside this check timer.
112
+ A live regression times out an 80-variable pigeonhole model, then immediately
113
+ solves a satisfiable Boolean model on the same runtime. It checks the returned
114
+ assignment and preserves the earlier timeout result. This verifies recovery
115
+ for that workload, not termination or recovery from every native stall.
116
+ This adapter accepts `timeoutMs` in 1..2147483647; larger deadlines
117
+ return `unknown` with a `backend-error` before compilation, preventing timer
118
+ overflow from becoming an immediate interrupt. This adapter-specific bound
119
+ does not change other adapters' portable limit contract.
120
+ The portable memory limit is applied through Z3's process-wide
48
121
  `memory_max_size` parameter while requests are serialized.
49
122
  - Preflight declaration and expression limits remain in `@cavelang/solver`.
50
123
  This adapter additionally enforces `maxOutputBytes` on the serialized result.
@@ -55,17 +128,66 @@ await z3.close()
55
128
  ## Compilation semantics
56
129
 
57
130
  Booleans, bounded integers, exact rationals, conditionals, arithmetic, and
58
- hard constraints compile directly. Decimal strings are reduced by
131
+ hard constraints compile directly. A live truth-table regression checks negation,
132
+ conjunction, disjunction, implication, Boolean equality/inequality and conditional
133
+ selection for all four Boolean input pairs. It also checks a conditional whose
134
+ branches mix an exact real half with an integer, preserving the selected value.
135
+ Decimal strings are reduced by
59
136
  `@cavelang/solver` and passed to Z3 as `bigint` numerator/denominator pairs,
60
137
  never as JavaScript floating point. Finite enums use bounded integer codes and
61
138
  are decoded through the domain's canonical lexical order, so declaration
62
139
  reordering cannot change an unconstrained assignment.
140
+ Enum literal lookup uses a value-to-code map built once from each sorted domain
141
+ for the compilation call. This removes repeated linear domain scans while
142
+ retaining the sorted array for decoding assignments. The extra maps use space
143
+ proportional to enum members and are rebuilt for each request; domain mutation
144
+ and reordering cannot reuse stale codes. Empty strings, prototype-like names
145
+ and distinct Unicode spellings retain their exact member identities.
146
+
147
+ Expression compilation uses an explicit traversal stack and caches shared
148
+ subexpressions within one model. Models accepted with raised depth limits can
149
+ reach Z3 without recursive JavaScript call-stack exhaustion. Repeated operands
150
+ remain repeated operations, and compiled expressions never cross requests.
151
+ Wide conjunctions and disjunctions compile in groups of at most 1,024 operands,
152
+ preserving every operand through associativity. This avoids JavaScript argument
153
+ limits in both the adapter and the Z3 binding, including for expressions within
154
+ the default node budget.
155
+ Generated variable-domain bounds are also submitted in batches of at most
156
+ 1,024 assertions, for both feasibility and optimization. Raising the variable
157
+ limit therefore does not create one unbounded JavaScript argument list. Large
158
+ models may also need an explicit `maxNumericDigits` increase for their aggregate
159
+ bound and literal digits; raising the variable limit does not raise that budget.
63
160
 
64
161
  Named hard constraints use tracked Boolean literals, so an unsatisfiable core
65
- maps back to portable constraint IDs. Objective declaration order is Z3's
162
+ maps back to portable constraint IDs. After optimization proves infeasibility,
163
+ a separate tracked feasibility check extracts the optional core. That check
164
+ has its own configured timeout; if it returns no infeasibility proof, the
165
+ original result retains its proof with no core. A live regression checks the
166
+ same stable core IDs through both feasibility and optimization. Trackers use fresh Z3 symbols to avoid
167
+ collisions with user variable names; requesting a core cannot add a constraint
168
+ on a user variable through a shared symbol. Objective declaration order is Z3's
66
169
  lexicographic order. Explicit weighted soft constraints form the final,
67
170
  lowest-priority objective; their weights are never inferred from CAVE belief
68
- confidence.
171
+ confidence. A live tradeoff regression minimizes `x` and `y` under `x + y >= 10`
172
+ with both variables bounded to 0..10. Reversing objective order switches the
173
+ assignment from `(0, 10)` to `(10, 0)`; a weight-1,000,000 preference for `x = 5`
174
+ cannot override either explicit priority. Returned objective values retain
175
+ declaration order.
176
+ `Solve.run` charges soft weights to the model's aggregate `maxNumericDigits`
177
+ allowance before exact validation and backend invocation. Compilation reduces each weight and passes its
178
+ `numerator/denominator` text directly to Z3; it does not expand repeating decimals
179
+ or convert weights to JavaScript numbers. A real-backend regression swaps two
180
+ nonterminating rational weights that collapse to the same binary64 value and
181
+ verifies that the chosen Boolean follows the stronger exact weight both ways.
182
+ Fraction formatting copies the normalized components; exact reduction and native
183
+ optimization remain separate costs. Numeric preflight is not a wall-clock bound,
184
+ and the check timer does not cover compilation.
185
+
186
+ An `optimal` result requires each explicit objective's lower and upper bounds
187
+ to be the same finite rational, attained by the returned assignment. This check
188
+ uses exact arithmetic for every lexicographic objective. Unbounded objectives,
189
+ unattained strict bounds, or a model that does not attain the proved bound
190
+ return `unknown` with reason `indeterminate`, never a proof of optimality.
69
191
 
70
192
  See [BENCHMARK.md](BENCHMARK.md) for artifact, initialization, solve, memory,
71
193
  packaging, and lifecycle measurements. The spike accepts Z3 for optional
@@ -103,3 +225,139 @@ uses the workflow API, so model validation, capability negotiation, limits,
103
225
  deterministic tie-breaking, and `unknown` semantics cannot be bypassed. The
104
226
  JSON output is a versioned workflow/explanation report. The fixture's relative
105
227
  cost formula is deliberately illustrative and does not use belief confidence.
228
+ Argument errors return exit code 2. Runtime startup, workflow execution and
229
+ shutdown failures return code 1 with empty stdout and a diagnostic on stderr.
230
+ If execution and shutdown both fail, the diagnostic retains the execution error
231
+ followed by the shutdown error. A successful report is emitted only after the
232
+ owned runtime closes successfully. Programmatic `runWorkflowFixture` calls
233
+ return the same output fields and close only runtimes created by the fixture;
234
+ caller-supplied adapters remain caller-owned. Exception formatting is guarded:
235
+ values whose message or string conversion throws produce
236
+ `[unprintable thrown value]`, so diagnostic rendering cannot skip owned cleanup
237
+ or replace the error result. Backend-error diagnostics use the same fallback.
238
+
239
+ All four operations accept `--max-explanation-bits <positive integer>` to set
240
+ the local constraint-evaluation size budget (default: 1,000,000). The value is
241
+ validated before loading Z3 and travels in the report's resolved limits.
242
+ For example, `cave-solver-workflow architecture feasibility --max-explanation-bits 2000000`
243
+ allows a larger local evaluation than the default. Exceeding the budget leaves
244
+ the backend outcome intact and marks affected constraints indeterminate; retry
245
+ with a larger value to request a fresh evaluation. This conservative fraction-size
246
+ budget is separate from `--timeout-ms` and backend memory limits.
247
+ All four operations also accept `--max-explanation-work <positive integer>`
248
+ (default: 10,000,000) for cumulative estimated-bit charges within each report.
249
+ It is validated before Z3 starts and recorded with the resolved limits. Exhaustion
250
+ marks affected local evaluations indeterminate while preserving the backend result;
251
+ use a larger allowance for a fresh report requiring more distinct arithmetic.
252
+ This counter does not cover every report phase or guarantee elapsed time.
253
+ The CLI accepts `--timeout-ms` only in Z3's supported range, 1..2147483647.
254
+ Larger values fail as argument errors before adapter startup, rather than
255
+ producing a backend-error workflow report. Both endpoints of the range remain
256
+ valid, and a corrected invocation can run normally.
257
+
258
+ The real-adapter suite also checks sensitivity batch preflight and provenance.
259
+ A later sample that exceeds the generated-model digit budget is rejected before
260
+ any Z3 submission. Raising the limit solves both distinct sample models, reports
261
+ their exact assignments and the observed transition, and retains the authored
262
+ model's digest in the batch, point and explanation reports. Generated sample
263
+ models have their own distinct digests; they do not replace source identity.
264
+
265
+ ## Deadline responsiveness diagnostic
266
+
267
+ Run `node scripts/z3-deadline-probe.mjs` from the repository root to exercise
268
+ three fresh processes, each issuing ten 1 ms pigeonhole requests on one runtime
269
+ and following every timeout result with a satisfiable recovery request. The
270
+ parent imposes a 15-second allowance per process, retains partial event output
271
+ and terminates an overrun. This is a diagnostic harness limit, not an adapter
272
+ guarantee; the probe exits unsuccessfully if any process fails or overruns.
273
+
274
+ A Node 26 suite observed a 208.5-second timeout test. The initial bounded probe
275
+ reproduced a stall in one of three processes; the other 25 requests completed in
276
+ 118–191 ms. Retrying interrupts while native work remains pending then completed
277
+ all 30 requests and recovery checks on each supported Node major: 118–220 ms on
278
+ Node 24.21.0 and 113–237 ms on Node 26.8.1. These complete-call times include
279
+ preparation. [Partial reproduction events and subsequent results](../../benchmarks/z3-deadline-retry-review.json)
280
+ retain the failure evidence and source hashes. A deterministic test verifies
281
+ repeated cancellation after a check ignores the first interrupt, and timer
282
+ cleanup after settlement. The precise native cause remains unproven; successful
283
+ retries do not establish a hard elapsed-time bound.
284
+
285
+ A later [current-source checkpoint](../../benchmarks/z3-deadline-current-review.json)
286
+ also completes all 30 timeout/recovery pairs per major and closes all three
287
+ runtimes: 112–220 ms per complete call on Node 26.8.1 and 114–226 ms on Node
288
+ 24.21.0. The probe now fingerprints every solver and Z3 adapter TypeScript
289
+ source, including the deadline helper and error formatting, plus its own source
290
+ and the lockfile. Earlier reports retain their original narrower fingerprint
291
+ scope; the historical stalled run remains evidence for the unresolved native
292
+ cause. These finite runs do not guarantee recovery from every native stall.
293
+
294
+ ## Memory recovery diagnostic
295
+
296
+ Run `pnpm --filter @cavelang/solver-z3 test:memory-stress` to exercise ten
297
+ memory-limit failures followed by optimization of 3,000 bounded variables in
298
+ the same runtime, with forced garbage collection during asynchronous checks.
299
+ The command enables `--expose-gc`, logs environment and phase metadata, and
300
+ verifies each recovery's complete assignment and exact optimum. Assertion
301
+ failures exit unsuccessfully after worker cleanup. This diagnostic is separate
302
+ from the ordinary test suite and does not establish recovery from every fatal
303
+ Wasm error.
304
+
305
+ A [current-source checkpoint](../../benchmarks/z3-memory-current-review.json)
306
+ passes all ten default cycles on Node 26.8.1 and 24.21.0: each 1 MiB memory-limit
307
+ failure is followed by the exact optimum of 3,000 and validation of all 3,000
308
+ assignments in the same runtime. Both processes exit cleanly after runtime
309
+ closure. The record includes phase output and solver/adapter source fingerprints.
310
+ This checkpoint does not rerun `--large` or cover every fatal native error.
311
+
312
+ Add `--large` to run three cycles that insert the original 75,000-variable
313
+ domain-bound contradiction in both feasibility and optimization modes between
314
+ each memory-limit failure and the 3,000-variable recovery probe. The large
315
+ fixture explicitly permits 75,000 variables and 150,002 numeric digits: two
316
+ one-digit bounds per variable, one constraint literal and an optional objective
317
+ literal. These diagnostic settings let the intended backend stress run reach Z3
318
+ without changing production validation defaults. Large phases
319
+ log the complete result, wall time, and Node process memory counters. These
320
+ counters describe process memory, not Z3's internal allocation accounting.
321
+ The diagnostic submits subsequent requests before asserting large-phase
322
+ results, preserving evidence of whether the same runtime remains usable.
323
+ The runtime CI matrix runs the large diagnostic on each configured runtime/OS
324
+ entry. Each large solve has a 120-second budget and each recovery solve has a
325
+ 30-second budget; the CI step has a
326
+ 20-minute timeout to bound native hangs across all three cycles. Its solve
327
+ budgets total up to 14 minutes, leaving at least six minutes for initialization,
328
+ validation, compilation, forced GC, logging and cleanup. The enclosing
329
+ runtime job allows 40 minutes for that step plus setup, native suites and CLI
330
+ checks; a runtime-policy test keeps at least 15 minutes outside the stress step. The larger
331
+ budget accommodates shared-runner performance: Windows CI exceeded the previous
332
+ 30-second feasibility deadline while the following optimization and recovery
333
+ still returned the expected results. A later Windows run reached recovery but
334
+ exceeded its default 10-second budget, so that phase now also has an explicit
335
+ diagnostic budget. This diagnostic checks cleanup correctness;
336
+ it does not impose a 30-second performance requirement. Workload size, forced
337
+ collection frequency and exact result assertions remain unchanged.
338
+
339
+ The adapter defers native reference cleanup while its asynchronous feasibility
340
+ or optimization check runs, draining releases once the native promise settles,
341
+ including on rejection. The binding's garbage-collection finalizers otherwise
342
+ call native reference decrements outside its check mutex, allowing AST deletion
343
+ to overlap the solver thread. The large diagnostic exposed both an incorrect
344
+ recovery optimum (`0` instead of `3000`) and a memory-access crash in that
345
+ cleanup path. The guard belongs to CAVE's private Z3 instance; it changes no
346
+ global JavaScript finalizer behavior and keeps synchronous cleanup immediate.
347
+ Queued releases are attempted in order even when an earlier release throws.
348
+ A lone cleanup failure propagates unchanged; multiple failures are retained in
349
+ an `AggregateError`. If the native check also failed, its error comes first and
350
+ remains the cause, followed by cleanup failures in release order. Unprintable
351
+ thrown values cannot interrupt diagnostic formatting or skip later releases.
352
+ Drained releases are not replayed by a subsequent check. This preserves cleanup
353
+ attempts and error ownership; it does not promise that damaged native state is
354
+ recoverable after a failed release.
355
+ A [post-fix native recovery checkpoint](../../benchmarks/z3-cleanup-recovery-review.json)
356
+ passes three large cycles on each supported Node major under forced garbage
357
+ collection, retaining the 75,000-variable checks and exact recovery optimum.
358
+ It verifies this workload and clean process exit, not arbitrary native failures.
359
+
360
+ Process lifecycle tests launch fixtures using native paths converted with
361
+ `fileURLToPath`, so Windows drive letters and percent-encoded checkout names
362
+ reach Node correctly. These tests exercise worker shutdown and runtime reopening
363
+ in a separate process.
@@ -1,12 +1,14 @@
1
+ import assert from 'node:assert/strict';
1
2
  import { readFile, readdir, stat } from 'node:fs/promises';
2
3
  import { dirname, join } from 'node:path';
3
4
  import { createRequire } from 'node:module';
4
5
  import { gzipSync } from 'node:zlib';
5
6
  import { performance } from 'node:perf_hooks';
6
- import { Model, Solve } from '@cavelang/solver';
7
+ import { Canonical, Model, Solve } from '@cavelang/solver';
7
8
  import { create } from '@cavelang/solver-z3';
8
9
  const require = createRequire(import.meta.url);
9
10
  const packageRoot = dirname(require.resolve('z3-solver/package.json'));
11
+ const dependencyVersion = JSON.parse(await readFile(join(packageRoot, 'package.json'), 'utf8')).version;
10
12
  const files = async (directory) => {
11
13
  const entries = await readdir(directory, { withFileTypes: true });
12
14
  return (await Promise.all(entries.map(async (entry) => {
@@ -49,23 +51,41 @@ const architecture = {
49
51
  }],
50
52
  objectives: [{ id: 'min-monthly-cost', direction: 'minimize', expression: ref('monthly-cost') }]
51
53
  };
54
+ const verify = (result, phase) => {
55
+ if (result.status !== 'optimal')
56
+ throw new Error(`${phase} fixture returned ${result.status}`);
57
+ assert.equal(result.optimalityProved, true, `${phase}: optimality must be proved`);
58
+ const cost = { sort: 'real', numerator: '321', denominator: '4' };
59
+ assert.deepEqual(result.assignment, {
60
+ choice: { sort: 'enum', domain: 'architecture', value: 'monolith' },
61
+ 'team-size': { sort: 'int', value: '12' },
62
+ 'monthly-cost': cost
63
+ }, `${phase}: incorrect benchmark assignment`);
64
+ assert.equal(result.objectives.length, 1, `${phase}: expected one objective`);
65
+ assert.equal(result.objectives[0]?.objectiveId, 'min-monthly-cost');
66
+ assert.deepEqual(result.objectives[0]?.value, cost, `${phase}: incorrect exact optimum`);
67
+ };
52
68
  const beforeRssBytes = process.memoryUsage().rss;
53
69
  const runtime = await create();
54
- const firstStarted = performance.now();
55
- const first = await Solve.run(runtime, architecture);
56
- const firstCheckMs = performance.now() - firstStarted;
57
- if (first.status !== 'optimal')
58
- throw new Error(`benchmark fixture returned ${first.status}`);
70
+ let firstCheckMs;
59
71
  const warmRuns = 25;
60
72
  const warm = [];
61
- for (let index = 0; index < warmRuns; index += 1) {
62
- const started = performance.now();
63
- const result = await Solve.run(runtime, architecture);
64
- if (result.status !== 'optimal')
65
- throw new Error(`warm fixture returned ${result.status}`);
66
- warm.push(performance.now() - started);
73
+ try {
74
+ const firstStarted = performance.now();
75
+ const first = await Solve.run(runtime, architecture);
76
+ firstCheckMs = performance.now() - firstStarted;
77
+ verify(first, 'benchmark');
78
+ for (let index = 0; index < warmRuns; index += 1) {
79
+ const started = performance.now();
80
+ const result = await Solve.run(runtime, architecture);
81
+ const elapsed = performance.now() - started;
82
+ verify(result, 'warm');
83
+ warm.push(elapsed);
84
+ }
85
+ }
86
+ finally {
87
+ await runtime.close();
67
88
  }
68
- await runtime.close();
69
89
  warm.sort((left, right) => left - right);
70
90
  const mean = warm.reduce((sum, value) => sum + value, 0) / warm.length;
71
91
  console.log(JSON.stringify({
@@ -73,6 +93,8 @@ console.log(JSON.stringify({
73
93
  platform: `${process.platform}-${process.arch}`,
74
94
  node: process.version,
75
95
  backend: runtime.backend,
96
+ dependency: { name: 'z3-solver', version: dependencyVersion },
97
+ fixture: { modelDigest: Canonical.digest(architecture), verifiedChecks: warm.length + 1 },
76
98
  artifacts: {
77
99
  files: paths.length,
78
100
  installedBytes,
@@ -1 +1 @@
1
- {"version":3,"file":"benchmark.js","sourceRoot":"","sources":["../../scripts/benchmark.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"benchmark.js","sourceRoot":"","sources":["../../scripts/benchmark.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=memory-stress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-stress.d.ts","sourceRoot":"","sources":["../../scripts/memory-stress.ts"],"names":[],"mappings":""}
@@ -0,0 +1,74 @@
1
+ import * as assert from 'node:assert/strict';
2
+ import { Model, Solve } from '@cavelang/solver';
3
+ import { create } from '@cavelang/solver-z3';
4
+ const gc = globalThis.gc;
5
+ assert.ok(gc, 'run this diagnostic with node --expose-gc');
6
+ assert.ok(process.argv.slice(2).every(argument => argument === '--large') && process.argv.length <= 3, 'usage: memory-stress.ts [--large]');
7
+ const large = process.argv.includes('--large');
8
+ const iterations = large ? 3 : 10;
9
+ const runtime = await create();
10
+ const count = 3_000;
11
+ const model = {
12
+ schema: Model.schema,
13
+ variables: Array.from({ length: count }, (_, index) => ({ id: `x${index}`, sort: 'int', min: 0, max: 1 })),
14
+ constraints: [],
15
+ objectives: [{
16
+ id: 'total', direction: 'maximize',
17
+ expression: { kind: 'add', operands: Array.from({ length: count }, (_, index) => ({ kind: 'variable', id: `x${index}` })) }
18
+ }]
19
+ };
20
+ const small = { schema: Model.schema, variables: [{ id: 'ok', sort: 'bool' }], constraints: [] };
21
+ const timer = setInterval(gc, 1);
22
+ try {
23
+ console.log(JSON.stringify({ node: process.version, platform: process.platform, arch: process.arch, backend: runtime.backend, iterations, variables: count, largeVariables: large ? 75_000 : 0 }));
24
+ for (let iteration = 0; iteration < iterations; iteration++) {
25
+ const limited = await Solve.run(runtime, small, { limits: { maxMemoryBytes: 1024 * 1024 } });
26
+ console.log(JSON.stringify({ iteration, phase: 'limited', result: limited }));
27
+ assert.equal(limited.status, 'unknown');
28
+ if (limited.status === 'unknown') {
29
+ assert.equal(limited.reason.kind, 'resource-limit');
30
+ assert.equal(limited.reason.limit, 'maxMemoryBytes');
31
+ }
32
+ const largeResults = [];
33
+ if (large) {
34
+ const largeCount = 75_000;
35
+ const variables = Array.from({ length: largeCount }, (_, index) => ({ id: `v${index}`, sort: 'int', min: 0, max: 1 }));
36
+ for (const optimize of [false, true]) {
37
+ console.log(JSON.stringify({ iteration, phase: optimize ? 'large-optimization-start' : 'large-feasibility-start' }));
38
+ const started = performance.now();
39
+ const result = await Solve.run(runtime, {
40
+ schema: Model.schema,
41
+ variables,
42
+ constraints: [{ id: 'outside-last-domain', expression: { kind: 'lt', left: { kind: 'variable', id: `v${largeCount - 1}` }, right: { kind: 'literal', sort: 'int', value: '0' } } }],
43
+ ...(optimize ? { objectives: [{ id: 'constant', direction: 'minimize', expression: { kind: 'literal', sort: 'int', value: '0' } }] } : {})
44
+ }, { limits: {
45
+ maxVariables: largeCount,
46
+ // Two one-digit bounds per variable, plus the constraint and optional objective literals.
47
+ maxNumericDigits: 2 * largeCount + 2,
48
+ // This diagnostic checks cleanup correctness, not shared-runner latency.
49
+ timeoutMs: 120_000
50
+ } });
51
+ console.log(JSON.stringify({ iteration, phase: optimize ? 'large-optimization' : 'large-feasibility', result, wallMs: performance.now() - started, memory: process.memoryUsage() }));
52
+ // Probe subsequent requests before asserting so a poisoned runtime is visible.
53
+ largeResults.push(result);
54
+ }
55
+ }
56
+ console.log(JSON.stringify({ iteration, phase: 'recovery-start' }));
57
+ const result = await Solve.run(runtime, model, { limits: { maxVariables: count, timeoutMs: 30_000 } });
58
+ console.log(JSON.stringify({ iteration, phase: 'recovered', status: result.status, elapsedMs: result.elapsedMs, ...(result.status === 'unknown' ? { reason: result.reason } : {}), ...(result.status === 'optimal' ? { objectives: result.objectives } : {}) }));
59
+ assert.equal(result.status, 'optimal', JSON.stringify(result));
60
+ if (result.status === 'optimal') {
61
+ assert.deepEqual(result.objectives, [{ objectiveId: 'total', value: { sort: 'int', value: String(count) } }]);
62
+ assert.equal(Object.keys(result.assignment).length, count);
63
+ for (const value of Object.values(result.assignment))
64
+ assert.deepEqual(value, { sort: 'int', value: '1' });
65
+ }
66
+ for (const result of largeResults)
67
+ assert.equal(result.status, 'unsatisfied', JSON.stringify(result));
68
+ }
69
+ }
70
+ finally {
71
+ clearInterval(timer);
72
+ await runtime.close();
73
+ }
74
+ //# sourceMappingURL=memory-stress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-stress.js","sourceRoot":"","sources":["../../scripts/memory-stress.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The binding's finalizers call this instance's low-level API directly, outside
3
+ * its async mutex. Native AST deletion must not race a pthread solver check.
4
+ * Guard the two async native operations CAVE uses; normal synchronous releases
5
+ * remain immediate, and queued releases drain before a check returns to CAVE.
6
+ */
7
+ export declare const guardCleanup: (api: object) => void;
8
+ //# sourceMappingURL=cleanup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../src/cleanup.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,KAAG,IA8C1C,CAAA"}