Files
alkstore/docs/architecture/decisions/002-feature-scope.md
T
glm-5.3-flash 79a135c934 docs: resolve OQ-05 + OQ-09 — queue semantics depth (ADR-010) and scheduler collapse (ADR-009)
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.
2026-10-05 03:10:52 +00:00

104 lines
4.4 KiB
Markdown

# 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_commit`
POC 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/`@every` enqueueing 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](009-scheduler-collapse.md): 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](009-scheduler-collapse.md) §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 in
`docs/architecture/open-questions.md`.
- [core-contract.md](../core-contract.md) — the trait surface this
scope defines.
- [ADR-007](007-transactional-seam.md) — the transactional property all
in-scope enqueue/publish/notify features lean on.