Skip to content

Commit 0a4d1e9

Browse files
docs: defer crates.io distribution (ADR 0015)
crates.io is append-only — a first release locks the name/version permanently. Defer it; ship v0.1.0 via cargo install --git + prebuilt GitHub Release binaries (both reversible). Amends the distribution channel of ADR 0004; the head-on-competition strategy is unchanged. Syncs ADR index, DESIGN section 10, README install/status, docs/RELEASE.md, CLAUDE.md status, CHANGELOG. No code change; the v0.1.0 tag is unaffected.
1 parent 4729fb1 commit 0a4d1e9

7 files changed

Lines changed: 257 additions & 32 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
_No unreleased changes — v0.1.0 was just cut._
10+
### Changed
11+
12+
- Distribution: v0.1.0 ships via `cargo install --git` + prebuilt GitHub
13+
Release binaries instead of crates.io, to keep the first public release off
14+
crates.io's append-only commitment (ADR 0015 — amends the distribution
15+
channel of ADR 0004; the head-on-competition strategy is unchanged). No code
16+
change; the `v0.1.0` tag is unaffected. README / DESIGN.md §10 /
17+
docs/RELEASE.md install instructions updated accordingly.
1118

1219
## [0.1.0] — 2026-05-17
1320

CLAUDE.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,12 @@ These rules govern how Claude works on this repo. **They override Claude default
8484

8585
## How to add things
8686

87-
| Want to add... | Where / how |
88-
|---|---|
89-
| New scenario | [crates/mcp-loadtest/src/scenario/CLAUDE.md](crates/mcp-loadtest/src/scenario/CLAUDE.md) — or `/new-scenario <name>` |
87+
| Want to add... | Where / how |
88+
| ---------------- | -------------------------------------------------------------------------------------------------------------------- |
89+
| New scenario | [crates/mcp-loadtest/src/scenario/CLAUDE.md](crates/mcp-loadtest/src/scenario/CLAUDE.md) — or `/new-scenario <name>` |
9090
| New mock fixture | [crates/mcp-loadtest/tests/fixtures/CLAUDE.md](crates/mcp-loadtest/tests/fixtures/CLAUDE.md) — or `/new-mock <name>` |
91-
| New ADR | `docs/adr/NNNN-title.md` (next number); update `docs/adr/README.md` |
92-
| New CLI flag | `crates/mcp-loadtest-cli/src/main.rs` clap struct; thread through to lib via `Run` builder |
91+
| New ADR | `docs/adr/NNNN-title.md` (next number); update `docs/adr/README.md` |
92+
| New CLI flag | `crates/mcp-loadtest-cli/src/main.rs` clap struct; thread through to lib via `Run` builder |
9393

9494
## Don't
9595

@@ -106,5 +106,6 @@ Run `/release-checks` before tagging. See `.claude/commands/release-checks.md` f
106106

107107
## Status
108108

109-
- v0.0.0 — scaffolding (current)
110-
- v0.1.0 (target) — M1-M4 in DESIGN.md §10 complete, published to crates.io
109+
- v0.1.0 — tagged (`v0.1.0``a3ff6d4`, annotated, pushed); CI green (368 tests); M1–M7 + post-M7 shipped
110+
- Distribution: `cargo install --git` + GitHub Release binaries; crates.io **deferred** (ADR 0015, amends ADR 0004)
111+
- Pending gate before release: repo → public, then Gate A–D in [docs/RELEASE.md](docs/RELEASE.md)

DESIGN.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ CI matrix: ubuntu-latest, macos-latest, windows-latest × stable Rust × Python
403403

