fsqlite-core 0.1.5

Core engine: connection, prepare, schema, DDL/DML codegen
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
# Primitive-Selection Decision Cards — bd-db300.5.3.2.2

> One explicit decision card per hot metadata class from
> `HOT_METADATA_INVENTORY.md`.
> This artifact is meant to be strong enough that `bd-db300.5.3.3.1`
> can implement against it without reopening the selection debate.

---

## Closure Map

| Metadata Class | Selected Primitive | Change Type | EV | Relevance |
|----------------|--------------------|-------------|----|-----------|
| M1 `PublishedPagerState` | Keep seqlock publication, remove `Condvar`, retain bounded retry | Tune current primitive | 6/10 | High |
| M2 `BoundPagerPublication` | Keep stack-copy local binding | No change | 2/10 | Medium |
| M3 schema cookie + generation | Keep per-connection scalar invalidation tokens | No change | 1/10 | Medium |
| M4 cached read snapshot | Keep per-connection parked snapshot reuse | No change | 5/10 | Medium |
| M5 cached write txn (`:memory:`) | Keep per-connection retained write txn | No change | 7/10 | High for `:memory:` |
| M6 `PagerInner` committed state | Immutable snapshot publication with generation replacement | New primitive | 8/10 | Critical |
| M7 WAL frame count + checksum state | Keep per-handle refresh model | No change | 2/10 | Medium |
| M8 WAL generation identity | Keep copy-sized per-handle identity | No change | 1/10 | Low |
| M9 MemDB visible commit seq gate | Keep local monotone staleness gate | No change | 3/10 | Medium |
| M10 cached VDBE engine | Keep per-connection engine reuse | No change | 4/10 | Medium |
| M11 concurrent registry + page-lock table | Shard the registry, keep CAS lock table fast path | New primitive for registry only | 7/10 | High |
| M12 parse cache + compiled statement cache | Keep per-connection cache; reject shared publication for now | No change | 4/10 | Medium |

The cards below all include the same closure fields:
EV, relevance, primary risk, fallback trigger, logging, verification,
user-visible failure signature, rejected alternatives, baseline comparator,
adoption wedge, rollback recipe, source status, and interference status.

---

## Card 1: M6 — `PagerInner` Committed State

**Current primitive:** `Mutex<PagerInner>` in `pager.rs`; every `pager.begin()`
and `pager.commit()` passes through it.

**Chosen primitive:** Immutable snapshot publication with generation
replacement. Writers materialize a frozen `PagerCommittedSnapshot` and publish
it atomically; readers bind the latest published snapshot without taking the
hot `PagerInner` mutex.

**Why this fits:**
- Readers need a coherent committed-state summary, not mutable access to the
  whole `PagerInner`.
- Writers already have a natural publish boundary at commit.
- Snapshot reclamation can stay simple via `Arc` ownership.
- This attacks the hottest remaining shared lock in the file-backed path.

**EV score:** 8/10

**Relevance:** Critical for c4+ file-backed workloads because it removes the
highest-value shared read-side mutex from the begin/commit path.

**Primary risk and countermeasure:**
- Risk: snapshot publication drifts from the authoritative inner state across
  DDL, freelist, or checkpoint transitions.
- Countermeasure: build the snapshot while `PagerInner` is still held, publish
  only after the committed state is finalized, and shadow-compare snapshot
  fields against the legacy read path during rollout.

**Budgeted mode and fallback trigger:**
- Target mode: immutable snapshot publication on by default after proof.
- Fallback trigger: snapshot allocation or refcount churn exceeds 5% of commit
  time, or shadow compare reports any field mismatch.
- Fallback action: force legacy mutex read path and keep publishing diagnostic
  snapshots only.

**Required logging fields:**
- `trace_id`
- `metadata_class="M6"`
- `operation=publish|read|shadow_compare`
- `snapshot_generation`
- `visible_commit_seq`
- `db_size`
- `freelist_count`
- `alloc_ns`
- `snapshot_age_commits`
- `control_mode`
- `shadow_verdict`

