Files
alkstore/tasks/sqlite-engine-integration.md

10 KiB
Raw Permalink Blame History

id, name, status, depends_on, scope, risk, impact, level, tags
id name status depends_on scope risk impact level tags
sqlite-engine-integration SQLite engine — integration (lint removal, full-surface wiring, suite adoption) completed
sqlite-engine-notify-listen
sqlite-engine-streams
sqlite-engine-queues
sqlite-engine-locks
sqlite-engine-scheduler-outbox
moderate medium phase implementation
wave-3
sqlite-engine
integration

Description

Close the wave's integration gaps once every mechanism is wired:

  • Remove the two lint suppressions in alkstore-sqlite/src/substrate/mod.rs (#![allow(dead_code)], #![allow(unused_imports)]) — the waves-1–2 review's explicit wave-3 obligation. With the engine layer wired, every substrate surface should be reachable; genuinely dead ported code must be cut, not suppressed (and the cut registered in PROVENANCE.md if it diverges from the lineage's kept set — ADR-018's delta discipline). Fix whatever the lints surface; do not re-add allows.
  • Full-surface sweep: every Store/TxHandle/mechanism trait method implemented (no todo!/unimplemented! anywhere); the engine crate's lib re-exports its public surface (open, SqliteOpts, the concrete store type) per the module-per-file convention.
  • Contract-suite adoption (the plan's "waves 3 and 4 adopt the harness"): implement StoreFactory for the SQLite engine (fresh temp-file store per open, idempotent teardown) and wire the engine's backlog column — the rows this engine owns now, per ADR-023's verification-backlog additions and the engine-scoped backlog rows:
    • Extent-clamp semantics (claim_batch(n <= 0), stream reads limit <= 0) — stamped ADR-023 §2
    • Duration-refusal (try_lock/renew with ttl <= 0) — stamped ADR-023 §2
    • encode_payload typed-failure round-trip (enqueue/publish store
      • decode the exact serialization) — stamped ADR-023 §1
    • Plain-path open (?name=value suffix = literal filename) — stamped ADR-023 §3
    • Poll-cadence default + knob (config-surface accounting, not wall-clock) — stamped ADR-023 §4
    • The engine-scoped rows core-contract.md assigns this engine: PayloadTooLarge never produced (ADR-016 §5), drop = rollback no-ghosts (ADR-021 §4), in-tx read-your-own-writes (ADR-021 §1), enqueue-opts resolution (ADR-020 §1–§3), receiver close arms (ADR-021 §5) — each stamped per the suite's convention. Rows run against the SQLite factory now; wave 5 runs the same rows against pg (the factory-parameterized point).
  • Crate docs: the engine crate's lib-level doc comments state the single-host posture and the writer-parking honesty note (ADR-016, ADR-007's negative consequence) — the identity statements this crate owns.
  • Gates: full workspace build/test/clippy/fmt; coverage spot-check on the engine crate (no large uncovered regions outside error arms).

Acceptance Criteria

  • Both #![allow] lints removed from substrate/mod.rs; any code the removal surfaces is cut (and registered) or wired — clippy -D warnings green without them
  • No todo!/unimplemented! in the engine crate; full trait surface implemented
  • StoreFactory implemented for SQLite; the backlog-column rows listed above exist, version-stamped, and run green against the SQLite factory
  • Engine crate lib docs carry the single-host + writer-parking posture statements
  • Workspace gates green: cargo build, cargo test, clippy -D warnings, fmt clean

References

  • docs/reviews/001-waves-1-2-general-review.md (§2 smells, §7)
  • docs/architecture/decisions/023-fourth-review-round.md (Verification backlog additions)
  • docs/architecture/core-contract.md (§Verification backlog)
  • docs/architecture/decisions/022-contract-suite-layout.md
  • docs/architecture/decisions/016-deployment-honesty.md
  • alkstore-contract-suite/src/factory.rs

Notes

Decisions of record the description didn't pin:

  • Lint removal cut six substrate items, registered D-32..D-36 — lifting the two #![allow]s surfaced six genuinely dead surfaces, all cut rather than wired, each an ADR-018 register entry with its reason: arg_opt_i64 (upstream consumed it at the optional-arg SQL entry points whose queue functions D-04 dropped — the re-derivation resolves opts engine-side), ops::now_unix (the fold D-17 gave each module region its own clock read; resolution.rs owns the engine layer's single clock), queue_next_claim_at (upstream's external-poll-driver function; the v1 scheduler surface has no next-wake query — the engine's leader loop derives the boundary from scheduler_tick + scheduler_soonest), Writer::try_acquire (a try-shaped begin doesn't exist on the contract — begin_tx parks on the lease by design, ADR-007; a try-acquire would contradict the posture), and UpdateWatcher::spawn / SharedUpdateWatcher::new (the config-less convenience twins — this engine's single open posture always constructs WatcherConfig from SqliteOpts, ADR-023 §4; a second config-less path would recreate the mixed-posture shape D-29 resolved). None changed kept-fidelity posture — all six are drop-category entries against the kept set, with the affected substrate tests re-expressing their properties through the reachable surface (no assertion strength lost: the probe tests test the same coercion/lease behavior through the surviving forms).
  • Two substrate items kept under #[cfg(test)] rather than cut: SharedUpdateWatcher::subscriber_count and the DEFAULT_WATCHER_POLL_INTERVAL re-export — engine-side tests reach them (the leak/no-leak pins, the 1 ms default pin); they are test-observation surface, honestly gated #[cfg(test)], not suppressed. open_store was likewise promoted pub(crate) (the store tests construct the concrete handle; open stays the consumer surface).
  • The exemplar row had a real-engine sequencing defect — fixed suite-side (a suite-text change, ADR-017 §2 class 4): the name-validation row held its tx handle alive across the whole battery including the final with_tx leg. The mock harness never saw this; a real engine's begin_tx parks a concurrent begin on the writer slot (ADR-007's honest model), so the row deadlocked a real factory. The tx battery is now scoped so the handle drops before the with_tx leg. The row's assertions are unchanged — only the sequencing fix — but the incident is the wave-5 lesson the factory exists to surface: rows are mock-blind until an executes them against a real single-writer engine.
  • Row set (nine total — the exemplar + eight new, all in alkstore-contract-suite/src/properties.rs, one owner, version-stamped per the convention, each a public pub async fn(&dyn StoreFactory)): the ADR-023-stamped rows (extent-clamp including the boundary-totality legs, duration-refusal, encode_payload round-trip), and the engine-scoped rows the task names (PayloadTooLarge-never-produced — the SQLite arm, the pg rejection arm rides wave 4's adoption of the same row text; the drop=rollback no-ghosts; in-tx read-your-own-writes; enqueue-opts resolution — ADR-020 §1–§3; receiver close/save arms — ADR-021 §5). Plain-path open (§3) and poll cadence (§4) pin engine-side, not as suite rows: both are constructor-level SQLite-native facts not expressible over StoreFactory::open() — uri_shaped_open_path_is_a_literal_filename and poll_interval_flows_to_the_watcher_config (stamped, open_tests.rs) are this engine's rows for them; wave 4's pg column simply doesn't carry them (they're SQLite-scoped backlog rows per core-contract.md).
  • The factory (alkstore-sqlite/tests/contract_suite.rs): a fresh never-before-used temp file per open under a per-instance directory; teardown = directory delete, idempotent (already-deleted ⇒ Ok). Its isolation/idempotence contract has its own pin in the test target (opens_are_isolated_and_teardown_is_idempotent); receiver-close rows drive the terminal-close arm by dropping the store handle (the dispose carrier every engine has), not teardown — deleting files under a live store is not a close mechanism.
  • Coverage spot-check (cargo-llvm-cov, engine crate): 93.59% lines / ~89% regions, every file ≥85% lines; the misses are error arms and Windows-only file_id variants (watcher.rs's HighRes/LowRes halves) — no large uncovered regions outside error arms, the gate the task pinned.

Summary

Landed: the two #![allow] lints removed from substrate/mod.rs (its module docs re-stated to the wave-3 wired posture); the removal surfaced six dead surfaces — cut, each registered D-32..D-36 in PROVENANCE.md (queue-side: arg_opt_i64, now_unix, queue_next_claim_at; writer/watcher-side: Writer::try_acquire, UpdateWatcher::spawn, SharedUpdateWatcher::new), their probe tests re-expressed through the reachable surface; two test-observation items honestly #[cfg(test)]-gated (subscriber_count, the DEFAULT_WATCHER_POLL_INTERVAL re-export); no allows re-added — clippy -D warnings green without them.

Contract-suite adoption: eight new rows in alkstore-contract-suite/src/properties.rs (extent-clamp + boundary-totality, duration-refusal, payload round-trip, PayloadTooLarge-never-produced SQLite arm, drop=rollback no-ghosts, in-tx read-your-own-writes, enqueue-opts resolution, receiver close/save arms) — all version-stamped, all re-exported from the suite lib. store::SqliteFactory implemented in the engine's new integration test target (alkstore-sqlite/tests/contract_suite.rs), running all nine rows (the exemplar + the eight) against the SQLite factory — 10 tests, green; the factory's isolation/idempotence contract pinned alongside. Found and fixed suite-side: the exemplar row's hold-the-tx-across-with_tx sequencing deadlocked any real single-writer factory (the mock never saw it).

Engine crate lib docs: the posture statements surfaced under a # Posture heading — single-host (ADR-016) and writer-parking (ADR-007's negative consequence) spelled as the crate's identity statements.

Verified: workspace cargo build/cargo test green (25 + 186 + 10 + 3 + harness), clippy --workspace --all-targets -D warnings clean, cargo fmt --check clean; engine-crate coverage 93.59% lines (cargo-llvm-cov), misses confined to error arms and Windows-only watcher variants.