@cavelang/query 0.35.0 → 0.36.1

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/README.md CHANGED
@@ -8,19 +8,114 @@ the store's public `QuerySql` primitives; CAVE-Q adds pattern joins, filters,
8
8
  resolution, transitive traversal, and result binding without redefining those
9
9
  shared semantics.
10
10
 
11
+ Structured `match()` calls capture the declared slot, payload, context, tag and
12
+ filter fields before numeric detection or compilation. Fields at each pattern
13
+ position are read once into ordinary data, including inherited or non-enumerable properties, so
14
+ changing getters cannot select a different term at a later stage. Text queries
15
+ use their freshly parsed pattern directly.
16
+ Query and match options are captured once before historical vocabulary lookup,
17
+ SQL compilation and post-filtering. The same `asOf` boundary and row limit apply
18
+ throughout the operation even when supplied through changing getters.
19
+ Each query or structured match reads vocabulary and candidate claims within one
20
+ deferred SQLite snapshot, including source priorities with `resolve: true`.
21
+ A peer commit cannot combine old vocabulary or priorities with new claims in one result;
22
+ the next query sees the committed state. The snapshot nests inside caller-owned
23
+ transactions and sees their staged claims without committing them. Read and
24
+ snapshot-release failures preserve both errors and the original read cause.
25
+ `queryRecords()` captures its options before opening the read savepoint, so
26
+ adapter callbacks cannot replace its limit or historical boundary. It extends
27
+ the snapshot through claim/provenance record projection,
28
+ including transitive support rows. Concurrent appends or direct metadata changes
29
+ cannot mix selected rows with newer tags or provenance; the next call sees the
30
+ committed changes. A projection failure releases the read savepoint and retains
31
+ the thrown value without committing a caller-owned transaction. If releasing
32
+ that snapshot also throws, an `AggregateError` retains the projection failure
33
+ first in `errors` and as `cause`, followed by the release failure. Diagnostic
34
+ formatting tolerates thrown values that cannot be converted to text. After a
35
+ successful release, correcting the projection failure allows the same query to
36
+ retry inside the caller transaction; the caller still controls commit or rollback.
37
+ Lineage edges
38
+ are not embedded in these records. Calling `Record.of()` separately on previously
39
+ selected matches does not extend the original query snapshot; separate page
40
+ windows retain their pagination revision checks. Supported legacy provenance
41
+ backfill occurs during schema migration before the store is returned; read-only
42
+ opens reject an unmigrated schema. Reopening a current-schema database preserves
43
+ explicit empty provenance, and sync treats a present provenance table as
44
+ authoritative rather than filling its empty sets from claim contexts.
45
+ Empty provenance arrays are valid; empty-string entries are malformed.
46
+ If record projection encounters a malformed entry, correcting the stored evidence
47
+ allows a retry within the same caller transaction. Both the failed and successful
48
+ read leave the caller in control of committing or rolling back its staged claims.
49
+
50
+ Text queries reject unpaired UTF-16 surrogates with their one-based source line
51
+ before parsing. Structured patterns passed to `match` are checked before SQL
52
+ compilation as well, including slots, contexts, tags and filters. This prevents
53
+ SQLite's UTF-8 conversion from turning malformed input into a match for `�`.
54
+ Valid Unicode (including emoji), embedded NUL, and an explicitly authored `�`
55
+ remain queryable; failed reads leave stored history unchanged.
56
+
11
57
  `query()`/`match()` expose storage rows for in-process reasoning engines. For
12
58
  public JSON and durable integrations, use `queryRecords()`: every result is a
13
59
  `cave.query-match/v1` carrying bindings plus optional `cave.claim/v1` claim or
14
- transitive support records. `Record.decode` verifies the version and all
15
- nested claim records.
60
+ transitive support records. `Record.of` captures the match's bindings, evidence
61
+ rows and interpolation fields once before projecting evidence. Later caller
62
+ mutation, including during evidence projection, cannot substitute those captured
63
+ values. These fields must contain structured-cloneable data.
64
+ Construction and decoding share the binding-map and interpolation checks below;
65
+ construction rejects malformed metadata or holes/non-object entries in supporting
66
+ rows before projecting any evidence.
67
+ Object inputs to `Record.decode` are captured before
68
+ metadata validation and their nested data is owned by the decoded result;
69
+ changing getters or later caller mutation cannot replace validated bindings or
70
+ interpolation values. Binding maps must still have a plain or null prototype.
71
+ Like claim-record decoding, object input must contain structured-cloneable data.
72
+ `Record.decode` verifies the version and all
73
+ nested claim records, including consistency of their scalar, trajectory and
74
+ uncertainty projections with raw value text. The same checks apply to direct
75
+ claim evidence and each supporting record. Optional interpolation metadata must contain a finite
76
+ numeric `num`, string `text`, and a string `unit` when supplied; malformed
77
+ metadata is rejected for both object and JSON input.
78
+ Bindings must be a plain object of string values (a null-prototype map is also
79
+ accepted). Boxed strings, dates, maps, sets, and class instances are rejected
80
+ rather than accepted with a different or lossy JSON representation.
81
+ Interpolation `num` retains the computed numeric precision, while `text` and
82
+ value-slot bindings use the trajectory formatter's four significant digits.
83
+ For example, a computed `549.995 req/s` is displayed and bound as `550 req/s`;
84
+ the record preserves `at.num: 549.995`. If display rounding would overflow,
85
+ the formatter keeps the finite unrounded value instead. Decoding preserves
86
+ the intentional rounding difference instead of requiring exact numeric equality
87
+ with `text`.
88
+ Supporting evidence must be a dense array of valid claim records. Array holes,
89
+ `undefined` and `null` entries are rejected; an empty support array is valid.
90
+ Object input is read by array index, matching JSON array contents; a custom
91
+ iterator cannot hide invalid entries or discard supporting evidence.
16
92
 
