A release is a SemVer tag such as v0.1.0 plus a matching GitHub release, cut from one exact commit. Merging to main changes source and deploying changes one running deployment; neither publishes a release, and Chickpea publishes no npm package. This page covers the three gates (source, attended acceptance, publish), the export allowlist, the model catalog’s immutable revisions, and what release notes must contain.
Prerequisites
- A clean checkout of the exact candidate commit, with no private environment files. Never
.env,.dev.vars, SQLite databases, credential keyrings, setup links, or live transcripts. - Node 24.20.0.
.nvmrcpins the single release-verification baseline. Only Node 24.x is supported, minimum 24.20.0. Update the pin for later patch/security releases and verify it once, without a second runtime sweep. - An approved disposable deployment and Slack workspace for acceptance. Not a production workspace, and not another task’s environment.
- Repository settings in place. Force pushes and deletions blocked on
main, GitHub Actions disabled with no required workflow checks, Dependabot alerts and security updates on, private vulnerability reporting on. - Explicit authorization to tag and publish that exact commit.
Run the source gates
Start from the clean checkout. Run the checks serially, once on the pinned Node 24 baseline, because tests inspect generated artifacts and a concurrent build makes them read a partially written artifact.
nvm install && nvm use
npm ci
npm audit --omit=dev
npm run verify:regression -- --mode releaseThe release command restores artifacts and runs authoring, Admin, local workerd,
lockfile, and committed-source export checks. The export installs, builds, and
runs the full root/CLI suite plus offline turn, durability, and provider checks.
Those checks run once inside the export; the outer command does not repeat them.
Use --record <private-run.json> with the existing verification notebook to keep
logs and checkpoint evidence. Missing evidence, stale configuration, unresolved
failures, and the final merge-result check remain release gates.
-
Commit first.
verify:oss-exportreads committed HEAD. Release mode refuses dirty source or private environment files; an earlier commit’s pass does not validate new changes. -
verify:cf-smoketakes several minutes. It builds both Cloudflare profiles and runs the core profile in local workerd, and it treats.wrangler-statein that directory as disposable. -
No hosted run counts. There is no GitHub workflow that runs or deploys a release. Record the local results with the change or the release evidence.
What verify:oss-export checks
The verifier reads the tracked file list at HEAD, extracts that archive into a temporary directory it prints as SCRATCH=<path>, and then:
- Scans every tracked file for denied terms. Private project and company names, local home-directory paths, and, in the live-verification sources, Slack tokens, provider API keys, and Slack coordinate IDs.
- Blocks unlisted documents. A tracked path under
docs/that is not on theallowedPublicDocslist fails withOSS export contains forbidden public-source paths:. - Blocks unlisted binaries. Image and media extensions fail unless the path is on the
allowedBinaryFileslist with a matching SHA-256 digest. - Blocks local agent and tool state. Tracked paths under
evidence/,screenshots/,transcripts/,tmp/, and the agent-state dot directories never ship, apart from one allowed skill file. - Requires the license files and public package metadata.
LICENSEandNOTICEmust exist, andpackage.jsonmust stayprivatewith anApache-2.0license, a description, and a repository. - Rebuilds the archive from nothing.
npm ci,npm run build,npm run test:ci, the offline Flue turn,verify:durability,verify:providers, and a deployment dry run, all inside the scratch directory.
A pass prints OSS export verification passed and deletes the scratch directory. Set KEEP_EXPORT_SCRATCH=1 to keep it for inspection.
Adding a public document to the release means adding its path to the allowlist in scripts/verify-oss-export.mjs in the same change. A new runtime image goes on the binary allowlist with its digest, and into src/assets/public-assets.ts so Node serves it.
Record the compressed upload size
The deployment dry run at the end of verify:oss-export is the measurement. To take it by hand, run npm run deploy -- --dry-run; the deploy wrapper uploads nothing on a dry run and passes wrangler’s output through untouched.
- Record the compressed upload size from that dry run, not the size of the repository or
node_modules. - Keep headroom below the advertised plan’s Worker limit. The current bundle exceeds the Free plan’s 3 MiB limit, which is why Chickpea advertises Workers Paid.
- A dynamic import still contributes its uploaded chunk to that limit.
- Do not advertise Free compatibility unless the release artifact fits those limits and the relevant runtime checks pass.
Rebuild the model catalog when its source changed
catalog/current.json is generated from catalog/source.json. Do not edit it by hand.
npm run catalog:buildwith no arguments only checks. It printsCatalog revision <n> is deterministic and immutable., or fails withcatalog/current.json is not the deterministic output of catalog/source.json.npm run catalog:build -- --writewritescatalog/current.jsonandcatalog/releases/revision-<n>.json. The revision file is written exclusively: rewriting a published revision with different bytes fails withImmutable release already exists with different bytes:.- Bump
revisionincatalog/source.jsonwhen entries change. A published revision keeps its bytes forever. - The source gates above do not include this check. Run
npm run verify:model-catalogyourself when the catalog changed; it rebuilds the catalog and runs the catalog tests.
Run attended acceptance
Fake-backend tests and local workerd give deterministic coverage. They do not establish real Slack and Admin acceptance, so this gate is a live run against a disposable environment. Keep identities, bindings, secrets, and evidence outside the public repository.
- Choose the approved disposable deployment and Slack workspace before you install anything.
- Install the exact candidate from a fresh public source checkout with no local private files.
- Exercise each advertised target and Slack lane. Name any lane you did not test instead of claiming general acceptance.
- Complete Slack installation, first-Owner sign-in, provider and model selection, and a real DM or mention that receives a reply from the candidate deployment.
- Confirm a second user cannot reach Owner-only configuration or another Agent’s personal connection. Check an approved connector flow if the release advertises one.
- Restart or redeploy, then confirm sign-in, existing state, and Slack delivery still work. Check retry and deduplication behavior without claiming exactly-once delivery.
- On Cloudflare, verify an approved schedule fires and stops when paused. Test the coding sandbox if this release advertises the optional profile.
- For a second or later release, test the documented upgrade from the prior supported version and the stated recovery procedure. A fresh install does not replace upgrade testing.
- Remove only the fixtures and resources this run created. Record the source commit, the target and version, observed outcomes, limitations, and cleanup.
Write the release notes
The notes are the operator’s contract for that version. They must cover:
- Prerequisites and installation links, so an operator can install the tagged source the documented way.
- Changes and known limitations in this version.
- Supported upgrade origins. State the supported starting versions and the required operator actions. There is no implied upgrade path from an unlisted experimental schema.
- Migrations, and whether a downgrade is safe. If the notes do not declare a downgrade safe, operators use the tested recovery procedure instead.
- Recovery limits. Reverting code does not undo a schema migration or recover deleted data.
- Experimental status. For a 0.x release, say so, and decide whether GitHub’s prerelease flag applies.
Verify
Confirm all of this before you create the tag.
package.json, the lockfile, and the tag carry the same version. The tag isv0.1.0, notv0.01.- The candidate commit is on
main, and the local checks were rerun or confirmed on that final commit. - Attended acceptance ran against this commit, with untested lanes named rather than implied.
- The release notes cover every item above.
- You hold explicit authorization to tag and publish this exact commit.
Publish
- Land the release preparation through a GitHub pull request or a maintainer-authorized local merge into root
main, verified before pushing. - Create the version tag and the GitHub release together, on the authorized commit.
- Download the release’s source archive and confirm it installs by the documented path.
- Publish no secrets and no private acceptance evidence.
Next steps
- Contribute: the development checkout and the source layout behind these gates.
- Upgrades and compatibility: the upgrade and recovery procedures your release notes commit operators to.
- Security model: the guarantees a release must not weaken.