**Required verification:**
- Unit: concurrent read during publish never blocks on the legacy mutex.
- Unit: published snapshot matches the just-committed inner state.
- Unit: retired snapshots remain valid until the last reader drops them.
- E2E: file-backed c4 write-heavy replay shows reduced mutex wait time versus
  baseline.
- Topology: c8 cross-node run confirms refcount churn does not erase the gain.

**User-visible symptom signature and operator diagnostic cues:**
- Symptom: stale reads immediately after another connection commits.
- Diagnostic: published `snapshot_generation` or `visible_commit_seq` trails the
  live commit sequence; `shadow_verdict=diverged`.

**Rejected alternatives and why they lost:**
- Seqlock on `PagerInner` fields: `freelist` and other mutable members are not
  safe torn-read payloads.
- Left-Right: duplicates too much pager state for a relatively small summary.
- `RwLock`: adds reader-side atomic traffic without removing enough contention.
- BRAVO: wrong shape for a ~2:1 read/write surface.
- Sharding `PagerInner`: the committed state is singular, not partitionable.

**Baseline comparator:** current `Mutex<PagerInner>` read-side access during
begin/commit.

**Adoption wedge / shadow-run plan:**
- Add a control mode with `legacy`, `shadow_compare`, and `snapshot`.
- Ship `shadow_compare` first and require clean comparator runs on the canonical
  file-backed workloads before defaulting to `snapshot`.

**Rollback recipe:**
1. Force `legacy` mode.
2. Leave snapshot construction optionally enabled for diagnostics only.
3. Drain outstanding snapshots naturally via `Arc` lifetime.

**Primary source or paper status:** inventory-backed and code-indexed from
`HOT_METADATA_INVENTORY.md`; no external paper dependency is required to adopt
this primitive family.

**Interference-test requirement / status:**
- Requirement: verify interaction with WAL policy and admission control under
  mixed workloads.
- Current status: baseline file-backed rationale complete; cross-node stress is
  still required before default-on promotion.

---

## Card 2: M1 — `PublishedPagerState`

**Current primitive:** seqlock-style atomic summary with a writer-side
`publish_lock: Mutex` and `sequence_cv: Condvar`.

**Chosen primitive:** retain the seqlock summary, remove the `Condvar`, and keep
bounded retry as the read-side contract.

**Why this fits:**
- The payload is already a tiny fixed-width summary.
- Readers are retry-capable and only need coherence, not ownership.
- The expensive parts are wake/sleep choreography and auxiliary serialization,
  not the seqlock itself.

**EV score:** 6/10

**Relevance:** High for multi-connection file-backed visibility checks, but
below M6 because the core seqlock shape is already close to optimal.

**Primary risk and countermeasure:**
- Risk: replacing `Condvar` wakeups with polling burns CPU or starves a waiter
  under heavy churn.
- Countermeasure: bounded exponential backoff, striped retry counters, and a
  forced-legacy mode for shadow comparison.

**Budgeted mode and fallback trigger:**
- Target mode: `seqlock_polling`.
- Fallback trigger: polling spin budget exceeds threshold or p99 visibility wait
  regresses against the `Condvar` baseline.
- Fallback action: re-enable `Condvar` waiting while preserving the seqlock
  publication layout.

**Required logging fields:**
- `trace_id`
- `metadata_class="M1"`
- `operation=publish|read|wait`
- `sequence_value`
- `read_retry_count`
- `poll_spins`
- `wait_ns`
- `control_mode`
- `shadow_verdict`

**Required verification:**
- Unit: concurrent publish/read never surfaces torn field combinations.
- Unit: retry count stays bounded under publish churn.
- Unit: forced legacy and forced polling modes produce identical snapshots.
- E2E: c8 mixed file-backed workload shows lower wait overhead than the
  `Condvar` path.
- Topology: cross-node run measures whether polling changes tail latency.

**User-visible symptom signature and operator diagnostic cues:**
- Symptom: unusually stale visibility checks or CPU spikes during read polling.
- Diagnostic: `read_retry_count`, `poll_spins`, or `wait_ns` spike above the
  comparator baseline.

**Rejected alternatives and why they lost:**
- Arc snapshot swap: adds allocation/refcount cost to an already-small payload.
- Epoch/EBR publication: too much machinery for a retryable summary.
- Independent atomics without seqlock: permits torn cross-field reads.

