## Description
Harden the release artifact workflow so maintainers can reproduce npm
and Python release checks locally and so OpenClaw packaging does not
depend on a not-yet-published SDK version. This follow-up also keeps the
OpenClaw loader export contract explicit and updates the PR after the
branch was merged with current `headroomlabs/main`.
## Type of Change
- [x] Bug fix (non-breaking change that fixes an issue)
- [x] New feature (non-breaking change that adds functionality)
- [x] Documentation update
## Changes Made
- Added reusable local release smoke scripts for npm assets, Python
wheel/sdist artifacts, and the combined release gate.
- Switched the release workflow npm asset build to the reusable npm
builder.
- Made OpenClaw release asset building install against the just-built
local SDK tarball before rewriting packed metadata to the release
dependency range.
- Kept the OpenClaw source dependency registry-installable at
`headroom-ai@^0.22.3` while the packed artifact still ships
`^<release-version>`.
- Added `registerHeadroomPlugin` as a named export while preserving the
default `{ register }` OpenClaw loader contract.
- Added Windows development bootstrap docs/script and regression tests
for version sync, npm asset ordering, OpenClaw source installability,
and Python wheel smoke import isolation.
## Testing
- [x] Unit tests pass (`pytest`)
- [x] New tests added for new functionality
- [x] Manual testing performed
### Test Output
```text
uv run --with pytest python -m pytest tests/test_release_workflows.py scripts/tests/test_version_sync.py -q
44 passed, 1 warning
node --check scripts/build_npm_release_assets.mjs
node --check scripts/verify_npm_release_assets.mjs
python -m py_compile scripts/build_python_release_smoke.py scripts/release_smoke_all.py scripts/version-sync.py
python scripts/verify-versions.py
All versions aligned at 0.31.0
npm ci (plugins/openclaw)
added 88 packages, audited 89 packages, found 0 vulnerabilities
python scripts/release_smoke_all.py --out release-assets-local/all-pr1824-postmerge-fixed-20260710-104739
Verified npm release assets for 0.31.0
wheel metadata OK: headroom_ai-0.31.0-cp310-abi3-win_amd64.whl contains headroom/_core.pyd
sdist License-File metadata OK: ['LICENSE', 'NOTICE']
smoke-import OK: version=0.31.0 hello=headroom-core
```
## Real Behavior Proof
- Environment: Windows 11, Python 3.14.3, Node v24.14.0, npm 11.9.0, uv
0.11.15, Rust/Cargo already installed in the local development
environment.
- Exact command / steps: Ran `python scripts/release_smoke_all.py --out
release-assets-local/all-pr1824-postmerge-fixed-20260710-104739` after
syncing this branch with current `headroomlabs/main`.
- Observed result: The command built `headroom-ai-0.31.0.tgz`,
`headroom-openclaw-0.31.0.tgz`,
`headroom_ai-0.31.0-cp310-abi3-win_amd64.whl`, and
`headroom_ai-0.31.0.tar.gz`; npm verifier passed; the wheel installed
into a fresh venv and imported `headroom._core`.
- Not tested: Full GitHub Actions release matrix and publish jobs with
real PyPI/npm/GitHub Packages credentials.
## Review Readiness
- [x] I have performed a self-review
- [x] This PR is ready for human review
## Checklist
- [x] My code follows the project's style guidelines
- [x] I have performed a self-review of my code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] I have updated the CHANGELOG.md if applicable
## Screenshots (if applicable)
N/A.
## Additional Notes
The post-merge full smoke initially failed because the OpenClaw source
package depended on `headroom-ai@^0.31.0`, which is not available on
public npm yet. The builder now installs OpenClaw against the just-built
local SDK tarball before build/pack, and then rewrites packed metadata
to `^0.31.0`.
---------
Co-authored-by: JerrettDavis <mxjerrett@gmail.com>
Co-authored-by: Tejas Chopra <chopratejas@gmail.com>
|
||
|---|---|---|
| .. | ||
| ci | ||
| fixtures | ||
| tests | ||
| audit_wheel_glibc_symbols.py | ||
| bootstrap-windows-dev.ps1 | ||
| build_npm_release_assets.mjs | ||
| build_python_release_smoke.py | ||
| build_rust_extension.sh | ||
| changelog-gen.py | ||
| eval_output_shaper.py | ||
| export_kompress_v2_onnx.py | ||
| install-git-hooks.sh | ||
| install.ps1 | ||
| install.sh | ||
| pr-governance.py | ||
| README.md | ||
| record_fixtures.py | ||
| refresh_model_limits.sh | ||
| release_smoke_all.py | ||
| replay_codex_ws_load.py | ||
| repro_codex_replay.py | ||
| smoke_issue_327.py | ||
| sync-plugin-versions.py | ||
| validate-workflows.sh | ||
| verify-versions.py | ||
| verify_npm_release_assets.mjs | ||
| version-sync.py | ||
scripts/
Utility scripts bundled with the Headroom repo. Most are one-off operator tools; a few are runnable as part of development workflows.
Reproducing the reconnect storm
repro_codex_replay.py reproduces the multi-agent Codex reconnect/retry storm
against a local Headroom proxy (default http://127.0.0.1:8787). Use it to:
- Regression-check that
/livezstays responsive under a cold-start storm. - Empirically tune the Unit 4 pre-upstream semaphore default
(
HEADROOM_ANTHROPIC_PRE_UPSTREAM_CONCURRENCY). - Exercise the Codex WS lifecycle + Anthropic HTTP path simultaneously without needing to replay captured production traffic.
Run
# Default: 8 WS + 4 HTTP clients, 30s storm, p99 /livez must stay <= 500ms.
python scripts/repro_codex_replay.py
# Tighter budget, shorter run:
python scripts/repro_codex_replay.py \
--url http://127.0.0.1:8787 \
--ws-clients 16 \
--anthropic-clients 8 \
--duration 60 \
--livez-threshold-ms 100
# Dump the full summary as JSON for downstream tooling:
python scripts/repro_codex_replay.py --json
Exit code:
0— warmup succeeded (or was skipped), storm ran for the requested duration, and/livezp99 stayed under--livez-threshold-ms.1— soft assertion failed, proxy unreachable, or unhandled exception. Proxy-unreachable is detected and reported within ~5 seconds.
Fixtures
The script loads two hand-crafted, fully synthetic JSON fixtures:
scripts/fixtures/anthropic_replay_body.json— shape of a large agent reconnect replay/v1/messages?beta=truePOST body.scripts/fixtures/codex_response_create_frame.json— first Codex WS frame with the{"type": "response.create", "response": {...}}envelope.
Override via --ws-frame-fixture / --anthropic-body-fixture if you have
captured traffic to replay instead.
Interpretation
/livez p99under threshold means the event loop is not starved during the storm. If it rises with the semaphore unbounded (HEADROOM_ANTHROPIC_PRE_UPSTREAM_CONCURRENCY=10000) and drops back under the default, Unit 4's backpressure is working.Codex WS: openedshould equal--ws-clients.response.completedtypically stays low when upstream auth isn't configured locally — the goal is handshake + relay wiring, not real upstream traffic.Anthropic HTTP: ok_2xx + non_2xx + timed_out + errorsshould roughly equalattempted. Sustained non-zerotimed_outduring the storm is the failure signal the plan targets.
A smoke test at tests/test_scripts/test_repro_codex_replay_smoke.py
exercises the script against a mock FastAPI server on every PR.
Install scripts
install.sh— POSIX installer.install.ps1— Windows PowerShell installer.
These are generated by the release pipeline; edit with care.
Windows development bootstrap
bootstrap-windows-dev.ps1 prepares a Windows development checkout. It resolves
or creates a repo-local Python virtual environment, checks for Rust, installs
Python build/test tooling, installs npm dependencies for the TypeScript SDK and
OpenClaw plugin, and runs a small smoke set.
powershell -ExecutionPolicy Bypass -File scripts/bootstrap-windows-dev.ps1
Use -CheckOnly to print detected tool versions without installing packages.
Use -SkipSmoke, -SkipDocs, -SkipNode, or -SkipRust when intentionally
debugging one part of the environment.
npm release asset smoke
build_npm_release_assets.mjs locally reproduces the release workflow's npm
asset build. It builds the TypeScript SDK tarball, installs that tarball into
OpenClaw, rewrites OpenClaw's release dependency to the same version,
regenerates dist/package.json, packs OpenClaw, and then runs
verify_npm_release_assets.mjs.
node scripts/build_npm_release_assets.mjs <version>
By default, output goes into a timestamped release-assets-local/<version>-*
directory. Pass an explicit empty directory when you want a predictable path:
node scripts/build_npm_release_assets.mjs <version> release-assets-local/smoke
Expected tarballs:
headroom-ai-<version>.tgzheadroom-openclaw-<version>.tgz
The script restores package metadata after it finishes so the source tree keeps the registry-installable development dependency range.
Python release artifact smoke
build_python_release_smoke.py locally reproduces the Python artifact smoke:
it builds a wheel with maturin, builds an sdist, verifies the sdist
License-File metadata against tarball contents, installs the wheel into a
fresh python -m venv environment, and imports the native headroom._core
extension from that installed wheel.
python scripts/build_python_release_smoke.py
By default, the wheel uses the faster Cargo ci profile and output goes into a
timestamped release-assets-local/python-<version>-* directory. Use --release
when you want the slower shipped-wheel profile:
python scripts/build_python_release_smoke.py --release --out release-assets-local/python-release-smoke
Expected artifacts:
headroom_ai-<version>-*.whlheadroom_ai-<version>.tar.gz
Full local release smoke
release_smoke_all.py is the one-command local release gate. It first runs
scripts/verify-versions.py, then runs the npm release asset smoke and the
Python wheel/sdist smoke into sibling output directories.
python scripts/release_smoke_all.py
By default, output goes into release-assets-local/all-<version>-*/npm and
release-assets-local/all-<version>-*/python. Pass an explicit empty output
directory for a predictable evidence path:
python scripts/release_smoke_all.py --out release-assets-local/full-release-smoke
Use --python-release when the Python smoke should build with maturin's slower
release profile. Use --skip-npm or --skip-python only when intentionally
debugging one side of the artifact pipeline.