release-crate-docs: crate docs final pass — doc gate green with -D warnings posture recorded in AGENTS.md, warn(missing_docs) adopted on the four published crates (audit probe 0; Error enum+fields, sqlite substrate fuzz-surface fns filled), accuracy pass fixed core drifts (trim_to wake nuance, ADR-023 §2 domain rules + shared paragraph, QueueOpts gloss, MemStore::new exception) and engine postures (eleven-vs-ten tx-method count, spawn_blocking rationale via sync substrate, one-host cross-process honesty element), spec-side aligned (pg locks family + row-backed wording, mem clock-accessor home), two boundary-wording dispositions recorded for release-contract-window-close (try_recv arms, 8000-byte <= reading), queue round-trip doctest landed in alkstore-mem crate docs; task -> completed with Notes + Summary
This commit is contained in:
1 parent
5f5f6741d5
commit
8e27f4b64b
22 files changed
+289
-49
No files matched your search
@@ -93,6 +93,17 @@ cargo clippy --all-targets -- -D warnings
|
||||
cargo fmt --check
|
||||
```
|
||||
|
||||
Doc gates (task `release-crate-docs`, 2026-10-11):
|
||||
|
||||
```
|
||||
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps # zero rustdoc warnings (broken/redundant/private-intra-doc links)
|
||||
```
|
||||
|
||||
The four published crates (`alkstore`, `alkstore-sqlite`,
|
||||
`alkstore-postgres`, `alkstore-mem`) carry `#![warn(missing_docs)]` in
|
||||
their roots — enforced through the clippy `-D warnings` gate above
|
||||
(rustc lints promote to errors there).
|
||||
|
||||
Fuzzing is adopted (2026-10-10 — wave 7; `docs/research/fuzzing.md`),
|
||||
modeled on the alkcall/alktunnels/alksocks/alktty pattern: the `fuzz/`
|
||||
sidecar workspace pins nightly inside `fuzz/` only, the stable
|
||||
|
||||
@@ -87,7 +87,7 @@ fn expect_ok<T>(label: &str, out: alkstore::Result<T>) {
|
||||
/// **Exemplar row** — name validation at every entry point,
|
||||
/// auto-commit and tx paths alike.
|
||||
///
|
||||
/// Every name-bearing entry point on the [`Store`](alkstore::Store)
|
||||
/// Every name-bearing entry point on the [`Store`]
|
||||
/// surface — and every name-bearing `*_tx` twin on the
|
||||
/// [`TxHandle`](alkstore::TxHandle) — rejects, before any engine round
|
||||
/// trip: the empty (or whitespace-only) name with
|
||||
@@ -1143,7 +1143,7 @@ async fn await_claimable(
|
||||
/// equivalence pin (ADR-012 §2: engine-side arithmetic, suite-pinned
|
||||
/// identical outputs). The row drives claim → `retry(err, None)`
|
||||
/// cycles on a queue with `backoff_base_s = 2` (integer-second
|
||||
/// resolution: the ranges [1,2], [2,4], [4,8] land across the first
|
||||
/// resolution: the ranges \[1,2\], \[2,4\], \[4,8\] land across the first
|
||||
/// three attempts), reading each computed delay back through
|
||||
/// `get_job`'s resolved `run_at` minus a clock read taken before the
|
||||
/// retry, asserting range membership per attempt — with a
|
||||
|
||||
+36
-3
@@ -29,13 +29,46 @@
|
||||
//! # Architecture
|
||||
//!
|
||||
//! The store instance *is* the state: guarded per-mechanism sections
|
||||
//! (`crate::state`) behind short-held synchronous internal locks,
|
||||
//! behind short-held synchronous internal locks,
|
||||
//! tokio primitives for cross-await coordination. The tx seam is an
|
||||
//! overlay ([`crate::tx`] — stage in the handle-private overlay, commit
|
||||
//! overlay (`tx` — stage in the handle-private overlay, commit
|
||||
//! merges atomically + wakes at commit, drop discards); wakes route
|
||||
//! through in-process channels (`crate::wakes`) — delivered at the
|
||||
//! through in-process channels (`wakes`) — delivered at the
|
||||
//! commit boundary directly to each open receiver, per-subscriber FIFO
|
||||
//! by commit order, never coalesced (no batching boundary exists).
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! The honest-ephemeral engine is the zero-config one, so this crate
|
||||
//! carries the contract round-trip example: open with one call,
|
||||
//! enqueue a job, claim it back (the other engines construct through
|
||||
//! their `open` form with a connection target and opts — the surface
|
||||
//! round-trip below is identical there).
|
||||
//!
|
||||
//! ```
|
||||
//! use alkstore::{EnqueueOpts, JobHandle, QueueOpts, Store};
|
||||
//! use alkstore_mem::MemStore;
|
||||
//!
|
||||
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! let rt = tokio::runtime::Builder::new_current_thread().build()?;
|
||||
//! let store = MemStore::new();
|
||||
//! let queue = rt.block_on(store.queue("demo", QueueOpts::default()))?;
|
||||
//! let job_id = rt.block_on(queue.enqueue(
|
||||
//! serde_json::json!("hello"),
|
||||
//! EnqueueOpts::default(),
|
||||
//! ))?;
|
||||
//! let claimed = rt.block_on(queue.claim_one("worker-1"))?
|
||||
//! .expect("the enqueued job is claimable");
|
||||
//! assert_eq!(claimed.job().id, job_id);
|
||||
//! assert_eq!(claimed.job().state, alkstore::JobState::Processing);
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! Every public item carries doc text; the missing-docs lint is part
|
||||
//! of the standing gates.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
mod clock;
|
||||
mod lock;
|
||||
|
||||
@@ -34,6 +34,11 @@
|
||||
//! module's bootstrap the DDL ground. The verification backlog's pg
|
||||
//! column (the ADR-022 contract suite rows this engine owns) runs in
|
||||
//! this crate's `contract_suite` test target.
|
||||
//!
|
||||
//! Every public item carries doc text; the missing-docs lint is part
|
||||
//! of the standing gates.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
mod forwarder;
|
||||
mod lock;
|
||||
|
||||
@@ -35,7 +35,8 @@ pub(crate) const LISTENER_APPLICATION_NAME: &str = "alkstore-pg-listener";
|
||||
/// are `open`'s first argument (the tokio-postgres
|
||||
/// [`Config`](::tokio_postgres::Config)-parseable form, ADR-008 §6's
|
||||
/// `open(url, PgOpts)` pin); the engine never invents defaults for
|
||||
/// them. `open` fails with [`Error::Database`] if the config is
|
||||
/// them. `open` fails with
|
||||
/// [`Error::Database`](alkstore::Error::Database) if the config is
|
||||
/// unparseable or the server unreachable. TLS: the engine hardwires
|
||||
/// `NoTls` on every connection path regardless of the config's
|
||||
/// sslmode — v1 TLS is a post-v1 deployment concern
|
||||
@@ -54,7 +55,8 @@ pub struct PgOpts {
|
||||
/// Default [`DEFAULT_MAX_SIZE`] = 8 (the deployment matrix's
|
||||
/// budget line; the listener adds +1 per LISTEN-ing process
|
||||
/// *outside* the pool — sizing rule `max_size + 1`). A value of
|
||||
/// `0` fails [`open`](crate::open) with [`Error::Database`]
|
||||
/// `0` fails [`open`](crate::open) with
|
||||
/// [`Error::Database`](alkstore::Error::Database)
|
||||
/// immediately (validated at entry — no round trip; deadpool
|
||||
/// cannot grant checkouts from an empty pool).
|
||||
pub max_size: usize,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! The transactional seam's pg arm (ADR-007) — `begin_tx`, the
|
||||
//! [`PgTxHandle`] (the pooled-object handle), and all eleven `*_tx`
|
||||
//! methods (`pg-engine-seam-tx`).
|
||||
//! [`PgTxHandle`] (the pooled-object handle), and all eleven
|
||||
//! transaction-handle methods (ten `*_tx` ops plus `commit`). (`pg-engine-seam-tx`).
|
||||
//!
|
||||
//! # No bridge, no `spawn_blocking`
|
||||
//!
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
//! # Posture
|
||||
//!
|
||||
//! **Single-host** (ADR-016): this engine is file-backed, one machine,
|
||||
//! no runtime capability surface, no multi-host mode — NFS
|
||||
//! two-writers-unsupported is the inherited lineage honesty, stated
|
||||
//! no runtime capability surface, no multi-host mode — cross-process
|
||||
//! *on one host* is verified ground (`data_version` is cross-process
|
||||
//! by nature; deployment.md's matrix line), NFS two-writers
|
||||
//! unsupported is the inherited lineage honesty, stated
|
||||
//! here as the crate's deployment boundary (compile-time engine
|
||||
//! identity is the only engine-discovery mechanism — pick this crate,
|
||||
//! get this posture). Durability knobs, pool sizing, and the watcher
|
||||
@@ -20,8 +22,14 @@
|
||||
//! points holds it; consumers budget transactions accordingly.
|
||||
//!
|
||||
//! Every engine call round-trips a blocking thread (the
|
||||
//! `spawn_blocking` seam — rusqlite connections are not
|
||||
//! `Send`-across-await; the family REQ-TTY-01 posture).
|
||||
//! `spawn_blocking` seam — the substrate is sync machinery, std
|
||||
//! threads and blocking iterators, bridged at the trait seam per the
|
||||
//! family REQ-TTY-01 posture).
|
||||
//!
|
||||
//! Every public item carries doc text; the missing-docs lint is part
|
||||
//! of the standing gates.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
mod lock;
|
||||
mod notify;
|
||||
|
||||
@@ -26,9 +26,12 @@
|
||||
//! [`SqliteStore::close`].
|
||||
//!
|
||||
//! Every engine call round-trips the `spawn_blocking` seam
|
||||
//! ([`crate::seam`]); rusqlite connections are not
|
||||
//! `Send`-across-await. Single-host by nature: file-backed, one
|
||||
//! machine; NFS two-writers-unsupported (the lineage's honesty
|
||||
//! ([`crate::seam`]) — the substrate is sync machinery (std threads,
|
||||
//! blocking iterators) bridged at the trait seam, per the family
|
||||
//! REQ-TTY-01 posture. Single-host by nature: file-backed, one
|
||||
//! machine; cross-process on one host is verified ground
|
||||
//! (`data_version` is cross-process by nature); NFS
|
||||
//! two-writers-unsupported (the lineage's honesty
|
||||
//! posture, inherited — ADR-016).
|
||||
|
||||
use alkstore::Store;
|
||||
|
||||
@@ -487,6 +487,8 @@ fn now_unix(conn: &Connection) -> rusqlite::Result<i64> {
|
||||
conn.query_row("SELECT unixepoch()", [], |r| r.get(0))
|
||||
}
|
||||
|
||||
/// The substrate's `@every` interval parser (`@every <n><unit>`,
|
||||
/// `<unit>` in `s|m|h|d`) — fuzz/test-only surface.
|
||||
pub fn parse_every_interval(spec: &str) -> Result<i64, String> {
|
||||
let body = spec
|
||||
.strip_prefix("@every ")
|
||||
@@ -524,6 +526,8 @@ fn next_every_boundary(spec: &str, after: i64) -> Result<i64, String> {
|
||||
.ok_or_else(|| format!("next boundary overflows for {spec:?}"))
|
||||
}
|
||||
|
||||
/// The substrate's schedule skip-forward arithmetic — fuzz/test-only
|
||||
/// surface.
|
||||
pub fn skip_forward(next_fire_at: i64, now: i64, interval: i64) -> i64 {
|
||||
let gap = now as i128 - next_fire_at as i128;
|
||||
let interval = interval.max(1) as i128;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! The transactional seam's SQLite arm — `begin_tx`, the
|
||||
//! [`SqliteTxHandle`] (the writer-slot lease, ADR-007), and all eleven
|
||||
//! `*_tx` methods (`sqlite-engine-seam-tx`).
|
||||
//! transaction-handle methods (ten `*_tx` ops plus `commit`) (`sqlite-engine-seam-tx`).
|
||||
//!
|
||||
//! `begin_tx` acquires the writer slot and opens `BEGIN IMMEDIATE` on
|
||||
//! the acquired connection; the handle owns connection + slot. Every
|
||||
|
||||
+20
-4
@@ -1,3 +1,7 @@
|
||||
/// The crate's error taxonomy (ADR-008 §5): a small closed set of
|
||||
/// contract-meaningful variants plus the opaque [`Error::Database`]
|
||||
/// fallback. Nothing engine-specific ever rides the surface; driver
|
||||
/// detail lives in the source chain of the opaque variant.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
@@ -11,16 +15,25 @@ pub enum Error {
|
||||
/// outbox shape — the id in the notification, the payload in a
|
||||
/// table row.
|
||||
#[error("payload exceeds the engine's notification payload limit (limit: {limit} bytes)")]
|
||||
PayloadTooLarge { limit: usize },
|
||||
PayloadTooLarge {
|
||||
/// The engine's byte limit that was exceeded.
|
||||
limit: usize,
|
||||
},
|
||||
/// A name carrying the reserved `__alkstore_` prefix was supplied
|
||||
/// to a shared-namespace entry point.
|
||||
#[error(
|
||||
"reserved name `{name}`: names carrying the reserved `__alkstore_` prefix are engine-owned"
|
||||
)]
|
||||
ReservedName { name: String },
|
||||
ReservedName {
|
||||
/// The rejected name, as supplied.
|
||||
name: String,
|
||||
},
|
||||
/// A name failed entry-point validation.
|
||||
#[error("invalid name `{name}`")]
|
||||
InvalidName { name: String },
|
||||
InvalidName {
|
||||
/// The rejected name, as supplied.
|
||||
name: String,
|
||||
},
|
||||
/// A schedule spec that doesn't parse under the v1 grammar
|
||||
/// (`@every <n><unit>` with `<n>` a positive integer and `<unit>`
|
||||
/// in `s|m|h|d`).
|
||||
@@ -28,7 +41,10 @@ pub enum Error {
|
||||
/// Rejected by `schedule()` before storage, with no engine round
|
||||
/// trip; the caller fixes the spec string (ADR-009 §2, §6).
|
||||
#[error("invalid schedule spec `{spec}`")]
|
||||
InvalidSpec { spec: String },
|
||||
InvalidSpec {
|
||||
/// The spec string that failed the grammar.
|
||||
spec: String,
|
||||
},
|
||||
/// The `run_schedules` runner's leadership lock was lost mid-run.
|
||||
///
|
||||
/// A distinct, matchable outcome from the clean `Ok(())` a stop
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
//! This crate is the contract artifact: no driver dependencies, ever.
|
||||
//! Engine crates (`alkstore-sqlite`, `alkstore-postgres`) depend on
|
||||
//! this crate and implement its traits.
|
||||
//!
|
||||
//! This crate's docs are part of the contract artifact (ADR-001 §1) —
|
||||
//! every public item carries doc text, and the missing-docs lint is
|
||||
//! part of the standing gates.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
mod error;
|
||||
mod event_receiver;
|
||||
|
||||
@@ -13,7 +13,9 @@ pub trait Lock: Send {
|
||||
/// namespace — validated at the constructor).
|
||||
fn name(&self) -> &str;
|
||||
|
||||
/// Renew: a new **full** TTL window from now. `false` = lost it
|
||||
/// Renew: a new **full** TTL window from now. `ttl` is a duration
|
||||
/// argument (ADR-023 §2): `ttl <= 0` is a rejection (opaque `Err`,
|
||||
/// never a silent immediately-expired lease). `false` = lost it
|
||||
/// (expired and re-acquired elsewhere — exclusivity lapses
|
||||
/// silently on expiry; no revocation event; ADR-008 §7). Holders
|
||||
/// of long work must renew within TTL.
|
||||
|
||||
@@ -51,9 +51,11 @@ pub struct EnqueueOpts {
|
||||
/// Opts changes apply to *future enqueues only*.
|
||||
///
|
||||
/// The numeric fields are trusted as given, engine-side — no domain
|
||||
/// validation (ADR-023 §2's domain table covers the trait-surface
|
||||
/// arguments, not these consumer-constructed constants; a
|
||||
/// consumer-obligation note, not a defect). A non-positive
|
||||
/// validation: ADR-023 §2's domain table enumerates its covered
|
||||
/// arguments by call-site kind (extents, `try_lock`/`renew` TTLs,
|
||||
/// `trim_to`'s horizon, enqueue `delay`/`run_at`/`expires`) and does
|
||||
/// not cover these queue-stamp fields (a consumer-obligation note,
|
||||
/// not a defect). A non-positive
|
||||
/// `visibility_timeout_s` stamps claims instantly reclaimable, and
|
||||
/// `max_attempts <= 0` stamps rows that are never claimed — see
|
||||
/// deployment.md's consumer-obligation notes for the per-field
|
||||
|
||||
@@ -41,7 +41,9 @@ pub trait Queue: Send + Sync {
|
||||
|
||||
/// Claim up to `n` rows, same ordering and exclusivity as
|
||||
/// [`Queue::claim_one`]. `worker_id` is consumer-local, non-empty
|
||||
/// only (`InvalidName`).
|
||||
/// only (`InvalidName`). `n` is an extent argument (ADR-023 §2):
|
||||
/// `n <= 0` claims nothing — the empty `Vec`, a value (never an
|
||||
/// error, never the whole queue).
|
||||
fn claim_batch<'a>(
|
||||
&'a self,
|
||||
worker_id: &str,
|
||||
|
||||
+22
-2
@@ -4,7 +4,25 @@
|
||||
//! Constructors and their option structs live in the engine crates
|
||||
//! (ADR-008 §6): the engine's `open` returns this trait, and a store
|
||||
//! handed out by an engine crate honors the full v1 surface. Consumer
|
||||
//! code never branches on engine type.
|
||||
//! code never branches on engine type. (The mem engine's engine-native
|
||||
//! constructor — `MemStore::new()`, no connection string — is the one
|
||||
//! documented exception to the open-prose, ADR-024 §4; the engine
|
||||
//! choice remains a dependency-graph fact made before any store
|
||||
//! exists.)
|
||||
//!
|
||||
//! # Numeric-argument domains (ADR-023 §2)
|
||||
//!
|
||||
//! Caller-supplied numeric arguments are classified by argument kind,
|
||||
//! uniform on every engine (validation at the trait-impl entry point):
|
||||
//! **extents** (`claim_batch`, the stream `read` forms, on the direct
|
||||
//! and tx paths alike) clamp to the empty value result on non-positive
|
||||
//! values — a value, not an error; **durations** (`try_lock`/
|
||||
//! `ttl`, `renew`/`ttl`) reject non-positive values (`Err`) — never a
|
||||
//! silent immediately-expired lease; **boundaries/offsets**
|
||||
//! (`trim_to`'s horizon; enqueue `delay`/`run_at`/`expires`) are
|
||||
//! total — any `i64` is well-defined. Queue/schedule opts-stamp fields
|
||||
//! are not in the domain table (trusted as given; deployment.md's
|
||||
//! consumer-obligation notes carry the per-field symptoms).
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
@@ -70,7 +88,9 @@ pub trait Store: Send + Sync {
|
||||
|
||||
/// Acquire a named TTL lock, or fail: `None` = someone holds it
|
||||
/// (no-work is a value, not an error). `owner` is consumer-local
|
||||
/// (non-empty only); `ttl` is seconds. Exclusion lapses silently
|
||||
/// (non-empty only); `ttl` is seconds — a duration argument
|
||||
/// (ADR-023 §2): `ttl <= 0` is a rejection (opaque `Err`, never a
|
||||
/// silent immediately-expired lease). Exclusion lapses silently
|
||||
/// at TTL expiry — holders renew within TTL (ADR-008 §7).
|
||||
fn try_lock<'a>(
|
||||
&'a self,
|
||||
|
||||
@@ -35,7 +35,9 @@ pub trait StreamHandle: Send + Sync {
|
||||
) -> BoxedFuture<'a, Result<i64>>;
|
||||
|
||||
/// Cursor read: events with `offset > ` cursor, `offset ASC`
|
||||
/// (global FIFO — ADR-015 §4), up to `limit`. A read from a
|
||||
/// (global FIFO — ADR-015 §4), up to `limit`. `limit` is an
|
||||
/// extent argument (ADR-023 §2): `limit <= 0` reads nothing —
|
||||
/// the empty `Vec`. A read from a
|
||||
/// trimmed-away region resumes at the trim horizon's first
|
||||
/// remaining row.
|
||||
fn read_since<'a>(
|
||||
@@ -46,7 +48,9 @@ pub trait StreamHandle: Send + Sync {
|
||||
|
||||
/// Cursor read from a consumer's stored checkpoint (absent
|
||||
/// consumer = 0). `consumer` is a consumer-local identifier
|
||||
/// (non-empty only — `InvalidName`).
|
||||
/// (non-empty only — `InvalidName`). `limit` is an extent
|
||||
/// argument (ADR-023 §2): `limit <= 0` reads nothing — the
|
||||
/// empty `Vec`.
|
||||
fn read_from_consumer<'a>(
|
||||
&'a self,
|
||||
consumer: &str,
|
||||
@@ -66,9 +70,14 @@ pub trait StreamHandle: Send + Sync {
|
||||
fn get_offset<'a>(&'a self, consumer: &str) -> BoxedFuture<'a, Result<i64>>;
|
||||
|
||||
/// Bounded growth, consumer-invoked: delete events with
|
||||
/// `offset <= horizon` (exact boundary; ADR-015 §5). Surviving
|
||||
/// `offset <= horizon` (exact boundary; ADR-015 §5). The horizon
|
||||
/// is a boundary argument (ADR-023 §2) — total: a negative
|
||||
/// horizon deletes nothing, idempotently. Surviving
|
||||
/// events keep their offsets (gaps legal, never renumbered); trim
|
||||
/// emits no wake. Returns the deleted-row count.
|
||||
/// emits no dedicated wake and no notify (on SQLite the
|
||||
/// `data_version` watcher may still fire on the trim commit — any
|
||||
/// committed write bumps it; a spurious hint, contract-legal).
|
||||
/// Returns the deleted-row count.
|
||||
fn trim_to<'a>(&'a self, horizon: i64) -> BoxedFuture<'a, Result<i64>>;
|
||||
|
||||
/// Durable consumption: attach a consumer that reads to the
|
||||
|
||||
+5
-2
@@ -107,7 +107,8 @@ pub trait TxHandle: Send {
|
||||
|
||||
/// Read stream events from `offset`, inside the caller's
|
||||
/// transaction (`offset ASC`, global FIFO — ADR-015 §4). Pure
|
||||
/// read. Validates `stream`.
|
||||
/// read. Validates `stream`. `limit` is an extent argument
|
||||
/// (ADR-023 §2): `limit <= 0` reads nothing — the empty `Vec`.
|
||||
fn read_since_tx<'a>(
|
||||
&'a mut self,
|
||||
stream: &str,
|
||||
@@ -117,7 +118,9 @@ pub trait TxHandle: Send {
|
||||
|
||||
/// Read stream events from a consumer's stored checkpoint, inside
|
||||
/// the caller's transaction. Pure read. Validates `stream`;
|
||||
/// `consumer` is consumer-local (non-empty only).
|
||||
/// `consumer` is consumer-local (non-empty only). `limit` is an
|
||||
/// extent argument (ADR-023 §2): `limit <= 0` reads nothing — the
|
||||
/// empty `Vec`.
|
||||
fn read_from_consumer_tx<'a>(
|
||||
&'a mut self,
|
||||
stream: &str,
|
||||
|
||||
@@ -36,11 +36,13 @@ pub enum NameKind {
|
||||
///
|
||||
/// - non-empty (after no trimming — whitespace-only counts as
|
||||
/// non-empty content but is nonetheless rejected below) →
|
||||
/// [`Error::InvalidName`] when empty
|
||||
/// [`Error::InvalidName`](crate::Error::InvalidName) when empty
|
||||
/// - `NameKind::Shared` additionally rejects names carrying
|
||||
/// [`RESERVED_PREFIX`] with [`Error::ReservedName`]
|
||||
/// [`RESERVED_PREFIX`] with
|
||||
/// [`Error::ReservedName`](crate::Error::ReservedName)
|
||||
///
|
||||
/// Whitespace-only names are rejected as [`Error::InvalidName`] for
|
||||
/// Whitespace-only names are rejected as
|
||||
/// [`Error::InvalidName`](crate::Error::InvalidName) for
|
||||
/// both kinds: they are never a meaningful name on any engine.
|
||||
pub fn validate_name(kind: NameKind, name: &str) -> crate::Result<()> {
|
||||
if name.is_empty() || name.trim().is_empty() {
|
||||
|
||||
@@ -58,7 +58,10 @@ daemons, the mem engine realizes with process-owned state:
|
||||
- **No background machinery to bootstrap** — no schema bootstrap, no
|
||||
watcher thread (nothing cross-connection to poll), no forwarder.
|
||||
The only async machinery is tokio-time-driven: lock TTLs, the
|
||||
scheduler's boundary waits, and wake delivery.
|
||||
scheduler's boundary waits, and wake delivery. All timestamps read
|
||||
the process clock through one internal accessor — no server clock
|
||||
to defer to, no per-mechanism clock reads (the wasm-posture note,
|
||||
[ADR-024](decisions/024-mem-engine.md) §5).
|
||||
- **Wake substrate**: in-process channels. Wakes deliver at the
|
||||
commit boundary directly to each open receiver — no batching
|
||||
boundary exists, so coalescing is not applicable; per-subscriber
|
||||
|
||||
@@ -27,7 +27,8 @@ obligations live in the core spec.
|
||||
family as design reference
|
||||
([ADR-005](decisions/005-dependency-ownership.md)); semantics depth
|
||||
pinned by [ADR-010](decisions/010-queue-semantics-depth.md) — all
|
||||
engine-owned tables (job, dead, stream, offsets, schedule) in one
|
||||
engine-owned tables (job, dead, stream, offsets, schedule, locks)
|
||||
in one
|
||||
PostgreSQL schema (default `alkstore`), queues as rows, no
|
||||
per-queue tables.
|
||||
|
||||
@@ -66,7 +67,7 @@ obligations live in the core spec.
|
||||
| notify / listen | `pg_notify(...)` inside the caller's tx (delivers at commit — native commit-atomicity, [ADR-007](decisions/007-transactional-seam.md)); `listen()` via LISTEN on the forwarder's connection, fanout to receivers |
|
||||
| streams | durable event table + per-consumer offset cursors; `pg_notify` as the wake trigger ([ADR-006](decisions/006-wake-and-delivery-contract.md) mechanism split: durable row, LISTEN wake — the pg-boss-family shape). Depth per [ADR-015](decisions/015-streams-depth.md): the event table carries the nullable key column (carried metadata), reads yield `offset ASC` (global FIFO — bigserial offsets, immutable), `publish_with_key_tx` validates + inserts inside the caller's tx, `trim_to` is a `DELETE … WHERE offset <= ?` on a pool connection (wakes nothing) |
|
||||
| queues | re-derived queue table + `FOR UPDATE SKIP LOCKED` claim + LISTEN-driven wake with re-poll safety net (default consumption posture, measured 5–16× vs 50 ms poll; poll-only fallback); states/dead-letter/backoff/visibility per [ADR-010](decisions/010-queue-semantics-depth.md), all in the engine-owned schema; the curve/stamps arithmetic is computed engine-side per [ADR-012](decisions/012-forked-substrate-design.md) §2 (equivalence with the SQLite engine pinned by the contract suite) |
|
||||
| named locks | advisory-lock-semantics TTL locks (pg-boss-family design reference; guarantee row pinned by [ADR-008](decisions/008-contract-v1-pinning.md) §7) |
|
||||
| named locks | row-backed TTL locks (a `locks` table in the engine-owned schema — ADR-008 §7 pins row-backed machinery, not pg advisory locks; the pg-boss family is the design reference; guarantee row pinned by [ADR-008](decisions/008-contract-v1-pinning.md) §7) |
|
||||
| scheduler / outbox | collapse shape ([ADR-009](decisions/009-scheduler-collapse.md)): schedule rows in the engine-owned schema, tick re-derived (boundary advance + 64-boundary catch-up cap, honker parity), leadership via the engine's lock machinery on `__alkstore_scheduler`; outbox = helper over queues |
|
||||
| begin_tx | pool checkout + `BEGIN`, returning the caller-held handle ([ADR-007](decisions/007-transactional-seam.md)) |
|
||||
| handle ops | straight `.await`s through the held object; commit/rollback returns the object to the pool |
|
||||
|
||||
+117
-9
@@ -1,7 +1,7 @@
|
||||
---
|
||||
id: release-crate-docs
|
||||
name: Crate docs final pass — rustdoc across the four published crates
|
||||
status: pending
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: moderate
|
||||
risk: low
|
||||
@@ -50,17 +50,17 @@ polish.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `cargo doc --no-deps` (workspace) zero warnings; the
|
||||
- [x] `cargo doc --no-deps` (workspace) zero warnings; the
|
||||
`-D warnings` doc gate posture recorded
|
||||
- [ ] Missing-docs audit recorded (public items × 4 crates); gaps
|
||||
- [x] Missing-docs audit recorded (public items × 4 crates); gaps
|
||||
filled; the `#![deny(missing_docs)]` decision made and recorded
|
||||
- [ ] Core doc text spot-checked against core-contract.md; engine
|
||||
- [x] Core doc text spot-checked against core-contract.md; engine
|
||||
lib-doc posture claims spot-checked against deployment.md —
|
||||
drifts fixed or recorded
|
||||
- [ ] The doc-hidden fuzz re-exports documented as fuzz/test-only at
|
||||
- [x] The doc-hidden fuzz re-exports documented as fuzz/test-only at
|
||||
all three engine roots
|
||||
- [ ] The example decision made (landed or recorded not-taken)
|
||||
- [ ] Workspace gates green (`cargo build`, `cargo test`, clippy
|
||||
- [x] The example decision made (landed or recorded not-taken)
|
||||
- [x] Workspace gates green (`cargo build`, `cargo test`, clippy
|
||||
`-D warnings`, fmt)
|
||||
|
||||
## References
|
||||
@@ -77,8 +77,116 @@ polish.
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
- **`#![deny(missing_docs)]` decision**: **`#![warn(missing_docs)]`**
|
||||
adopted at the four published crate roots instead of deny — with
|
||||
rationale: the standing merge gate (`cargo clippy --all-targets --
|
||||
-D warnings`) promotes rustc lints to errors anyway, so the lint is
|
||||
*enforced* while plain `cargo build`/`cargo test` remain unblocked
|
||||
mid-task (a deny attribute fails every build the moment a pub item
|
||||
lacks text, punishing incremental work). Recorded as the standing
|
||||
posture in AGENTS.md's Verification Commands section alongside the
|
||||
doc-gate line.
|
||||
- **Doc gate posture recorded**: `RUSTDOCFLAGS="-D warnings" cargo
|
||||
doc --no-deps` — zero warnings, added to AGENTS.md's Verification
|
||||
Commands block as the doc gate.
|
||||
- **Missing-docs audit results** (full-surface probe via
|
||||
`RUSTFLAGS='-Wmissing_docs' RUSTDOCFLAGS='-Wmissing_docs' cargo doc
|
||||
--no-deps`, probe reports 0 after fixes): every public item across
|
||||
the four crates carries doc text. Gaps found and filled: the
|
||||
`Error` enum's own doc + its four struct-variant fields
|
||||
(`alkstore/src/error.rs`); the sqlite substrate's two fuzz-surface
|
||||
functions (`substrate/queue_ops.rs` — their source-side docs had
|
||||
never been written; the pg/mem scheduler twins already had them).
|
||||
- **Core doc fixes against core-contract.md** (drifts fixed): (1)
|
||||
`StreamHandle::trim_to`'s flat "trim emits no wake" corrected to the
|
||||
contract's exact row — no *dedicated wake and no notify*, with the
|
||||
SQLite `data_version` spurious-hint nuance (a trim commit does wake
|
||||
the watcher, contract-legal); (2) the ADR-023 §2 numeric-domain
|
||||
rules were missing from every affected method's doc text — added the
|
||||
extent rules (`claim_batch`/`read_since`/`read_from_consumer` and
|
||||
their tx twins: `n/limit <= 0` → empty value), duration rules
|
||||
(`try_lock`/`renew`: `ttl <= 0` rejects, opaque `Err`), the
|
||||
boundary-totality rule for `trim_to`'s horizon, and the shared
|
||||
domain-rule paragraph (store.rs module doc, per ADR-023 §2's own
|
||||
amendment text); (3) the `QueueOpts` "trusted as given" gloss drew
|
||||
the wrong criterion (opts-struct constants it names *are*
|
||||
trait-surface arguments) — reworded to the domain table's real
|
||||
boundary (enumeration by call-site kind); (4) store.rs's
|
||||
constructor prose gained the ADR-024 §4 `MemStore::new()` exception
|
||||
clause (amended into the contract 2026-10-10).
|
||||
- **Engine doc fixes**: (1) "all eleven `*_tx` methods" was an
|
||||
off-by-one — the trait carries ten `*_tx` ops plus `commit`;
|
||||
reworded in both `alkstore-sqlite/src/tx.rs` and
|
||||
`alkstore-postgres/src/tx.rs` module docs; (2) the SQLite
|
||||
`spawn_blocking` rationale misstated the driver property (rusqlite
|
||||
`Connection` *is* `Send`; the honest reason is that the substrate is
|
||||
sync machinery — std threads, blocking iterators) — reworded in
|
||||
`alkstore-sqlite/src/lib.rs` + `store.rs` per engine-sqlite.md; (3)
|
||||
the SQLite lib doc gained the missing third element of ADR-016 §3's
|
||||
mandated single-host prose — one-host cross-process *supported*
|
||||
(`data_version` is cross-process by nature), matching deployment.md;
|
||||
(4) cosmetic: a malformed doc-comment indent line on `PgOpts::max_size`.
|
||||
- **Spec-side alignments** (code docs + ADR win, spec aligned to
|
||||
match): `docs/architecture/engine-postgres.md` now lists `locks` in
|
||||
the schema-family enumeration and rewords "advisory-lock-semantics
|
||||
TTL locks" to the ADR-008 §7 row-backed realization;
|
||||
`docs/architecture/engine-mem.md` gained the one-clock-accessor line
|
||||
(no server clock to defer to) that the mem lib doc's wasm identity
|
||||
and `clock.rs` already cited — the claim now has its spec home.
|
||||
- **Recorded, not fixed — two boundary-wording drifts for
|
||||
`release-contract-window-close`** (the task that owns the class-1
|
||||
window's last dispositions): (1) `EventReceiver::try_recv`'s error
|
||||
arms (`Database` or `Closed`) are claimed in core doc text but not
|
||||
yet pinned in contract text — that task's §2 already decides
|
||||
`Err(Closed)` mirroring `WakeReceiver::try_recv` (mem-test-pinned),
|
||||
so the doc text is a forward-claim of the decided disposition, left
|
||||
as-is; (2) the notify payload boundary: ADR-004/core-contract say
|
||||
"≤ 8000 bytes" loosely, but the engine (POC + test-pinned against a
|
||||
live server) rejects a serialization of exactly 8000 bytes — the
|
||||
server's NUL-inclusive budget — so acceptance is ≤ 7999; the pg
|
||||
engine's docs state the truthful predicate; a normative-spec
|
||||
wording amendment is the window task's call.
|
||||
- **Example decision: landed** — the honest-ephemeral engine is the
|
||||
zero-config one, so `alkstore-mem`'s crate docs now carry the
|
||||
contract round-trip example (a compile-checked doctest:
|
||||
`MemStore::new()` → `queue` → `enqueue` → `claim_one` →
|
||||
`job().id`/`JobState` asserts), with a pointer that the other
|
||||
engines' round-trip is identical via their `open` form. The mem
|
||||
lib's architecture paragraph dropped its `crate::state`/`crate::wakes`
|
||||
intra-doc links (private modules — the private-intra-doc-link
|
||||
warning class) in favor of plain code spans.
|
||||
- **Fuzz re-exports (AC 4)**: verified all six `#[doc(hidden)]`
|
||||
re-exports at the three engine roots carry the four-part
|
||||
fuzz/test-only disclosure, name the correct module of origin, and
|
||||
match the re-exported function's semantics — no changes needed.
|
||||
|
||||
- Status of record: 2026-10-11, gates green (`cargo build`, `cargo
|
||||
test` incl. the new doctest, `cargo clippy --all-targets -- -D
|
||||
warnings`, `cargo fmt --check`, `RUSTDOCFLAGS="-D warnings" cargo
|
||||
doc --no-deps`, missing-docs probe 0).
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
The release doc pass over the four published crates: `RUSTDOCFLAGS="-D
|
||||
warnings" cargo doc --no-deps` is now the standing doc gate (recorded
|
||||
in AGENTS.md) and the workspace builds docs with zero warnings (8
|
||||
baseline rustdoc-warning classes fixed: broken `Error::*` links,
|
||||
redundant explicit links, bracket-escape escapes, and a
|
||||
private-intra-doc link). The `#![warn(missing_docs)]` enforcement
|
||||
posture landed on all four crate roots (clippy gate enforces);
|
||||
audit-probe reports zero undocumented public items after filling the
|
||||
`Error` enum/field docs and the sqlite substrate's fuzz-surface fn
|
||||
docs. Accuracy spot-checks ran core docs against core-contract.md and
|
||||
engine lib-doc posture claims against deployment.md/ADR-016/ADR-024:
|
||||
eight drifts fixed (trim_to wake nuance, the full ADR-023 §2
|
||||
numeric-domain doc text + shared paragraph, the QueueOpts gloss, the
|
||||
`MemStore::new()` constructor exception, the eleven-vs-ten tx-method
|
||||
count, the SQLite `Send`-misstatement rationale, the missing
|
||||
one-host-cross-process honesty element, a cosmetic indent), two
|
||||
spec-side alignments made (pg locks table family + row-backed
|
||||
wording; mem clock-accessor spec home), and two boundary-wording
|
||||
dispositions recorded for `release-contract-window-close`
|
||||
(`try_recv` arm pinning; the 8000-byte boundary's "≤" reading). The
|
||||
example decision landed: alkstore-mem's crate docs carry the
|
||||
zero-config queue round-trip doctest (green in the workspace test
|
||||
run). All workspace gates re-verified green.
|
||||
Reference in new issue
Block a user