Repository navigation
The first tagged release since 0.2.0. package.json carried 0.2.1 to 0.2.5
during this cycle without tags or changelog sections; those changes are
included here.
Added
- Since 0.2.0, before this changelog existed: same-case review
(--prove rail), which verifies the act stage's rail bundle with the rail's
own verifier and records a MandateBound review of the same bytes (#20);
portableaas exportand offlineaas replay(#27);aas runsand
aas prune(#32);aas casesandaas compare(#35);aas inspect,
aas verify, andaas latestfor saved cases, and--rootfor an
alternate case store (#40, #41); the inventory allocation domain,
--domain inventory(#36); and Python 3.11+ interpreter selection with
AAS_PYTHON(#16). - Since 0.2.0, the local GUI grew into a case workbench: same-case review,
imported-case replay, guided scenarios, paged and searchable case history,
comparison, and saved-case inspection, verification, and Markdown review
downloads (#26, #34, #35, #39, #40). The integrator examples
review-handoff(#28) andconnector-conformance(#37) and the Playwright
real-browser tests (#34) arrived in the same period. aas --version(alsoaas version, with--json) prints the orchestrator
version frompackage.json. New runs record it asstack_versionin
report.jsonandmanifest.json;aas cases --json,aas inspect, and
the Markdown case review show it ("Orchestrator version"); and the GUI's
GET /api/healthreturns it asversion. The field is additive within the
agent-action-stack.run/v1schema: cases saved before it was added load
and verify unchanged and report it as unavailable. It is informational and
is not one of the fieldsaas comparecompares..github/workflows/release.ymlpublishes a GitHub Release when avX.Y.Z
tag is pushed. It re-runsnpm run checkand the integration proof on the
tagged commit, requires the tag to matchpackage.json, the lock, and this
changelog, and uses the version's changelog section as the release notes.
Only that job can write, and only to create the release;ci.ymlstays
read-only.package.jsonis now"private": true: the package works from a
checkout, not as an installed dependency (it clones intodeps/beside
itself), sonpm publishnow refuses it.npm linkand theaasbin are
unaffected.npm run check:version(scripts/check-version.mjs), part of
npm run checkand CI's test job. It fails whenpackage-lock.json, this
changelog, thedocs/release-readiness.mdpin list, or a--tagdisagree
with thepackage.jsonversion, and--notesextracts one release's
section. It found and this change repairs two drifts: the lock still said
0.2.0 after five patch bumps, and[Unreleased]carried three separate
### Changedheadings and acompare/main...HEADlink that compared
nothing.CONTRIBUTING.mdnow describes the versioning and changelog
rules.CONTRIBUTING.mddescribing cross-repo coordination and the
stack-lock.jsonpolicy.CHANGELOG.md(this file).- Playwright setup note in the Quick start section of
README.md.
Changed
- BREAKING: requires Node.js 22.12.0 or newer (was 20 in 0.2.0); see #18.
Bootstrap,aas demo,aas replay,aas verify, and GUI runs refuse an
older runtime before any side effect. Python stays at 3.11 or newer. - The three component pins were refreshed to reviewed revisions, with a
pinned legacy replay fixture (fixtures/legacy-rail-review.json) and a
component compatibility test in the integration proof (9449bd3, #87). The
current pins are instack-lock.jsonanddocs/release-readiness.md. - The pins then moved to the component releases of 2026-10-09, each at its
release merge commit: Constitutional Agent Testbench 0.6.0 (ed46f0c),
Consequence Rail 0.3.0 (78d6f8a), and MandateBound 2.0.0 (32d5256). No
entrypoint, install, or build hook changed, and the orchestrator needed no
code change: MandateBound 2.0.0's breaking changes are in commands and APIs
the stack does not call, and itssimulateandreviewbehave as before.
Cases saved under the previous pins still replay and verify. The component
compatibility test now also replays
fixtures/previous-pins-inventory-review.json, an inventory case saved under
the previous pins, against the new Rail verification checks. npm run checknow runs the unit suite and a syntax gate,
npm run check:syntax, which parses every modulegit ls-filesreports
withnode --check. CI's test job runs the same gate in place of a
hand-written list of nine files, which had already drifted once. A new
.gitattributespins LF line endings, so a Windows checkout no longer
turns every tracked file into a CRLF modification.- CI now tests the documented Python 3.11 floor: the integration matrix adds
a fifth leg on Ubuntu with Node.js 22.12.0 and Python 3.11, next to the four
Python 3.13 legs. The pinned workflow actions move to their current
releases (checkout v7.0.1, setup-node v7.0.0, setup-python v7.0.0), still
pinned by commit SHA, and.github/dependabot.ymlproposes weekly grouped
updates for GitHub Actions and npm. A test fails if any workflow step uses
an action without a full commit SHA and version comment. The browser job's
install step no longer claims a cache it does not have. aas helpnow documents the whole CLI surface. The usage block listed
neitheraas runsnoraas prunealthough both are dispatched, the command
list omittedhelp, and the option reference omitted nine accepted flags:
--out,--overwrite,--before,--limit,--outcome,--search,
--keep,--dry-run, and--markdown.aas cases --searchno longer accepts a value that begins with-.
aas cases --search --jsonused to consume--jsonas the search term and
exit 0 with an unfiltered listing, so a mistyped or reordered flag silently
changed the result set. It now reports a missing value and exits 2, matching
every other value-taking option in the CLI. Theaas casesusage message
also lists the--domain,--outcome, and--searchfilters it accepts.loadComponentLocknow rejects aninstallorbuildvalue other than the
supported tokens (npm-ci,npm-run-build). Those fields are dispatched by
exact string match, so an unrecognised value was skipped silently and
npm run bootstrapstill reported success for a component that had never
been installed or built. The shippedstack-lock.jsonis unchanged.npm run gui:smokenow requests/api/healthand the workbench page over
loopback HTTP and asserts the response status, thetext/htmlcontent type,
the content security policy header, and the presence of every embedded page
helper. It previously started the server, closed it, and asserted nothing, so
it passed even when the page renderer or a route was broken. The rendered
helper list is now a singlePAGE_HELPERSconstant shared byrenderPage
and the smoke check.- The connector conformance example now runs rules 2 to 6 on both synthetic
connectors and measures the effect itself: active refunds bound to the
action, and inventory on hand against the connector's baseline. It used to
touch the refund connector only for capability advertisement and checked
only call counts and equal return values, so it could not see a second
effect or an incomplete reversal. The refund remedy check covers the
duplicate fault (only the duplicate is voided, the primary stays active)
and a clean refund (no_change). - Documentation now matches the code.
aas helpand the export and prune
usage errors list the--rootoption both commands accept, the--root
reference names every command that takes it, and the flow line says
consequence-rail demo <domain>rather than alwaysrefund.
docs/stack-lock.mdnow says a lock mismatch stops the run before any
stage and writes no bundle; it claimed the mismatch was recorded in the
manifest. The examples README uses the directnodeinvocation CI uses and
says the conformance example drives rail modules in-process. The pull
request template drops issue-form front matter that rendered as text, and
its checklist now followsCONTRIBUTING.mdinstead of forbidding the
changelog and CI edits this repository requires.
Fixed
aas,aas-gui, andbootstrapnow run when launched through a link: an
npm bin symlink,npm link, a macOS /tmp checkout, or a Windows junction.
The entrypoint guard compared the realpathed module URL with the unresolved
process.argv[1], so a linked launch exited 0 without doing anything, and
npm run gui:smokethrough a link reported success without checking. The
six scripts with a#!/usr/bin/env nodeline are now tracked as
executable, so./bin/aas.mjsruns on a POSIX checkout.aas demovalidates its options before it checks the runtime. With a broken
AAS_PYTHON, a usage error such asaas demo --bogusexited 1 with the
interpreter message, after up to seven interpreter probes, instead of the
documented exit 2.parseDemoOptionsnow runs first, andrunDemouses
the same parser, so the messages are unchanged.aas export,verify,inspect, andlatestnow report a missing saved
case asSaved case not found: <run-id>, and a missing or unreadable file
inside a case by its case-relative name. They printed the rawENOENT
error, including the absolute local path. The error code staysENOENT,
so the GUI's 404 responses are unchanged.- After an act-stage error,
report.jsonnow records prove as skipped with
reasonact_error, matchingmanifest.json. The report kept its initial
not_reached, so the GUI summary and the saved-case review disagreed about
the same run. The GUI summary also labels theact_failedskip reason
("act failed") instead of printing the raw token. - The workbench now reports run progress and failures in a visible status line
under Run stack (#run-status, announced by screen readers): running,
run not started (including the busy 503 and thenpm run bootstraphint
whendeps/is missing), request failed, finished with its flow, finished
with a stage failure, and finished without a bundle. Those messages used
to go only to the raw report inside a collapsed<details>, so a click on
Run stack before bootstrapping appeared to do nothing. The raw report
now holds only the run JSON. npm run guinow exits 1 withCannot listen on 127.0.0.1:<port> (EADDRINUSE). Set AAS_GUI_PORT to a free loopback port.when the port is
taken. It crashed with an unhandled'error'event and a stack trace.GET /api/historynow answers a refused case store (arunspath that is
not a regular directory) with HTTP 422, as/api/comparealready did, and
a worker failure with HTTP 500History could not be loaded.instead of
the genericRequest failed. Its busy 503 now sendsRetry-After: 1, like
the run and replay routes.npm run bootstrapnow clones each missing component into a hidden staging
directory and moves it intodeps/<component>only after checkout. A failed
or interrupted first fetch used to leave a.gitwith an origin and no
HEADin the final directory, and every later bootstrap refused it with no
way forward except deleting it by hand. A failure now removes the staging
directory and says nothing was left; the refusal for an older
half-initialized checkout names the directory to remove; a failed command
now includes the tool's own error text; and bootstrap errors print
bootstrap failed: <reason>instead of a stack trace.- The review-handoff example now gates refunds on the stack's own policy,
fixtures/policy.json(aas-refund-gate-v1, 6 rules), with the stack's
response fixtures. It used the testbench checkout's 5-rule example policy,
so step 1 did not demonstrate the gate thataas demoin step 6 applies.
The example now fails if step 1 or the orchestrated run reports a policy
other than the domain's gate. - Saved-case review now includes persisted stage stderr.
aas inspectand
the Markdown review omitted the diagnostic that the run report already
stored, so a failed act looked like it had no child output. runActnow rejects a domain other thanrefundorinventorybefore it
spawns the rail CLI. Any other value was passed through as the demo
command.- An act result that explicitly reports
fault: nullis stored as null. The
report used to replace that null with the requested fault, so a cleared
fault was recorded asduplicatewhen--fault duplicatewas passed. - The integrator example now removes its scratch directory when decide fails
or policy refuses the response.process.exitskipped the cleanup, so each
failed run left anaas-integrator-directory behind. aas inspect --rootnow rejects a blank path, matching the other saved-case
commands. A whitespace root used to be treated as a directory and failed
with a filesystem error.- The act stage now reads the rail bundle file with the same byte cap and
symlink refusal as a saved case. The previous read followed a link and
accepted a file larger than the child stdout cap. - Bootstrap and provenance now refuse a
depsdirectory that is a symlink.
Checkout and later reads followed the link, so a planteddepslink was
accepted as the component root. - Saved-case GUI paths with malformed percent-encoding now return HTTP 400.
decodeURIComponentused to throw, and the request became HTTP 500. - The workbench comparison panel now says the comparison is unavailable when
the pair is not comparable. It previously said no compared field differs,
which disagreed with the Markdown download for the same result. - Comparison and case summaries now refuse a
runsdirectory that is a
symlink, matching list, export, and prune. The comparison path used to
follow the link and read the target tree. The GUI compare route returns
HTTP 422 for that refusal. - Verify saved case now returns HTTP 422 when the saved case is structurally
invalid, the same distinction inspect already makes with 404 and 422. The
route used to report that failure as HTTP 500. - Bootstrap now refuses a pre-existing dependency directory that is a symlink.
existsSyncfollows the link, so a symlink whose git metadata matched the
lock was accepted as the component checkout. - Listing, export, and prune now refuse a
runsdirectory that is a symlink.
Those paths followed the link, so prune deleted runs in the target tree. runCaptureno longer lets caller options enable a shell or replace the
stdout cap. Those fields were applied before...opts, soshell: trueor
a largermaxBufferoverrode the limits. Timeout remains caller-set.runDecidenow rejects a domain other thanrefundorinventory. Any
other value used to load the refund policy and could report a pass for the
wrong gate.GET /api/comparenow setsokto false when the pair is not comparable,
matchingaas compare --json. It previously reportedok: truefor a
missing or unreadable pair. The HTTP status stays 200 so the workbench can
still render the classification.- The GUI import endpoint now requires one JSON document. It used the child
stdout parser, so a log line followed by an object was accepted and replayed. persistRunBundlenow uses the same run-id rule as readers. The old check
accepted.,.., and..., andRegExp.teststringified non-strings, so
a run id of..resolved outsideruns/.npm testnow runs every declared test. The default process-isolation
runner carries test events on stdout, and this suite also writes captured
CLI output there, so the parent only reported the last few dozen tests and
turned any earlier failure into a nameless "test failed". In-process
reporting keeps the full count and the failing test's name.- A structured act failure now keeps the child stderr on the run report and in
the manifest. Decide and prove already did; act returned the text and then
dropped it, soreport.json,aashuman output, and later case summaries
showed a failed act with no diagnostic. - A prove-stage error no longer wipes the act result from the run report. The
error path rebuiltreport.stages.actfrom the internal stage record, so
outcome,state,fault,action_id,assurance_mode, and
bundle_verificationdisappeared fromreport.json, from the human output,
and fromaas runs/aas cases/aas comparesummaries for any run whose
proof failed. Only the raw child payload is stripped now.
Security
- Every CI checkout now sets
persist-credentials: false. The integration and
browser jobs runnpm ciand builds inside the pinned components, and the
job token used to stay in.git/configwhile that code ran. - The workbench content security policy no longer allows
'unsafe-inline'
script or style. The page's one script and one stylesheet are static per
process, soscript-srcandstyle-srcnow pin their SHA-256 hashes, and
the policy addsobject-src 'none'. Imported and saved case data still
reaches the page only throughescapeHtml; an injected inline script would
now also be refused by the browser. The script is hashed after CRLF is
normalized to LF, so a Windows checkout serves a matching hash, and
npm run gui:smokechecks the served policy against the page content.