feat(foundry): close the operational map data loop

This commit is contained in:
Codex
2026-07-20 20:45:05 +03:00
parent aac44d057f
commit a02c3ff3dd
44 changed files with 4158 additions and 811 deletions
+115
View File
@@ -0,0 +1,115 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
import ts from "typescript";
const source = await readFile(new URL("../apps/catalog/src/mapPresentationProfile.ts", import.meta.url), "utf8");
const transpiled = ts.transpileModule(source, {
compilerOptions: { module: ts.ModuleKind.ESNext, target: ts.ScriptTarget.ES2022 },
}).outputText;
const moduleUrl = `data:text/javascript;base64,${Buffer.from(transpiled).toString("base64")}`;
const {
mapFactMatchesFilters,
mapPresentationBindingIsAll,
mapPresentationFacetCounts,
toggleMapPresentationFacetSelection,
} = await import(moduleUrl);
const profile = {
id: "map.moving-object.operational.default",
facets: [
{
field: "signal_state",
values: [{ value: "active" }, { value: "inactive" }],
},
{
field: "movement_state",
values: [{ value: "moving" }, { value: "stopped" }],
},
],
};
const onlineMoving = {
attributes: { signal_state: "active", movement_state: "moving" },
};
test("missing subject state is explicit all for that binding", () => {
assert.equal(mapPresentationBindingIsAll("fleet", {}), true);
assert.equal(mapFactMatchesFilters(onlineMoving, profile, {}, "fleet"), true);
});
test("a selected chip restricts only its own facet", () => {
const filters = { fleet: { visible: true, facets: { signal_state: ["active"] } } };
assert.equal(mapFactMatchesFilters(onlineMoving, profile, filters, "fleet"), true);
assert.equal(mapFactMatchesFilters({ attributes: { signal_state: "inactive", movement_state: "moving" } }, profile, filters, "fleet"), false);
});
test("deselecting the last chip remains empty and never normalizes to all", () => {
const filters = { fleet: { visible: true, facets: { signal_state: [] } } };
assert.equal(mapPresentationBindingIsAll("fleet", filters), false);
assert.equal(mapFactMatchesFilters(onlineMoving, profile, filters, "fleet"), false);
});
test("interactive deselect of the last chip removes the facet constraint", () => {
const selected = toggleMapPresentationFacetSelection({}, "signal_state", "active");
assert.deepEqual(selected, { signal_state: ["active"] });
const unconstrained = toggleMapPresentationFacetSelection(selected, "signal_state", "active");
assert.deepEqual(unconstrained, {});
assert.equal(mapPresentationBindingIsAll("fleet", { fleet: { visible: true, facets: unconstrained } }), true);
});
test("interactive deselect preserves other values and facet constraints", () => {
const facets = {
signal_state: ["active", "inactive"],
movement_state: ["moving"],
};
assert.deepEqual(toggleMapPresentationFacetSelection(facets, "signal_state", "active"), {
signal_state: ["inactive"],
movement_state: ["moving"],
});
});
test("chips from different facets form one global union", () => {
const filters = {
fleet: {
visible: true,
facets: {
signal_state: ["active"],
movement_state: ["moving"],
},
},
};
assert.equal(mapFactMatchesFilters(onlineMoving, profile, filters, "fleet"), true);
assert.equal(mapFactMatchesFilters({ attributes: { signal_state: "active", movement_state: "stopped" } }, profile, filters, "fleet"), true);
assert.equal(mapFactMatchesFilters({ attributes: { signal_state: "inactive", movement_state: "moving" } }, profile, filters, "fleet"), false);
});
test("movement filters and counters ignore stale movement on inactive subjects", () => {
const activeStopped = { attributes: { signal_state: "active", movement_state: "stopped" } };
const inactiveMoving = { attributes: { signal_state: "inactive", movement_state: "moving" } };
const inactiveStopped = { attributes: { signal_state: "inactive", movement_state: "stopped" } };
const filters = { fleet: { visible: true, facets: { movement_state: ["moving"] } } };
assert.equal(mapFactMatchesFilters(onlineMoving, profile, filters, "fleet"), true);
assert.equal(mapFactMatchesFilters(inactiveMoving, profile, filters, "fleet"), false);
assert.deepEqual(mapPresentationFacetCounts([
onlineMoving,
activeStopped,
inactiveMoving,
inactiveStopped,
], profile), {
signal_state: { active: 2, inactive: 2 },
movement_state: { moving: 1, stopped: 1 },
});
});
test("explicitly disabled All hides only the selected binding", () => {
const filters = {
fleet: { visible: false, facets: {} },
service: { visible: true, facets: {} },
};
assert.equal(mapFactMatchesFilters(onlineMoving, profile, filters, "fleet"), false);
assert.equal(mapFactMatchesFilters(onlineMoving, profile, filters, "service"), true);
});
+4 -2
View File
@@ -323,10 +323,12 @@ try {
return payload.result;
};
const initialized = await mcpRequest("initialize", { protocolVersion: "2025-06-18", capabilities: {}, clientInfo: { name: "runtime-smoke", version: "1" } });
assert.equal(initialized.serverInfo.version, "0.2.0");
assert.equal(initialized.serverInfo.version, "0.5.0");
const listedTools = await mcpRequest("tools/list");
assert.equal(listedTools.tools.length, 13);
assert.equal(listedTools.tools.length, 17);
assert.ok(listedTools.tools.some((tool) => tool.name === "foundry_apply_map_data_product_consumer"));
assert.ok(listedTools.tools.some((tool) => tool.name === "foundry_save_map_page_view_state"));
assert.ok(listedTools.tools.some((tool) => tool.name === "foundry_remove_map_pin_binding"));
const workloadHeaders = {
authorization: `Bearer ${bindingGrantToken}`,
+47 -1
View File
@@ -15,6 +15,8 @@ const applicationManifestSchema = await parse("registry/schemas/application-mani
const designProfileSchema = await parse("registry/schemas/design-profile-v0.1.schema.json");
const mapSceneFixtureSchema = await parse("registry/schemas/map-scene-fixture-v0.1.schema.json");
const mapLodPolicySchema = await parse("registry/schemas/map-lod-policy-v0.1.schema.json");
const mapPresentationProfiles = await parse("registry/map-presentation-profiles.json");
const dataProductConsumerPolicies = await parse("registry/data-product-consumer-policies.json");
const failures = [];
const requireValue = (condition, message) => {
@@ -26,7 +28,13 @@ requireValue(registry.libraryVersion, "registry libraryVersion is required");
requireValue(Array.isArray(registry.packages) && registry.packages.length >= 4, "registry must list all packages");
requireValue(Array.isArray(sources.sources) && sources.sources.length >= 6, "source audit is incomplete");
requireValue(Array.isArray(components.components) && components.components.length >= 10, "component registry is incomplete");
requireValue(Array.isArray(candidates.candidates) && candidates.candidates.length >= 10, "candidate registry is incomplete");
requireValue(Array.isArray(candidates.candidates) && candidates.candidates.length >= 1, "candidate registry is incomplete");
requireValue(
Array.isArray(components.components)
&& Array.isArray(candidates.candidates)
&& components.components.length + candidates.candidates.length >= 20,
"component inventory is incomplete",
);
requireValue(Array.isArray(icons.groups) && icons.groups.length >= 5, "icon registry is incomplete");
requireValue(pages.schemaVersion === "0.1.0", "page registry schemaVersion must be 0.1.0");
requireValue(Array.isArray(pages.templates) && pages.templates.length >= 1, "page registry must contain at least one template");
@@ -34,10 +42,48 @@ requireValue(registry.applicationManifestSchema === "schemas/application-manifes
requireValue(registry.designProfileSchema === "schemas/design-profile-v0.1.schema.json", "design profile schema must be registered");
requireValue(registry.mapSceneFixtureSchema === "schemas/map-scene-fixture-v0.1.schema.json", "map scene fixture schema must be registered");
requireValue(registry.mapLodPolicySchema === "schemas/map-lod-policy-v0.1.schema.json", "map LOD policy schema must be registered");
requireValue(registry.mapPresentationProfileRegistry === "map-presentation-profiles.json", "map presentation profile registry must be registered");
requireValue(applicationManifestSchema.properties?.schemaVersion?.const === "0.1.0", "application manifest schema version must be 0.1.0");
requireValue(designProfileSchema.properties?.schemaVersion?.const === "0.1.0", "design profile schema version must be 0.1.0");
requireValue(mapSceneFixtureSchema.properties?.schemaVersion?.const === "0.1.0", "map scene fixture schema version must be 0.1.0");
requireValue(mapLodPolicySchema.properties?.schemaVersion?.const === "0.1.0", "map LOD policy schema version must be 0.1.0");
requireValue(mapPresentationProfiles.schemaVersion === "nodedc.map-presentation-profiles/v1", "map presentation profile registry schema is unsupported");
requireValue(Array.isArray(mapPresentationProfiles.profiles) && mapPresentationProfiles.profiles.length >= 1, "map presentation profiles are missing");
requireValue(dataProductConsumerPolicies.schemaVersion === "nodedc.foundry.data-product-consumer-policies/v1", "data product consumer policy registry schema is unsupported");
requireValue(Array.isArray(dataProductConsumerPolicies.policies) && dataProductConsumerPolicies.policies.length >= 1, "data product consumer policies are missing");
const consumerPolicies = dataProductConsumerPolicies.policies ?? [];
const consumerPolicyIds = consumerPolicies.map((policy) => `${policy.id}@${policy.version}`);
const consumerPolicyProducts = consumerPolicies.map((policy) => `${policy.dataProductId}@${policy.productVersion}`);
requireValue(new Set(consumerPolicyIds).size === consumerPolicyIds.length, "data product consumer policy id/version pairs must be unique");
requireValue(new Set(consumerPolicyProducts).size === consumerPolicyProducts.length, "data product consumer policy product/version pairs must be unique");
for (const policy of consumerPolicies) {
const statusContract = policy.statusContract;
requireValue(typeof policy.id === "string" && /^[a-z0-9._-]{1,160}$/.test(policy.id), `invalid data product consumer policy id: ${policy.id ?? "unknown"}`);
requireValue(typeof policy.version === "string" && /^\d+\.\d+\.\d+$/.test(policy.version), `invalid data product consumer policy version: ${policy.id ?? "unknown"}`);
requireValue(typeof policy.dataProductId === "string" && /^[A-Za-z0-9._:-]{1,160}$/.test(policy.dataProductId), `invalid data product consumer policy product: ${policy.id ?? "unknown"}`);
requireValue(typeof policy.productVersion === "string" && /^\d+\.\d+\.\d+$/.test(policy.productVersion), `invalid data product consumer product version: ${policy.id ?? "unknown"}`);
requireValue(Array.isArray(policy.terminalStatuses) && policy.terminalStatuses.every((value) => typeof value === "string" && /^[a-z0-9_-]{1,64}$/.test(value)), `invalid terminal statuses: ${policy.id ?? "unknown"}`);
requireValue(policy.removeMode === "canonical-tombstone-or-snapshot-rebase", `invalid remove mode: ${policy.id ?? "unknown"}`);
if (statusContract === undefined) {
requireValue(Number.isInteger(policy.staleAfterMs) && policy.staleAfterMs >= 1_000 && policy.staleAfterMs <= 7 * 24 * 60 * 60 * 1000, `invalid stale window: ${policy.id ?? "unknown"}`);
continue;
}
requireValue(statusContract && typeof statusContract === "object" && !Array.isArray(statusContract), `invalid status contract: ${policy.id ?? "unknown"}`);
requireValue(typeof statusContract?.attribute === "string" && /^[A-Za-z][A-Za-z0-9_.-]{0,127}$/.test(statusContract.attribute), `invalid status attribute: ${policy.id ?? "unknown"}`);
requireValue(Array.isArray(statusContract?.allowedValues) && statusContract.allowedValues.length >= 1 && new Set(statusContract.allowedValues).size === statusContract.allowedValues.length, `invalid allowed status values: ${policy.id ?? "unknown"}`);
requireValue(statusContract?.allowedValues?.every((value) => typeof value === "string" && /^[a-z0-9_-]{1,64}$/.test(value)), `invalid allowed status value: ${policy.id ?? "unknown"}`);
requireValue(statusContract?.missing === "reject", `status contract must reject missing values: ${policy.id ?? "unknown"}`);
requireValue(["none", "observed-at"].includes(statusContract?.freshness), `invalid status freshness mode: ${policy.id ?? "unknown"}`);
requireValue(statusContract?.freshness === "none" ? policy.staleAfterMs === null : Number.isInteger(policy.staleAfterMs), `status freshness/stale window mismatch: ${policy.id ?? "unknown"}`);
requireValue(policy.terminalStatuses.every((value) => statusContract.allowedValues.includes(value)), `terminal status is outside the closed value contract: ${policy.id ?? "unknown"}`);
}
const movingObjectV4Policy = consumerPolicies.find((policy) => policy.dataProductId === "fleet.positions.current.v4" && policy.productVersion === "4.0.0");
requireValue(movingObjectV4Policy?.id === "map-moving-object-current-v4", "fleet.positions.current.v4 consumer policy is missing");
requireValue(movingObjectV4Policy?.statusContract?.attribute === "signal_state", "fleet.positions.current.v4 status source must be signal_state");
requireValue(JSON.stringify(movingObjectV4Policy?.statusContract?.allowedValues) === JSON.stringify(["active", "inactive"]), "fleet.positions.current.v4 signal states must remain closed active/inactive");
requireValue(movingObjectV4Policy?.statusContract?.freshness === "none" && movingObjectV4Policy?.staleAfterMs === null, "fleet.positions.current.v4 must not invent freshness states");
const ids = components.components.map((component) => component.id);
requireValue(new Set(ids).size === ids.length, "component ids must be unique");