404404
Original 3-week plan replaced after discovering [reaatech/mcp-load-test](https://github.com/reaatech/mcp-load-test) ships v0.1 functionality already (see §10.5 for parity matrix). v0.1.0 of mcp-loadtest must reach feature parity _and_ surface our differentiators before re-publishing.
405405

406-
Repo was private through the M1-M7 development phase. The new public repo URL will be added once v0.1.0 is published to crates.io.
406+
Repo was private through the M1-M7 development phase. v0.1.0 ships from a **public** repo via `cargo install --git` + prebuilt GitHub Release binaries; the crates.io publish is deferred to keep the first release off append-only (ADR 0015 — amends the distribution channel of ADR 0004).
407407

408408
M1 through M7 are all shipped. Post-M7 work (spike scenario, HTML reporter, WebSocket transport, hot-path zero-copy refactor, criterion benches) is captured under `[Unreleased]` in CHANGELOG rather than as a new milestone — the work is small + cohesive enough that bundling it into v0.1.0 makes more sense than coining "M8" for it. The "Week N" column is dropped because milestones are no longer time-boxed — they're released.
409409

@@ -417,12 +417,12 @@ M1 through M7 are all shipped. Post-M7 work (spike scenario, HTML reporter, WebS
417417
| **M6**| Differentiators v1 | Real-time terminal TUI dashboard (live latency/throughput/RSS); server resource sampling beyond RSS (CPU, fd, threads); `race_detector` scenario; cross-server compare (`run --server srv-a --server srv-b`) |
418418
| **M7**| Differentiators v2 + v0.1 polish | Protocol fuzzer (basic — random/malformed payloads); coverage tracking (tools registered vs. exercised); per-tool SLO assertions; README rewrite with competitive positioning; `cargo install` smoke test on all 3 OS |
419419
| **Post-M7**| Pre-public-release close-out | Spike scenario; HTML reporter; WebSocket transport; hot-path zero-copy refactor; criterion benches (DESIGN §19 claims now reproducible). See CHANGELOG `[Unreleased]`. |
420-
| **v0.1.0-rc** | Pre-publish review in flight | repo back to **public**; crates.io publish; HN/lobste.rs/r/rust announce |
420+
| **v0.1.0-rc** | Pre-publish review in flight | repo back to **public**; `cargo install --git` + GitHub Release binaries (crates.io deferred — ADR 0015); HN/lobste.rs/r/rust announce |
421421
| _M8+ stretch_ | Beyond | AI-assisted pattern generator; distributed mode (multi-worker); replay/record; PyO3 binding |
422422

423423
**Definition of done for v0.1.0:**
424424

425-
- `cargo install mcp-loadtest-cli` works on Linux/macOS/Windows.
425+
- `cargo install --git <repo-url> mcp-loadtest-cli` works on Linux/macOS/Windows, and prebuilt binaries are attached to the GitHub Release (crates.io publish deferred — ADR 0015).
426426
- `mcp-loadtest deadlock-probe -s "python -m vibe_trading_mcp"` reproduces the original bug on commit `~PR-85`.
427427
- All §10.5 parity-must-have rows are checked.
428428
- All §10.5 differentiator rows are checked.

README.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,10 @@ Full GitHub Actions example: [docs/examples/ci-integration.md](https://github.co
7878
## Quick start
7979

8080
```bash
81-
# Install (after v0.1.0 publish)
82-
cargo install mcp-loadtest-cli
81+
# Install from the public repo (not on crates.io yet — see docs/adr/0015)
82+
cargo install --git https://github.com/Teerapat-Vatpitak/mcp-loadtest mcp-loadtest-cli
83+
# ...or download a prebuilt binary from the GitHub Release:
84+
# https://github.com/Teerapat-Vatpitak/mcp-loadtest/releases
8385
8486
# Quick deadlock smoke against a real MCP server
8587
mcp-loadtest deadlock-probe --server "python -m my_mcp" --tool foo
@@ -217,14 +219,17 @@ Three worked examples in [`docs/examples/`](https://github.com/Teerapat-Vatpitak
217219
## Install
218220
219221
```bash
220-
cargo install mcp-loadtest-cli
222+
# From the public repo (not on crates.io yet — see docs/adr/0015)
223+
cargo install --git https://github.com/Teerapat-Vatpitak/mcp-loadtest mcp-loadtest-cli
221224
```
222225
223-
Available on crates.io once v0.1.0 publishes.
226+
Or download a prebuilt binary for Linux/macOS/Windows from the
227+
[GitHub Release](https://github.com/Teerapat-Vatpitak/mcp-loadtest/releases).
228+
The crates.io publish is deferred to keep the first release off append-only ([ADR 0015](https://github.com/Teerapat-Vatpitak/mcp-loadtest/blob/main/docs/adr/0015-defer-crates-io-distribution.md)).
224229
225230
## Status
226231
227-
Pre-public release. M1–M7 implementation is in place; `pwsh scripts/ci-checks.ps1` is green on Windows (fmt, clippy, build, test, doc; `cargo-deny` skipped when absent) with 260+ tests. The killer demo (deadlock_probe catches the Vibe-Trading PR #85 bug on the unpatched commit) is in `crates/mcp-loadtest/tests/vibe_trading_regression.rs`. Repo is private during a security/code review pass; v0.1.0 tag + crates.io publish are the next steps.
232+
v0.1.0 is tagged (`v0.1.0`, annotated) and validated: the CI checks (fmt, clippy, build, test, doc) are green on Windows with **368 tests passing**, plus `cargo deny` / `cargo audit` clean. The killer demo (deadlock_probe catches the Vibe-Trading PR #85 bug on the unpatched commit) is in `crates/mcp-loadtest/tests/vibe_trading_regression.rs`. Distribution is via `cargo install --git` + prebuilt GitHub Release binaries; crates.io is deferred (ADR 0015). Flipping the repo to public is the remaining gate before the v0.1.0 release.
228233
229234
## Development
230235

docs/RELEASE.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# Release runbook — v0.1.0 (manual, operator-executed)
2+
3+
Distribution model is set by [ADR 0015](adr/0015-defer-crates-io-distribution.md):
4+
**no crates.io for v0.1.0** — ship via `cargo install --git` + prebuilt GitHub
5+
Release binaries. Every step here is **reversible** (re-privatise the repo, edit
6+
or delete a Release, move a tag) — the opposite of a crates.io publish.
7+
8+
Going public and creating a Release are **hard-confirm gates** (CLAUDE.md
9+
"Decision points"). Run these yourself in your own terminal.
10+
11+
This runbook is append-friendly: future versions add a new section.
12+
13+
---
14+
15+
## Precondition — already satisfied (do NOT redo)
16+
17+
Verified this cycle on `a3ff6d4` (= `v0.1.0^{commit}` = the annotated, pushed tag):
18+
19+
- `v0.1.0` annotated tag exists on origin → `a3ff6d4` (object `c0b583d8`).
20+
- CI green: `fmt` · `clippy -D warnings` · `build --locked` · **test 368 passed / 2 skipped** · `doc -D warnings`.
21+
- `cargo deny check` ok · `cargo audit` 0 vuln (3 allowed warns, ADR 0011).
22+
- `cargo package -p mcp-loadtest` packages + verifies (115 files).
23+
- Cargo.toml registry metadata complete (used by the deferred crates.io path; harmless now).
24+
- crates.io names `mcp-loadtest` / `mcp-loadtest-cli` free as of 2026-05-18 (only relevant if the Appendix is ever taken).
25+
26+
Optional quick re-confirm (safe, read-only):
27+
28+
```bash
29+
git fetch origin --tags
30+
test "$(git rev-parse 'v0.1.0^{commit}')" = "$(git rev-parse HEAD || true)" && echo "HEAD == tag" || echo "HEAD != tag (build the release from the tag)"
31+
cargo package -p mcp-loadtest --locked # must succeed
32+
```
33+
34+
---
35+
36+
## Gate A — make the repo public _(hard-confirm; reversible)_
37+
38+
Both distribution channels need a public repo. `Cargo.toml`
39+
`repository`/`homepage` already point at the canonical URL.
40+
41+
- [ ] Repo is **public** at `https://github.com/Teerapat-Vatpitak/mcp-loadtest`.
42+
- [ ] `v0.1.0` tag is on origin: `git ls-remote --tags origin v0.1.0` → prints `…refs/tags/v0.1.0`.
43+
- [ ] Push the post-tag docs commits so the public `main` matches reality:
44+
`git push origin main` (carries the v0.2 backlog + the ADR 0015 doc set;
45+
the `v0.1.0` tag does **not** include them — intentional).
46+
- [ ] Smoke the first channel immediately:
47+
`cargo install --git https://github.com/Teerapat-Vatpitak/mcp-loadtest mcp-loadtest-cli`
48+
then `mcp-loadtest --version && mcp-loadtest doctor`.
49+
50+
Reversibility: the repo can be set private again; nothing is permanent here.
51+
52+
## Gate B — prebuilt binaries via cargo-dist _(hard-confirm; reversible)_
53+
54+
```bash
55+
cargo install cargo-dist # or: cargo binstall cargo-dist
56+
dist init # interactive: pick Linux/macOS/Windows targets,
57+
# writes [workspace.metadata.dist] + .github/workflows/release.yml
58+
git add Cargo.toml dist-workspace.toml .github/workflows/release.yml
59+
git commit -m "build(dist): add cargo-dist release workflow"
60+
git push origin main
61+
```
62+
63+
Then produce the binaries for the existing `v0.1.0` tag. The generated workflow
64+
triggers on a tag push; since `v0.1.0` is already pushed, pick one:
65+
66+
- **Build + attach locally** (no tag churn): `dist build` then attach the
67+
artifacts when you create the Release in Gate C (`gh release create … <files>`).
68+
- **Or** re-run the release workflow via `workflow_dispatch` for the `v0.1.0`
69+
tag from the Actions tab.
70+
71+
Do **not** delete-and-repush the `v0.1.0` tag to re-trigger CI — moving a pushed
72+
tag is destructive and needs explicit sign-off.
73+
74+
- [ ] Binaries built for Linux + macOS + Windows.
75+
76+
## Gate C — GitHub Release _(hard-confirm; tag already pushed; reversible)_
77+
78+
```bash
79+
sed -n '/## \[0\.1\.0\]/,/^\[Unreleased\]:/p' CHANGELOG.md | sed '$d' > /tmp/relnotes-0.1.0.md
80+
gh release create v0.1.0 --verify-tag --title "v0.1.0" \
81+
--notes-file /tmp/relnotes-0.1.0.md \
82+
./target/distrib/* # the cargo-dist artifacts (path per `dist build` output)
83+
```
84+
85+
(Short body alternative: `--notes "First public release. Install: cargo install --git … . See CHANGELOG.md §[0.1.0]."`)
86+
87+
- [ ] Release shows the binaries + source archives.
88+
- [ ] A Release can be edited or deleted later — nothing append-only here.
89+
90+
## Gate D — announce _(manual)_
91+
92+
- [ ] HN / lobste.rs / r/rust per DESIGN.md §10. Lead with the deadlock demo;
93+
install line is the `cargo install --git` one (not `cargo install`).
94+
95+
---
96+
97+
## Caveats & rollback
98+
99+
- **Everything here is reversible** — the whole reason for ADR 0015. Repo →
100+
private again; Release → edit/delete; binaries → re-upload; tag → (with
101+
sign-off) move. Contrast crates.io: a version there is permanent.
102+
- **Post-tag commits** (v0.2 backlog `4729fb1`, the ADR 0015 doc set) are _not_
103+
in `v0.1.0`. Intentional — planning/distribution docs must not mutate a
104+
shipped tag. The tag is the code; `main` is where the docs move forward.
105+
- If a Bash/permission rule blocks a step inside Claude, run it in your own
106+
terminal; nothing about this release requires it to run inside Claude.
107+
108+
## Appendix — crates.io (deferred, ADR 0015)
109+
110+
Recorded for the future revisit (trigger: 0.x API stabilises, or a real external
111+
library consumer asks). Append-only — only do this when the name/API commitment
112+
is acceptable.
113+
114+
```bash
115+
# scoped token (publish-new + publish-update) at https://crates.io/settings/tokens
116+
cargo login
117+
git switch --detach v0.1.0 # publish from the exact tagged commit
118+
cargo publish -p mcp-loadtest --locked
119+
# wait ~1–2 min for the index
120+
cargo publish -p mcp-loadtest-cli --locked
121+
git switch main
122+
```
123+
124+
When this is taken, update README/DESIGN install lines back to plain
125+
`cargo install mcp-loadtest-cli` and write the follow-up ADR that closes ADR
126+
0015's revisit question.
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# 15. Defer crates.io; distribute v0.1.0 via git-install + GitHub Release binaries
2+
3+
Date: 2026-05-18
4+
Status: Accepted
5+
6+
Amends [0004](0004-compete-with-reaatech.md): the head-on-competition strategy
7+
stands; only the v0.1.0 distribution **channel and timing** change. Supersedes
8+
the "published to crates.io" clause of DESIGN.md §10 "Definition of done".
9+
10+
## Context
11+
12+
v0.1.0 is code-complete and tagged (`v0.1.0``a3ff6d4`, annotated, pushed; CI
13+
green, names free, packaging verified). The original Definition of Done assumed a
14+
crates.io publish.
15+
16+
crates.io is **append-only and irreversible**:
17+
18+
- A published version can never be deleted — only `yank`ed (hidden from new
19+
resolves; existing `Cargo.lock`s still pull it).
20+
- The crate name is claimed permanently on first publish.
21+
- The first publish must be correct because it cannot be retracted.
22+
23+
For a first public release we are not ready to accept that permanent commitment —
24+
the public API surface may still move in 0.x, and the name/version is forever once
25+
taken. We still want ADR 0004's public-feedback loop reopened _now_, just without
26+
the irreversible lock-in.
27+
28+
## Decision
29+
30+
Defer crates.io. Ship v0.1.0 through two reversible channels:
31+
32+
1. **`cargo install --git`**
33+
`cargo install --git https://github.com/Teerapat-Vatpitak/mcp-loadtest mcp-loadtest-cli`.
34+
Needs only a public repo; nothing to maintain server-side.
35+
2. **Prebuilt binaries on the GitHub Release**`cargo-dist`-generated artifacts
36+
for Linux/macOS/Windows attached to the `v0.1.0` release, for users without a
37+
Rust toolchain.
38+
39+
The repo goes **public** (reversible — it can be re-privatised; a Release/tag can
40+
be edited or removed). crates.io stays a recorded future option.
41+
42+
## Alternatives considered
43+
44+
| Option | Why rejected (for v0.1.0) |
45+
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- |
46+
| **Publish to crates.io now** | Append-only/irreversible; premature while the 0.x API and the permanent name commitment are not something we want to lock today. |
47+
| **Private / from-source only** | Kills the public-feedback loop ADR 0004 explicitly wants reopened; contradicts the head-on-competition goal. |
48+
| **git-install only** | No path for users without a Rust toolchain. |
49+
| **binaries only** | No `cargo install`; library consumers have no dependency path at all. |
50+
51+
Both channels together cover the dev (Rust) and toolchain-free audiences while
52+
staying fully reversible.
53+
54+
## Consequences
55+
56+
**Positive**
57+
58+
- Fully reversible: no permanent name/version/API commitment; a bad release can be
59+
edited or deleted, unlike a crates.io version.
60+
- ADR 0004's public-feedback loop reopens immediately, sooner than a
61+
publish-perfect crates.io gate would allow.
62+
- Library is still consumable by other Rust code via a git dependency.
63+
64+
**Negative**
65+
66+
- No `cargo add mcp-loadtest` for downstream crates — a git dependency is
67+
second-class (no semver resolution from a registry, no docs.rs).
68+
- Not discoverable through crates.io search — weaker than reaatech's npm presence.
69+
- `--git` installs build from source (slower than a registry binary).
70+
- The `cargo-dist` release workflow must be set up and maintained.
71+
72+
**Mitigations**
73+
74+
- crates.io remains open via a future ADR once the 0.x API stabilises or a real
75+
external library consumer asks for it.
76+
- docs.rs gap covered by README + `docs/` + local `cargo doc`.
77+
78+
## Open questions
79+
80+
- **Revisit trigger for crates.io.** Proposed: after the 0.x API stabilises _or_
81+
the first external library consumer requests a registry dependency.
82+
- **Name-squatting risk.** Deferring leaves `mcp-loadtest` / `mcp-loadtest-cli`
83+
unclaimed (verified free 2026-05-18). Reserving them needs a placeholder
84+
publish — itself an append-only act, i.e. the exact commitment this ADR avoids.
85+
Default: **do not** reserve; accept the squat risk until the real publish.

0 commit comments

Comments
 (0)