**Baseline comparator:** current seqlock + `publish_lock` + `sequence_cv`
implementation.

**Adoption wedge / shadow-run plan:**
- Ship `legacy`, `shadow_compare`, and `polling` modes.
- Require stable retry counts and lower p99 wait on the canonical mixed
  workloads before promotion.

**Rollback recipe:**
1. Restore `Condvar` wait path.
2. Keep counters and shadow-compare instrumentation.
3. Preserve the seqlock read contract.

**Primary source or paper status:** inventory-backed and already represented in
code; no new external primitive research is required.

**Interference-test requirement / status:**
- Requirement: prove admission-control readers are not misled by the new wait
  distribution.
- Current status: controller-composition risk identified; replay validation
  remains mandatory before default-on.

---

## Card 3: M11 — Concurrent Registry + Page-Lock Table

**Current primitive:** global `Arc<Mutex<ConcurrentRegistry>>` for session
begin/commit/abort, plus CAS fast path in `InProcessPageLockTable`.

**Chosen primitive:** shard the registry by session id / core-locality while
keeping the existing CAS page-lock table as the ownership primitive.

**Why this fits:**
- The registry is the contended shared control-plane object.
- The page-lock table already uses the correct exact-ownership primitive.
- Sharding cuts mutex scope without introducing unsafe or complicated
  reclamation.

**EV score:** 7/10

**Relevance:** High for c8+ file-backed concurrent-writer workloads where
session lifecycle contention is material.

**Primary risk and countermeasure:**
- Risk: shard routing bugs or uneven shard hot spots create session lookup
  errors or move contention rather than removing it.
- Countermeasure: deterministic shard mapping, shard-local instrumentation, and
  global shadow verification that every routed session is found on the expected
  shard.

**Budgeted mode and fallback trigger:**
- Target mode: `sharded_registry`.
- Fallback trigger: `session_not_found`, shard skew, or commit/abort routing
  mismatch above zero tolerance.
- Fallback action: route all operations back through the legacy global mutex.

**Required logging fields:**
- `trace_id`
- `metadata_class="M11"`
- `operation=begin|commit|abort|lookup`
- `session_id`
- `shard_id`
- `lock_wait_ns`
- `active_sessions_in_shard`
- `control_mode`
- `shadow_verdict`

**Required verification:**
- Unit: deterministic shard assignment for a given `session_id`.
- Unit: begin/commit/abort on different shards never lose sessions.
- Unit: page-lock CAS semantics remain unchanged.
- E2E: c8 hot-page contention replay shows lower registry wait time versus the
  global mutex comparator.
- Topology: cross-node run checks for shard skew and routing imbalance.

**User-visible symptom signature and operator diagnostic cues:**
- Symptom: unexpected `SQLITE_BUSY` or session lookup failures during concurrent
  traffic.
- Diagnostic: non-zero `session_not_found`, shard skew above threshold, or
  `shadow_verdict=diverged`.

**Rejected alternatives and why they lost:**
- Lock-free registry with epoch reclamation: too complex for the ownership
  shape and error surface.
- `RwLock` registry: lifecycle ops are mostly writes, so reader bias does not
  help.
- Session pre-allocation: does not solve shared registry visibility or variable
  write-set ownership.

**Baseline comparator:** current single global `ConcurrentRegistry` mutex plus
existing CAS page locks.

**Adoption wedge / shadow-run plan:**
- Ship `legacy`, `shadow_compare`, and `sharded` modes.
- Promote only after comparator runs show lower lock wait without correctness
  drift.

**Rollback recipe:**
1. Force `legacy` mode.
2. Let in-flight sharded sessions drain naturally.
3. Keep shard instrumentation for postmortem analysis.

**Primary source or paper status:** inventory-backed; no external lock-free
paper is required because the selected answer is deliberate sharding, not a
novel concurrent structure.

**Interference-test requirement / status:**
- Requirement: verify interaction with admission control and hot-page routing.
- Current status: baseline rationale complete; topology-sensitive replay still
  required before graduation.

---

## Card 4: M2 — `BoundPagerPublication`

**Current primitive:** write-once local struct copied at statement entry.

**Chosen primitive:** keep the stack/local binding exactly local.

**Why this fits:** M2 is a statement-scoped bundle of M1 output, not an
independent shared publication surface.

**EV score:** 2/10

**Relevance:** Medium because it sits on the hot path, but the leverage is in
M1, not in wrapping M2 with another primitive.

**Primary risk and countermeasure:**
- Risk: turning M2 into a shared cache reuses stale visibility evidence across
  statements.
- Countermeasure: keep M2 write-once per statement and refresh from M1/M6.

**Budgeted mode and fallback trigger:**
- Target mode: unchanged local binding.
- Fallback trigger: none specific to M2; if statement-entry bind cost matters,
  optimize the upstream publication source instead.

**Required logging fields:**
- `trace_id`
- `metadata_class="M2"`
- `operation=bind`
- `visible_commit_seq`
- `read_retry_count`
- `statement_id`
- `is_memory`

**Required verification:**
- Unit: each file-backed statement binds a fresh local copy.
- Unit: no cross-statement reuse occurs.
- E2E: another connection's commit is visible after a fresh bind.
- Topology: none beyond upstream M1 checks.

**User-visible symptom signature and operator diagnostic cues:**
- Symptom: statement runs against a stale publication bind.
- Diagnostic: local `visible_commit_seq` trails the current published sequence
  at statement entry.

**Rejected alternatives and why they lost:**
- Shared `Arc` wrapper: no reuse benefit, adds lifetime coupling.
- Seqlock around local struct: no shared readers exist.
- RCU cache of bound structs: wrong scope and stale by construction.

**Baseline comparator:** current stack-copy bind path.

**Adoption wedge / shadow-run plan:** no rollout needed; keep instrumentation
only.

**Rollback recipe:** unchanged local bind remains the fallback and default.

**Primary source or paper status:** code-indexed inventory only; no external
primitive source required.

**Interference-test requirement / status:** none independent; M2 inherits M1/M6
interference behavior.

---

## Card 5: M3 — Schema Cookie + Generation

**Current primitive:** per-connection `RefCell<u32>` / `Cell<u64>` invalidation
tokens.

**Chosen primitive:** keep local scalar invalidation tokens.

**Why this fits:** readers only need cheap mismatch detection so they can
re-prepare locally; they do not need a shared schema-object publication plane.

**EV score:** 1/10

**Relevance:** Medium for correctness, low for performance.

**Primary risk and countermeasure:**
- Risk: over-engineering this into a shared publication object creates extra
  invalidation states and stale-schema failure modes.
- Countermeasure: keep scalar mismatch checks local and continue reparsing on
  mismatch.

**Budgeted mode and fallback trigger:**
- Target mode: unchanged local token checks.
- Fallback trigger: none; if reparses become expensive, optimize schema reload
  itself, not token publication.

**Required logging fields:**
- `trace_id`
- `metadata_class="M3"`
- `schema_cookie`
- `schema_generation`
- `operation=check|invalidate`
- `reprepare_triggered`

**Required verification:**
- Unit: DDL bumps the cookie/generation as expected.
- Unit: prepared statements invalidate on mismatch.
- E2E: cross-connection schema change forces reprepare.
- Topology: none.

**User-visible symptom signature and operator diagnostic cues:**
- Symptom: stale prepared statement or unnecessary reprepare churn.
- Diagnostic: mismatch counters versus prepare cache hit rate.

**Rejected alternatives and why they lost:**
- Shared schema-object RCU publication: wrong scope for the current local cache
  model.
- Seqlock token pair: unnecessary for two local scalars.
- BRAVO / lock-based schema publication: adds shared traffic without value.

**Baseline comparator:** current local scalar check at execute/query time.

**Adoption wedge / shadow-run plan:** none; the chosen primitive is the current
one.

**Rollback recipe:** current local tokens remain the permanent rollback path.

**Primary source or paper status:** inventory-backed and code-local.

**Interference-test requirement / status:** none; M3 is connection-local.

---

## Card 6: M4 — Cached Read Snapshot

**Current primitive:** per-connection parked read-only transaction handle guarded
by a local cookie.

**Chosen primitive:** keep per-connection parked snapshot reuse.

**Why this fits:** the value is already local, already profitable, and already
expresses the correct ownership model for a read snapshot.

**EV score:** 5/10

**Relevance:** Medium because it materially reduces begin overhead for read
paths, but it is not a shared publication surface.

**Primary risk and countermeasure:**
- Risk: sharing parked snapshots across connections would break ownership and
  visibility assumptions.
- Countermeasure: retain connection-local parking with stale-cookie
  invalidation.

**Budgeted mode and fallback trigger:**
- Target mode: unchanged cache-reuse path.
- Fallback trigger: cookie mismatch or any write/DDL boundary.
- Fallback action: fresh `pager.begin()`.

**Required logging fields:**
- `trace_id`
- `metadata_class="M4"`
- `operation=park|reuse|invalidate`
- `cookie_match`
- `snapshot_age_statements`
- `cache_hit`

**Required verification:**
- Unit: reuse path returns a valid read snapshot.
- Unit: stale cookie or write invalidates the parked snapshot.
- E2E: repeated read statements reuse locally without cross-connection leakage.
- Topology: none.

**User-visible symptom signature and operator diagnostic cues:**
- Symptom: missed reuse or reuse of a stale read snapshot.
- Diagnostic: `cache_hit` drops unexpectedly or stale-cookie reuse appears.

**Rejected alternatives and why they lost:**
- Shared snapshot pool: wrong ownership and reclamation model.
- RCU registry of parked read snapshots: no cross-connection sharing benefit.
- Seqlock wrapper: no shared read/write conflict exists here.

**Baseline comparator:** current local park/reuse mechanism versus always
starting a fresh read txn.

**Adoption wedge / shadow-run plan:** none beyond tracing the existing reuse
path.

**Rollback recipe:** call the existing invalidation path and fall back to fresh
begin.

**Primary source or paper status:** inventory-backed and already validated by
existing local wins.

**Interference-test requirement / status:** none independent; M4 consumes M6 and
M1 publication but does not publish shared state itself.

---

## Card 7: M5 — Cached Write Transaction (`:memory:` Fast Path)

**Current primitive:** per-connection retained write transaction guarded by a
local cookie.

**Chosen primitive:** keep retained local write-transaction reuse for `:memory:`
only.

**Why this fits:** the current path already removes a large amount of pager
ceremony and is intentionally constrained to a single-connection ownership
model.

**EV score:** 7/10

**Relevance:** High for `:memory:` throughput, but not a candidate for shared
metadata publication.

**Primary risk and countermeasure:**
- Risk: broadening this into a file-backed or shared cross-connection primitive
  would break concurrency and durability assumptions.
- Countermeasure: keep the optimization `:memory:`-only and invalidate on DDL,
  explicit `BEGIN`, or cookie mismatch.

**Budgeted mode and fallback trigger:**
- Target mode: current retained path for `:memory:`.
- Fallback trigger: stale cookie, schema change, explicit transactional mode
  change, or any non-memory backend.
- Fallback action: fresh write begin.

**Required logging fields:**
- `trace_id`
- `metadata_class="M5"`
- `operation=park|reuse|invalidate`
- `is_memory`
- `cookie_match`
- `cache_hit`

**Required verification:**
- Unit: `commit_and_retain()` reuse works on `:memory:`.
- Unit: file-backed paths never route through this optimization.
- E2E: repeated `:memory:` writes reuse without stale schema leakage.
- Topology: none.

**User-visible symptom signature and operator diagnostic cues:**
- Symptom: incorrect reuse after invalidation or missing reuse on the hot path.
- Diagnostic: reuse/park counters and cookie mismatch events.

**Rejected alternatives and why they lost:**
- Shared cross-connection write-handle pool: violates ownership and concurrency
  rules.
- RCU transaction pool: wrong lifetime model for mutable write state.
- Seqlock gating: adds shared mechanics to a local handle.

**Baseline comparator:** current retained write-txn path versus always opening a
fresh write transaction.

**Adoption wedge / shadow-run plan:** no new rollout needed; keep existing
instrumentation as the proof surface.

**Rollback recipe:** disable reuse and always open a fresh write transaction.

**Primary source or paper status:** inventory-backed and already justified by
existing measured wins.

**Interference-test requirement / status:** none; M5 is intentionally excluded
from shared publication.

---

## Card 8: M7 — WAL Frame Count + Running Checksum State

**Current primitive:** per-handle owned fields refreshed from the WAL file.

**Chosen primitive:** keep the per-handle refresh model.

**Why this fits:** the authoritative state is the WAL file plus durable snapshot
machinery elsewhere; this handle-local cache should remain cheap and private.

**EV score:** 2/10

**Relevance:** Medium for correctness and refresh cost, low as a publication
primitive candidate.

**Primary risk and countermeasure:**
- Risk: inventing a shared authoritative frame-count cache creates divergence
  from on-disk truth.
- Countermeasure: keep handle-local refresh semantics and optimize authoritative
  WAL publication separately.

**Budgeted mode and fallback trigger:**
- Target mode: unchanged per-handle refresh.
- Fallback trigger: none unique to M7; if refresh becomes dominant, address the
  upstream durable publication plane instead.

**Required logging fields:**
- `trace_id`
- `metadata_class="M7"`
- `operation=refresh|append`
- `frame_count`
- `checkpoint_seq`
- `refresh_ns`
- `rebuild_required`

**Required verification:**
- Unit: append updates local frame count and checksum correctly.
- Unit: refresh detects new frames and checkpoint resets.
- E2E: reader catches up to another writer's frames after refresh.
- Topology: file-backed mixed-reader/writer replay only.

**User-visible symptom signature and operator diagnostic cues:**
- Symptom: reader misses newly visible frames or rebuilds too often.
- Diagnostic: `rebuild_required` frequency, frame-count deltas, refresh latency.

**Rejected alternatives and why they lost:**
- Shared RCU frame index: too easy to diverge from the WAL file.
- Seqlock around local handle state: no shared readers.
- Global mutex cache: centralizes a handle-local concern.

**Baseline comparator:** current per-handle refresh model.

**Adoption wedge / shadow-run plan:** none; keep the current model and instrument
it.

**Rollback recipe:** current per-handle refresh remains the rollback path.

**Primary source or paper status:** inventory-backed and code-local.

**Interference-test requirement / status:** no independent controller
composition; topology sensitivity comes from WAL I/O, not the chosen primitive.

---

## Card 9: M8 — WAL Generation Identity

**Current primitive:** per-handle copy-sized identity
(`checkpoint_seq`, salts).

**Chosen primitive:** keep the copy-sized identity local to the handle.

**Why this fits:** this is a tiny comparison token, not a shared mutable data
structure.

**EV score:** 1/10

**Relevance:** Low for performance, moderate for correctness.

**Primary risk and countermeasure:**
- Risk: wrapping a tiny token in a shared publication primitive creates more
  invalidation machinery than payload.
- Countermeasure: keep it as a copied identity and use the existing refresh path
  when it changes.

**Budgeted mode and fallback trigger:**
- Target mode: unchanged local copy.
- Fallback trigger: if generation checks ever need a coherent multi-field
  durable bundle, piggyback through the durable SHM snapshot surface instead of
  inventing a new wrapper here.

**Required logging fields:**
- `trace_id`
- `metadata_class="M8"`
- `operation=compare|refresh`
- `checkpoint_seq`
- `salts_changed`

**Required verification:**
- Unit: checkpoint reset changes the generation identity.
- Unit: handle refresh rebuilds derived state on identity change.
- E2E: another connection's checkpoint invalidates stale local generation.
- Topology: none.

**User-visible symptom signature and operator diagnostic cues:**
- Symptom: missed checkpoint generation change or needless rebuilds.
- Diagnostic: salts mismatch counts and repeated rebuild events.

**Rejected alternatives and why they lost:**
- Seqlock pair: too much structure for a tiny local token.
- RCU object swap: same problem with added allocation.
- Left-Right / BRAVO: no shared reader population exists.

**Baseline comparator:** current local copy-and-compare behavior.

