Repository navigation
build(release): an open release-hold issue stops the release - #329
Merged
Merged
Conversation
added 5 commits
October 14, 2026 03:06
When releasing main as it stands would harm someone running it, an issue labelled release-hold says what has to happen first. Nothing in the release read such a thing before: 0.21.1 and 0.22.0 were both published while anima-research#300, the uncapped retryAfterMs sleep, was open. scripts/release-holds.mjs lists the repository's open issues with the label (pull requests aren't holds: one isn't on main until it merges) and exits 1 naming each, or when the holds can't be read. It runs as npm's preversion hook, so npm version stops before it bumps, commits or tags anything, and as the publish workflow's first step for the pushed tag, which both releasing jobs wait on. The repository is GITHUB_REPOSITORY or the one package.json names; GH_TOKEN or GITHUB_TOKEN is sent when set, and the workflow's build job gets issues: read for it. The tests run the real script against a local stand-in for GitHub's issues endpoint: no hold, a hold beside a labelled pull request, only pull requests, several holds, GITHUB_REPOSITORY and the token, an error answer, and no repository to ask. CONTRIBUTING says when to file a hold and what it stops.
… publish meets them too From the read of 94d02ee: - The repository is the one package.json names, so a fork's workflow or a copy asks the package's home; GITHUB_REPOSITORY is used only when package.json names none. - prepublishOnly runs the check, so npm publish from a maintainer's machine stops while a hold is open, as npm version and the workflow do. - The workflow's step runs for a tag only: a dispatch on a branch releases nothing, so an open hold doesn't fail it. - Messages say what a maintainer can do: an unreachable GitHub names the network error's code (or 'no answer within 30 s'); 401 points at the token; 403 and 429 at the token's access or the rate limit; 404 at package.json's repository field or a private repository's token. - Tests: package.json over GITHUB_REPOSITORY, GITHUB_REPOSITORY when package.json names none, a 404, and GitHub out of reach (a port just freed: fetch refuses the discard port as 'bad port' without connecting).
prepublishOnly now runs the release-hold check inside the publish job's npm publish too. That job had no GITHUB_TOKEN for it and no issues: read, so the request went out unauthenticated from a shared runner address, where GitHub's limit of 60 an hour could fail the publish after the build job's check had passed. The job gets issues: read, and its Publish step passes GH_TOKEN, so the check there asks as the build job's does.
…ms, every page, both release jobs From the review of 97487d5 (sw-af-holds): - The repository field is read in each form npm reads: a github.com URL, github:owner/repo, or owner/repo. A field that names no GitHub repository the check can ask stops it, saying so, rather than falling back to GITHUB_REPOSITORY, which is used only when package.json names none. - Every page of open labelled items is read, so a hold behind a hundred labelled pull requests still counts (up to 5,000 items, past which it stops and says so). - The GitHub release notes job checks the holds too, with issues: read and the job's token, so a hold filed during the run stops the notes as prepublishOnly stops the npm publish. - The stop message says how to go on where it ran: re-run the workflow for the tag, run npm version again, or run npm publish again. - Refusals: 403 and 429 without a token say to set GH_TOKEN, with one that the token can't read the issues or its rate limit is spent; 404 says which; a 5xx says to retry; another status no longer promises a retry helps. A body that can't be read is named as such. - The message is written synchronously before the exit, so a piped stderr gets it whole. - CONTRIBUTING: someone who can't label asks a maintainer; close a hold that no longer applies; both release jobs refuse; the check also stops when it can't read the holds; npm's --ignore-scripts skips it. - Tests (21): npm itself runs this repository's preversion and prepublishOnly in a throwaway package (npm version stops before the bump with a hold open and bumps without one; npm publish --dry-run stops), each repository form, a field naming no GitHub repository, a package.json that isn't JSON, GITHUB_TOKEN alone, the refusals with and without a token, a 200 that isn't a list, a trailing slash on the API URL, a hold on the second page, the message per caller, and the request asserted for labelled pull requests alone. The throwaway directories are removed.
When package.json names no repository, the hold check asks the one GITHUB_REPOSITORY names. A 404 for it then said to check package.json's repository field, which named nothing. The hint now names whichever setting named the repository: package.json's repository field, or GITHUB_REPOSITORY. A test runs both: package.json naming a repository (with GITHUB_REPOSITORY set to another, which the check ignores), and package.json naming none. Under the old wording the second case fails.
|
This was referenced Oct 14, 2026
This was referenced Oct 14, 2026
Merged
nissa-seru
pushed a commit
to nissa-seru/agent-framework
that referenced
this pull request
Oct 14, 2026
…nima-research#286's branch anima-research#333 (a continuing start keeps the turn's routing) conflicts with this branch at the two sites where both change how deferred messages are stored: - At the tool boundary, this branch drains only injectable messages, stores them through writeDrainedInOrder (queue order, cut at the first that can't be stored) and injects what was stored. anima-research#333 added noteForOtherResidents for each message stored there, and recorded each injected message's id (injectedIds) for a boundary that leaves its injections to the turn's next start as arrivals. Both are kept: the write records each stored message's id, and the loop over what was stored notes it for the other residents and records its injection's id. - At the turn-start flush, this branch stores through writeDrainedInOrder, and anima-research#333 noted each stored message for the turn now starting (noteArrival) and for the other residents. Kept, in the loop over what was stored, so a store that throws notes nothing and the entries it cuts stay queued. anima-research#333's flush at a start that ends before its stream, in startAgentStream's finally, merged as text but not as types. It re-added through addMessage with deferredWriteId, which this branch replaced with fromQueue { id, seq }, stored each message on its own, logging and dropping one the store refused, and was gated on every agent's tool cycle (pendingAssistantBlocks.size === 0). It now goes through readdDrained, in queue order and cut per target as every other flush is, and is gated on the agent's own cycle, as the turn-end and withdrawn-turn flushes are. Its tests in present-while-acting follow: - The test that a message deferred in a failed start lands then now has another agent's tool cycle pending while the start fails, and asserts the flush stored the message then. - The test that the flush stored each message on its own, the one behind a refused message stored and the refused one lost, is replaced by its form under the cut: the store refuses the first of two messages, both stay queued in order, nothing behind the refused one is stored, and once the store takes it the next start stores both in order. (Its refusal hook keyed on deferredWriteId, which no longer exists, so it refused nothing here.) The fragment names that flush among those that wait only on their own agent's state. addMessage merged without a conflict: anima-research#333's noteArrival and noteForOtherResidents after a store sit beside this branch's fromQueue re-deferral and per-target gate. anima-research#329 (release holds) changes no file this branch changes.
nissa-seru
pushed a commit
to nissa-seru/agent-framework
that referenced
this pull request
Oct 16, 2026
…pawn wait and transport listeners, and TypeScript 7 main since cd036e0 brings anima-research#329, anima-research#333, anima-research#273, anima-research#368, anima-research#343, anima-research#340, anima-research#365, anima-research#341, anima-research#352, anima-research#338, anima-research#274 (TypeScript 7), anima-research#347, anima-research#346, anima-research#348 and anima-research#344 (no hang after a signal kill; no uncaught transport or spawn errors). Merged, not rebased, so the commits slimepriestess and the round's readers read stay as they were (Ada-1017, room-203 #105340). `git show --remerge-diff` shows the resolution alone. Conflicts, three files: - src/mcpl/transport.ts: - anima-research#344's listeners for a read error on the child's stdout or stderr go in beside the stdin listener this branch already had. - close() stays this branch's reap (SIGTERM, then SIGKILL, then an explicit failure; one shared attempt). anima-research#344's version waited for an exit that kill() made likely, and its change there, counting a signalCode as an exit (anima-research#331), the reap already does. - src/mcpl/server-connection.ts: - handshake keeps this branch's signature (the abort signal and the negotiated protocolVersion) and its abortable open, and takes anima-research#344's logAfterSettled. That listener takes over 'error' once the initialize race settles, comes off just before a successful return, and stays on a failed handshake's transport. - The race's cleanup keeps both lines: this branch's removal of the abort listener and anima-research#344's logAfterSettled. - The reconnect attempt keeps this branch's reconnectOnce, which reaps the previous launch before another starts and closes a handshaken transport itself when close() has ended the attempt. anima-research#344's version kept that transport for close() with a logging listener. That intent carries over as a listener put on before the attempt's own close, since the transport isn't wired and the handshake's listeners are off. - package.json: devDependencies take both @babel/parser (anima-research#274) and @modelcontextprotocol/server (this branch). Beyond the conflicts, for main's async StdioTransport.spawn (anima-research#344): - SpawnerStdioTransport.start() awaits it. So a modern stdio command that can't be spawned rejects start() with its spawn error, and the connect fails with that error instead of waiting out its 30 s discover probe. This is R1-4's fix (Anarchid's review, room-364 #105270), and its test follows this merge. - The tests that wrap spawn to make a child unreapable await it, in mcp-modern-outcomes, mcp-server-ownership and mcpl-legacy-lifetime. So does the unreapable-close test's own spawn. tsc can't see the old synchronous wrappers, since they reach the child through a cast, and they hung those suites. - anima-research#344's test of a transport kept for close() during a reconnect now follows this branch's reconnectOnce. The attempt closes that transport itself and close() waits for it, so the test resolves the held close before it awaits the attempt. It's retitled for what it checks: the transport logs the errors it reports while it closes, and after. tsc (TypeScript 7) is clean. A trial of this resolution on main bdc5dc4 with anima-research#344's head 8387393, the head that merged, passed every test file on Node 24.21.0 and Node 20.20.2 (J10968). The only failures were 69 in a stale dist/test/mcp-modern-http-2025.test.js that the VPS copy kept from anima-research#337's runs. The full check of this commit follows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 16, 2026
Merged
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.
An open issue labelled
release-holdnow stops a release. Whoever finds that releasingmainas it stands would harm someone running it files one, saying what has to happen first, and closes it once that has happened.@antra-tess @Anarchid, this changes how you release, so please look before it merges.
npm versionandnpm publishnow ask GitHub for open holds first, and the publish workflow asks again for a tag.Why
Nothing in the release read such a thing before. #300, the uncapped
retryAfterMssleep, was opened on Oct 12 at 16:15Z and closed on Oct 14 at 04:08Z, once #251 merged. 0.21.1 (Oct 13, 13:37Z) and 0.22.0 (Oct 13, 18:32Z) were both published while it was open. 0.22.0 required membrane^0.5.87, the first release carrying antra-tess/membrane#56, which attaches a provider's whole retry-after hint to the errors it returns. #300 itself said hosts shouldn't take that membrane before the fix, and a host that installed 0.22.0 got the sleep.The change
scripts/release-holds.mjslists the repository's open issues labelledrelease-hold, reading every page, and exits 1 naming each while any is open. A pull request with the label isn't a hold, since it isn't onmainuntil it merges. The repository is the GitHub repository thatpackage.json'srepositoryfield names, as a URL or in npm's shorthand, so a fork asks the package's home.GITHUB_REPOSITORYis used only whenpackage.jsonnames none. A token inGH_TOKENorGITHUB_TOKENis sent when present, and a public repository's issues read without one.npm versionruns it aspreversion, so a held release stops before anything is bumped, committed or tagged. With no hold open, it costs one request to GitHub.npm publishruns it asprepublishOnly, before the build, so a release published by hand stops too.prepublishOnly, and the release-notes job has a step of its own. A held tag reaches neither npm nor the GitHub release notes, and a hold filed after the build job's check still stops both. Each of the three jobs getsissues: readand passes the job's token. A dispatch on a branch releases nothing, so it isn't held.npm versionornpm publishagain.--ignore-scriptsskips it, as it skips every lifecycle script.It doesn't stop a hazard nobody files, or a publish with npm's scripts turned off.
Tests
test/release-holds.test.tsruns the real script against a local stand-in for GitHub's issues endpoint, each case in a throwaway directory with its ownpackage.json. It covers:repositoryfield,package.jsonoverGITHUB_REPOSITORY,GITHUB_REPOSITORYwhenpackage.jsonnames none, a field that names no GitHub repository, apackage.jsonthat isn't JSON, and no repository at all;GH_TOKEN, or fromGITHUB_TOKENalone;npm versionand undernpm publish.The last three cases run npm itself with this repository's
preversionandprepublishOnlyentries. With a hold open,npm versionstops before the bump andnpm publishrefuses. With none,npm versionbumps.Evidence
At
cb2a3365, on Linux:npm run typecheckandnpm run buildare clean.--test-force-exitand without, and on Node 20.20.2 with it.release-holdspasses 22/22 in each run.Ezra-2423 also ran it by hand. With a hold open,
npm version patchexits 1 and leaves the version as it was. Against GitHub itself, with no token, the check exits 0.Review
The review workflow's two batteries read the change at
97487d58, and the fixes for its findings are ine1525a84. Ezra-2423 (Claude Opus 5.5) read each version,94d02ee9,97487d58,e1525a84andcb2a3365, and approved each. His six points on the first are in693459f1and97487d58, andcb2a3365takes his note on the 404's wording.We'll merge this once our pre-merge checks pass on its merge with the then-current
main, and not before 15:00 UTC on October 14. After it merges, we'll create therelease-holdlabel.— Ada-1017 (Claude Opus 5.5)