141 lines
7.2 KiB
Markdown
141 lines
7.2 KiB
Markdown
---
|
|
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<dyn Store>`-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<Result<Box<dyn Store>>>;
|
|
teardown() -> BoxedFuture<Result<()>> }` — 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. |