13 KiB
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.setDigestand 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, andbuildManifestArtifactSchema. -
Produces:
parseReleaseArtifact(value),parseRuntimeConfigArtifact(value),parseBuildManifestArtifact(value). -
Produces:
projectReleaseTokens(release)returning common tokens plus exactly one ofapiContractVersionorcontractSetDigest. -
Consumes:
contractSetSchemafromsrc/contracts/contract-set.ts. -
Step 1: Write failing V2 projection tests
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
export const releaseManifestArtifactSchema = z.discriminatedUnion(
"schemaVersion",
[releaseManifestV1ArtifactSchema, releaseManifestV2ArtifactSchema],
);
export type ReleaseArtifact = z.output<typeof releaseManifestArtifactSchema>;
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
RuntimeConfigValidationwith a reliableschema: "V1" | "V2"discriminator. -
Keeps the existing normalized
RuntimeConfigfacade for downstream callers. -
Step 1: Add failing future-version and protocol tests
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
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_MISMATCHtoReleaseManifestErrorCode. -
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
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
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.
-
validationDurationMsexcludes 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:
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.