--- id: contract-suite-scaffold name: Contract-suite crate scaffold + ADR-022 (suite layout decision) status: completed depends_on: [core-trait-surface] scope: narrow risk: low impact: phase level: implementation tags: [wave-1, contract-suite] --- ## Description Scaffold the contract suite as a fourth, internal, unpublished crate — `alkstore-contract-suite` — per the layout decision recorded in docs/plans/implementation.md (option (a)): property tests parameterized over a `Store` factory, consumed by each engine crate as a dev-dependency. This discharges ADR-017 §4.2's "layout decided at implementation" deferral; the decision gets its own ADR (ADR-022) since it shapes the pairing instrument ADR-017 §4 names. The crate: - Workspace member, `publish = false`, depends on `alkstore` only. - Exposes a `StoreFactory` trait (or function alias — implementer's choice, but one shape, documented): something that yields a fresh `Box`-shaped store over an isolated backing store (fresh file / fresh schema), plus teardown. The exact factory shape may evolve when engines adopt it (wave 3/4 feedback) — the ADR records the layout decision, not a frozen API. - A `tests/` harness that runs the property set against a supplied factory, structured so each backlog row is one named, independently runnable property with a version-stamp slot (ADR-017 §4.2: each row version-stamped with the contract change that added it — a doc-comment or attribute convention, decided here and used by every later row). - One exemplar property implemented end-to-end to prove the harness works — the reserved-name/empty-name validation property (pure contract, engine-independent, runnable against any factory): every name-bearing entry point rejects empty with `InvalidName` and reserved-prefix with `ReservedName`, on auto-commit and tx paths alike. This row is version-stamped `ADR-008 §4`. - Engine crates get the dev-dependency edge now (empty suite compiles against them trivially once they have code; the edge existing early means wave 3/4 tasks adopt the harness rather than debate it). ## Acceptance Criteria - [ ] `alkstore-contract-suite` crate exists, `publish = false`, depends on core only, compiles in the workspace - [ ] Factory abstraction defined and documented; exemplar property (name validation) green against a trivial in-crate mock store - [ ] Version-stamp convention established and documented in the crate (used by the exemplar row) - [ ] ADR-022 written (`docs/architecture/decisions/022-contract-suite-layout.md`): option (a) chosen, rationale (one normative owner per property, ADR-012 §2 mirror), the factory-shape latitude stated, ADR-017 §4.2 deferral discharged; ADR index tables in docs/architecture/README.md and overview.md updated - [ ] Engine crate manifests carry the dev-dependency edge - [ ] `cargo test` workspace-wide, clippy `-D warnings`, fmt clean ## References - docs/architecture/decisions/017-contract-versioning.md §4.2 - docs/plans/implementation.md (Decided points) - docs/architecture/core-contract.md §Verification backlog ## Notes - **Factory shape (the implementer's choice)**: a named trait, `StoreFactory { open() -> BoxedFuture>>; teardown() -> BoxedFuture> }` — chosen over a function alias because teardown needs an identity to be idempotent *per instance* (a bare fn can't carry "this factory's backing store"), and the async surface rides core's `BoxedFuture` posture so implementers need no macros. One shape, documented in `src/factory.rs`; the ADR records the layout, not a frozen API — wave 3/4 feedback may evolve it. - **Version-stamp convention (the "decided here" row convention)**: a row's doc comment carries one `Contract stamp:` item per contract change pinning the behavior tested, citing ADR § (never bare "v1" or dates); stamps *accumulate* on amendment; greppable via the exported `STAMP_MARKER` constant. Normative statement lives in `src/version_stamp.rs` — every later row cites against it. The exemplar row is stamped `ADR-008 §4` + `ADR-021 §3` + `ADR-015 §2` (its assertions span all three changes' behavior). - **Assertion posture**: rows panic on violation (labeled with the entry point and expectation) — the test-side-artifact relaxation of the family no-panic rule, documented in the crate docs as deliberate. - **Exemplar scope**: the property runs the full validation surface — all four `Store` constructor kinds (`listen`/`stream`/`queue`/ `outbox`), `try_lock` (both name and owner), `schedule` (both name and queue argument — ADR-021 §3), `notify`, and *every* name-bearing `*_tx` twin on `TxHandle` (all eleven, incl. ADR-015 §2's non-empty-key rule on `publish_with_key_tx`), plus a `with_tx` spot-check proving the commit-atomic path can't bypass validation. Happy-path spot-checks (`stream`/`queue`/`try_lock` with a reserved-prefixed owner) prove the rejections are the validation's doing, not store-wide failure. - **`Schedule` is `#[non_exhaustive]`** (ADR-017 §3): the mock cannot struct-construct it, so the mock's `schedule()` returns a `Database` stub after running the row-validation; the row asserts only the validation outcomes, and `Schedule` construction stays core's own tests' business. Same for the mock's `subscribe` (never exercised by the row). Noted so wave 3/4 factory impls copy the posture knowingly. - **serde_json is a direct dep of the suite** (not just core's): rows construct payload `Value`s as contract surface (ADR-020 §4's trait crossing). No driver deps — the ADR-001 §2 mirror holds. - **Dev-dep edges are path deps** (`path = "../alkstore-contract-suite"`) with no version: correct for an unpublished internal crate — a `version` key would be meaningless. - ADR-017 §4.2's text gained a layout-resolution annotation (dated, citing ADR-022) at the deferral site — the discharge is visible from the deferring document itself. ## Summary The `alkstore-contract-suite` crate is scaffolded per the option-(a) layout decision: `publish = false`, workspace member, depends on `alkstore` only (+ serde_json for payload construction). It defines the `StoreFactory` trait (open/teardown over an isolated backing store), the version-stamp convention (`Contract stamp:` doc-comment items, `STAMP_MARKER` constant, normative statement in version_stamp.rs), and the exemplar property `name_validation_rejects_empty_and_reserved` — full auto-commit + tx validation surface, stamped ADR-008 §4 / ADR-021 §3 / ADR-015 §2. A `tests/suite_harness.rs` runs it green against a trivial in-crate mock store + factory (3 tests: the exemplar row, happy-path construction, stamp-marker check). Engine crates carry the dev-dependency edge (path-dep). ADR-022 written (docs/architecture/decisions/022-contract-suite-layout.md): option (a) chosen, one-normative-owner-per-property rationale (ADR-012 §2 mirror), factory-shape latitude stated, ADR-017 §4.2 deferral discharged (with an annotation added at the deferral site); ADR index tables in docs/architecture/README.md and overview.md updated. Workspace `cargo build`, `cargo test` (26 tests workspace wide), `cargo clippy --all-targets -- -D warnings`, `cargo fmt --check` all clean.