Skip to content

release: cut v0.3.0, and make the package prove it contains what the notes claim - #96

Open
AetherAI3 wants to merge 4 commits into
mainfrom
lane/aa-rel-01-release
Open

release: cut v0.3.0, and make the package prove it contains what the notes claim#96
AetherAI3 wants to merge 4 commits into
mainfrom
lane/aa-rel-01-release

Conversation

@AetherAI3

@AetherAI3 AetherAI3 commented Aug 22, 2026

Copy link
Copy Markdown
Owner

Rebased onto 426b124 (post-#98) and re-cut. Head is now e0f9458. Every figure below was re-measured against the new base — nothing was carried forward. See the "What changed on rebase" section at the end.

The problem

On 2026-08-20 this repository described five different releases at once, and nothing compared them:

Statement Said
package.json 0.2.0
package-lock.json (both version fields) 0.1.0 — the #81 bump never touched it
RELEASE_NOTES.md top entry a 0.2.0 whose feature list was written at 477f0fc, before #72
npm registry 0.1.0, the only version ever published
aether-agents-0.2.0.tgz committed in the repo root, #83#90 a build with no skills runtime in it at all

None of it was deliberate. The statements drifted apart because nothing asserted they had to agree.

Semver decision: 0.2.0 → 0.3.0 (minor)

#72 is a backwards-compatible feature addition the v0.2.0 notes never mention — a skills runtime, three new CLI commands (aether skills, aether capabilities, aether support-bundle), and six built-in skills shipped inside the package. Semver makes that a minor bump.

Reusing 0.2.0 was rejected on identity grounds, not style. 0.2.0 already names a specific dated artifact: the August 19 notes entry, the README availability paragraph, the install.sh pin example, and that committed tarball — whose contents predate the skills runtime entirely. Publishing different contents under the same version makes two materially different artifacts answer to one name, which is precisely the defect this release exists to close.

0.2.0 is retired unused. Nothing will ever be published under it.

Feature boundary: everything the v0.2.0 notes described (never shipped) plus 477f0fc..426b124 — 17 commits, 2026-08-19 08:39 EDT → 2026-08-22 11:12 EDT:

What this PR does

  1. Reconciles the versionpackage.json, both package-lock.json version fields, and src/version.ts all say 0.3.0.
  2. Rewrites the release surfaceRELEASE_NOTES.md leads with v0.3.0; the v0.2.0 entry is marked superseded rather than rewritten, because it is the record of what was written that day, not an install instruction. New dated log at docs/releases/2026-08-22.md, indexed.
  3. Keeps the availability language honest — README and the notes still say plainly that npm i -g aether-agents gives you 0.1.0, because it does.
  4. Adds test/release_coherence.test.ts — a bidirectional gate (below).
  5. Adds npm run release:candidate — runs the exact release.yml sequence before the tag exists.
  6. Adds docs/releases/OPERATOR-PACKET-v0.3.0.md — the founder-owned publish sequence with digest, manifest, the unannounced-command list, and an explicit list of what is not proven.

The gate runs both directions

Gate A asserts every place naming the release names the same one — including both lockfile version fields, the drift nothing was watching.

Gate B (notes → package). For every feature the release notes claim, the code behind it must be in the file list npm pack would actually ship, and the command exposing it must be in the CLI registry. The source checkout's dist/ is not the package — the allowlist is dist/src plus four docs.

Gate C (package → notes). The inverse: a user-visible command that ships with no claim anywhere in the notes. That is the direction that actually keeps happening — it happened to this very PR while it was open, when #98 landed on main and the notes said nothing about it. Every non-hidden command in the CLI registry must now be announced by some release note, or named in SHIPPED_WITHOUT_A_NOTE with a reason. An explicit list is fine; silence is not.

The list is enforced in both directions — a stale entry fails, and an entry that is announced fails — so it cannot rot into a permanent bypass that quietly absorbs the next unannounced command. Every entry must also appear in the operator packet, because the founder cutting the tag is the person who needs to know what goes out unannounced. 15 commands are on it today; all predate the release log or were announced by capability in the June 2026 entry, and none is new in this release. login/logout are exempt by rule via the registry's own hidden flag.

Both mutations, measured at this base

Gate B — added "!dist/src/commands/skills.js" to the files allowlist, silently dropping aether skills from the tarball:

verify:production  ->  {"ok":true, ... "packedFiles":526}   MISSED IT
release_coherence  ->  FAIL: dist/src/commands/skills.js
                             (agent skills runtime - `aether skills`)

Gate C — added a command to the registry and mentioned it nowhere:

{ name: "teleport", args: "<dest>", summary: "beam the working tree somewhere",
  section: "System" }

verify:production  ->  {"ok":true, ... "packedFiles":527}   MISSED IT
release_coherence  ->  FAIL: no user-visible command ships without either a
                       release note or a named exemption
                       + [ 'teleport - beam the working tree somewhere' ]
                       - []

Both restored: 527 packed files, 10/10 pass. The pre-existing production gate caught neither, because it does not know what the notes promised and does not know what the registry exposes.

src/commands/cli_registry.ts is read-only for this lane. The Gate C mutation existed only in the working tree, was never committed, and the restore is verified by digest (md5 37b3f944…, git status src/ empty).

Evidence — npm run release:candidate, commit-bound

Run against 22aa02141fba158927cb1f01e4344cfa3e8f1a01 in a detached worktree of that commit, not the working checkout. commitBound: true, ok: true, exit 0:

PASS     commit-identity - 22aa02141fba158927cb1f01e4344cfa3e8f1a01
PASS     stage-commit - detached worktree of that commit
PASS     npm-ci-ignore-scripts - found 0 vulnerabilities
PASS     npm-audit-high - found 0 vulnerabilities
PASS     typecheck - tsc --noEmit exit 0
PASS     build - copied 18 built-in skill assets -> dist/src/skills/builtin
PASS     release-tests - 4 release test files, exit 0
NOT-RUN  npm-test - NOT RUN here. The full suite is release.yml's gate.
                    This report says nothing about it.
PASS     verify-production - {"ok":true,"package":"aether-agents","version":"0.3.0",
                             "packedFiles":527,"packedBytes":2459474,"workflows":3}
PASS     pack - aether-agents-0.3.0.tgz
                sha256:8c5c119d93cabf49af0c49c97addb055308d508af93f8675a26b6f5c8ecba307
PASS     install-tarball - <prefix>/node_modules/aether-agents
PASS     installed --version - 0.3.0
PASS     installed --help - 46 lines, lists skills, capabilities, resume, agent, doctor
PASS     installed skills list - aether/frontend-from-screenshot@1.0.0 builtin enabled ...
PASS     installed capabilities - instructions        supported
PASS     installed demo:handoff - independent test run in machine-b/slugify: green

RELEASE CANDIDATE OK

The last five lines all ran the binary npm install --global placed on disk from that exact tarball, in a clean prefix. The handoff demo's harness is not shipped (allowlist is dist/src plus four docs), so it is placed beside the installed package and resolves both the CLI and its own imports from the tarball's dist/src.

Tarball: aether-agents-0.3.0.tgz, 527 entries, 597,400 bytes packed / 2,459,474 unpacked, sha256:8c5c119d93cabf49af0c49c97addb055308d508af93f8675a26b6f5c8ecba307.

Registry / tag / release readings taken 2026-08-22, verbatim:

$ npm view aether-agents versions --json      ->  [ "0.1.0" ]
$ npm view aether-agents dist-tags --json     ->  { "latest": "0.1.0" }
$ gh release list -R AetherAI3/aether-agent   ->  (no output - zero releases)
$ git tag -l                                  ->  frozen-seam-v1, v0.1.0   (no v0.2.0)

What changed on rebase

main moved to 426b124 while this PR was open. The branch was rebased (trial-merge was clean; no conflicts) and everything was re-derived:

  • The notes now cover feat(cli): command registration seam, and the flags doctor never received #98. Its user-visible half is three fixes, and the first is the exact failure class this codebase forbids: aether doctor --live never received --live. The argv parse is non-strict, so a flag a command had not declared was captured globally and stripped from what the command was handed — --live ran the fast configured-only report and exited 0, presenting a live end-to-end verification that was never performed. --deep, --dry-run, --no-ui and --only were lost the same way, and the whole --fix path was unreachable. Second: aether doctor --fix --yes replied "re-run with --yes" to someone who had just passed it. Third: command lookup lowercased the token while dispatch was case-sensitive, so aether Vault fell past the typo guard into a chat turn and billed a paid model call.
  • The digest changed, and that is the correct outcome. The earlier candidate at a63e1c6e produced 25f33524… over 524 entries. feat(cli): command registration seam, and the flags doctor never received #98 added dist/src/core/command_dispatch.* and rewrote main.js, so the packed content genuinely differs. A digest that had survived that change would have meant the pack was not reading the tree.
  • Both mutation checks were re-run, not carried forward. I had initially edited the Gate B figure from 523 to 526 by inference; that is a number nobody measured, so it was re-run before being written down.
  • Gate B now reads the registry by importing it, not by regex over its source. feat(cli): command registration seam, and the flags doctor never received #98 replaced "reachability asserted by a regex over main.ts" for exactly this reason, and it immediately split the commands across CLI_COMMANDS and DISPATCH_COMMANDS — a source regex keyed on one array would have silently stopped covering the other. doctor is in the second one.
  • A miscount was fixed while re-deriving: the packet said "9 user-visible fixes" over a list of ten.

Explicitly NOT proven

Named as unproven rather than omitted:

Shared-file collisions

None committed. src/main.ts and src/commands/cli_registry.ts are not modified by this PR — this lane registers no command. The registry was read (Gate B/C import it) and temporarily mutated in the working tree for the Gate C mutation proof, then restored byte-identical and verified by digest before committing.

Every changed file is inside the AA-REL-01 lease:

package.json  package-lock.json  src/version.ts  README.md  RELEASE_NOTES.md
docs/releases/{README.md, 2026-08-22.md, OPERATOR-PACKET-v0.3.0.md}
scripts/{release-candidate.ts, verify-production.ts}
test/{release_coherence.test.ts, release_canaries.test.ts}

scripts/verify-production.ts gained exactly one thing: createPackReport is now exported, so a gate reasoning about what a user receives asks npm instead of reading the build directory. No policy was weakened — the allowlist, audit, provenance and exact-tarball smoke test in release.yml are untouched.

test/release_canaries.test.ts had a coverage map claiming canaries 5 and 7 were unwritable. #86 and #87 wrote them two commits later; the excuse outlived the condition. The map is now enforced by a test that reads the delegated files rather than asserted in a comment.

Founder-owned — this PR does none of it

  1. Merge this PR; note the merge SHA.
  2. Re-run npm run release:candidate on the merge commit and confirm ok: true, commitBound: true. Do not tag against this PR's digest — the base is still moving.
  3. git tag -a v0.3.0 <merge-sha> and push it.
  4. Publish a GitHub release for v0.3.0 — publication, not tag creation, triggers release.yml.
  5. Confirm the npm-production environment exists and NPM_TOKEN is set before publishing the release, or the run fails after the release is already public.
  6. Verify availability from the registry itself, not the workflow log.
  7. Only then update the availability language — test/release_coherence.test.ts enforces that the claim cannot be added without the registry actually serving it.

Full detail: docs/releases/OPERATOR-PACKET-v0.3.0.md.

No Aether credential, npm token or GitHub token was used, read, or written by any part of this work.

🤖 Generated with Claude Code

@AetherAI3

Copy link
Copy Markdown
Owner Author

CI closes the one thing the local candidate reported as not-run.

All four required checks green on 02a8aa8, plus CodeQL:

Check Result
Test (ubuntu-latest) pass, 35s
Test (windows-latest) pass, 1m9s
analyze pass, 1m21s
supply-chain pass, 16s
CodeQL pass

Those Test jobs run the full npm test suite — the step the release-candidate report deliberately marked NOT-RUN on this Windows box rather than guessing at. It is now read from the authority, not from a local skip.

mergeStateStatus: CLEAN. Not merging — this is a release cut and the tag/publish steps after it are founder-owned.

AetherAI3 and others added 4 commits August 22, 2026 11:24
…notes claim

On 2026-08-20 this repository described five different releases at once.
package.json said 0.2.0. package-lock.json still said 0.1.0 — the #81 bump never
touched it. RELEASE_NOTES.md's top entry described a 0.2.0 whose feature list was
written at 477f0fc, before #72 added `aether skills`, `aether capabilities` and
`aether support-bundle`. The npm registry served 0.1.0 and nothing else. And a
packed aether-agents-0.2.0.tgz sat in the repo root from #83 until #90 deleted
it, containing a build with no skills runtime in it at all.

Nothing was lying on purpose. The four statements drifted apart because nothing
compared them.

0.2.0 -> 0.3.0, not 0.2.1
-------------------------
#72 is a backwards-compatible feature addition — a skills runtime, three new CLI
commands, six built-in skills shipped inside the package — that the v0.2.0 notes
never mentioned. Semver makes that a minor bump.

Reusing 0.2.0 was rejected on identity grounds. 0.2.0 already names a specific
dated artifact: the August 19 notes entry, the README availability paragraph,
install.sh's pin example, and that committed tarball. Publishing different
contents under the same version would make two materially different artifacts
answer to one name, which is the defect this release exists to close. 0.2.0 is
retired unused; it will never be published.

Reconciled
----------
- package.json, package-lock.json (both version fields) and src/version.ts all
  say 0.3.0.
- RELEASE_NOTES.md leads with v0.3.0 covering 477f0fc..ed094dc: one feature,
  nine user-visible fixes, three test-only commits, one unwired module (#86 ship
  rail, which no command invokes), one docs commit. The v0.2.0 entry is marked
  superseded rather than rewritten — it is the record of what was written that
  day, not an install instruction.
- README and the release log name 0.3.0, and both still say plainly that
  `npm i -g aether-agents` gives you 0.1.0, because it does.

New gate: test/release_coherence.test.ts
----------------------------------------
Gate A asserts every place that names the release names the same one — including
both package-lock version fields, which is the drift nothing was watching.

Gate B is the load-bearing one: for every feature the release notes claim, the
code behind it must be present in the file list `npm pack` would actually ship,
and the command exposing it must be in the CLI registry. The source checkout's
dist/ is NOT the package — the allowlist is dist/src plus four docs.

Mutation-checked. Adding "!dist/src/commands/skills.js" to the files allowlist
silently drops `aether skills` from the tarball:

  verify:production  ->  {"ok":true, ... "packedFiles":523}   MISSED IT
  release_coherence  ->  FAIL: dist/src/commands/skills.js
                              (agent skills runtime — `aether skills`)

Restored: 7/7 pass, 524 packed files.

New: npm run release:candidate
------------------------------
release.yml only runs after a founder has already tagged and published, so
everything it checks is checked too late to change the decision. This runs the
same sequence, in the same order, before the tag exists — against a detached
git worktree of a specific commit, never the dirty checkout. A dirty tree is
refused unless --allow-dirty, which marks the report commitBound:false rather
than pretending.

Every CLI proof runs the binary npm installed from the packed tarball into a
clean prefix: --version, --help, `skills list`, `capabilities`, and the handoff
demo (the demo harness is not shipped, so it is placed beside the installed
package and resolves the CLI and its imports from the tarball's own dist/src).

Skipped steps are recorded as "not-run" with a reason and can never read as a
green tick — the full npm test suite is release.yml's gate and says so in the
report. No string is handed to a shell: npm is reached through its own JS
entrypoint under node, so every argument stays an argument.

Also
----
- scripts/verify-production.ts exports createPackReport, so a gate reasoning
  about what a user receives asks npm instead of reading the build directory.
- test/release_canaries.test.ts's coverage map said canaries 5 and 7 were
  unwritable. #86 and #87 wrote them two commits later; the excuse outlived the
  condition. The map is now enforced by a test that reads the delegated files.
- docs/releases/OPERATOR-PACKET-v0.3.0.md: the founder-owned publish sequence,
  with what is proven and what is explicitly not.

Gates at this commit:
  npm run typecheck                      exit 0
  release + production test files        21 pass / 0 fail
  npm run verify:production --tag v0.3.0 ok

Full npm test NOT run locally (a tool_executor test does not return on this
Windows box); CI is the authority.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…te evidence

Run against a63e1c6 in a detached worktree: commitBound true, ok true, exit 0.
Tarball aether-agents-0.3.0.tgz, 524 entries, 589,829 bytes packed / 2,435,029
unpacked, sha256 25f33524bd866275674eccbf8cfe5706f14e925cb0ba35861dc6bc21a9245a2d.

The digest came out identical from an uncommitted tree and from the detached
worktree, which is recorded as content stability across a docs-only commit and
explicitly NOT as a cross-machine reproducibility claim.

Also records the mutation check: dropping dist/src/commands/skills.js from the
files allowlist leaves verify:production reporting ok:true at 523 files, and
fails release_coherence by name.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ional

The ground moved while PR #96 was open. #98 squash-merged to main, main became
426b124, and the v0.3.0 notes said nothing about it — which is this lane's own
defect arriving from the direction the lane did not gate.

WORK A: the notes now cover #98
-------------------------------
Its user-visible half is three fixes, and the first is the exact failure class
this codebase forbids:

- `aether doctor --live` never received --live. main.ts parses non-strictly, so
  a flag the command had not declared was captured globally and stripped from
  what the command was handed. --live ran the fast configured-only report and
  EXITED 0, presenting a live end-to-end verification that was never performed.
  Unknown rendered as verified. --deep, --dry-run, --no-ui and --only were lost
  the same way, and the whole --fix repair path was unreachable.
- `aether doctor --fix --yes` answered "re-run with --yes" to someone who had
  just passed it, because the global --yes never arrived either.
- Command lookup lowercased the token while dispatch was case-sensitive, so
  `aether Vault` fell past the typo guard into a chat turn and billed a paid
  model call.

The seam itself is announced too: a command now carries its own help metadata,
flag table and loader in one entry, flag collisions are load-time errors, and
reachability is structural rather than a regex over main.ts's source.

Range moves 477f0fc..ed094dc -> 477f0fc..426b124, 16 commits -> 17, across
RELEASE_NOTES.md, docs/releases/2026-08-22.md and the operator packet. Fixed a
miscount while re-deriving: the packet said "9 user-visible fixes" over a list of
ten.

WORK B: the gate now runs both directions
-----------------------------------------
Gate B ran notes -> package: a claim with no code behind it fails. It could not
see the inverse — a user-visible command that ships with NO claim anywhere — and
the inverse is what actually keeps happening, once per lane that lands between
the note being written and the tag being cut.

Gate C: every non-hidden command in the CLI registry must be announced by some
release note, or named in SHIPPED_WITHOUT_A_NOTE with a reason. An explicit list
is fine; silence is not. It is enforced in both directions — a stale entry fails,
and an entry that IS announced fails — so it cannot rot into a permanent bypass
that quietly absorbs the next unannounced command. Every entry must also appear
in the operator packet, because the founder cutting the tag is the person who
needs to know what goes out unannounced. 15 commands are on it today; all predate
the release log or were announced by capability in the June 2026 entry, and none
is new in this release.

Mutation-checked, per the controller's instruction. Added to the registry:

  { name: "teleport", args: "<dest>",
    summary: "beam the working tree somewhere", section: "System" }

  verify:production  -> {"ok":true, ... "packedFiles":527}        MISSED IT
  release_coherence  -> FAIL: no user-visible command ships without either a
                        release note or a named exemption
                        + [ 'teleport — beam the working tree somewhere' ]
                        - []

Registry restored byte-identical (md5 37b3f944…, `git status src/` empty),
10/10 pass. cli_registry.ts is read-only for this lane: it was mutated only in
the working tree, never committed, and the restore is verified by digest.

Also: Gate B now reads the registry by IMPORTING it instead of by regex over its
source. #98 replaced "reachability asserted by a regex over main.ts" for exactly
this reason, and it immediately split the commands across CLI_COMMANDS and
DISPATCH_COMMANDS — a source regex keyed on one array would have silently
stopped covering the other. doctor is in the second one.

Gates at this commit:
  npm run typecheck            exit 0
  release_coherence            10 pass / 0 fail

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…igest change

The base moved to 426b124 (#98), so every number in the packet was taken against
a tree that no longer exists. Re-run, commit-bound, at 22aa021:

  commitBound true, ok true, exit 0
  aether-agents-0.3.0.tgz
  sha256 8c5c119d93cabf49af0c49c97addb055308d508af93f8675a26b6f5c8ecba307
  527 entries, 597,400 packed / 2,459,474 unpacked

The digest CHANGED from the a63e1c6 run (25f33524…, 524 entries), and the packet
now says why that is the correct outcome rather than a discrepancy: #98 added
dist/src/core/command_dispatch.* and rewrote main.js, so the packed content is
genuinely different. A digest that had survived that change would have meant the
pack was not reading the tree.

Both mutation checks were re-measured against this base rather than carried
forward:

  skills drop   verify:production {"ok":true, ... "packedFiles":526}  MISSED IT
                release_coherence FAIL: dist/src/commands/skills.js
                restored -> 527 files, 10/10 pass

  teleport      verify:production {"ok":true, ... "packedFiles":527}  MISSED IT
                release_coherence FAIL: 'teleport — beam the working tree
                somewhere'
                restored -> registry byte-identical, 10/10 pass

I had initially edited the skills figure from 523 to 526 by inference. That is a
number nobody measured, so it was re-run before being written down.

Release-owned suite at this commit: 24 pass / 0 fail.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@AetherAI3
AetherAI3 force-pushed the lane/aa-rel-01-release branch from 02a8aa8 to e0f9458 Compare August 22, 2026 15:35
@AetherAI3

Copy link
Copy Markdown
Owner Author

Rebased onto 426b124 (post-#98) and re-cut. Head is now e0f9458; the body has been rewritten so every figure in it was measured at this base rather than carried forward.

All five checks green at e0f9458 — these readings are at the exact ref, not the pre-rebase one:

Check Result
Test (ubuntu-latest) pass, 30s
Test (windows-latest) pass, 2m18s
analyze pass, 1m23s
supply-chain pass, 14s
CodeQL pass

mergeStateStatus: CLEAN, mergeable: MERGEABLE.

Two things worth flagging to a reviewer:

The digest changed and that is correct. 25f33524… / 524 entries → 8c5c119d… / 527 entries. #98 added dist/src/core/command_dispatch.* and rewrote main.js, so the packed content genuinely differs. A digest that had survived that would have meant the pack was not reading the tree.

Gate C is the new direction. Gate B caught a note promising something the package lacks. It could not see a command shipping with no note at all — which is what #98 just did to this very PR. Both directions are now enforced, both mutation-proven at this base, and verify:production caught neither mutation.

Still not merging. Tag and publish remain founder-owned, and #93/#94/#95/#97/#99/#100/#101/#102 are all expected to land first — each one moves the range and the digest again, which is why step 2 of the packet re-runs the candidate on the merge commit instead of trusting this PR's number.

@AetherAI3

Copy link
Copy Markdown
Owner Author

MERGE-TRAIN STATE — HELD (2026-08-22T15:56Z)

Head: e0f94587dfae6876ba7cf7b92799956a9f36a537; base/main: 426b12464c2a19549f421adb43348f83d028628e.

Evidence: required Ubuntu, Windows, supply-chain, and CodeQL checks passed on this exact head. Independent release review found the recorded release:candidate artifact/digest is bound to ancestor 22aa02141fba158927cb1f01e4344cfa3e8f1a01, not this final head. No tag, GitHub release, or npm publication is authorized.

Owner: release PR branch writer. Next legal action: run npm run release:candidate on the frozen final head (or exact merge candidate), retain a successful commitBound:true report, then re-fetch head/base/checks and obtain a fresh merge decision.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant