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.
296 lines
16 KiB
Markdown
296 lines
16 KiB
Markdown
---
|
|
status: draft
|
|
last_updated: 2026-10-05
|
|
---
|
|
|
|
# alkstore — Open Questions
|
|
|
|
Centralized tracker. IDs `OQ-NN` are stable — never renumber; append.
|
|
Promoted, one-to-one, from Phase 0's OQ-ST register. Resolution order
|
|
so far: **OQ-04 resolved** (2026-10-05,
|
|
[ADR-008](decisions/008-contract-v1-pinning.md) — the contract surface
|
|
everything else hangs off); **OQ-09 + OQ-05 resolved** (2026-10-05,
|
|
[ADR-009](decisions/009-scheduler-collapse.md) and
|
|
[ADR-010](decisions/010-queue-semantics-depth.md) — the queue semantics
|
|
track and the scheduler collapse/guarantee row); **OQ-06 resolved**
|
|
(2026-10-05, [ADR-011](decisions/011-sqlite-substrate-fork.md) — the
|
|
fork trigger fired on the published-artifact facts); **the fork
|
|
design follow-through** (2026-10-05,
|
|
[ADR-012](decisions/012-forked-substrate-design.md) — substrate
|
|
boundaries, fidelity posture, port deltas; its substrate-side residue
|
|
is OQ-11). Next: OQ-08 (rides the now-pinned trait shape and the
|
|
ADR-011 substrate fork), OQ-10 (versioning discipline for contract
|
|
extensions; note ADR-011 changes its substrate-side facts for SQLite —
|
|
the forked crate is versioned in this repository's Cargo workspace, so
|
|
the engine/core contract pairing is what the discipline must track), and
|
|
OQ-11 (fork follow-through items — substrate-side, non-consumer-facing).
|
|
|
|
Resolved questions stay listed with their resolution; they are not
|
|
deleted.
|
|
|
|
## Theme: Scope
|
|
|
|
### OQ-01: Scope boundary — which honker features are in-scope? *(== OQ-ST-01)*
|
|
|
|
- **Origin**: [consumer-inventory.md](../research/consumer-inventory.md),
|
|
[ADR-002](decisions/002-feature-scope.md)
|
|
- **Status**: resolved (2026-10-04, Phase 0)
|
|
- **Priority**: medium
|
|
- **Resolution**: Answered by the consumer inventory — scope votes
|
|
shrink to named rows, not the whole honker feature list. First-class:
|
|
notify/listen (pinned), streams (operator-authority record). In
|
|
scope: named locks, queues + outbox (documented), scheduler
|
|
(documented-thin). Cut-flags: rate limits, result storage. Out:
|
|
honker's exclusion lines. Decision recorded in
|
|
[ADR-002](decisions/002-feature-scope.md).
|
|
- **Cross-references**: OQ-07, OQ-04.
|
|
|
|
### OQ-02: Crate scope — one store crate, or reactive-core + engines? *(== OQ-ST-02)*
|
|
|
|
- **Origin**: [overview.md](overview.md)
|
|
- **Status**: resolved (2026-10-04, operator decision; Phase 0)
|
|
- **Priority**: medium
|
|
- **Resolution**: Reactive-core crate + per-engine engine crates; the
|
|
split isolates the engines' asymmetry of work and keeps engine
|
|
binaries single-driver (which the libsqlite3-sys collision
|
|
effectively requires). Decision recorded in
|
|
[ADR-001](decisions/001-crate-split.md) — including the reasoning
|
|
that superseded the inventory's single-crate lean.
|
|
- **Cross-references**: OQ-03, OQ-10.
|
|
|
|
### OQ-03: Driver story — sqlx, tokio-postgres, or per-engine drivers? *(== OQ-ST-03)*
|
|
|
|
- **Origin**: [overview.md](overview.md)
|
|
- **Status**: resolved (2026-10-04, POC-backed both engines; Phase 0)
|
|
- **Priority**: medium
|
|
- **Resolution**: Per-engine drivers — rusqlite + honker-core 0.5.0
|
|
(SQLite; bridged seam), tokio-postgres 0.7.18 + deadpool-postgres
|
|
0.14.2 (Postgres), under OQ-02's crate split. Decisions recorded in
|
|
[ADR-003](decisions/003-sqlite-driver.md) and
|
|
[ADR-004](decisions/004-postgres-driver.md); measurements in
|
|
`docs/research/poc-sqlite-posture-findings.md` and
|
|
`docs/research/poc-pg-posture-findings.md`.
|
|
- **Cross-references**: OQ-02, OQ-05, OQ-06.
|
|
|
|
### OQ-07: SQLite-side scope — loadable extension? *(== OQ-ST-07)*
|
|
|
|
- **Origin**: [ADR-002](decisions/002-feature-scope.md)
|
|
- **Status**: resolved (cut-only, 2026-10-04; Phase 0)
|
|
- **Priority**: low
|
|
- **Resolution**: The loadable-extension surface is out: every
|
|
identified consumer is in-process Rust attaching to its own
|
|
connection (the honker-core `attach_honker_functions` shape). Folded
|
|
into OQ-01's scope resolution and [ADR-002](decisions/002-feature-scope.md)
|
|
(explicitly, so the slot's disposition is visible — the Phase 0
|
|
register carried it as its own entry).
|
|
- **Cross-references**: OQ-01.
|
|
|
|
## Theme: Core contract
|
|
|
|
### OQ-04: Contract pinning — the exact trait surface and its semantics *(== OQ-ST-04)* — **RESOLVED**
|
|
|
|
*(Retitled in promotion: OQ-ST-04's framing — "the reactive
|
|
abstraction — what does the unified notify surface look like?" —
|
|
narrowed to the pinning work its own record already scoped.)*
|
|
|
|
- **Origin**: [core-contract.md](core-contract.md),
|
|
[ADR-006](decisions/006-wake-and-delivery-contract.md),
|
|
[ADR-007](decisions/007-transactional-seam.md)
|
|
- **Status**: resolved (2026-10-05, Phase 1 — [ADR-008](decisions/008-contract-v1-pinning.md))
|
|
- **Priority**: high
|
|
- **Resolution**: Pinned as contract v1 by
|
|
[ADR-008](decisions/008-contract-v1-pinning.md): (1) the v1
|
|
surface partition — notify/streams/queue-skeleton/locks/outbox/tx
|
|
seam are contract; queue depth rides OQ-05, scheduler shape rides
|
|
OQ-09, capabilities ride OQ-08, `claim_waker` stays engine-internal,
|
|
cut-flag rows stay out. (2) `TxHandle`: the `*_tx` methods live on
|
|
the handle trait — boxed `dyn` handle, no downcast, no enum (the
|
|
enum would invert ADR-001's dependency direction); the POCs'
|
|
`as_any_mut` friction dissolves by construction. (3) `listen()`
|
|
returns a `WakeReceiver` delivering opaque `Wake { channel }` —
|
|
honker's payload transport is not surfaced (uniformity with the
|
|
opaque contract, ADR-006). (4) Reserved prefix `__alkstore_`, with
|
|
`__alkstore_listener_reconnected__` as the one v1-reserved string
|
|
(the POC's ad-hoc `__listener_reconnected__` renamed at
|
|
implementation). (5) Error taxonomy: `PayloadTooLarge` /
|
|
`ReservedName` / `InvalidName` / `Closed` / `Codec` / `Database`,
|
|
pinned by the act-differently rule. (6) `Store::open` is engine-crate
|
|
surface; the contract is the trait it returns. (7) Locks guarantee
|
|
row added to ADR-006's table (TTL-bounded mutual exclusion, silent
|
|
expiry); the scheduler row is explicitly transferred to OQ-09's
|
|
resolution. A verification backlog (core-contract.md §Verification
|
|
backlog) tracks the one-engine-pinned properties.
|
|
- **Cross-references**: OQ-01, OQ-10, OQ-09, OQ-08.
|
|
|
|
## Theme: Queues and scheduling
|
|
|
|
### OQ-05: Queue semantics depth — retry/backoff/dead-letter/sweep design *(== OQ-ST-05)* — **RESOLVED**
|
|
|
|
- **Origin**: [queues.md](queues.md)
|
|
- **Status**: resolved (2026-10-05, Phase 1 — [ADR-010](decisions/010-queue-semantics-depth.md))
|
|
- **Priority**: high
|
|
- **Resolution**: Pinned by ADR-010: (1) job state machine — three
|
|
states (`pending/processing/dead`), delete-on-ack, move-to-dead
|
|
(honker's model, pg-boss's seven-state enum not inherited),
|
|
`get_job` sees dead rows (`last_error`/`died_at` — post-mortem by
|
|
API; `cancel` unconditional, not an interrupt). (2)
|
|
Visibility/heartbeat — renewal semantics (absolute reset),
|
|
late-heartbeat refusal (dual-execution window is contract-honest),
|
|
**reclaim-consumes-an-attempt** stated as contract text. (3)
|
|
Retry/backoff — `retry(err, None)` computes the queue's
|
|
equal-jitter exponential curve (range definitionally pinned, not a
|
|
labeled distribution; base `backoff_base_s`, cap 1 h, attempt index
|
|
= row `attempts` at retry), `Some(d)` overrides; queue-level
|
|
`QueueOpts {
|
|
visibility_timeout_s, max_attempts, backoff_base_s,
|
|
dead_letter_retention_s }` **stamped onto each job row at enqueue**
|
|
(§3a — no per-queue registry; two handles with different opts for
|
|
one queue name don't fight). (4) Dead-letter — move-to-table,
|
|
retention forever by default, no redrive API (recipe: `get_job` +
|
|
fresh `enqueue`). (5) `sweep_expired` — the no-stranded-rows
|
|
property (every past-expiry row in any state moves to dead; honker's
|
|
expired-processing zombie hole fails this — fixed at contract
|
|
level, SQLite-side realization rides OQ-06). (6) Sweep cadence —
|
|
no ambient sweeper; the recipe is the collapse machinery
|
|
(schedule + runner). (7) Result-storage cut-flag reconsidered and
|
|
**stands** (delete-on-ack; pg-boss's completed-row model is result
|
|
storage under another name). (8) Table layout — pg engine-owned
|
|
schema (`alkstore` default), queues are rows not tables; SQLite
|
|
rides `_honker_*`. *(Item 8's SQLite half superseded 2026-10-05:
|
|
the `_honker_*` family is re-owned as `__alkstore_*` by the fork —
|
|
[ADR-011](decisions/011-sqlite-substrate-fork.md), designed per
|
|
[ADR-012](decisions/012-forked-substrate-design.md).)* No new error
|
|
variants (ADR-008 §5's rule).
|
|
- **Cross-references**: OQ-09, OQ-06.
|
|
|
|
### OQ-09: Is the scheduler a first-class mechanism, or queues + `schedule()`? — **RESOLVED**
|
|
|
|
- **Origin**: [queues.md](queues.md) (spin-out of OQ-05's Phase 0
|
|
framing, where the scheduler's boundary question lived)
|
|
- **Status**: resolved (2026-10-05, Phase 1 — [ADR-009](decisions/009-scheduler-collapse.md))
|
|
- **Priority**: medium
|
|
- **Resolution**: **Collapse confirmed**, decided against the
|
|
inventory rows (alkblobs sweep cadence, alkfs orphan reaping — both
|
|
intervals; no row names schedule objects or wall-clock cron): the
|
|
contract surface is `schedule(name, spec, queue, payload, opts)`
|
|
(upsert by name), `unschedule(name)`, and the opt-in
|
|
`run_schedules(stop)` runner — no `Scheduler` handle, no
|
|
schedule objects; update = re-register, pause = unregister +
|
|
re-register. **Spec grammar v1: `@every <n><unit>` only** — cron
|
|
strings rejected (`InvalidSpec`), dissolving the timezone question
|
|
honker's local-time cron brittleness poses (extension path requires
|
|
a consumer-inventory row). Boundary guarantee row pinned (the
|
|
transfer from [ADR-008](decisions/008-contract-v1-pinning.md) §7
|
|
resolved): at-least-once per elapsed boundary while a leader runs,
|
|
fire+advance commit atomically under a row lock on the schedule row
|
|
(engine-generic no-double-fire floor; the leadership lock is the
|
|
efficiency layer), bounded catch-up (**fixed contract-constant cap
|
|
64**) with skip-forward; leadership lock is the reserved
|
|
`__alkstore_scheduler` name; `run_schedules` returns
|
|
`Err(LeadershipLost)` on lock loss, `Ok(())` on clean stop. Schedule
|
|
storage engine-internal; schedules never fire without a runner (no
|
|
ambient timers).
|
|
- **Cross-references**: OQ-05, OQ-04.
|
|
|
|
## Theme: Engines and dependencies
|
|
|
|
### OQ-06: honker-core quality read — does the default posture hold? *(== OQ-ST-06)* — **RESOLVED**
|
|
|
|
- **Origin**: [ADR-005](decisions/005-dependency-ownership.md),
|
|
[engine-sqlite.md](engine-sqlite.md)
|
|
- **Status**: resolved (2026-10-05, Phase 1 —
|
|
[ADR-011](decisions/011-sqlite-substrate-fork.md))
|
|
- **Priority**: high (fork trigger is a gate on the SQLite engine's
|
|
dependency posture)
|
|
- **Resolution**: **The fork trigger fires.** Evidence:
|
|
[quality-read-honker-core.md](../research/quality-read-honker-core.md).
|
|
The read's original target is clean (Writer/Readers, the polling
|
|
watcher's three-layer failure handling, `WatcherDeathGuard` — all
|
|
verified in source, present in 0.5.0; schema migrations minor). But
|
|
the published artifact is materially behind the reference revision:
|
|
crates.io's honker-core 0.5.0 predates upstream's own fix train for
|
|
its documented defect class (issue #133's savepoint hardening —
|
|
silent job loss on mid-flight errors in the dead-letter paths; five
|
|
`.ok()` error-swallows mapping every SQLite error to "no row"/"lock
|
|
held"), stranded unreleased with no announced date. And contract v1's
|
|
queue depth ([ADR-010](decisions/010-queue-semantics-depth.md) §3a/§5)
|
|
requires engine-owned enqueue/claim/sweep/get_job SQL in any posture
|
|
— making a post-fix ride a minority-shape, dual-owning the schema.
|
|
Resolution: fork honker-core at the reference revision into owned
|
|
code, inherit the clean watcher/transactional core + test suites,
|
|
re-derive the queue ops on contract v1, drop cron/experimental
|
|
backends, rename `_honker_*` → `__alkstore_*`. Recorded in
|
|
[ADR-011](decisions/011-sqlite-substrate-fork.md); ADR-005's posture
|
|
calculus applied, not renegotiated.
|
|
|
|
## Theme: Deployment and capabilities
|
|
|
|
### OQ-08: Where does the honest single-host/multi-host boundary live in the trait surface? *(== OQ-ST-08)*
|
|
|
|
- **Origin**: [deployment.md](deployment.md)
|
|
- **Status**: open
|
|
- **Priority**: medium
|
|
- **Resolution**: open. The pg engine is natively multi-host (POC #2
|
|
verified — no single-host assumption to remove); SQLite is
|
|
single-machine by nature (file-backed, NFS-two-writers unsupported —
|
|
honker's honesty posture, inherited by the forked substrate). The
|
|
unified surface must not pretend SQLite is multi-host. Options:
|
|
per-engine capability flags (`Store::capabilities()`), a documented
|
|
deployment matrix only ([deployment.md](deployment.md) carries the
|
|
facts), or compile-time knowledge only (a consumer choosing the
|
|
SQLite engine knows). Rides the now-pinned contract shape
|
|
([ADR-008](decisions/008-contract-v1-pinning.md)): the trait
|
|
constrains where capability differences can surface.
|
|
- **Cross-references**: OQ-04, [ADR-006](decisions/006-wake-and-delivery-contract.md), [ADR-012](decisions/012-forked-substrate-design.md) (the substrate inherits the honesty posture).
|
|
|
|
### OQ-10: How do engine crates track core-contract version changes?
|
|
|
|
- **Origin**: [overview.md](overview.md),
|
|
[ADR-001](decisions/001-crate-split.md)
|
|
- **Status**: open
|
|
- **Priority**: medium
|
|
- **Resolution**: open. The core crate's trait surface is a contract
|
|
the engine crates must track ([ADR-001](decisions/001-crate-split.md) negative consequence). What
|
|
is the versioning/sync discipline — semver-bump-only-when-
|
|
contract-changes, engines pin core ranges, a contract-compatibility
|
|
test suite the engines run against the core's trait definitions?
|
|
What happens to a released engine crate when core makes a contract
|
|
breaking change?
|
|
- **Cross-references**: OQ-04 (the contract being versioned — now
|
|
including its first post-v1 extensions, ADR-009/ADR-010), OQ-02.
|
|
|
|
### OQ-11: Forked-substrate follow-through — scaffold, provenance register, and cherry-pick discipline
|
|
|
|
- **Origin**: [ADR-012](decisions/012-forked-substrate-design.md)
|
|
(the fork design; substrate-side residue), [ADR-011](decisions/011-sqlite-substrate-fork.md)
|
|
- **Status**: open
|
|
- **Priority**: medium (nothing consumer-facing rides on it; it
|
|
resolves within the fork-scaffold task, which it does not gate)
|
|
- **Resolution**: open. ADR-012 fixed the *design* (contract-blind
|
|
boundary, fidelity posture, port deltas, bootstrap machinery,
|
|
upstream-tracking stance) and also pinned the provenance register's
|
|
location, timing, and initial contents (§1). The residue is: (1) the
|
|
exact Cargo-workspace layout and build wiring of
|
|
`alkstore-substrate` in this repository (crate location, feature
|
|
gating of the `bundled-sqlite` interplay with the engine crate's own
|
|
rusqlite dependency); (2) the provenance register's *format and
|
|
delta-list granularity* (per-commit entries vs per-delta-class;
|
|
whether cherry-pick records append at adoption time — location,
|
|
timing, and initial contents are ADR-012 §1's, not re-opened here);
|
|
(3) the cherry-pick *procedure* in practice — how a candidate
|
|
upstream fix is evaluated, applied, and recorded so ADR-012 §6's
|
|
deliberate-work posture stays auditable. These resolve *within* the
|
|
fork-scaffold task (its opening section), not before it and not as a
|
|
gate on writing it; the scaffold's build wiring depends on (1)'s
|
|
outcome.
|
|
- **Cross-references**: OQ-08 (the contract surface the engine maps
|
|
the substrate under), OQ-10 (the substrate is a crate in this
|
|
repository's Cargo workspace — its versioning interacts with the
|
|
discipline), ADR-011, ADR-012.
|
|
|
|
## Deferred / Blocked
|
|
|
|
None currently. Every open OQ above is actionable Phase 1 work
|
|
(capability-surface shape, versioning discipline, fork-scaffold
|
|
follow-through) with its evidence base complete — no external arrivals
|
|
are being waited on. |