import assert from "node:assert/strict"; import { test } from "node:test"; import { capabilityId as id, valueType, validateWorkspaceRevision } from "../src/capability-model/index.js"; import { contentDigest, contentDigestAsync, planEvolution, reviewSemanticMajors, runtimeContracts, storageContracts, storageChangeRequiresMigration, } from "../src/capability-model/evolution.js"; import { capabilityFixtureSource, capabilityResourceSources, compileCapabilityFixture, makeValidCapabilityWorkspace, } from "./fixtures/capability-model.js"; test("QX carries stable conformance IDs and implementation semantic majors", () => { const result = compileCapabilityFixture({ workspace: capabilityFixtureSource.replace( "conform Project as Named {", 'conform Project as Named id "conformance:project:named" semantic-major 3 {', ), todo: capabilityResourceSources.todo.replace( 'revision "package:todo-runtime@1" {', 'revision "package:todo-runtime@1" semantic-major 2 {', ), }); assert.ok(result.ok, JSON.stringify(result)); assert.equal(result.workspace.conformances[0]!.id, "conformance:project:named"); assert.equal(result.workspace.conformances[0]!.semanticMajor, 3); assert.equal(result.workspace.packageImports[0]!.semanticMajor, 2); }); test("invalid majors and duplicate owner identities are rejected", () => { const workspace = makeValidCapabilityWorkspace(); workspace.packageImports[0]!.semanticMajor = 0; workspace.conformances[0]!.semanticMajor = 1.5; workspace.conformances[0]!.id = id.conformance("same"); workspace.conformances[1]!.id = id.conformance("same"); const issues = validateWorkspaceRevision(workspace); assert.equal(issues.filter((entry) => entry.code === "invalid-semantic-major").length, 2); assert.ok(issues.some((entry) => entry.code === "duplicate-conformance-id")); }); test("hashing is canonical and rejects values outside the persisted JSON contract", () => { assert.equal(contentDigest({ z: 1, a: { y: 3, b: 2 } }), contentDigest({ a: { b: 2, y: 3 }, z: 1 })); assert.notEqual(contentDigest([1, 2]), contentDigest([2, 1])); assert.throws(() => contentDigest(NaN)); assert.throws(() => contentDigest(new Date())); }); test("cooperative hashing preserves canonical identities and services the event loop", async () => { const sparse = [1, 2, 3]; delete sparse[1]; const values = [null, false, -0, "δΈ­\\n\ud800", sparse, { z: 1, a: { omitted: undefined, b: [false, 1e100] } }]; for (const value of values) assert.equal(await contentDigestAsync(value), contentDigest(value)); for (const value of [undefined, NaN, Infinity, new Date(), [undefined]]) await assert.rejects(contentDigestAsync(value)); const large = Array.from({ length: 50000 }, (_, index) => ({ z: "definition".repeat(8), a: index })); const expected = contentDigest(large); let serviced = false; const timer = setTimeout(() => { serviced = true; }, 0); try { assert.equal(await contentDigestAsync(large), expected); assert.equal(serviced, true, "large verification must let host renewals and RPCs run"); } finally { clearTimeout(timer); } }); test("a workspace root change and display names do not restart package runtimes", () => { const before = makeValidCapabilityWorkspace(); const after = structuredClone(before); after.id = id.workspaceRevision("next"); after.sourceRootCommit = "f".repeat(40); after.atoms[0]!.displayName = "Renamed"; after.sharedAttachments[0]!.displayName = "Renamed storage"; after.conformances.reverse(); after.interfaceImports.reverse(); const report = planEvolution(before, after, { allowLegacy: true }); assert.deepEqual( report.runtimeActions.map((entry) => entry.action), ["keep"], ); assert.deepEqual(report.storageChanges, []); assert.deepEqual(report.packageChecks, []); }); test("consumer changes preserve unrelated resource owners", () => { const before = makeValidCapabilityWorkspace(); before.packageImports.push({ packageId: id.package("package:resource-owner"), revisionId: id.packageRevision("package:resource-owner@1"), displayName: "ResourceOwner", source: { repository: "https://example.org/owner.git", commit: "a".repeat(40) }, exports: [ { kind: "function", id: id.packageExport("export:owner:ping"), displayName: "ping", inputType: valueType.unit, outputType: valueType.unit, dependencyPorts: [], }, ], }); const after = structuredClone(before); after.packageImports[0]!.source.commit = "e".repeat(40); const actions = planEvolution(before, after, { allowLegacy: true }).runtimeActions; assert.equal(actions.find((entry) => entry.groupId === "package:resource-owner")!.action, "keep"); assert.equal(actions.find((entry) => entry.groupId === "package:todo-runtime")!.action, "replace"); }); test("storage defaults and ownership changes invalidate consumers without requiring a major", () => { const before = makeValidCapabilityWorkspace(); const after = structuredClone(before); const slot = after.sharedAttachments[0]!; assert.equal(slot.kind, "state"); if (slot.kind === "state") slot.defaultValue = "Different default"; const report = planEvolution(before, after, { allowLegacy: true }); assert.equal(report.storageChanges.length, 1); assert.deepEqual(report.migrationRequired, []); assert.equal(report.runtimeActions[0]!.action, "replace"); assert.deepEqual(report.reviews, []); assert.notDeepEqual(storageContracts(before), storageContracts(after)); }); test("semantic review receipts cover exact consumer/provider contracts", () => { const before = makeValidCapabilityWorkspace(); before.conformances[0]!.id = id.conformance("conformance:project:named"); const after = structuredClone(before); after.conformances[0]!.semanticMajor = 2; const report = planEvolution(before, after, { allowLegacy: true }); assert.equal(report.reviews.length, 1); assert.equal(report.reviews[0]!.accepted, false); const receipt = { requirementDigest: report.reviews[0]!.requirementDigest, decision: "accepted-unchanged" as const, rationale: "Reviewed the semantic change against summary behavior", agentId: "workspace-agent", }; assert.equal(planEvolution(before, after, { allowLegacy: true, reviews: [receipt] }).reviews[0]!.accepted, true); after.packageImports[0]!.source.commit = "f".repeat(40); assert.equal(planEvolution(before, after, { allowLegacy: true, reviews: [receipt] }).reviews[0]!.accepted, false); }); test("execution semantic review is independent of storage migration and process action planning", () => { const before = makeValidCapabilityWorkspace(); before.conformances[0]!.id = id.conformance("conformance:project:named"); const after = structuredClone(before); after.conformances[0]!.semanticMajor = 2; const report = reviewSemanticMajors(before, after); assert.equal(report.checkerVersion, "quixos-semantic-review-v1"); assert.equal(report.reviews.length, 1); assert.equal(report.reviews[0]!.accepted, false); assert.equal("migrationRequired" in report, false); assert.equal("runtimeActions" in report, false); assert.equal(report.blockers.length, 1); const receipt = { requirementDigest: report.reviews[0]!.requirementDigest, decision: "accepted-unchanged" as const, rationale: "The consumer only displays this provider's unchanged label contract", agentId: "fixture-agent", }; assert.deepEqual(reviewSemanticMajors(before, after, [receipt]).blockers, []); const oldReceipt = { ...receipt, requirementDigest: planEvolution(before, after, { allowLegacy: true }).reviews[0]!.requirementDigest, }; assert.equal(reviewSemanticMajors(before, after, [oldReceipt]).reviews[0]!.accepted, false); after.packageImports[0]!.source.commit = "f".repeat(40); assert.equal(reviewSemanticMajors(before, after, [receipt]).reviews[0]!.accepted, false); assert.deepEqual(reviewSemanticMajors(null, after).blockers, []); assert.throws( () => reviewSemanticMajors(before, { ...after, workspaceId: id.workspace("different") }), /different workspace/, ); const earlier = structuredClone(after); earlier.conformances[0]!.semanticMajor = 1; assert.ok(reviewSemanticMajors(after, earlier).blockers.some((message) => message.includes("decreases"))); }); test("evolution enrollment is explicit and never erases legacy ownership", () => { const workspace = makeValidCapabilityWorkspace(); const report = planEvolution(workspace, workspace); assert.ok(report.blockers.some((entry) => entry.includes("workspace-shared"))); assert.ok(report.blockers.some((entry) => entry.includes("authored ID"))); assert.equal(runtimeContracts(workspace).length, 1); assert.throws( () => planEvolution(workspace, { ...workspace, workspaceId: id.workspace("other") }), /different workspace/, ); }); test("automatic preservation distinguishes additions and defaults from incompatible storage", () => { const workspace = makeValidCapabilityWorkspace(); const before = storageContracts(workspace).find((entry) => entry.kind === "state")!; const next = structuredClone(before); const value = next.definition as Record; value.defaultValue = { displayName: "important user data" }; assert.equal(storageChangeRequiresMigration(before, next, new Set()), false); next.ownerId = "another-owner"; assert.equal(storageChangeRequiresMigration(before, next, new Set()), true); assert.equal(storageChangeRequiresMigration(before, undefined, new Set()), true); delete value.defaultValue; assert.equal(storageChangeRequiresMigration(undefined, next, new Set([value.attachedTo as string])), true); assert.equal(storageChangeRequiresMigration(undefined, next, new Set()), false); const slot = workspace.sharedAttachments.find((entry) => entry.kind === "state")!; if (slot.kind !== "state") throw new Error("fixture slot"); slot.defaultValue = { displayName: "one" }; const first = storageContracts(workspace); slot.defaultValue = { displayName: "two" }; assert.notDeepEqual(storageContracts(workspace), first, "user data must not be stripped as schema metadata"); });