Repository navigation
Release 0.3.0: release engineering, GUI and bootstrap hardening, aas --version - #88
Merged
Merged
Conversation
The entrypoint guard compared the realpathed import.meta.url with the unresolved process.argv[1], so aas, aas-gui and bootstrap exited 0 without running when reached through an npm bin symlink, npm link, a macOS /tmp checkout or a Windows junction. gui:smoke through a link reported success without checking anything. Add isEntrypoint() to scripts/bootstrap.mjs, which compares both paths with realpathSync.native, and use it in all three guards. Track the six shebang scripts as executable.
CI's syntax step was a hand-written list of nine node --check calls that had already drifted once (#59). Replace it with scripts/check-syntax.mjs, which parses every module git ls-files reports and falls back to a tree walk outside a checkout. npm run check now runs the unit suite and the gate, and CI's test job calls npm run check:syntax. Add .gitattributes with text=auto eol=lf so Windows checkouts stay LF; every tracked file was already LF in the index, so renormalizing staged nothing.
- Every checkout sets persist-credentials: false; the integration and browser jobs run npm ci and builds inside the pinned components. - Repin checkout v7.0.1, setup-node v7.0.0 and setup-python v7.0.0 by SHA (v7.1.0 of setup-node is one day old and is left for dependabot). - The integration matrix gains a fifth leg on Ubuntu with Node.js 22.12.0 and Python 3.11, the documented decide-stage floor. - Add a weekly grouped dependabot config for actions and npm. - Drop 'cached' from the Chromium step name; there is no cache. - A test now fails on any unpinned action, an action pinned to two SHAs, or a checkout that keeps credentials.
package.json, package-lock.json and CHANGELOG.md had drifted: the lock still said 0.2.0 after the 0.2.1 to 0.2.5 bumps, [Unreleased] carried three separate ### Changed headings, and its only link compared main with HEAD. scripts/check-version.mjs treats package.json as the source of truth and checks the lock, the Keep a Changelog structure (one [Unreleased] first, released sections dated and strictly descending, one heading per category, tag-based links), the release-readiness pin list against stack-lock.json, and with --tag that the tag matches. --notes extracts one release section for the release workflow. It runs in npm run check and in CI's test job. Repair the drift in the same change: set both lock version fields to 0.2.5, merge the Changed blocks without rewording them, and point [Unreleased] at the 0.2.0 release commit.
main probed Node.js and up to seven Python candidates before runDemo parsed its options, so with a broken AAS_PYTHON a demo usage error exited 1 with the interpreter message instead of the documented 2. Extract parseDemoOptions, call it first in main's demo branch, and reuse it in runDemo so the messages stay the same. Saved-case reads printed raw ENOENT errors that carried the absolute local path. readRunBundleBody now reports 'Saved case not found: <id>' or names the missing case-relative file, keeping the error code so the GUI's 404 mapping is unchanged.
…error The runDemo catch block set stages.prove to act_error but left report.stages.prove at its initial not_reached, and finalize takes the reason from the report. The saved report and manifest therefore disagreed about why prove was skipped. Set report.stages.prove alongside stages.prove; the prove-error branch and its act-field preservation are unchanged. The GUI stage headline also gains a label for act_failed, which it printed as a raw token.
The run handler wrote 'Running...', request failures, the 503 busy message and the missing-deps bootstrap hint only to #output, which sits inside a collapsed <details>, while the aria-live summary was cleared. A first run before bootstrapping looked like a dead button. Add a role=status line under Run stack and write every progress, refusal and completion message there, keeping the existing latest-run stale guards. #output now holds only the raw run JSON. Unit stubs and the Playwright refusal test assert the status text.
startGui listened with no 'error' listener, so a taken port crashed npm run gui with an unhandled event. Attach a one-shot listener and reject with a message that names AAS_GUI_PORT; main's catch prints it and exits 1. /api/history had no catch, so a refused runs directory became the generic 500 'Request failed' while /api/compare answered 422 for the same store. Share a storeErrorStatus helper, answer other worker failures with 'History could not be loaded.', and send Retry-After with the history 503. createGuiServer takes an injectable historyTask so tests can hold a page open. Correct the stale comment on missing versus wrong Origin.
The workbench renders imported and saved case data through innerHTML, so escaping was the only XSS control while the CSP allowed any inline script and style. The page's script and stylesheet are static per process. Build PAGE_STYLE and PAGE_SCRIPT once at module scope, with CRLF normalized to LF because fn.toString() returns CRLF on a CRLF checkout while the browser hashes the parsed text, and serve PAGE_CSP with their sha256 hashes plus object-src 'none'. The rendered page is byte-identical to before. gui:smoke now checks the served policy and the normalized helper sources; a unit test recomputes both hashes from the page, and a browser test confirms the stylesheet applies and the script runs with no policy violation.
cloneAtCommit ran git init and remote add in the final deps/<name> and then fetched. A failed or interrupted fetch left .git with an origin and no HEAD, and every later bootstrap refused it as an unusable dependency with no recovery hint. Clone into a hidden deps/.tmp-XXXXXX directory made by mkdtemp, check out there, then rename into place, retrying EPERM/EBUSY/EACCES up to three times for Windows antivirus and indexer locks. The staging name is shorter than every component name: a longer one pushed Git for Windows past its 260-character object path limit in a deep checkout. On failure remove the staging directory and say nothing was left. A failed command now carries git's own stderr, the unusable-dependency refusal names the directory to remove, and main prints 'bootstrap failed:' instead of a stack trace. The git runner is injectable through prepareDependencies so the staging path is tested without network access.
The refund domain evaluated deps/constitutional-agent-testbench's own examples/policy.json (public-example-policy, 5 rules), while step 6 runs aas demo, which gates on fixtures/policy.json (aas-refund-gate-v1, 6 rules). Point the refund domain at the stack's policy and response fixtures, record the expected policy id for both domains, and fail if the decide output in step 1 or the orchestrated report in step 6 names a different policy.
The conformance example claimed to prove at-most-once effects and exact reversal on both connectors, but it ran rules 2 to 6 only on the inventory connector, compared call counts and return values, and never checked on-hand stock against the baseline. Add effect probes to the harness (inventory on hand and active refunds bound to the action), a positive control for the reservation refusals, and run every rule on both connectors. The refund remedy rule covers the duplicate fault (only the duplicate is voided, the primary stays active, a replayed key returns the same result, a second key is refused) and a clean refund that remediates to no_change. A mutated expectation of twice the quantity makes rule 3 exit 1.
- aas help and the export and prune usage errors now list --root, which both commands accept; the --root reference names all eight commands that take it; the flow line says 'consequence-rail demo <domain>'. - docs/stack-lock.md said a lock mismatch is recorded in the manifest. The code refuses the run before any stage and writes no bundle. - examples/README.md uses the direct node invocation CI uses on Windows and says the conformance example drives rail modules in-process. - The PR template drops issue-form front matter that rendered as text and replaces a checklist that forbade CHANGELOG and CI edits with the rules CONTRIBUTING.md actually sets.
Nothing could report which orchestrator version was running, and a saved case could not say which release produced it. package.json is now the single source every runtime surface reads: - aas --version and aas version print it (--json for a machine form); extra arguments are a usage error. - runDemo records report.stack_version and persistRunBundle writes an additive manifest.stack_version. schema_version stays run/v1; cases saved earlier load, compare and verify unchanged and report null. - summarizeRun and inspectCase expose it (not a compared field), the Markdown review prints 'Orchestrator version', and /api/health returns it as version. stackVersion() reads package.json lazily and caches it, because the integration proof's offline replay copies bin/aas.mjs without package.json. printHuman is untouched; its field order is pinned to the README sample.
There was no repeatable way to cut a release, and CHANGELOG.md fed nothing. Add .github/workflows/release.yml, triggered only by vX.Y.Z tag pushes: it re-runs npm run check and the integration proof on the tagged commit, runs check-version --tag to require the tag, package.json, lock and changelog to agree, and creates the GitHub Release from that version's changelog section. contents: write is scoped to the one job, the checkout keeps no token, no cache is restored, and the tag reaches the shell through env. package.json becomes private: the package clones into deps/ beside itself, so it cannot work as an installed dependency, and npm publish now refuses it. bin and npm link are unaffected. docs/architecture.md and CONTRIBUTING.md describe the release boundary and the tagging steps, and a test pins release.yml's trigger and permissions.
Cut the first tagged release since the untagged 0.2.0 commit. package.json carried 0.2.1 to 0.2.5 without tags or changelog sections; 0.3.0 replaces them with one pre-1.0 minor, because the cycle added commands and changed the runtime floor. - npm version 0.3.0: package.json and both package-lock.json version fields, nothing else in the lock. - CHANGELOG.md: [Unreleased] becomes [0.3.0] - 2026-10-09 with categories in Keep a Changelog order, a BREAKING line for the Node.js 22.12.0 floor (#18), a summary of the features that predate the changelog, and retroactive [0.2.0] (c9e89c0) and [0.1.0] (70ce404) sections. Links are tag based and resolve once v0.1.0, v0.2.0 and v0.3.0 are pushed. - SECURITY.md: a supported-versions table (0.3.x only), and a warning not to put exploit details in a public issue while private vulnerability reporting stays disabled. - docs/release-readiness.md: a release gate for check and check-version --tag, and a Release 0.3.0 record with the unchanged pins and the counts measured on this branch. - README.md: a release badge, and a note that npm run check mirrors CI's test job.
Refresh stack-lock.json to the release merge commits of the three sibling releases published on 2026-10-09: - constitutional-agent-testbench v0.6.0 (PR #63): ed46f0cb9af4 - consequence-rail v0.3.0 (PR #69): 78d6f8a23dbc - mandatebound v2.0.0 (PR #104): 32d525618e23 No expected entrypoint, post-build entrypoint, install or build hook changed, and the orchestrator needs no code change. MandateBound 2.0.0's breaking changes are in commands and package APIs the stack does not call; simulate and review behave as before and review records still carry engine 1.0.0. Rail 0.3.0 adds receipt-outcome and evidence-fact checks to verification, so add fixtures/previous-pins-inventory-review.json, an inventory case saved by main at 8179e59 under the previous pins, and replay it in the component compatibility test. Update the lock-load provenance fixture, the release-readiness pin list and 0.3.0 evidence, the lock policy notes, and the 0.3.0 changelog.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Release 0.3.0: the first tagged release since the untagged 0.2.0 commit, built from a 32-finding review. The first 16 commits fix linked-launch entrypoints, GUI status and errors, bootstrap recovery, CSP, CLI exit codes and messages, the examples, and documentation drift, and they add release engineering: a syntax gate derived from git, a version consistency check, a tag-triggered release workflow,
aas --version, andstack_versionon every run. Commit 17 refreshes the three component pins to the sibling releases published on 2026-10-09 (testbench 0.6.0, rail 0.3.0, MandateBound 2.0.0); see "Component pin refresh" below.package.jsonis the single source of truth for the version.scripts/check-version.mjsenforces package, lock, CHANGELOG, release-readiness pins and (with--tag) the tag. It runs innpm run check, CI's test job, and the newrelease.yml.stack_versionis additive withinagent-action-stack.run/v1. Cases saved earlier load, compare, inspect and verify unchanged and report it as unavailable, and a test covers that.fixtures/legacy-rail-review.jsonis untouched and still replays under the new pins, and commit 17 adds a fixture of an inventory case saved under the previous pins that the compatibility test replays.Review findings addressed
npm run guicrashes with an unhandled error on a busy portCommits
e13c0d8fix(cli): run entrypoints when launched through a symlinked path19e1535build: pin LF line endings and derive the syntax gate from gita57adcdci: harden checkouts, test the Python 3.11 floor, update pinned actions1f6c32fbuild(release): add version consistency check; repair lock and changelogaed688efix(cli): validate demo arguments first and name missing saved cases05a0dd9fix(run): record one prove skip reason in report and manifest on act errorfd97792fix(gui): announce run progress and failures in a visible status8e7c908fix(gui): exit cleanly on a busy port; align history errors0342c16fix(gui): pin page script and style by hash instead of unsafe-inline1c47f0efix(bootstrap): stage clones so a failed fetch cannot wedge deps/7b6ce5bfix(examples): gate the review handoff on the stack refund policyb242ae8test(examples): measure connector effects on both synthetic connectorsfd75e14docs: align help, lock policy, examples and PR template with the codeccf3ee5feat(cli): add aas --version and record stack_version in runs91229c6ci(release): publish GitHub Releases from version tags; mark privated954076chore(release): 0.3.0bd8767fbuild(lock): pin testbench 0.6.0, rail 0.3.0 and MandateBound 2.0.0Component pin refresh (commit 17)
The three siblings were released on 2026-10-09.
stack-lock.jsonnow pins each release merge commit; every tag resolves to its pinned commit, and each merge commit passed its own CI onmain.ed46f0cb9af400560aa9a9e73bbb9864ed5678a016b2faa778d6f8a23dbce69bb443b453c8d471645f67daacc430383c32d525618e23678f86c754e4a536e4d094988a1b708256d4simulate --scenario operatorandreview --input; the 2.0.0 breaking changes are indecide,preview,replay,appeal,serve,operator,ap2-disputeand the package API. The simulate result keeps its structure and review records still carry engine1.0.0.fixtures/legacy-rail-review.jsonstill replays, and cases saved bymainat8179e59list, inspect and verify from their own case store under the new pins. Rail 0.3.0 adds receipt-outcome and evidence-fact checks to verification, so the commit addsfixtures/previous-pins-inventory-review.json, an inventory case saved under the previous pins, and replays it intest/component-compatibility.test.mjs.test/stack.test.mjs, the pin list and 0.3.0 evidence indocs/release-readiness.md(whichcheck:versionenforces), the pin notes indocs/stack-lock.md, and a### Changedentry in the 0.3.0 CHANGELOG section.Versioning and release plan
--version) and raised the Node.js floor from 20 to 22.12.0 (chore: require Node 22.12+ for the full-stack workflow and prove it in CI #18). Both take a minor bump in 0.x, and 0.3.0 sorts above the untagged 0.2.1 to 0.2.5.package.json, bothpackage-lock.jsonfields (npm versionchanged only those two lines),CHANGELOG.md## [0.3.0] - 2026-10-09,aas --version,/api/health, andstack_versionon new runs.node scripts/check-version.mjs --tag v0.3.0passes.gh pr merge --merge(not squash) after all 10 CI jobs pass, so the 17 commits stay on main as reviewed. main has no branch protection, so wait forgh pr checks --watchfirst.Tags to create after merge, with the noreply identity:
git pull --ff-only, runnode scripts/check-version.mjs --tag v0.3.0(must print consistent).git tag -a v0.1.0 70ce4046ba05dc5b7efbfa02ca371922c6255477 -m "Agent Action Stack 0.1.0"git tag -a v0.2.0 c9e89c001f51b48f1ebda6716996680546033a8b -m "Agent Action Stack 0.2.0"git push origin v0.1.0 v0.2.0git tag -a v0.3.0 -m "Agent Action Stack 0.3.0"on the merge commit, thengit push origin v0.3.0. This runs.github/workflows/release.yml, which re-runsnpm run checkand the integration proof, checks the tag, and creates the GitHub Release from the[0.3.0]CHANGELOG section. Confirm withgh run list -R EauDoon/agent-action-stack -w release.yml -L 1andgh release view v0.3.0 -R EauDoon/agent-action-stack.Nothing is published to npm:
package.jsonis now"private": true.Verification
After the pin refresh (
bd8767f)Windows 11, Node.js v24.18.0, Python 3.12.10, on the refreshed pins.
npm run integrationin a freshcore.autocrlf=falseclonedeps/(all detached, clean, at the pinned SHAs)npm run integrationwithAAS_PYTHONon CPython 3.11.15npm run checknode scripts/check-version.mjs --tag v0.3.0npm run gui:smokenode --test test/component-compatibility.test.mjs--domain inventory,--response failnpm run test:browser--extra-filePR CI (run 37962686074 on
bd8767f): all 10 jobs pass, 4 test, 5 integration (including the Python 3.11 leg) and 1 browser, against the new pins.Before the pin refresh (
d954076)Run on the head
d954076in a freshcore.autocrlf=falseclone. Environment: Windows 11, Node.js v24.18.0, npm 12.0.2, Python 3.12.10.npm ci --ignore-scriptsnpm testnpm run check:syntaxsyntax: 15 modules parsednpm run check:versionversion: 0.3.0 consistent (package, lock, changelog, release pins)node scripts/check-version.mjs --tag v0.3.0 --notes notes.mdnpm run gui:smokeGUI smoke test passed.npm run integration --ignore-scriptsdeps/node --test test/component-compatibility.test.mjsnpm run example:review-handoffdecide: policy aas-refund-gate-v1 passed (6 rules), 6 replay checksnode examples/review-handoff.mjs --domain inventoryaas-inventory-gate-v1node examples/review-handoff.mjs --response failnpm run example:connector-conformancenpm run test:browser(Chromium 1243, isolated port)npm run checkat each of the 16 commitsAAS_PYTHON)--extra-filePR CI (run 37944585925 on
d954076): all 10 jobs pass, 4 test, 5 integration and 1 browser, on the v7 actions. The Linux test jobs report 208 of 208 with 0 skipped, so the symlink and junction cases run there. The Python 3.11 integration leg ran on CPython 3.11.17.Mutation checks: a wrong style hash fails the new browser CSP test; expecting twice the allocated quantity makes conformance rule 3 exit 1; the old entrypoint guard, CI file and lock each fail their new tests.
Deviations from the plan
.<name>.<pid>.<uuid>.tmpbroke the real clone on Windows. In a deep checkout the longer path pushed git past the 260-character object path limit, and the real fetch failed with "Filename too long". The staging directory is nowmkdtemp(deps/.tmp-XXXXXX), which is shorter than every component name. Failed commands now carry git's stderr; that is how this was diagnosed. A test pins the length rule.manifest.json, for example) to case-relative messages, keeping the error code. The plan covered only the case directory.npm publish --dry-rundoes not refuse a private package on npm 12; it skips that check.private: trueis set and pinned by a test, and the realnpm publishrefusal (EPRIVATE) was not exercised, because exercising it would mean publishing. npm leftpackage-lock.jsonunchanged for the flag.upgrade/v0.3.0, as the task instructed, rather than theimp/...example in the plan.