fix(registry): exempt proven first releases from holdback - #3078
Conversation
🦋 Changeset detectedLatest commit: e10de24 The changes in this PR will be included in the next version bump. This PR includes changesets to release 24 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Scope checkThis PR changes 686 lines across 27 files. Large PRs are harder to review and more likely to be closed without review. If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs. See CONTRIBUTING.md for contribution guidelines. |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
docs | 04f2fd2 | Sep 12 2026, 08:14 AM |
🚀 Deploying Preview to Cloudflare 🚀Preview URL: https://codex-1350-first-release-history.try.emdashcms.com, https://codex-1350-first-release-history-emdash-playground.emdash-cms.workers.dev (commit e10de24)This URL reflects your latest Preview deploymentPreview Deployments by commit
|
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/auth-atproto
@emdash-cms/blocks
@emdash-cms/cloudflare
@emdash-cms/contentful-to-portable-text
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/plugin-cli
@emdash-cms/plugin-types
@emdash-cms/registry-client
@emdash-cms/registry-lexicons
@emdash-cms/registry-moderation
@emdash-cms/registry-verification
@emdash-cms/sandbox-workerd
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-field-kit
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
There was a problem hiding this comment.
This is a substantial but well-scoped change: it tracks per-package release-history completeness in the aggregator, exposes the evidence through the registry lexicon/client, and lets the admin UI and core install handler exempt a proven first release from the optional minimum-release-age holdback. The design is fail-closed (backfill, dead-letter, missing or multi-release evidence stays held back; tombstones still count), which matches the intent in #1350 and fits EmDash's registry trust model.
What I checked:
- SQL safety in
apps/aggregator/src/routes/xrpc/listing-query.ts— the new columns are a constant string, not interpolated with user input. - Data integrity/transaction boundaries in
records-consumer.ts— history writes are batched with package/release writes, D1's transactional batch keeps them consistent;ON CONFLICT DO NOTHINGand the explicitUPDATEpreserve existing incomplete history. - Lexicon and client validation — new fields are optional in the schema and validated by atcute;
isProvenFirstReleaserejects malformed evidence. - Admin and core install gates — both consume the evidence and fail closed when it's missing/malformed.
- Tests and changeset — broad coverage across aggregator, registry-client, admin, and core; changeset is accurate and user-facing.
Headline conclusion: the implementation is mostly solid, but there is one real bug in the admin UI. RegistryPluginDetail was updated to pass the full pkg view to policyOk and to the release-picker badge, but the defaultVersion picker still evaluates releasePassesPolicy with a partial { did, slug } object that lacks historicalReleaseCount/releaseHistoryComplete. For a package whose only installable release is a proven first release but whose newest listed release is still inside the holdback, the picker defaults to the too-new release (disabling Install) instead of defaulting to the exempt first release. This undermines the feature's UX for multi-release packages and should be fixed before merge, together with a regression test.
Findings
-
[needs fixing]
packages/admin/src/components/RegistryPluginDetail.tsx:204The
defaultVersionpicker still passes{ did: pkg.did, slug }toreleasePassesPolicy, but the function now needs the fullhistoricalReleaseCount/releaseHistoryCompleteevidence to recognize a proven first-release exemption. The other two call sites in this file (policyOkand the per-release(too new)badge) were already updated to passpkg; this one was missed.For a package with a proven first release and a newer release still inside the holdback window,
find()will not see the first release as passing and will fall back toreleases[0](the newest, too-new release). The user then lands on a disabled Install button and a "too new" banner even though an installable first release exists. This is the same mis-evaluation the PR fixes for the picker badge and the install button.Fix by passing the full
pkgview here too, then add a component test with two releases (a newer too-new release and an older proven first release) asserting that the picker defaults to the first release and the Install button is enabled.const passes = releases.find((r) => releasePassesPolicy(r, pkg, config.policy), );
|
Thanks — I traced this through the component and the history invariant, and I don't think the suggested change is safe. A proven first release requires With two releases, A two-release regression that is red before and green after the suggested one-line change therefore cannot represent a valid registry state. I have left the code unchanged and would appreciate reconsideration of this finding. ~ 🤖 Codex |
There was a problem hiding this comment.
Re-review of the first-release holdback exemption. The aggregator, lexicon, registry-client, and core install-gate changes are still consistent with the fail-closed design described in #1350. However, the one bug reported in the previous review is still present in the PR head: RegistryPluginDetail was updated to pass the full pkg view to policyOk and to the per-release picker badge, but the defaultVersion picker still evaluates releasePassesPolicy with a partial { did: pkg.did, slug } object. Because that object lacks historicalReleaseCount and releaseHistoryComplete, a package whose newest release is inside the holdback window but whose older proven first release is exempt will still default to the too-new release — disabling Install and showing the “too new” banner even though an installable first release exists. The newly added component tests only exercise single-release packages, so they cannot catch this regression.
What I checked:
packages/admin/src/components/RegistryPluginDetail.tsx— all threereleasePassesPolicycall sites; two were fixed, one was not.packages/admin/tests/components/RegistryPluginDetail.test.tsx— added minimum-release-age tests cover only single-release fixtures.packages/admin/src/lib/api/registry.ts—releasePassesPolicycorrectly consumes the history evidence viaisProvenFirstRelease.apps/aggregator/src/routes/xrpc/listing-query.ts— scalar subqueries in an existing SELECT; no extra round-trip and no user input interpolation.apps/aggregator/src/records-consumer.tsand migration — fail-closed history tracking is unchanged and transactionally batched.packages/core/src/api/handlers/registry.ts— install gate independently applies the same first-release exemption.- Changeset and docs — still accurate and user-facing.
Headline conclusion: the implementation is solid except for the unresolved admin picker default. Passing the full pkg view at line 204 and adding a multi-release regression test would finish the PR.
Findings
-
[needs fixing]
packages/admin/src/components/RegistryPluginDetail.tsx:204The
defaultVersionpicker still passes a partial{ did: pkg.did, slug }package view toreleasePassesPolicy. WithouthistoricalReleaseCountandreleaseHistoryComplete,isProvenFirstReleasealways fails closed here, so the picker behaves as if no release is exempt.For a package with a newer release still inside the holdback and an older proven first release,
find()will not see the first release as passing and falls back toreleases[0](the newest, too-new release). The user lands on a disabled Install button and a “too new” banner even though a first release is installable. The other two call sites in this file (policyOkat line 360 and the(too new)badge at line 580) already pass the fullpkg; this one was missed.The newly added component tests use a single release each, so they cannot exercise this regression. Add a test with two releases — a newer too-new release and an older proven first release — asserting that the picker defaults to the first release and the Install button is enabled.
releasePassesPolicy(r, pkg, config.policy),
There was a problem hiding this comment.
This is the right fix for #1350 and the prior review's admin picker bug is resolved in the current head: RegistryPluginDetail's defaultVersion picker now passes the full pkg view to releasePassesPolicy, matching the other two call sites.
What I checked:
packages/admin/src/components/RegistryPluginDetail.tsx— all threereleasePassesPolicycall sites now use the full package view withhistoricalReleaseCount/releaseHistoryComplete.packages/admin/src/lib/api/registry.ts—releasePassesPolicycorrectly appliesisProvenFirstReleaseafter the explicit exemption and before the age check.packages/core/src/api/handlers/registry.ts— install gate independently enforces the same fail-closed exemption viaisProvenFirstRelease(packageView); update flow is unchanged (it didn't apply the holdback before this PR either).apps/aggregator/src/records-consumer.tsand0006_release_history.sql— history tracking is inserted on every profile ingest, marked incomplete on any non-jetstreamrelease or dead-letter, and never upgraded from incomplete.apps/aggregator/src/routes/xrpc/listing-query.ts— history evidence is carried as correlated scalar subqueries in the existinggetPackage/resolvePackageselects, adding no extra round-trip.- Lexicon, registry-client discovery, docs, and changeset changes are consistent with the design.
Headline conclusion: the implementation is sound and the previously reported picker bug is fixed. I have two non-blocking suggestions: update a stale docstring in releasePassesPolicy and add a multi-release fixture to the admin component tests so the picker-default behavior is covered when the package is no longer a single-release first release.
Findings
-
[suggestion]
packages/admin/src/lib/api/registry.ts:236-245The
releasePassesPolicydocstring no longer matches the implementation. It now also exempts a proven first release viaisProvenFirstRelease, not just the minimum-release-age window andminimumReleaseAgeExclude./** * Returns whether a release should be considered installable given the * configured policy. Implements (in order): * * 1. The `minimumReleaseAgeExclude` allowlist. * 2. A fail-closed first-release exemption when the aggregator proves * exactly one complete observed release. * 3. The minimum-release-age holdback described in RFC 0001's * "Pre-label gap and launch tempo" section. * * Returns `false` (release blocked) when the policy is configured but * the release is missing a valid `indexedAt` -- we fail closed rather * than silently letting unbounded-age releases through. */ -
[suggestion]
packages/admin/tests/components/RegistryPluginDetail.test.tsx:324-368The new minimum-release-age component tests only exercise single-release packages. Add a multi-release fixture to cover the picker-default path when the package is no longer a proven first release (e.g., a newer too-new release alongside an older release). This guards the default-version logic against accidentally regressing back to the newest blocked release when history evidence says the package has more than one retained release.
it("defaults to an older age-qualified release when the newest release is too new", async () => { setup(makePackage({ historicalReleaseCount: 2, releaseHistoryComplete: true }), [ makeRelease({ version: "2.0.0", indexedAt: new Date().toISOString() }), makeRelease({ version: "1.0.0", indexedAt: "2025-03-01T00:00:00Z" }), ]); const screen = await render( <Wrapper> <RegistryPluginDetail pluginId="acme.dev/myplugin" config={{ ...CONFIG, policy: { minimumReleaseAgeSeconds: 48 * 60 * 60 }, }} /> </Wrapper>, ); await expect.element(screen.getByRole("button", { name: "Install" })).toBeEnabled(); await expect.element(screen.getByText("1.0.0")).toBeInTheDocument(); expect(screen.getByText("Release is too new to install").query()).toBeNull(); });(Assumes
makeReleaseaccepts aversionoverride; the test can be placed inside the existingdescribe("RegistryPluginDetail minimum release age"block.)
There was a problem hiding this comment.
This is the right fix for #1350 and the re-review resolves both findings from the prior pass.
What changed since the last review:
packages/admin/src/lib/api/registry.ts— thereleasePassesPolicydocstring now correctly lists the three gates in order:minimumReleaseAgeExcludeallowlist, proven-first-release exemption, then the age holdback.packages/admin/tests/components/RegistryPluginDetail.test.tsx— a multi-release fixture now covers the default-version picker path, proving the detail page falls back to an older age-qualified release when history evidence rules out a single-release exemption.
What I checked:
RegistryPluginDetailnow passes the fullpkgview (includinghistoricalReleaseCount/releaseHistoryComplete) to all threereleasePassesPolicycall sites.releasePassesPolicyand the core install gate both applyisProvenFirstReleasefail-closed after explicit exemptions and before the age check;isProvenFirstReleaserequires bothreleaseHistoryComplete === trueandhistoricalReleaseCount === 1.- The aggregator migration creates
package_release_history, seeds existing packages as incomplete/unknown, and is idempotent viaINSERT OR IGNORE. ingestPackageProfilemarks live profile creations as complete and never upgrades incomplete history (ON CONFLICT DO NOTHING);ingestPackageReleaseand dead-letter handling mark history incomplete for any non-Jetstream or dead-lettered release.- The read API carries history evidence as two correlated scalar subqueries in the existing
getPackage/resolvePackageselects, adding no extra round trip. - The lexicon, generated types, registry-client validation, admin tests, core conformance tests, aggregator consumer tests, and docs all line up with the fail-closed design.
- No new user-facing strings, no raw SQL interpolation, no extra logged-out queries, and the changeset is proportional and accurate.
Headline: all previously raised suggestions are addressed and the implementation is sound. LGTM.
What does this PR do?
The optional registry minimum-release-age policy now exempts a package's first release only when the aggregator can prove that it continuously observed the package and retained exactly one release version.
The aggregator records whether a package first arrived through a live Jetstream creation or through an incomplete source such as backfill or an older queue producer. Existing packages start incomplete during migration. A backfilled release or dead-lettered release permanently makes that package's history incomplete. Historical counts include tombstoned releases, so deleting an earlier version cannot turn a later release into a first release.
The package view carries optional history evidence through the lexicon and registry client. The admin uses it to present the policy state, while the install handler enforces the same rule after independently matching the selected release against the publisher's signed PDS records. Missing, malformed, incomplete, or multi-release evidence remains held back. Explicit DID/package exemptions and the opt-in policy default are unchanged.
Part of #1350.
Type of change
Checklist
pnpm typecheckpassespnpm lintpassespnpm testpasses (or targeted tests for my change)pnpm formathas been runmessages.pochanges except in translation PRs — a workflow extracts catalogs on merge tomain.AI-generated code disclosure
Screenshots / test output
Incomplete, backfilled, or unavailable history remains held back:
A proven first release remains installable despite the configured holdback: