Public package releases
Two workflows publish from JuliusBrussee/caveman:
release-packages.yml: the npm and PyPI packages below, one tag per release.release-binaries.yml: the signed Go runtime binaries (bin-v*) and theghcr.io/juliusbrussee/caveman-proxycontainer image.
Build jobs have no OIDC permission. Only the isolated publish jobs mint registry tokens, through npm and PyPI trusted publishing; no long-lived registry token exists in the repository. Trusted publishing from GitHub Actions attaches npm provenance and PyPI attestations automatically.
Release tags
Tags must be annotated, GitHub-verified, and point to a commit on main. The
workflow rejects a tag whose version differs from the package metadata.
| Tag | Artifact | Latest published (2026-10-07) | Version in main (Caveman 3.2.0) | GitHub Release |
|---|---|---|---|---|
sdk-ts-v* | npm @caveman-ai/sdk | 1.2.0 | 1.2.0 | Yes |
sdk-python-v* | PyPI caveman-sdk | 1.2.0 | 1.2.0 | Yes |
middleware-ts-v* | npm @caveman-ai/middleware | 1.0.1 | 1.0.1 | Yes |
middleware-python-v* | PyPI caveman-middleware | 1.0.0 | 1.0.0 | Yes |
contracts-v* | npm @caveman-ai/contracts | 2.0.0 | 2.0.0 | Yes |
pi-v* | npm @caveman-ai/pi | 0.3.0 | 0.3.0 | No |
bin-v* | Go binaries and container image | bin-v2.1.0 | bin-v2.1.0 (pinned in packages/cli/BINARY_RELEASE) | Yes, with the binaries |
cli-v* | npm @caveman-ai/cli | 2.1.0 | 2.1.0 | No |
v* | Caveman product (installer, plugin, skills) | v3.2.0 | v3.2.0 | Yes, "Latest" |
tests/verify_repo.py checks that each SDK and middleware package's version,
version constant (SDK_VERSION, MIDDLEWARE_VERSION), and top CHANGELOG.md
heading agree, that the middleware packages' SDK floor is the repository SDK
version, and that every published package ships LICENSE and NOTICE.
@caveman-ai/cli publishes from cli-v* through release-packages.yml like
the others; its release order is in
packages/cli/PUBLISHING.md.
What each lane checks before publishing
npm lane (sdk-ts, pi): npm ci --ignore-scripts
from the package's committed lockfile, npm audit (runtime graph at low, full
graph at high), full tests, npm pack, and a fresh-install import smoke of the
exact tarball.
pnpm lane (middleware-ts): @caveman-ai/middleware depends on the
workspace SDK, so it builds in the pnpm workspace (pnpm install --frozen-lockfile), builds the SDK, runs the adapter suite with
CAVEMAN_REQUIRE_FRAMEWORKS=1 plus the consumer test, and packs with pnpm, which
rewrites workspace:^ to a concrete range. A fresh npm project then installs
the tarball with the AI SDK and imports @caveman-ai/middleware/ai-sdk; the
build fails if the workspace protocol leaked into the manifest.
CLI lane (cli): also a pnpm workspace package. Before building it
requires every asset of the binary release pinned in
packages/cli/BINARY_RELEASE (36 binaries, checksums.txt,
checksums.txt.keysig, RELEASE) to download anonymously, since the CLI installs them.
It then checks the generated profile and binary-pin constants against their
sources (node agents/compile.mjs, gen-binaries.mjs --check), builds the real
proxy, runs the full CLI suite, packs with pnpm, and checks that the installed
tarball's caveman --version reports the tagged version and the binary pin.
node agents/probe-installed.mjs --all stays a release-machine check; the
agent-conformance workflow covers every profile in CI.
All npm lanes then unpack the tarball, resolve its runtime dependencies the
way a consumer would, run npm audit --omit=dev --audit-level=low on that
graph, and write a CycloneDX SBOM with npm sbom. For the middleware this is the
only audit: pnpm audit covers the whole workspace (and fails on unrelated
packages), and npm cannot resolve the adapters' framework test graph, whose
optional peers conflict. Dependabot watches that test graph instead.
PyPI lanes (sdk-python, middleware-python): the build job installs only
.github/requirements/release-python.txt (--require-hashes), builds the sdist
and wheel with --no-isolation, uploads them, and records their SHA-256
digests as a job output. Nothing unpinned runs in that job. Everything that
installs third-party code from PyPI runs in a separate python-gates job on a
downloaded copy, and publish-pypi waits for both, then publishes the build
job's artifact only after its files match the recorded digests. In
python-gates the middleware lane runs the suite once per certified adapter family (langchain, openai,
anthropic, litellm), each in its own virtualenv with the SDK from this
checkout, and requires that family's adapter to run
(CAVEMAN_REQUIRED_ADAPTERS) instead of skipping. Wheel and sdist are each
installed into a fresh virtualenv and imported. python-gates uploads
nothing. The CycloneDX SBOM of pip install <wheel> (no extras) is written in
the build job instead, by the hash-pinned cyclonedx-py, into a virtualenv
filled with --only-binary :all:, so installing runs no third-party code
(no sdist builds). cyclonedx-py then starts that virtualenv's interpreter to
read its sys.path; the workflow first deletes every *.pth,
sitecustomize.py and usercustomize.py in it, so site startup cannot execute
code a dependency wheel shipped there. The SBOM comes from each package's
dist-info metadata; no dependency module is imported.
Experimental Python families are not in the release gate; they run in
middleware-python.yml. The TypeScript release lane runs every adapter family.
Outside releases, engine-ci.yml runs the TypeScript SDK and middleware suites
on Node 22 and 24, and the nightly middleware-canary.yml runs the TypeScript
suite against the latest release of every framework and opens an issue when it
fails.
contracts lane (contracts): a pnpm workspace package with no lockfile of
its own. It runs the schema validators, packs with pnpm, and checks that a fresh
npm install resolves the schemas and the OpenAPI document through the exports
map. It gets a GitHub Release from its CHANGELOG.md.
Dist-tags and prereleases
Only a stable version takes the npm latest dist-tag, and only when it is
semver-greater than the current latest (read from the registry at publish
time). A stable patch on an older line, say 1.1.1 after 1.2.0, publishes
under release-<major>.x instead. A prerelease publishes under its channel,
alpha, beta, or rc, and any other prerelease identifier under next. So
npm install <package> never resolves a prerelease once the package has a
stable version, and never moves backwards.
@caveman-ai/middleware@latest points at stable 1.0.1. Its earlier
0.1.0-alpha.2 publication remains available by exact version.
PyPI has no dist-tags: pip skips prereleases (0.1.0a1) unless the user pins
one or passes --pre.
GitHub Releases
Middleware, SDK, and contracts tags get a GitHub Release, created only after the registry publish succeeded:
- Notes are the package's
CHANGELOG.mdsection for that version (heading## <version> — <date>; the version is the heading's first word). A missing or empty section fails the build job, so the release stops before anything is published. Add the section in the release PR. - Attached: the CycloneDX SBOM (
<name>-<version>.cdx.json). The release job downloads the build job'sgithub-releaseartifact by exact name and requires its two files (notes.mdand that SBOM) to match digests the build job recorded as an output, so nothing uploaded later in the run can swap them. - Linked: the registry's provenance for the version (npm provenance, PyPI attestations from trusted publishing).
- Prereleases are marked as such, and no package release takes the repository's "Latest" badge; that belongs to the Caveman product release.
Changelogs: packages/sdk/typescript/CHANGELOG.md,
packages/sdk/python/CHANGELOG.md,
packages/middleware/typescript/CHANGELOG.md,
packages/middleware/python/CHANGELOG.md,
packages/shared/contracts/CHANGELOG.md, packages/pi-extension/CHANGELOG.md.
Changes land under ## Unreleased; the release PR renames that heading to
## <version> — <YYYY-MM-DD> (verify_repo.py requires the top heading of the
SDK, middleware, and contracts changelogs to be the package version).
Binary and container releases
release-binaries.yml builds the 36-binary matrix, requires it complete, and
signs checksums.txt with the pinned release key (checksums.txt.keysig). The
CLI and the npm launchers check that signature against the public key compiled
into them before installing any binary. The signed manifest also covers the
license files attached to every binary release: LICENSE (Apache-2.0),
LICENSE-MIT (the pre-3.0.0 MIT text), NOTICE, LICENSING.md, the third-party notices for the embedded pixel renderer,
its fonts, and caveman-browse, the notices for what zig cc links into the
cgo binaries (LICENSE.zig, COPYRIGHT.musl, COPYING.mingw-w64), and
THIRD_PARTY_GO_LICENSES.tar.gz: the license texts of every third-party Go
module the six binaries link on any platform, plus the Go runtime's, collected
with a pinned github.com/google/go-licenses/v2@v2.0.1.
caveman-proxy and caveman-engine are cgo builds, so they carry the
tree-sitter code compressor (TypeScript, JavaScript, Python, Rust, Java, C,
C++); scripts/build-release-binaries.mjs cross-compiles them with zig cc
(static musl on Linux, mingw-w64 on Windows, macOS 12 floor, no debug info so
two builds produce the same bytes) and refuses any zig but
RELEASE_ZIG_VERSION, which both workflows download by sha256. The other four
binaries stay pure Go, and so does the container image (Dockerfile), whose
code compressor therefore parses Go only; the release notes say so. The workflow runs the linux/amd64 engine's embedded evals
and requires the Linux cgo binaries (amd64 and arm64) to be static before
anything is signed. On every PR, engine-ci's release-shape job builds the
linux/amd64 set, runs the release-shape tests and evals, and checks that set is
static; the cross-build of all six targets runs only after merge. Locally:
ZIG=/path/to/zig node scripts/build-release-binaries.mjs --test builds the
host target, runs the TestReleaseShape* tests with the release flags, and runs
the built engine's evals.
Optional platform code signing runs when its secrets exist on the
binary-release environment and is skipped when they don't:
- macOS: Developer ID signing and notarization with
rcodesign(bare binaries cannot be stapled; Gatekeeper finds the ticket online). Secrets:APPLE_DEVELOPER_ID_P12_BASE64,APPLE_DEVELOPER_ID_P12_PASSWORD,APPLE_NOTARY_API_KEY_JSON(the App Store Connect API key JSON written byrcodesign encode-app-store-connect-api-key). - Windows: timestamped Authenticode with
osslsigncode. Secrets:WINDOWS_AUTHENTICODE_PFX_BASE64,WINDOWS_AUTHENTICODE_PFX_PASSWORD.
Signing rewrites the binaries, so the workflow recomputes checksums.txt before
the release key signs it.
From bin-v2.0.0 the signed manifest names its release: the workflow attaches
a RELEASE file holding the tag and lists its SHA-256 in checksums.txt like
any other file (so the npm launchers' strict parsers still accept it).
scripts/sign-binary-checksums.mjs takes the tag as its fourth argument and
refuses a manifest without the matching entry. A CLI pinned to bin-v2.0.0 or
later refuses a manifest that does not name its pinned release, and so do the
browse, MCP and shrink npm launchers (their shared installer requires the entry
unconditionally), so a release page serving an older signed manifest and
binaries fails the install. CLIs pinned to earlier runtimes accept a manifest
without the entry.
The container image carries OCI labels (org.opencontainers.image.source,
version, revision, licenses = Apache-2.0, title, description), ships the
license texts under /licenses/ (third-party Go modules under
/licenses/third_party/, from the same pinned go-licenses), has buildx SBOM and provenance attestations,
and is signed keyless with cosign through GitHub OIDC. The :latest tag moves
to an image only after it is signed and its GitHub Release exists, only for a
stable tag (no prerelease suffix) that is the highest stable bin-v* tag, so
an older-line patch or a prerelease leaves it alone; the workflow then checks
that :latest resolves to the signed digest. The gate ranks every bin-v*
tag in the repository, including tags whose release workflow failed, so a
pushed higher tag keeps :latest pinned where it is until that tag is
released or deleted. If the :latest step fails after gh release create
succeeded, point it by hand at the signed digest (after the cosign verify
below):
digest="$(docker buildx imagetools inspect ghcr.io/juliusbrussee/caveman-proxy:bin-vX.Y.Z --format '{{json .Manifest}}' | jq -r .digest)"
docker buildx imagetools create -t ghcr.io/juliusbrussee/caveman-proxy:latest "ghcr.io/juliusbrussee/caveman-proxy@$digest"
Binary releases never take the repository's "Latest" badge. Verify a release image with:
cosign verify ghcr.io/juliusbrussee/caveman-proxy:bin-vX.Y.Z \
--certificate-identity-regexp '^https://github.com/JuliusBrussee/caveman/.github/workflows/release-binaries.yml@refs/tags/bin-v' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
Images released before this step are unsigned.
Mirroring the binaries
caveman setup --install and the caveman-mcp, caveman-shrink, and
caveman-browse npm launchers download binaries from
https://github.com/JuliusBrussee/caveman/releases/download unless
CAVE_BINARY_RELEASE_BASE names another base URL. A mirror must serve the
release layout unchanged:
$CAVE_BINARY_RELEASE_BASE/<bin-vX.Y.Z>/checksums.txt
$CAVE_BINARY_RELEASE_BASE/<bin-vX.Y.Z>/checksums.txt.keysig
$CAVE_BINARY_RELEASE_BASE/<bin-vX.Y.Z>/<binary>_<darwin|linux|win32>_<amd64|arm64>
The release tag is the one pinned in the installed CLI or launcher, not one the
mirror chooses. The signature and every SHA-256 are still checked against the
compiled-in public key, so a mirror can serve the bytes but cannot swap them.
CAVE_SETUP_TIMEOUT (seconds, default 300) bounds each download for slow
mirrors. To fill a mirror, copy the pinned GitHub Release's assets as they are,
for example
gh release download <bin-vX.Y.Z> --repo JuliusBrussee/caveman --dir <mirror>/<bin-vX.Y.Z>.
Repository settings the release path depends on
These live in GitHub settings, not in this repository, and only the owner can
change them. State checked with gh api on 2026-09-23.
Required, and in place:
- Environments
npm,pypi, andbinary-releaseeach require approval fromJuliusBrusseeand accept deployments only from their own release tag patterns (npmtoday:sdk-ts-v*,middleware-ts-v*,pi-v*, plus the staleagent-v*andcreate-agent-v*;pypi:sdk-python-v*,middleware-python-v*;binary-release:bin-v*). Thenpmpolicy still needscontracts-v*andcli-v*added and the two agent patterns removed (the Agent SDK no longer releases from this repository). binary-releaseholdsCAVEMAN_BINARY_SIGNING_PRIVATE_KEY_PEM, matchingpackages/cli/BINARY_SIGNING_PUBKEY.pub.- Private vulnerability reporting is on.
Required, and not in place yet. The two ruleset items block the 3.0.0 release; turn on immutable releases before it too:
mainrulesets.mainis unprotected and has no rulesets, yet every release gate trustsgit merge-base --is-ancestor <tag> origin/main. Anyone who can push tomaincan rewrite what that check accepts. Create two rulesets on the default branch: one that blocks force-pushes and deletion with no bypass, and one that requires a pull request and the required status checks, with a repository-admin bypass for the maintainer.sync-skill.ymlpushes its mirror commit straight tomainasgithub-actions[bot], so the pull-request ruleset needs a bypass for the GitHub Actions app, or that workflow has to stop pushing (see the note below).- Release-tag ruleset. Tags
v*and*-v*can be moved or deleted today. A ruleset on those patterns should block update and deletion, and restrict creation to the maintainer. The installer pinsv3.0.0and the hook checksum manifest comes from that same tag, so a moved tag would change what a detached install downloads. - Immutable releases (Settings, General, Releases). Off today; every
release reports
immutable: false. With it on, a published release's assets and tag cannot change. - Dependabot alerts and Dependabot security updates (Settings, Code security).
Both are off;
.github/dependabot.ymlonly configures version updates. - CodeQL default setup must stay off (it is today):
codeql.ymlis the advanced setup, and GitHub refuses advanced-setup uploads while default setup is on.
sync-skill.yml note: bypassing the pull-request rule for the GitHub Actions
app lets any workflow holding contents: write push to main, not only the
sync job. The narrower alternative is to stop auto-pushing: commit the mirrors
in the pull request itself and have CI fail when they drift.
Recommended:
- The Apple and Windows signing secrets above, once the certificates exist.
Registry side, per package (identity fields are case-sensitive): npm trusted
publisher = owner JuliusBrussee, repository caveman, workflow
release-packages.yml, environment npm; PyPI trusted publisher = the same
owner, repository, and workflow with environment pypi. @caveman-ai/cli
exists on npm (hand-published so far) and needs this trusted publisher added
before cli-v2.0.0. After the first trusted publish, set each npm package's
publishing access to require 2FA and disallow tokens. References:
npm trusted publishing and
PyPI OIDC from GitHub.
The Python SDK's import name stays caveman_cloud.
First publish of a new npm package (@caveman-ai/contracts)
npm can only attach a trusted publisher to a package that already exists, and it does not yet allow a first publish through OIDC (npm/cli#8544, open). So a brand-new package needs one manual publish first. Manual, owner-only, once per new package. Publish a placeholder, not the real version, so the real release still comes from CI with provenance:
dir="$(mktemp -d)" && cd "$dir"
cat > package.json <<'EOF'
{"name":"@caveman-ai/contracts","version":"0.0.0-bootstrap.0","description":"Name placeholder for trusted publishing. Use 2.0.0 or later.","license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/JuliusBrussee/caveman.git","directory":"packages/shared/contracts"}}
EOF
npm login # interactive, 2FA; no token is stored in CI
npm publish --access public --tag bootstrap
Then, on npmjs.com, add the trusted publisher above to @caveman-ai/contracts,
push contracts-v2.0.0, and once it is live run
npm deprecate @caveman-ai/contracts@0.0.0-bootstrap.0 "placeholder; use 2.0.0 or later".
The publish job's dist-tag rule gives 2.0.0 latest, whether or not the
registry had pointed latest at the placeholder.
Caveman 3.0.0 release runbook
The feat/middleware-enterprise branch is the 3.0.0 release: the Apache-2.0
relicense plus stable middleware. Its last commit is the release commit. Run
these in order from a clean, up-to-date main checkout with a signing key
configured for git tag -s (every release workflow refuses unsigned,
lightweight, or off-main tags). Each git tag is followed by
git push origin <tag>.
Why this order. The binaries come first because everything else names
them: packages/cli/BINARY_RELEASE and the npm launchers already pin
bin-v2.0.0, and deploy/* names its image. The SDKs publish before the
middleware because middleware 1.0.0 needs SDK 1.2.0 (^1.2.0 /
caveman-sdk>=1.2,<2); published first, a middleware install would fail to
resolve. v3.0.0 goes out right after the merge because the README and
INSTALL one-liners (raw.githubusercontent.com/.../v3.0.0/install.sh) 404
until it exists, and the installer at that ref needs nothing unpublished.
-
Merge the branch to
main(merge commit or fast-forward; the release commit must stay intact). Wait forsync-skill.ymlto push its[skip ci]commit, thengit pull. -
Product tag, immediately.
git tag -s v3.0.0 -m "Caveman 3.0.0" git push origin v3.0.0 gh release create v3.0.0 --verify-tag --latest --title "Caveman 3.0.0" --notes-file <notes.md>The one-liners now resolve.
deploy/*at this tag still carries@sha256:REPLACE_AT_RELEASE, so those manifests fail to pull (by design) until step 4; the docs point readers atmain. -
Runtime binaries.
release-binaries.ymlchecks thatpackages/cli/BINARY_RELEASEalready equals the tag (it does):node packages/cli/scripts/gen-binaries.mjs --check git tag -s bin-v2.0.0 -m "bin-v2.0.0" git push origin bin-v2.0.0 gh run watch "$(gh run list --workflow release-binaries.yml --limit 1 --json databaseId --jq '.[0].databaseId')"Approve the
binary-releaseenvironment. Then, without any GitHub credential, confirm anonymous HTTP 200 for all 36 binaries,checksums.txt,checksums.txt.keysigandTHIRD_PARTY_GO_LICENSES.tar.gz(packages/cli/PUBLISHING.md, step 3). -
Pin the image digest in a small PR to
main(no script exists; this is the whole change):digest="$(docker buildx imagetools inspect ghcr.io/juliusbrussee/caveman-proxy:bin-v2.0.0 --format '{{json .Manifest}}' | jq -r .digest)" cosign verify "ghcr.io/juliusbrussee/caveman-proxy@$digest" \ --certificate-identity-regexp '^https://github.com/JuliusBrussee/caveman/.github/workflows/release-binaries.yml@refs/tags/bin-v' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com sed -i.bak "s/sha256:REPLACE_AT_RELEASE/${digest}/" deploy/kubernetes.yaml deploy/kubernetes-ha.yaml deploy/aws-ecs-task-definition.json rm deploy/*.bak git grep -n REPLACE_AT_RELEASE -- deploy # must print nothingThe CLI and launcher pins (
packages/cli/BINARY_RELEASE, generated bynode packages/cli/scripts/gen-binaries.mjsandnode packages/cli/scripts/gen-wedge-installer.mjs) already namebin-v2.0.0in the release commit; re-run both with--check/git diff --exit-codeto prove nothing drifted. -
SDKs, and wait for both to publish (approve
npmandpypi):git tag -s sdk-ts-v1.2.0 -m "@caveman-ai/sdk 1.2.0" && git push origin sdk-ts-v1.2.0 git tag -s sdk-python-v1.2.0 -m "caveman-sdk 1.2.0" && git push origin sdk-python-v1.2.0 npm view @caveman-ai/sdk@1.2.0 version # 1.2.0 pip index versions caveman-sdk # lists 1.2.0 -
Packages (each is its own
release-packages.ymlrun; approve each):for tag in middleware-ts-v1.0.0 middleware-python-v1.0.0 contracts-v2.0.0 pi-v0.2.0; do git tag -s "$tag" -m "$tag" && git push origin "$tag" donecontracts-v2.0.0needscontracts-v*in thenpmenvironment's tag policy and the one-time bootstrap in First publish of a new npm package. Then the CLI. Its lane refuses to build until everybin-v2.0.0asset downloads, and needscli-v*in thenpmtag policy plus the CLI's trusted publisher:node agents/probe-installed.mjs --all --json # release-machine check git tag -s cli-v2.0.0 -m "@caveman-ai/cli 2.0.0" && git push origin cli-v2.0.0 -
Environment approvals. Every run above waits on JuliusBrussee:
binary-releaseonce (step 3),npmforsdk-ts,middleware-ts,contracts,pi,cli, andpypiforsdk-python,middleware-python. A rejected or failed run publishes nothing; fix forward with a new version, never re-tag. -
Post-release checks.
npm view @caveman-ai/middleware dist-tagsshowslatest: 1.0.0(moved off0.1.0-alpha.2);@caveman-ai/sdklatest: 1.2.0;@caveman-ai/contractslatest: 2.0.0;@caveman-ai/pilatest: 0.2.0;@caveman-ai/clilatest: 2.0.0, each with provenance on its npm page.pip install 'caveman-middleware[langchain]==1.0.0'in a fresh venv pullscaveman-sdk1.2.0, andpython -c "import caveman_middleware; print(caveman_middleware.__version__)"prints1.0.0.- Each package's GitHub Release exists with its SBOM, and none took
"Latest" (that is
v3.0.0). node tests/middleware-e2e/skew.mjspasses against the published clients; setN1_RUNTIMEthere tobin-v2.0.0only when the next runtime release makes it N-1.- The README one-liners install from
v3.0.0on macOS, Linux and Windows;caveman setup --installfetchesbin-v2.0.0; the image runs from its pinned digest (docker run --rm ghcr.io/juliusbrussee/caveman-proxy@<digest> versionprintsbin-v2.0.0) and lists/licenses/third_party. - Then the checks in Post-publish proof.
User-owned before or during the release (GitHub settings and other repos; agents cannot change these):
- The
mainrulesets, the release-tag ruleset, and immutable releases (settings above). - Dependabot alerts and security updates.
- The Apple and Windows signing secrets, if platform-signed binaries are wanted for this release (the workflow skips signing without them).
- In the
npmenvironment's deployment tag policy: addcontracts-v*andcli-v*, removeagent-v*andcreate-agent-v*. - npm trusted publishers for
@caveman-ai/cliand (after the bootstrap publish)@caveman-ai/contracts. - The docs.caveman.so pages: middleware 1.0 / SDK 1.2 install lines, the Apache-2.0 license, and the pages the package READMEs link.
- The same Apache-2.0 relicense in
caveman-browse(source ofbrowse/; otherwise the next sync reverts it) and in the Agent SDK's own repository.
Operator migration notes (put these in the bin-v2.0.0 and v3.0.0
release notes):
- Principal names carry their source. OIDC principals are now
oidc:<issuer>#<claim>and certificate principalsmtls:{uri,dns,cn}:<name>. Rename token-map entries that configure them, and expect sessions such principals created before the upgrade to be unreachable (404). Steps: deploy.md, Identity. - Python log prefix. The middleware warn-once line now reads
Caveman middleware passed content through unchanged: adapter=… reason=…, the same as TypeScript. Log filters matching the oldCaveman middleware decision:prefix need updating. - First 1.1 deploy resets persisted choices once, because scope ids
changed: the first call per session after the upgrade compresses from
scratch. Grants a 1.0 runtime issued keep reading from the shared recovery
store until they expire, and
sessions/deletereportsoriginals_deleted: falsefor sessions that hold them.
Post-publish proof
Do not flip public install commands until each registry endpoint resolves to this project from clean environments. Prove exact version, package owner/repository, fresh install, and import. Provider spend is outside release workflow.
For the CLI, dispatch release-smoke.yml with the exact published cli-version
and expected binary-tag. Its macOS ARM, Linux x64 and Windows x64 jobs run
.blocks/release-smoke.py in temporary homes without inherited credentials.
Each job installs the registry package, verifies its version and binary pin,
installs the signed companions, checks record-mode wrapping against the published
proxy, compresses and retrieves byte-exact content, then uninstalls the npm
package while preserving an unrelated configuration file. The block also runs
locally with --version, --binary-tag and --repo; it cleans up its proxy and home.
If smoke fails, deprecate affected npm version or yank PyPI release, remove public install command, fix forward with new version, and preserve failed artifact and workflow logs. Never overwrite a published version.