--- id: release-publish-prep name: Publish prep — package verification, exclusion check, publish records status: completed depends_on: [release-readme, release-flake-investigation] scope: moderate risk: medium impact: project level: implementation tags: [wave-8, publish] --- ## Description The plan's wave-8 "publish prep": everything short of the manual `cargo publish` itself (CI and publishing are manual per the plan's decided points — self-hosted Gitea, supply-chain posture; the actual publish is the user's action, post-gate). 1. **Package metadata audit**: the four published crates currently carry only version/edition/license/repository — `description` is missing on all four (crates.io requires it). Land honest descriptions per crate; decide the remaining optional fields (`keywords`, `categories`, `homepage`/`docs` — the family's published crates are the precedent to check) and record the decisions. 2. **`cargo package` verification per published crate** (`alkstore`, `alkstore-sqlite`, `alkstore-postgres`, `alkstore-mem`): each packages clean (no warnings that block, `.crate` contents inspected), builds + tests from the packaged source (`cargo package --no-verify` off — let it verify, or verify manually per crate and record which). 3. **The publish-exclusion verification** (parked from wave 7 — `tasks/review-wave-7.md` item 4(iii)): the mechanism (`exclude = ["fuzz"]` on the root workspace) landed; this is the verification that **no published package leaks the sidecar** — inspect each `.crate`'s file list: no `fuzz/` paths, no `alkstore-fuzz-shared` references, no grown corpora/artifacts. Also verify `alkstore-contract-suite` (`publish = false`) and the fuzz sidecar are absent from any package. 4. **Path-dep + version records**: the engines' `alkstore = { version = "0.1", path = … }` shape is publish-legal (version bound present) — verify each path dep carries its version bound; record the publish order (core first, then engines — ADR-017 §4.1's pairing) and the version decision (0.1.0 as contract v1's initial release, per ADR-017's semver-is-the-contract discipline) in the task Notes as the publish checklist of record. 5. **Substrate packaging check** (ADR-013/018): `alkstore-sqlite`'s package includes the in-tree substrate — verify `PROVENANCE.md` and the dual-license notice ride in the `.crate`, and the package is honest about carrying them. 6. **The publish checklist of record**: a Notes section listing the exact manual commands in order (per crate: package → publish), including the dry-run evidence, so the user's publish session is mechanical. ## Acceptance Criteria - [x] `description` landed on all four published crates; optional metadata decisions recorded - [x] `cargo package` clean per published crate (verification posture recorded); `.crate` contents inspected - [x] Publish-exclusion verification recorded: no fuzz/sidecar/ contract-suite paths in any package's file list - [x] Path-dep version bounds verified; publish order + version decision recorded - [x] Substrate PROVENANCE + license notice confirmed in the `alkstore-sqlite` package - [x] The manual publish checklist of record written (commands in order, evidence attached) - [x] Workspace gates + the fuzz corpus-replay gate green at session end ## References - docs/plans/implementation.md §Wave 8, §Decided points (CI/publishing manual) - tasks/review-wave-7.md (the parked exclusion verification, item 4) - docs/architecture/decisions/017-contract-versioning.md §4.1 (the version pin + pairing) - docs/architecture/decisions/013-fold-substrate-into-sqlite.md + docs/architecture/decisions/018-provenance-register-and-cherry-picks.md (the in-tree substrate's packaging posture) - docs/architecture/decisions/022-contract-suite-layout.md (the suite is not published) ## Notes ### Decisions of record 1. **Release version — 0.1.0, by user decision at this task (2026-10-11).** This contradicted ADR-017 §1's "the initial release is 1.0.0" pin, so a **pre-release class-1 amendment** was recorded inline in ADR-017 §1 (the window this release closes): contract v1's content ships whole in 0.1.0; the §4.1 manifest pins take the ADR's own pre-1.0 `0.y` form (`alkstore = { version = "0.1", path = … }`); breaking changes bump `0.y`, additions `0.y.z`; the full coupling binds at the future 1.0. Motive recorded: the family's 0.x-initial precedent (alkcall 0.8.1, alktunnels 0.2.1, alksocks 0.1.0, alkblobs 0.1.0). This task's description itself assumed "0.1.0" — the CHANGELOG (release-readme) had seeded it, but the ADR pin was never dispositioned; the discrepancy was raised with the user rather than landing silently in either direction. 2. **Optional metadata — `homepage` / `documentation` omitted.** The family precedent (alkcall/alktunnels/alksocks/alktty/alkblobs) carries neither field on any published crate; docs.rs links are automatic on crates.io and `repository` is the de-facto homepage. `keywords` + `categories` landed per crate (≤5 keywords, lowercase; valid crates.io category slugs). Descriptions follow the family's colon-form one-liners. 3. **License texts ride the packages — per-crate `LICENSE-MIT` / `LICENSE-APACHE` copies.** The texts live at the workspace root, and cargo (1.99) does **not** package workspace-root license files into member `.crate`s: workspace-inherited `license-files` was tried first and proved a packaging no-op (verified in a sandbox — the field is accepted and inherited, but cargo's rewrite drops it and the tarball carries no texts), so the field was reverted and the canonical texts copied into the four published crate dirs, where cargo's auto-detect (`LICENSE*` in the package dir) packages them. The sqlite substrate's separate in-src bundled notice (`src/substrate/LICENSE`) is unaffected — ADR-018's carrier. 4. **Verification posture.** `cargo package` verification (registry resolution + verify build) is core-first by construction: for the engines, the packaged manifest drops the path dep, so packaging/verification must resolve `alkstore = "0.1"` from the index — which fails pre-core-publish with `no matching package named 'alkstore' found … searched: crates.io index` (captured twice: `cargo package -p alkstore-postgres` and `cargo publish --dry-run -p alkstore-mem`). Posture: **core got the full cargo-native path** (`cargo package` clean, verification build green, `cargo publish --dry-run` green to the "aborting upload due to dry run" line); **engines got reconstructed-tree verification**: per engine, the exact `cargo package --list` file set + a hand-normalized publish-shaped manifest (the only semantic deltas from the workspace manifest: the workspace-inheritance concretization and the core dep's registry edge restored as its publish-legal `version = "0.1", path = "../alkstore"` form — the substitution; the suite stays a path-only dev-dep in the published shape, which consumers never fetch) → `cargo build` + `cargo test` green in each. The engines' registry-side verification happens live, in order, at the publish session (checklist below). ### Field audit (the evidence) - `cargo package --list` per crate saved at the time of the run; the core `.crate` (the real cargo artifact, `target/package/alkstore-0.1.0.crate`, 30 files) inspected: license texts at the tarball root, README, the normalized `Cargo.toml` (concrete fields, path dep dropped), no stray paths. - **Exclusion verification (review-wave-7 item 4(iii)) — clean.** Each package's file list diffed against its directory contents (identical mod sort order): **no `fuzz/` paths, no `alkstore-fuzz-shared` references, no grown corpora/artifacts in any package**; the suite (`publish = false`) appears in no `[dependencies]` — engines carry it only as a path-only dev-dep, and it is not a package itself. The workspace `exclude = ["fuzz"]` holds (fuzz is a separate, excluded workspace). The engines' `src/fuzz_surface_tests.rs` files DO ride their packages — expected: they are the documented doc-hidden re-export probe tests *inside the crate* (release-crate-docs), not sidecar leakage. - **`alkstore-sqlite` substrate — both carriers confirmed in the package**: `src/substrate/PROVENANCE.md` (the full register with fork-point statement and delta entries) and `src/substrate/LICENSE` (the dual MIT+Apache text + fork-point + modification statements); `substrate/mod.rs`'s module doc names both, and the crate README + root README point at the notice — the package is honest about carrying them (ADR-013 §3 / ADR-018 §1). - **Path-dep version bounds verified**: `alkstore = { version = "0.1", path = "../alkstore" }` on all three engines (the §4.1 carrier 1 manifest pin, pre-1.0 `0.y` form) and on the suite's dep; no versioned path dep missing its bound. - Packaged-source test evidence (all green, 2026-10-11): core `.crate` extracted → 25 + 0 doc-tests; sqlite tree 193 + 25 suite rows + 0; mem tree 102 + 25 + 1 doctest; postgres tree 127 + 25 + 9 + 0 (postgres rows skip-green without a server env, the same posture as the workspace test run). ### The manual publish checklist of record (the user's publish session) Run from the workspace root; gates are assumed green (this task's evidence; re-run if any later commit touched code): ``` cargo build cargo test cargo test --manifest-path fuzz/shared/Cargo.toml cargo clippy --all-targets -- -D warnings cargo fmt --check RUSTDOCFLAGS="-D warnings" cargo doc --no-deps ``` ``` # 1. Core first — the engines' packaged manifests resolve `alkstore # = "0.1"` from the index; until this lands, cargo package/publish # for the engines is impossible (the error this task captured). cargo package -p alkstore && tar tf target/package/alkstore-0.1.0.crate # eyeball: LICENSE-MIT/LICENSE-APACHE at the tarball root, README, # no stray paths (this task's evidence: 30 files, all clean). cargo publish -p alkstore # wait for index availability (a minute or two; # `cargo search alkstore` resolving is the readiness probe). # 2. Engines — any order among them (mutually independent); each now # packages + verifies fully against the live core: cargo package -p alkstore-sqlite tar tf target/package/alkstore-sqlite-0.1.0.crate | grep -E 'LICENSE|substrate' # eyeball: LICENSE-MIT/LICENSE-APACHE at the tarball root AND # src/substrate/{PROVENANCE.md,LICENSE} riding in-tree. cargo publish -p alkstore-sqlite cargo package -p alkstore-postgres && cargo publish -p alkstore-postgres cargo package -p alkstore-mem && cargo publish -p alkstore-mem # 3. Tag + forge release: v0.1.0 (the CHANGELOG's tag link points here). ``` ## Summary Landed (2026-10-11): ADR-017 §1's pre-release class-1 amendment (initial release 0.1.0, user decision — family 0.x-initial precedent, §4.1 pins stay the pre-1.0 `0.y` form); package metadata on the four published crates (colon-form descriptions, keywords ≤5, categories; homepage/ documentation omitted per family precedent); per-crate LICENSE-MIT + LICENSE-APACHE copies (workspace-inherited `license-files` proved a cargo-1.99 packaging no-op in a sandbox, reverted — the copies are what cargo's auto-detect packages). Package verification: core full cargo-native (package + verify build + `publish --dry-run` clean, 30 files); engines blocked pre-core-publish by construction (captured error) — verified from reconstructed exact-`--list` trees with the publish-shaped manifests (core dep as its publish-legal version+path form), all green (sqlite 193+25, mem 102+25+1, postgres 127+25+9). Publish-exclusion verification clean: no fuzz/sidecar/corpora/ suite paths in any package's file list (engines' in-src `fuzz_surface_tests.rs` are the documented crate-internal probe tests, not leakage); sqlite's package carries `src/substrate/PROVENANCE.md` + the dual-license `src/substrate/LICENSE`, documented in `mod.rs` and the readmes. Path-dep bounds verified on all engines + suite; the manual publish checklist of record (core → engines, per-crate package → eyeball → publish, plus the gates) written into Notes. Gates at session end: build, full `cargo test` (15 result blocks green), clippy `--all-targets -- -D warnings`, fmt --check, doc gate (`RUSTDOCFLAGS="-D warnings"`), fuzz corpus replay — all green.