17
93
  For interactive or remote reads, `page()` returns a bounded
18
94
  `cave.query-page/v1` envelope. The first request freezes the maximum visible
19
95
  transaction; pass its opaque `next` cursor back with the same pattern,
20
- options, and limit to continue. Appends made between pages are therefore
21
- excluded, while a new first page sees them. The default page size is 100 and
22
- the maximum is 1,000. `query()` and `queryRecords()` remain unbounded for
23
- in-process callers that explicitly want the complete result.
96
+ options, and limit to continue. Later local appends are excluded, while a new
97
+ first page sees them. Alias resolution uses that same frozen boundary: a later
98
+ alias retraction from another connection does not change direct or transitive
99
+ continuation results. A fresh first page sees the retraction and new claims.
100
+ Later value revisions and retractions also leave an existing continuation's
101
+ claims unchanged, including exact-number filtered pages. Reusing the same cursor
102
+ with the same options returns the same page while that historical snapshot stays
103
+ valid; reading a cursor does not consume it.
104
+ If sync adds older rows or lineage touching the cutoff,
105
+ continuation fails with a restart instruction instead of repeating or skipping
106
+ results. The default page size is 100 and
107
+ the maximum is 1,000. Defaults apply only when the limit is omitted or
108
+ undefined; explicit null is invalid on both first and continuation requests.
109
+ Correcting an invalid limit allows reuse of the original cursor. `query()` and `queryRecords()` remain unbounded for
110
+ in-process callers that explicitly want the complete result. When supplying a
111
+ SQL `limit`, use a positive safe integer; its `offset` must be a non-negative
112
+ safe integer. An omitted or undefined offset defaults to zero; explicit null
113
+ is invalid when a limit is supplied. Values beyond `Number.MAX_SAFE_INTEGER` are rejected before SQL
114
+ binding, avoiding rounded bounds and adapter-dependent errors.
115
+
116
+ Result bindings are ordinary objects with an own property for each bound
117
+ variable. Names such as `__proto__`, `constructor` and `toString` retain their
118
+ values, including in transitive results and JSON serialization.
24
119
 
25
120
  ```ts
26
121
  import { open } from '@cavelang/store'
@@ -50,10 +145,20 @@ const second = first.next === undefined ? undefined :
50
145
 
51
146
  - `?name` variables bind subject, verb, object or attribute value;
52
147
  a variable repeated in two positions forces equality (`?x NEEDS ?x`).
148
+ A bare `?` is invalid: supply a name, or use `_` for a wildcard.
149
+ Programmatic `match` patterns also require a non-empty variable `name`.
53
150
  - `_` is a wildcard.
54
151
  - `attr: ?v` matches attribute claims.
55
152
  - `@ctx` and `#tag[:value]` on the pattern line filter contexts/tags
56
153
  (flat `#tag` matches flat tags only, mirroring `store.byTag`).
154
+ Every requested context and tag must be present. Lists of up to 16 items use
155
+ direct indexed predicates; larger lists use one bound JSON array each. This
156
+ bounds SQL expression depth and parameter count from metadata lists while
157
+ retaining the direct lookup path for ordinary queries. Native SQLite and the
158
+ playground's SQLite WASM have regressions
159
+ with 1,500 contexts and 1,500 tags, including missing-item rejection; Unicode
160
+ and embedded NUL suffixes retain exact matching. This does not impose a general
161
+ query-size budget.
57
162
  - `VERB NOT` matches negated claims; patterns without `NOT` match
58
163
  positive ones — direction is always explicit.
59
164
  - **Inverse verbs** are valid: `?x PART-OF monorepo` compiles to
@@ -76,6 +181,22 @@ const second = first.next === undefined ? undefined :
76
181
 
77
182
  ## Filters (§12.2)
78
183
 
