Follow-through on OQ-06/ADR-011: pin the fork's structural decisions (alkstore-substrate as a vendored path-dep crate, contract-blind API boundary with contract formulas computed engine-side and pinned equivalent by the contract suite, keep-the-kept-half API fidelity for cheap cherry-picks, the W-1/W-2/dead-man's-switch/W-4 port deltas decided per item, bootstrap re-keying off error-string matching, no rename migration, deliberate upstream tracking). Consistency sweep across the doc set for the fork: annotate ADR-003/ 005/009/010 and core-contract for superseded ownership facts, fix schedule-storage table naming (ADR-009 §5, queues.md), re-key ADR-010 §6's notifications hygiene to the at-attach cap the fork scope realizes, add OQ-11 (scaffold-time residue), and complete both ADR indexes. Independent review: 0 critical, warnings addressed.
6.9 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:
- 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, alongsideclaimed_atand 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. - 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_jobwith 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. - 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), thein_savepointmutation-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_expiredwith retention deletion; dead- visibleget_job; scheduler tick with@everyboundary 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.
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. - A vendored workspace-family crate 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.
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).