From 88c2ebf2ef4f3040bbca7afe48d2c85b814dacac Mon Sep 17 00:00:00 2001 From: Rickylabs Date: Fri, 17 Jul 2026 10:32:04 +0200 Subject: [PATCH 1/2] docs(release): codify same-semver republish recovery + min-dependency-age handling + first-publish checklist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Owner directive after the v0.0.1-beta.10 cut (2026-07-17): two release patterns were proven live and need to be reachable before an agent defaults to an unwanted stable-channel semver bump. - Same-semver republish (partial-publish recovery): JSR version immutability binds only published members; a failed/skipped member can still publish at the same semver via fix-forward + tag fast-forward + publish.yml re-dispatch. Cites the beta.10 precedent (33/70 members published, mcp/cli recovered without a version bump — runs 29558968037 -> 29562537123). - Minimum-dependency-age at release time: Deno 2.9's ~24h default rejects same-day publishes; the harness pins --minimum-dependency-age=0 and keeps fresh-plugin resolution in one Deno process (child deno x re-exec drops the flag) — #813 + #817. User-facing 24h window tracked in #818. - First-publish checklist: manual README/tagline/license/exports/docs-site/live-validation checks until #811/#812's publish-readiness automation lands. Regenerated the .claude mirror via agentic:sync-claude; docs:links, sync-claude:check, and fmt all green on touched files. --- .agents/skills/netscript-release/SKILL.md | 83 +++++++++++++++++++++++ .claude/skills/netscript-release/SKILL.md | 83 +++++++++++++++++++++++ 2 files changed, 166 insertions(+) diff --git a/.agents/skills/netscript-release/SKILL.md b/.agents/skills/netscript-release/SKILL.md index 33449fe6d..39735e440 100644 --- a/.agents/skills/netscript-release/SKILL.md +++ b/.agents/skills/netscript-release/SKILL.md @@ -96,6 +96,27 @@ After the release PR merges: `jsr:@netscript/cli@`, and runs the published CLI smoke and scaffold runtime suite. 5. The manual `workflow_dispatch` path still accepts `published-version` for targeted rechecks. +### Minimum-Dependency-Age At Release Time + +Deno 2.9's default ~24h minimum-dependency-age rejects a same-day publish: a package published +minutes ago is treated as too fresh to depend on. This bit the beta.10 cut twice in the same +release, back to back: + +- #813 pinned `--minimum-dependency-age=0` on every `jsr:@netscript/*` invocation the production E2E + harness runs — but the harness still failed the very next run + ([`29564434302`](https://github.com/rickylabs/netscript/actions/runs/29564434302)). +- #817 found the real cause: `deno x` resolves and installs the JSR executable, then internally + re-invokes `deno run` in a **child process** that does not inherit the parent's + `--minimum-dependency-age` flag. The fix bypasses `deno x` for fresh-plugin resolution and runs + `deno run --minimum-dependency-age=0` directly against the resolved entrypoint, keeping fresh + plugin resolution in one Deno process. + +Both fixes are release-harness-only (`.llm/tools/release/` and the E2E suite). The same exposure +exists in the **shipped CLI's** own `deno x` plugin dispatch — tracked separately as a user-facing +product-policy decision in [#818](https://github.com/rickylabs/netscript/issues/818) (a project on a +fresh release hits the 24h wall on every `jsr:@netscript/*` resolution for its first day), not +silently patched here. + ## Hard Release Completion Gate A release is **done only when both** of these independent workflow verdicts are green: @@ -117,6 +138,68 @@ prerelease and repeat both gates. The release is not complete until the pair is dry-run can miss and real publish can reject. - #146: `deno ci --prod` is already lockfile-frozen in Deno 2.9; an explicit extra flag is rejected. +## First-Publish Checklist + +`publish.yml` auto-provisions new JSR packages (`.llm/tools/release/jsr-provision-packages.ts`), but +provisioning is not readiness. Before the first release that carries a brand-new workspace member, +verify by hand: + +- README meets the production standard (Install / Quick example / Docs sections) — see #807 for the + bar `@netscript/mcp` was held to before its first publish. +- Tagline stays under the JSR byte cap. +- `license`, `exports`, and workspace `deno.json` config are complete and correct for the new + member. +- The docs site has coverage for the new package (or an explicit stub with a tracked follow-up). +- For a server or CLI package, do a live tool validation pass — actually run the published surface, + not just `publish:dry-run` (see the same-semver republish section below for why dry-run passing is + not sufficient). + +This checklist is manual today. #811/#812 track a `publish-readiness.ts` gate (new-package +detection, README/tagline/license/exports checks, docs-site coverage pointer) that runs inside a +mandatory canary-publish channel and will automate it — once #812 merges, prefer the automated +`release:canary` verdict over this manual checklist. + +## Same-Semver Republish (Partial-Publish Recovery) + +Proven during the 0.0.1-beta.10 cut (2026-07-17, +[run `29558968037`](https://github.com/rickylabs/netscript/actions/runs/29558968037)): a +`publish.yml` run can partially succeed. 33 workspace members published, 10 more failed on a +transient `invalidBearerToken` OIDC error, `@netscript/mcp` failed on the registry-rejected text +import that #810 later fixed, and 26 further members were skipped as dependents of a failed publish +— `@netscript/cli` among them. + +**JSR version immutability binds only to published members.** A member that failed or was skipped +can still publish at the exact same semver, and workspace `deno publish` skips already-registered +versions on the members that already succeeded (`Warning: Skipping, already published`) rather than +re-publishing or failing on them. This makes recovery a **fix-forward**, not a version bump, +whenever the fix does not touch content that already published. + +Recovery steps, as executed for beta.10: + +1. Fix forward to `main` with every version file unchanged since the cut. `8a8a9537` (#810) fixed + the `@netscript/mcp` text-import rejection two commits after the `a5adb706` release-cut commit, + still at `0.0.1-beta.10` everywhere. +2. Fast-forward the release tag to the fixed commit. This is legitimate **only** when every + already-published member is byte-identical across the move — verify with + `git diff -- ` before moving the tag; a + member whose published content changed cannot ride a tag move at the same semver. +3. Re-dispatch `publish.yml` via its `workflow_dispatch` `tag` input set to the existing tag (for + example `v0.0.1-beta.10`) — do not create a new GitHub Release; that would demand a new semver. + The workflow checks out the fast-forwarded tag ref, so the 33 already-published members log + `Skipping, already published` and only the previously-failed/skipped members publish for the + first time. +4. The Hard Release Completion Gate above still applies to the **re-dispatched** run, not the + original: beta.10's first `publish.yml` run failed + ([`29558968037`](https://github.com/rickylabs/netscript/actions/runs/29558968037)); the + re-dispatch on the fast-forwarded tag succeeded + ([`29562537123`](https://github.com/rickylabs/netscript/actions/runs/29562537123), `mcp` and + `cli` publishing for the first time); only that green run's artifact is what `e2e-cli-prod` is + allowed to validate against. + +**Fallback:** a semver-bump hotfix is required instead whenever the fix changes content in a member +that is already published — same-semver republish can only ever fill in members that never reached +the registry, never re-publish over one that did. + ## Rollback And Retry - If `release:cut` fails before branch creation, fix the reported gate and rerun. Restore any diff --git a/.claude/skills/netscript-release/SKILL.md b/.claude/skills/netscript-release/SKILL.md index 33449fe6d..39735e440 100644 --- a/.claude/skills/netscript-release/SKILL.md +++ b/.claude/skills/netscript-release/SKILL.md @@ -96,6 +96,27 @@ After the release PR merges: `jsr:@netscript/cli@`, and runs the published CLI smoke and scaffold runtime suite. 5. The manual `workflow_dispatch` path still accepts `published-version` for targeted rechecks. +### Minimum-Dependency-Age At Release Time + +Deno 2.9's default ~24h minimum-dependency-age rejects a same-day publish: a package published +minutes ago is treated as too fresh to depend on. This bit the beta.10 cut twice in the same +release, back to back: + +- #813 pinned `--minimum-dependency-age=0` on every `jsr:@netscript/*` invocation the production E2E + harness runs — but the harness still failed the very next run + ([`29564434302`](https://github.com/rickylabs/netscript/actions/runs/29564434302)). +- #817 found the real cause: `deno x` resolves and installs the JSR executable, then internally + re-invokes `deno run` in a **child process** that does not inherit the parent's + `--minimum-dependency-age` flag. The fix bypasses `deno x` for fresh-plugin resolution and runs + `deno run --minimum-dependency-age=0` directly against the resolved entrypoint, keeping fresh + plugin resolution in one Deno process. + +Both fixes are release-harness-only (`.llm/tools/release/` and the E2E suite). The same exposure +exists in the **shipped CLI's** own `deno x` plugin dispatch — tracked separately as a user-facing +product-policy decision in [#818](https://github.com/rickylabs/netscript/issues/818) (a project on a +fresh release hits the 24h wall on every `jsr:@netscript/*` resolution for its first day), not +silently patched here. + ## Hard Release Completion Gate A release is **done only when both** of these independent workflow verdicts are green: @@ -117,6 +138,68 @@ prerelease and repeat both gates. The release is not complete until the pair is dry-run can miss and real publish can reject. - #146: `deno ci --prod` is already lockfile-frozen in Deno 2.9; an explicit extra flag is rejected. +## First-Publish Checklist + +`publish.yml` auto-provisions new JSR packages (`.llm/tools/release/jsr-provision-packages.ts`), but +provisioning is not readiness. Before the first release that carries a brand-new workspace member, +verify by hand: + +- README meets the production standard (Install / Quick example / Docs sections) — see #807 for the + bar `@netscript/mcp` was held to before its first publish. +- Tagline stays under the JSR byte cap. +- `license`, `exports`, and workspace `deno.json` config are complete and correct for the new + member. +- The docs site has coverage for the new package (or an explicit stub with a tracked follow-up). +- For a server or CLI package, do a live tool validation pass — actually run the published surface, + not just `publish:dry-run` (see the same-semver republish section below for why dry-run passing is + not sufficient). + +This checklist is manual today. #811/#812 track a `publish-readiness.ts` gate (new-package +detection, README/tagline/license/exports checks, docs-site coverage pointer) that runs inside a +mandatory canary-publish channel and will automate it — once #812 merges, prefer the automated +`release:canary` verdict over this manual checklist. + +## Same-Semver Republish (Partial-Publish Recovery) + +Proven during the 0.0.1-beta.10 cut (2026-07-17, +[run `29558968037`](https://github.com/rickylabs/netscript/actions/runs/29558968037)): a +`publish.yml` run can partially succeed. 33 workspace members published, 10 more failed on a +transient `invalidBearerToken` OIDC error, `@netscript/mcp` failed on the registry-rejected text +import that #810 later fixed, and 26 further members were skipped as dependents of a failed publish +— `@netscript/cli` among them. + +**JSR version immutability binds only to published members.** A member that failed or was skipped +can still publish at the exact same semver, and workspace `deno publish` skips already-registered +versions on the members that already succeeded (`Warning: Skipping, already published`) rather than +re-publishing or failing on them. This makes recovery a **fix-forward**, not a version bump, +whenever the fix does not touch content that already published. + +Recovery steps, as executed for beta.10: + +1. Fix forward to `main` with every version file unchanged since the cut. `8a8a9537` (#810) fixed + the `@netscript/mcp` text-import rejection two commits after the `a5adb706` release-cut commit, + still at `0.0.1-beta.10` everywhere. +2. Fast-forward the release tag to the fixed commit. This is legitimate **only** when every + already-published member is byte-identical across the move — verify with + `git diff -- ` before moving the tag; a + member whose published content changed cannot ride a tag move at the same semver. +3. Re-dispatch `publish.yml` via its `workflow_dispatch` `tag` input set to the existing tag (for + example `v0.0.1-beta.10`) — do not create a new GitHub Release; that would demand a new semver. + The workflow checks out the fast-forwarded tag ref, so the 33 already-published members log + `Skipping, already published` and only the previously-failed/skipped members publish for the + first time. +4. The Hard Release Completion Gate above still applies to the **re-dispatched** run, not the + original: beta.10's first `publish.yml` run failed + ([`29558968037`](https://github.com/rickylabs/netscript/actions/runs/29558968037)); the + re-dispatch on the fast-forwarded tag succeeded + ([`29562537123`](https://github.com/rickylabs/netscript/actions/runs/29562537123), `mcp` and + `cli` publishing for the first time); only that green run's artifact is what `e2e-cli-prod` is + allowed to validate against. + +**Fallback:** a semver-bump hotfix is required instead whenever the fix changes content in a member +that is already published — same-semver republish can only ever fill in members that never reached +the registry, never re-publish over one that did. + ## Rollback And Retry - If `release:cut` fails before branch creation, fix the reported gate and rerun. Restore any From 9d148d88cb7ccc3ccaf70b8f6bd7fa368b1436eb Mon Sep 17 00:00:00 2001 From: Rickylabs Date: Fri, 17 Jul 2026 10:47:49 +0200 Subject: [PATCH 2/2] fix(docs): correct beta.10 same-semver recovery accounting per IMPL-EVAL FAIL_FIX Proves: preflight-vs-publish outcomes are no longer conflated (33 published / mcp failed / cli skipped, separate from the unauthenticated 10-failed/25-skipped graph preflight); byte-identity of already-published members is now a MANDATORY precondition for any tag move, with beta.10 recast honestly as a non-byte-identical move (6 plugins changed) that JSR's skip-existing/fill-unpublished behavior papered over, not a safe precedent; first-publish live-validation wording now matches #808/#809 (in-tree maintainer CLI against a real scaffolded app) with an explicit note that #812's canary channel changes this to canary-published surface + pinned prod E2E verdict. Cures findings 1-3 of .llm/runs/beta10-cli--orchestrator/slices/819-relskill-eval/evaluate.md. Finding 4 (non-blocking, #812 merge-conflict guidance) is untouched. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_017LHrkXyMzsQwb9bqr82EFK --- .agents/skills/netscript-release/SKILL.md | 65 ++++++++++++++++------- .claude/skills/netscript-release/SKILL.md | 65 ++++++++++++++++------- 2 files changed, 94 insertions(+), 36 deletions(-) diff --git a/.agents/skills/netscript-release/SKILL.md b/.agents/skills/netscript-release/SKILL.md index 39735e440..f633d7e30 100644 --- a/.agents/skills/netscript-release/SKILL.md +++ b/.agents/skills/netscript-release/SKILL.md @@ -150,9 +150,11 @@ verify by hand: - `license`, `exports`, and workspace `deno.json` config are complete and correct for the new member. - The docs site has coverage for the new package (or an explicit stub with a tracked follow-up). -- For a server or CLI package, do a live tool validation pass — actually run the published surface, - not just `publish:dry-run` (see the same-semver republish section below for why dry-run passing is - not sufficient). +- For a server or CLI package, do a live release-candidate/runtime validation pass, not just + `publish:dry-run` — matching #809: run the in-tree maintainer CLI against a real scaffolded app + (see the same-semver republish section below for why dry-run passing is not sufficient). Once + #812's canary channel exists, this step changes: live validation must run against the + canary-published surface and cite its pinned production E2E verdict, not the in-tree CLI. This checklist is manual today. #811/#812 track a `publish-readiness.ts` gate (new-package detection, README/tagline/license/exports checks, docs-site coverage pointer) that runs inside a @@ -163,10 +165,17 @@ mandatory canary-publish channel and will automate it — once #812 merges, pref Proven during the 0.0.1-beta.10 cut (2026-07-17, [run `29558968037`](https://github.com/rickylabs/netscript/actions/runs/29558968037)): a -`publish.yml` run can partially succeed. 33 workspace members published, 10 more failed on a -transient `invalidBearerToken` OIDC error, `@netscript/mcp` failed on the registry-rejected text -import that #810 later fixed, and 26 further members were skipped as dependents of a failed publish -— `@netscript/cli` among them. +`publish.yml` run can partially succeed. Keep the unauthenticated graph-preflight outcomes separate +from the real Publish step's registry outcomes — they are two different steps in the same run and +must not be summed: + +- **Publish preflight (real graph build, no upload)** deliberately runs with an invalid token to + build the full dependency graph without uploading. It reported 10 token failures and 25 skipped + dependents, then stopped at the auth boundary. None of these are registry outcomes — nothing was + uploaded. +- **Publish** (the real upload) then ran: 33 of the 35 workspace members published, `@netscript/mcp` + failed on the registry-rejected text import that #810 later fixed, and `@netscript/cli` was + skipped as a dependent of the failed `mcp` publish. **JSR version immutability binds only to published members.** A member that failed or was skipped can still publish at the exact same semver, and workspace `deno publish` skips already-registered @@ -174,18 +183,29 @@ versions on the members that already succeeded (`Warning: Skipping, already publ re-publishing or failing on them. This makes recovery a **fix-forward**, not a version bump, whenever the fix does not touch content that already published. +Byte identity of every already-published member across the tag move is a **MANDATORY precondition** +for step 2 below, not an aspiration. Verify it with +`git diff -- ` for every member that already +published; if the diff is non-empty for even one of them, that member's published content changed +and the tag move is not safe — use the semver-bump fallback instead. + Recovery steps, as executed for beta.10: -1. Fix forward to `main` with every version file unchanged since the cut. `8a8a9537` (#810) fixed - the `@netscript/mcp` text-import rejection two commits after the `a5adb706` release-cut commit, - still at `0.0.1-beta.10` everywhere. -2. Fast-forward the release tag to the fixed commit. This is legitimate **only** when every - already-published member is byte-identical across the move — verify with - `git diff -- ` before moving the tag; a - member whose published content changed cannot ride a tag move at the same semver. +1. Fix forward to `main`. `8a8a9537` (#810) fixed the `@netscript/mcp` text-import rejection and is + the **direct child commit** of the `a5adb706` release-cut commit. Version **values** stayed at + `0.0.1-beta.10` in both commits, but this is not the same claim as "every version file + unchanged": `deno.json` itself changed between the two commits (its content differs even though + the version field does not). +2. Fast-forward the release tag to the fixed commit — **only** after the byte-identity precondition + above passes. Applied literally to beta.10, it does not: `git diff a5adb706 8a8a9537` is + non-empty for six members that run `29558968037` had already published — `@netscript/plugin-ai`, + `plugin-auth`, `plugin-sagas`, `plugin-streams`, `plugin-triggers`, and `plugin-workers` — + because their publish-included `src/**`/`services/**` files changed alongside the `mcp` fix. + beta.10 is therefore not a compliant byte-identical precedent for this step; it is evidence for a + narrower claim (see below). 3. Re-dispatch `publish.yml` via its `workflow_dispatch` `tag` input set to the existing tag (for example `v0.0.1-beta.10`) — do not create a new GitHub Release; that would demand a new semver. - The workflow checks out the fast-forwarded tag ref, so the 33 already-published members log + The workflow checks out the fast-forwarded tag ref, so already-published members log `Skipping, already published` and only the previously-failed/skipped members publish for the first time. 4. The Hard Release Completion Gate above still applies to the **re-dispatched** run, not the @@ -196,9 +216,18 @@ Recovery steps, as executed for beta.10: `cli` publishing for the first time); only that green run's artifact is what `e2e-cli-prod` is allowed to validate against. -**Fallback:** a semver-bump hotfix is required instead whenever the fix changes content in a member -that is already published — same-semver republish can only ever fill in members that never reached -the registry, never re-publish over one that did. +**What beta.10 actually demonstrates:** because JSR's `deno publish` skips already-registered +versions on members that already succeeded and fills in members that never reached the registry, the +re-dispatch on the fast-forwarded tag still completed the partial publish even though six +already-published members were not byte-identical across the move — those six simply logged +`Skipping, already published` with their pre-fix content permanently on the registry at +`0.0.1-beta.10`. This is evidence that JSR skips existing versions and fills unpublished members; it +is **not** evidence that a non-byte-identical tag move is safe. Do not repeat the beta.10 tag move +as a template — treat it as the incident that motivated the mandatory precondition above. + +**Fallback:** a semver-bump hotfix is required instead whenever the byte-identity precondition fails +for any already-published member — same-semver republish can only ever fill in members that never +reached the registry, never re-publish over one that did. ## Rollback And Retry diff --git a/.claude/skills/netscript-release/SKILL.md b/.claude/skills/netscript-release/SKILL.md index 39735e440..f633d7e30 100644 --- a/.claude/skills/netscript-release/SKILL.md +++ b/.claude/skills/netscript-release/SKILL.md @@ -150,9 +150,11 @@ verify by hand: - `license`, `exports`, and workspace `deno.json` config are complete and correct for the new member. - The docs site has coverage for the new package (or an explicit stub with a tracked follow-up). -- For a server or CLI package, do a live tool validation pass — actually run the published surface, - not just `publish:dry-run` (see the same-semver republish section below for why dry-run passing is - not sufficient). +- For a server or CLI package, do a live release-candidate/runtime validation pass, not just + `publish:dry-run` — matching #809: run the in-tree maintainer CLI against a real scaffolded app + (see the same-semver republish section below for why dry-run passing is not sufficient). Once + #812's canary channel exists, this step changes: live validation must run against the + canary-published surface and cite its pinned production E2E verdict, not the in-tree CLI. This checklist is manual today. #811/#812 track a `publish-readiness.ts` gate (new-package detection, README/tagline/license/exports checks, docs-site coverage pointer) that runs inside a @@ -163,10 +165,17 @@ mandatory canary-publish channel and will automate it — once #812 merges, pref Proven during the 0.0.1-beta.10 cut (2026-07-17, [run `29558968037`](https://github.com/rickylabs/netscript/actions/runs/29558968037)): a -`publish.yml` run can partially succeed. 33 workspace members published, 10 more failed on a -transient `invalidBearerToken` OIDC error, `@netscript/mcp` failed on the registry-rejected text -import that #810 later fixed, and 26 further members were skipped as dependents of a failed publish -— `@netscript/cli` among them. +`publish.yml` run can partially succeed. Keep the unauthenticated graph-preflight outcomes separate +from the real Publish step's registry outcomes — they are two different steps in the same run and +must not be summed: + +- **Publish preflight (real graph build, no upload)** deliberately runs with an invalid token to + build the full dependency graph without uploading. It reported 10 token failures and 25 skipped + dependents, then stopped at the auth boundary. None of these are registry outcomes — nothing was + uploaded. +- **Publish** (the real upload) then ran: 33 of the 35 workspace members published, `@netscript/mcp` + failed on the registry-rejected text import that #810 later fixed, and `@netscript/cli` was + skipped as a dependent of the failed `mcp` publish. **JSR version immutability binds only to published members.** A member that failed or was skipped can still publish at the exact same semver, and workspace `deno publish` skips already-registered @@ -174,18 +183,29 @@ versions on the members that already succeeded (`Warning: Skipping, already publ re-publishing or failing on them. This makes recovery a **fix-forward**, not a version bump, whenever the fix does not touch content that already published. +Byte identity of every already-published member across the tag move is a **MANDATORY precondition** +for step 2 below, not an aspiration. Verify it with +`git diff -- ` for every member that already +published; if the diff is non-empty for even one of them, that member's published content changed +and the tag move is not safe — use the semver-bump fallback instead. + Recovery steps, as executed for beta.10: -1. Fix forward to `main` with every version file unchanged since the cut. `8a8a9537` (#810) fixed - the `@netscript/mcp` text-import rejection two commits after the `a5adb706` release-cut commit, - still at `0.0.1-beta.10` everywhere. -2. Fast-forward the release tag to the fixed commit. This is legitimate **only** when every - already-published member is byte-identical across the move — verify with - `git diff -- ` before moving the tag; a - member whose published content changed cannot ride a tag move at the same semver. +1. Fix forward to `main`. `8a8a9537` (#810) fixed the `@netscript/mcp` text-import rejection and is + the **direct child commit** of the `a5adb706` release-cut commit. Version **values** stayed at + `0.0.1-beta.10` in both commits, but this is not the same claim as "every version file + unchanged": `deno.json` itself changed between the two commits (its content differs even though + the version field does not). +2. Fast-forward the release tag to the fixed commit — **only** after the byte-identity precondition + above passes. Applied literally to beta.10, it does not: `git diff a5adb706 8a8a9537` is + non-empty for six members that run `29558968037` had already published — `@netscript/plugin-ai`, + `plugin-auth`, `plugin-sagas`, `plugin-streams`, `plugin-triggers`, and `plugin-workers` — + because their publish-included `src/**`/`services/**` files changed alongside the `mcp` fix. + beta.10 is therefore not a compliant byte-identical precedent for this step; it is evidence for a + narrower claim (see below). 3. Re-dispatch `publish.yml` via its `workflow_dispatch` `tag` input set to the existing tag (for example `v0.0.1-beta.10`) — do not create a new GitHub Release; that would demand a new semver. - The workflow checks out the fast-forwarded tag ref, so the 33 already-published members log + The workflow checks out the fast-forwarded tag ref, so already-published members log `Skipping, already published` and only the previously-failed/skipped members publish for the first time. 4. The Hard Release Completion Gate above still applies to the **re-dispatched** run, not the @@ -196,9 +216,18 @@ Recovery steps, as executed for beta.10: `cli` publishing for the first time); only that green run's artifact is what `e2e-cli-prod` is allowed to validate against. -**Fallback:** a semver-bump hotfix is required instead whenever the fix changes content in a member -that is already published — same-semver republish can only ever fill in members that never reached -the registry, never re-publish over one that did. +**What beta.10 actually demonstrates:** because JSR's `deno publish` skips already-registered +versions on members that already succeeded and fills in members that never reached the registry, the +re-dispatch on the fast-forwarded tag still completed the partial publish even though six +already-published members were not byte-identical across the move — those six simply logged +`Skipping, already published` with their pre-fix content permanently on the registry at +`0.0.1-beta.10`. This is evidence that JSR skips existing versions and fills unpublished members; it +is **not** evidence that a non-byte-identical tag move is safe. Do not repeat the beta.10 tag move +as a template — treat it as the incident that motivated the mandatory precondition above. + +**Fallback:** a semver-bump hotfix is required instead whenever the byte-identity precondition fails +for any already-published member — same-semver republish can only ever fill in members that never +reached the registry, never re-publish over one that did. ## Rollback And Retry