Files
alkstore/alkstore-sqlite/src/substrate/PROVENANCE.md
T
glm-5.3-flash 7d400906f5 SQLite engine open: SqliteOpts, connection architecture, spawn_blocking seam posture (task sqlite-engine-open-opts)
- SqliteOpts (poll_interval: Option<Duration>, None = 1 ms shipping
  default ADR-023 §4; max_readers, DEFAULT_MAX_READERS = 8; opts
  exemption from non_exhaustive per ADR-017 §3)
- open(path, opts) -> Box<dyn Store>: writer slot, reader pool,
  SharedUpdateWatcher with fallible spawn mapped W-2-style into
  Error::Database; plain-path posture (ADR-023 §3) pinned by test
- Substrate delta D-30: open_conn_bootstrapped — every connection
  (writer and pooled readers) carries the full bootstrap surface
  (pragmas, notify, alkstore functions, schema) per engine-sqlite.md;
  lineage opened readers pragmas-only. Registered in PROVENANCE.md
- spawn_blocking seam helper + error mappings (string/rusqlite ->
  Database, source chains preserved); helper cfg(test)-gated until
  sqlite-engine-seam-tx wires the trait impls
- Store trait stubbed with Database errors (no panics); close()/Drop
  join the watcher, clear subscribers (death-guard), close pool+writer
- 15 new tests (open boot, watcher fanout/death-close, cadence
  accounting, plain-path literal filename, :memory:, pool bounding,
  drop teardown, seam smoke incl. panic mapping); gates green
2026-10-08 11:25:16 +00:00

18 KiB
Raw Blame History

PROVENANCE — the substrate fork's register

The provenance register for the src/substrate/ module subtree of alkstore-sqlite — per ADR-018 (format and procedure), ADR-011 (the fork decision), ADR-012 (the fork design), and ADR-013 (the fold into the engine crate).

This file records: what lineage this code is, what has been changed relative to that lineage, and how each change arrived. It is the audit trail review works from; it is not itself a guard and not a decision record — the ADRs remain the decisions of record. Entries are append-only (amendment only to correct the record — a wrong path, a mis-cited hash — never to change the story; a superseded delta is a new entry citing the one it supersedes).

