# Release and Boot Integrity Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Make generated Release Manifest V2 artifacts verifiable and make the browser accept only coherent V1/V1 or V2/V2 boot protocol pairs. **Architecture:** Zod schemas define artifact shapes and version-specific token projection. Runtime config preserves an exact V1/V2 discriminator through release-manifest loading, where mixed pairs fail before contract or application composition. **Tech Stack:** TypeScript 7, Node.js 24, Zod 4, Vitest 4, Vite 8, pnpm 11. ## Global Constraints - Preserve all pre-existing dirty-worktree changes; never reset or restore them. - Do not stage or commit mixed existing source/test files without explicit user authorization. - Runtime Config versions accepted by browser boot are exactly `"1"` and `"2.0"`. - Release Manifest V1 is read-only compatibility; all writers emit V2. - V2 never requires or emits `API_CONTRACT_VERSION`/`apiContractVersion`. - V2 contract identity is `contractSet.setDigest` and the full package set. - Local/development endpoints allow only HTTP or HTTPS; staging/production allow only HTTPS. - Every production behavior change must be preceded by a failing test. --- ### Task 1: Executable release artifact schemas and token projection **Files:** - Create: `scripts/contracts/release-artifacts.ts` - Create: `tests/unit/release-artifacts.test.ts` - Modify: `src/contracts/release-tokens.ts` **Interfaces:** - Produces: `releaseManifestV1ArtifactSchema`, `releaseManifestV2ArtifactSchema`, `releaseManifestArtifactSchema`, `runtimeConfigV1ArtifactSchema`, `runtimeConfigV2ArtifactSchema`, `runtimeConfigArtifactSchema`, and `buildManifestArtifactSchema`. - Produces: `parseReleaseArtifact(value)`, `parseRuntimeConfigArtifact(value)`, `parseBuildManifestArtifact(value)`. - Produces: `projectReleaseTokens(release)` returning common tokens plus exactly one of `apiContractVersion` or `contractSetDigest`. - Consumes: `contractSetSchema` from `src/contracts/contract-set.ts`. - [ ] **Step 1: Write failing V2 projection tests** ```ts it("projects the nested V2 contract-set digest without a legacy scalar", () => { const release = parseReleaseArtifact(v2ReleaseFixture); expect(projectReleaseTokens(release)).toMatchObject({ schemaVersion: 2, contractSetDigest: v2ReleaseFixture.contractSet.setDigest, }); expect(projectReleaseTokens(release)).not.toHaveProperty("apiContractVersion"); }); it("rejects a V2 release carrying the removed scalar", () => { expect(() => parseReleaseArtifact({ ...v2ReleaseFixture, apiContractVersion: "1", })).toThrow(); }); ``` - [ ] **Step 2: Run tests and confirm RED** Run: `corepack pnpm exec vitest run tests/unit/release-artifacts.test.ts` Expected: module/export resolution failure because the artifact contract module does not exist. - [ ] **Step 3: Implement discriminated artifact schemas** ```ts export const releaseManifestArtifactSchema = z.discriminatedUnion( "schemaVersion", [releaseManifestV1ArtifactSchema, releaseManifestV2ArtifactSchema], ); export type ReleaseArtifact = z.output; export function projectReleaseTokens(release: ReleaseArtifact) { const common = { schemaVersion: release.schemaVersion, appVersion: release.appVersion, buildId: release.buildId, commitSha: release.commitSha, configSchemaVersion: release.configSchemaVersion, assetManifestHash: release.assetManifestHash, releaseId: release.releaseId, builtAt: release.builtAt, } as const; return release.schemaVersion === 1 ? { ...common, apiContractVersion: release.apiContractVersion } : { ...common, contractSetDigest: release.contractSet.setDigest }; } ``` Build Manifest V1 must include the fields currently emitted by the generator: `releaseId`, `moduleInventoryHash`, `buildContext.sourceDateEpoch`, and output paths for module inventory, route chunks, and runtime-config schema. - [ ] **Step 4: Run focused tests and confirm GREEN** Run: `corepack pnpm exec vitest run tests/unit/release-artifacts.test.ts tests/unit/release-coherence.test.ts` Expected: all tests pass; V1 projection retains `apiContractVersion`; V2 projection contains only `contractSetDigest`. ### Task 2: Generate and verify artifacts through the same contracts **Files:** - Modify: `scripts/generate-build-manifest.ts` - Modify: `scripts/verify-release.ts` - Modify: `schemas/artifacts/build-manifest.schema.json` - Test: `tests/unit/release-artifacts.test.ts` **Interfaces:** - Consumes Task 1 parsers and token projection. - Produces V2 release and V1 build manifest that have been parsed before write. - [ ] **Step 1: Add failing parser/writer round-trip tests** Assert that the exact generator shapes parse, that unknown root/output fields fail, and that a runtime-config V2 artifact parses without `API_CONTRACT_VERSION`. - [ ] **Step 2: Run the tests and confirm RED** Run: `corepack pnpm exec vitest run tests/unit/release-artifacts.test.ts` Expected: current build-manifest schema/parser rejects emitted fields or the V2 runtime parser requires the removed scalar. - [ ] **Step 3: Parse before every write and parse before verification** In `generate-build-manifest.ts`, wrap the existing build-manifest object with `buildManifestArtifactSchema.parse(...)`, wrap the existing release-manifest object with `releaseManifestV2ArtifactSchema.parse(...)`, and replace the runtime-config parser with `runtimeConfigV2ArtifactSchema.parse(...)`. Preserve the exact existing values and output paths; the schema call is the only writer boundary added in this step. In `verify-release.ts`, replace `CompatibilityTuple` parsing and the loop over all registry keys with version-specific projection. Keep legacy coherence fixtures on the existing numeric compatibility policy, but do not apply that legacy tuple parser to V2 artifacts. - [ ] **Step 4: Generate JSON Schema from the executable build schema** Replace the checked-in `schemas/artifacts/build-manifest.schema.json` with the deterministic `z.toJSONSchema(buildManifestArtifactSchema)` representation. The generated schema must use draft 2020-12 and `additionalProperties: false`. - [ ] **Step 5: Run focused tests and confirm GREEN** Run: `corepack pnpm exec vitest run tests/unit/release-artifacts.test.ts tests/unit/release-coherence.test.ts` Expected: all release artifact and legacy compatibility tests pass. - [ ] **Step 6: Run the actual release pipeline in an isolated temporary copy** Run the existing contract generation, app build, manifest generation, and `node scripts/verify-release.ts` with local build environment values. Expected: release verification exits 0 and reports no `releaseToken:apiContractVersion` or `releaseToken:contractSetDigest` mismatch. ### Task 3: Exact runtime-config version and endpoint selection **Files:** - Modify: `src/bootstrap/runtime-config-schema.ts` - Modify: `tests/runtime-schema/runtime-config.test.ts` **Interfaces:** - Produces `RuntimeConfigValidation` with a reliable `schema: "V1" | "V2"` discriminator. - Keeps the existing normalized `RuntimeConfig` facade for downstream callers. - [ ] **Step 1: Add failing future-version and protocol tests** ```ts it.each(["0", "1.0", "2.0.1", "3.0"])( "rejects unsupported boot config version %s", (version) => { expect(validateRuntimeConfig({ ...validV1Config, CONFIG_SCHEMA_VERSION: version, }).success).toBe(false); }, ); it.each(["file:///tmp/api/", "data:text/plain,x", "blob:https://test/id"])( "rejects non-http endpoint %s", (API_BASE_URL) => { expect(validateRuntimeConfig({ ...validConfig, API_BASE_URL }).success).toBe(false); }, ); ``` - [ ] **Step 2: Run tests and confirm RED** Run: `corepack pnpm exec vitest run tests/runtime-schema/runtime-config.test.ts` Expected: `3.0` and at least `file:`/`data:` cases are currently accepted. - [ ] **Step 3: Implement literal version dispatch and scheme allow-list** ```ts export const runtimeConfigV1Schema = base.extend({ CONFIG_SCHEMA_VERSION: z.literal("1"), API_CONTRACT_VERSION: z.string().regex(VERSION_PATTERN), }).strict().superRefine(runtimeConfigInvariants); const selectedSchema = declared === "1" ? runtimeConfigV1Schema : declared === "2.0" ? runtimeConfigV2Schema : null; ``` `assertEndpointUrl` must reject every protocol outside `http:` and `https:` before applying the non-local HTTPS rule. - [ ] **Step 4: Run tests and confirm GREEN** Run: `corepack pnpm exec vitest run tests/runtime-schema/runtime-config.test.ts` Expected: exact V1/V2 cases pass and future/non-HTTP cases fail. ### Task 4: Enforce config/manifest protocol pairing **Files:** - Modify: `src/bootstrap/load-release-manifest.ts` - Modify: `tests/runtime-schema/release-manifest.test.ts` **Interfaces:** - Adds `MANIFEST_PROTOCOL_PAIR_MISMATCH` to `ReleaseManifestErrorCode`. - Requires V1 runtime config with V1 manifest and V2 runtime config with V2 manifest. - [ ] **Step 1: Replace the permissive compatibility test with a pairing matrix** ```ts it.each([ ["V1", 2], ["V2", 1], ] as const)("rejects %s runtime with manifest V%s", async (configSchema, schemaVersion) => { await expect(loadReleaseManifest( runtimeFor(configSchema), { fetcher: async () => jsonResponse(manifestFor(schemaVersion)) }, )).rejects.toMatchObject({ code: "MANIFEST_PROTOCOL_PAIR_MISMATCH" }); }); ``` Also test that a V1 scalar mismatch fails, and that V2 contract-set verification is mandatory rather than conditional. - [ ] **Step 2: Run tests and confirm RED** Run: `corepack pnpm exec vitest run tests/runtime-schema/release-manifest.test.ts` Expected: V2 runtime plus V1 manifest currently resolves successfully. - [ ] **Step 3: Implement pair validation before tuple checks** ```ts const expectedManifestVersion = runtime.configSchema === "V1" ? 1 : 2; if (manifest.schemaVersion !== expectedManifestVersion) { throw new ReleaseManifestError("MANIFEST_PROTOCOL_PAIR_MISMATCH", identity); } ``` For V1, require both legacy scalar values and compare them. For V2, require the contract set and always call `verifyContractSet`. Do not use presence checks to choose security validation. - [ ] **Step 4: Run tests and confirm GREEN** Run: `corepack pnpm exec vitest run tests/runtime-schema/release-manifest.test.ts tests/runtime-schema/runtime-config.test.ts` Expected: complete pairing matrix passes and all tampered V2 sets fail. ### Task 5: Close boot cancellation and timing semantics **Files:** - Modify: `src/bootstrap/read-bounded-boot-json.ts` - Modify: `src/bootstrap/load-runtime-config.ts` - Modify: `tests/runtime-schema/runtime-config.test.ts` - Create: `tests/unit/read-bounded-boot-json.test.ts` **Interfaces:** - Pre-aborted external signals prevent fetch admission. - `validationDurationMs` excludes network acquisition. - [ ] **Step 1: Add a failing pre-abort test** Create an already-aborted controller, call `readBoundedBootJson`, and assert the fetcher is never called and the outcome is a stable fetch/abort failure. - [ ] **Step 2: Add a failing network-exclusion timing test** Use a deferred fetcher and a deterministic `now()` sequence. Assert that elapsed network time does not contribute to `validationDurationMs`. - [ ] **Step 3: Run both tests and confirm RED** Run: `corepack pnpm exec vitest run tests/unit/read-bounded-boot-json.test.ts tests/runtime-schema/runtime-config.test.ts` - [ ] **Step 4: Implement admission precheck and move the timer start** Check `options.signal?.aborted` before installing listeners or invoking fetch. In `loadRuntimeConfig`, set `startedAt` immediately after a successful bounded read and before safe-name/schema validation. - [ ] **Step 5: Run both tests and confirm GREEN** Run the same focused command and expect all tests to pass with no leaked abort listeners or timers. ### Task 6: Full verification and handoff **Files:** - Verify all files touched by Tasks 1-5. - [ ] **Step 1: Run focused suites** Run: ```sh corepack pnpm exec vitest run \ tests/unit/release-artifacts.test.ts \ tests/unit/release-coherence.test.ts \ tests/unit/read-bounded-boot-json.test.ts \ tests/runtime-schema/runtime-config.test.ts \ tests/runtime-schema/release-manifest.test.ts ``` - [ ] **Step 2: Run repository static and non-browser suites** Run `corepack pnpm check:types`, `corepack pnpm lint`, and `corepack pnpm test:all`. - [ ] **Step 3: Run release verification from a clean generated output** Run the complete local build and `corepack pnpm verify:release`. Record the actual exit status and mismatch list. - [ ] **Step 4: Run diff hygiene** Run `git diff --check` and a NUL-byte scan. Do not attribute pre-existing unrelated failures to this sub-project. - [ ] **Step 5: Report exact remaining gates** List passing commands, failing commands, files changed, and any browser-only coverage that still requires a Playwright-capable environment.