Files
alkstore/docs/architecture/decisions/011-sqlite-substrate-fork.md
T
glm-5.3-flash 8e68b44194 ADR-013: fold the forked substrate into alkstore-sqlite — no fourth crate
Operator review of ADR-012's fork design re-litigated §1's crate
identity. alkstore-substrate misdescribed what the code is (unpublished,
path-dep-only, one consumer, SQLite-only — not a family-wide substrate);
the mechanical-diff hope was gone at fork time regardless (port deltas,
renames, re-derived half); and the alksocks F-1 lesson applies — a
vendored region under a second, weaker instruction set is a defect seam.
The fork folds into alkstore-sqlite as a bounded module subtree
(src/substrate/); ADR-012 §3–§6 retained verbatim, §2 retained with its
enforcement re-sited from the crate graph to diff fence + review +
contract-suite equivalence pins. OQ-11 item (1) dissolved.
2026-10-05 11:56:01 +00:00

7.3 KiB

ADR-011: SQLite substrate — fork honker-core into owned code

Status

Accepted (2026-10-05, Phase 1 — OQ-06's resolution; the fork trigger ADR-005 named, fired)

Context

ADR-005 fixed the dependency posture: published honker-core 0.5 by default, with the fork trigger being the Phase 1 quality read finding a defect the POCs wouldn't surface. ADR-010 handed the read two concrete fork candidates (the expired-processing-row zombie hole; dead-row get_job visibility) and made the SQLite realization of contract v1's queue depth contingent on it. OQ-06 is the tracked assessment; the evidence is docs/research/quality-read-honker-core.md (full read of all five honker-core sources at the reference revision, cross-checked against the published crates.io artifact).

Three facts drive this decision:

  1. The published artifact carries confirmed silent-job-loss defects. crates.io's newest honker-core is 0.5.0 (2026-08-23). The upstream fix train for its own documented defect class — issue #133's savepoint hardening of the DELETE→INSERT dead-letter paths (a job stranded in neither table on mid-flight error = silent job loss) and the five .ok() error-swallows that map every SQLite error to "no row"/"not our claim"/"lock held" — sits in the repository unreleased, alongside claimed_at and six weeks of hardening. The ride posture's core value ("defects inherited for free" cuts both ways: fixes do too) currently inherits the defects, not the fixes.
  2. Contract v1 requires re-deriving the queue-op surface in any posture. Per-job option stamps (ADR-010 §3a — visibility/backoff/ retention stamped at enqueue, honored per-row in the claim statement), the no-stranded-rows sweep (§5), and dead-visible get_job with stamps (§1) all mean the engine owns enqueue / claim / retry / fail / sweep / get_job SQL whether riding or forking. The post-fork ride would cover only ack/heartbeat/cancel, locks, streams, notify transport, scheduler storage, and the watcher/ connection plumbing — the minority of what the engine touches, dual- owning the _honker_* schema with our migrations in the complement posture.
  3. The read's original target — the watcher/transactional core — is clean. Writer, Readers, and the polling watcher's three-layer failure handling (transient-BUSY-vs-fatal classification, conservative wakes, re-baseline discipline, the stat-identity dead-man's switch) and WatcherDeathGuard's death-closes-subscribers property all verified in source, present in 0.5.0. This fact steers the fork's scope (inherit that machinery almost verbatim), not the posture.

Decision

The fork trigger fires. The SQLite engine's substrate becomes owned code: fork honker-core at the reference revision (/workspace/honker @ f4e53c6, MIT OR Apache-2.0, provenance recorded per AGENTS.md §3) into the alkstore family, ported to the family standard (the discipline deltas — no comments discipline, panics out of library code — with tokio-facing consumers at the engine seam above; per ADR-012 §4 the substrate itself stays sync), re-derived on contract v1 where ADR-010 pinned semantics honker's functions don't provide.

Per-scope (the full register with keeps/re-derivations/drops is docs/research/quality-read-honker-core.md §6):

  • Inherited near-verbatim: the PRAGMA/WAL open posture, Writer, Readers, the polling watcher + SharedUpdateWatcher + WatcherDeathGuard + dead-man's switch (with three port deltas the read named / ADR-012 decided: reconnect backoff, fallible watcher spawn, and the dead-man's-switch panic replaced by a deliberate watcher-fatal death — ADR-012 §4), the in_savepoint mutation-discipline machinery, arg-coercion helpers, the notify scalar + notifications table (renamed, with engine- internal hygiene per ADR-010 §6), streams, locks.
  • Re-derived on contract v1: enqueue + opt stamping; single- statement claim with per-row visibility from the job's stamps; savepoint-guarded retry/fail/dead-letter; the both-states no-stranded-rows sweep_expired with retention deletion; dead- visible get_job; scheduler tick with @every boundary math.
  • Dropped: cron parsing (ADR-009 rejects cron strings), the experimental watcher backends and their optional deps, the rate-limit and result tables (cut-flags), the superseded queue functions.
  • Table naming: _honker_* → __alkstore_* — storage-internal (ADR-008 §4); ADR-010 §8 pre-authorized the fork re-owning names.
  • Tests: honker-core's suites inherited as the floor (watcher lifecycle/failure, savepoint, multiprocess pressure) + the contract- property tests (no-stranded-rows, dead-visible get_job, stamps).

Packaging follows ADR-001's split: the forked substrate is the SQLite engine crate's dependency — vendored as a workspace-family crate (provenance + license recorded), not re-published. (Packaging amended 2026-10-05 by ADR-013: the fork folds into alkstore-sqlite as a bounded module subtree — no fourth crate; ADR-001's three-crate shape stands.)

Consequences

Positive

  • The confirmed defect class (D-1..D-5) is owned, fixed, and testable in-tree; no release cadence gates correctness we already know how to fix.
  • Contract v1's queue depth realizes directly in owned SQL — no two-statement re-stamp bridge, no engine migrations over a dual-owned _honker_* table family.
  • The fork inherits ~4,000 lines of upstream hardening tests plus the watcher machinery the POCs measured as the quality win of the ride posture — most of the fork cost is pre-paid.

Negative

  • We own the SQLite machinery's future: upstream fixes (rare and incremental, but real — the #80/#133 train proves the stream is alive) must be deliberately cherry-picked rather than inherited by cargo update.
  • Vendored forked code in the tree breaks "zero vendored code in the tree by default" (ADR-005's positive consequence) — this is the exception the posture always named, not a renegotiation. (Packaging since amended by ADR-013: the vendored code is carried in-tree inside alkstore-sqlite, not as a workspace-family crate.)

References

  • OQ-06 (docs/architecture/open-questions.md) — the resolved assessment; docs/research/quality-read-honker-core.md — the evidence (published-artifact fact, watcher-core verdict, defect register, fork calculus and scope).
  • ADR-005 — the posture and trigger this ADR fires; fork-is-normal-work applies.
  • ADR-003 — driver, seam, and watcher architecture (unchanged in ownership; the fork inherits the measured machinery).
  • ADR-010 — §3a/§5/§1's realization is now owned-code work; §8's naming authorization applies.
  • ADR-001 — packaging (engine crate's single-driver substrate).
  • engine-sqlite.md — the living spec, updated to the forked substrate.
  • ADR-012 — the fork's design decisions (crate identity, contract-blind boundary, fidelity posture, port deltas, bootstrap machinery, upstream tracking).