184
+ Filter diagnostics use one-based line numbers from the original query text.
185
+ Blank and full-line comment lines count toward the location even though they
186
+ are ignored when parsing; LF and CRLF inputs use the same numbering.
187
+ Numeric confidence thresholds must be finite; `NaN`, infinities, and exponent
188
+ overflow are parse errors. Finite thresholds outside the stored confidence range
189
+ remain valid comparisons, such as `WHERE conf > 1` matching no claims.
190
+
191
+ Up to 16 `WHERE` filters compile to direct predicates. Longer lists group
192
+ operands by predicate shape into bound JSON arrays, keeping SQL expression
193
+ depth and parameter count bounded by the supported filter forms. Every filter
194
+ must still hold, including comparisons against missing numeric values or units,
195
+ which reject the row. Native SQLite and SQLite WASM regressions exercise 33,000
196
+ filters, mixed requirements, metadata and numeric/transaction comparisons.
197
+ The full query and operand arrays remain in memory; this is not a general
198
+ query-size or execution-time budget.
199
+
79
200
  ```
80
201
  WHERE conf >= 0.8 (also accepts 80%)
81
202
  WHERE tag = security (bare key matches any value; topic:auth is exact)
@@ -90,6 +211,10 @@ WHERE tx <= 2026-01-01T12:00:00 (offset-less timestamps mean UTC)
90
211
  `<` excludes, and `>` starts after it. A timestamp value covers one second.
91
212
  An explicit `Z` or numeric offset is honored; a timestamp without either is
92
213
  UTC, so `2026-01-01T12:00:00` equals `2026-01-01T12:00:00Z` on every host.
214
+ Transaction periods before the UUID epoch (1970-01-01T00:00:00Z) are empty;
215
+ `asOf` and `tx` comparisons accept those dates without attempting a negative
216
+ UUID timestamp. Periods crossing the epoch retain their nonnegative portion.
217
+ Valid-time `at` remains independent and can select claims about earlier dates.
93
218
 
94
219
  ## Semantics
95
220
 
@@ -133,11 +258,18 @@ UTC, so `2026-01-01T12:00:00` equals `2026-01-01T12:00:00Z` on every host.
133
258
  transaction id (that append included). The alias closure and transitive
134
259
  hops reconstruct at the same instant; `{ all: true }` composes as
135
260
  full-history-up-to-the-boundary.
261
+ Qualifier parents obey the same boundary as declarations: later `WHEN`,
262
+ `VIA`, or `BECAUSE` parents cannot remove vocabulary from an earlier query.
136
263
  - **`{ at }` anchors valid time** (§32.4): timeless claims always remain
137
264
  visible; date-like contexts and ranges must cover the selected instant; and
138
265
  a trajectory value such as `200 -> 900 PLN/mo @1950..1974` is returned with
139
266
  its exact interpolated value. It composes with `asOf`: transaction time
140
267
  selects what was believed, while valid time selects when that belief applies.
268
+ Finite trajectory endpoints remain finite during interpolation even when
269
+ their difference would overflow, and endpoint instants retain endpoint values.
270
+ Multiple range contexts, including a closed range combined with an open
271
+ range, leave the trajectory textual. Valid-time coverage still uses the
272
+ normal union of time contexts.
141
273
  - **`{ resolve: true }` matches resolved winners only** (§26): coexisting
142
274
  series about one fact — §9.5 actor stamps, content sources, opposite
143
275
  polarity — collapse to the row the resolution policy picks (precedence
@@ -150,12 +282,44 @@ UTC, so `2026-01-01T12:00:00` equals `2026-01-01T12:00:00Z` on every host.
150
282
  - **Pagination is deterministic and transaction-snapshot stable.** SQL applies
151
283
  `LIMIT`/`OFFSET` after the query's stable transaction or endpoint ordering,
152
284
  and every continuation pins `asOf` to the first page's maximum transaction.
153
- Cursors are scoped to the exact pattern, options, and page size. Valid-time
285
+ Cursors are scoped to the exact pattern, options, and page size. Each page
286
+ reads the declared option fields once, including inherited or non-enumerable
287
+ values, so snapshot selection, execution and cursor identity use the same
288
+ values even when supplied through getters. Query syntax
289
+ and option compatibility are validated even when the store or requested
290
+ historical snapshot is empty. Valid-time
154
291
  coverage and exact numeric approximation checks run after row selection, so
155
- those pages consume bounded one-row SQL windows to advance past rejected
156
- rows without skipping the next match. A heavily filtered page can therefore
292
+ those pages read candidate batches capped at the scan budget. An internal
293
+ row-position index preserves each match's original offset through both
294
+ post-filters. A full page resumes immediately after its last returned match,
295
+ leaving later fetched candidates for the next call. A heavily filtered page can therefore
157
296
  contain fewer than its requested limit (including zero) while still carrying
158
297
  `next`; following it continues from the bounded scan frontier.
298
+ Each page materializes its selected records within a read snapshot, including
299
+ all numeric/valid-time candidate batches and their tag/provenance projection.
300
+ Metadata changed by a peer during projection appears on the next call.
301
+ Projection failure releases the page snapshot without committing a caller
302
+ transaction; if release also throws, the aggregate retains the projection
303
+ failure first and as its cause.
304
+ Each page checks an append-only revision before and after its reads: row count
305
+ and local rowid tail below the cutoff, plus edge count and tail for lineage
306
+ touching either side of that historical row set. Changes reject the page
307
+ with `pagination snapshot changed; restart from the first page`. Wholly
308
+ future rows and edges remain compatible. Revision tokens do not detect direct
309
+ metadata edits between separate page calls. The cursor is database-local;
310
+ its internal encoding is version 2 (the public page envelope remains v1).
311
+ Older or malformed tokens also require a restart. Rejecting a malformed token
312
+ does not mutate the store or invalidate an otherwise valid continuation.
313
+ This contract relies on
314
+ immutable stored claims and side metadata; arbitrary raw SQL rewrites are
315
+ outside the append-only pagination contract.
316
+
317
+ Live connections refresh vocabulary after another SQLite connection commits.
318
+ `store.registry()`, query compilation, and inverse reads therefore see new
319
+ verb and lifecycle declarations without reopening or writing. The read path
320
+ works on read-only connections and reuses its registry cache while SQLite's
321
+ data-version counter is unchanged. Raw SQL writes on the same connection still
322
+ require `reloadRegistry`; precompiled SQL retains their supplied vocabulary.
159
323
 
160
324
  ## Tests
161
325
 
@@ -181,3 +345,328 @@ The deterministic transitive benchmark emits NDJSON timings and result counts
181
345
  for a chain, a branching tree, and a cycle, comparing a source-seeded query
182
346
  with the corresponding unbound all-pairs workload. Plan tests additionally
183
347
  assert forward/reverse seed shape and SQLite object-index use.
348
+
349
+ ## Pagination benchmark
350
+
351
+ `pnpm bench:pagination` measures first-page valid-time and exact-number queries
352
+ against 1,000 and 10,000 claims, with a page limit of 100 and all candidates
353
+ passing the post-filter. A selective valid-time fixture uses a one-result page
354
+ with 99 expired candidates before each matching claim. First-page measurements
355
+ report five samples and their median. Each fixture also follows every cursor
356
+ in three full traversals, reporting page and match counts plus the median.
357
+ Node, SQLite and platform versions accompany the output. Completeness, ordering,
358
+ constant snapshot and termination checks run outside the timing interval.
359
+ The store is populated before timing, so these are repeated in-process reads,
360
+ not CLI startup measurements or fixed CI latency gates.
361
+
362
+ On macOS arm64 with Node 26.5.0 and SQLite 3.53.3, batching changed the median
363
+ first-page times as follows:
364
+
365
+ | Rows | Valid-time before / after | Exact-number before / after |
366
+ |---|---|---|
367
+ | 1,000 | 116.54 / 4.71 ms | 116.49 / 4.20 ms |
368
+ | 10,000 | 1,079.97 / 24.39 ms | 1,093.66 / 25.06 ms |
369
+
370
+ The scan budget and continuation semantics stay unchanged. The selective
371
+ one-result fixture initially measured 113.75 ms at 1,000 rows and 1,068.77 ms at
372
+ 10,000 rows. Tracking original row positions now permits larger candidate reads
373
+ even for that small match limit; medians fell to 2.87 ms and 22.95 ms respectively.
374
+ The internal position index is allocated only for filtered page reads and never
375
+ appears in query records or continuation tokens.
376
+
377
+ A historical-vocabulary cache was trialled and removed before this change
378
+ because its 10,000-row selective median remained about 1.07 seconds. Reducing
379
+ repeated SQL reads provided the measurable improvement. Results are not fixed
380
+ latency guarantees.
381
+
382
+ With the row-position implementation, three-sample full-traversal medians on the
383
+ same runtime were:
384
+
385
+ | Claims | Pages | Valid-time (all matches) | Exact-number (all matches) | Selective valid-time |
386
+ |---|---|---|---|---|
387
+ | 1,000 | 10 | 47.53 ms / 1,000 matches | 44.75 ms / 1,000 matches | 30.75 ms / 10 matches |
388
+ | 10,000 | 100 | 2,887.18 ms / 10,000 matches | 2,889.77 ms / 10,000 matches | 2,739.98 ms / 100 matches |
389
+
390
+ Traversal timing includes following cursors and retaining page results; assertions
391
+ run afterward. The fixture uses immutable in-memory data and does not model
392
+ concurrent ingestion, HTTP transport or a streaming consumer's memory use.
393
+ Increasing the fixture tenfold increased total traversal time much more than
394
+ tenfold. First-page latency therefore does not establish full-scan scalability;
395
+ repeated snapshot checks and offset-based SQL remain costs to assess for larger
396
+ workloads.
397
+
398
+ ### Current-runtime pagination baseline
399
+
400
+ Sequential reruns of the unchanged fixtures on macOS arm64 with SQLite 3.53.4
401
+ retain first-page and full-traversal samples in
402
+ [`benchmarks/pagination-current-review.json`](../../benchmarks/pagination-current-review.json).
403
+ At 10,000 claims, the medians were:
404
+
405
+ | Query | Node 24.21.0 first page / traversal | Node 26.8.1 first page / traversal |
406
+ |---|---|---|
407
+ | Valid-time | 25.33 ms / 3.146 s | 25.50 ms / 3.109 s |
408
+ | Exact-number | 25.25 ms / 3.210 s | 25.09 ms / 3.114 s |
409
+ | Selective valid-time | 22.18 ms / 2.772 s | 22.13 ms / 2.746 s |
410
+
411
+ These are current baselines, not before/after optimization measurements. Each
412
+ first-page median uses five samples and each traversal median uses three; the
413
+ benchmark has no separate untimed warmup. All ordering, completeness, snapshot
414
+ and termination assertions passed. Full traversal still costs substantially more
415
+ than a first page. Before changing cursor semantics or introducing caches,
416
+ measure time spent in SQL windows, snapshot revision checks and record projection
417
+ separately. Any optimization must retain rejection of historical arrivals and
418
+ new lineage touching the snapshot, while accepting wholly future appends.
419
+
420
+ ### Pagination cost attribution
421
+
422
+ Run `node --disable-warning=ExperimentalWarning scripts/pagination-profile.mjs`
423
+ alone for diagnostic attribution. Each 1,000/10,000-row fixture traverses once
424
+ without instrumentation, then once with synchronous method timers. It compares
425
+ complete results and checks order, termination, the fixed snapshot and unchanged
426
+ history outside timing. These are single diagnostic runs in a fixed order, not
427
+ latency medians or an estimate of instrumentation overhead. Original store methods
428
+ retain their own database, so their internal SQL is charged to vocabulary or
429
+ projection rather than counted again in the SQL categories. Unassigned time
430
+ includes other JavaScript work and instrumentation overhead.
431
+
432
+ [Raw profiles](../../benchmarks/pagination-profile-review.json) include runtime
433
+ versions, source hashes, method counts, category times and the plain traversals.
434
+ At 10,000 claims on macOS arm64 / SQLite 3.53.4:
435
+
436
+ | Query | Node 24.21.0 SQL windows / total | Node 26.8.1 SQL windows / total |
437
+ |---|---|---|
438
+ | Valid-time | 2.698 / 3.128 s | 2.663 / 3.085 s |
439
+ | Exact-number | 2.794 / 3.209 s | 2.700 / 3.093 s |
440
+ | Selective valid-time | 2.675 / 2.783 s | 2.664 / 2.779 s |
441
+
442
+ SQL windows dominated these pre-optimization profiles. Snapshot revision checks take under 43 ms
443
+ per traversal and historical vocabulary under 9 ms; caching either is therefore
444
+ unlikely to remove most of this fixture's cost. This motivated the bounded tuple-membership optimization below, retaining the
445
+ existing snapshot checks. Compare any
446
+ candidate on revised claim histories as well as one-row-per-key fixtures: a
447
+ faster query must not revive a superseded or retracted belief. The profile does
448
+ not identify which part of SQLite execution dominates or establish a general
449
+ query-performance guarantee.
450
+
451
+ ### Current-belief SQL comparison
452
+
453
+ `node scripts/current-query-bench.mjs` compares the production grouped latest-row
454
+ join with a read-only experimental correlated `MAX(tx)` lookup. Both use 10,000
455
+ historical rows and issue 100 SQL requests of 100 rows. With 10,000 current keys,
456
+ the requests traverse 100 pages; with 100 current keys, they repeat the single
457
+ current page. Three samples, execution plans and runtime versions are reported.
458
+ Row identities must agree across both alternatives; assertions run after timing.
459
+ This isolates SQL selection and does not include semantic record construction,
460
+ valid-time filtering or pagination revision checks.
461
+
462
+ On the runtime above, medians were:
463
+
464
+ | Current keys | Grouped production SQL | Correlated alternative |
465
+ |---|---|---|
466
+ | 10,000 | 844.81 ms | 517.86 ms |
467
+ | 100 | 381.47 ms | 518.86 ms |
468
+
469
+ The alternative improves the all-distinct fixture but regresses the fixture with
470
+ many revisions per key, so it is not used as a blanket replacement. Both plans
471
+ still sort results for transaction ordering. Future query-plan changes should
472
+ cover both history distributions and preserve historical boundaries and latest
473
+ row semantics before replacing the shared store fragment.
474
+
475
+
476
+ ### Late full-row fetch trial
477
+
478
+ `node --disable-warning=ExperimentalWarning scripts/pagination-row-width-bench.mjs`
479
+ compares full-row sorting with selecting ordered page IDs first and fetching
480
+ complete rows afterward. Both keep the same grouped latest-belief selection and
481
+ historical boundary. Each fixture has 10,000 historical rows and issues 100
482
+ requests of 100 rows, traversing distinct keys or repeatedly reading one page
483
+ of heavily revised keys. Three timed samples alternate variant order, with full
484
+ row equality and page-size assertions outside timing; there is no separate
485
+ warmup. [Raw results and plans](../../benchmarks/pagination-row-width-review.json)
486
+ retain SQL and runtime metadata.
487
+
488
+ On macOS arm64 with SQLite 3.53.4, medians were:
489
+
490
+ | Current keys | Node 24.21.0 full / late fetch | Node 26.8.1 full / late fetch |
491
+ |---|---|---|
492
+ | 10,000 | 1078.14 / 945.02 ms | 1056.83 / 880.51 ms |
493
+ | 100 | 405.61 / 421.93 ms | 404.55 / 403.53 ms |
494
+
495
+ The distinct-key fixture improves by about 12–17%, but the revised-key fixture
496
+ is about 4% slower on Node 24 and nearly unchanged on Node 26. This is not enough
497
+ evidence for a blanket production replacement. The trial remains read-only
498
+ experimental SQL; it does not change query execution or cursor formats. It also
499
+ omits record projection, valid-time post-filtering and snapshot revision checks,
500
+ so its timings are not end-to-end page latencies. Further trials should target
501
+ repeated selection work without sacrificing revised-history behavior.
502
+
503
+ ### Current-row join plan trial
504
+
505
+ `node --disable-warning=ExperimentalWarning scripts/current-join-bench.mjs`
506
+ retains both transaction and claim-key equality checks, but adds unary plus to
507
+ the outer claim-key expression. The experiment uses the same two history
508
+ fixtures and three alternating-order samples as the row-width trial above.
509
+ Complete returned rows must agree. On Node 24.21.0 and 26.8.1 with SQLite 3.53.4,
510
+ the query plan remained unchanged: SQLite still looked up grouped latest rows
511
+ through an automatic claim-key index. This did not establish a speedup.
512
+
513
+ `--plans-only` inspects a second variant with unary plus on both claim-key
514
+ expressions without timing or executing it. It loses the automatic lookup and
515
+ scans the grouped rows repeatedly. A Node 24 timing attempt was interrupted
516
+ with SIGINT after becoming substantially slower; no completed timing is claimed
517
+ for that attempt. [Raw completed samples and plans](../../benchmarks/current-join-review.json)
518
+ retain the evidence from both runtimes. Neither experiment changes production
519
+ SQL. Do not simply drop the claim-key check: the SQL schema indexes transaction
520
+ IDs but does not declare them unique, and structural schema validation does not
521
+ validate every stored row's identity. A read-plan optimization must preserve the
522
+ existing join semantics instead of assuming stronger physical constraints.
523
+
524
+ ### Bounded tuple-membership selection
525
+
526
+ Bounded, unresolved direct queries now select current rows using membership in
527
+ `(claim_key, MAX(tx))` pairs. Both identity columns and the complete historical
528
+ source remain part of selection, so filtering a page cannot revive an older or
529
+ retracted belief. Unbounded, resolved and transitive plans retain their existing
530
+ selection strategy. Snapshot revision checks, ordering, scan budgets and cursor
531
+ formats are unchanged.
532
+
533
+ `node --disable-warning=ExperimentalWarning scripts/current-membership-bench.mjs`
534
+ compares the former join and tuple membership on the same distinct/revised-key
535
+ SQL fixtures used above, with three alternating-order samples and full-row
536
+ comparison outside timing. Membership improves both distributions on Node 24.21.0
537
+ and 26.8.1. Sequential end-to-end reruns of `scripts/pagination-bench.mjs` also
538
+ pass every traversal assertion. At 10,000 claims on macOS arm64 / SQLite 3.53.4:
539
+
540
+ | Traversal | Node 24.21.0 before → after | Node 26.8.1 before → after |
541
+ |---|---|---|
542
+ | Valid-time | 3.146 → 2.561 s | 3.109 → 2.587 s |
543
+ | Exact-number | 3.210 → 2.541 s | 3.114 → 2.611 s |
544
+ | Selective valid-time | 2.772 → 2.239 s | 2.746 → 2.239 s |
545
+
546
+ [Raw controls and before/after samples](../../benchmarks/pagination-membership-review.json)
547
+ retain the three-sample traversal and five-sample first-page measurements.
548
+ These gains apply to the recorded in-memory workloads, not every query or
549
+ adapter. Regression tests compare bounded and unbounded results across historical
550
+ boundaries, numeric filters, retractions and reactivation; existing tests retain
551
+ alias, pagination revision, resolved and transitive coverage. Full traversal
552
+ still repeats selection and offset work, so this is not a general scan-cost bound.
553
+
554
+ ## Query read-snapshot trial
555
+
556
+ Run `node --disable-warning=ExperimentalWarning scripts/query-snapshot-bench.mjs`
557
+ alone. It queries `?item IS service` with `limit: 1` over 100 and 1,000 current
558
+ claims, in memory and through a read-only WAL connection, both outside and inside
559
+ a caller read transaction. Each result is a median of five batches of 20 calls,
560
+ after one warm-up call. Setup, transaction entry/exit and result assertions are
561
+ outside the timing. Resolved queries are included as a control: they already
562
+ used snapshots before ordinary matching gained the same consistency guarantee.
563
+
564
+ On macOS arm64, the following ordinary-query medians compare the preceding
565
+ resolved-only snapshot path with snapshots around all matching. The baseline was
566
+ measured by temporarily restoring the previous conditional path; the script
567
+ itself always measures the checked-out implementation and has no unsafe mode.
568
+
569
+ | Claims | Database | Caller transaction | Node 24 before → after | Node 26 before → after |
570
+ |---|---|---|---|---|
571
+ | 100 | memory | no | 0.172 → 0.174 ms | 0.173 → 0.180 ms |
572
+ | 100 | memory | yes | 0.167 → 0.167 ms | 0.172 → 0.165 ms |
573
+ | 100 | wal-read-only | no | 0.168 → 0.167 ms | 0.173 → 0.167 ms |
574
+ | 100 | wal-read-only | yes | 0.167 → 0.169 ms | 0.160 → 0.161 ms |
575
+ | 1,000 | memory | no | 1.058 → 1.043 ms | 1.045 → 1.042 ms |
576
+ | 1,000 | memory | yes | 1.061 → 1.042 ms | 1.062 → 1.048 ms |
577
+ | 1,000 | wal-read-only | no | 1.041 → 1.061 ms | 1.080 → 1.063 ms |
578
+ | 1,000 | wal-read-only | yes | 1.051 → 1.062 ms | 1.090 → 1.042 ms |
579
+
580
+ Runtime versions were Node 24.16.0 and 26.5.0. Resolved control medians ranged
581
+ from 1.080–1.139 ms at 100 claims and 5.758–6.410 ms at 1,000 claims across both
582
+ runs. The small before/after differences do not isolate a reliable savepoint
583
+ cost or establish a speedup. Baseline runs preceded final runs, so cache and
584
+ execution-order effects remain possible. These fixtures do not bound large-graph
585
+ costs, contention, transitive queries, or WASM performance. Snapshot correctness
586
+ is established by the separate concurrent-writer regressions, not these timings.
587
+
588
+
589
+ ## Structured-record projection trial
590
+
591
+ Run `node --disable-warning=ExperimentalWarning scripts/query-record-bench.mjs`
592
+ alone. It returns 100 or 1,000 records for `?item IS service`, with either no
593
+ metadata or three contexts, three tags and source provenance. Memory and read-only
594
+ WAL stores are exercised outside and inside a caller read transaction. Each
595
+ median uses five batches of three calls after one warm-up. Setup, transaction
596
+ entry/exit, full record decoding and result equality checks are untimed.
597
+ Raw reports include Node launch arguments and selected source-file SHA-256 hashes
598
+ captured before the workloads. Launch arguments identify an enabled `--import`
599
+ baseline loader. Hashes describe the on-disk inputs, including the loader files;
600
+ the guarded loaders describe their in-memory transformations. This is selected
601
+ source provenance, not a complete dependency or machine-environment fingerprint.
602
+
603
+ The [paired measurements](../../benchmarks/query-record-validation-review.json)
604
+ compare the earlier reviewed implementation with a
605
+ [benchmark-only loader](../../benchmarks/query-record-baseline.mjs) that omits
606
+ record-construction identity/key guards and the outer projection snapshot. The
607
+ loader checks its source replacements and does not edit runtime files. The
608
+ historical loader is tied to the source hashes in that artifact and now rejects
609
+ the changed constructor. Use the capture comparison below for the current source.
610
+
611
+ For 1,000 returned records on macOS arm64, medians were:
612
+
613
+ | Metadata | Database | Caller transaction | Node 24.21.0 before → reviewed | Node 26.8.1 before → reviewed |
614
+ |---|---|---|---|---|
615
+ | none | memory | no | 14.77 → 16.60 ms | 15.02 → 16.67 ms |
616
+ | none | memory | yes | 13.37 → 15.24 ms | 14.95 → 15.79 ms |
617
+ | none | read-only WAL | no | 17.59 → 15.74 ms | 17.71 → 15.95 ms |
618
+ | none | read-only WAL | yes | 13.11 → 15.21 ms | 14.92 → 15.18 ms |
619
+ | populated | memory | no | 32.73 → 33.67 ms | 31.17 → 32.26 ms |
620
+ | populated | memory | yes | 32.49 → 33.19 ms | 30.95 → 32.21 ms |
621
+ | populated | read-only WAL | no | 38.69 → 33.12 ms | 36.41 → 37.28 ms |
622
+ | populated | read-only WAL | yes | 32.22 → 33.32 ms | 32.31 → 36.28 ms |
623
+
624
+ This combined comparison does not isolate the cost of a particular guard or
625
+ savepoint. Baselines preceded current runs, so cache and run-order effects can
626
+ explain some differences; faster cases do not establish a speedup. These results
627
+ do not justify weakening consistency checks. They do not bound large graphs,
628
+ transitive support, malformed-data handling, HTTP or browser performance. The
629
+ artifact retains all 100-record cases, raw samples and source hashes.
630
+
631
+ A [later capture trial](../../benchmarks/query-record-capture-review.json) measures
632
+ claim and provenance capture plus provenance validation with identity guards and
633
+ query snapshots retained on both paths. Its guarded
634
+ [baseline loader](../../benchmarks/query-record-capture-baseline.mjs) is enabled
635
+ with `--import ./benchmarks/query-record-capture-baseline.mjs`. The same 16 cases
636
+ and untimed correctness checks apply. Baseline and current runs were sequential
637
+ on Node 24, then Node 26, without concurrent builds or tests.
638
+
639
+ For 1,000 returned records, current median ranges across memory/WAL and caller
640
+ transaction settings were:
641
+
642
+ | Metadata | Node 24.21.0 | Node 26.8.1 |
643
+ |---|---|---|
644
+ | none | 21.07–23.27 ms | 20.98–21.35 ms |
645
+ | populated | 40.23–41.42 ms | 40.33–41.50 ms |
646
+
647
+ On Node 24, the capture boundary added roughly 6–9 ms in most 1,000-record cases.
648
+ One Node 26 baseline case was anomalously slow; its apparent improvement is not
649
+ interpreted as a speedup. An additional trial combining both captures into one
650
+ `structuredClone` call showed no consistent benefit and was not retained. All
651
+ samples and the trial's source hash are preserved in the artifact. These local
652
+ measurements support keeping the ownership guarantees, not a general latency
653
+ bound or an independently measured cost for each validation step.
654
+
655
+ The [query-match capture trial](../../benchmarks/query-match-capture-review.json)
656
+ isolates the later `Query.Record.of` capture and metadata validation boundary
657
+ while retaining nested claim/provenance capture and query snapshots. Its guarded
658
+ [baseline loader](../../benchmarks/query-match-baseline.mjs) uses
659
+ `--import ./benchmarks/query-match-baseline.mjs` with the same benchmark command.
660
+ Sequential before/current runs on Node 24 and then Node 26 passed all equality
661
+ and decode assertions. For 1,000 direct records, current median ranges were:
662
+
663
+ | Metadata | Node 24.21.0 | Node 26.8.1 |
664
+ |---|---|---|
665
+ | none | 22.15–24.38 ms | 22.81–24.29 ms |
666
+ | populated | 43.39–43.89 ms | 42.15–43.02 ms |
667
+
668
+ The observed increment was 0.3–3.4 ms per 1,000 records across these cases.
669
+ The artifact retains raw samples, commands and source hashes. These are direct,
670
+ non-interpolated matches; dense transitive support and interpolation need separate
671
+ measurements. One local paired trial does not establish a latency guarantee or
672
+ separate the cost of capture from metadata validation.
@@ -3,7 +3,7 @@ import * as Compile from './compile.ts';
3
3
  import * as Pattern from './pattern.ts';
4
4
  export type { Match, Options } from './compile.ts';
5
5
  export type { Window } from './compile.ts';
6
- export declare const window: (store: Store, pattern: Pattern.t, options?: Compile.Options) => Compile.Window;
6
+ export declare const window: (store: Store, pattern: Pattern.t, options?: Compile.Options, positions?: WeakMap<object, number>) => Compile.Window;
7
7
  export declare const match: (store: Store, pattern: Pattern.t, options?: Compile.Options) => Compile.Match[];
8
8
  export declare const query: (store: Store, input: string, options?: Compile.Options) => Compile.Match[];
9
9
  //# sourceMappingURL=bounded.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bounded.d.ts","sourceRoot":"","sources":["../../src/bounded.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AACpD,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AACvC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AAEvC,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAClD,YAAY,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AA4D1C,eAAO,MAAM,MAAM,GACjB,OAAO,KAAK,EACZ,SAAS,OAAO,CAAC,CAAC,EAClB,UAAS,OAAO,CAAC,OAAY,KAC5B,OAAO,CAAC,MAA0C,CAAA;AAErD,eAAO,MAAM,KAAK,GAChB,OAAO,KAAK,EACZ,SAAS,OAAO,CAAC,CAAC,EAClB,UAAS,OAAO,CAAC,OAAY,KAC5B,OAAO,CAAC,KAAK,EAA8C,CAAA;AAE9D,eAAO,MAAM,KAAK,GAChB,OAAO,KAAK,EACZ,OAAO,MAAM,EACb,UAAS,OAAO,CAAC,OAAY,KAC5B,OAAO,CAAC,KAAK,EAC6B,CAAA"}
1
+ {"version":3,"file":"bounded.d.ts","sourceRoot":"","sources":["../../src/bounded.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AACpD,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AACvC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AAIvC,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAClD,YAAY,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAwE1C,eAAO,MAAM,MAAM,GACjB,OAAO,KAAK,EACZ,SAAS,OAAO,CAAC,CAAC,EAClB,UAAS,OAAO,CAAC,OAAY,EAC7B,YAAY,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,KAClC,OAAO,CAAC,MAAqD,CAAA;AAEhE,eAAO,MAAM,KAAK,GAChB,OAAO,KAAK,EACZ,SAAS,OAAO,CAAC,CAAC,EAClB,UAAS,OAAO,CAAC,OAAY,KAC5B,OAAO,CAAC,KAAK,EAA8D,CAAA;AAE9E,eAAO,MAAM,KAAK,GAChB,OAAO,KAAK,EACZ,OAAO,MAAM,EACb,UAAS,OAAO,CAAC,OAAY,KAC5B,OAAO,CAAC,KAAK,EACuC,CAAA"}
@@ -1,6 +1,8 @@
1
1
  import { Value } from '@cavelang/core';
2
2
  import * as Compile from "./compile.js";
3
3
  import * as Pattern from "./pattern.js";
4
+ import { captureOptions, capturePattern } from "./capture.js";
5
+ import { readSnapshot } from "./read-snapshot.js";
4
6
  /**
5
7
  * Compile historical queries against vocabulary reconstructed at the same
6
8
  * transaction-time boundary as the rows. The store remains otherwise
@@ -20,13 +22,18 @@ const storeAtBoundary = (store, options) => {
20
22
  * value filter pushes the normalized comparison into SQL; the final check
21
23
  * preserves exact unit and approximation semantics.
22
24
  */
23
- const execute = (store, pattern, options = {}) => {
25
+ const execute = (store, pattern, options = {}, positions) => {
26
+ // Use the same caller values for historical vocabulary, SQL and post-filters.
27
+ options = captureOptions(options);
28
+ return readSnapshot(store, () => executeCaptured(store, pattern, options, positions));
29
+ };
30
+ const executeCaptured = (store, pattern, options, positions) => {
24
31
  if (pattern.payload.kind !== 'attribute' || pattern.payload.value.kind !== 'term') {
25
- return Compile.window(storeAtBoundary(store, options), pattern, options);
32
+ return Compile.window(storeAtBoundary(store, options), pattern, options, positions);
26
33
  }
27
34
  const value = Value.parse(pattern.payload.value.text);
28
35
  if (value.kind !== 'number' || value.num === undefined) {
29
- return Compile.window(storeAtBoundary(store, options), pattern, options);
36
+ return Compile.window(storeAtBoundary(store, options), pattern, options, positions);
30
37
  }
31
38
  const expectedNum = value.num;
32
39
  const filter = {
@@ -44,7 +51,7 @@ const execute = (store, pattern, options = {}) => {
44
51
  };
45
52
  const expectedUnit = value.unit ?? null;
46
53
  const expectedApprox = value.approx ? 1 : 0;
47
- const result = Compile.window(storeAtBoundary(store, options), normalized, options);
54
+ const result = Compile.window(storeAtBoundary(store, options), normalized, options, positions);
48
55
  return { ...result, matches: result.matches.filter(match => {
49
56
  const row = match.row;
50
57
  return row !== undefined &&
@@ -53,7 +60,7 @@ const execute = (store, pattern, options = {}) => {
53
60
  row.value_approx === expectedApprox;
54
61
  }) };
55
62
  };
56
- export const window = (store, pattern, options = {}) => execute(store, pattern, options);
57
- export const match = (store, pattern, options = {}) => execute(store, pattern, options).matches;
58
- export const query = (store, input, options = {}) => match(store, Pattern.parse(input), options);
63
+ export const window = (store, pattern, options = {}, positions) => execute(store, pattern, options, positions);
64
+ export const match = (store, pattern, options = {}) => execute(store, capturePattern(pattern), options).matches;
65
+ export const query = (store, input, options = {}) => execute(store, Pattern.parse(input), options).matches;
59
66
  //# sourceMappingURL=bounded.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bounded.js","sourceRoot":"","sources":["../../src/bounded.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bounded.js","sourceRoot":"","sources":["../../src/bounded.ts"],"names":[],"mappings":"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"}