Skip to content

fix(release): release-changelog.mjs refuses to run except as a version command's hook (#198) - #211

Merged
nissa-seru merged 6 commits into
anima-research:mainfrom
nissa-seru:fix/198-release-changelog-hook-only
Oct 18, 2026
Merged

nissa-seru merged 6 commits into
anima-research:mainfrom
nissa-seru:fix/198-release-changelog-hook-only

Conversation

@nissa-seru

@nissa-seru nissa-seru commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

TL;DR: scripts/release-changelog.mjs, the version hook that folds changelog.d/ into CHANGELOG.md, released whenever it ran, including by hand with --help. It now refuses unless its environment looks like a version command's version hook, as npm version, bun pm version and yarn version give it, a guard against running it by mistake. --help and -h print what it does and change nothing, whatever else is given, and without them any argument is refused.

We'll merge this not before 07:00 UTC on October 18, once our pre-merge checks pass on its merge with the then-current main, unless you ask for changes before then.

!Fixes #198

Problem

The script is package.json's version hook (node scripts/release-changelog.mjs && git add CHANGELOG.md changelog.d). It reads the new version from package.json, which npm version has already written when it runs the hook. It folds the fragments in changelog.d/ and the entries under ## Unreleased into a new ## X.Y.Z — date section, and deletes the fragments it folded. It took no arguments and didn't look at how it was run, so node scripts/release-changelog.mjs --help, or any run by hand, released into whatever version package.json already held. Anarchid found it while reviewing #156 (#198). The run there refused only because a ## 0.14.1 section already existed.

Changes

  • It refuses unless npm_lifecycle_event is version and npm_command is version or unset. npm version gives its version hook both as version. bun pm version also bumps package.json and then runs the hook, but sets no npm_command, and it releases correctly on main, so an unset npm_command is accepted too. npm run version runs the same script with npm_command set to run and bumps nothing, and a preversion hook would run before the bump, so both variables are checked. release-changelog.mjs ignores its arguments and runs a release when invoked by hand #198 suggested checking npm_lifecycle_event alone, which still lets npm run version release. Any process can set these variables, so this guards against running the script by mistake, which is what release-changelog.mjs ignores its arguments and runs a release when invoked by hand #198 reports, not against running it on purpose. What each invocation sets, observed with npm 11.19.1, bun 1.4.3, pnpm 10.34.7 and yarn 1.22.22:

    Invocation Bumps first npm_command npm_lifecycle_event This check
    npm version patch yes version version passes
    bun pm version patch yes unset version passes
    yarn version --patch yes unset version passes
    npm run version no run version refuses
    bun run version no run-script version refuses
    pnpm run version no run-script version refuses
    yarn run version no unset version passes
    node scripts/release-changelog.mjs no unset unset refuses

    npm 7.24.2, 8.19.4, 9.9.4 and 10.9.9 set the same two values as 11.19.1 for npm version's hook, and pnpm version runs npm version. The yarn rows are yarn run as a global install runs it; through npx, which sets npm_command to exec, both are refused. yarn run version sets what yarn version sets, so this check can't tell them apart. The release's existing refusal of a version that already has a ## section still stops it whenever package.json holds a released version, as it stopped the run in release-changelog.mjs ignores its arguments and runs a release when invoked by hand #198. npm version <current> --allow-same-version also passes while bumping nothing; it's a deliberate version command, and the same refusal stops it for a released version. The other way round, a version command that sets no npm_command itself, launched by npx or from an npm script, inherits that runner's (exec, run), so it's refused after it has bumped package.json: observed with bun pm version run from an npm script, while npm version run the same way sets its own and passes. Run such a command directly.

  • The refusal reads release-changelog: refused, and this script changed nothing: it runs only as the version hook of a version command such as `npm version <patch|minor|major>` …, names what it saw in both variables, says that a version bump made before it ran stays in package.json, and names node scripts/release-changelog.mjs --help for what it does. The release's refusal of a version that already has a section, which now stops yarn run version against a released version, says the same about changing nothing and about a bump, and to release the pending entries under a new version.

  • --help or -h prints what the script does and how it's meant to run, and exits 0 without reading or touching anything, wherever it's run and whatever else is given. Without either, any argument is refused, since the script takes none. npm run version -- --help doesn't reach the script: npm appends the argument to the script string, so it goes to git add (observed), and the run is refused like any npm run version.

  • No changelog fragment. Like the script's earlier fixes (c3253b1, 6fe1685, b5087e9), this changes only maintainer tooling, which no host or strategy author sees, so this pull request takes the no-changelog label.

What each part is for

The change should stop the script releasing when it's run by mistake, and make --help safe, while npm version, bun pm version and yarn version keep releasing as they do on main.

  • The invocation check (one condition, scripts/release-changelog.mjs:248, with a comment on what the two variables can't see) is what stops a run by mistake. That covers a run by hand, which release-changelog.mjs ignores its arguments and runs a release when invoked by hand #198 reports, and also npm run version, bun run version, pnpm run version, a command npx starts and a preversion hook. Without it, a plain run that could release is stopped only by the release's refusal of a version that already has a section. That refusal works only while package.json holds a released version, as in release-changelog.mjs ignores its arguments and runs a release when invoked by hand #198, and its message then names the section, not how the script was run. When package.json holds a version with no section yet, such as after a bump made by hand or a release that stopped partway, a plain run would fold the fragments and delete them.

  • Accepting an unset npm_command (one clause of that condition) keeps bun pm version and yarn version releasing, since neither sets it. The cost is that yarn run version passes too, since both variables look as they do under yarn version. The section refusal still stops it while package.json holds a released version.

  • --help and -h first (one line, :241, plus the usage text). --help is how release-changelog.mjs ignores its arguments and runs a release when invoked by hand #198 was found. Refusing them as unknown arguments would be simpler, but release-changelog.mjs ignores its arguments and runs a release when invoked by hand #198 suggests handling --help, and someone asking the script what it does should get an answer.

  • Refusing any other argument (one if, :242). The script takes none, and without this a flag it doesn't know, such as --dry-run, would be ignored and the release would run, as on main.

  • The three refusal texts this change writes, for the invocation, an argument and a version that already has a section, say what the script saw and that nothing was changed. The invocation and argument refusals point to --help. The invocation and section refusals, the two of them that can follow a version command's bump, also say that the bump stays in package.json. That way whoever ran it knows the state they're in. They're text only.

  • The test helper's default environment (one const) is npm version's hook's, so the 24 existing cases keep running as the hook runs them.

  • The 13 new cases are:

    • one for each refused way of running it: by hand outside npm, npm run version, bun run version (whose run-script is what pnpm run version sets too), an npm_command of exec as npx gives, a preversion hook, and an unknown argument;
    • --help and -h, each in the hook's environment and outside npm;
    • --help after another argument;
    • the section refusal with no npm_command;
    • the bun pm version release.

    Each of the 9 removals listed under "Tests" fails at least one test.

Left out:

  • A --dry-run preview, which release-changelog.mjs ignores its arguments and runs a release when invoked by hand #198 also suggests. It would need a second path through main() that writes nothing, and tests to keep that path from drifting from the real one. What it would show is already at hand: the fragments are plain files in changelog.d/, and a release in a scratch checkout writes the exact section. With the refusal, trying the script is already safe, so --dry-run is refused as an unknown argument and changes nothing.
  • A bump check through git, comparing package.json's version with HEAD's. It would see whether package.json was bumped, which the environment can't show. So it would refuse yarn run version, which bumps nothing, and let a version command launched from an npm script release. It costs a git subprocess, the ways that can fail (no repository, no HEAD), and git fixtures in every test. The environment check covers what release-changelog.mjs ignores its arguments and runs a release when invoked by hand #198 reports.

Tests

At ec1023fb, on Linux x86_64, from a fresh npm ci:

The test helper now gives each case npm version's hook environment unless the case overrides it. The new cases run the script directly (both variables unset), through npm run version, through bun run version, with npm_command set to exec as npx gives a command it starts, as a preversion hook, and with an unknown argument, and each is refused with what it saw and leaves CHANGELOG.md and the fragments untouched. With npm_command unset against a version that already has a section, the section refusal stops the run and touches nothing. --help and -h each run both in the hook's environment and outside npm, against a fixture the release would refuse, and each prints the usage, exits 0 and changes nothing, as --help does after an unknown argument. They protect against what #198 reports, a run by hand that releases. The bun pm version case releases with npm_command unset, and protects the release path bun had before this change.

Out of scope

  • agent-framework (14e25dc0), membrane (8242eb83) and chronicle (3575686) run the same scripts/release-changelog.mjs as their version hook, and connectome-host (38b55da) runs scripts/release-changelog.ts the same way. None checks how it's run.

  • No changelog fragment: this change is internal-only (maintainer tooling), so it takes the no-changelog label.

Ezra-2423 read it internally, besides an automated internal review.

— Hamish-2699 (Claude Opus 5.5). Oscar-1808 (Claude Opus 5.5) decided that bun pm version keeps releasing, after Ezra-2423 found that an earlier head refused it. Anarchid reported the issue in #198.

Hamish-2699 and others added 6 commits October 16, 2026 14:08
…n hook; --help says what it does

The script is package.json's `version` hook and reads the version from
package.json, which npm version has already bumped when it runs the hook.
It took no arguments, so `node scripts/release-changelog.mjs --help`, or
any other by-hand run, released into the version package.json already
held: it folded changelog.d/ into CHANGELOG.md and deleted the fragments.
Anarchid found it reviewing anima-research#156 (anima-research#198); it refused there only because
`## 0.14.1` already existed.

- It now runs only when npm_command is `version` and npm_lifecycle_event
  is `version`, which is npm version running its version hook. Both are
  needed: `npm run version` runs the same script with npm_command `run`
  and bumps nothing (an `npm run version -- --help` passes the flag to git
  add, not to the script), and a preversion hook runs before the bump.
  Anything else is refused with what it saw and nothing changed. The issue
  suggested checking npm_lifecycle_event alone, which lets `npm run
  version` release; this refuses it too.
- `--help`/`-h` prints what the script does and how it's meant to run,
  and exits 0 without touching anything, wherever it's run. Any other
  argument is refused: it takes none.
- No changelog fragment: like the script's earlier fixes (c3253b1,
  6fe1685, b5087e9), this is maintainer tooling no host or strategy
  author notices.

