Three defects found while running the release checklist against a live
backend, all on main.
1. Every TechLog route registered `access: "public"`, including the whole
Studio surface. `decideRouteAccessForDefinition` was therefore a no-op
for Studio: a signed-out visitor who typed /studio, /studio/documents,
or /studio/assets got the Studio shell rendered, and the page went on
to issue Studio API calls. Access is now derived from the spec's own
`layoutGroup`, so a newly added Studio route is gated by construction
rather than by remembering to restate it.
Verified against a production-profile build: /studio* now renders the
sign-in surface, / and /explore are unchanged, and after signing in
the router returns to the originally requested Studio screen.
2. `public/release-manifest.json` still declared the contract set at
2.0.0 while the vendored contract had moved to 3.0.0 (eb86708). Boot
verification fails closed on that mismatch, so `pnpm dev` served a
blank screen. Regenerated from the same producer `dist/` uses.
3. `release-manifest.test.ts` asserted the same stale 2.0.0. The literal
is deliberately independent of `EXPECTED_CONTRACT_SET_PACKAGES` (see
the comment above it), so it is updated in place, not derived.
Also drops a dead `= null` initializer that failed `no-useless-assignment`.
check:types, lint, check:architecture, check:tech-log-contract and
check:dev-release-manifest all pass. test:all is 1818 passed with one
pre-existing load-dependent flake (provider-guardian-transaction, passes
in isolation, untouched by this change).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
305 lines
9.0 KiB
TypeScript
305 lines
9.0 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
loadReleaseManifest,
|
|
ReleaseManifestError,
|
|
} from "../../src/bootstrap/load-release-manifest.ts";
|
|
import {
|
|
computeContractSetDigest,
|
|
type ContractSetPackage,
|
|
} from "../../src/contracts/contract-set-canonical.ts";
|
|
import { EXPECTED_CONTRACT_SET_PACKAGES } from "../../src/features/installed-contract-contributions.ts";
|
|
|
|
const EMPTY_SET_DIGEST = await computeContractSetDigest([]);
|
|
/**
|
|
* TechLog's Studio contribution is always installed (Task 3), so the build's
|
|
* real expected contract set is no longer empty. A coherent manifest fixture
|
|
* must declare exactly what this build actually compiled in, or the boot-time
|
|
* `verifyContractSet` check rejects it as `CONTRACT_SET_PACKAGE_MISSING`.
|
|
*/
|
|
const EXPECTED_PACKAGES =
|
|
EXPECTED_CONTRACT_SET_PACKAGES as readonly ContractSetPackage[];
|
|
const EXPECTED_SET_DIGEST = await computeContractSetDigest(EXPECTED_PACKAGES);
|
|
|
|
const runtime: Parameters<typeof loadReleaseManifest>[0] = {
|
|
build: {
|
|
buildId: "build-a",
|
|
commitSha: "abc123",
|
|
routerBasePath: "/",
|
|
runtimeConfigUrl: "/config.json",
|
|
},
|
|
config: {
|
|
APP_ENV: "local",
|
|
API_BASE_URL: "https://api.test",
|
|
REQUEST_TIMEOUT_MS: 10_000,
|
|
MAX_RETRY_ATTEMPTS: 2,
|
|
TELEMETRY_ENABLED: false,
|
|
AUTH_MODE: "external",
|
|
RELEASE_MANIFEST_URL: "/release-manifest.json",
|
|
BUILD_ID: "build-a",
|
|
RELEASE_ID: "release-a",
|
|
CONFIG_SCHEMA_VERSION: "2.0",
|
|
CAPABILITY_OVERRIDES: {
|
|
REALTIME: "DEFAULT",
|
|
WEB_WORKER: "DEFAULT",
|
|
SERVICE_WORKER: "DEFAULT",
|
|
OFFLINE_COMMANDS: "DEFAULT",
|
|
},
|
|
FEATURE_OVERRIDES: {},
|
|
TECH_LOG_STUDIO_SOURCE: "MOCK",
|
|
},
|
|
configSchema: "V2",
|
|
validationDurationMs: 0,
|
|
};
|
|
const manifest = {
|
|
schemaVersion: 2,
|
|
appVersion: "0.1.0",
|
|
buildId: "build-a",
|
|
commitSha: "abc123",
|
|
configSchemaVersion: "2.0",
|
|
assetManifestHash: "hash-a",
|
|
releaseId: "release-a",
|
|
builtAt: "2026-07-25T00:00:00Z",
|
|
routeChunks: { "route-home": "assets/home.js" },
|
|
contractSet: {
|
|
setAlgorithm: "CA_CONTRACT_SET_V1",
|
|
setDigest: EXPECTED_SET_DIGEST,
|
|
packages: EXPECTED_PACKAGES,
|
|
},
|
|
};
|
|
|
|
const runtimeV1: Parameters<typeof loadReleaseManifest>[0] = {
|
|
...runtime,
|
|
config: {
|
|
...runtime.config,
|
|
CONFIG_SCHEMA_VERSION: "1",
|
|
LEGACY_API_CONTRACT_VERSION: "1",
|
|
},
|
|
configSchema: "V1",
|
|
};
|
|
|
|
const manifestV1 = {
|
|
schemaVersion: 1,
|
|
appVersion: "0.1.0",
|
|
buildId: "build-a",
|
|
commitSha: "abc123",
|
|
configSchemaVersion: "1",
|
|
apiContractVersion: "1",
|
|
assetManifestHash: "hash-a",
|
|
releaseId: "release-a",
|
|
builtAt: "2026-07-25T00:00:00Z",
|
|
routeChunks: { "route-home": "assets/home.js" },
|
|
};
|
|
|
|
function jsonResponse(body: unknown): Response {
|
|
return new Response(JSON.stringify(body), {
|
|
headers: { "content-type": "application/json" },
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Round 2 review finding: a fixture built entirely from
|
|
* `EXPECTED_CONTRACT_SET_PACKAGES` proves the manifest matches itself, not
|
|
* that composition actually produced the TechLog package. This assertion is
|
|
* independent of that derivation — the expected value is a literal written
|
|
* here, not read back from `EXPECTED_PACKAGES`/`EXPECTED_SET_DIGEST` above —
|
|
* so if the unconditional install in `installed-contract-contributions.ts`
|
|
* is ever reverted and `EXPECTED_CONTRACT_SET_PACKAGES` silently shrinks to
|
|
* empty, this fails on its own regardless of what the fixture below does.
|
|
*/
|
|
describe("expected contract set composition", () => {
|
|
it("actually contains the TechLog Studio contract, not just an empty set matching itself", () => {
|
|
const techLog = EXPECTED_CONTRACT_SET_PACKAGES.find(
|
|
(entry) => entry.packageId === "@tech-log/studio-contract",
|
|
);
|
|
expect(techLog).toBeTruthy();
|
|
expect(techLog?.version).toBe("3.0.0");
|
|
});
|
|
});
|
|
|
|
describe("release manifest boot boundary", () => {
|
|
it("loads a coherent release tuple", async () => {
|
|
await expect(
|
|
loadReleaseManifest(
|
|
runtime,
|
|
{ fetcher: async () => jsonResponse(manifest) },
|
|
),
|
|
).resolves.toMatchObject({ releaseId: "release-a" });
|
|
});
|
|
|
|
it("fails before mount when release and runtime differ", async () => {
|
|
await expect(
|
|
loadReleaseManifest(
|
|
runtime,
|
|
{
|
|
fetcher: async () =>
|
|
jsonResponse({ ...manifest, buildId: "build-b" }),
|
|
},
|
|
),
|
|
).rejects.toMatchObject({
|
|
kind: "BUILD_MISMATCH",
|
|
code: "MANIFEST_BUILD_MISMATCH",
|
|
});
|
|
});
|
|
|
|
it.each([
|
|
[
|
|
{ releaseId: "release-b" },
|
|
{},
|
|
"RELEASE_MISMATCH",
|
|
"MANIFEST_RELEASE_MISMATCH",
|
|
],
|
|
[
|
|
{},
|
|
{ expectedAssetManifestHash: "different" },
|
|
"ASSET_MISMATCH",
|
|
"MANIFEST_ASSET_MISMATCH",
|
|
],
|
|
])(
|
|
"classifies tuple mismatch %# without a generic deploy error",
|
|
async (manifestOverride, options, kind, code) => {
|
|
await expect(
|
|
loadReleaseManifest(
|
|
runtime,
|
|
{
|
|
fetcher: async () =>
|
|
jsonResponse({ ...manifest, ...manifestOverride }),
|
|
...options,
|
|
},
|
|
),
|
|
).rejects.toMatchObject({ kind, code });
|
|
},
|
|
);
|
|
|
|
it.each(["3.0", "9.0"])(
|
|
"rejects unsupported V2 manifest config version %s at the schema boundary",
|
|
async (configSchemaVersion) => {
|
|
await expect(
|
|
loadReleaseManifest(runtime, {
|
|
fetcher: async () =>
|
|
jsonResponse({ ...manifest, configSchemaVersion }),
|
|
}),
|
|
).rejects.toMatchObject({
|
|
kind: "RELEASE_MANIFEST_FAILURE",
|
|
code: "MANIFEST_SCHEMA_INVALID",
|
|
});
|
|
},
|
|
);
|
|
|
|
it("rejects a contract set the build did not compile", async () => {
|
|
await expect(
|
|
loadReleaseManifest(runtime, {
|
|
fetcher: async () =>
|
|
jsonResponse({
|
|
...manifest,
|
|
contractSet: {
|
|
setAlgorithm: "CA_CONTRACT_SET_V1",
|
|
setDigest: EMPTY_SET_DIGEST,
|
|
packages: [
|
|
{
|
|
packageId: "@org-contracts/worklog",
|
|
version: "1.2.3",
|
|
digest: `sha256:${"a".repeat(64)}`,
|
|
runtimeProtocolVersion: 1,
|
|
sourceRevision: "abc1234",
|
|
},
|
|
],
|
|
},
|
|
}),
|
|
}),
|
|
).rejects.toMatchObject({
|
|
kind: "CONTRACT_SET_MISMATCH",
|
|
code: "CONTRACT_SET_PACKAGE_UNEXPECTED",
|
|
});
|
|
});
|
|
|
|
/**
|
|
* Round 2 review finding: deriving the fixture's `contractSet.packages`
|
|
* from `EXPECTED_CONTRACT_SET_PACKAGES` made `CONTRACT_SET_PACKAGE_MISSING`
|
|
* unreachable from any test — the equality was satisfied by construction.
|
|
* This restores that failure path with an independently built manifest
|
|
* that omits a package the real expected set actually requires, mirroring
|
|
* the shape of "rejects a contract set the build did not compile" above.
|
|
*/
|
|
it("rejects a manifest that omits a package the build actually compiled in", async () => {
|
|
await expect(
|
|
loadReleaseManifest(runtime, {
|
|
fetcher: async () =>
|
|
jsonResponse({
|
|
...manifest,
|
|
contractSet: {
|
|
setAlgorithm: "CA_CONTRACT_SET_V1",
|
|
setDigest: EMPTY_SET_DIGEST,
|
|
packages: [],
|
|
},
|
|
}),
|
|
}),
|
|
).rejects.toMatchObject({
|
|
kind: "CONTRACT_SET_MISMATCH",
|
|
code: "CONTRACT_SET_PACKAGE_MISSING",
|
|
});
|
|
});
|
|
|
|
it("still reads a V1 manifest during the compatibility window", async () => {
|
|
await expect(
|
|
loadReleaseManifest(
|
|
runtimeV1,
|
|
{
|
|
fetcher: async () => jsonResponse(manifestV1),
|
|
},
|
|
),
|
|
).resolves.toMatchObject({ schemaVersion: 1, contractSet: null });
|
|
});
|
|
|
|
it("rejects a V2 runtime paired with a V1 manifest", async () => {
|
|
await expect(
|
|
loadReleaseManifest(runtime, {
|
|
fetcher: async () => jsonResponse({
|
|
...manifestV1,
|
|
configSchemaVersion: "2.0",
|
|
}),
|
|
}),
|
|
).rejects.toMatchObject({
|
|
kind: "PROTOCOL_PAIR_MISMATCH",
|
|
code: "MANIFEST_PROTOCOL_PAIR_MISMATCH",
|
|
});
|
|
});
|
|
|
|
it("rejects a V1 runtime paired with a V2 manifest", async () => {
|
|
await expect(
|
|
loadReleaseManifest(runtimeV1, {
|
|
fetcher: async () => jsonResponse(manifest),
|
|
}),
|
|
).rejects.toMatchObject({
|
|
kind: "PROTOCOL_PAIR_MISMATCH",
|
|
code: "MANIFEST_PROTOCOL_PAIR_MISMATCH",
|
|
});
|
|
});
|
|
|
|
it("requires matching legacy scalar versions for a V1 pair", async () => {
|
|
await expect(
|
|
loadReleaseManifest(runtimeV1, {
|
|
fetcher: async () => jsonResponse({
|
|
...manifestV1,
|
|
apiContractVersion: "2",
|
|
}),
|
|
}),
|
|
).rejects.toMatchObject({
|
|
kind: "API_CONTRACT_MISMATCH",
|
|
code: "MANIFEST_API_CONTRACT_MISMATCH",
|
|
});
|
|
});
|
|
|
|
it("rejects a manifest without a complete route chunk map", async () => {
|
|
const malformed = Object.fromEntries(
|
|
Object.entries(manifest).filter(([key]) => key !== "routeChunks"),
|
|
);
|
|
await expect(
|
|
loadReleaseManifest(
|
|
runtime,
|
|
{ fetcher: async () => jsonResponse(malformed) },
|
|
),
|
|
).rejects.toBeInstanceOf(ReleaseManifestError);
|
|
});
|
|
});
|