Files
alkstore/docs/architecture/open-questions.md
T

31 KiB
Raw Blame History

status, last_updated
status last_updated
draft 2026-10-08 (fourth review round — waves-1–2 general-review findings resolved as ADR-023; question set remains closed)

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 — the contract surface everything else hangs off); OQ-09 + OQ-05 resolved (2026-10-05, ADR-009 and ADR-010 — the queue semantics track and the scheduler collapse/guarantee row); OQ-06 resolved (2026-10-05, ADR-011 — the fork trigger fired on the published-artifact facts); the fork design follow-through (2026-10-05, ADR-012 — substrate boundaries, fidelity posture, port deltas; its substrate-side residue is OQ-11); the fork packaging folded (2026-10-05, ADR-013 — no alkstore-substrate crate; the forked machinery is a module subtree of alkstore-sqlite); OQ-13 resolved (2026-10-05, ADR-014 — outbox_enqueue_tx joins the TxHandle trait, the hole is closed); OQ-12 resolved (2026-10-05, ADR-015 — streams depth pinned: carried-metadata keys, global-FIFO ordering row, StreamEvent shape, publish_with_key_tx, trim_to); OQ-08 resolved (2026-10-06, ADR-016 — no runtime capability surface; the honest boundary lives in compile-time engine identity + the documented deployment matrix); OQ-10 resolved (2026-10-06, ADR-017 — the core crate's semver is the contract version; four change classes with amend-in-place terminating at first release; the pairing tracked by manifest pin + version-stamped contract suite + docs); OQ-11 resolved (2026-10-06, ADR-018 — PROVENANCE.md in the substrate subtree; per-delta category-tagged entries; five-step cherry-pick procedure recorded at adoption). The Phase 1 question set is closed — no open OQs remain; Phase 1 moves to architecture review and the implementation-phase gates. Second review round (2026-10-06, pre-decomposition): the review found no new open questions — every finding was decidable from decided material, so it resolved directly as ADR-019 (handle-trait surfaces, Job/Schedule shapes, claimant identity, StopToken — closing the "pin at implementation" residue before ADR-017's class-1 window ends at first release) and ADR-020 (delay/run_at precedence, relative expires, scheduler-fired jobs' stamp source, the serde_json payload encoding bridge), plus mechanical fixes (ADR index staleness in overview/engine-postgres, stale wording, framing drift). Third review round (2026-10-07, pre-decomposition): likewise found no new open questions — five findings (a factually-incorrect ADR-019 §1 claim about tx-side reads, an ADR-010/ADR-019 Job-struct disagreement, an unvalidated schedule() write path into reserved names, the unpinned tx-handle drop disposition, unpinned EventReceiver error/close arms), all decidable from decided material, resolved directly as ADR-021 (tx-read methods on TxHandle, claimed_at restored, schedule queue-argument validation, drop = rollback, receiver arms pinned). Fourth review round (2026-10-08, the waves-1–2 general review — the first round after implementation began): the code review's four decision-needing findings (encode_payload's empty-Vec failure arm, claim_batch(n <= 0)'s LIMIT-dialect semantics, the inherited SQLITE_OPEN_URI flag, the 1 ms watcher-poll default) were likewise all decidable from decided material — resolved directly as ADR-023 (the helper fallible with Codec; numeric-argument domains pinned by kind — extents clamp empty, durations reject, boundaries total — a core-contract amendment, since engine-side divergence here would break engine-agnostic composition; plain-path open; the 1 ms default standing with the cadence carried onto SqliteOpts). No new open questions; the register stays closed.

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, ADR-002
  • 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.
  • Cross-references: OQ-07, OQ-04.

OQ-02: Crate scope — one store crate, or reactive-core + engines? (== OQ-ST-02)

  • Origin: 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 — including the reasoning that superseded the inventory's single-crate lean.
  • Cross-references: OQ-03, OQ-10 (resolved — ADR-017).

OQ-03: Driver story — sqlx, tokio-postgres, or per-engine drivers? (== OQ-ST-03)

  • Origin: 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 and ADR-004; 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
  • 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 (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, ADR-006, ADR-007
  • Status: resolved (2026-10-05, Phase 1 — ADR-008)
  • Priority: high
  • Resolution: Pinned as contract v1 by ADR-008: (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 (resolved — ADR-017, the discipline these v1 extensions now fall under), OQ-09, OQ-08; OQ-13 (the one gap the v1 pinning's own Phase 1 review found in it), OQ-12 (the sibling depth gap — resolved, ADR-015).

Theme: Queues and scheduling

OQ-05: Queue semantics depth — retry/backoff/dead-letter/sweep design (== OQ-ST-05) — RESOLVED

  • Origin: queues.md
  • Status: resolved (2026-10-05, Phase 1 — ADR-010)
  • 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, designed per ADR-012.) 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 (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)
  • 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 §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, engine-sqlite.md
  • Status: resolved (2026-10-05, Phase 1 — ADR-011)
  • 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. 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 §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; ADR-005's posture calculus applied, not renegotiated.

OQ-11: Forked-substrate follow-through — scaffold, provenance register, and cherry-pick discipline — RESOLVED

  • Origin: ADR-012 (the fork design; substrate-side residue), ADR-011, ADR-013
  • Status: resolved (2026-10-06, Phase 1 — ADR-018; item (1) dissolved 2026-10-05 by ADR-013)
  • Priority: medium (nothing consumer-facing rides on it; it resolves within the fork-scaffold task, which it does not gate)
  • Resolution: Pinned by ADR-018: (1) dissolved by the fold (ADR-013 §4) — no separate substrate crate, no wiring question. (2) Register format/granularity: a single in-tree file, alkstore-sqlite/src/substrate/PROVENANCE.md (the ADR-013 placement, the file named; timing and initial contents stay ADR-012 §1's as carried in-tree), carrying two sections — upstream identity, and the delta register (cherry-picks ride it as cherry-pick-category entries; no separate log). The delta register is per-delta, category-tagged, not per-commit (this repo's git history is the per-commit record; a per-commit register would duplicate it), each entry pinning category / what / where / why / lineage (upstream commit hashes for cherry-picks, "ours" for born deltas). The initial entries are fixed by ADR-011's scope register and ADR-012 §3–§5 — the register formats them, decides nothing new. (3) Cherry-pick procedure: five steps — notice (no ambient sweep; triggered candidate arrival), evaluation against ADR-012 §6's port-cost logic (fixes for re-derived-half classes are not adoptable — the correct response there is an owned fix), hand application preserving ADR-012 §3's fidelity posture and verified against the inherited suites + contract suite, record at adoption (register entry + one engine-changelog line citing the register — visible but non-versioning per ADR-017 §2 class 4), and a commit whose message names the upstream hash(es), cross-linking git history and register. A change touching the re-derived half or crossing the contract boundary is not a cherry-pick — it is an owned fork change under the engine's own discipline and, if contract-visible, ADR-017's taxonomy; ADR-012 §2's boundary is what splits the two record types. Its content lands as the fork-scaffold task's opening section; gates nothing.
  • Cross-references: OQ-08 (the contract surface the engine maps the substrate under), OQ-10 (the engine's versioning discipline now carries the fold's provenance duties in-tree — and its class-4 non-events rule is what keeps substrate cherry-picks from being contract events; ADR-017), ADR-011, ADR-012, ADR-013.

OQ-10: How do engine crates track core-contract version changes? — RESOLVED

  • Origin: overview.md, ADR-001
  • Status: resolved (2026-10-06, Phase 1 — ADR-017)
  • Priority: medium
  • Resolution: Pinned by ADR-017: the core crate's semver is the contract version — no parallel versioning surface (the OQ's three candidate mechanisms compose rather than rival: semver discipline → the change-class rule, engine range pins → the manifest carrier, the contract suite → the compatibility instrument). (1) One-normative-owner rule applied to the version question: the crate is a contract artifact and nothing else, so its version ledger is the contract ledger, and its changelog cites the ADR of every contract change (no silent contract changes — the semantic-break guard, since guarantee-row breaks are not compiler-visible). (2) Four change classes: amend-in-place (the ADR-014/015 precedent — terminates permanently at the core crate's first release; ADR-009/010, though framed "post-v1," factually fall in this class and ship in the initial contract v1 text); additive extension (the ADR-009/010 shape, post-release — core minor, row-first discipline); breaking change (signatures and semantic breaks — the act-differently measure generalized to change classification; core major); non-events (engine-internal change — structurally guaranteed by ADR-012 §2's contract-blind boundary: substrate cherry-picks cannot be contract events). (3) #[non_exhaustive] on the consumer-read value types (Error, StreamEvent, Job, Schedule, Wake) — mechanically making additions there minor and enforcing the catch-all matching posture; the consumer-constructed opts structs are exempt (non_exhaustive bans struct-literal construction), so their field additions are breaking (class 3), priced deliberately; trait methods stay the honest lockstep cost. (4) The pairing tracked by three carriers of one fact: the engine's manifest pin (alkstore = "1.y"), the version-stamped contract suite (the backlog matures into the OQ's option-(c) compatibility instrument), and engine-crate docs. (5) Released engines on a breaking change: nothing forced — the released engine serves its pinned contract majors until archived; the new major gains its own engine line, migration is the consumer's deliberate two-line bump; and no default-trait-method shims to dodge adoption (the "no trait- default drift" line, stated). Engine semver stays independent (it versions the engine's own surface).
  • Cross-references: OQ-04 (the contract being versioned — its first post-v1 extensions, ADR-009/010, resolved pre-release into contract v1's initial text), OQ-02, OQ-08 (the narrowing — no capability struct to govern), ADR-011/ ADR-012/ ADR-013 (the fold that removed the substrate's versioning surface), OQ-11 (the cherry-pick procedure governing the substrate-side non-events).

Theme: Core contract (Phase 1 review finds)

OQ-12: Streams depth — key semantics, StreamEvent shape, ordering row, retention — RESOLVED

  • Origin: core-contract.md streams section, ADR-008 §1 (Phase 1 architecture review, 2026-10-05)
  • Status: resolved (2026-10-05, Phase 1 — ADR-015)
  • Priority: high (streams is a first-class ADR-002 mechanism; ADR-008's "no placeholder semantics remain" positive consequence was false for publish_with_key until this resolved — the contract suite also could not pin cross-engine stream equivalence without an ordering row)
  • Resolution: Pinned by ADR-015. (1) Key semantics — option (a): the key is carried metadata (honker's shape — a stored nullable column, round-tripped on every read, no engine-enforced behavioral role); the ordering guarantee stays global FIFO by offset per stream; per-key in-order reading is the documented emergent pattern, not a promise. Option (b) (server-enforced per-key ordering) rejected on its own decision rule — no consumer row names the need; the consumer inventory's streams row and the alkcall ecosystem-shape row (added for this decision — alkcall's Sub/Pub/fan-out vocabulary routes the topic/event-type dimension to separate streams, never per-key lanes) both name none. Re-entry gate: a consumer-inventory row naming server-enforced per-key ordering. Option (c) (cut publish_with_key) rejected — removing a pinned v1 method name would be a contract-breaking event for OQ-10, the cost is one nullable column the inherited realizations already have, and the key is a legitimately useful grouping token. (2) A seam gap the key decision exposed, closed in the same ADR: publish_with_key_tx(stream, key, payload) -> offset joins the TxHandle trait — keyed publishes were otherwise unreachable commit-atomically, the same hole shape OQ-13 exposed for the outbox (publish_tx = the None-key call, not a separate engine path; empty-Some-key → InvalidName, no new variants). (3) StreamEvent shape — honker's near-verbatim with one rename: { offset: i64, stream: String, key: Option<String>, payload: Vec<u8>, created_at: i64 } (topic → stream — one term everywhere; the substrate's column name stays per ADR-012 §3 fidelity); offset immutable, never renumbered (gaps after trim legal); created_at unix-seconds-at-publish, informational, not an ordering field. (4) Ordering guarantee row — ADR-006 §2's streams row extended: global FIFO by offset (read paths yield offset ASC), offsets immutable — the row the contract suite pins cross-engine equivalence against. (5) Retention — trim_to(horizon) added to the stream handle (delete offset <= horizon), consumer-invoked, no engine-default retention, no ambient sweeper (the ADR-010 §6 posture); the replay-forever default is documented with the in-contract tool to bound it; reads from a trimmed-away region resume at the horizon; saved offsets below the horizon stay valid. Framed as completing contract v1 in place, pre-implementation — no versioning event, OQ-10 untouched. Contract-suite rows added (cross-engine stream equivalence, keyed-tx commit-atomicity, trim_to semantics — both engines).
  • Cross-references: OQ-04 (the pinning that left this depth open), OQ-13 (the sibling depth gap; the amend-in-place framing), OQ-01 (scope row), ADR-002, ADR-006 §2 (the guarantee table the ordering row extends), ADR-008 §1/§4/§5/§8, ADR-010 §6 (the no-ambient-sweeper posture the trim decision applies), ADR-012 §3 (the fidelity posture the column-name keep rides).

OQ-13: Transactional outbox enqueue shape — the TxHandle surface cannot express it — RESOLVED

  • Origin: core-contract.md (outbox section), ADR-008 §1 (Phase 1 architecture review, 2026-10-05)
  • Status: resolved (2026-10-05, Phase 1 — ADR-014)
  • Priority: high (the outbox's only reason to exist is the commit-atomic enqueue; as pinned, the v1 surface cannot perform it — a decomposition off the current text would produce an unimplementable task)
  • Resolution: Pinned by ADR-014: one method, outbox_enqueue_tx(outbox, opts, payload) -> job_id, added to the TxHandle trait. The method takes the outbox name (validated like store.outbox(name) — InvalidName/ReservedName), derives the backing queue name __alkstore_outbox:{outbox} engine-side, and lands the job row in the caller's transaction with EnqueueOpts stamped per ADR-010 §3a over the backing queue's derived QueueOpts — commit-atomic with the business write, rollback drops both (the no-ghosts property). The reserved-prefix rejection is unchanged: it governs directly-supplied names; the derivation is legitimate engine-internal naming (the same status run_once's consumption of the backing queue already had). Option (b) (an OutboxHandle off the tx handle) is rejected — a second boxed handle to deliver one op; option (c) (non-transactional only) is rejected — it drops the property the mechanism exists for. Honker's raw-Transaction seam is not inherited (no engine-neutral Transaction type exists; it would force a core→engine dependency or a downcast seam — the two things ADR-008 §2 dissolved). Framed as completing contract v1 in place, pre-implementation — no versioning event, OQ-10 untouched. Contract-suite row added (commit-atomicity on both engines).
  • Cross-references: OQ-04 (the pinning that missed this), OQ-12 (the sibling depth gap found by the same review — resolved the same day, ADR-015), ADR-002 (outbox scope row), ADR-007/ADR-008 (the seam design), ADR-010 §3 (the outbox's derived QueueOpts — unchanged by this).

Theme: Deployment and capabilities

OQ-08: Where does the honest single-host/multi-host boundary live in the trait surface? (== OQ-ST-08) — RESOLVED

  • Origin: deployment.md
  • Status: resolved (2026-10-06, Phase 1 — ADR-016)
  • Priority: medium
  • Resolution: Pinned by ADR-016: no runtime capability surface — in v1 and by default ever. The honest single-host/multi-host boundary lives in the two places it is already true, which compose rather than rival: (1) compile-time engine identity — the engine crate a binary depends on is the deployment statement (single-driver binaries, ADR-001; constructors in engine crates, ADR-008 §6 — "the engine choice is a dependency-graph fact, not a runtime branch"); (2) deployment.md's documented matrix — the ops-facing facts of record, unchanged. Option 2 (Store::capabilities()) rejected field-by-field under ADR-008 §5's act-differently rule generalized to surface: host semantics admit no in-process action (a flag would invite the engine-type branch principle 4 bans); payload limits already have their runtime carriage — the universal, contract-wide matchable PayloadTooLarge variant (a capabilities() field would be a second normative home); wake cadence and knobs are engine-crate config (§6's split); and no consumer-inventory row names any runtime-adapt need. No engine_name(), no #[cfg] capability axes. The "must not pretend SQLite is multi-host" obligation resolves into three standing statements (contract text carries asymmetries via the taxonomy, engine-crate docs carry posture, deployment.md carries ops facts); the misconfiguration case (SQLite as shared network storage) follows the family's deployment-asserts-truth posture (alkblobs precedent) — documented boundary, no fabricated detection. Contract-suite row added: PayloadTooLarge occurrence asymmetry (pg client-side pre-round- trip, SQLite never) — with no capabilities API, the variant is the one runtime carriage of an engine asymmetry, so its matchability is pinned. Re-entry gate: a consumer-inventory row naming a runtime-adapt need.
  • Cross-references: OQ-04 (the pinning that parked this), OQ-10 (narrowed by this — no capability struct to govern), OQ-11, ADR-001, ADR-006, ADR-008 §1/§5/§6, ADR-012, deployment.md.

Deferred / Blocked

None. The open question set is empty — every OQ above is resolved (2026-10-04 through 2026-10-06, ADR-001 through ADR-018). Phase 1's remaining work is architecture review closure and the implementation phase's gates; no external arrivals are being waited on. The review rounds after closure (2026-10-06 second, 2026-10-07 third, 2026-10-08 fourth) likewise produced no new OQs — their findings resolved directly as ADR-019/020, ADR-021, and ADR-023 respectively.