diff --git a/docs/eve-migration-roadmap.md b/docs/eve-migration-roadmap.md index 47a0d20..23cf7c8 100644 --- a/docs/eve-migration-roadmap.md +++ b/docs/eve-migration-roadmap.md @@ -274,14 +274,17 @@ Exit criteria: ## Active Work Queue -1. Add authority/staleness witness fixture to browser reference. -2. Add conformance-pack metadata files beside fixtures. -3. Add a Direct2D adapter spike or explicitly demote Direct2D until Fensalir owns +1. Add conformance-pack metadata files beside fixtures. +2. Add a Direct2D adapter spike or explicitly demote Direct2D until Fensalir owns the body. -4. Add split-readiness reports for EveFlutter and EveUnity. +3. Add split-readiness reports for EveFlutter and EveUnity. Recently cut: +- The browser reference now has an authority/staleness witness fixture. The + parity report verifies visible `fresh`, `accepted`, `stale`, `missing`, + `pending`, `denied`, `predicted`, and `reconciled` states, with provider and + renderer ownership kept separate. - Command descriptor validation now runs for strict fixtures. Sai proves `story.*`/`style.patch`; Aetheria proves the daemon command boundary; the web lowerer resolves authored `action.command` through the shared Eve command diff --git a/tools/parity/parity-manifest.json b/tools/parity/parity-manifest.json index 20f7fe6..112bffc 100644 --- a/tools/parity/parity-manifest.json +++ b/tools/parity/parity-manifest.json @@ -192,6 +192,35 @@ "commandReferencesDeclared": true } }, + { + "id": "authority-staleness", + "title": "Authority And Staleness Witness", + "pack": "core", + "ownerRepo": "Eve", + "surface": { + "transport": "local-json", + "path": "web/fixtures/authority-staleness-witness.json" + }, + "expect": { + "providerId": "gamecult.eve.authority-witness", + "componentKinds": ["surface", "card", "text", "metric", "partition"], + "styleTokens": ["colorBackground", "colorPanel", "colorText", "colorMuted", "colorAccent", "colorWarning", "colorDanger"], + "bindings": [ + "authority.temperature.accepted", + "authority.sensor.echo", + "authority.missing.field", + "authority.orbit.predicted", + "authority.orbit.accepted" + ], + "commandDescriptors": ["authority.fixture.patch"], + "commandDescriptorSchema": "gamecult.eve.command.v1", + "commandReferencesDeclared": true, + "authorityStates": ["accepted", "denied", "fresh", "missing", "pending", "predicted", "reconciled", "stale"], + "authorityOwners": ["provider", "renderer"], + "witnessRefs": 8, + "receiptRefs": 2 + } + }, { "id": "reactive-dsl", "title": "Reactive DSL", diff --git a/tools/parity/run-parity.mjs b/tools/parity/run-parity.mjs index a061a4c..8b708f7 100644 --- a/tools/parity/run-parity.mjs +++ b/tools/parity/run-parity.mjs @@ -70,6 +70,11 @@ async function evaluateFixture(fixture) { const commandIds = commandDescriptors.map(command => command.command).sort(); const commandReferences = readCommandReferences(nodes); const commandDescriptorErrors = await validateCommandDescriptors(fixture, commandDescriptors); + const authorityWitnesses = readAuthorityWitnesses(nodes); + const authorityStates = [...new Set(authorityWitnesses.map(witness => witness.state))].filter(Boolean).sort(); + const authorityOwners = [...new Set(authorityWitnesses.map(witness => witness.owner))].filter(Boolean).sort(); + const receiptRefs = [...new Set(authorityWitnesses.map(witness => witness.receiptRef))].filter(Boolean).sort(); + const witnessRefs = [...new Set(authorityWitnesses.map(witness => witness.witnessRef))].filter(Boolean).sort(); const checks = []; addCheck(checks, "providerId", state.providerId === fixture.expect.providerId, { @@ -149,6 +154,34 @@ async function evaluateFixture(fixture) { } } + for (const state of fixture.expect.authorityStates || []) { + addCheck(checks, `authorityState:${state}`, authorityStates.includes(state), { + expected: "present", + actual: authorityStates, + }); + } + + for (const owner of fixture.expect.authorityOwners || []) { + addCheck(checks, `authorityOwner:${owner}`, authorityOwners.includes(owner), { + expected: "present", + actual: authorityOwners, + }); + } + + if (fixture.expect.receiptRefs) { + addCheck(checks, "receiptRefs", receiptRefs.length >= fixture.expect.receiptRefs, { + expected: fixture.expect.receiptRefs, + actual: receiptRefs.length, + }); + } + + if (fixture.expect.witnessRefs) { + addCheck(checks, "witnessRefs", witnessRefs.length >= fixture.expect.witnessRefs, { + expected: fixture.expect.witnessRefs, + actual: witnessRefs.length, + }); + } + const knownPacks = new Set((manifest.conformancePacks || []).map(pack => pack.id)); if (fixture.pack) { addCheck(checks, "conformancePack", knownPacks.has(fixture.pack), { @@ -181,6 +214,11 @@ async function evaluateFixture(fixture) { commands: commandIds, commandReferences, commandDescriptorErrors, + authorityStates, + authorityOwners, + receiptRefs, + witnessRefs, + authorityWitnesses, checks, }; } @@ -552,6 +590,26 @@ function readCommandReferences(nodes) { }).sort((a, b) => `${a.nodeId}:${a.command}`.localeCompare(`${b.nodeId}:${b.command}`)); } +function readAuthorityWitnesses(nodes) { + return nodes.flatMap(node => { + const props = node.props || {}; + const freshness = objectProps(props.freshness); + const state = firstNonEmptyString(props.authorityState, props.truthState, props.commandState, freshness.state); + const owner = firstNonEmptyString(props.authorityOwner, props.owner, props.authority); + const witnessRef = firstNonEmptyString(props.witnessRef, props.sourceId); + const receiptRef = firstNonEmptyString(props.receiptRef, props.receiptId); + if (!state && !owner && !witnessRef && !receiptRef) return []; + return [{ + nodeId: node.id || "", + kind: node.kind || "", + state, + owner, + witnessRef, + receiptRef, + }]; + }).sort((a, b) => `${a.nodeId}:${a.state}`.localeCompare(`${b.nodeId}:${b.state}`)); +} + function firstNonEmptyString(...values) { for (const value of values) { if (typeof value === "string" && value.trim()) return value; @@ -651,6 +709,11 @@ function renderMarkdown(report) { lines.push(`| ${fixture.title} | ${fixture.commands.join(", ")} | ${refs} | ${fixture.commandDescriptorErrors.join(", ")} |`); } + lines.push("", "## Authority Witnesses", "", "| Fixture | States | Owners | Witness Refs | Receipt Refs |", "| --- | --- | --- | ---: | ---: |"); + for (const fixture of report.fixtures) { + lines.push(`| ${fixture.title} | ${fixture.authorityStates.join(", ")} | ${fixture.authorityOwners.join(", ")} | ${fixture.witnessRefs.length} | ${fixture.receiptRefs.length} |`); + } + lines.push("", "## Plugins", "", "| Plugin | Status | Owner | Capabilities | Missing |", "| --- | --- | --- | --- | --- |"); for (const plugin of report.plugins || []) { const missing = [ diff --git a/web/fixtures/authority-staleness-witness.json b/web/fixtures/authority-staleness-witness.json new file mode 100644 index 0000000..4bd02c4 --- /dev/null +++ b/web/fixtures/authority-staleness-witness.json @@ -0,0 +1,198 @@ +{ + "type": "surface-state", + "schema": "gamecult.eve.surface.v1", + "providerId": "gamecult.eve.authority-witness", + "providerKind": "surface.fixture", + "title": "Authority And Staleness Witness", + "version": 1, + "updatedAtUtc": "2026-07-08T00:00:00Z", + "surface": { + "id": "gamecult.eve.authority-witness", + "root": { + "id": "authority.root", + "kind": "surface", + "props": { + "authorityModel": "provider-truth-with-local-prediction" + }, + "children": [ + { + "id": "authority.summary", + "kind": "card", + "props": { + "title": "Truth Boundary", + "authorityOwner": "provider", + "authorityState": "fresh", + "freshness": { + "state": "fresh", + "lastSeenAtUtc": "2026-07-08T00:00:00Z" + }, + "witnessRef": "cultmesh://eve/fixtures/authority/fresh" + }, + "children": [ + { + "id": "authority.summary.text", + "kind": "text", + "props": { + "text": "Provider state is the accepted truth. Eve may preview local pressure, but preview is not authority." + }, + "children": [] + } + ] + }, + { + "id": "authority.grid", + "kind": "partition", + "props": { + "direction": "vertical" + }, + "children": [ + { + "id": "authority.field.fresh", + "kind": "metric", + "props": { + "label": "Accepted Temperature", + "value": "298 K", + "bind": "authority.temperature.accepted", + "authorityOwner": "provider", + "authorityState": "accepted", + "freshness": { + "state": "fresh", + "lastSeenAtUtc": "2026-07-08T00:00:00Z" + }, + "witnessRef": "cultmesh://eve/fixtures/authority/temperature" + }, + "children": [] + }, + { + "id": "authority.field.stale", + "kind": "metric", + "props": { + "label": "Sensor Echo", + "value": "14.2 m/s", + "bind": "authority.sensor.echo", + "authorityOwner": "provider", + "authorityState": "stale", + "freshness": { + "state": "stale", + "lastSeenAtUtc": "2026-07-08T00:00:00Z", + "maxAgeMs": 5000 + }, + "witnessRef": "cultmesh://eve/fixtures/authority/sensor" + }, + "children": [] + }, + { + "id": "authority.field.missing", + "kind": "metric", + "props": { + "label": "Missing Provider Field", + "value": "unresolved", + "bind": "authority.missing.field", + "authorityOwner": "provider", + "authorityState": "missing", + "freshness": { + "state": "missing" + }, + "witnessRef": "cultmesh://eve/fixtures/authority/missing" + }, + "children": [] + }, + { + "id": "authority.command.pending", + "kind": "card", + "props": { + "title": "Local Command Pressure", + "authorityOwner": "renderer", + "authorityState": "pending", + "commandId": "authority.fixture.patch", + "commandBoundary": "provider-receipt-required", + "witnessRef": "cultmesh://eve/fixtures/authority/pending" + }, + "children": [ + { + "id": "authority.command.pending.text", + "kind": "text", + "props": { + "text": "The renderer can show the pending intent. It cannot mark the provider value accepted." + }, + "children": [] + } + ] + }, + { + "id": "authority.command.denied", + "kind": "card", + "props": { + "title": "Denied Intent", + "authorityOwner": "provider", + "authorityState": "denied", + "receiptRef": "cultmesh://eve/fixtures/authority/receipts/denied", + "witnessRef": "cultmesh://eve/fixtures/authority/denied" + }, + "children": [ + { + "id": "authority.command.denied.text", + "kind": "text", + "props": { + "text": "Denied commands remain visible so local preview cannot erase provider refusal." + }, + "children": [] + } + ] + }, + { + "id": "authority.prediction", + "kind": "metric", + "props": { + "label": "Predicted Orbit", + "value": "0.74 AU", + "bind": "authority.orbit.predicted", + "authorityOwner": "renderer", + "authorityState": "predicted", + "reconcilesWith": "authority.orbit.accepted", + "witnessRef": "cultmesh://eve/fixtures/authority/predicted" + }, + "children": [] + }, + { + "id": "authority.reconciled", + "kind": "metric", + "props": { + "label": "Reconciled Orbit", + "value": "0.72 AU", + "bind": "authority.orbit.accepted", + "authorityOwner": "provider", + "authorityState": "reconciled", + "reconciledFrom": "authority.orbit.predicted", + "receiptRef": "cultmesh://eve/fixtures/authority/receipts/reconciled", + "witnessRef": "cultmesh://eve/fixtures/authority/reconciled" + }, + "children": [] + } + ] + } + ] + }, + "styles": { + "tokens": { + "colorBackground": "#0B1114", + "colorPanel": "#162022", + "colorText": "#F2F7F1", + "colorMuted": "#9AA8A2", + "colorAccent": "#78D6A3", + "colorWarning": "#D6B24A", + "colorDanger": "#C95D63" + } + } + }, + "commands": [ + { + "schema": "gamecult.eve.command.v1", + "command": "authority.fixture.patch", + "label": "Patch witness value", + "transport": "fixture-receipt", + "authority": "provider", + "result": "accepted-denied-or-reconciled" + } + ] +} diff --git a/web/surface.js b/web/surface.js index 2b54cba..fd9c92b 100644 --- a/web/surface.js +++ b/web/surface.js @@ -61,6 +61,13 @@ const localProviders = [ freshness: { state: "fixture" }, surfaces: [{ transport: "local-eve-dsl", surfaceId: "eve.reactive.dsl", url: "./fixtures/reactive-composition.eve" }], }, + { + providerId: "gamecult.eve.authority-witness", + title: "Authority Witness", + kind: "surface.fixture", + freshness: { state: "fixture" }, + surfaces: [{ transport: "local-json", surfaceId: "gamecult.eve.authority-witness", url: "./fixtures/authority-staleness-witness.json" }], + }, { providerId: "eve.cultui.inspector", title: "CultUI Inspector",