fix(release): give each crate package rerun its own root and keep the token out of it

Follow-up to 8b92463a2c, fixing three review findings on that commit (the
fourth, its Refs line, was already corrected to CW-7445).

Unique rerun root. The refusal and the runbook named the rerun root
release-verify-<sha10> only. The documented flow runs a receipt dry run,
then publish, then possibly partial-publish reruns, all at the same frozen
SHA, so the printed mkdir failed with "File exists" on the very next use:
/Volumes/VIXinSSD/cw-build-cache/release-verify-14683c9983 already exists
from the 0.10.2 receipt, and 14683c9983 is the tag source. The root is now
release-verify-<sha10>-<mode>-<UTC %Y%m%dT%H%M%SZ> in publish-crates.sh and
in both runbook blocks. The refusal and the runbook say that publish and
each partial-publish rerun get their own root.

Credentials. The runbook and refusal told the operator to run cargo login
into the per-run CARGO_HOME. That leaves a plaintext token in
<cache root>/release-verify-*/cargo-home/credentials.toml after every
publish, in a tree agents list and prune. Both now pass the token as
CARGO_REGISTRY_TOKEN for the one publish command. Cargo's config reference
names it as the crates.io token, and crates-io is the default registry
name the script's --registry flag uses. Cargo writes no file for it. After a
cargo login there instead, run cargo logout with that CARGO_HOME. Step 5 of
the Rust crates release is now the full isolated block (read -rs token, a
new root, CARGO_REGISTRY_TOKEN, unset token). The workstation note
(~:340), the crates.io gate's recovery step (~:602-603, which still said to
run cargo login and then publish on the default home, which the guard
refuses on this Mac) and the partial-publish bullet now use that block.
docs/RELEASE_CHECKLIST.md section 6 also said to run plain
publish-crates.sh publish. It now points at the block, so the two release
docs agree. web/data/agent-docs.generated.json was regenerated with npm
run prebuild because check:web flagged it as stale.

Fallbacks and scope. The runbook block used ${CODEWHALE_CACHE_ROOT} with no
default. When it was unset, the root became /release-verify-<sha> at the
filesystem root. The block now sets cache and lock with the same fallbacks
dev-cache.sh and the script use:
${XDG_CACHE_HOME:-$HOME/.cache}/codewhale and ${cache}/build.lock. The
runbook and the refusal say that CODEWHALE_CACHE_ROOT and
CODEWHALE_BUILD_LOCK_FILE take effect only when cargo on PATH is
scripts/dev-cargo.sh or a wrapper that execs it, such as the strict
dry-run wrapper the 0.10.2 receipt used. Plain Cargo ignores both and takes
no machine lock. With plain Cargo, the runbook says to use CARGO_TARGET_DIR,
plus scripts/build-lock.py when other builds may run.

Verification (this Mac, HEAD 8b92463a2c plus this diff):
- Real state, through the no-build shim (exits 99 on cargo
  publish/package/build/check/...), then the strict wrapper, then
  dev-cargo.sh: dry-run exit 1 in about 4 s. It listed 28 extractions and
  145 compiled units across 28 crates. It printed root
  release-verify-8b92463a2c-dry-run-20261016T154914Z. Publish mode also
  refused, printing release-verify-8b92463a2c-publish-20261016T154936Z,
  before the tag guard. The shim was never reached. The extraction listing,
  the deps entry count (181348) and the codewhale-state eb1a8b21e14a9d0b
  rlib did not change. The printed roots were not created.
- Isolated: the runbook preflight block, extracted from the doc and run
  as written, with CODEWHALE_CACHE_ROOT and CODEWHALE_BUILD_LOCK_FILE
  unset, XDG_CACHE_HOME in scratch, CARGO_NET_OFFLINE=true and the shim.
  It ran once under bash and once under zsh. Both resolved the fallback
  cache. Each got its own root (...T155014Z and ...T155016Z), and mkdir
  succeeded both times. build.lock was created at the fallback
  ${cache}/build.lock, not inside the root. The guard passed, the script
  reached "Verifying all 29 release package tarballs" and the shim stopped
  cargo publish (exit 99). Each root holds only an empty build/.