Tests: the black-box run helper now gives each case npm version's hook
environment unless the case overrides it. New cases: run directly (both
variables unset), through `npm run version`, as a preversion hook, and
with an unknown argument each refuse with what they saw and leave
CHANGELOG.md and the fragments untouched; `--help` in the hook and `-h`
outside npm print the usage, exit 0 and change nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… command; tests pin bun's runs and help on any input (hm-cm198)

Answers the haiku review of e5760a6 (hm-cm198, wf-1957).

- The refusals start "refused", say the script changed nothing, and name
  `node scripts/release-changelog.mjs --help`, since through `npm run
  version` a --help reaches `git add`, not the script (I5, I11). The
  hook-shaped refusal adds that a version bump made before it ran, as by
  `bun pm version`, stays in package.json: bun writes the new version
  before running the hook (observed with bun 1.4.3), so the refusal leaves
  it bumped (I3). An unknown argument's refusal says it takes none but
  --help or -h.
- The header and USAGE say the check guards against running the script by
  mistake, from the environment npm gives its hook, which any process can
  set (I2); that a run any other way is refused, and why; that it takes no
  arguments but --help or -h (I8); and that npm then makes its release
  commit and tag as it's configured to, which --no-git-tag-version turns
  off (I13).

Tests: refusal rows for `bun run version` (npm_command run-script) and
`bun pm version` (npm_command unset, npm_lifecycle_event version), which
only the strict comparison kept out (I1). --help and -h each run in the
hook's environment and outside npm (I4), on a fixture the release refuses
(nothing to release), so help that read its inputs first would fail (I12).
The help cases' stderr assertion is gone: the run helper returns an empty
stderr on exit 0, so it couldn't fail (I10). The refusal regexes match the
new wording.

Not changed: I6 is not real: npm 7.24.2, 8.19.4, 9.9.4, 10.9.9 and 11.19.1
each give the version hook npm_command=version and
npm_lifecycle_event=version (through npx on the VPS). I7: --help or -h wins
over other arguments, as in most tools. I9: the help's prose isn't pinned,
since that would pin wording.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pm version still releases

efa7072 refused the hook unless npm_command was "version", which refused
`bun pm version`. bun bumps package.json and then runs the version hook, as
npm version does, but sets no npm_command, and on main that release comes
out whole. Refusing it took away a path that works today and guarded
against no mistake, since a version command that has already bumped isn't
one (the reader's finding; the decision to keep bun working).

The check now refuses unless npm_lifecycle_event is "version" and
npm_command is "version" or unset. Every refusal stays: a direct run (both
unset), `npm run version` ("run"), `bun run version` and `pnpm run version`
("run-script"), npm version's preversion hook, and an unknown argument.

What else passes: an environment with the event and no npm command.
`yarn version` makes it after bumping, which is right. `yarn run version`
makes it too, bumping nothing (yarn 1.22.22 with npm_command cleared, as a
global yarn runs), and only the release's own refusal of a version that
already has a section stops that. The comment above checkInvocation says
so.

Also:
- The refusal names a version command "such as npm version", and drops
  its clause about a bump made before it ran: no refused path bumps first
  now.
- USAGE and the header say npm version's or bun pm version's hook.
- The bun pm version case moves from the refusals to a test that it
  releases: the fragment is folded into the new section and deleted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mp stays; tests pin npx, help precedence and the section refusal (hm-cm198-r1)

Round 2, answering the delta review hm-cm198-r1 (wf-2048) of efa7072..5ec683a.

The check is unchanged: it refuses unless npm_lifecycle_event is "version"
and npm_command is "version" or unset. What changed is what the texts say
about it.

- I1: the usage, the header and checkInvocation's comment promised that
  only npm version's and bun pm version's hooks run. The check reads two
  variables and can't see a bump, so yarn version passes (rightly) and
  yarn run version passes too (it bumps nothing). The usage now says the
  environment must look like a version command's hook, names npm version,
  bun pm version and yarn version, and says yarn run version looks the
  same and the release's section refusal is what stops it. The comment
  names both runs it judges wrongly.
- I2, I10: 5ec683a's message said no refused path bumps first, and its
  refusal dropped the sentence about a bump made before it ran. Wrong: a
  bump-first version command launched by npx (npm_command "exec") or from
  an npm script ("run") inherits that npm_command and is refused after its
  bump, and so would a postversion hook. The refusal says again that a
  bump made before it ran stays in package.json, without bun as its
  example, and the comment names the inherited case.
- I4: the section refusal, the one stop for yarn run version against a
  released version, now leads with "refused, and this script changed
  nothing" and says to release under a new version with npm version.
- I5: the header and usage say the version command makes the release
  commit and tag, not npm alone.
- I8: the comment's "only the section refusal" now names nothing to
  release as well.

Tests (I3, I6, I9): --help after an unknown argument still prints the
usage (help wins over other arguments); npm_command "exec" is refused, with
the bump sentence; an unset npm_command against a version that already has
a section is refused by the section refusal and touches nothing.

Not changed: I7 (npm version <current> --allow-same-version passes) is a
deliberate version command, and the PR text names it beside yarn run
version; I11 is the PR text's; I12 isn't real (the refusal says it runs only
as the version hook and shows npm_lifecycle_event 'preversion').

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…inherit a launcher's npm_command

The comment said any version command launched by npx or from an npm script
is refused after its bump. Checked through npm run on the VPS
(runs/cm198/npm-script-launch.txt): a script running bun pm version gives
the hook npm_command 'run' and bumps, so it's refused; one running npm
version gives npm_command 'version' and passes, since npm sets its own. The
comment now says it's a command that sets no npm_command itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s in package.json

After a version command bumps to a version that already has a section, the
refusal's advice to run npm version again needs package.json restored
first, since npm version refuses a working tree that isn't clean. The
refusal now says the bump stays, as the invocation refusal does. Against
yarn run version nothing was bumped, so the advice works as it stands
there (the reader's optional point on round 2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nissa-seru nissa-seru added the no-changelog PR intentionally ships without a CHANGELOG entry label Oct 17, 2026
@greptile-apps

greptile-apps Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5 Tier: apex

[Low impact] This PR appears safe to merge; no actionable issues were found.

Summary

Adds an invocation check to scripts/release-changelog.mjs so accidental direct runs refuse before reading or changing release files.

  • The changelog release script checks its invocation before releasing a version.

Reviews (1) · Last reviewed commit: "fix(release): the section refusal says a..." · Reviewed by Greptile

@nissa-seru
nissa-seru merged commit 2d552dd into anima-research:main Oct 18, 2026
9 checks passed
nissa-seru pushed a commit to nissa-seru/context-manager that referenced this pull request Oct 18, 2026
Main moved 212 commits since this branch's head (3b6fb9c), through
anima-research#211 (2d552dd). The merge is clean; this branch's
compressionResponseInputTokens, accounting version 3 and fallback spec
apply over main's unchanged versions of those lines, and main's new
call site (the ladder's base count) goes through the same function.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tel-0s added a commit that referenced this pull request Oct 18, 2026
…ls and the R2 provenance guards meet the derived context

What main brings since 9db44db (20 merges): the greedy-fold and kv-stable
fixes for a chunk's summary at a level (#194, #195), the shadow-pick
kv-overlap log (#222), the tool-prose regime's key order (#232), named
bodies in attribution (#206), the R2 journal and provenance guards (#146),
carried-projection overlap (#186), blob-ref media types (#226), malformed
content refused at the store's rule (#184), the omission-marker cap (#218),
the before-user fallback (#220), the lock on membrane (#221), one lister per
summary (#208), over-budget cleared-by (#190), serialized media (R2), the
discarded-cache counts (#199), and the release-changelog hook (#211).

One conflict, src/strategies/autobiographical.ts: both sides added a
declaration after `fills`: the branch's `AutobiographicalRenderingFrontier`
(what `exportRenderingFrontier` hands a fresh instance over a derived
context) and main's `sentForm` (a block as a compression request's route
sends it). Both kept, the branch's first. No seam decision: nothing main
changed reaches `derive()`, the branch-bound states or the frontier export.

Build clean; suite 1862 tests, 1862 pass, 0 fail, 0 skipped (branch
chronicle linked, one membrane copy).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-changelog PR intentionally ships without a CHANGELOG entry

Projects

None yet

Development

Successfully merging this pull request may close these issues.

release-changelog.mjs ignores its arguments and runs a release when invoked by hand

1 participant