**Adoption wedge / shadow-run plan:** none.

**Rollback recipe:** current behavior is already the rollback path.

**Primary source or paper status:** inventory-backed and code-local.

**Interference-test requirement / status:** none; M8 is handle-local.

---

## Card 10: M9 — MemDB Visible Commit Seq Gate

**Current primitive:** per-connection local staleness gate.

**Chosen primitive:** keep the local monotone gate.

**Why this fits:** the gate is a cheap local check that decides whether MemDB
needs refresh; making it shared would reintroduce the very coordination it
avoids.

**EV score:** 3/10

**Relevance:** Medium because it protects correctness on the MemDB fast path.

**Primary risk and countermeasure:**
- Risk: making the gate shared turns a cheap local invalidation check into a
  shared publication surface.
- Countermeasure: keep the gate local and compare only against upstream
  authoritative publication state.

**Budgeted mode and fallback trigger:**
- Target mode: unchanged local staleness gate.
- Fallback trigger: stale detection mismatch.
- Fallback action: refresh MemDB from the authoritative publication source.

**Required logging fields:**
- `trace_id`
- `metadata_class="M9"`
- `operation=check|refresh`
- `visible_commit_seq`
- `stale_detected`
- `refresh_reason`

**Required verification:**
- Unit: stale local gate triggers refresh.
- Unit: local commits update the gate monotonically.
- E2E: remote commit invalidates local MemDB view and refreshes correctly.
- Topology: none.

**User-visible symptom signature and operator diagnostic cues:**
- Symptom: stale MemDB read after another commit.
- Diagnostic: local gate trails authoritative visible sequence.

**Rejected alternatives and why they lost:**
- Shared seqlock gate: no benefit over local compare.
- RCU global staleness object: wrong scope and added traffic.
- Lock-based shared cache: regresses the MemDB fast path.

**Baseline comparator:** current local gate plus refresh path.

**Adoption wedge / shadow-run plan:** none; retain instrumentation only.

**Rollback recipe:** current local gate is the rollback path.

**Primary source or paper status:** inventory-backed and code-local.

**Interference-test requirement / status:** none independent; M9 depends on
authoritative upstream publication, but is not a shared publication class.

---

## Card 11: M10 — Cached VDBE Engine

**Current primitive:** per-connection cached engine stored locally.

**Chosen primitive:** keep per-connection engine reuse.

**Why this fits:** the engine cache is tightly coupled to connection-local
schema state and execution ownership.

**EV score:** 4/10

**Relevance:** Medium because it affects repeated execution cost, but it should
not be turned into shared publication.

**Primary risk and countermeasure:**
- Risk: shared engine pooling introduces stale-bytecode and ownership bugs.
- Countermeasure: keep engine reuse local and invalidate on schema/token
  mismatch.

**Budgeted mode and fallback trigger:**
- Target mode: unchanged local engine reuse.
- Fallback trigger: schema mismatch or cached-engine divergence.
- Fallback action: rebuild the engine for the current connection.

**Required logging fields:**
- `trace_id`
- `metadata_class="M10"`
- `operation=reuse|rebuild|invalidate`
- `schema_generation`
- `cache_hit`
- `reset_ns`

**Required verification:**
- Unit: engine reuse works within one connection.
- Unit: schema change invalidates the cached engine.
- E2E: repeated prepared execution hits the local engine cache without
  cross-connection leakage.
- Topology: none.

**User-visible symptom signature and operator diagnostic cues:**
- Symptom: stale bytecode or unexpected recompilation churn.
- Diagnostic: cache-hit rate and invalidation reasons.

**Rejected alternatives and why they lost:**
- Shared engine pool: ownership and invalidation become much harder.
- RCU published engine images: wrong coupling with connection-local state.
- Lock-based global engine cache: adds shared traffic to the statement path.

**Baseline comparator:** current per-connection engine cache versus always
  rebuilding.

**Adoption wedge / shadow-run plan:** none.

**Rollback recipe:** disable reuse and rebuild per statement if necessary.

**Primary source or paper status:** inventory-backed and code-local.

**Interference-test requirement / status:** none; M10 is connection-local.

---