- bash -n and zsh -n pass on both runbook blocks. bash -n passes on the
  script. shellcheck shows only the 3 findings HEAD already has.
- python3 scripts/release/publish-crates.test.py (under build-lock.py):
  "Ran 6 tests in 2.791s OK"; ~/.cargo/registry/src unchanged.
- bash scripts/release/validate-crate-publish-order.test.sh: passed.
  node .github/scripts/release-workflows.test.js: exit 0.
- cargo fmt --all -- --check (dev-cargo.sh): exit 0. No Rust changed, so
  there is no clippy or nextest target.
- npm test: root 101/101, runtime-sdk 19/19, extension-host 420 (413 pass,
  7 skipped), web node 7/7. npm run check:web: exit 0 after the prebuild
  regeneration, vitest 772/772 (74 files).

Not verified: CARGO_REGISTRY_TOKEN with a real token (no token was used;
the claim rests on Cargo's bundled config reference), the step 5 publish
block end to end (it needs the tag, network and a token), cargo logout in
a per-run CARGO_HOME, a full dry run in a new root (a cold build of about
19 GB) and GNU grep in CI/CNB. Not mirrored to GitHub. Not a CHANGELOG
item (maintainer release tooling).

Refs CW-7445

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NBKbhx19BRbZtvWK8rVKjz
This commit is contained in:
CodeWhale BotandClaude Opus 5.5 committed 2026-10-16 08:52:33 -07:00
1 parent 8b92463a2c
commit 9ded7ce896
4 files changed
+71 -38

No files matched your search

+7 -5
View File
@@ -212,11 +212,13 @@ known limitations and publication approval. These proofs are separate.
switch release lanes or force-fetch a conflicting tag. If `main` advanced,
coordinate with Hunter for a new candidate or separately authorized
recovery plan.
- [ ] Publish Cargo manually within the approved scope:
`./scripts/release/publish-crates.sh publish`. It verifies every package,
skips already-published versions and waits for each new dependency to
appear. npm normally uses Trusted Publishing; manual recovery keeps the
same clean-source and public-asset guards.
- [ ] Publish Cargo manually within the approved scope with the
[runbook's step 5 block](RELEASE_RUNBOOK.md#rust-crates-release), which
runs `./scripts/release/publish-crates.sh publish` from a new, empty
`CARGO_HOME` and cache root with the token in `CARGO_REGISTRY_TOKEN`. It
verifies every package, skips already-published versions and waits for
each new dependency to appear. npm normally uses Trusted Publishing;
manual recovery keeps the same clean-source and public-asset guards.
## 7. Verify public results
+58 -28
View File
@@ -146,27 +146,39 @@ crate's `<crate>-<version>` directory under any `$CARGO_HOME/registry/src/*`
other than crates.io's `index.crates.io-*`, or a dep-info file under the build
directory's `*/deps/` that names such a directory. The refusal lists what it
found and deletes nothing; removing shared cache directories still needs
explicit cleanup approval. It prints, with the paths filled in, the rerun that
produced the 0.10.2 receipt at 14683c9983 on 2026-10-16: a new, empty
`CARGO_HOME` and `CODEWHALE_CACHE_ROOT`.
explicit cleanup approval. It prints, with the paths filled in, the kind of
rerun that produced the 0.10.2 receipt at 14683c9983 on 2026-10-16: a new,
empty `CARGO_HOME` and `CODEWHALE_CACHE_ROOT`.
```bash
lock="${CODEWHALE_CACHE_ROOT}/build.lock"
root="${CODEWHALE_CACHE_ROOT}/release-verify-$(git rev-parse --short=10 HEAD)"
mode=dry-run
cache="${CODEWHALE_CACHE_ROOT:-${XDG_CACHE_HOME:-$HOME/.cache}/codewhale}"
lock="${CODEWHALE_BUILD_LOCK_FILE:-${cache}/build.lock}"
root="${cache}/release-verify-$(git rev-parse --short=10 HEAD)-${mode}-$(date -u +%Y%m%dT%H%M%SZ)"
mkdir "${root}"
CODEWHALE_BUILD_LOCK_FILE="${lock}" CARGO_HOME="${root}/cargo-home" \
CODEWHALE_CACHE_ROOT="${root}" ./scripts/release/publish-crates.sh dry-run
CODEWHALE_CACHE_ROOT="${root}" ./scripts/release/publish-crates.sh "${mode}"
```
That run was a cold build of about 19 GB and 962 compile steps, so check
`df -h` first. It kept its lock file inside the new root and did not serialize
with other builds on the machine; `CODEWHALE_BUILD_LOCK_FILE` keeps the machine
lock. `CODEWHALE_CACHE_ROOT` moves the build directory only when `cargo` runs
through `scripts/dev-cargo.sh`; with plain Cargo, set
`CARGO_TARGET_DIR="${root}/target"` instead. Every run leaves state that the
next run at the same version refuses, so publish mode needs another new root,
with `cargo login` run for that `CARGO_HOME` (the token lives there). The CNB
`work/v*` release preflight in `.cnb.yml` calls this script in a fresh
`df -h` first. `cache` and `lock` use the same fallbacks as
`scripts/dev-cache.sh`. `CODEWHALE_CACHE_ROOT` and `CODEWHALE_BUILD_LOCK_FILE`
take effect only when `cargo` on `PATH` is `scripts/dev-cargo.sh` or a wrapper
that execs it, such as the strict dry-run wrapper used for the 0.10.2 receipt.
That run kept its lock file inside the new root and did not serialize with
other builds on the machine; `CODEWHALE_BUILD_LOCK_FILE` keeps the machine
lock. Plain Cargo ignores both variables and takes no machine lock: set
`CARGO_TARGET_DIR="${root}/target"` in place of `CODEWHALE_CACHE_ROOT`, and
wrap the command in `python3 scripts/build-lock.py "${lock}" --` if other
builds may run. Every run leaves state that the next run at the same version
refuses, so the root name carries the SHA, the mode and a UTC timestamp:
publish mode (step 5 of the Rust crates release) and each partial-publish
rerun get their own root. Pass the crates.io token to publish mode as
`CARGO_REGISTRY_TOKEN` for that one command, as step 5 shows; Cargo then
writes no `credentials.toml` under the root. If you run `cargo login` with
that `CARGO_HOME` instead, run `CARGO_HOME="${root}/cargo-home" cargo logout`
once the publish finishes, so no plaintext token copy stays in the cache tree.
The CNB `work/v*` release preflight in `.cnb.yml` calls this script in a fresh
container, where the check finds nothing. Known limits: the check also refuses
compiled units recorded against a different `CARGO_HOME`, which Cargo would
rebuild rather than reuse, and it does not scan `<target-triple>/`
@@ -337,8 +349,8 @@ handoff. Keep backups as recovery artifacts, not alternate development lanes.
Crate publishing to crates.io is **manual** — there is no automated
`crates-publish` GitHub workflow. Operators run the helpers in
`scripts/release/` from a developer workstation that has `cargo login`
configured.
`scripts/release/` from a developer workstation, passing a crates.io publish
token to the publish command as `CARGO_REGISTRY_TOKEN` (step 5).
Release preparation lands directly on canonical `main` in verified slices;
contributor PRs retain their normal review/merge process. The source must be on
@@ -386,7 +398,16 @@ and fails branch-only release sources before assets are published.
```bash
./scripts/release/require-release-tag-checkout.sh X.Y.Z
./scripts/release/publish-crates.sh publish
mode=publish
cache="${CODEWHALE_CACHE_ROOT:-${XDG_CACHE_HOME:-$HOME/.cache}/codewhale}"
lock="${CODEWHALE_BUILD_LOCK_FILE:-${cache}/build.lock}"
root="${cache}/release-verify-$(git rev-parse --short=10 HEAD)-${mode}-$(date -u +%Y%m%dT%H%M%SZ)"
mkdir "${root}"
read -rs token
CARGO_REGISTRY_TOKEN="${token}" CODEWHALE_BUILD_LOCK_FILE="${lock}" \
CARGO_HOME="${root}/cargo-home" CODEWHALE_CACHE_ROOT="${root}" \
./scripts/release/publish-crates.sh "${mode}"
unset token
```
Both Cargo and npm publication fail closed unless `HEAD`, the clean local
@@ -394,9 +415,13 @@ and fails branch-only release sources before assets are published.
dependency order lives in `scripts/release/crates.sh`; do not maintain a
second handwritten order in this runbook. The helper waits for each new
version to appear on crates.io before moving to dependents and safely skips
versions that are already public on a rerun. Run it from a new, empty
`CARGO_HOME` and cache root, as the package preflight above describes; the
dry run's state makes it refuse otherwise.
versions that are already public on a rerun. The block runs it from a new,
empty `CARGO_HOME` and cache root of its own, as the package preflight
above describes; the dry run's state makes it refuse otherwise. `read -rs`
keeps the token out of shell history, and the environment variable keeps
it out of `credentials.toml`. The cache and lock variables take effect only
through `scripts/dev-cargo.sh` or a wrapper that execs it; see the package
preflight for plain Cargo.
If shared `main` advances or the checkout becomes dirty, stop and coordinate
a new candidate or separately authorized recovery with Hunter. Do not switch
branches, create another checkout, force-fetch a conflicting tag or bypass
@@ -586,8 +611,9 @@ for that version — bump to the next patch instead.
### External publish gates (not code defects)
- **crates.io:** publishing needs a valid `cargo login` token on the operator
machine. Verify it with an authenticated, read-only *client* call rather
- **crates.io:** publishing needs a valid crates.io publish token, passed to
the publish command as `CARGO_REGISTRY_TOKEN` (Rust crates release, step 5).
Verify it with an authenticated, read-only *client* call rather
than the `/api/v1/me` endpoint: crates.io answers `/api/v1/me` with
`403 {"errors":[{"detail":"this action can only be performed on the
crates.io website"}]}` even for a good token (checked 2026-09-21 with a
@@ -595,12 +621,16 @@ for that version — bump to the next patch instead.
credential.
```bash
cargo owner --list codewhale-tui # prints the owner, e.g. `Hmbown (Hunter Bown)`
read -rs token
CARGO_REGISTRY_TOKEN="${token}" cargo owner --list codewhale-tui # prints the owner, e.g. `Hmbown (Hunter Bown)`
unset token
```
A 403 or `401` from this call, or `cargo publish` refusing credentials,
means the token is missing/expired — `cargo login`, then
`./scripts/release/publish-crates.sh publish`.
means the token is missing/expired. Create a new token, check it with this
call, then rerun the step 5 block. It makes another new `CARGO_HOME` and
cache root, which the rerun needs because the failed run's own state makes
the guard refuse otherwise, and it keeps the token out of that root.
- **npm:** the OIDC job publishes only if the npmjs.com Trusted Publisher for
`codewhale-hq` / `Codewhale` / workflow `release.yml` / blank environment is
configured. Missing config → the `npm` job fails `E404 No match found`. Fix
@@ -621,9 +651,9 @@ for that version — bump to the next patch instead.
- keep [docs/INSTALL.md](INSTALL.md#roll-back-to-a-previous-release) in sync
with these commands
- Crates publish partially:
- rerun `./scripts/release/publish-crates.sh publish` from another new,
empty `CARGO_HOME` and cache root (see the package preflight); the failed
run's own state makes it refuse otherwise
- rerun the Rust crates release step 5 block; its timestamped root gives
each rerun another new, empty `CARGO_HOME` and cache root, and the failed
run's own state makes `publish-crates.sh publish` refuse otherwise
- already-published crate versions will be skipped
- GitHub assets missing or checksum manifest incomplete:
- fix `.github/workflows/release.yml`, but do not rerun it over an existing
+4 -3
View File
@@ -85,15 +85,16 @@ if [[ -d "${deps_dirs[0]}" ]]; then
fi
if (( ${#reused[@]} > 0 )); then
cache_root="${CODEWHALE_CACHE_ROOT:-${XDG_CACHE_HOME:-${HOME}/.cache}/codewhale}"
verify_root="${cache_root}/release-verify-$(git rev-parse --short=10 HEAD 2>/dev/null || echo candidate)"
verify_root="${cache_root}/release-verify-$(git rev-parse --short=10 HEAD 2>/dev/null || echo candidate)-${mode}-$(date -u +%Y%m%dT%H%M%SZ)"
{
echo "Refusing to package release crates: Cargo would reuse ${workspace_version} state from an earlier package run instead of the current sources:"
printf ' %s\n' "${reused[@]}"
echo "Cargo treats crates served from its temporary package registry as immutable by name and version. It skips extraction once .cargo-ok exists and keeps compiled units that do not track their sources, so this run would verify old code. Nothing was deleted."
echo "Rerun from a new, empty Cargo home and cache root:"
echo "Rerun from a new, empty Cargo home and cache root. Every run leaves state the next run at this version refuses, so publish and each partial-publish rerun need their own root:"
echo " mkdir \"${verify_root}\""
echo " CODEWHALE_BUILD_LOCK_FILE=\"${CODEWHALE_BUILD_LOCK_FILE:-${cache_root}/build.lock}\" CARGO_HOME=\"${verify_root}/cargo-home\" CODEWHALE_CACHE_ROOT=\"${verify_root}\" $0 ${mode}"
echo "If cargo is not scripts/dev-cargo.sh, set CARGO_TARGET_DIR=\"${verify_root}/target\" in place of CODEWHALE_CACHE_ROOT. Publish mode also needs cargo login with that CARGO_HOME. See docs/RELEASE_RUNBOOK.md."
echo "CODEWHALE_CACHE_ROOT and CODEWHALE_BUILD_LOCK_FILE take effect only when cargo on PATH is scripts/dev-cargo.sh or a wrapper that execs it. Plain cargo ignores both and takes no machine build lock; with it, set CARGO_TARGET_DIR=\"${verify_root}/target\" in place of CODEWHALE_CACHE_ROOT."
echo "For publish mode, pass the crates.io token as CARGO_REGISTRY_TOKEN for that one command, so Cargo writes no credentials.toml under the new root. After cargo login with that CARGO_HOME instead, run cargo logout with it once the publish finishes. See docs/RELEASE_RUNBOOK.md."
} >&2
exit 1
fi
+2 -2
View File
@@ -357,8 +357,8 @@
},
{
"path": "docs/RELEASE_CHECKLIST.md",
"sha256": "48264ce4cbd3429546ee8ad9dbd28df565a8d3ab814d5a2c77d6450f96b82d2c",
"content": "# Release Checklist\n\nFollow this sequence from the canonical `/Volumes/VIXinSSD/CW/codewhale`\ncheckout on `main`. Agent-owned preparation lands as verified slices on `main`;\nit requires no release branch, worktree, nested clone or preparation PR.\nContributor PRs still land as PRs. Treat any unchecked required gate as a release\nblocker.\n\nFor deeper context on the underlying tools (preflight scripts, npm smoke,\npublish-crates), see [`RELEASE_RUNBOOK.md`](RELEASE_RUNBOOK.md).\nFor larger milestone releases, add any version-specific acceptance matrix to\nthe existing release packet before tagging; use it for provider routes, feature\ngates, GUI/runtime smoke, remote-workbench decisions, and credit hygiene that the\ngeneric checklist does not enumerate. Record its candidate SHA, owners, local\ncheck results, hosted run URLs, artifact inventory, install/provider receipts,\nknown limitations and publication approval. These proofs are separate.\n\n## 0. Reconcile the canonical checkout\n\n- [ ] Inspect the branch, status, upstream and alternate checkouts before editing:\n ```bash\n git branch --show-current\n git status --short --branch\n git log -5 --oneline\n git worktree list\n git fetch origin main\n git log --left-right --oneline main...origin/main\n ```\n- [ ] The canonical checkout is on `main`. Recover stranded work only after\n preserving dirty/untracked files and comparing commits with current\n `origin/main`. Keep a recoverable backup and unresolved inventory; do not\n reset away work, stash it indefinitely or create another development lane.\n- [ ] Partition files among active writers. Check status before staging and\n stage only owned paths. Historical branches/checkouts are inventoried;\n deleting them requires explicit Hunter approval even if a helper calls\n their tips safe to delete.\n\n## 1. CHANGELOG entry exists for the version\n\n- [ ] `CHANGELOG.md` has a `## [X.Y.Z] - YYYY-MM-DD` heading at the top\n- [ ] The entry credits every external contributor, harvested PR author,\n linked issue reporter, reproduction/log provider, reviewer, and\n verification helper whose work materially shaped this version. Get the\n commit list with:\n ```\n git log vPREV..HEAD --no-merges --format=\"%h %an <%ae> %s\" \\\n | grep -v '<your-email@…>'\n ```\n For each contributor, link both their display name and (when known)\n `@github-handle`. Then inspect linked issues and harvested PRs so\n reporters/helpers are not lost just because they did not author commits.\n Keep `docs/CONTRIBUTORS.md` and `web/lib/release-credits.ts` consistent.\n- [ ] The entry uses the Keep a Changelog headers — `Added`, `Changed`,\n `Fixed`, `Security`, `Removed`, `Deprecated`. Add `Known issues` only\n if there is something material the user must work around.\n- [ ] The entry mentions all referenced issue/PR numbers as `#NNNN` so the\n auto-linker on GitHub picks them up.\n- [ ] Run `scripts/sync-changelog.sh` to regenerate `crates/tui/CHANGELOG.md`\n (the recent-releases slice embedded in the binary for `/change`). Do\n not edit that file by hand, and do not copy the full root changelog\n into it — older entries live in `docs/CHANGELOG_ARCHIVE.md`.\n- [ ] Run `scripts/release/check-feature-release-notes.sh vPREV HEAD`. Every\n issue-linked `feat` commit must leave a receipt in `CHANGELOG.md` or the\n archive; the Version drift CI gate runs the same check with full history.\n\n## 2. Version pins are in sync\n\n- [ ] Use `./scripts/release/prepare-release.sh X.Y.Z` when version pins or\n generated sources need preparation. Reuse a valid preparation receipt\n when those sources are already current. The helper bumps the\n workspace version, every per-crate dependency pin, the npm wrapper\n (`version` + `codewhaleBinaryVersion`), Runtime SDK, VS Code extension\n and lock, remote-smoke default, public source-candidate facts, and README\n install-tag examples; it refreshes the Cargo/npm locks, regenerates\n `crates/tui/CHANGELOG.md` and `web/lib/facts.generated.ts`, and ends\n by running the version and OHOS gates. Write the CHANGELOG entry\n **before** running it. The helper is safe to rerun at the requested\n workspace version; it skips replacements but refreshes both generated\n files and reruns the gates.\n- [ ] `npm/deepseek-tui/package.json` remains private/compatibility-only and\n is **not** bumped or published.\n- [ ] `./scripts/release/check-versions.sh` reports\n `Version state OK: workspace=X.Y.Z, npm=X.Y.Z, npm-binary=X.Y.Z, lockfile in sync.`\n- [ ] `./scripts/release/check-ohos-deps.sh` reports that the OpenHarmony\n Windows linker keeps the target/sysroot flags, the target enables the\n `rquickjs-sys` bindgen edge, and its graph does not pull the unsupporLine truncated
"sha256": "4a0cab2b895cdc01067f0663b1f77c4993d090b3daa29b9eb29557fdfb9eb88d",
"content": "# Release Checklist\n\nFollow this sequence from the canonical `/Volumes/VIXinSSD/CW/codewhale`\ncheckout on `main`. Agent-owned preparation lands as verified slices on `main`;\nit requires no release branch, worktree, nested clone or preparation PR.\nContributor PRs still land as PRs. Treat any unchecked required gate as a release\nblocker.\n\nFor deeper context on the underlying tools (preflight scripts, npm smoke,\npublish-crates), see [`RELEASE_RUNBOOK.md`](RELEASE_RUNBOOK.md).\nFor larger milestone releases, add any version-specific acceptance matrix to\nthe existing release packet before tagging; use it for provider routes, feature\ngates, GUI/runtime smoke, remote-workbench decisions, and credit hygiene that the\ngeneric checklist does not enumerate. Record its candidate SHA, owners, local\ncheck results, hosted run URLs, artifact inventory, install/provider receipts,\nknown limitations and publication approval. These proofs are separate.\n\n## 0. Reconcile the canonical checkout\n\n- [ ] Inspect the branch, status, upstream and alternate checkouts before editing:\n ```bash\n git branch --show-current\n git status --short --branch\n git log -5 --oneline\n git worktree list\n git fetch origin main\n git log --left-right --oneline main...origin/main\n ```\n- [ ] The canonical checkout is on `main`. Recover stranded work only after\n preserving dirty/untracked files and comparing commits with current\n `origin/main`. Keep a recoverable backup and unresolved inventory; do not\n reset away work, stash it indefinitely or create another development lane.\n- [ ] Partition files among active writers. Check status before staging and\n stage only owned paths. Historical branches/checkouts are inventoried;\n deleting them requires explicit Hunter approval even if a helper calls\n their tips safe to delete.\n\n## 1. CHANGELOG entry exists for the version\n\n- [ ] `CHANGELOG.md` has a `## [X.Y.Z] - YYYY-MM-DD` heading at the top\n- [ ] The entry credits every external contributor, harvested PR author,\n linked issue reporter, reproduction/log provider, reviewer, and\n verification helper whose work materially shaped this version. Get the\n commit list with:\n ```\n git log vPREV..HEAD --no-merges --format=\"%h %an <%ae> %s\" \\\n | grep -v '<your-email@…>'\n ```\n For each contributor, link both their display name and (when known)\n `@github-handle`. Then inspect linked issues and harvested PRs so\n reporters/helpers are not lost just because they did not author commits.\n Keep `docs/CONTRIBUTORS.md` and `web/lib/release-credits.ts` consistent.\n- [ ] The entry uses the Keep a Changelog headers — `Added`, `Changed`,\n `Fixed`, `Security`, `Removed`, `Deprecated`. Add `Known issues` only\n if there is something material the user must work around.\n- [ ] The entry mentions all referenced issue/PR numbers as `#NNNN` so the\n auto-linker on GitHub picks them up.\n- [ ] Run `scripts/sync-changelog.sh` to regenerate `crates/tui/CHANGELOG.md`\n (the recent-releases slice embedded in the binary for `/change`). Do\n not edit that file by hand, and do not copy the full root changelog\n into it — older entries live in `docs/CHANGELOG_ARCHIVE.md`.\n- [ ] Run `scripts/release/check-feature-release-notes.sh vPREV HEAD`. Every\n issue-linked `feat` commit must leave a receipt in `CHANGELOG.md` or the\n archive; the Version drift CI gate runs the same check with full history.\n\n## 2. Version pins are in sync\n\n- [ ] Use `./scripts/release/prepare-release.sh X.Y.Z` when version pins or\n generated sources need preparation. Reuse a valid preparation receipt\n when those sources are already current. The helper bumps the\n workspace version, every per-crate dependency pin, the npm wrapper\n (`version` + `codewhaleBinaryVersion`), Runtime SDK, VS Code extension\n and lock, remote-smoke default, public source-candidate facts, and README\n install-tag examples; it refreshes the Cargo/npm locks, regenerates\n `crates/tui/CHANGELOG.md` and `web/lib/facts.generated.ts`, and ends\n by running the version and OHOS gates. Write the CHANGELOG entry\n **before** running it. The helper is safe to rerun at the requested\n workspace version; it skips replacements but refreshes both generated\n files and reruns the gates.\n- [ ] `npm/deepseek-tui/package.json` remains private/compatibility-only and\n is **not** bumped or published.\n- [ ] `./scripts/release/check-versions.sh` reports\n `Version state OK: workspace=X.Y.Z, npm=X.Y.Z, npm-binary=X.Y.Z, lockfile in sync.`\n- [ ] `./scripts/release/check-ohos-deps.sh` reports that the OpenHarmony\n Windows linker keeps the target/sysroot flags, the target enables the\n `rquickjs-sys` bindgen edge, and its graph does not pull the unsupporLine truncated
}
]
},