Bundles the v0.8.8 stabilization fixes that were already implemented in the working tree, plus the workflow/doc reconciliation called out in #507. ### Sub-agent runtime fixes - **#509** Default sub-agent cap raised to 10 (configurable via `[subagents].max_concurrent` in `config.toml`, hard ceiling 20). The running-count calculation now ignores non-running, no-handle, and finished handles so completed agents stop counting against the cap. - **#510** `SharedSubAgentManager` is now `Arc<RwLock<...>>`; the read paths that previously held a `Mutex` for inspection now take a read lock, eliminating the multi-agent fan-out UI freeze. - **#511** `compact_tool_result_for_context` summarizes `agent_result` / `agent_wait` payloads before they are folded into the parent context. - **#512** RLM tool cards map to `ToolFamily::Rlm` and render `rlm`, not `swarm`. Stale "swarm" wording cleaned in docs/comments/tests. - **#513** (foreground stopgap only) Foreground RLM work is visible in the Agents sidebar projection. Full async RLM lifecycle remains v0.8.9 — the issue stays open with a refined scope. ### TUI / UX fixes - **#487** Offline composer queue is now session-scoped; legacy unscoped queues fail closed. - **#488** Composer Option+Backspace deletes by word; cross-platform key routing helpers added. - **#443/#444** Keyboard enhancement flags pop on normal AND panic exit; the raw-mode startup probe is now bounded by a configurable timeout. - **#449** Production footer reads statusline colors from `app.ui_theme` rather than the bespoke palette. - **#506** `display_path_with_home` no longer mutates `HOME` in tests; the flake on shared-env CI is gone. ### Self-update / packaging - **#503** `update.rs` arch mapping uses release-asset naming (`arm64`/`x64`) instead of the raw Rust constants. The platform-asset selector also rejects `.sha256` siblings as primary binaries. Tests now live alongside the source in `mod tests` (the `#[path]`-based integration test was removed because it duplicated test runs and forced a `pub(crate)` helper that no real caller used). - **`Max 5 in flight` wording updated** in `agent_spawn` description, `prompts/base.md`, and `docs/TOOL_SURFACE.md` so the model sees the real default cap (10) and the configuration knob name. ### CI / release docs (#507) - Pruned three duplicated/dead workflows: `crates-publish.yml`, `parity.yml`, `publish-npm.yml`. Their gates already run in `ci.yml` for every push/PR. - `release.yml` build job now allows `parity` to be skipped (it only runs on tag push), unblocking `workflow_dispatch` reruns. The job still fails closed on a real parity failure. - `RELEASE_RUNBOOK.md` reconciled: crate publishing is documented as the manual `scripts/release/publish-crates.sh` flow (no automated workflow); references to the deleted workflows removed. - `CLAUDE.md` notes the `RELEASE_TAG_PAT` requirement for the auto-tag → release.yml chain (without it, the tag is created but `release.yml` does not fire) and documents the `workflow_dispatch` parity-skip behavior. ### Docs - `docs/COMPETITIVE_ANALYSIS.md` added — capability matrix vs OpenCode and Codex CLI, gap analysis, and recommended implementation order. ### Verification (this branch) - `cargo fmt --all -- --check` ✓ - `cargo check --workspace --all-targets --locked` ✓ - `cargo clippy --workspace --all-targets --all-features --locked -- -D warnings` ✓ - `cargo test --workspace --all-features --locked` ✓ (1809 + supporting) - Parity gates ✓ (snapshot, parity_protocol, parity_state) - `cargo build --release --locked -p deepseek-tui-cli -p deepseek-tui` ✓ - Lockfile drift guard ✓ - `deepseek doctor --json` clean - `deepseek eval` (offline harness) success=true, 0 tool errors Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8.4 KiB
DeepSeek TUI Release Runbook
This runbook is the source of truth for shipping Rust crates, GitHub release assets,
and the deepseek-tui npm wrapper.
Current packaging note:
deepseek-tuiis the live runtime and TUI package shipped to users today.deepseek-tui-coreis a supporting workspace crate for the extraction/parity effort, not a replacement for the shipping runtime.
Canonical Publish Targets
- End-user crates:
deepseek-tuideepseek-tui-cli
- Supporting crates published from this workspace:
deepseek-secretsdeepseek-configdeepseek-protocoldeepseek-statedeepseek-agentdeepseek-execpolicydeepseek-hooksdeepseek-mcpdeepseek-toolsdeepseek-coredeepseek-app-serverdeepseek-tui-core
deepseek-clion crates.io is an unrelated crate and is not part of this release flow.
Version Coordination
- Rust crates inherit the shared workspace version from Cargo.toml.
- Internal path dependency versions should match the shared workspace version; stale older pins are release blockers once the workspace version moves.
- The npm wrapper version lives in npm/deepseek-tui/package.json.
deepseekBinaryVersioncontrols which GitHub release binaries the npm wrapper downloads.- Packaging-only npm releases are allowed:
- bump the npm package version
- leave
deepseekBinaryVersionpinned to the previously released Rust binaries - rerun
npm packsmoke checks beforenpm publish
Preflight
Run these from the repository root before cutting a tag:
./scripts/release/check-versions.sh # version drift between workspace, npm, lockfile
cargo fmt --all -- --check
cargo check --workspace --all-targets --locked
cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
cargo test --workspace --all-features --locked
cargo publish --dry-run --locked --allow-dirty -p deepseek-tui
./scripts/release/publish-crates.sh dry-run
check-versions.sh also runs in CI on every push/PR (the versions job in
.github/workflows/ci.yml), so drift between Cargo.toml, the per-crate
manifests, npm/deepseek-tui/package.json, and Cargo.lock is caught before
release time rather than at it.
publish-crates.sh dry-run performs a full cargo publish --dry-run for crates
without unpublished workspace dependencies and a packaging preflight for dependent
workspace crates. That avoids false negatives from crates.io not yet containing the
new workspace version while still validating package contents before publish.
For npm wrapper verification, build the two shipped binaries and run the
cross-platform smoke harness. This packs the npm wrapper, installs it into a
clean temporary project, serves local release assets over HTTP, and checks both
the dispatcher-to-TUI path (deepseek doctor --help) and the direct TUI
entrypoint (deepseek-tui --help).
cargo build --release --locked -p deepseek-tui-cli -p deepseek-tui
node scripts/release/npm-wrapper-smoke.js
Set DEEPSEEK_TUI_KEEP_SMOKE_DIR=1 to keep the temporary pack/install
directory for inspection.
To exercise npm run release:check locally as well, regenerate the local asset
directory with a full asset matrix fixture before starting the server:
DEEPSEEK_TUI_PREPARE_ALL_ASSETS=1 node scripts/release/prepare-local-release-assets.js
cd npm/deepseek-tui
DEEPSEEK_TUI_VERSION=X.Y.Z DEEPSEEK_TUI_RELEASE_BASE_URL=http://127.0.0.1:8123/ npm run release:check
Set DEEPSEEK_TUI_VERSION to the npm package version you are verifying for that local run.
The CI workflow runs the same tarball install + delegated-entrypoint smoke test on Linux, macOS, and Windows.
After publishing, prove the release is visible in both registries:
./scripts/release/check-published.sh X.Y.Z
Do not mark a Rust release complete until that command sees deepseek-tui@X.Y.Z
on npm and every deepseek-* crate at X.Y.Z on crates.io. For a rare
npm packaging-only release, run with --allow-npm-binary-mismatch and keep the
release notes explicit that no new Rust binary version shipped.
Rust Crates Release
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.
- Update the workspace version in Cargo.toml.
- Run
./scripts/release/check-versions.shand./scripts/release/publish-crates.sh dry-runlocally; both must be clean. - Tag the release as
vX.Y.Z(typically by pushing the version bump tomainand lettingauto-tag.ymlcreate the tag — see the npm wrapper release section below for theRELEASE_TAG_PATrequirement). - Publish crates in this order with
./scripts/release/publish-crates.sh publish:deepseek-secretsdeepseek-configdeepseek-protocoldeepseek-statedeepseek-agentdeepseek-execpolicydeepseek-hooksdeepseek-mcpdeepseek-toolsdeepseek-coredeepseek-app-serverdeepseek-tui-coredeepseek-tui-clideepseek-tui
- Wait for each published crate version to appear on crates.io before publishing dependents.
The publish helper is idempotent for reruns: already-published crate versions are skipped.
GitHub Release Assets
.github/workflows/release.yml builds these binaries:
deepseek-linux-x64deepseek-macos-x64deepseek-macos-arm64deepseek-windows-x64.exedeepseek-tui-linux-x64deepseek-tui-macos-x64deepseek-tui-macos-arm64deepseek-tui-windows-x64.exe
The release job also uploads deepseek-artifacts-sha256.txt. The npm installer and
release verification script both depend on that checksum manifest.
npm Wrapper Release
The npm publish step is manual. release.yml no longer runs npm publish
because the npm account requires 2FA OTP on every publish, and an automation
token that bypasses 2FA has not been provisioned. The GitHub Release flow
remains fully automated; only the npm wrapper publish requires a developer
on a workstation with npm login and an authenticator app.
Steps
- Set the npm package version in npm/deepseek-tui/package.json to match the workspace
Cargo.toml. CI's version-drift guard will catch mismatches before tag. - Set
deepseekBinaryVersionto the GitHub release tag that should supply binaries. - Push the version bump to
main.auto-tag.ymlcreates the matchingvX.Y.Ztag, andrelease.ymlbuilds the binary matrix and drafts the GitHub Release. - Wait for the GitHub Release to finalize with all eight signed binaries plus
deepseek-artifacts-sha256.txt. The npmprepublishOnlyhook (scripts/verify-release-assets.js) requires every asset to be present. - From a developer machine, publish the npm wrapper manually:
cd npm/deepseek-tui
npm publish --access public
# (you will be prompted for the npm OTP from your authenticator)
Why not automated?
release.yml's oldpublish-npmjob usedsecrets.NPM_TOKEN, but npm's 2FA-by-default policy means a publish token must be either an automation token with "Bypass 2FA for token authentication" enabled OR an account-level 2FA-disabled state. We don't have either configured.- The standalone
publish-npm.ymlandcrates-publish.ymlworkflows have been removed; no inert automation plumbing remains. A future move to npm Trusted Publishing (OIDC) would re-introduce a dedicated workflow at that point.
If you fix the token later
To re-enable automated publish: provision an npm automation token with "Bypass 2FA for token authentication" enabled (or set up npm Trusted Publishing via OIDC), store the corresponding secret on the repo, and re-add a publish-npm job to release.yml (or a dedicated workflow) along with reverting this section's "manual" framing.
Recovery and Rollback
- Crates publish partially:
- rerun
./scripts/release/publish-crates.sh publish - already-published crate versions will be skipped
- rerun
- GitHub assets missing or checksum manifest incomplete:
- fix
.github/workflows/release.yml - retag or upload corrected assets before
npm publish
- fix
- npm packaging-only problem:
- bump only the npm package version
- keep
deepseekBinaryVersionon the last known-good Rust release - repack and republish the wrapper
- A bad npm publish cannot be overwritten:
- publish a new npm version with corrected metadata or install logic