stack-lock.json is the single source of truth for which versions of the
three sibling libraries the orchestrator is allowed to compose. Schema:
agent-action-stack.lock/v1. It pins each sibling's public URL, exact
reviewed commit, expected entrypoints, and where applicable the build hooks.
One record per sibling. The current lock has three:
constitutional-agent-testbench(decide). Public URL, one reviewed commit. Expected entrypointspyproject.tomlandsrc/constitutional_agent_testbench/cli.py. No install or build step.consequence-rail(act). Public URL, one reviewed commit. Expected entrypointspackage.jsonandcmd/crctl.js. No install or build step.mandatebound(prove). Public URL, one reviewed commit. Expected entrypointspackage.json,package-lock.json,src/cli.ts. Post-build entrypointdist/cli.js;installhooknpm-ci;buildhooknpm-run-build. Bootstrap runsnpm ci --ignore-scriptsthen the build. The lock also pins, by construction, the public-only origin: every record points at the public EauDoon GitHub repo. Substituting a private URL or editingdeps/directly is out of scope.
scripts/bootstrap.mjs loads the lock, asserts the full-stack Node version,
and prepares each dependency under deps/:
- If
deps/<component>is present,inspectDependencyDirectoryverifies it is a regular directory, detached (HEADmatches the pinned commit exactly), clean, and contains every expected entrypoint. Substituted or dirty pre-existing directories are rejected. - If absent, bootstrap clones the public URL at the pinned commit, checks out detached, and verifies the entrypoints.
- For components with an
installhook, bootstrap runs the declared step (npm-ci, which runsnpm ci --ignore-scripts, for MandateBound). - For components with a
buildhook, bootstrap runs the declared step (npm-run-buildfor MandateBound), then verifies post-build entrypoints. Both hook fields accept only those exact tokens;loadComponentLockrejects any other value rather than skipping the step, because a silently ignored hook would report a successful bootstrap for an unprepared component. The orchestrator records each component's provenance on every run bundle. A run resolving components whose checkout disagrees with the lock surfaces the disagreement in its manifest.
Changing a pinned component is a coordinated cross-repo change. Do not bump the lock on its own.
- Land the upstream change in the sibling repo first; it must pass that sibling's own CI and review.
- Bump the matching
commitfield to the reviewed merge SHA. - Update
expected_entrypoints,post_build_entrypoints,install, orbuildonly if the sibling's published contract actually changed. - Run
npm run integrationlocally on Ubuntu and Windows. The CI integration job runs the same proof on every push and pull request. Security fixes follow the same path. The schema version (schema_version) is bumped only when the shape changes in a way that requires loader changes; existing tools keep reading older versions until the bump lands across all consumers.
The integration proof also runs test/component-compatibility.test.mjs against
the prepared components. It checks canonical bytes independently of producer
round trips, both currency validation boundaries, and refusal of re-signed
synthetic evidence whose currency contradicts the proposal. The historical
fixtures/legacy-rail-review.json was exported using Rail 6c61e9f and
MandateBound e526c4c; it pins replay compatibility for an ordinary synthetic
refund. Its public demonstration signatures establish no real-world provenance.
The current Rail pin also binds a receipt's close time to its terminal event, so a clock advancing between reads still produces evidence that survives the same-case handoff and replay. The MandateBound pin rejects weak Ed25519 keys in caller-pinned CasePack checkpoint trust snapshots using its existing strict key validator. Ordinary valid evidence retains the same format; neither update rewrites old artifacts. The testbench runtime pin is unchanged.
An older artifact with numeric-looking object keys may contain signatures made with the former Rail canonical ordering. The current verifier does not try that obsolete ordering after verification fails. Preserve the original artifact and its recorded producer revision for historical inspection; do not rewrite its signatures or describe a newly generated case as the same evidence. A successful legacy fixture replay establishes compatibility for that fixture, not every previously accepted artifact or an alternate canonical profile.
The MandateBound pin also corrects U+2028/U+2029 bytes under its existing RFC8785 profile. Legacy proofs containing those separators can fail current integrity checks. Keep original bytes and the exact producer commit for historical replay; version labels alone do not identify the affected behavior. There is no alternate-byte verification or automatic migration. Follow the producer's compatibility guidance before reissuing affected artifacts.
The lock is owned by the Agent Action Stack maintainers. Updates land via
pull request on imp/<short-topic>-<date> branches. PRs that change
stack-lock.json must cite the sibling repo, PR, and reviewed merge SHA;
show a passing integration job on both Ubuntu and Windows; and show a
passing test job (lock-load and dependency helpers live in the unit
suite).
Do not bypass the lockfile. Editing deps/ directly, swapping a remote
URL, or relaxing the dirty-checkout rejection are out of scope. A PR that
needs any of those should propose a permanent fix in scripts/bootstrap.mjs
or in this policy document.
Real connectors, real secrets, real payment or merchant integrations; changes that would read or write private repositories; policy or rail rules that should live in their owning library. The lock pins reviewable public artifacts. Anything else belongs in the sibling that owns it.