## Card 12: M12 — Parse Cache + Compiled Statement Cache

**Current primitive:** per-connection `RefCell`-backed LRU / compiled-statement
cache.

**Chosen primitive:** keep the per-connection cache and explicitly reject shared
publication at this stage.

**Why this fits:** the locality evidence is lane- and connection-shaped, and
shared compile publication would couple invalidation and ownership too early.

**EV score:** 4/10

**Relevance:** Medium because compile reuse matters, but the next question is
cache-policy evidence, not publication machinery.

**Primary risk and countermeasure:**
- Risk: a shared compile cache smears schema invalidation and hurts lane
  locality.
- Countermeasure: keep caches local until the dedicated compile-cache bead
  proves a better answer.

**Budgeted mode and fallback trigger:**
- Target mode: unchanged per-connection cache.
- Fallback trigger: poor hit rate or invalidation churn.
- Fallback action: tune cache policy locally or defer to the dedicated compile
  cache work; do not introduce shared publication here.

**Required logging fields:**
- `trace_id`
- `metadata_class="M12"`
- `operation=parse_hit|parse_miss|compile_hit|compile_miss|invalidate`
- `schema_cookie`
- `schema_generation`
- `cache_size`
- `lane_id`

**Required verification:**
- Unit: schema change invalidates affected cached statements.
- Unit: no cross-connection cache leakage occurs.
- E2E: repeated statement workloads show warm-hit reuse on a single connection.
- Topology: any lane-locality work belongs to the compile-cache track, not this
  bead.

**User-visible symptom signature and operator diagnostic cues:**
- Symptom: wrong-plan reuse or unnecessary recompilation.
- Diagnostic: compile-hit rate, invalidation rate, and schema-mismatch counts.

**Rejected alternatives and why they lost:**
- Shared compile cache: not yet justified by locality evidence.
- RCU-published AST/bytecode cache: same invalidation and ownership problem.
- Seqlock cache map: wrong fit for large cached objects.

**Baseline comparator:** current per-connection LRU versus shared-cache
proposals.

**Adoption wedge / shadow-run plan:** if `bd-db300.6.1.3` later proves a shared
cache is warranted, that lands as a separate bead with separate validation.

**Rollback recipe:** retain current per-connection cache as the permanent
fallback.

**Primary source or paper status:** inventory-backed; future shared-cache work
is intentionally deferred to its own measurement bead.

**Interference-test requirement / status:**
- Requirement: none for the current keep-local decision.
- Current status: explicit linkage noted to the compile-cache decision track;
  no shared-publication interference proof is required until that track changes
  the primitive.

---

## Rejection Log Summary

The following option families were considered across the cards and rejected
unless a future bead supplies materially new evidence:

| Primitive Family | Rejection Pattern |
|------------------|-------------------|
| Whole-surface `RwLock` / BRAVO | Too much shared reader-side bookkeeping for the actual access shapes |
| Whole-object Left-Right duplication | Memory and duplication cost do not fit these metadata surfaces |
| Seqlock everywhere | Only correct for tiny retryable summaries, not mutable ledgers or local-only state |
| Shared RCU/epoch publication for local-only classes | Adds coupling and invalidation surface without value |
| Shared compile / engine pools | Prematurely smears ownership and schema invalidation across connections |

Future work must address the specific rejection reason before reopening any of
the above families.

---

## Implementation Priority

| Priority | Metadata Class | Action | Why Next |
|----------|----------------|--------|----------|
| 1 | M6 | Immutable committed-state snapshot publication | Highest-value shared mutex removal |
| 2 | M11 | Sharded registry with comparator mode | Best remaining concurrency-control-plane win |
| 3 | M1 | Remove `Condvar`, keep seqlock | Clean tune of an already-correct summary plane |
| 4 | M2/M3/M4/M5/M7/M8/M9/M10/M12 | Keep local/current primitives and preserve their boundaries | Avoid accidental complexity while the shared hotspots are fixed |

This is the closure contract for `bd-db300.5.3.2.2`: every hot metadata class
now has an explicit selected primitive, a rejected-alternatives trail, an
operator-visible failure signature, verification obligations, rollout posture,
and rollback path.