Repository navigation
fix(release): keep release PR qualification stable - #19
Conversation
|
Warning Review limit reached
Next review available in: 14 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughThe release workflow now reuses the automation identity admitted in candidate metadata for repair and publication validation. The maintenance job verifies the Release Please token identity. Test helpers synchronize all release version surfaces. ChangesRelease provenance identity validation
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
* fix(dev): detect a superseded development cache version (#18) * fix(dev): detect a superseded development cache version A version bump leaves the previous cache directory behind. Claude Code stops listing it and writes `.orphaned_at` into it, so it becomes files nothing reads inside the profile this lifecycle owns. Existing orphan detection could not see this. It runs only when Claude lists no installation at all, and it defines an orphan by a dangling link. A superseded directory dangles nothing: it points at the same live payload as the installed one, so every link resolves. Registration is the discriminator, not link health, so detection now compares the version directories on disk against the install path Claude reports. `check` and `install` refuse while one stands, because reporting a profile clean while it holds files nothing reads is the silent result this detection exists to end. `restore` and `watch` stay reachable, since a superseded directory is inert until the next install reuses the name. * refactor(dev): drop the unused orphan marker constant ORPHAN_MARKER_FILE was declared and never referenced. Detection reads registration, not the marker, so the constant asserted a dependency the code does not have. Records why the marker is not the test: Claude Code writes `.orphaned_at` and later collects the directory on its own schedule, so gating on it would bind this lifecycle to an undocumented internal file and miss any directory left behind without one. --------- Co-authored-by: Nathan Vale <hi@nathanvale.com> * feat(dev): report whether the payload came from the last successful build (#14) * feat(dev): report whether the payload came from the last successful build A failed build was silent. Its message scrolled past in a terminal the workflow gives nobody reason to watch, `/reload-plugins` loaded the previous build, and the session served code that was not on screen. `build` now writes a receipt after every attempt, including a failed one, and `check` verifies it against the payload digest rather than believing it. The receipt lives at `.dev/claude/build-receipt.json`, outside the payload. Inside `plugin/` it would have no counterpart symlink in the installed cache, so `verifyLinkedPayload` would report the installation as no longer canonical; `validateBunOnlyPayload` separately rejects unrecognized payload surfaces, and the release archive is compared byte for byte across two builds. Freshness is read before `prepare` rebuilds. Evaluating it afterwards compared the payload against the receipt that same build had just written, so every answer was `fresh` and a payload edited since the last reload reported ready. `generate:check` moves inside `build.ts` rather than running as a separate `&&` stage, where it failed before any receipt was written and left the previous `succeeded` receipt to be read as proof the payload was current. * test(dev): assert receipt identity against a fixture, not the ambient build The test read the repository's own receipt, so it passed on a machine that had built and failed in CI, where `.dev/` is gitignored and never present. It proved the local machine's history rather than the writer's behaviour. It now writes a receipt into a controlled checkout and asserts on that. A second test covers the payload copied outside git: the git fields are omitted rather than faked, and the digest that proves freshness is still recorded. Verified by moving the local receipt aside and running the full suite in CI's condition: 745 pass, 0 fail. * fix(dev): keep one freshness state across install re-inspection Applies four review findings. The re-enable path reported two freshness states at once. `install` re-inspects the profile after enabling the plugin, and that second inspection reads the profile rather than the receipt, so `result` fell through and re-evaluated freshness after the build had already rewritten the receipt. The status read `fresh` beside a `nextAction` describing the state before the build. That is the masking this feature exists to end, reappearing in its own install path. The captured snapshot now carries forward. `writeBuildReceipt` read plugin.config.json inside its own write guard, so an invalid configuration returned before any receipt was written. The failure most likely to break a build therefore left `check` reporting `unproven` instead of `build-failed`. The version now falls back to `unknown`, because the field is required and a reader rejects a receipt without it. `readBuildReceipt` accepted any numeric schemaVersion, so a receipt from an incompatible schema could pass digest verification and report `fresh`. The unwritable-location test never reached the write: its checkout had no configuration, so it returned at the read above. It now uses a valid configuration and a locked directory. Each fix carries coverage that fails without it; the re-enable test was confirmed to reproduce `fresh` against `bundler-failure` before the fix was restored. --------- Co-authored-by: Nathan Vale <hi@nathanvale.com> * fix(dev): name the production installation the install preview replaces (#19) * fix(dev): name the production installation the install preview replaces The preview is the only disclosure before a profile write, and `--apply` uninstalls the production Plugin Installation and removes its Marketplace before installing the development one. The message read "Review the captured production state, then rerun with `--apply`" whether or not production was present, so it asked for consent to an action it never named. `sideEffects` could not carry this: it records mutations that have already happened, and a planned one there would misreport the profile as changed. The preview now names the plugin id, the `--keep-data` flag that makes the replacement recoverable, the Marketplace removal, and `restore` as the exit. With no production installation present it claims no replacement. * fix(dev): correct the preview recovery command and prove every branch Applies review findings across three axes. The preview named `bun run dev -- claude restore` as the command that puts production back. Bare `restore` previews like every operation here, so an agent following that sentence runs a no-op and reports the profile restored. It now names `restore --apply`. This was a fail-closed defect inside the string added to make the operation honest. The new doc comment was inserted between `readyNextAction`'s comment and its function, stripping documentation from a function this change does not own. The preview helper now sits after it with its own comment. `${pluginName}@${pluginName}` was reassembled at five sites. They read `productionPluginId` now, so the id the glossary names has one owner. The absent-case test asserted only the absence of "uninstall", which the replaced string also satisfied, so it pinned nothing. It asserts the sentence. A Marketplace-only production state is a third reachable branch that was unproved; it has coverage. All three preview tests were confirmed to fail against the string they replaced. The dev-mode skill told an agent to preview and seek approval without saying the preview may name a production uninstall. It now says to quote that sentence rather than summarize it. * refactor(dev): carry the inspected plugin name on the state it describes The install preview re-read plugin.config.json to name the plugin, and a comment claimed the name was unavailable any earlier because the restoration snapshot is captured after the preview returns. That reason was wrong. Both inspectors already resolve the name to read the profile at all, and `prepare` returns their result, so the state handed to the preview was produced by a function that had the name and discarded it. The snapshot was never the only carrier. InspectedState now carries `pluginName`, and the preview reads it from the state it was given. A consumer naming the plugin therefore names the one the inspection named, instead of re-reading the configuration and being able to disagree with the state it holds. * docs(dev): describe the preview replacement as the conditional it is The doc comment and the dev-mode skill both stated that `--apply` uninstalls the production Plugin Installation and removes its Marketplace, as one unconditional pair. The code branches on each separately, and a Marketplace can stand without its Plugin Installation, so a reader of either text expected a mutation the profile may never see. Both now name the conditions. The comment also records that the inverse case throws PRODUCTION_MARKETPLACE_MISSING before reaching the preview, so the reachable states are named rather than implied. --------- Co-authored-by: Nathan Vale <hi@nathanvale.com> * fix(test): give the dev lifecycle cases an explicit subprocess budget (#20) `isolated profile snapshots cannot overwrite each other's restore state` timed out at Bun's 5s default on an isolated run, not only under load. The cost is the lifecycle, not the assertions. Each `run` here spawns `dev.ts`, and the lifecycle rebuilds the payload before it inspects anything, so one invocation measures ~940ms. That case makes four and runs at 4.2s; the disabled-link repair case runs at 2.5s. Every other case in the file measures 1.3s or less and is left alone. The budget is declared once, beside the `run` helper that spends it, matching how update-hosted.test.ts states its own process timeouts rather than inheriting one. Raising the ceiling does not make these faster: the payload rebuild per invocation is the real cost, and removing it would change the spawn seam. Co-authored-by: Nathan Vale <hi@nathanvale.com> --------- Co-authored-by: Nathan Vale <hi@nathanvale.com>
Summary
Release Please PRs now stay qualified when the configured automation account changes, while historical releases remain bound to the identity recorded at admission.
The release tests now build explicit version fixtures instead of inheriting the repository's current version. This keeps the suite valid after
0.1.0and clears the path to publish0.1.1.Related: #17
Validation
bun run prove:allpassed.Summary by CodeRabbit