1. Upstream identity

  • Upstream project: honker (the SQLite queue / notify library), https://github.com/russellromney/honker — substrate package honker-core.
  • Fork point: the reference checkout /workspace/honker @ f4e53c6 (full hash f4e53c6b83ad2d6979c04098d576db024ce7e98d, dated 2026-10-02; git describe → package node-v0.5.1-10-gf4e53c6). The fork point is deliberately past the published release: it carries the unreleased fix train (issue #133's savepoint hardening of the DELETE→INSERT dead-letter paths, the claimed_at addition, and related hardening) that the published crates.io artifact (0.5.0, 2026-08-23) does not.
  • Published-release lineage: honker-core 0.5.0 (2026-08-23) on crates.io; the fork continues from the repository lineage, not the published tag.
  • License: MIT OR Apache-2.0 (upstream's dual license, carried upstream as LICENSE-MIT / LICENSE-APACHE; bundled in-tree as src/substrate/LICENSE, this directory — the preservation obligation's carrier). Sublicensing is permitted by both licenses; the engine crate's license statement covers the whole crate.
  • Fork-point statement: the scaffold commits — this subtree's landing in alkstore's git history — are the provenance line, the alksocks extraction-commit-window shape. Everything in the subtree before the first port commit is lineage; every divergence is register-addressable below.
  • Inherited lineage facts: the fork point itself carries upstream changes we inherited verbatim and therefore do not list as deltas — the fix train above (4881f27, 3ab43aa, claimed_at et al.) is upstream code, recorded here under upstream identity, not in the delta register.
  • Reference checkout: /workspace/honker @ f4e53c6 remains the standing reference for cherry-pick candidates and differential testing — no dependency, no wiring (ADR-012 §6).

2. Delta register

Per [ADR-018] §2: one entry per distinct delta, category-tagged — never one per commit (git history is the per-commit record; this register is the semantic index over it). Each entry pins five fields: Category / What / Where / Why / Lineage.

Categories: port delta (kept-half adaptation), re-derivation (new contract-v1 code), drop (not ported), hygiene (family-standard porting), cherry-pick (adopted upstream lineage).

The entries below are the initial entries fixed by the retained records (ADR-011's scope register, ADR-012 §4–§5) as pre-declared checklist rows — they name deltas the port tasks will make. At the port tasks' landing, entries are updated in place to the landed state (paths finalized, what/why made precise); entries for port-surfaced adaptations the plan didn't anticipate are appended (D-17..D-20, landed with fork-port-connection-watcher).

The entries below are the fork-port deltas as landed by fork-port-connection-watcher (paths finalized against the ported tree). D-12 landed as the re-derivation's queue_ops.rs module + schema extensions with fork-rederive-queue-ops. The gate-time completion pass (fork-provenance-and-floor): the lineage diff vs honker-core @ f4e53c6 was produced and skimmed end-to-end (local only, not committed — the wave-2 review's primary input), and every divergence it surfaced is either a register entry below (D-21..D-26, appended; D-12 annotated in place) or a fixed omission (the lineage-less placeholder test in ops.rs's pressure suite, removed). No unregistered divergence remains. The wave-2 review gate (review-wave-2) re-ran the full-coverage lineage diff (clean — everything register-addressable) and appended D-27/D-28 for its two fixes. Cherry-picks: empty at scaffold, append-only forever.

ID Category What Where Why Lineage
D-01 drop cron.rs (cron-string scheduling) not ported — ADR-009 rejects cron strings (scheduler collapse; §3) ours
D-02 drop experimental watcher backends (kernel-watcher, shm-fast-path) and their optional deps (notify, memmap2, libc, file-id) not ported — ADR-011 scope (drop); quality-read §6; cuts optional deps ours
D-03 drop rate-limit and result tables (cut-flags) not ported — ADR-002 cut-flags; ADR-011 scope (drop) ours
D-04 drop superseded queue functions not ported — ADR-011 scope (drop); the queue half is re-derived (see R-01) ours
D-05 port delta W-1 — bounded reconnect backoff in the watcher reconnect path (MAX_RECONNECT_TICKS = 100 poll ticks between attempts; the lineage reconnected on every tick) watcher machinery (run_poll_loop region; watcher.rs) ADR-012 §4; quality-read W-1 ours
D-06 port delta W-2 — watcher spawn becomes fallible (UpdateWatcher::spawn* / SharedUpdateWatcher::new* return Result); the engine surfaces the failure at open time watcher machinery (spawn_with_config / spawn_thread region; watcher.rs) ADR-012 §4; quality-read W-2 ours
D-07 port delta dead-man's switch — the db-file-identity-change panic replaced by a deliberate watcher-fatal death (log the precise diagnostic, exit through the ordinary death path); WatcherDeathGuard's death-closes-subscribers behavior unchanged stat_identity / WatcherDeathGuard region; watcher.rs (run_poll_loop, death guard) ADR-012 §4 (family no-panics standard; observable semantics unchanged) ours
D-08 port delta W-4 — watcher connection opens RW: deliberate inheritance, keep RW run_poll_loop region; watcher.rs ADR-012 §4 (keep-RW recorded; not actionable as a change — recorded where the read put it) ours
D-09 port delta W-3 — data_version u32 wrap: recorded on poll_data_version, no action (a wrap fires one spurious wake; wakes are re-read hints) watcher machinery (notes on poll_data_version; watcher.rs) ADR-012 §4 (not actionable — recorded) ours
D-10 port delta table family renamed _honker_* → __alkstore_* across the ported storage surface; no online rename migration (fresh bootstrap only; leftover _honker_* orphans are inert and untouched — pinned by test) bootstrap / schema + notify/stream/lock SQL (schema.rs, ops.rs) ADR-011 (naming); ADR-010 §8 (pre-authorization); ADR-008 §4 (storage-internal); ADR-012 §5 ours
D-11 port delta bootstrap race swallow re-keyed — on ALTER TABLE duplicate-column failure, verify via pragma_table_info (present ⇒ benign race swallowed; absent ⇒ propagate); upstream's error-string matching not inherited bootstrap / schema machinery (column_present / add_column_if_absent; schema.rs) ADR-012 §5; quality-read §4 (schema brittleness) ours
D-12 re-derivation queue ops re-derived on contract v1 (new code, contract-derived names): enqueue + per-job option stamping (Stamps — max_attempts, visibility, backoff base, retention — resolved engine-side, applied per row); single-statement claim_batch with per-row visibility from the job's own stamps in the claim UPDATE (claim_expires_at = unixepoch() + visibility_timeout_s), ordering priority DESC, run_at ASC, id ASC, attempts += 1 per claim, pre-claim dead-letter sweep for exhausted reclaimables; ack/ack_batch/heartbeat/retry/fail carry the uniform validity predicate (processing + unexpired claim deadline; refusal = 0/false, not error); all dead-letter moves (retry-at-budget, fail, pre-claim sweep, sweep_expired) savepoint-guarded via in_savepoint (the #133 defect class); both-states no-stranded-rows sweep_expired with dead_letter_retention_s deletion; dead-visible get_job returning the full stamp field list + last_error/died_at; cancel unconditional; default error strings ("max attempts exceeded", "expired") passed in, never owned here; scheduler register/tick/soonest/unregister over __alkstore_scheduler_tasks (pause/resume/list/update not carried — register D-21) with @every-only boundary math (parse_every_interval, s/m/h/d), 64-cap catch-up per tick with skip-forward (SCHEDULER_MAX_CATCHUP_FIRES), fire enqueue + row advance in one caller transaction; stamp columns + claimed_at added to live/dead/scheduler-table schemas with append-column migrations queue-op modules (queue_ops.rs) + schema (stamp columns, dead-table extensions, retention index) ADR-011 scope (re-derive); ADR-010 §3a/§5/§1; quality-read D-1–D-3, D-5–D-7 ours
D-13 hygiene the substrate stays sync — family porting is the discipline deltas, not an asyncification port; the bridged seam at the engine layer is the async story whole subtree ADR-012 §4; ADR-003 (seam) ours
D-14 hygiene no panics in library code (see D-07 for the one substantive instance), no unwrap()/expect() outside tests whole subtree AGENTS.md code conventions; ADR-011 (family standard) ours
D-15 hygiene no comments in code (doc comments fine) — ported upstream comments elided whole subtree AGENTS.md code conventions; ADR-011 (family standard) ours
D-16 port delta notify machinery — notify() scalar + __alkstore_notifications table kept, upstream's no-pruning posture replaced by an at-attach pruning cap (NOTIFY_ATTACH_MAX_ROWS = 10k rows, oldest-first trim, implementing ADR-010 §6's engine-internal hygiene) notify machinery (attach_notify; schema.rs) ADR-011 scope; ADR-010 §6 ours
D-17 port delta the lineage module structure (everything in lib.rs + honker_ops.rs) mapped to the substrate subtree's modules: schema.rs (pragmas/open, notify, bootstrap, Writer, Readers), watcher.rs (the polling watcher family), ops.rs (savepoint discipline, arg coercion, stream/lock ops, function attachments); upstream names preserved inside each schema.rs / watcher.rs / ops.rs (subtree layout) ADR-012 §3's fidelity posture applies to names and kept-body shape; the ADR-013 fold makes the module file the unit, and a ~4k-line flat file does not fit a bounded module subtree (AGENTS.md module-per-file); diff reviewability is preserved register-entry by register-entry against upstream's file+line region ours
D-18 port delta file-id retained (target-gated cfg(any(unix, windows)) dep, as upstream) — the register's D-02 drop-list names it among the experimental backends' optional deps, but it drives the kept dead-man's switch (stat_identity), so it stays watcher.rs (stat_identity); engine manifest the kept-half scope (quality-read §6 keeps the dead-man's switch verbatim modulo deltas) overrides D-02's enumeration for this dep; the experimental backends' true deps (notify, memmap2, libc) are cut ours
D-19 hygiene scalar-function attachment sites renamed attach_honker_functions → attach_alkstore_functions, honker_bootstrap → alkstore_bootstrap (the surface carries the fork's identity in its function names; the coercion helpers keep upstream names arg_i64/arg_opt_i64 per the fidelity posture) ops.rs (attach_alkstore_functions) ADR-011 (fork re-owning names); D-10's naming delta at the SQL-function layer ours
D-20 port delta WatcherConfig slims to the polling backend's fields (poll_interval only; WatcherBackend and its parse/probe machinery dropped with D-02); the default poll interval (1 ms) and the 100 ms identity-check interval are inherited verbatim watcher.rs (WatcherConfig) ADR-012 §4 (polling is the only backend); ADR-012 §3 (kept values stay) ours
D-21 drop scheduler pause/resume/list/update — the lineage's scalar registrations (honker_scheduler_pause/resume/list/update in the attach_honker_functions family) and their backing functions not ported: the contract v1 scheduler surface is register-upsert + tick + soonest + unregister (ADR-009: schedule() upserts by name — update = re-register; pause = unregister + re-register are the honest v1 substitutes; no schedule-object CRUD) honker_ops.rs:1669–1844 (pause/resume/list/update) — absent here ADR-009 (scheduler collapse; §Negative: richer scheduler deliberately not consumed; contract pins only the v1 surface), realized in D-12 ours
D-22 drop the lineage's in_savepoint regression tests driven through the re-derive-side fail() call shape (a_self_rolled_back_transaction_returns_the_cause_unchanged; fail_leaves_no_open_transaction_when_release_fails) not ported: the substrate's in_savepoint discipline tests (ops.rs optional_error_tests) drive the machinery directly and cover the same property classes honker_ops.rs:2826–2900 — drivers only; the in_savepoint/undo_savepoint_frame machinery itself is ported verbatim (ops.rs region, register D-17) drivers belong to the re-derived fail (D-04); the discipline tests re-express the same properties directly against the kept machinery ours
D-23 drop two lineage watcher/lib.rs durability tests not ported: writer_killed_mid_workload_leaves_db_consistent (python-child WAL crash recovery, lib.rs:1983–2148) and soak_watcher_durability (lib.rs:2163, #[ignore]) lib.rs (test modules) — absent here external-process/python-child harness not carried into the engine crate's test floor at this wave (test-floor-scoping decision, recorded here for the wave-2 review's call; the ported-surface concurrency floor is ops.rs's pressure suite) ours
D-24 hygiene real_to_i64 boundary error text re-owned ("honker:" prefix → "alkstore:"; the lineage's better-sqlite3-facing sentence elided per D-15) ops.rs (real_to_i64) D-19's re-owning pattern at the error-string layer ours
D-25 hygiene watcher thread name "honker-update-poll" → "alkstore-update-poll" watcher.rs (spawn path) D-19's re-owning pattern at the thread-name layer ours
D-26 hygiene volume_serial_number as u64 → u64::from(volume_serial_number) in VolumeIdentity (clippy lint conformance, zero behavior change) watcher.rs (stat_identity region) family clippy -D warnings gate ours
D-27 re-derivation wave-2 review fix — scheduler_tick's fire enqueue resolved the schedule row's relative expires_s into the absolute row expires_at (now + s at the fire transaction, one clock read) instead of passing the relative value through; regression test pins a fired job's expires_at = fire-instant + s and its claimability. The pre-fix pass-through would have produced rows expired in 1970 under any expires_s schedule queue_ops.rs (scheduler_tick, fire-expiry resolution + test) ADR-020 §2 (relative expires → resolved absolute row value, resolved at enqueue against the single clock) — D-12's landed state carried the lineage's expires_s field shape without carrying the lineage's enqueue-side resolution; wave-2 review (task review-wave-2) re-derivation spot-check found it ours
D-28 hygiene wave-2 review fix — the cross-mechanism pressure test's lock-churn branch exercised a per-producer lock name (literal "p{producer}" had no interpolation, so all producers contended on one name) with its result swallowed via .unwrap_or(0); now interpolates, releases periodically, propagates errors ops.rs (pressure test, lock branch) test-quality only (no lineage counterpart, no library code touched); fork-provenance-and-floor's ported-surface pressure intent ours
D-29 port delta SQLITE_OPEN_URI dropped from open_conn's flags (the watcher's own opens never carried it — the inherited posture was internally inconsistent); the path argument is a plain filesystem path, ?name=value suffixes are literal filenames, no connection-semantics mutation outside the engine constructor's opts; pinned by test (open_conn_treats_uri_shaped_path_as_plain_filename) schema.rs (open_conn) ADR-023 §3 (plain-path open — the URI parameter surface is SQLite's, not this engine's; no consumer-inventory row names URI features) ours
D-30 port delta open_conn_bootstrapped — new function stacking the engine's full per-connection open posture (pragmas, notify, attach_alkstore_functions, bootstrap_schema), and Readers::acquire's open path switched from bare open_conn to it (fresh connections only — the pool never re-bootstraps a reused connection). The lineage's pools opened readers pragmas+notify only, the function set + schema living only on the writer; this engine's reader-slot operations (claim/ack, stream reads, lock ops, scheduler checks, leadership probes) run this substrate's SQL functions on pooled connections, so every pooled connection carries the full surface schema.rs (open_conn_bootstrapped, Readers::acquire open path) engine-sqlite.md's connection architecture ("each connection runs the substrate's bootstrap at open — the writer and each pooled reader") — engine-layer obligation of sqlite-engine-open-opts; landed with the engine's open wiring ours

Cherry-picks

Empty at scaffold. Entries land here at adoption time (never swept), one per adopted upstream change, category cherry-pick, following [ADR-018] §3's five-step procedure; each cites the upstream commit hash(es) in its Lineage field and lands in the same commit as the applied change, plus one changelog line in the engine crate.