From e35f8c315c8ca59c48f53b7ee6f33cb0519035ee Mon Sep 17 00:00:00 2001 From: Mike Long Date: Mon, 16 Mar 2026 11:06:08 -0700 Subject: [PATCH] feat(runtime): add governance-aware runtime bundle preparation --- .../interfacectl-cli/dist/adapter/bundle.d.ts | 1 + .../dist/adapter/bundle.d.ts.map | 2 +- .../interfacectl-cli/dist/adapter/bundle.js | 12 + .../dist/commands/compile.d.ts.map | 2 +- .../interfacectl-cli/dist/commands/compile.js | 180 ++++++++++++ .../dist/commands/prepare-generation.d.ts | 8 + .../dist/commands/prepare-generation.d.ts.map | 2 +- .../dist/commands/prepare-generation.js | 22 ++ .../dist/commands/prepare-runtime.d.ts | 90 ++++++ .../dist/commands/prepare-runtime.d.ts.map | 1 + .../dist/commands/prepare-runtime.js | 157 +++++++++++ packages/interfacectl-cli/dist/index.js | 14 + .../prepare-generation-output.schema.json | 15 +- .../prepare-runtime-output.schema.json | 195 +++++++++++++ .../interfacectl-cli/src/adapter/bundle.ts | 15 + .../interfacectl-cli/src/commands/compile.ts | 227 +++++++++++++++ .../src/commands/prepare-generation.ts | 22 ++ .../src/commands/prepare-runtime.ts | 203 ++++++++++++++ packages/interfacectl-cli/src/index.ts | 15 + .../interfacectl-cli/test/compile.test.mjs | 54 +++- .../surfaces/demo-surface/generation.json | 22 ++ .../surfaces/demo-surface/runtime.json | 87 ++++++ .../test/prepare-generation.test.mjs | 30 ++ .../test/prepare-runtime.test.mjs | 200 ++++++++++++++ .../interfacectl-validator/dist/index.d.ts | 2 +- .../dist/index.d.ts.map | 2 +- packages/interfacectl-validator/dist/index.js | 34 +++ .../schema/web.surface.contract.schema.json | 259 ++++++++++++++++++ .../interfacectl-validator/dist/types.d.ts | 54 ++++ .../dist/types.d.ts.map | 2 +- packages/interfacectl-validator/src/index.ts | 83 ++++++ .../schema/web.surface.contract.schema.json | 259 ++++++++++++++++++ packages/interfacectl-validator/src/types.ts | 88 ++++++ .../test/authoring-contract.test.mjs | 56 ++++ 34 files changed, 2405 insertions(+), 10 deletions(-) create mode 100644 packages/interfacectl-cli/dist/commands/prepare-runtime.d.ts create mode 100644 packages/interfacectl-cli/dist/commands/prepare-runtime.d.ts.map create mode 100644 packages/interfacectl-cli/dist/commands/prepare-runtime.js create mode 100644 packages/interfacectl-cli/schemas/prepare-runtime-output.schema.json create mode 100644 packages/interfacectl-cli/src/commands/prepare-runtime.ts create mode 100644 packages/interfacectl-cli/test/fixtures/compile/expected/surfaces/demo-surface/runtime.json create mode 100644 packages/interfacectl-cli/test/prepare-runtime.test.mjs diff --git a/packages/interfacectl-cli/dist/adapter/bundle.d.ts b/packages/interfacectl-cli/dist/adapter/bundle.d.ts index 9e8ea3b..deb1474 100644 --- a/packages/interfacectl-cli/dist/adapter/bundle.d.ts +++ b/packages/interfacectl-cli/dist/adapter/bundle.d.ts @@ -26,6 +26,7 @@ export interface LoadedCompiledSurfaceBundle { components: LoadedJsonFile; constraints: LoadedJsonFile; repairMap: LoadedJsonFile; + runtime?: LoadedJsonFile; authoring?: LoadedJsonFile; }; } diff --git a/packages/interfacectl-cli/dist/adapter/bundle.d.ts.map b/packages/interfacectl-cli/dist/adapter/bundle.d.ts.map index f710a6c..9edd36f 100644 --- a/packages/interfacectl-cli/dist/adapter/bundle.d.ts.map +++ b/packages/interfacectl-cli/dist/adapter/bundle.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../../src/adapter/bundle.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,wBAAwB,QAAQ,CAAC;AAE9C,MAAM,WAAW,UAAU;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,cAAe,SAAQ,UAAU;IAChD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,CAAC,CAAC;CACV;AAED,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,cAAc,CAAC,cAAc,CAAC,CAAC;IACzC,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE;QACP,EAAE,EAAE,MAAM,CAAC;QACX,GAAG,EAAE,MAAM,CAAC;QACZ,UAAU,EAAE,cAAc,CAAC;QAC3B,QAAQ,EAAE,cAAc,CAAC;QACzB,UAAU,EAAE,cAAc,CAAC;QAC3B,WAAW,EAAE,cAAc,CAAC;QAC5B,SAAS,EAAE,cAAc,CAAC;QAC1B,SAAS,CAAC,EAAE,cAAc,CAAC;KAC5B,CAAC;CACH;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAEnB,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAO;CAM7F;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAE9E;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAE5D;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,CAalG;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAOxE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAO5E;AAQD,wBAAgB,yBAAyB,CACvC,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GACV,2BAA2B,CAiH7B"} \ No newline at end of file +{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../../src/adapter/bundle.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,wBAAwB,QAAQ,CAAC;AAE9C,MAAM,WAAW,UAAU;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,cAAe,SAAQ,UAAU;IAChD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,CAAC,CAAC;CACV;AAED,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,cAAc,CAAC,cAAc,CAAC,CAAC;IACzC,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE;QACP,EAAE,EAAE,MAAM,CAAC;QACX,GAAG,EAAE,MAAM,CAAC;QACZ,UAAU,EAAE,cAAc,CAAC;QAC3B,QAAQ,EAAE,cAAc,CAAC;QACzB,UAAU,EAAE,cAAc,CAAC;QAC3B,WAAW,EAAE,cAAc,CAAC;QAC5B,SAAS,EAAE,cAAc,CAAC;QAC1B,OAAO,CAAC,EAAE,cAAc,CAAC;QACzB,SAAS,CAAC,EAAE,cAAc,CAAC;KAC5B,CAAC;CACH;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAEnB,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAO;CAM7F;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAE9E;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAE5D;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,CAalG;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAOxE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAO5E;AAQD,wBAAgB,yBAAyB,CACvC,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GACV,2BAA2B,CA+H7B"} \ No newline at end of file diff --git a/packages/interfacectl-cli/dist/adapter/bundle.js b/packages/interfacectl-cli/dist/adapter/bundle.js index 1cd3909..f0e1104 100644 --- a/packages/interfacectl-cli/dist/adapter/bundle.js +++ b/packages/interfacectl-cli/dist/adapter/bundle.js @@ -116,6 +116,17 @@ export function loadCompiledSurfaceBundle(bundleRootInput, surfaceId, cwd) { value: readJsonFile(authoringPath, "Authoring bundle"), }; } + let runtime; + const runtimeRef = typeof refs.runtime === "string" && refs.runtime.trim().length > 0 + ? refs.runtime + : "./runtime.json"; + const runtimePath = path.resolve(path.dirname(generationPath), runtimeRef); + if (fs.existsSync(runtimePath) && fs.statSync(runtimePath).isFile()) { + runtime = { + path: runtimePath, + value: readJsonFile(runtimePath, "Runtime bundle"), + }; + } const generationProvenance = isRecord(generation.value.provenance) ? generation.value.provenance : undefined; @@ -147,6 +158,7 @@ export function loadCompiledSurfaceBundle(bundleRootInput, surfaceId, cwd) { components, constraints, repairMap, + ...(runtime ? { runtime } : {}), ...(authoring ? { authoring } : {}), }, }; diff --git a/packages/interfacectl-cli/dist/commands/compile.d.ts.map b/packages/interfacectl-cli/dist/commands/compile.d.ts.map index 936a25c..3c3510c 100644 --- a/packages/interfacectl-cli/dist/commands/compile.d.ts.map +++ b/packages/interfacectl-cli/dist/commands/compile.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/commands/compile.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA0qBD,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,qBAAqB,EAC9B,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC,CAkGjB"} \ No newline at end of file +{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/commands/compile.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA64BD,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,qBAAqB,EAC9B,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC,CAkGjB"} \ No newline at end of file diff --git a/packages/interfacectl-cli/dist/commands/compile.js b/packages/interfacectl-cli/dist/commands/compile.js index c9bd867..2d0e10c 100644 --- a/packages/interfacectl-cli/dist/commands/compile.js +++ b/packages/interfacectl-cli/dist/commands/compile.js @@ -53,6 +53,119 @@ function uniqueStrings(values) { } return result; } +function getPolicyRank(policy) { + switch (policy) { + case "strict": + return 2; + case "warn": + return 1; + case "off": + default: + return 0; + } +} +function maxPolicy(...policies) { + let strongest = "off"; + for (const policy of policies) { + if (getPolicyRank(policy) > getPolicyRank(strongest)) { + strongest = policy ?? "off"; + } + } + return strongest; +} +function buildGovernancePayload(surface) { + return { + owner: surface.owner ?? null, + domain: surface.domain ?? null, + phase0: surface.phase0 ?? null, + status: surface.governance?.status ?? "draft", + roles: surface.governance?.roles ?? {}, + approvals: surface.governance?.approvals ?? [], + }; +} +function inferMutationMode(surface, sections) { + const explicitMode = surface.runtime?.mutationEnvelope?.mode; + if (explicitMode) + return explicitMode; + const sectionModes = uniqueStrings(sections.map((section) => section.editPolicy?.mode)); + const allowedOperations = uniqueStrings(sections.flatMap((section) => section.editPolicy?.allowedOperations ?? [])); + if (sectionModes.length > 0 && sectionModes.every((mode) => mode === "locked")) { + return "locked"; + } + if (sectionModes.includes("freeform")) { + return "freeform"; + } + if (allowedOperations.includes("adjust-layout")) { + return "layout-tuning"; + } + if (sectionModes.includes("slot-bound")) { + return "slot-bound"; + } + return "content-only"; +} +function inferMutationScopes(actions) { + return uniqueStrings(actions.flatMap((action) => { + switch (action) { + case "update-copy": + case "change-media": + case "bind-data": + return ["content"]; + case "swap-variant": + case "swap-component": + return ["components"]; + case "adjust-layout": + return ["layout"]; + case "add-section": + case "remove-section": + case "reorder-sections": + return ["sections"]; + case "wire-interaction": + return ["interactions"]; + case "reorder-items": + return ["content", "layout"]; + default: + return []; + } + })); +} +function buildMutationEnvelope(surface, sections) { + const explicit = surface.runtime?.mutationEnvelope; + const inferredActions = uniqueStrings(sections.flatMap((section) => section.editPolicy?.allowedOperations ?? [])); + const allowedActions = uniqueStrings([ + ...(explicit?.allowedActions ?? []), + ...(explicit?.allowedActions ? [] : inferredActions), + inferredActions.length === 0 ? "update-copy" : undefined, + ]); + const scopes = uniqueStrings([ + ...(explicit?.scopes ?? []), + ...(explicit?.scopes ? [] : inferMutationScopes(allowedActions)), + ]); + return { + mode: inferMutationMode(surface, sections), + ...(scopes.length > 0 ? { scopes } : {}), + ...(allowedActions.length > 0 ? { allowedActions } : {}), + ...(explicit?.allowedSections ? { allowedSections: explicit.allowedSections } : {}), + ...(explicit?.prohibitedSections ? { prohibitedSections: explicit.prohibitedSections } : {}), + }; +} +function buildPolicySeverities(contract, surface) { + const boundary = surface.mustNotEmit?.length || contract.shell?.owns?.length + ? "strict" + : "off"; + const structure = maxPolicy(surface.requiredSections.length > 0 ? "strict" : "off", surface.layout.landingPattern?.policy); + const layout = maxPolicy(surface.layout.pageFrame?.enforcement, surface.layout.chromePolicy?.policy, surface.layout.landingPattern?.policy, surface.layout.landingPattern?.marketingLayoutPolicy); + const visual = maxPolicy(contract.color.policy, surface.icons?.policy, contract.tokens?.typography?.policy, contract.tokens?.motion?.policy, surface.marketingTypographyPolicy); + const interaction = surface.flows?.policy ?? "off"; + const runtime = maxPolicy(surface.runtime?.policy, boundary, structure, layout, visual, interaction); + return { + boundary, + structure, + layout, + visual, + interaction, + runtime, + }; +} function collectComponentIdsFromSlots(slots) { if (!slots) return []; @@ -262,6 +375,12 @@ function buildGenerationPayload(contract, surface, sections) { const requiredSections = surface.requiredSections; const landingPattern = surface.layout.landingPattern; const observationRefs = buildObservationRefs(contract); + const governance = buildGovernancePayload(surface); + const adaptation = { + policy: surface.runtime?.policy ?? buildPolicySeverities(contract, surface).runtime, + mutationEnvelope: buildMutationEnvelope(surface, sections), + contextIds: (surface.runtime?.contexts ?? []).map((context) => context.id), + }; return { identity: { surfaceId: surface.id, @@ -318,6 +437,8 @@ function buildGenerationPayload(contract, surface, sections) { tokenPolicyCategories: Object.keys(contract.tokens ?? {}), }, }, + governance, + adaptation, guidance: buildGuidance(contract, surface, sections), refs: { contract: "../../contract/normalized.json", @@ -326,6 +447,7 @@ function buildGenerationPayload(contract, surface, sections) { constraints: "./constraints.json", ...(surface.authoring ? { authoring: "./authoring.json" } : {}), repairMap: "./repair-map.json", + runtime: "./runtime.json", ...(observationRefs.length > 0 ? { evidence: observationRefs } : {}), }, }; @@ -485,6 +607,59 @@ function buildRepairMapPayload(contract, surface, sections) { repairs, }; } +function buildRuntimePayload(contract, surface, sections, components) { + const policySeverities = buildPolicySeverities(contract, surface); + const mutationEnvelope = buildMutationEnvelope(surface, sections); + return { + provenance: makeBundleProvenance(contract, surface.id), + identity: { + surfaceId: surface.id, + displayName: surface.displayName, + type: surface.type, + }, + governance: buildGovernancePayload(surface), + runtime: { + policy: surface.runtime?.policy ?? policySeverities.runtime, + policySeverities, + mutationEnvelope, + contexts: surface.runtime?.contexts ?? [], + boundary: { + shellOwns: contract.shell?.owns ?? [], + contentSlot: contract.shell?.contentSlot ?? null, + mustNotEmit: surface.mustNotEmit ?? [], + allowSources: surface.shellOwnedPrimitiveAllowSources ?? [], + }, + structure: { + requiredSections: surface.requiredSections, + allowedSections: sections.map((section) => section.id), + allowedComponents: components.map((component) => component.id), + }, + layout: { + maxContentWidth: surface.layout.maxContentWidth, + requiredContainers: surface.layout.requiredContainers ?? [], + ...(surface.layout.pageFrame ? { pageFrame: surface.layout.pageFrame } : {}), + ...(surface.layout.chromePolicy ? { chromePolicy: surface.layout.chromePolicy } : {}), + ...(surface.layout.landingPattern ? { landingPattern: surface.layout.landingPattern } : {}), + ...(surface.viewports ? { viewports: surface.viewports } : {}), + }, + visual: { + allowedFonts: surface.allowedFonts, + color: contract.color, + motion: contract.constraints.motion, + ...(contract.tokens ? { tokens: contract.tokens } : {}), + ...(surface.icons ? { icons: surface.icons } : {}), + }, + ...(surface.flows ? { interaction: { flows: surface.flows } } : {}), + }, + refs: { + contract: "../../contract/normalized.json", + sections: "./sections.json", + components: "./components.json", + constraints: "./constraints.json", + repairMap: "./repair-map.json", + }, + }; +} function buildSurfaceBundleFiles(contract, surface) { const surfaceDir = `surfaces/${surface.id}`; const sections = resolveSurfaceSections(contract, surface); @@ -495,6 +670,7 @@ function buildSurfaceBundleFiles(contract, surface) { const componentsPayload = buildComponentsPayload(contract, surface, components); const repairMapPayload = buildRepairMapPayload(contract, surface, sections); const authoringPayload = buildAuthoringPayload(contract, surface); + const runtimePayload = buildRuntimePayload(contract, surface, sections, components); const files = [ { path: `${surfaceDir}/generation.json`, @@ -516,6 +692,10 @@ function buildSurfaceBundleFiles(contract, surface) { path: `${surfaceDir}/repair-map.json`, content: stringifyDeterministic(repairMapPayload), }, + { + path: `${surfaceDir}/runtime.json`, + content: stringifyDeterministic(runtimePayload), + }, ]; if (authoringPayload) { files.push({ diff --git a/packages/interfacectl-cli/dist/commands/prepare-generation.d.ts b/packages/interfacectl-cli/dist/commands/prepare-generation.d.ts index 570949e..e72915e 100644 --- a/packages/interfacectl-cli/dist/commands/prepare-generation.d.ts +++ b/packages/interfacectl-cli/dist/commands/prepare-generation.d.ts @@ -14,6 +14,7 @@ interface SummaryRepairItem { export declare function buildPreparedGenerationPayload(bundle: LoadedCompiledSurfaceBundle): { evidenceRefs: any[]; authoring?: JsonRecord | undefined; + runtime?: JsonRecord | undefined; surface: { surfaceId: string; displayName: string; @@ -25,6 +26,7 @@ export declare function buildPreparedGenerationPayload(bundle: LoadedCompiledSur manifestPath: string; sourcePaths: { authoring?: string | undefined; + runtime?: string | undefined; contract: string; generation: string; sections: string; @@ -55,6 +57,8 @@ export declare function buildPreparedGenerationPayload(bundle: LoadedCompiledSur structure: JsonRecord; layout: JsonRecord; visual: JsonRecord; + governance: JsonRecord; + adaptation: JsonRecord; guidance: JsonRecord; }; sections: any[]; @@ -65,6 +69,7 @@ export declare function buildPreparedGenerationPayload(bundle: LoadedCompiledSur export declare function loadPreparedGenerationPayload(bundleRoot: string, surfaceId: string, cwd?: string): { evidenceRefs: any[]; authoring?: JsonRecord | undefined; + runtime?: JsonRecord | undefined; surface: { surfaceId: string; displayName: string; @@ -76,6 +81,7 @@ export declare function loadPreparedGenerationPayload(bundleRoot: string, surfac manifestPath: string; sourcePaths: { authoring?: string | undefined; + runtime?: string | undefined; contract: string; generation: string; sections: string; @@ -106,6 +112,8 @@ export declare function loadPreparedGenerationPayload(bundleRoot: string, surfac structure: JsonRecord; layout: JsonRecord; visual: JsonRecord; + governance: JsonRecord; + adaptation: JsonRecord; guidance: JsonRecord; }; sections: any[]; diff --git a/packages/interfacectl-cli/dist/commands/prepare-generation.d.ts.map b/packages/interfacectl-cli/dist/commands/prepare-generation.d.ts.map index 80806aa..968c7e1 100644 --- a/packages/interfacectl-cli/dist/commands/prepare-generation.d.ts.map +++ b/packages/interfacectl-cli/dist/commands/prepare-generation.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"prepare-generation.d.ts","sourceRoot":"","sources":["../../src/commands/prepare-generation.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,UAAU,EACf,KAAK,2BAA2B,EACjC,MAAM,sBAAsB,CAAC;AAG9B,MAAM,WAAW,+BAA+B;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,cAAc,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEhD,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AA8HD,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAzDnD,MAAM;mBAAS,MAAM;oBAAU,MAAM;;;;;;;;;;;;;;;EA+GnE;AAED,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,GAAG,SAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBApHU,MAAM;mBAAS,MAAM;oBAAU,MAAM;;;;;;;;;;;;;;;EAwHnE;AAgBD,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,MAAM,CAAC,CA+BjB"} \ No newline at end of file +{"version":3,"file":"prepare-generation.d.ts","sourceRoot":"","sources":["../../src/commands/prepare-generation.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,UAAU,EACf,KAAK,2BAA2B,EACjC,MAAM,sBAAsB,CAAC;AAG9B,MAAM,WAAW,+BAA+B;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,cAAc,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEhD,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AA2ID,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBApEnD,MAAM;mBAAS,MAAM;oBAAU,MAAM;;;;;;;;;;;;;;;;;EAmInE;AAED,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,GAAG,SAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAxIU,MAAM;mBAAS,MAAM;oBAAU,MAAM;;;;;;;;;;;;;;;;;EA4InE;AAgBD,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,MAAM,CAAC,CA+BjB"} \ No newline at end of file diff --git a/packages/interfacectl-cli/dist/commands/prepare-generation.js b/packages/interfacectl-cli/dist/commands/prepare-generation.js index 749d3b3..739b6a3 100644 --- a/packages/interfacectl-cli/dist/commands/prepare-generation.js +++ b/packages/interfacectl-cli/dist/commands/prepare-generation.js @@ -51,6 +51,8 @@ function buildSummary(bundle) { const generation = asRecord(bundle.surface.generation.value); const boundary = asRecord(generation.boundary); const structure = asRecord(generation.structure); + const adaptation = asRecord(generation.adaptation); + const mutationEnvelope = asRecord(adaptation.mutationEnvelope); const guidance = asRecord(generation.guidance); const repairs = getRepairSummary(bundle.surface.repairMap.value); const focusOrder = asStringArray(guidance.generationFocusOrder); @@ -82,6 +84,14 @@ function buildSummary(bundle) { detail: `Focus order: ${focusOrder.join(" -> ")}.`, }); } + const mutationMode = asString(mutationEnvelope.mode); + if (mutationMode) { + checklist.push({ + id: "mutation-envelope", + label: "Stay within the mutation envelope", + detail: `Allowed mutation mode: ${mutationMode}.`, + }); + } if (repairs.length > 0) { checklist.push({ id: "repair-priorities", @@ -102,6 +112,9 @@ function buildSummary(bundle) { if (prohibitedRoles.length > 0) { textParts.push(`avoid shell-owned roles ${prohibitedRoles.join(", ")}`); } + if (mutationMode) { + textParts.push(`stay within ${mutationMode} mutation scope`); + } if (repairs.length > 0) { textParts.push(`prioritize repairs ${repairs.slice(0, 3).map((repair) => repair.code).join(", ")}`); } @@ -122,6 +135,9 @@ export function buildPreparedGenerationPayload(bundle) { const componentsDoc = asRecord(bundle.surface.components.value); const constraintsDoc = asRecord(bundle.surface.constraints.value); const repairMapDoc = asRecord(bundle.surface.repairMap.value); + const runtimeDoc = bundle.surface.runtime + ? asRecord(bundle.surface.runtime.value) + : undefined; const authoringDoc = bundle.surface.authoring ? asRecord(bundle.surface.authoring.value) : undefined; @@ -142,6 +158,7 @@ export function buildPreparedGenerationPayload(bundle) { components: bundle.surface.components.path, constraints: bundle.surface.constraints.path, repairMap: bundle.surface.repairMap.path, + ...(bundle.surface.runtime ? { runtime: bundle.surface.runtime.path } : {}), ...(bundle.surface.authoring ? { authoring: bundle.surface.authoring.path } : {}), }, }, @@ -156,12 +173,17 @@ export function buildPreparedGenerationPayload(bundle) { structure: asRecord(generation.structure), layout: asRecord(generation.layout), visual: asRecord(generation.visual), + governance: asRecord(generation.governance), + adaptation: asRecord(generation.adaptation), guidance: asRecord(generation.guidance), }, sections: Array.isArray(sectionsDoc.sections) ? sectionsDoc.sections : [], components: Array.isArray(componentsDoc.components) ? componentsDoc.components : [], constraints: asRecord(constraintsDoc.constraints), repairMap: Array.isArray(repairMapDoc.repairs) ? repairMapDoc.repairs : [], + ...(runtimeDoc && isRecord(runtimeDoc.runtime) + ? { runtime: runtimeDoc.runtime } + : {}), ...(authoringDoc && isRecord(authoringDoc.authoring) ? { authoring: authoringDoc.authoring } : {}), diff --git a/packages/interfacectl-cli/dist/commands/prepare-runtime.d.ts b/packages/interfacectl-cli/dist/commands/prepare-runtime.d.ts new file mode 100644 index 0000000..7e422df --- /dev/null +++ b/packages/interfacectl-cli/dist/commands/prepare-runtime.d.ts @@ -0,0 +1,90 @@ +import { type JsonRecord, type LoadedCompiledSurfaceBundle } from "../adapter/bundle.js"; +export interface PrepareRuntimeCommandOptions { + bundleRoot?: string; + surfaceId?: string; + outPath?: string; +} +export declare function buildPreparedRuntimePayload(bundle: LoadedCompiledSurfaceBundle): { + surface: { + surfaceId: string; + displayName: string; + type: string; + }; + bundle: { + root: string; + version: string; + manifestPath: string; + sourcePaths: { + contract: string; + runtime: string; + generation: string; + sections: string; + components: string; + constraints: string; + repairMap: string; + }; + }; + contract: { + id: string; + version: string; + normalizedPath: string; + }; + summary: { + text: string; + requiredSectionIds: string[]; + mutationMode: string; + strictCategories: string[]; + contextIds: string[]; + checklist: { + id: string; + label: string; + detail: string; + }[]; + }; + governance: JsonRecord; + runtime: JsonRecord; + evidenceRefs: any[]; +}; +export declare function loadPreparedRuntimePayload(bundleRoot: string, surfaceId: string, cwd?: string): { + surface: { + surfaceId: string; + displayName: string; + type: string; + }; + bundle: { + root: string; + version: string; + manifestPath: string; + sourcePaths: { + contract: string; + runtime: string; + generation: string; + sections: string; + components: string; + constraints: string; + repairMap: string; + }; + }; + contract: { + id: string; + version: string; + normalizedPath: string; + }; + summary: { + text: string; + requiredSectionIds: string[]; + mutationMode: string; + strictCategories: string[]; + contextIds: string[]; + checklist: { + id: string; + label: string; + detail: string; + }[]; + }; + governance: JsonRecord; + runtime: JsonRecord; + evidenceRefs: any[]; +}; +export declare function runPrepareRuntimeCommand(options: PrepareRuntimeCommandOptions): Promise; +//# sourceMappingURL=prepare-runtime.d.ts.map \ No newline at end of file diff --git a/packages/interfacectl-cli/dist/commands/prepare-runtime.d.ts.map b/packages/interfacectl-cli/dist/commands/prepare-runtime.d.ts.map new file mode 100644 index 0000000..f121f9b --- /dev/null +++ b/packages/interfacectl-cli/dist/commands/prepare-runtime.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"prepare-runtime.d.ts","sourceRoot":"","sources":["../../src/commands/prepare-runtime.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,UAAU,EACf,KAAK,2BAA2B,EACjC,MAAM,sBAAsB,CAAC;AAG9B,MAAM,WAAW,4BAA4B;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAsFD,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAjDhD,MAAM;mBAAS,MAAM;oBAAU,MAAM;;;;;;EA4FnE;AAED,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,GAAG,SAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAjGU,MAAM;mBAAS,MAAM;oBAAU,MAAM;;;;;;EAqGnE;AAgBD,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,MAAM,CAAC,CA+BjB"} \ No newline at end of file diff --git a/packages/interfacectl-cli/dist/commands/prepare-runtime.js b/packages/interfacectl-cli/dist/commands/prepare-runtime.js new file mode 100644 index 0000000..746a345 --- /dev/null +++ b/packages/interfacectl-cli/dist/commands/prepare-runtime.js @@ -0,0 +1,157 @@ +import fs from "node:fs"; +import path from "node:path"; +import { AdapterInputError, isRecord, loadCompiledSurfaceBundle, } from "../adapter/bundle.js"; +import { stringifyDeterministicJson } from "../utils/deterministic-json.js"; +function asRecord(value) { + return isRecord(value) ? value : {}; +} +function asString(value) { + return typeof value === "string" && value.trim().length > 0 ? value : undefined; +} +function asStringArray(value) { + if (!Array.isArray(value)) + return []; + return value + .map((entry) => (typeof entry === "string" ? entry.trim() : "")) + .filter(Boolean); +} +function buildSummary(bundle) { + if (!bundle.surface.runtime) { + throw new AdapterInputError(`Bundle for surface "${bundle.surface.id}" does not include runtime.json.`, { code: "adapter.bundle.runtime-missing" }); + } + const runtimeDoc = asRecord(bundle.surface.runtime.value); + const runtime = asRecord(runtimeDoc.runtime); + const structure = asRecord(runtime.structure); + const mutationEnvelope = asRecord(runtime.mutationEnvelope); + const policySeverities = asRecord(runtime.policySeverities); + const contexts = Array.isArray(runtime.contexts) ? runtime.contexts : []; + const requiredSectionIds = asStringArray(structure.requiredSections); + const mutationMode = asString(mutationEnvelope.mode) ?? "content-only"; + const strictCategories = Object.entries(policySeverities) + .filter(([, policy]) => policy === "strict") + .map(([category]) => category); + const checklist = []; + if (requiredSectionIds.length > 0) { + checklist.push({ + id: "required-sections", + label: "Preserve required sections", + detail: `Runtime must keep sections: ${requiredSectionIds.join(", ")}.`, + }); + } + checklist.push({ + id: "mutation-envelope", + label: "Stay inside the mutation envelope", + detail: `Allowed runtime mutation mode: ${mutationMode}.`, + }); + if (strictCategories.length > 0) { + checklist.push({ + id: "strict-categories", + label: "Honor strict enforcement categories", + detail: `Strict categories: ${strictCategories.join(", ")}.`, + }); + } + if (contexts.length > 0) { + checklist.push({ + id: "contexts", + label: "Apply contextual runtime rules", + detail: `Context rules: ${contexts.map((context) => asString(asRecord(context).id) ?? "unknown").join(", ")}.`, + }); + } + const textParts = [ + requiredSectionIds.length > 0 ? `preserve required sections ${requiredSectionIds.join(", ")}` : undefined, + `stay within ${mutationMode} mutation scope`, + strictCategories.length > 0 ? `treat ${strictCategories.join(", ")} as strict runtime categories` : undefined, + contexts.length > 0 ? `evaluate ${contexts.length} contextual runtime rules` : undefined, + ].filter((value) => Boolean(value)); + return { + text: textParts.length > 0 + ? `${textParts.join("; ")}.` + : "Use the prepared runtime bundle as the authoritative enforcement payload.", + requiredSectionIds, + mutationMode, + strictCategories, + contextIds: contexts + .map((context) => asString(asRecord(context).id)) + .filter((value) => Boolean(value)), + checklist, + }; +} +export function buildPreparedRuntimePayload(bundle) { + if (!bundle.surface.runtime) { + throw new AdapterInputError(`Bundle for surface "${bundle.surface.id}" does not include runtime.json.`, { code: "adapter.bundle.runtime-missing" }); + } + const runtimeDoc = asRecord(bundle.surface.runtime.value); + const identity = asRecord(runtimeDoc.identity); + const generation = asRecord(bundle.surface.generation.value); + const generationRefs = asRecord(generation.refs); + return { + surface: { + surfaceId: asString(identity.surfaceId) ?? bundle.surface.id, + displayName: asString(identity.displayName) ?? bundle.surface.id, + type: asString(identity.type) ?? "unknown", + }, + bundle: { + root: bundle.root, + version: bundle.version, + manifestPath: bundle.manifest.path, + sourcePaths: { + contract: bundle.contract.path, + runtime: bundle.surface.runtime.path, + generation: bundle.surface.generation.path, + sections: bundle.surface.sections.path, + components: bundle.surface.components.path, + constraints: bundle.surface.constraints.path, + repairMap: bundle.surface.repairMap.path, + }, + }, + contract: { + id: bundle.contractId, + version: bundle.contractVersion, + normalizedPath: bundle.contract.path, + }, + summary: buildSummary(bundle), + governance: asRecord(runtimeDoc.governance), + runtime: asRecord(runtimeDoc.runtime), + evidenceRefs: Array.isArray(generationRefs.evidence) ? generationRefs.evidence : [], + }; +} +export function loadPreparedRuntimePayload(bundleRoot, surfaceId, cwd = process.cwd()) { + const bundle = loadCompiledSurfaceBundle(bundleRoot, surfaceId, cwd); + return buildPreparedRuntimePayload(bundle); +} +function writeError(error, code) { + process.stderr.write(`${JSON.stringify({ + status: "error", + code, + error: error.message, + }, null, 2)}\n`); +} +export async function runPrepareRuntimeCommand(options) { + try { + if (!options.bundleRoot) { + throw new AdapterInputError("--bundle-root is required."); + } + if (!options.surfaceId) { + throw new AdapterInputError("--surface is required."); + } + const payload = loadPreparedRuntimePayload(options.bundleRoot, options.surfaceId, process.cwd()); + const serialized = stringifyDeterministicJson(payload); + if (options.outPath) { + const outPath = path.resolve(options.outPath); + fs.mkdirSync(path.dirname(outPath), { recursive: true }); + fs.writeFileSync(outPath, serialized, "utf8"); + return 0; + } + process.stdout.write(serialized); + return 0; + } + catch (error) { + if (error instanceof AdapterInputError) { + writeError(error, error.code); + return 10; + } + const internalError = error instanceof Error ? error : new Error(String(error)); + writeError(internalError, "prepare-runtime.internal"); + return 1; + } +} diff --git a/packages/interfacectl-cli/dist/index.js b/packages/interfacectl-cli/dist/index.js index 8fd2efc..f261c97 100755 --- a/packages/interfacectl-cli/dist/index.js +++ b/packages/interfacectl-cli/dist/index.js @@ -10,6 +10,7 @@ import { runMigrateColorPolicyCommand } from "./commands/migrate-color-policy.js import { runValidateExtractedCommand } from "./commands/validate-extracted.js"; import { runDescribeCommand } from "./commands/describe.js"; import { runPrepareGenerationCommand } from "./commands/prepare-generation.js"; +import { runPrepareRuntimeCommand } from "./commands/prepare-runtime.js"; import { runValidateGenerationCommand } from "./commands/validate-generation.js"; import { runServeGenerationAdapterCommand } from "./commands/serve-generation-adapter.js"; import { runEmitRunArtifactCommand } from "./commands/emit-run-artifact.js"; @@ -207,6 +208,19 @@ program outPath: options.out, }); }); +program + .command("prepare-runtime") + .description("Resolve a compiled runtime bundle into one adapter-ready JSON payload") + .requiredOption("--bundle-root ", "Path to the compiled generation bundle directory") + .requiredOption("--surface ", "Surface identifier") + .option("--out ", "Write the prepared JSON payload to the provided file") + .action(async (options) => { + process.exitCode = await runPrepareRuntimeCommand({ + bundleRoot: options.bundleRoot, + surfaceId: options.surface, + outPath: options.out, + }); +}); program .command("init-generation-session") .description("Freeze one compiled bundle revision into a tracked local generation session") diff --git a/packages/interfacectl-cli/schemas/prepare-generation-output.schema.json b/packages/interfacectl-cli/schemas/prepare-generation-output.schema.json index c228024..c09213b 100644 --- a/packages/interfacectl-cli/schemas/prepare-generation-output.schema.json +++ b/packages/interfacectl-cli/schemas/prepare-generation-output.schema.json @@ -89,6 +89,10 @@ "type": "string", "minLength": 1 }, + "runtime": { + "type": "string", + "minLength": 1 + }, "authoring": { "type": "string", "minLength": 1 @@ -206,7 +210,7 @@ "generation": { "type": "object", "additionalProperties": false, - "required": ["boundary", "structure", "layout", "visual", "guidance"], + "required": ["boundary", "structure", "layout", "visual", "governance", "adaptation", "guidance"], "properties": { "boundary": { "type": "object" @@ -220,6 +224,12 @@ "visual": { "type": "object" }, + "governance": { + "type": "object" + }, + "adaptation": { + "type": "object" + }, "guidance": { "type": "object" } @@ -246,6 +256,9 @@ "type": "object" } }, + "runtime": { + "type": "object" + }, "authoring": { "type": "object" }, diff --git a/packages/interfacectl-cli/schemas/prepare-runtime-output.schema.json b/packages/interfacectl-cli/schemas/prepare-runtime-output.schema.json new file mode 100644 index 0000000..530e9e3 --- /dev/null +++ b/packages/interfacectl-cli/schemas/prepare-runtime-output.schema.json @@ -0,0 +1,195 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://surfaces.dev/schemas/prepare-runtime-output.schema.json", + "title": "PrepareRuntimeOutput", + "type": "object", + "additionalProperties": false, + "required": [ + "surface", + "bundle", + "contract", + "summary", + "governance", + "runtime", + "evidenceRefs" + ], + "properties": { + "surface": { + "type": "object", + "additionalProperties": false, + "required": ["surfaceId", "displayName", "type"], + "properties": { + "surfaceId": { + "type": "string", + "minLength": 1 + }, + "displayName": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "minLength": 1 + } + } + }, + "bundle": { + "type": "object", + "additionalProperties": false, + "required": ["root", "version", "manifestPath", "sourcePaths"], + "properties": { + "root": { + "type": "string", + "minLength": 1 + }, + "version": { + "type": "string", + "minLength": 1 + }, + "manifestPath": { + "type": "string", + "minLength": 1 + }, + "sourcePaths": { + "type": "object", + "additionalProperties": false, + "required": [ + "contract", + "runtime", + "generation", + "sections", + "components", + "constraints", + "repairMap" + ], + "properties": { + "contract": { + "type": "string", + "minLength": 1 + }, + "runtime": { + "type": "string", + "minLength": 1 + }, + "generation": { + "type": "string", + "minLength": 1 + }, + "sections": { + "type": "string", + "minLength": 1 + }, + "components": { + "type": "string", + "minLength": 1 + }, + "constraints": { + "type": "string", + "minLength": 1 + }, + "repairMap": { + "type": "string", + "minLength": 1 + } + } + } + } + }, + "contract": { + "type": "object", + "additionalProperties": false, + "required": ["id", "version", "normalizedPath"], + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "version": { + "type": "string", + "minLength": 1 + }, + "normalizedPath": { + "type": "string", + "minLength": 1 + } + } + }, + "summary": { + "type": "object", + "additionalProperties": false, + "required": [ + "text", + "requiredSectionIds", + "mutationMode", + "strictCategories", + "contextIds", + "checklist" + ], + "properties": { + "text": { + "type": "string", + "minLength": 1 + }, + "requiredSectionIds": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + } + }, + "mutationMode": { + "type": "string", + "minLength": 1 + }, + "strictCategories": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + } + }, + "contextIds": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + } + }, + "checklist": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["id", "label", "detail"], + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "label": { + "type": "string", + "minLength": 1 + }, + "detail": { + "type": "string", + "minLength": 1 + } + } + } + } + } + }, + "governance": { + "type": "object" + }, + "runtime": { + "type": "object" + }, + "evidenceRefs": { + "type": "array", + "items": { + "type": "object" + } + } + } +} diff --git a/packages/interfacectl-cli/src/adapter/bundle.ts b/packages/interfacectl-cli/src/adapter/bundle.ts index 8e2dd3d..6782304 100644 --- a/packages/interfacectl-cli/src/adapter/bundle.ts +++ b/packages/interfacectl-cli/src/adapter/bundle.ts @@ -33,6 +33,7 @@ export interface LoadedCompiledSurfaceBundle { components: LoadedJsonFile; constraints: LoadedJsonFile; repairMap: LoadedJsonFile; + runtime?: LoadedJsonFile; authoring?: LoadedJsonFile; }; } @@ -176,6 +177,19 @@ export function loadCompiledSurfaceBundle( }; } + let runtime: LoadedJsonFile | undefined; + const runtimeRef = + typeof refs.runtime === "string" && refs.runtime.trim().length > 0 + ? refs.runtime + : "./runtime.json"; + const runtimePath = path.resolve(path.dirname(generationPath), runtimeRef); + if (fs.existsSync(runtimePath) && fs.statSync(runtimePath).isFile()) { + runtime = { + path: runtimePath, + value: readJsonFile(runtimePath, "Runtime bundle"), + }; + } + const generationProvenance = isRecord(generation.value.provenance) ? generation.value.provenance : undefined; @@ -210,6 +224,7 @@ export function loadCompiledSurfaceBundle( components, constraints, repairMap, + ...(runtime ? { runtime } : {}), ...(authoring ? { authoring } : {}), }, }; diff --git a/packages/interfacectl-cli/src/commands/compile.ts b/packages/interfacectl-cli/src/commands/compile.ts index 1360c8f..3568eb8 100644 --- a/packages/interfacectl-cli/src/commands/compile.ts +++ b/packages/interfacectl-cli/src/commands/compile.ts @@ -136,6 +136,159 @@ function uniqueStrings(values: Array): string[] { return result; } +type PolicyLevel = "off" | "warn" | "strict"; + +function getPolicyRank(policy: PolicyLevel | undefined): number { + switch (policy) { + case "strict": + return 2; + case "warn": + return 1; + case "off": + default: + return 0; + } +} + +function maxPolicy(...policies: Array): PolicyLevel { + let strongest: PolicyLevel = "off"; + for (const policy of policies) { + if (getPolicyRank(policy) > getPolicyRank(strongest)) { + strongest = policy ?? "off"; + } + } + return strongest; +} + +function buildGovernancePayload(surface: ContractSurface) { + return { + owner: surface.owner ?? null, + domain: surface.domain ?? null, + phase0: surface.phase0 ?? null, + status: surface.governance?.status ?? "draft", + roles: surface.governance?.roles ?? {}, + approvals: surface.governance?.approvals ?? [], + }; +} + +function inferMutationMode(surface: ContractSurface, sections: ContractSection[]) { + const explicitMode = surface.runtime?.mutationEnvelope?.mode; + if (explicitMode) return explicitMode; + + const sectionModes = uniqueStrings( + sections.map((section) => section.editPolicy?.mode), + ); + const allowedOperations = uniqueStrings( + sections.flatMap((section) => section.editPolicy?.allowedOperations ?? []), + ); + + if (sectionModes.length > 0 && sectionModes.every((mode) => mode === "locked")) { + return "locked"; + } + if (sectionModes.includes("freeform")) { + return "freeform"; + } + if (allowedOperations.includes("adjust-layout")) { + return "layout-tuning"; + } + if (sectionModes.includes("slot-bound")) { + return "slot-bound"; + } + return "content-only"; +} + +function inferMutationScopes(actions: string[]) { + return uniqueStrings( + actions.flatMap((action) => { + switch (action) { + case "update-copy": + case "change-media": + case "bind-data": + return ["content"]; + case "swap-variant": + case "swap-component": + return ["components"]; + case "adjust-layout": + return ["layout"]; + case "add-section": + case "remove-section": + case "reorder-sections": + return ["sections"]; + case "wire-interaction": + return ["interactions"]; + case "reorder-items": + return ["content", "layout"]; + default: + return []; + } + }), + ); +} + +function buildMutationEnvelope( + surface: ContractSurface, + sections: ContractSection[], +) { + const explicit = surface.runtime?.mutationEnvelope; + const inferredActions = uniqueStrings( + sections.flatMap((section) => section.editPolicy?.allowedOperations ?? []), + ); + const allowedActions = uniqueStrings([ + ...(explicit?.allowedActions ?? []), + ...(explicit?.allowedActions ? [] : inferredActions), + inferredActions.length === 0 ? "update-copy" : undefined, + ]); + const scopes = uniqueStrings([ + ...(explicit?.scopes ?? []), + ...(explicit?.scopes ? [] : inferMutationScopes(allowedActions)), + ]); + + return { + mode: inferMutationMode(surface, sections), + ...(scopes.length > 0 ? { scopes } : {}), + ...(allowedActions.length > 0 ? { allowedActions } : {}), + ...(explicit?.allowedSections ? { allowedSections: explicit.allowedSections } : {}), + ...(explicit?.prohibitedSections ? { prohibitedSections: explicit.prohibitedSections } : {}), + }; +} + +function buildPolicySeverities( + contract: InterfaceContract, + surface: ContractSurface, +): Record { + const boundary = surface.mustNotEmit?.length || contract.shell?.owns?.length + ? "strict" + : "off"; + const structure = maxPolicy( + surface.requiredSections.length > 0 ? "strict" : "off", + surface.layout.landingPattern?.policy, + ); + const layout = maxPolicy( + surface.layout.pageFrame?.enforcement, + surface.layout.chromePolicy?.policy, + surface.layout.landingPattern?.policy, + surface.layout.landingPattern?.marketingLayoutPolicy, + ); + const visual = maxPolicy( + contract.color.policy, + surface.icons?.policy, + contract.tokens?.typography?.policy, + contract.tokens?.motion?.policy, + surface.marketingTypographyPolicy, + ); + const interaction = surface.flows?.policy ?? "off"; + const runtime = maxPolicy(surface.runtime?.policy, boundary, structure, layout, visual, interaction); + + return { + boundary, + structure, + layout, + visual, + interaction, + runtime, + }; +} + function collectComponentIdsFromSlots(slots: ContractSlot[] | undefined): string[] { if (!slots) return []; return slots.flatMap((slot) => slot.acceptsComponents ?? []); @@ -399,6 +552,12 @@ function buildGenerationPayload( const requiredSections = surface.requiredSections; const landingPattern = surface.layout.landingPattern; const observationRefs = buildObservationRefs(contract); + const governance = buildGovernancePayload(surface); + const adaptation = { + policy: surface.runtime?.policy ?? buildPolicySeverities(contract, surface).runtime, + mutationEnvelope: buildMutationEnvelope(surface, sections), + contextIds: (surface.runtime?.contexts ?? []).map((context) => context.id), + }; return { identity: { @@ -456,6 +615,8 @@ function buildGenerationPayload( tokenPolicyCategories: Object.keys(contract.tokens ?? {}), }, }, + governance, + adaptation, guidance: buildGuidance(contract, surface, sections), refs: { contract: "../../contract/normalized.json", @@ -464,6 +625,7 @@ function buildGenerationPayload( constraints: "./constraints.json", ...(surface.authoring ? { authoring: "./authoring.json" } : {}), repairMap: "./repair-map.json", + runtime: "./runtime.json", ...(observationRefs.length > 0 ? { evidence: observationRefs } : {}), }, }; @@ -659,6 +821,66 @@ function buildRepairMapPayload( }; } +function buildRuntimePayload( + contract: InterfaceContract, + surface: ContractSurface, + sections: ContractSection[], + components: ContractComponent[], +) { + const policySeverities = buildPolicySeverities(contract, surface); + const mutationEnvelope = buildMutationEnvelope(surface, sections); + + return { + provenance: makeBundleProvenance(contract, surface.id), + identity: { + surfaceId: surface.id, + displayName: surface.displayName, + type: surface.type, + }, + governance: buildGovernancePayload(surface), + runtime: { + policy: surface.runtime?.policy ?? policySeverities.runtime, + policySeverities, + mutationEnvelope, + contexts: surface.runtime?.contexts ?? [], + boundary: { + shellOwns: contract.shell?.owns ?? [], + contentSlot: contract.shell?.contentSlot ?? null, + mustNotEmit: surface.mustNotEmit ?? [], + allowSources: surface.shellOwnedPrimitiveAllowSources ?? [], + }, + structure: { + requiredSections: surface.requiredSections, + allowedSections: sections.map((section) => section.id), + allowedComponents: components.map((component) => component.id), + }, + layout: { + maxContentWidth: surface.layout.maxContentWidth, + requiredContainers: surface.layout.requiredContainers ?? [], + ...(surface.layout.pageFrame ? { pageFrame: surface.layout.pageFrame } : {}), + ...(surface.layout.chromePolicy ? { chromePolicy: surface.layout.chromePolicy } : {}), + ...(surface.layout.landingPattern ? { landingPattern: surface.layout.landingPattern } : {}), + ...(surface.viewports ? { viewports: surface.viewports } : {}), + }, + visual: { + allowedFonts: surface.allowedFonts, + color: contract.color, + motion: contract.constraints.motion, + ...(contract.tokens ? { tokens: contract.tokens } : {}), + ...(surface.icons ? { icons: surface.icons } : {}), + }, + ...(surface.flows ? { interaction: { flows: surface.flows } } : {}), + }, + refs: { + contract: "../../contract/normalized.json", + sections: "./sections.json", + components: "./components.json", + constraints: "./constraints.json", + repairMap: "./repair-map.json", + }, + }; +} + function buildSurfaceBundleFiles( contract: InterfaceContract, surface: ContractSurface, @@ -672,6 +894,7 @@ function buildSurfaceBundleFiles( const componentsPayload = buildComponentsPayload(contract, surface, components); const repairMapPayload = buildRepairMapPayload(contract, surface, sections); const authoringPayload = buildAuthoringPayload(contract, surface); + const runtimePayload = buildRuntimePayload(contract, surface, sections, components); const files: BundleFile[] = [ { @@ -694,6 +917,10 @@ function buildSurfaceBundleFiles( path: `${surfaceDir}/repair-map.json`, content: stringifyDeterministic(repairMapPayload), }, + { + path: `${surfaceDir}/runtime.json`, + content: stringifyDeterministic(runtimePayload), + }, ]; if (authoringPayload) { diff --git a/packages/interfacectl-cli/src/commands/prepare-generation.ts b/packages/interfacectl-cli/src/commands/prepare-generation.ts index 8a0940b..1be72b7 100644 --- a/packages/interfacectl-cli/src/commands/prepare-generation.ts +++ b/packages/interfacectl-cli/src/commands/prepare-generation.ts @@ -80,6 +80,8 @@ function buildSummary(bundle: LoadedCompiledSurfaceBundle) { const generation = asRecord(bundle.surface.generation.value); const boundary = asRecord(generation.boundary); const structure = asRecord(generation.structure); + const adaptation = asRecord(generation.adaptation); + const mutationEnvelope = asRecord(adaptation.mutationEnvelope); const guidance = asRecord(generation.guidance); const repairs = getRepairSummary(bundle.surface.repairMap.value); @@ -113,6 +115,14 @@ function buildSummary(bundle: LoadedCompiledSurfaceBundle) { detail: `Focus order: ${focusOrder.join(" -> ")}.`, }); } + const mutationMode = asString(mutationEnvelope.mode); + if (mutationMode) { + checklist.push({ + id: "mutation-envelope", + label: "Stay within the mutation envelope", + detail: `Allowed mutation mode: ${mutationMode}.`, + }); + } if (repairs.length > 0) { checklist.push({ id: "repair-priorities", @@ -134,6 +144,9 @@ function buildSummary(bundle: LoadedCompiledSurfaceBundle) { if (prohibitedRoles.length > 0) { textParts.push(`avoid shell-owned roles ${prohibitedRoles.join(", ")}`); } + if (mutationMode) { + textParts.push(`stay within ${mutationMode} mutation scope`); + } if (repairs.length > 0) { textParts.push(`prioritize repairs ${repairs.slice(0, 3).map((repair) => repair.code).join(", ")}`); } @@ -156,6 +169,9 @@ export function buildPreparedGenerationPayload(bundle: LoadedCompiledSurfaceBund const componentsDoc = asRecord(bundle.surface.components.value); const constraintsDoc = asRecord(bundle.surface.constraints.value); const repairMapDoc = asRecord(bundle.surface.repairMap.value); + const runtimeDoc = bundle.surface.runtime + ? asRecord(bundle.surface.runtime.value) + : undefined; const authoringDoc = bundle.surface.authoring ? asRecord(bundle.surface.authoring.value) : undefined; @@ -177,6 +193,7 @@ export function buildPreparedGenerationPayload(bundle: LoadedCompiledSurfaceBund components: bundle.surface.components.path, constraints: bundle.surface.constraints.path, repairMap: bundle.surface.repairMap.path, + ...(bundle.surface.runtime ? { runtime: bundle.surface.runtime.path } : {}), ...(bundle.surface.authoring ? { authoring: bundle.surface.authoring.path } : {}), }, }, @@ -191,12 +208,17 @@ export function buildPreparedGenerationPayload(bundle: LoadedCompiledSurfaceBund structure: asRecord(generation.structure), layout: asRecord(generation.layout), visual: asRecord(generation.visual), + governance: asRecord(generation.governance), + adaptation: asRecord(generation.adaptation), guidance: asRecord(generation.guidance), }, sections: Array.isArray(sectionsDoc.sections) ? sectionsDoc.sections : [], components: Array.isArray(componentsDoc.components) ? componentsDoc.components : [], constraints: asRecord(constraintsDoc.constraints), repairMap: Array.isArray(repairMapDoc.repairs) ? repairMapDoc.repairs : [], + ...(runtimeDoc && isRecord(runtimeDoc.runtime) + ? { runtime: runtimeDoc.runtime } + : {}), ...(authoringDoc && isRecord(authoringDoc.authoring) ? { authoring: authoringDoc.authoring } : {}), diff --git a/packages/interfacectl-cli/src/commands/prepare-runtime.ts b/packages/interfacectl-cli/src/commands/prepare-runtime.ts new file mode 100644 index 0000000..d4a7a39 --- /dev/null +++ b/packages/interfacectl-cli/src/commands/prepare-runtime.ts @@ -0,0 +1,203 @@ +import fs from "node:fs"; +import path from "node:path"; +import { + AdapterInputError, + isRecord, + loadCompiledSurfaceBundle, + type JsonRecord, + type LoadedCompiledSurfaceBundle, +} from "../adapter/bundle.js"; +import { stringifyDeterministicJson } from "../utils/deterministic-json.js"; + +export interface PrepareRuntimeCommandOptions { + bundleRoot?: string; + surfaceId?: string; + outPath?: string; +} + +function asRecord(value: unknown): JsonRecord { + return isRecord(value) ? value : {}; +} + +function asString(value: unknown): string | undefined { + return typeof value === "string" && value.trim().length > 0 ? value : undefined; +} + +function asStringArray(value: unknown): string[] { + if (!Array.isArray(value)) return []; + return value + .map((entry) => (typeof entry === "string" ? entry.trim() : "")) + .filter(Boolean); +} + +function buildSummary(bundle: LoadedCompiledSurfaceBundle) { + if (!bundle.surface.runtime) { + throw new AdapterInputError( + `Bundle for surface "${bundle.surface.id}" does not include runtime.json.`, + { code: "adapter.bundle.runtime-missing" }, + ); + } + + const runtimeDoc = asRecord(bundle.surface.runtime.value); + const runtime = asRecord(runtimeDoc.runtime); + const structure = asRecord(runtime.structure); + const mutationEnvelope = asRecord(runtime.mutationEnvelope); + const policySeverities = asRecord(runtime.policySeverities); + const contexts = Array.isArray(runtime.contexts) ? runtime.contexts : []; + const requiredSectionIds = asStringArray(structure.requiredSections); + const mutationMode = asString(mutationEnvelope.mode) ?? "content-only"; + const strictCategories = Object.entries(policySeverities) + .filter(([, policy]) => policy === "strict") + .map(([category]) => category); + + const checklist: Array<{ id: string; label: string; detail: string }> = []; + if (requiredSectionIds.length > 0) { + checklist.push({ + id: "required-sections", + label: "Preserve required sections", + detail: `Runtime must keep sections: ${requiredSectionIds.join(", ")}.`, + }); + } + checklist.push({ + id: "mutation-envelope", + label: "Stay inside the mutation envelope", + detail: `Allowed runtime mutation mode: ${mutationMode}.`, + }); + if (strictCategories.length > 0) { + checklist.push({ + id: "strict-categories", + label: "Honor strict enforcement categories", + detail: `Strict categories: ${strictCategories.join(", ")}.`, + }); + } + if (contexts.length > 0) { + checklist.push({ + id: "contexts", + label: "Apply contextual runtime rules", + detail: `Context rules: ${contexts.map((context) => asString(asRecord(context).id) ?? "unknown").join(", ")}.`, + }); + } + + const textParts = [ + requiredSectionIds.length > 0 ? `preserve required sections ${requiredSectionIds.join(", ")}` : undefined, + `stay within ${mutationMode} mutation scope`, + strictCategories.length > 0 ? `treat ${strictCategories.join(", ")} as strict runtime categories` : undefined, + contexts.length > 0 ? `evaluate ${contexts.length} contextual runtime rules` : undefined, + ].filter((value): value is string => Boolean(value)); + + return { + text: textParts.length > 0 + ? `${textParts.join("; ")}.` + : "Use the prepared runtime bundle as the authoritative enforcement payload.", + requiredSectionIds, + mutationMode, + strictCategories, + contextIds: contexts + .map((context) => asString(asRecord(context).id)) + .filter((value): value is string => Boolean(value)), + checklist, + }; +} + +export function buildPreparedRuntimePayload(bundle: LoadedCompiledSurfaceBundle) { + if (!bundle.surface.runtime) { + throw new AdapterInputError( + `Bundle for surface "${bundle.surface.id}" does not include runtime.json.`, + { code: "adapter.bundle.runtime-missing" }, + ); + } + + const runtimeDoc = asRecord(bundle.surface.runtime.value); + const identity = asRecord(runtimeDoc.identity); + const generation = asRecord(bundle.surface.generation.value); + const generationRefs = asRecord(generation.refs); + + return { + surface: { + surfaceId: asString(identity.surfaceId) ?? bundle.surface.id, + displayName: asString(identity.displayName) ?? bundle.surface.id, + type: asString(identity.type) ?? "unknown", + }, + bundle: { + root: bundle.root, + version: bundle.version, + manifestPath: bundle.manifest.path, + sourcePaths: { + contract: bundle.contract.path, + runtime: bundle.surface.runtime.path, + generation: bundle.surface.generation.path, + sections: bundle.surface.sections.path, + components: bundle.surface.components.path, + constraints: bundle.surface.constraints.path, + repairMap: bundle.surface.repairMap.path, + }, + }, + contract: { + id: bundle.contractId, + version: bundle.contractVersion, + normalizedPath: bundle.contract.path, + }, + summary: buildSummary(bundle), + governance: asRecord(runtimeDoc.governance), + runtime: asRecord(runtimeDoc.runtime), + evidenceRefs: Array.isArray(generationRefs.evidence) ? generationRefs.evidence : [], + }; +} + +export function loadPreparedRuntimePayload( + bundleRoot: string, + surfaceId: string, + cwd = process.cwd(), +) { + const bundle = loadCompiledSurfaceBundle(bundleRoot, surfaceId, cwd); + return buildPreparedRuntimePayload(bundle); +} + +function writeError(error: AdapterInputError | Error, code: string) { + process.stderr.write( + `${JSON.stringify( + { + status: "error", + code, + error: error.message, + }, + null, + 2, + )}\n`, + ); +} + +export async function runPrepareRuntimeCommand( + options: PrepareRuntimeCommandOptions, +): Promise { + try { + if (!options.bundleRoot) { + throw new AdapterInputError("--bundle-root is required."); + } + if (!options.surfaceId) { + throw new AdapterInputError("--surface is required."); + } + + const payload = loadPreparedRuntimePayload(options.bundleRoot, options.surfaceId, process.cwd()); + const serialized = stringifyDeterministicJson(payload); + + if (options.outPath) { + const outPath = path.resolve(options.outPath); + fs.mkdirSync(path.dirname(outPath), { recursive: true }); + fs.writeFileSync(outPath, serialized, "utf8"); + return 0; + } + + process.stdout.write(serialized); + return 0; + } catch (error) { + if (error instanceof AdapterInputError) { + writeError(error, error.code); + return 10; + } + + const internalError = error instanceof Error ? error : new Error(String(error)); + writeError(internalError, "prepare-runtime.internal"); + return 1; + } +} diff --git a/packages/interfacectl-cli/src/index.ts b/packages/interfacectl-cli/src/index.ts index f974463..8625d69 100644 --- a/packages/interfacectl-cli/src/index.ts +++ b/packages/interfacectl-cli/src/index.ts @@ -11,6 +11,7 @@ import { runMigrateColorPolicyCommand } from "./commands/migrate-color-policy.js import { runValidateExtractedCommand } from "./commands/validate-extracted.js"; import { runDescribeCommand } from "./commands/describe.js"; import { runPrepareGenerationCommand } from "./commands/prepare-generation.js"; +import { runPrepareRuntimeCommand } from "./commands/prepare-runtime.js"; import { runValidateGenerationCommand } from "./commands/validate-generation.js"; import { runServeGenerationAdapterCommand } from "./commands/serve-generation-adapter.js"; import { runEmitRunArtifactCommand } from "./commands/emit-run-artifact.js"; @@ -315,6 +316,20 @@ program }); }); +program + .command("prepare-runtime") + .description("Resolve a compiled runtime bundle into one adapter-ready JSON payload") + .requiredOption("--bundle-root ", "Path to the compiled generation bundle directory") + .requiredOption("--surface ", "Surface identifier") + .option("--out ", "Write the prepared JSON payload to the provided file") + .action(async (options) => { + process.exitCode = await runPrepareRuntimeCommand({ + bundleRoot: options.bundleRoot, + surfaceId: options.surface, + outPath: options.out, + }); + }); + program .command("init-generation-session") .description("Freeze one compiled bundle revision into a tracked local generation session") diff --git a/packages/interfacectl-cli/test/compile.test.mjs b/packages/interfacectl-cli/test/compile.test.mjs index cda0591..fff48d5 100644 --- a/packages/interfacectl-cli/test/compile.test.mjs +++ b/packages/interfacectl-cli/test/compile.test.mjs @@ -71,7 +71,7 @@ test("compile: structure - required files exist and no extra files", async () => assert.equal(manifest.contractId, "demo-ui"); assert.equal(manifest.contractVersion, "1.0.0"); assert.ok(Array.isArray(manifest.files)); - assert.ok(manifest.files.length >= 6); + assert.ok(manifest.files.length >= 7); const paths = manifest.files.map((f) => f.path); assert.ok(paths.includes("contract/normalized.json"), "bundle must include contract/normalized.json"); @@ -80,6 +80,7 @@ test("compile: structure - required files exist and no extra files", async () => assert.ok(paths.includes("surfaces/demo-surface/components.json"), "bundle must include components.json"); assert.ok(paths.includes("surfaces/demo-surface/constraints.json"), "bundle must include constraints.json"); assert.ok(paths.includes("surfaces/demo-surface/repair-map.json"), "bundle must include repair-map.json"); + assert.ok(paths.includes("surfaces/demo-surface/runtime.json"), "bundle must include runtime.json"); assert.ok(!paths.includes("surfaces/demo-surface/authoring.json"), "authoring.json should be omitted when authoring is absent"); for (const entry of manifest.files) { @@ -100,7 +101,7 @@ test("compile: structure - required files exist and no extra files", async () => const surfaceFiles = await readdir(surfaceDir); assert.deepEqual( surfaceFiles.sort(), - ["components.json", "constraints.json", "generation.json", "repair-map.json", "sections.json"], + ["components.json", "constraints.json", "generation.json", "repair-map.json", "runtime.json", "sections.json"], "surface bundle should only include the expected generation files for the base fixture", ); } finally { @@ -141,7 +142,7 @@ test("compile: golden - generated generation bundle files match expected", async const generatedContract = await readJson(path.join(outDir, "contract", "normalized.json")); assert.deepEqual(generatedContract, expectedContract, "contract/normalized.json must match expected"); - for (const filename of ["generation.json", "sections.json", "components.json", "constraints.json", "repair-map.json"]) { + for (const filename of ["generation.json", "sections.json", "components.json", "constraints.json", "repair-map.json", "runtime.json"]) { const expected = await readJson(path.join(expectedDir, "surfaces", "demo-surface", filename)); const generated = await readJson(path.join(outDir, "surfaces", "demo-surface", filename)); assert.deepEqual(generated, expected, `${filename} must match expected`); @@ -172,6 +173,7 @@ test("compile: includes component catalog refs, authoring hints, and observation type: "web", requiredSections: ["main.hero"], allowedFonts: ["Inter", "var(--font-body)"], + owner: "designers@example.com", marketingTypographyProfile: "marketing-core", marketingTypographyPolicy: "warn", layout: { @@ -186,6 +188,40 @@ test("compile: includes component catalog refs, authoring hints, and observation marketingLayoutPolicy: "warn", }, }, + governance: { + status: "review", + roles: { + designers: ["designers@example.com"], + engineers: ["eng@example.com"], + approvers: ["design-approver@example.com"], + }, + approvals: [ + { + role: "designer", + owner: "design-approver@example.com", + status: "approved", + timestamp: "2026-03-16T09:00:00Z", + }, + ], + }, + runtime: { + policy: "strict", + mutationEnvelope: { + mode: "slot-bound", + scopes: ["content", "components"], + allowedActions: ["update-copy", "swap-variant"], + allowedSections: ["main.hero", "main.cta"], + }, + contexts: [ + { + id: "pricing-campaign", + when: "route == '/pricing'", + policy: "warn", + requiredSections: ["main.cta"], + allowedLayoutIntents: ["columns"], + }, + ], + }, authoring: { framework: "next", routing: "app-router", @@ -312,9 +348,21 @@ test("compile: includes component catalog refs, authoring hints, and observation const generation = await readJson(path.join(outDir, "surfaces", "demo-surface", "generation.json")); assert.equal(generation.refs.authoring, "./authoring.json"); + assert.equal(generation.refs.runtime, "./runtime.json"); assert.deepEqual(generation.refs.evidence, [{ kind: "contract-field", path: "/x_extracted" }]); assert.equal("x_extracted" in generation, false, "generation payload must not inline x_extracted evidence"); assert.equal("observations" in generation, false, "generation payload must not inline observation evidence"); + assert.equal(generation.governance.owner, "designers@example.com"); + assert.equal(generation.governance.status, "review"); + assert.equal(generation.adaptation.mutationEnvelope.mode, "slot-bound"); + assert.deepEqual(generation.adaptation.contextIds, ["pricing-campaign"]); + + const runtime = await readJson(path.join(outDir, "surfaces", "demo-surface", "runtime.json")); + assert.equal(runtime.runtime.policy, "strict"); + assert.equal(runtime.governance.owner, "designers@example.com"); + assert.equal(runtime.runtime.policySeverities.runtime, "strict"); + assert.deepEqual(runtime.runtime.mutationEnvelope.allowedSections, ["main.hero", "main.cta"]); + assert.deepEqual(runtime.runtime.structure.allowedComponents, ["hero-banner", "cta-group"]); } finally { await rm(outDir, { recursive: true, force: true }); } diff --git a/packages/interfacectl-cli/test/fixtures/compile/expected/surfaces/demo-surface/generation.json b/packages/interfacectl-cli/test/fixtures/compile/expected/surfaces/demo-surface/generation.json index 31d4592..7d633ab 100644 --- a/packages/interfacectl-cli/test/fixtures/compile/expected/surfaces/demo-surface/generation.json +++ b/packages/interfacectl-cli/test/fixtures/compile/expected/surfaces/demo-surface/generation.json @@ -1,10 +1,31 @@ { + "adaptation": { + "contextIds": [], + "mutationEnvelope": { + "allowedActions": [ + "update-copy" + ], + "mode": "content-only", + "scopes": [ + "content" + ] + }, + "policy": "strict" + }, "boundary": { "allowSources": [], "contentSlot": null, "mustNotEmit": [], "shellOwns": [] }, + "governance": { + "approvals": [], + "domain": null, + "owner": null, + "phase0": null, + "roles": {}, + "status": "draft" + }, "guidance": { "boundaryRules": [ { @@ -74,6 +95,7 @@ "constraints": "./constraints.json", "contract": "../../contract/normalized.json", "repairMap": "./repair-map.json", + "runtime": "./runtime.json", "sections": "./sections.json" }, "structure": { diff --git a/packages/interfacectl-cli/test/fixtures/compile/expected/surfaces/demo-surface/runtime.json b/packages/interfacectl-cli/test/fixtures/compile/expected/surfaces/demo-surface/runtime.json new file mode 100644 index 0000000..f349c59 --- /dev/null +++ b/packages/interfacectl-cli/test/fixtures/compile/expected/surfaces/demo-surface/runtime.json @@ -0,0 +1,87 @@ +{ + "governance": { + "approvals": [], + "domain": null, + "owner": null, + "phase0": null, + "roles": {}, + "status": "draft" + }, + "identity": { + "displayName": "Demo Surface", + "surfaceId": "demo-surface", + "type": "web" + }, + "provenance": { + "bundleVersion": "2.0", + "contractId": "demo-ui", + "contractVersion": "1.0.0", + "surfaceId": "demo-surface" + }, + "refs": { + "components": "./components.json", + "constraints": "./constraints.json", + "contract": "../../contract/normalized.json", + "repairMap": "./repair-map.json", + "sections": "./sections.json" + }, + "runtime": { + "boundary": { + "allowSources": [], + "contentSlot": null, + "mustNotEmit": [], + "shellOwns": [] + }, + "contexts": [], + "layout": { + "maxContentWidth": 960, + "requiredContainers": [] + }, + "mutationEnvelope": { + "allowedActions": [ + "update-copy" + ], + "mode": "content-only", + "scopes": [ + "content" + ] + }, + "policy": "strict", + "policySeverities": { + "boundary": "off", + "interaction": "off", + "layout": "off", + "runtime": "strict", + "structure": "strict", + "visual": "off" + }, + "structure": { + "allowedComponents": [], + "allowedSections": [ + "main.hero" + ], + "requiredSections": [ + "main.hero" + ] + }, + "visual": { + "allowedFonts": [ + "Demo Sans", + "sans-serif", + "var(--font-demo)" + ], + "color": { + "allowedValues": [], + "policy": "off" + }, + "motion": { + "allowedDurationsMs": [ + 120 + ], + "allowedTimingFunctions": [ + "linear" + ] + } + } + } +} diff --git a/packages/interfacectl-cli/test/prepare-generation.test.mjs b/packages/interfacectl-cli/test/prepare-generation.test.mjs index 973ea15..be0cad7 100644 --- a/packages/interfacectl-cli/test/prepare-generation.test.mjs +++ b/packages/interfacectl-cli/test/prepare-generation.test.mjs @@ -61,12 +61,37 @@ function buildContract(overrides = {}) { displayName: "Demo Surface", type: "web", requiredSections: ["main.hero"], + owner: "designers@example.com", mustNotEmit: ["header"], allowedFonts: ["Demo Sans", "sans-serif", "var(--font-demo)"], layout: { maxContentWidth: 960, requiredContainers: ["contract-container"], }, + governance: { + status: "review", + roles: { + designers: ["designers@example.com"], + engineers: ["eng@example.com"], + }, + }, + runtime: { + policy: "strict", + mutationEnvelope: { + mode: "slot-bound", + scopes: ["content", "components"], + allowedActions: ["update-copy", "swap-variant"], + allowedSections: ["main.hero"], + }, + contexts: [ + { + id: "launch", + when: "route == '/'", + policy: "warn", + requiredSections: ["main.hero"], + }, + ], + }, authoring: { framework: "next", routing: "app-router", @@ -165,6 +190,7 @@ test("prepare-generation: emits resolved payload with summary, provenance, autho assert.equal(payload.bundle.version, "2.0"); assert.equal(payload.bundle.manifestPath, path.join(bundleRoot, "manifest.json")); assert.equal(payload.bundle.sourcePaths.contract, path.join(bundleRoot, "contract", "normalized.json")); + assert.equal(payload.bundle.sourcePaths.runtime, path.join(bundleRoot, "surfaces", "demo-surface", "runtime.json")); assert.equal(payload.contract.id, "prepare-demo"); assert.equal(payload.contract.version, "1.0.0"); assert.equal(payload.contract.normalizedPath, path.join(bundleRoot, "contract", "normalized.json")); @@ -172,10 +198,13 @@ test("prepare-generation: emits resolved payload with summary, provenance, autho assert.ok(payload.summary.text.includes("Focus on"), "summary should include human-readable text"); assert.ok(Array.isArray(payload.summary.checklist)); assert.deepEqual(payload.generation.boundary.shellOwns, ["header", "footer"]); + assert.equal(payload.generation.governance.owner, "designers@example.com"); + assert.equal(payload.generation.adaptation.mutationEnvelope.mode, "slot-bound"); assert.equal(payload.sections.length, 1); assert.equal(payload.components.length, 1); assert.equal(payload.constraints.color.policy, "warn"); assert.equal(payload.repairMap[0].code, "shell.primitive.disallowed"); + assert.equal(payload.runtime.policy, "strict"); assert.equal(payload.authoring.framework, "next"); assert.deepEqual(payload.evidenceRefs, [{ kind: "contract-field", path: "/x_extracted" }]); const schemaValidation = validatePreparedGenerationOutput(payload); @@ -221,6 +250,7 @@ test("prepare-generation: --out writes the payload file and suppresses stdout", const written = JSON.parse(await fsp.readFile(outPath, "utf8")); assert.equal(written.surface.surfaceId, "demo-surface"); assert.equal(written.bundle.sourcePaths.generation, path.join(bundleRoot, "surfaces", "demo-surface", "generation.json")); + assert.equal(written.bundle.sourcePaths.runtime, path.join(bundleRoot, "surfaces", "demo-surface", "runtime.json")); const schemaValidation = validatePreparedGenerationOutput(written); assert.equal( schemaValidation.ok, diff --git a/packages/interfacectl-cli/test/prepare-runtime.test.mjs b/packages/interfacectl-cli/test/prepare-runtime.test.mjs new file mode 100644 index 0000000..e0f3aa1 --- /dev/null +++ b/packages/interfacectl-cli/test/prepare-runtime.test.mjs @@ -0,0 +1,200 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import fsp from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { spawn } from "node:child_process"; +import { once } from "node:events"; +import { fileURLToPath } from "node:url"; +import { validateDiffOutput } from "@surfaces/interfacectl-validator"; +import prepareRuntimeOutputSchema from "../schemas/prepare-runtime-output.schema.json" with { type: "json" }; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const cliPath = path.resolve(__dirname, "..", "dist", "index.js"); + +async function runCli(args, cwd = __dirname) { + const child = spawn("node", [cliPath, ...args], { + cwd, + env: process.env, + }); + + let stdout = ""; + let stderr = ""; + child.stdout.on("data", (chunk) => { + stdout += chunk.toString(); + }); + child.stderr.on("data", (chunk) => { + stderr += chunk.toString(); + }); + + const [exitCode] = await once(child, "exit"); + return { + exitCode: Number(exitCode), + stdout, + stderr, + }; +} + +async function writeJson(filePath, value) { + await fsp.mkdir(path.dirname(filePath), { recursive: true }); + await fsp.writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8"); +} + +function buildContract() { + return { + contractId: "runtime-demo", + version: "1.0.0", + shell: { + owns: ["header", "footer"], + contentSlot: "main-content", + }, + surfaces: [ + { + id: "demo-surface", + displayName: "Demo Surface", + type: "web", + owner: "designers@example.com", + requiredSections: ["main.hero"], + mustNotEmit: ["header"], + allowedFonts: ["Demo Sans", "sans-serif"], + layout: { + maxContentWidth: 960, + requiredContainers: ["contract-container"], + }, + governance: { + status: "published", + roles: { + designers: ["designers@example.com"], + engineers: ["eng@example.com"], + }, + approvals: [ + { + role: "designer", + owner: "designers@example.com", + status: "approved", + timestamp: "2026-03-16T10:00:00Z", + }, + ], + }, + runtime: { + policy: "strict", + mutationEnvelope: { + mode: "slot-bound", + scopes: ["content", "components"], + allowedActions: ["update-copy", "swap-variant"], + allowedSections: ["main.hero"], + }, + contexts: [ + { + id: "launch", + when: "route == '/'", + policy: "warn", + requiredSections: ["main.hero"], + }, + ], + }, + }, + ], + sections: [ + { + id: "main.hero", + intent: "primary-intro", + description: "Demo hero section", + }, + ], + components: [ + { + id: "hero-banner", + intent: "hero", + slots: [ + { id: "title", kind: "text", required: true }, + ], + }, + ], + constraints: { + motion: { + allowedDurationsMs: [120], + allowedTimingFunctions: ["linear"], + }, + }, + color: { + policy: "warn", + allowedValues: ["#ffffff"], + }, + x_extracted: { + evidenceMarker: "runtime-evidence", + }, + }; +} + +async function compileBundle(contractPath, outDir, cwd = path.dirname(contractPath)) { + const result = await runCli( + ["compile", "--contract", contractPath, "--out", outDir], + cwd, + ); + assert.equal(result.exitCode, 0, `compile should exit 0: ${result.stderr}`); + return outDir; +} + +test("prepare-runtime: emits resolved runtime payload with governance and enforcement data", async () => { + const tempDir = await fsp.mkdtemp(path.join(os.tmpdir(), "interfacectl-prepare-runtime-")); + const contractPath = path.join(tempDir, "contract.json"); + const bundleRoot = path.join(tempDir, "bundle"); + + try { + await writeJson(contractPath, buildContract()); + await compileBundle(contractPath, bundleRoot, tempDir); + + const result = await runCli( + ["prepare-runtime", "--bundle-root", bundleRoot, "--surface", "demo-surface"], + tempDir, + ); + + assert.equal(result.exitCode, 0, result.stderr); + const payload = JSON.parse(result.stdout); + + assert.equal(payload.surface.surfaceId, "demo-surface"); + assert.equal(payload.bundle.sourcePaths.runtime, path.join(bundleRoot, "surfaces", "demo-surface", "runtime.json")); + assert.equal(payload.summary.mutationMode, "slot-bound"); + assert.deepEqual(payload.summary.strictCategories, ["boundary", "runtime", "structure"]); + assert.equal(payload.governance.owner, "designers@example.com"); + assert.equal(payload.governance.status, "published"); + assert.equal(payload.runtime.policy, "strict"); + assert.deepEqual(payload.runtime.structure.requiredSections, ["main.hero"]); + assert.deepEqual(payload.evidenceRefs, [{ kind: "contract-field", path: "/x_extracted" }]); + + const schemaValidation = validateDiffOutput(payload, prepareRuntimeOutputSchema); + assert.equal( + schemaValidation.ok, + true, + `prepare-runtime output should satisfy schema: ${JSON.stringify(schemaValidation.errors)}`, + ); + } finally { + await fsp.rm(tempDir, { recursive: true, force: true }); + } +}); + +test("prepare-runtime: fails with adapter error when runtime bundle is missing", async () => { + const tempDir = await fsp.mkdtemp(path.join(os.tmpdir(), "interfacectl-prepare-runtime-missing-")); + const contractPath = path.join(tempDir, "contract.json"); + const bundleRoot = path.join(tempDir, "bundle"); + + try { + await writeJson(contractPath, buildContract()); + await compileBundle(contractPath, bundleRoot, tempDir); + await fsp.rm(path.join(bundleRoot, "surfaces", "demo-surface", "runtime.json")); + + const result = await runCli( + ["prepare-runtime", "--bundle-root", bundleRoot, "--surface", "demo-surface"], + tempDir, + ); + + assert.equal(result.exitCode, 10, result.stderr); + const payload = JSON.parse(result.stderr); + assert.equal(payload.code, "adapter.bundle.runtime-missing"); + } finally { + await fsp.rm(tempDir, { recursive: true, force: true }); + } +}); diff --git a/packages/interfacectl-validator/dist/index.d.ts b/packages/interfacectl-validator/dist/index.d.ts index 01382fb..900ea08 100644 --- a/packages/interfacectl-validator/dist/index.d.ts +++ b/packages/interfacectl-validator/dist/index.d.ts @@ -8,7 +8,7 @@ export interface ContractStructureValidation { export declare function validateContractStructure(contractData: unknown, schema: object): ContractStructureValidation; export declare function evaluateSurfaceCompliance(contract: InterfaceContract, descriptor: SurfaceDescriptor): SurfaceReport; export declare function evaluateContractCompliance(contract: InterfaceContract, descriptors: SurfaceDescriptor[]): ValidationSummary; -export type { InterfaceContract, ContractComponent, ContractSlot, ContractSlotKind, ContractSlotContentRules, ContractComponentVariant, ContractState, ContractInteraction, ContractInteractionEffect, ContractComponentImplementation, ExternalReference, ExternalReferenceSystem, AuthoringSource, SectionAnatomy, SectionEditPolicy, SectionEditMode, SectionAllowedOperation, SectionResponsive, SectionResponsiveRule, ResponsiveLayoutIntent, ResponsiveSlotBehavior, ResponsiveSlotBehaviorKind, ViewportProfile, SurfaceAuthoring, SurfaceAuthoringStyling, SurfaceAuthoringLibraries, ContractSurface, ContractSection, ContractConstraints, ContractTokenPolicies, SurfaceDescriptor, SurfaceSectionDescriptor, SurfaceFontDescriptor, SurfaceColorDescriptor, SurfaceIconDescriptor, SurfaceTokenDescriptor, SurfaceTokenUsage, SurfaceMotionDescriptor, SurfaceLayoutDescriptor, PageFrameLayoutDescriptor, ChromeLayoutDescriptor, ChromePolicyTarget, ChromeShadowKind, LandingPatternDescriptor, SurfacePrimitiveDescriptor, SurfaceReport, DriftViolation, ValidationSummary, DriftViolationType, ContractRef, RuleRef, DiffOutput, DiffEntry, DiffChangeType, DriftRisk, Severity, SafetyLevel, EnforcementPolicy, EnforcementMode, IconPolicy, TokenCategory, TokenMetadata, TokenPolicy, ContractMarketingProfiles, MarketingLayoutProfile, MarketingTypographyProfile, MarketingTypographyRoleProfile, MarketingHeroContainerMode, MarketingHeroVisualPlacement, MarketingSectionDividerMode, MarketingSectionSpacingProfile, MarketingTypographyRole, FlowPolicy, FlowRequirement, FlowTransitionRequirement, LandingPatternPolicy, SurfaceMarketingTypographyDescriptor, SurfaceMarketingTypographyRoleDescriptor, SurfaceFlowDescriptor, SurfaceFlowStepDescriptor, SurfaceFlowTransitionDescriptor, AutofixRule, FixSummary, FixEntry, FixError, } from "./types.js"; +export type { InterfaceContract, ContractComponent, ContractSlot, ContractSlotKind, ContractSlotContentRules, ContractComponentVariant, ContractState, ContractInteraction, ContractInteractionEffect, ContractComponentImplementation, ExternalReference, ExternalReferenceSystem, AuthoringSource, SectionAnatomy, SectionEditPolicy, SectionEditMode, SectionAllowedOperation, SectionResponsive, SectionResponsiveRule, ResponsiveLayoutIntent, ResponsiveSlotBehavior, ResponsiveSlotBehaviorKind, ViewportProfile, SurfaceAuthoring, SurfaceAuthoringStyling, SurfaceAuthoringLibraries, SurfacePhase0, SurfaceGovernanceRoles, SurfaceApprovalRole, SurfaceApprovalStatus, SurfaceApprovalRecord, SurfaceGovernance, SurfaceMutationMode, SurfaceMutationScope, SurfaceMutationAction, SurfaceMutationEnvelope, SurfaceRuntimeContextRule, SurfaceRuntimePolicy, ContractSurface, ContractSection, ContractConstraints, ContractTokenPolicies, SurfaceDescriptor, SurfaceSectionDescriptor, SurfaceFontDescriptor, SurfaceColorDescriptor, SurfaceIconDescriptor, SurfaceTokenDescriptor, SurfaceTokenUsage, SurfaceMotionDescriptor, SurfaceLayoutDescriptor, PageFrameLayoutDescriptor, ChromeLayoutDescriptor, ChromePolicyTarget, ChromeShadowKind, LandingPatternDescriptor, SurfacePrimitiveDescriptor, SurfaceReport, DriftViolation, ValidationSummary, DriftViolationType, ContractRef, RuleRef, DiffOutput, DiffEntry, DiffChangeType, DriftRisk, Severity, SafetyLevel, EnforcementPolicy, EnforcementMode, IconPolicy, TokenCategory, TokenMetadata, TokenPolicy, ContractMarketingProfiles, MarketingLayoutProfile, MarketingTypographyProfile, MarketingTypographyRoleProfile, MarketingHeroContainerMode, MarketingHeroVisualPlacement, MarketingSectionDividerMode, MarketingSectionSpacingProfile, MarketingTypographyRole, FlowPolicy, FlowRequirement, FlowTransitionRequirement, LandingPatternPolicy, SurfaceMarketingTypographyDescriptor, SurfaceMarketingTypographyRoleDescriptor, SurfaceFlowDescriptor, SurfaceFlowStepDescriptor, SurfaceFlowTransitionDescriptor, AutofixRule, FixSummary, FixEntry, FixError, } from "./types.js"; export { getBundledDiffSchema, getBundledPolicySchema, getBundledFixSummarySchema, validateDiffOutput, validatePolicy, validateFixSummary, type ValidationResult, } from "./schema-validate.js"; export { normalizeColorValue, normalizeColorValues, } from "./color-policy.js"; export { matchTokenPolicy, normalizeTokenLiteralValue, type TokenPolicyMatch, } from "./token-policy.js"; diff --git a/packages/interfacectl-validator/dist/index.d.ts.map b/packages/interfacectl-validator/dist/index.d.ts.map index 99708bf..15739f3 100644 --- a/packages/interfacectl-validator/dist/index.d.ts.map +++ b/packages/interfacectl-validator/dist/index.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EAQlB,MAAM,YAAY,CAAC;AASpB,wBAAgB,wBAAwB,IAAI,MAAM,CAEjD;AAED,MAAM,WAAW,2BAA2B;IAC1C,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;CAC9B;AAED,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,OAAO,EACrB,MAAM,EAAE,MAAM,GACb,2BAA2B,CA0C7B;AAo+BD,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,iBAAiB,EAC3B,UAAU,EAAE,iBAAiB,GAC5B,aAAa,CAgbf;AAED,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,iBAAiB,EAC3B,WAAW,EAAE,iBAAiB,EAAE,GAC/B,iBAAiB,CA6CnB;AAyCD,YAAY,EACV,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,EAChB,wBAAwB,EACxB,wBAAwB,EACxB,aAAa,EACb,mBAAmB,EACnB,yBAAyB,EACzB,+BAA+B,EAC/B,iBAAiB,EACjB,uBAAuB,EACvB,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,uBAAuB,EACvB,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,0BAA0B,EAC1B,eAAe,EACf,gBAAgB,EAChB,uBAAuB,EACvB,yBAAyB,EACzB,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,EACjB,wBAAwB,EACxB,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,EACvB,yBAAyB,EACzB,sBAAsB,EACtB,kBAAkB,EAClB,gBAAgB,EAChB,wBAAwB,EACxB,0BAA0B,EAC1B,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,OAAO,EACP,UAAU,EACV,SAAS,EACT,cAAc,EACd,SAAS,EACT,QAAQ,EACR,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,UAAU,EACV,aAAa,EACb,aAAa,EACb,WAAW,EACX,yBAAyB,EACzB,sBAAsB,EACtB,0BAA0B,EAC1B,8BAA8B,EAC9B,0BAA0B,EAC1B,4BAA4B,EAC5B,2BAA2B,EAC3B,8BAA8B,EAC9B,uBAAuB,EACvB,UAAU,EACV,eAAe,EACf,yBAAyB,EACzB,oBAAoB,EACpB,oCAAoC,EACpC,wCAAwC,EACxC,qBAAqB,EACrB,yBAAyB,EACzB,+BAA+B,EAC/B,WAAW,EACX,UAAU,EACV,QAAQ,EACR,QAAQ,GACT,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,0BAA0B,EAC1B,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,KAAK,gBAAgB,GACtB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,gBAAgB,EAChB,0BAA0B,EAC1B,KAAK,gBAAgB,GACtB,MAAM,mBAAmB,CAAC"} \ No newline at end of file +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EAQlB,MAAM,YAAY,CAAC;AASpB,wBAAgB,wBAAwB,IAAI,MAAM,CAEjD;AAED,MAAM,WAAW,2BAA2B;IAC1C,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;CAC9B;AAED,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,OAAO,EACrB,MAAM,EAAE,MAAM,GACb,2BAA2B,CA8C7B;AAuiCD,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,iBAAiB,EAC3B,UAAU,EAAE,iBAAiB,GAC5B,aAAa,CAgbf;AAED,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,iBAAiB,EAC3B,WAAW,EAAE,iBAAiB,EAAE,GAC/B,iBAAiB,CA6CnB;AAyCD,YAAY,EACV,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,EAChB,wBAAwB,EACxB,wBAAwB,EACxB,aAAa,EACb,mBAAmB,EACnB,yBAAyB,EACzB,+BAA+B,EAC/B,iBAAiB,EACjB,uBAAuB,EACvB,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,uBAAuB,EACvB,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,0BAA0B,EAC1B,eAAe,EACf,gBAAgB,EAChB,uBAAuB,EACvB,yBAAyB,EACzB,aAAa,EACb,sBAAsB,EACtB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,uBAAuB,EACvB,yBAAyB,EACzB,oBAAoB,EACpB,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,EACjB,wBAAwB,EACxB,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,EACvB,yBAAyB,EACzB,sBAAsB,EACtB,kBAAkB,EAClB,gBAAgB,EAChB,wBAAwB,EACxB,0BAA0B,EAC1B,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,OAAO,EACP,UAAU,EACV,SAAS,EACT,cAAc,EACd,SAAS,EACT,QAAQ,EACR,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,UAAU,EACV,aAAa,EACb,aAAa,EACb,WAAW,EACX,yBAAyB,EACzB,sBAAsB,EACtB,0BAA0B,EAC1B,8BAA8B,EAC9B,0BAA0B,EAC1B,4BAA4B,EAC5B,2BAA2B,EAC3B,8BAA8B,EAC9B,uBAAuB,EACvB,UAAU,EACV,eAAe,EACf,yBAAyB,EACzB,oBAAoB,EACpB,oCAAoC,EACpC,wCAAwC,EACxC,qBAAqB,EACrB,yBAAyB,EACzB,+BAA+B,EAC/B,WAAW,EACX,UAAU,EACV,QAAQ,EACR,QAAQ,GACT,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,0BAA0B,EAC1B,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,KAAK,gBAAgB,GACtB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,gBAAgB,EAChB,0BAA0B,EAC1B,KAAK,gBAAgB,GACtB,MAAM,mBAAmB,CAAC"} \ No newline at end of file diff --git a/packages/interfacectl-validator/dist/index.js b/packages/interfacectl-validator/dist/index.js index af0981b..f7407de 100644 --- a/packages/interfacectl-validator/dist/index.js +++ b/packages/interfacectl-validator/dist/index.js @@ -25,9 +25,11 @@ export function validateContractStructure(contractData, schema) { } const marketingReferenceErrors = validateMarketingProfileReferences(contractData); const authoringReferenceErrors = validateAuthoringMetadata(contractData); + const governanceReferenceErrors = validateGovernanceMetadata(contractData); const validationErrors = [ ...marketingReferenceErrors, ...authoringReferenceErrors, + ...governanceReferenceErrors, ]; if (validationErrors.length > 0) { return { @@ -107,6 +109,38 @@ function validateAuthoringMetadata(contract) { } return errors; } +function validateGovernanceMetadata(contract) { + const errors = []; + const sectionIds = new Set(contract.sections.map((section) => section.id)); + for (const surface of contract.surfaces) { + const mutationEnvelope = surface.runtime?.mutationEnvelope; + validateSurfaceSectionReferences(mutationEnvelope?.allowedSections ?? [], sectionIds, `/surfaces/${surface.id}/runtime/mutationEnvelope/allowedSections`, errors); + validateSurfaceSectionReferences(mutationEnvelope?.prohibitedSections ?? [], sectionIds, `/surfaces/${surface.id}/runtime/mutationEnvelope/prohibitedSections`, errors); + const allowedSections = new Set(mutationEnvelope?.allowedSections ?? []); + for (const sectionId of mutationEnvelope?.prohibitedSections ?? []) { + if (allowedSections.has(sectionId)) { + errors.push(`/surfaces/${surface.id}/runtime/mutationEnvelope section "${sectionId}" cannot be both allowed and prohibited`); + } + } + const contextIds = new Set(); + for (const context of surface.runtime?.contexts ?? []) { + if (contextIds.has(context.id)) { + errors.push(`/surfaces/${surface.id}/runtime/contexts/${context.id} must use a unique context id within the surface`); + } + contextIds.add(context.id); + validateSurfaceSectionReferences(context.requiredSections ?? [], sectionIds, `/surfaces/${surface.id}/runtime/contexts/${context.id}/requiredSections`, errors); + validateSurfaceSectionReferences(context.prohibitedSections ?? [], sectionIds, `/surfaces/${surface.id}/runtime/contexts/${context.id}/prohibitedSections`, errors); + } + } + return errors; +} +function validateSurfaceSectionReferences(refs, sectionIds, pathPrefix, errors) { + for (const ref of refs) { + if (!sectionIds.has(ref)) { + errors.push(`${pathPrefix}/${ref} must reference a declared section id`); + } + } +} function validateComponentAuthoring(component, componentIds, errors) { const slotIds = new Set(); for (const slot of component.slots) { diff --git a/packages/interfacectl-validator/dist/schema/web.surface.contract.schema.json b/packages/interfacectl-validator/dist/schema/web.surface.contract.schema.json index c8f44a9..b5d5a93 100644 --- a/packages/interfacectl-validator/dist/schema/web.surface.contract.schema.json +++ b/packages/interfacectl-validator/dist/schema/web.surface.contract.schema.json @@ -252,6 +252,12 @@ }, "authoring": { "$ref": "#/$defs/surfaceAuthoring" + }, + "governance": { + "$ref": "#/$defs/surfaceGovernance" + }, + "runtime": { + "$ref": "#/$defs/surfaceRuntime" } }, "allOf": [ @@ -870,6 +876,259 @@ } } }, + "surfaceGovernance": { + "type": "object", + "additionalProperties": false, + "properties": { + "status": { + "type": "string", + "enum": [ + "draft", + "review", + "approved", + "published" + ] + }, + "roles": { + "$ref": "#/$defs/surfaceGovernanceRoles" + }, + "approvals": { + "type": "array", + "items": { + "$ref": "#/$defs/surfaceApproval" + } + } + } + }, + "surfaceGovernanceRoles": { + "type": "object", + "additionalProperties": false, + "properties": { + "designers": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "uniqueItems": true + }, + "engineers": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "uniqueItems": true + }, + "approvers": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "uniqueItems": true + } + } + }, + "surfaceApproval": { + "type": "object", + "additionalProperties": false, + "required": [ + "role", + "owner", + "status" + ], + "properties": { + "role": { + "type": "string", + "enum": [ + "designer", + "engineering", + "product", + "qa", + "operations", + "other" + ] + }, + "owner": { + "type": "string", + "minLength": 1 + }, + "status": { + "type": "string", + "enum": [ + "pending", + "approved", + "rejected" + ] + }, + "note": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + } + } + }, + "surfaceRuntime": { + "type": "object", + "additionalProperties": false, + "properties": { + "policy": { + "type": "string", + "enum": [ + "off", + "warn", + "strict" + ] + }, + "mutationEnvelope": { + "$ref": "#/$defs/surfaceMutationEnvelope" + }, + "contexts": { + "type": "array", + "items": { + "$ref": "#/$defs/surfaceRuntimeContext" + } + } + } + }, + "surfaceMutationEnvelope": { + "type": "object", + "additionalProperties": false, + "required": [ + "mode" + ], + "properties": { + "mode": { + "type": "string", + "enum": [ + "locked", + "content-only", + "slot-bound", + "layout-tuning", + "section-assembly", + "freeform" + ] + }, + "scopes": { + "type": "array", + "items": { + "$ref": "#/$defs/surfaceMutationScope" + }, + "uniqueItems": true + }, + "allowedActions": { + "type": "array", + "items": { + "$ref": "#/$defs/surfaceMutationAction" + }, + "uniqueItems": true + }, + "allowedSections": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-z0-9]+(\\.[a-z0-9]+)*$" + }, + "uniqueItems": true + }, + "prohibitedSections": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-z0-9]+(\\.[a-z0-9]+)*$" + }, + "uniqueItems": true + } + } + }, + "surfaceMutationScope": { + "type": "string", + "enum": [ + "content", + "components", + "layout", + "sections", + "interactions" + ] + }, + "surfaceMutationAction": { + "type": "string", + "enum": [ + "update-copy", + "swap-variant", + "reorder-items", + "change-media", + "adjust-layout", + "bind-data", + "wire-interaction", + "add-section", + "remove-section", + "reorder-sections", + "swap-component" + ] + }, + "surfaceRuntimeContext": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "when" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^[a-z0-9]+([.-][a-z0-9]+)*$" + }, + "when": { + "type": "string", + "minLength": 1 + }, + "policy": { + "type": "string", + "enum": [ + "off", + "warn", + "strict" + ] + }, + "requiredSections": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-z0-9]+(\\.[a-z0-9]+)*$" + }, + "uniqueItems": true + }, + "prohibitedSections": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-z0-9]+(\\.[a-z0-9]+)*$" + }, + "uniqueItems": true + }, + "allowedLayoutIntents": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "stack", + "columns", + "auto-fit-grid", + "sidebar-main", + "single-column-form" + ] + }, + "uniqueItems": true + }, + "notes": { + "type": "string" + } + } + }, "authoringSource": { "type": "string", "enum": [ diff --git a/packages/interfacectl-validator/dist/types.d.ts b/packages/interfacectl-validator/dist/types.d.ts index 78113c4..5b0e9ca 100644 --- a/packages/interfacectl-validator/dist/types.d.ts +++ b/packages/interfacectl-validator/dist/types.d.ts @@ -158,6 +158,55 @@ export interface SurfaceAuthoring { preferredLibraries?: SurfaceAuthoringLibraries; sourcePriority: AuthoringSource[]; } +export interface SurfacePhase0 { + authPosture?: "public" | "auth-aware" | "auth-first"; + requiresShell?: boolean; + expectsAuthRoutes?: boolean; + expectsDesignSystem?: boolean; +} +export interface SurfaceGovernanceRoles { + designers?: string[]; + engineers?: string[]; + approvers?: string[]; +} +export type SurfaceApprovalRole = "designer" | "engineering" | "product" | "qa" | "operations" | "other"; +export type SurfaceApprovalStatus = "pending" | "approved" | "rejected"; +export interface SurfaceApprovalRecord { + role: SurfaceApprovalRole; + owner: string; + status: SurfaceApprovalStatus; + note?: string; + timestamp?: string; +} +export interface SurfaceGovernance { + status?: "draft" | "review" | "approved" | "published"; + roles?: SurfaceGovernanceRoles; + approvals?: SurfaceApprovalRecord[]; +} +export type SurfaceMutationMode = "locked" | "content-only" | "slot-bound" | "layout-tuning" | "section-assembly" | "freeform"; +export type SurfaceMutationScope = "content" | "components" | "layout" | "sections" | "interactions"; +export type SurfaceMutationAction = SectionAllowedOperation | "add-section" | "remove-section" | "reorder-sections" | "swap-component"; +export interface SurfaceMutationEnvelope { + mode: SurfaceMutationMode; + scopes?: SurfaceMutationScope[]; + allowedActions?: SurfaceMutationAction[]; + allowedSections?: string[]; + prohibitedSections?: string[]; +} +export interface SurfaceRuntimeContextRule { + id: string; + when: string; + policy?: "off" | "warn" | "strict"; + requiredSections?: string[]; + prohibitedSections?: string[]; + allowedLayoutIntents?: ResponsiveLayoutIntent[]; + notes?: string; +} +export interface SurfaceRuntimePolicy { + policy?: "off" | "warn" | "strict"; + mutationEnvelope?: SurfaceMutationEnvelope; + contexts?: SurfaceRuntimeContextRule[]; +} export interface ContractSurface { id: string; displayName: string; @@ -175,10 +224,15 @@ export interface ContractSurface { }; icons?: IconPolicy; flows?: FlowPolicy; + phase0?: SurfacePhase0; + domain?: string; + owner?: string; mustNotEmit?: string[]; shellOwnedPrimitiveAllowSources?: string[]; viewports?: ViewportProfile[]; authoring?: SurfaceAuthoring; + governance?: SurfaceGovernance; + runtime?: SurfaceRuntimePolicy; } export interface ContractSection { id: string; diff --git a/packages/interfacectl-validator/dist/types.d.ts.map b/packages/interfacectl-validator/dist/types.d.ts.map index 85cba21..555c537 100644 --- a/packages/interfacectl-validator/dist/types.d.ts.map +++ b/packages/interfacectl-validator/dist/types.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/interfacectl-validator/src/index.ts b/packages/interfacectl-validator/src/index.ts index 3c3ecc8..a6a1c0d 100644 --- a/packages/interfacectl-validator/src/index.ts +++ b/packages/interfacectl-validator/src/index.ts @@ -62,9 +62,13 @@ export function validateContractStructure( const authoringReferenceErrors = validateAuthoringMetadata( contractData as InterfaceContract, ); + const governanceReferenceErrors = validateGovernanceMetadata( + contractData as InterfaceContract, + ); const validationErrors = [ ...marketingReferenceErrors, ...authoringReferenceErrors, + ...governanceReferenceErrors, ]; if (validationErrors.length > 0) { return { @@ -193,6 +197,73 @@ function validateAuthoringMetadata(contract: InterfaceContract): string[] { return errors; } +function validateGovernanceMetadata(contract: InterfaceContract): string[] { + const errors: string[] = []; + const sectionIds = new Set(contract.sections.map((section) => section.id)); + + for (const surface of contract.surfaces) { + const mutationEnvelope = surface.runtime?.mutationEnvelope; + validateSurfaceSectionReferences( + mutationEnvelope?.allowedSections ?? [], + sectionIds, + `/surfaces/${surface.id}/runtime/mutationEnvelope/allowedSections`, + errors, + ); + validateSurfaceSectionReferences( + mutationEnvelope?.prohibitedSections ?? [], + sectionIds, + `/surfaces/${surface.id}/runtime/mutationEnvelope/prohibitedSections`, + errors, + ); + + const allowedSections = new Set(mutationEnvelope?.allowedSections ?? []); + for (const sectionId of mutationEnvelope?.prohibitedSections ?? []) { + if (allowedSections.has(sectionId)) { + errors.push( + `/surfaces/${surface.id}/runtime/mutationEnvelope section "${sectionId}" cannot be both allowed and prohibited`, + ); + } + } + + const contextIds = new Set(); + for (const context of surface.runtime?.contexts ?? []) { + if (contextIds.has(context.id)) { + errors.push( + `/surfaces/${surface.id}/runtime/contexts/${context.id} must use a unique context id within the surface`, + ); + } + contextIds.add(context.id); + validateSurfaceSectionReferences( + context.requiredSections ?? [], + sectionIds, + `/surfaces/${surface.id}/runtime/contexts/${context.id}/requiredSections`, + errors, + ); + validateSurfaceSectionReferences( + context.prohibitedSections ?? [], + sectionIds, + `/surfaces/${surface.id}/runtime/contexts/${context.id}/prohibitedSections`, + errors, + ); + } + } + + return errors; +} + +function validateSurfaceSectionReferences( + refs: string[], + sectionIds: ReadonlySet, + pathPrefix: string, + errors: string[], +): void { + for (const ref of refs) { + if (!sectionIds.has(ref)) { + errors.push(`${pathPrefix}/${ref} must reference a declared section id`); + } + } +} + function validateComponentAuthoring( component: ContractComponent, componentIds: ReadonlySet, @@ -1627,6 +1698,18 @@ export type { SurfaceAuthoring, SurfaceAuthoringStyling, SurfaceAuthoringLibraries, + SurfacePhase0, + SurfaceGovernanceRoles, + SurfaceApprovalRole, + SurfaceApprovalStatus, + SurfaceApprovalRecord, + SurfaceGovernance, + SurfaceMutationMode, + SurfaceMutationScope, + SurfaceMutationAction, + SurfaceMutationEnvelope, + SurfaceRuntimeContextRule, + SurfaceRuntimePolicy, ContractSurface, ContractSection, ContractConstraints, diff --git a/packages/interfacectl-validator/src/schema/web.surface.contract.schema.json b/packages/interfacectl-validator/src/schema/web.surface.contract.schema.json index c8f44a9..b5d5a93 100644 --- a/packages/interfacectl-validator/src/schema/web.surface.contract.schema.json +++ b/packages/interfacectl-validator/src/schema/web.surface.contract.schema.json @@ -252,6 +252,12 @@ }, "authoring": { "$ref": "#/$defs/surfaceAuthoring" + }, + "governance": { + "$ref": "#/$defs/surfaceGovernance" + }, + "runtime": { + "$ref": "#/$defs/surfaceRuntime" } }, "allOf": [ @@ -870,6 +876,259 @@ } } }, + "surfaceGovernance": { + "type": "object", + "additionalProperties": false, + "properties": { + "status": { + "type": "string", + "enum": [ + "draft", + "review", + "approved", + "published" + ] + }, + "roles": { + "$ref": "#/$defs/surfaceGovernanceRoles" + }, + "approvals": { + "type": "array", + "items": { + "$ref": "#/$defs/surfaceApproval" + } + } + } + }, + "surfaceGovernanceRoles": { + "type": "object", + "additionalProperties": false, + "properties": { + "designers": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "uniqueItems": true + }, + "engineers": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "uniqueItems": true + }, + "approvers": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "uniqueItems": true + } + } + }, + "surfaceApproval": { + "type": "object", + "additionalProperties": false, + "required": [ + "role", + "owner", + "status" + ], + "properties": { + "role": { + "type": "string", + "enum": [ + "designer", + "engineering", + "product", + "qa", + "operations", + "other" + ] + }, + "owner": { + "type": "string", + "minLength": 1 + }, + "status": { + "type": "string", + "enum": [ + "pending", + "approved", + "rejected" + ] + }, + "note": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + } + } + }, + "surfaceRuntime": { + "type": "object", + "additionalProperties": false, + "properties": { + "policy": { + "type": "string", + "enum": [ + "off", + "warn", + "strict" + ] + }, + "mutationEnvelope": { + "$ref": "#/$defs/surfaceMutationEnvelope" + }, + "contexts": { + "type": "array", + "items": { + "$ref": "#/$defs/surfaceRuntimeContext" + } + } + } + }, + "surfaceMutationEnvelope": { + "type": "object", + "additionalProperties": false, + "required": [ + "mode" + ], + "properties": { + "mode": { + "type": "string", + "enum": [ + "locked", + "content-only", + "slot-bound", + "layout-tuning", + "section-assembly", + "freeform" + ] + }, + "scopes": { + "type": "array", + "items": { + "$ref": "#/$defs/surfaceMutationScope" + }, + "uniqueItems": true + }, + "allowedActions": { + "type": "array", + "items": { + "$ref": "#/$defs/surfaceMutationAction" + }, + "uniqueItems": true + }, + "allowedSections": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-z0-9]+(\\.[a-z0-9]+)*$" + }, + "uniqueItems": true + }, + "prohibitedSections": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-z0-9]+(\\.[a-z0-9]+)*$" + }, + "uniqueItems": true + } + } + }, + "surfaceMutationScope": { + "type": "string", + "enum": [ + "content", + "components", + "layout", + "sections", + "interactions" + ] + }, + "surfaceMutationAction": { + "type": "string", + "enum": [ + "update-copy", + "swap-variant", + "reorder-items", + "change-media", + "adjust-layout", + "bind-data", + "wire-interaction", + "add-section", + "remove-section", + "reorder-sections", + "swap-component" + ] + }, + "surfaceRuntimeContext": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "when" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^[a-z0-9]+([.-][a-z0-9]+)*$" + }, + "when": { + "type": "string", + "minLength": 1 + }, + "policy": { + "type": "string", + "enum": [ + "off", + "warn", + "strict" + ] + }, + "requiredSections": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-z0-9]+(\\.[a-z0-9]+)*$" + }, + "uniqueItems": true + }, + "prohibitedSections": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-z0-9]+(\\.[a-z0-9]+)*$" + }, + "uniqueItems": true + }, + "allowedLayoutIntents": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "stack", + "columns", + "auto-fit-grid", + "sidebar-main", + "single-column-form" + ] + }, + "uniqueItems": true + }, + "notes": { + "type": "string" + } + } + }, "authoringSource": { "type": "string", "enum": [ diff --git a/packages/interfacectl-validator/src/types.ts b/packages/interfacectl-validator/src/types.ts index 13cb254..d5cf160 100644 --- a/packages/interfacectl-validator/src/types.ts +++ b/packages/interfacectl-validator/src/types.ts @@ -235,6 +235,89 @@ export interface SurfaceAuthoring { sourcePriority: AuthoringSource[]; } +export interface SurfacePhase0 { + authPosture?: "public" | "auth-aware" | "auth-first"; + requiresShell?: boolean; + expectsAuthRoutes?: boolean; + expectsDesignSystem?: boolean; +} + +export interface SurfaceGovernanceRoles { + designers?: string[]; + engineers?: string[]; + approvers?: string[]; +} + +export type SurfaceApprovalRole = + | "designer" + | "engineering" + | "product" + | "qa" + | "operations" + | "other"; + +export type SurfaceApprovalStatus = "pending" | "approved" | "rejected"; + +export interface SurfaceApprovalRecord { + role: SurfaceApprovalRole; + owner: string; + status: SurfaceApprovalStatus; + note?: string; + timestamp?: string; +} + +export interface SurfaceGovernance { + status?: "draft" | "review" | "approved" | "published"; + roles?: SurfaceGovernanceRoles; + approvals?: SurfaceApprovalRecord[]; +} + +export type SurfaceMutationMode = + | "locked" + | "content-only" + | "slot-bound" + | "layout-tuning" + | "section-assembly" + | "freeform"; + +export type SurfaceMutationScope = + | "content" + | "components" + | "layout" + | "sections" + | "interactions"; + +export type SurfaceMutationAction = + | SectionAllowedOperation + | "add-section" + | "remove-section" + | "reorder-sections" + | "swap-component"; + +export interface SurfaceMutationEnvelope { + mode: SurfaceMutationMode; + scopes?: SurfaceMutationScope[]; + allowedActions?: SurfaceMutationAction[]; + allowedSections?: string[]; + prohibitedSections?: string[]; +} + +export interface SurfaceRuntimeContextRule { + id: string; + when: string; + policy?: "off" | "warn" | "strict"; + requiredSections?: string[]; + prohibitedSections?: string[]; + allowedLayoutIntents?: ResponsiveLayoutIntent[]; + notes?: string; +} + +export interface SurfaceRuntimePolicy { + policy?: "off" | "warn" | "strict"; + mutationEnvelope?: SurfaceMutationEnvelope; + contexts?: SurfaceRuntimeContextRule[]; +} + export interface ContractSurface { id: string; displayName: string; @@ -252,10 +335,15 @@ export interface ContractSurface { }; icons?: IconPolicy; flows?: FlowPolicy; + phase0?: SurfacePhase0; + domain?: string; + owner?: string; mustNotEmit?: string[]; shellOwnedPrimitiveAllowSources?: string[]; viewports?: ViewportProfile[]; authoring?: SurfaceAuthoring; + governance?: SurfaceGovernance; + runtime?: SurfaceRuntimePolicy; } export interface ContractSection { diff --git a/packages/interfacectl-validator/test/authoring-contract.test.mjs b/packages/interfacectl-validator/test/authoring-contract.test.mjs index b062f0a..2055bf7 100644 --- a/packages/interfacectl-validator/test/authoring-contract.test.mjs +++ b/packages/interfacectl-validator/test/authoring-contract.test.mjs @@ -27,6 +27,45 @@ test("validateContractStructure accepts generic authoring metadata for a web sur assert.equal(result.ok, true); }); +test("validateContractStructure accepts governance and runtime metadata for a web surface", async () => { + const schema = getBundledContractSchema(); + const contract = await loadFixture(); + contract.surfaces[0].owner = "designers@example.com"; + contract.surfaces[0].governance = { + status: "review", + roles: { + designers: ["designers@example.com"], + engineers: ["eng@example.com"], + }, + approvals: [ + { + role: "designer", + owner: "designers@example.com", + status: "approved", + timestamp: "2026-03-16T10:00:00Z", + }, + ], + }; + contract.surfaces[0].runtime = { + policy: "strict", + mutationEnvelope: { + mode: "slot-bound", + scopes: ["content", "components"], + allowedActions: ["update-copy", "swap-variant"], + allowedSections: ["page.intro"], + }, + contexts: [ + { + id: "launch", + when: "route == '/'", + requiredSections: ["page.intro"], + }, + ], + }; + const result = validateContractStructure(contract, schema); + assert.equal(result.ok, true); +}); + test("validateContractStructure rejects section anatomy references to unknown components", async () => { const schema = getBundledContractSchema(); const contract = await loadFixture(); @@ -76,6 +115,23 @@ test("validateContractStructure rejects invalid viewport ranges", async () => { ); }); +test("validateContractStructure rejects runtime metadata that references unknown sections", async () => { + const schema = getBundledContractSchema(); + const contract = await loadFixture(); + contract.surfaces[0].runtime = { + mutationEnvelope: { + mode: "content-only", + allowedSections: ["missing.section"], + }, + }; + const result = validateContractStructure(contract, schema); + assert.equal(result.ok, false); + assert.ok( + result.errors.some((error) => error.includes("/surfaces/reference-target-web/runtime/mutationEnvelope/allowedSections/missing.section")), + `expected runtime section reference validation error, got ${JSON.stringify(result.errors)}`, + ); +}); + test("validateContractStructure rejects authoring metadata on non-web surfaces", async () => { const schema = getBundledContractSchema(); const contract = await loadFixture();