ADR-009: scheduler collapses into queues — schedule()/unschedule()/ run_schedules (opt-in, no ambient timers), @every-only v1 grammar (dissolves honker's local-TZ cron brittleness), boundary guarantee row (at-least-once per boundary, fixed 64-cap catch-up with skip-forward, row-locked fire tx as engine-generic no-double-fire floor), __alkstore_scheduler leadership lock, InvalidSpec + LeadershipLost taxonomy additions. ADR-010: queue depth pinned engine-uniformly — three-state machine (pending/processing/dead) with delete-on-ack, get_job sees dead rows, heartbeat = renewal with late-heartbeat refusal, reclaim-consumes- an-attempt stated as contract text, equal-jitter exponential backoff (range definitionally pinned, 1 h cap), QueueOpts stamped onto job rows at enqueue (no per-queue registry), move-to-dead dead-letter with retention-sweep support and no redrive API, sweep_expired carries the no-stranded-rows property (fixes honker's expired- processing zombie hole — SQLite-side realization rides OQ-06 as a concrete fork candidate), one engine-owned pg schema, queues are rows not tables, result-storage cut-flag stands. Also: full honker-machinery and pgboss-rs reference reads persisted (docs/research/reference-*.md — the honker defect list pre-stages the OQ-06 quality read), queues.md rewritten from design-space frame to resolved-depth spec, core-contract/engines/README/overview/deployment/ ADR-002 propagated.
4.4 KiB
ADR-002: Feature scope — what the store surface includes
Status
Accepted
Context
Honker's full surface (the interface prior art) covers nine feature
families: notify/listen, streams, queues, scheduler, outbox, named
locks, rate limits, result storage, and loadable-extension serving.
The crate must decide which of these it ships, and the evidence-first
answer (docs/research/consumer-inventory.md, 2026-10-04) grades each
row by whether any consumer document actually names a need. This ADR
fixes that scope so contract work isn't done against features no
consumer wants.
Decision
In scope, first-class (a named consumer is pinned or the operator has recorded the need):
- notify / listen — the transactional fire-and-forget signal layer.
Pinned (alkfs path-tree invalidation; the
watch_fires_on_commitPOC evidence) and documented (alkblobs fleet visibility). - streams — durable pub/sub with per-consumer offsets and
replay-on-attach. Operator-authority record (2026-10-04,
consumer-inventory.md): type-filtered event watching / repo-change subscriptions that notify cannot honestly serve (no replay, no durability).
In scope (documented need, thinner):
- named locks — TTL coordination locks. Pinned (alkblobs fleet sweeper) and documented (alkfs writer coordination); alkgit's CAS has an alternative design a lock is allowed to replace but needn't.
- queues — durable at-least-once work with the transactional enqueue shape. Documented (alkfs sync/fetch-on-miss outbox; alkblobs maintenance cadence).
- outbox helper — a thin helper over queues (enqueue inside the business transaction + a delivery/consumption worker shape), not a separately-needed feature. Same evidence as queues.
- scheduler — cron/
@everyenqueueing into named queues, leader-elected. Documented-thin; the family-wide "who sweeps/renews/reaps" need. May collapse into queues if design shows scheduler = queues + tick (tracked in OQ-09). (Narrowed 2026-10-05 by ADR-009: collapsed to queues +schedule()/run_schedules, spec grammar@every-only — the "cron" label reflected honker's menu, not any inventory row's body; no consumer names wall-clock cron — grammar pinned by ADR-009 §2.)
Cut-flag (no named consumer; not silently included):
- rate limits — alkgit enforces budgets in its own wire layer; a store-level rate limit is not the proven mechanism. The row stays listed with its flag visible; cut at implementation time rather than carried on momentum. If a distributed (cross-process) rate-limit need materializes, it gets a consumer-inventory row first.
- result storage (
save_result/get_result/sweep_results) — adjacent-tooling shape, useful but named by no consumer. Same cut-flag treatment.
Out:
- The loadable-extension surface — no consumer needs it; every identified consumer is in-process Rust attaching to its own connection (OQ-ST-07, resolved cut-only). Cut unless a consumer appears.
- Honker's exclusion lines, inherited as out: workflow DAGs, task chains/chords, multi-writer replication, distributed (cross-machine) locking. They stay out unless a consumer document grows a row.
New consumers add a row to docs/research/consumer-inventory.md
before being assumed into scope.
Consequences
Positive
- The contract surface is small and every part of it has a nameable consumer — contract-pinning work isn't spent on speculative surface.
- Scope honesty is mechanical: the inventory is the ledger, and the cut-flag rows keep their flags visible instead of being silently dropped or silently shipped.
Negative
- Rate limits and result storage, if ever needed, require the consumer inventory + contract process first — no ad-hoc additions.
- Cutting the extension surface narrows honker compatibility; any future non-Rust consumer of these SQLite files cannot use honker's extension (acceptable: none exists).
References
docs/research/consumer-inventory.md— per-feature evidence grades.- OQ-ST-01, OQ-ST-07 (
docs/research/phase-0.md) — resolved by this evidence; promoted as OQ-01 and OQ-07 indocs/architecture/open-questions.md. - core-contract.md — the trait surface this scope defines.
- ADR-007 — the transactional property all in-scope enqueue/publish/notify features lean on.