diff --git a/.agents/skills/netscript-release/SKILL.md b/.agents/skills/netscript-release/SKILL.md index 671118464..5a304e745 100644 --- a/.agents/skills/netscript-release/SKILL.md +++ b/.agents/skills/netscript-release/SKILL.md @@ -142,6 +142,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. + ### Exact-version resolution and same-semver recovery - Production E2E must resolve the exact just-published semver. Published NetScript invocations use @@ -188,6 +209,97 @@ prerelease and repeat both gates. The release is not complete until the pair is - #811: a SHA-bound canary publish plus exact canary-pinned production E2E is the mandatory evidence boundary before stable publication. +## 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 release-candidate/runtime validation pass, not just + `publish:dry-run` (see the same-semver republish section below for why dry-run passing is not + sufficient). With the canary channel (#812) now in place, live validation runs against the + canary-published surface and cites its pinned production E2E verdict — not the in-tree CLI, which + was the pre-canary bar (#808/#809: in-tree maintainer CLI against a real scaffolded app). + +The `publish-readiness.ts` gate (#811/#812: new-package detection, README/tagline/license/exports +checks, docs-site coverage pointer) now runs inside the mandatory canary-publish channel — prefer +the automated `release:canary` verdict over this manual checklist; keep the manual pass for items +the gate does not yet cover. + +## 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. 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 +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. + +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`. `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 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. + +**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 - 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 671118464..5a304e745 100644 --- a/.claude/skills/netscript-release/SKILL.md +++ b/.claude/skills/netscript-release/SKILL.md @@ -142,6 +142,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. + ### Exact-version resolution and same-semver recovery - Production E2E must resolve the exact just-published semver. Published NetScript invocations use @@ -188,6 +209,97 @@ prerelease and repeat both gates. The release is not complete until the pair is - #811: a SHA-bound canary publish plus exact canary-pinned production E2E is the mandatory evidence boundary before stable publication. +## 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 release-candidate/runtime validation pass, not just + `publish:dry-run` (see the same-semver republish section below for why dry-run passing is not + sufficient). With the canary channel (#812) now in place, live validation runs against the + canary-published surface and cites its pinned production E2E verdict — not the in-tree CLI, which + was the pre-canary bar (#808/#809: in-tree maintainer CLI against a real scaffolded app). + +The `publish-readiness.ts` gate (#811/#812: new-package detection, README/tagline/license/exports +checks, docs-site coverage pointer) now runs inside the mandatory canary-publish channel — prefer +the automated `release:canary` verdict over this manual checklist; keep the manual pass for items +the gate does not yet cover. + +## 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. 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 +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. + +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`. `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 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. + +**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 - If `release:cut` fails before branch creation, fix the reported gate and rerun. Restore any