fix: tell a lagging read from a broken release - #17
Merged
Conversation
v5.0.0 published all 716 packages in the planned order — 0 ordering defects over 7593 edges, measured from the log — and still ended red over 76 closure gaps, every one of them naming a dependency this same run had already published. `@girs/matekbd-1.0` was readable 4m12s after its own publish returned success. One `errors` counter stood for two different facts, a failed publish and a closure not yet readable. That is where `110.6%`, `Total: 792` (716 + 76 counted twice) and `76 of 792 package(s) failed to publish` came from. Classify a surviving gap against the PLAN instead: a dependency this run publishes earlier or in the same group is lag, one it publishes later is an ordering defect, one it never publishes is permanent. Only the last two are fatal. Whether a gap is a defect was never a function of how long the registry is asked, so the lag budget drops from 60s to 15s — it buys quiet, not correctness, and it cost the v5.0.0 sweep about an hour (19.2 s/package against the 10.7 of the release before). Phase 3 therefore becomes the check with the last word, so it runs whenever nothing failed to publish, and retries a resolution miss up to four times before believing it. That is also what left three SDK channels red after publishing cleanly. Claude-Session: https://claude.ai/code/session_012KM53BXFvEdP4PLgcojKCS
This was referenced Sep 11, 2026
JumpLink
added a commit
that referenced
this pull request
Sep 11, 2026
Ported from #15, now closed: everything else it carried arrived via #16 and #17. A sweep killed by `timeout-minutes` reports that the RUNNER exceeded its maximum execution time, naming neither the package it was on nor how far it got. `NPM_DEADLINE_MIN` (default 300, under release.yml's 360) makes the publisher fail by name first. Elapsed and ETA go on every progress line for the same reason: the run-level `updatedAt` GitHub exposes does not advance while a job streams logs, so from the API a sweep that is working looks exactly like one that is wedged. That reading is what got the 4.8.0 sweep cancelled at 38% while it was publishing normally. Both decisions are pure functions in publish-plan.ts and held to cases that go both ways — zero disables the deadline rather than expiring instantly, which a plain `now > start + budget` gets wrong. Claude-Session: https://claude.ai/code/session_012KM53BXFvEdP4PLgcojKCS
JumpLink
added a commit
that referenced
this pull request
Sep 11, 2026
`@girs/sdk-gnome-51@5.0.0` published cleanly at 20:12:33 and was reported uninstallable at 20:15:35. The retry from #17 did run, three times, and then ran out: four attempts at 60 s is three minutes, and readability lag on that registry the same day reached 4m12s (`@girs/matekbd-1.0`) — a number already measured when the budget was chosen. Attempts are the wrong unit. One probe costs 5.5 minutes on the 716-package set and seconds on a single SDK channel, so the same count means wildly different patience. Budget the wall clock instead: 10 minutes, re-asking every 30 s. The packument's own `time` entry is no guide either: it said 20:12:32.972 for a version no install could resolve three minutes later. Written and readable are different events. `shouldRetryResolution` keeps the arm that matters more than the budget — a 403, an EINTEGRITY, a corrupt tarball are never retried, or one clear red becomes a slow one. Claude-Session: https://claude.ai/code/session_012KM53BXFvEdP4PLgcojKCS
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.
The v5.0.0 sweep was the first real run of the topological publisher, and it worked: measured
from the run's own log, 0 ordering defects over 7593 edges, with the only out-of-order edges
being the 11 inside the six-member cycle, open for 27 seconds. Down from 508 packages over 128
minutes.
It still ended red, over 76 closure gaps. Not one of them was a defect. Every single one named a
dependency this same run had already published — the registry simply had not caught up. Measured
on that run:
@girs/matekbd-1.0was readable 4m12s after its own publish returned success.What was wrong
One
errorscounter stood for two different facts: a publish that failed, and a closure that isnot readable yet. Everything else followed from that.
A reader of that log looks for 76 broken publishes that do not exist.
What this changes
A surviving gap is classified against the plan, not the registry. Whether a gap is a defect
was never a function of how long npm is asked:
lagordering-defectnot-in-releaseThe middle row already threw before this PR; it never fired in v5.0.0, which is the independent
confirmation that the plan was right. The last row is now its own case rather than an unnamed one.
The lag budget drops from 60s to 15s. It buys quiet, not correctness — a gap that resolves
inside the window never reaches the log, and nothing else. At 60s it cost the v5.0.0 sweep roughly
an hour of its two hours: 19.2 s per package against the 10.7 s of the release before it.
Phase 3 gets the last word, so it has to run and has to be patient. It now runs whenever
nothing failed to publish (before: only when the gap count was also zero, so lag skipped it), and
it retries a resolution miss up to four times at a minute apart. That is also the fix for the three
SDK channels that published cleanly and were then reported uninstallable by this very probe,
seconds later.
Validation
npm run check,npm test— 23 tests, 4 of them new for the classification, one asserting thesame gap yields three different verdicts under three plans
npm run test:e2egreen, including both negative arms — the fatal paths still exit 1classifyGapforcedto return
lagfor everything, the e2e negative arm flips red. Without that mutation test thisPR would be indistinguishable from one that quietly switched the gate off.
🤖 Generated with Claude Code
https://claude.ai/code/session_012KM53BXFvEdP4PLgcojKCS