feat(lab): visualize semantic SLAM shadow

This commit is contained in:
DCCONSTRUCTIONS
2026-08-06 11:26:46 +03:00
parent 8eaa3ab497
commit 81ae4425cb
18 changed files with 1747 additions and 42 deletions
@@ -1,5 +1,6 @@
import {
forwardRef,
type CSSProperties,
useEffect,
useImperativeHandle,
useRef,
@@ -8,6 +9,13 @@ import {
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import {
recordedEvidenceSemanticCssColor,
resolveRecordedEvidenceSemanticRgb,
type RecordedEvidenceSemanticClass,
type RecordedEvidenceSemanticPaletteEntry,
} from "./RecordedEvidenceSemanticMaskOverlay";
export type LaboratoryMetricPoint3 = readonly [number, number, number];
export type LaboratoryMetricDecision = "threat" | "not-threat" | "unknown";
export type LaboratoryMetricSceneMode = "3d" | "plan";
@@ -114,6 +122,9 @@ LaboratoryMetricEvidenceSceneHandle,
showCurrentIncrement: boolean;
showLocalSurface: boolean;
showRollingMap: boolean;
pointSemanticClassIds?: readonly (number | null)[];
semanticClasses?: readonly RecordedEvidenceSemanticClass[];
semanticPalette?: readonly RecordedEvidenceSemanticPaletteEntry[];
}
>(function LaboratoryMetricEvidenceScene({
pointCloudBodyXyzM,
@@ -127,6 +138,9 @@ LaboratoryMetricEvidenceSceneHandle,
showCurrentIncrement,
showLocalSurface,
showRollingMap,
pointSemanticClassIds,
semanticClasses,
semanticPalette,
}, ref) {
const hostRef = useRef<HTMLDivElement | null>(null);
const sceneRef = useRef<THREE.Scene | null>(null);
@@ -240,10 +254,40 @@ LaboratoryMetricEvidenceSceneHandle,
"position",
new THREE.BufferAttribute(positions(pointCloudBodyXyzM), 3),
);
const hasAlignedSemanticClasses =
pointSemanticClassIds !== undefined
&& pointSemanticClassIds.length === pointCloudBodyXyzM.length
&& semanticClasses !== undefined
&& semanticPalette !== undefined;
if (hasAlignedSemanticClasses) {
const declaredIds = new Set(semanticClasses.map((item) => item.id));
const colorsByClassId = new Map<number, readonly [number, number, number]>();
for (const entry of semanticPalette) {
if (!declaredIds.has(entry.classId)) continue;
const rgb = resolveRecordedEvidenceSemanticRgb(host, entry.color);
if (rgb) colorsByClassId.set(entry.classId, rgb);
}
const context = tokenColor(host, "--nodedc-text-muted", [147, 151, 159]);
const pointColors = new Float32Array(pointCloudBodyXyzM.length * 3);
pointSemanticClassIds.forEach((classId, index) => {
const rgb = classId === null ? undefined : colorsByClassId.get(classId);
const offset = index * 3;
pointColors[offset] = rgb ? rgb[0] / 255 : context.r;
pointColors[offset + 1] = rgb ? rgb[1] / 255 : context.g;
pointColors[offset + 2] = rgb ? rgb[2] / 255 : context.b;
});
contextGeometry.setAttribute(
"color",
new THREE.BufferAttribute(pointColors, 3),
);
}
content.add(new THREE.Points(
contextGeometry,
new THREE.PointsMaterial({
color: tokenColor(host, "--nodedc-text-muted", [147, 151, 159]),
color: hasAlignedSemanticClasses
? new THREE.Color(1, 1, 1)
: tokenColor(host, "--nodedc-text-muted", [147, 151, 159]),
vertexColors: hasAlignedSemanticClasses,
size: 1.55,
sizeAttenuation: false,
transparent: true,
@@ -321,6 +365,9 @@ LaboratoryMetricEvidenceSceneHandle,
occupiedVoxelSizeM,
localSurfaceBodyXyzM,
pointCloudBodyXyzM,
pointSemanticClassIds,
semanticClasses,
semanticPalette,
showCurrentIncrement,
showLocalSurface,
showRollingMap,
@@ -411,6 +458,27 @@ LaboratoryMetricEvidenceSceneHandle,
useEffect(resetView, [corridor.forwardLengthM, mode]);
useImperativeHandle(ref, () => ({ resetView }));
const semanticLegendEntries = (() => {
if (
!showCurrentIncrement
|| !pointSemanticClassIds
|| pointSemanticClassIds.length !== pointCloudBodyXyzM.length
|| !semanticClasses
|| !semanticPalette
) return [];
const presentIds = new Set(pointSemanticClassIds.filter((item): item is number => item !== null));
const classesById = new Map(semanticClasses.map((item) => [item.id, item]));
return semanticPalette.flatMap((entry) => {
const semanticClass = classesById.get(entry.classId);
if (!semanticClass || !presentIds.has(entry.classId) || entry.color.kind === "transparent") return [];
return [{
id: entry.classId,
label: semanticClass.label,
cssColor: recordedEvidenceSemanticCssColor(entry.color),
}];
});
})();
return (
<div className="laboratory-metric-evidence-scene">
<div ref={hostRef} className="laboratory-metric-evidence-scene__viewport">
@@ -423,6 +491,15 @@ LaboratoryMetricEvidenceSceneHandle,
<span data-decision="context">Current increment</span>
<span data-decision="local-surface">Local SLAM surface</span>
<span data-decision="rolling">Rolling-map occupied</span>
{semanticLegendEntries.map((entry) => (
<span
key={entry.id}
data-decision="semantic"
style={{ "--laboratory-metric-legend-color": entry.cssColor } as CSSProperties}
>
{entry.label}
</span>
))}
</div>
</div>
);
@@ -5,18 +5,24 @@ import {
RecordedEvidenceBoxOverlay,
type RecordedEvidenceBox,
} from "./RecordedEvidenceBoxOverlay";
import {
RecordedEvidenceSemanticMaskOverlay,
type RecordedEvidenceSemanticOverlay,
} from "./RecordedEvidenceSemanticMaskOverlay";
export function RecordedEvidenceImageScene({
src,
imageWidth,
imageHeight,
boxes,
semanticOverlay,
ariaLabel,
}: {
src: string;
imageWidth: number;
imageHeight: number;
boxes: readonly RecordedEvidenceBox[];
semanticOverlay?: RecordedEvidenceSemanticOverlay;
ariaLabel: string;
}) {
const [state, setState] = useState<"loading" | "ready" | "error">("loading");
@@ -33,12 +39,21 @@ export function RecordedEvidenceImageScene({
onError={() => setState("error")}
/>
{state === "ready" ? (
<>
{semanticOverlay ? (
<RecordedEvidenceSemanticMaskOverlay
{...semanticOverlay}
imageWidth={imageWidth}
imageHeight={imageHeight}
/>
) : null}
<RecordedEvidenceBoxOverlay
imageWidth={imageWidth}
imageHeight={imageHeight}
boxes={boxes}
ariaLabel={ariaLabel}
/>
</>
) : (
<div className="l3-visual-audit__state" role="status">
{state === "loading" ? (
@@ -0,0 +1,391 @@
import { useEffect, useRef, useState } from "react";
export interface RecordedEvidenceSemanticClass {
id: number;
label: string;
}
export type RecordedEvidenceSemanticToken =
| "--nodedc-accent-rgb"
| "--nodedc-danger-rgb"
| "--nodedc-foreground-rgb"
| "--nodedc-success-rgb"
| "--nodedc-text-muted"
| "--nodedc-warning-rgb";
export type RecordedEvidenceSemanticColor =
| {
kind: "token";
token: RecordedEvidenceSemanticToken;
}
| {
kind: "diagnostic";
rgb: readonly [number, number, number];
}
| {
kind: "transparent";
};
export interface RecordedEvidenceSemanticPaletteEntry {
classId: number;
color: RecordedEvidenceSemanticColor;
opacity?: number;
}
export interface RecordedEvidenceSemanticOverlay {
src: string;
classes: readonly RecordedEvidenceSemanticClass[];
palette: readonly RecordedEvidenceSemanticPaletteEntry[];
opacity?: number;
ariaLabel: string;
}
interface DecodedSemanticMask {
key: string;
width: number;
height: number;
classIds: Uint8Array;
}
interface PendingSemanticMask {
controller: AbortController;
subscribers: number;
promise: Promise<DecodedSemanticMask>;
}
const MASK_CACHE_LIMIT = 48;
const decodedMaskCache = new Map<string, DecodedSemanticMask>();
const pendingMaskCache = new Map<string, PendingSemanticMask>();
const TOKEN_FALLBACKS: Record<RecordedEvidenceSemanticToken, readonly [number, number, number]> = {
"--nodedc-accent-rgb": [232, 56, 126],
"--nodedc-danger-rgb": [255, 104, 112],
"--nodedc-foreground-rgb": [245, 245, 245],
"--nodedc-success-rgb": [181, 255, 90],
"--nodedc-text-muted": [147, 151, 159],
"--nodedc-warning-rgb": [255, 197, 92],
};
function clampChannel(value: number): number {
return Math.max(0, Math.min(255, Math.round(value)));
}
function clampOpacity(value: number | undefined, fallback: number): number {
return Math.max(0, Math.min(1, Number.isFinite(value) ? Number(value) : fallback));
}
function semanticMaskKey(src: string, width: number, height: number): string {
return `${width}x${height}:${src}`;
}
function rememberMask(mask: DecodedSemanticMask): void {
decodedMaskCache.delete(mask.key);
decodedMaskCache.set(mask.key, mask);
while (decodedMaskCache.size > MASK_CACHE_LIMIT) {
const oldest = decodedMaskCache.keys().next().value;
if (typeof oldest !== "string") break;
decodedMaskCache.delete(oldest);
}
}
async function imageSourceFromBlob(
blob: Blob,
signal: AbortSignal,
): Promise<{ source: CanvasImageSource; width: number; height: number; release: () => void }> {
if (typeof createImageBitmap === "function") {
const bitmap = await createImageBitmap(blob, {
colorSpaceConversion: "none",
premultiplyAlpha: "none",
});
if (signal.aborted) {
bitmap.close();
throw new DOMException("Aborted", "AbortError");
}
return {
source: bitmap,
width: bitmap.width,
height: bitmap.height,
release: () => bitmap.close(),
};
}
const objectUrl = URL.createObjectURL(blob);
const image = new Image();
image.decoding = "async";
try {
await new Promise<void>((resolve, reject) => {
const cleanup = () => {
image.removeEventListener("load", onLoad);
image.removeEventListener("error", onError);
signal.removeEventListener("abort", onAbort);
};
const onLoad = () => {
cleanup();
resolve();
};
const onError = () => {
cleanup();
reject(new Error("Semantic mask image decode failed"));
};
const onAbort = () => {
cleanup();
reject(new DOMException("Aborted", "AbortError"));
};
image.addEventListener("load", onLoad, { once: true });
image.addEventListener("error", onError, { once: true });
signal.addEventListener("abort", onAbort, { once: true });
image.src = objectUrl;
});
return {
source: image,
width: image.naturalWidth,
height: image.naturalHeight,
release: () => URL.revokeObjectURL(objectUrl),
};
} catch (error) {
URL.revokeObjectURL(objectUrl);
throw error;
}
}
async function decodeSemanticMask(
key: string,
src: string,
expectedWidth: number,
expectedHeight: number,
signal: AbortSignal,
): Promise<DecodedSemanticMask> {
const response = await fetch(src, { cache: "force-cache", signal });
if (!response.ok) throw new Error(`Semantic mask request failed: ${response.status}`);
const blob = await response.blob();
if (signal.aborted) throw new DOMException("Aborted", "AbortError");
const decoded = await imageSourceFromBlob(blob, signal);
try {
if (decoded.width !== expectedWidth || decoded.height !== expectedHeight) {
throw new Error(
`Semantic mask dimensions ${decoded.width}x${decoded.height} do not match ${expectedWidth}x${expectedHeight}`,
);
}
const decodeCanvas = document.createElement("canvas");
decodeCanvas.width = decoded.width;
decodeCanvas.height = decoded.height;
const context = decodeCanvas.getContext("2d", { willReadFrequently: true });
if (!context) throw new Error("Semantic mask canvas is unavailable");
context.drawImage(decoded.source, 0, 0);
const rgba = context.getImageData(0, 0, decoded.width, decoded.height).data;
const classIds = new Uint8Array(decoded.width * decoded.height);
for (let sourceOffset = 0, targetOffset = 0; targetOffset < classIds.length; sourceOffset += 4, targetOffset += 1) {
const classId = rgba[sourceOffset] ?? 0;
if (rgba[sourceOffset + 1] !== classId || rgba[sourceOffset + 2] !== classId) {
throw new Error("Semantic mask must be an 8-bit grayscale class-id PNG");
}
classIds[targetOffset] = classId;
}
return { key, width: decoded.width, height: decoded.height, classIds };
} finally {
decoded.release();
}
}
function subscribeToSemanticMask(
src: string,
width: number,
height: number,
): { promise: Promise<DecodedSemanticMask>; release: () => void } {
const key = semanticMaskKey(src, width, height);
const cached = decodedMaskCache.get(key);
if (cached) {
decodedMaskCache.delete(key);
decodedMaskCache.set(key, cached);
return { promise: Promise.resolve(cached), release: () => undefined };
}
let pending = pendingMaskCache.get(key);
if (!pending) {
const controller = new AbortController();
let next: PendingSemanticMask;
const promise = decodeSemanticMask(key, src, width, height, controller.signal)
.then((mask) => {
rememberMask(mask);
return mask;
})
.finally(() => {
if (pendingMaskCache.get(key) === next) pendingMaskCache.delete(key);
});
next = { controller, subscribers: 0, promise };
pendingMaskCache.set(key, next);
pending = next;
}
pending.subscribers += 1;
let released = false;
return {
promise: pending.promise,
release: () => {
if (released) return;
released = true;
pending!.subscribers -= 1;
if (pending!.subscribers > 0 || decodedMaskCache.has(key)) return;
pending!.controller.abort();
if (pendingMaskCache.get(key) === pending) pendingMaskCache.delete(key);
},
};
}
export function recordedEvidenceSemanticCssColor(
color: RecordedEvidenceSemanticColor,
): string {
if (color.kind === "transparent") return "transparent";
if (color.kind === "token") return `rgb(var(${color.token}))`;
const [red, green, blue] = color.rgb.map(clampChannel);
return `rgb(${red} ${green} ${blue})`;
}
export function resolveRecordedEvidenceSemanticRgb(
host: HTMLElement,
color: RecordedEvidenceSemanticColor,
): readonly [number, number, number] | null {
if (color.kind === "transparent") return null;
if (color.kind === "diagnostic") return color.rgb.map(clampChannel) as [number, number, number];
const channels = getComputedStyle(host)
.getPropertyValue(color.token)
.trim()
.match(/[\d.]+/g)
?.slice(0, 3)
.map(Number);
return channels?.length === 3
? channels.map(clampChannel) as [number, number, number]
: TOKEN_FALLBACKS[color.token];
}
export function RecordedEvidenceSemanticMaskOverlay({
src,
imageWidth,
imageHeight,
classes,
palette,
opacity = 0.46,
ariaLabel,
}: RecordedEvidenceSemanticOverlay & {
imageWidth: number;
imageHeight: number;
}) {
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const [mask, setMask] = useState<DecodedSemanticMask | null>(null);
const [failure, setFailure] = useState<string | null>(null);
const expectedKey = semanticMaskKey(src, imageWidth, imageHeight);
const renderMask = failure
? null
: mask?.key === expectedKey
? mask
: decodedMaskCache.get(expectedKey) ?? null;
useEffect(() => {
setFailure(null);
const subscription = subscribeToSemanticMask(src, imageWidth, imageHeight);
let current = true;
void subscription.promise.then((decoded) => {
if (!current || decoded.key !== expectedKey) return;
const declaredClassIds = new Set(classes.map((item) => item.id));
const undeclaredClassId = decoded.classIds.find(
(classId) => !declaredClassIds.has(classId),
);
if (undeclaredClassId !== undefined) {
setFailure(`Semantic mask содержит необъявленный class ID ${undeclaredClassId}.`);
return;
}
setMask(decoded);
}).catch((error: unknown) => {
if (!current || (error instanceof DOMException && error.name === "AbortError")) return;
setFailure("Semantic mask не прошла проверку или декодирование.");
});
return () => {
current = false;
subscription.release();
};
}, [classes, expectedKey, imageHeight, imageWidth, src]);
useEffect(() => {
const canvas = canvasRef.current;
const host = canvas?.parentElement;
if (!canvas || !host) return;
const context = canvas.getContext("2d");
if (!context) return;
const render = () => {
const width = Math.max(host.clientWidth, 1);
const height = Math.max(host.clientHeight, 1);
const pixelRatio = Math.min(window.devicePixelRatio, 1.5);
canvas.width = Math.round(width * pixelRatio);
canvas.height = Math.round(height * pixelRatio);
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
context.clearRect(0, 0, width, height);
if (!renderMask) return;
const declaredClassIds = new Set(
classes
.map((item) => item.id)
.filter((classId) => Number.isInteger(classId) && classId >= 0 && classId <= 255),
);
const resolvedPalette = new Map<number, { rgb: readonly [number, number, number]; alpha: number }>();
for (const entry of palette) {
if (!declaredClassIds.has(entry.classId)) continue;
const rgb = resolveRecordedEvidenceSemanticRgb(host, entry.color);
if (!rgb) continue;
resolvedPalette.set(entry.classId, {
rgb,
alpha: clampOpacity(entry.opacity, 1) * clampOpacity(opacity, 0.46),
});
}
const colorCanvas = document.createElement("canvas");
colorCanvas.width = renderMask.width;
colorCanvas.height = renderMask.height;
const colorContext = colorCanvas.getContext("2d");
if (!colorContext) return;
const imageData = colorContext.createImageData(renderMask.width, renderMask.height);
for (let sourceOffset = 0, targetOffset = 0; sourceOffset < renderMask.classIds.length; sourceOffset += 1, targetOffset += 4) {
const color = resolvedPalette.get(renderMask.classIds[sourceOffset] ?? -1);
if (!color) continue;
imageData.data[targetOffset] = color.rgb[0];
imageData.data[targetOffset + 1] = color.rgb[1];
imageData.data[targetOffset + 2] = color.rgb[2];
imageData.data[targetOffset + 3] = Math.round(color.alpha * 255);
}
colorContext.putImageData(imageData, 0, 0);
const scale = Math.min(width / imageWidth, height / imageHeight);
const drawWidth = imageWidth * scale;
const drawHeight = imageHeight * scale;
const offsetX = (width - drawWidth) / 2;
const offsetY = (height - drawHeight) / 2;
context.imageSmoothingEnabled = false;
context.drawImage(colorCanvas, offsetX, offsetY, drawWidth, drawHeight);
};
const observer = new ResizeObserver(render);
observer.observe(host);
render();
return () => observer.disconnect();
}, [classes, expectedKey, imageHeight, imageWidth, opacity, palette, renderMask]);
return (
<>
<canvas
ref={canvasRef}
className="recorded-evidence-semantic-mask-overlay"
role="img"
aria-label={ariaLabel}
aria-busy={!failure && !renderMask}
data-state={failure ? "error" : renderMask ? "ready" : "loading"}
style={{ zIndex: 1 }}
/>
{failure ? (
<div className="recorded-evidence-semantic-mask-overlay__error" role="alert">
{failure}
</div>
) : null}
</>
);
}
@@ -8,6 +8,10 @@ import {
type RecordedEvidenceBox,
type RecordedEvidenceBoxTone,
} from "./RecordedEvidenceBoxOverlay";
import {
RecordedEvidenceSemanticMaskOverlay,
type RecordedEvidenceSemanticOverlay,
} from "./RecordedEvidenceSemanticMaskOverlay";
export type { RecordedEvidenceBox, RecordedEvidenceBoxTone };
@@ -17,6 +21,7 @@ export function RecordedEvidenceVideoScene({
imageWidth,
imageHeight,
boxes,
semanticOverlay,
ariaLabel,
interactive = true,
onPlaybackChange,
@@ -26,6 +31,7 @@ export function RecordedEvidenceVideoScene({
imageWidth: number;
imageHeight: number;
boxes: readonly RecordedEvidenceBox[];
semanticOverlay?: RecordedEvidenceSemanticOverlay;
ariaLabel: string;
interactive?: boolean;
onPlaybackChange?: (playback: RecordedObservationPlayback) => void;
@@ -39,6 +45,13 @@ export function RecordedEvidenceVideoScene({
prepare
onPlaybackChange={onPlaybackChange}
/>
{semanticOverlay ? (
<RecordedEvidenceSemanticMaskOverlay
{...semanticOverlay}
imageWidth={imageWidth}
imageHeight={imageHeight}
/>
) : null}
<RecordedEvidenceBoxOverlay
imageWidth={imageWidth}
imageHeight={imageHeight}
@@ -35,6 +35,7 @@ import { fetchE46GRectifiedDetectorBakeoff } from "./e46gRectifiedDetectorBakeof
import { fetchE46HFullRectifiedFrontReplay } from "./e46hFullRectifiedFrontReplay";
import { fetchE46IGroundingDinoFullReplay } from "./e46iGroundingDinoFullReplay";
import { fetchE46JRawFisheyeRealtime } from "./e46jRawFisheyeRealtime";
import { fetchE47SemanticSlamResult } from "./e47SemanticSlam";
import { fetchM4ThreatReplayResult } from "./m4ReplayThreat";
export type AdvancedLaboratoryWorkId =
@@ -63,6 +64,7 @@ export type AdvancedLaboratoryWorkId =
| "e46h-full-rectified-front-replay"
| "e46i-grounding-dino-full-replay"
| "e46j-raw-fisheye-realtime"
| "e47-semantic-slam-shadow"
| "l34-right-yolox-truth-island-freeze"
| "l34a-assisted-yolox-error-audit"
| "l34b-nested-box-consolidation-shadow"
@@ -103,6 +105,7 @@ const WORK_IDS: readonly AdvancedLaboratoryWorkId[] = [
"e46h-full-rectified-front-replay",
"e46i-grounding-dino-full-replay",
"e46j-raw-fisheye-realtime",
"e47-semantic-slam-shadow",
"l34-right-yolox-truth-island-freeze",
"l34a-assisted-yolox-error-audit",
"l34b-nested-box-consolidation-shadow",
@@ -138,6 +141,7 @@ const RESULT_PREFIX: Readonly<Record<AdvancedLaboratoryWorkId, string>> = {
"e46h-full-rectified-front-replay": "e46h-full-rectified-front-replay",
"e46i-grounding-dino-full-replay": "e46i-grounding-dino-full-replay",
"e46j-raw-fisheye-realtime": "e46j-raw-fisheye-realtime",
"e47-semantic-slam-shadow": "e47-semantic-slam",
"l34-right-yolox-truth-island-freeze": "l34-right-yolox-truth-island-freeze",
"l34a-assisted-yolox-error-audit": "l34a-assisted-yolox-error-audit",
"l34b-nested-box-consolidation-shadow": "l34b-nested-box-consolidation-shadow",
@@ -180,6 +184,7 @@ export function emptyAdvancedLaboratoryResults(): AdvancedLaboratoryResults {
e46h: null,
e46i: null,
e46j: null,
e47: null,
l34: null,
l34a: null,
l34b: null,
@@ -303,6 +308,7 @@ export function advancedLaboratoryResultAvailable(
: workId === "e46h-full-rectified-front-replay" ? results.e46h !== null
: workId === "e46i-grounding-dino-full-replay" ? results.e46i !== null
: workId === "e46j-raw-fisheye-realtime" ? results.e46j !== null
: workId === "e47-semantic-slam-shadow" ? results.e47 !== null
: workId === "l34-right-yolox-truth-island-freeze" ? results.l34 !== null
: workId === "l34a-assisted-yolox-error-audit" ? results.l34a !== null
: workId === "l34b-nested-box-consolidation-shadow" ? results.l34b !== null
@@ -403,6 +409,8 @@ export async function fetchAdvancedLaboratoryResult(
results.e46i = await fetchE46IGroundingDinoFullReplay({ fetcher, signal });
} else if (workId === "e46j-raw-fisheye-realtime") {
results.e46j = await fetchE46JRawFisheyeRealtime({ fetcher, signal });
} else if (workId === "e47-semantic-slam-shadow") {
results.e47 = await fetchE47SemanticSlamResult({ fetcher, signal });
} else if (workId === "l34-right-yolox-truth-island-freeze") {
results.l34 = await fetchL34RightYoloxTruthIsland({ fetcher, signal });
} else if (workId === "l34a-assisted-yolox-error-audit") {
@@ -31,6 +31,7 @@ import type { E46GRectifiedDetectorBakeoffResult } from "./e46gRectifiedDetector
import type { E46HFullRectifiedFrontReplayResult } from "./e46hFullRectifiedFrontReplay";
import type { E46IGroundingDinoFullReplayResult } from "./e46iGroundingDinoFullReplay";
import type { E46JRawFisheyeRealtimeResult } from "./e46jRawFisheyeRealtime";
import type { E47SemanticSlamResult } from "./e47SemanticSlam";
import type { M4ThreatReplayResult } from "./m4ReplayThreat";
export interface AdvancedLaboratoryResults {
@@ -59,6 +60,7 @@ export interface AdvancedLaboratoryResults {
e46h: E46HFullRectifiedFrontReplayResult | null;
e46i: E46IGroundingDinoFullReplayResult | null;
e46j: E46JRawFisheyeRealtimeResult | null;
e47: E47SemanticSlamResult | null;
l34: L34RightYoloxTruthIslandResult | null;
l34a: L34AAssistedYoloxErrorAuditResult | null;
l34b: L34BResult | null;
@@ -985,9 +985,9 @@ export async function fetchAdvancedLaboratoryResults({
e46b: null,
e46c: null,
e46d: null,
e46e: null,
e46f: null,
e46e: null, e46f: null,
e46g: null, e46h: null, e46i: null, e46j: null,
e47: null,
l34: null,
l34a: null,
l34b: null,
@@ -0,0 +1,516 @@
export type E47SemanticDisposition = "labeled" | "ambiguous";
export interface E47SemanticClass {
classId: number;
label: string;
disposition: E47SemanticDisposition;
colorRgb: readonly [number, number, number];
}
export interface E47SemanticSlamResult {
resultId: string;
createdAtUtc: string;
status: "diagnostic-semantic-slam-shadow";
profileId: string;
baseM4ResultId: string;
semanticResultId: string;
geometryResultId: string;
sourcePackId: string;
calibrationContentSha256: string;
provider: {
providerId: string;
modelId: string;
modelRevision: string;
modelWeightsSha256: string;
preprocessId: string;
};
temporalBinding: {
semanticToCamera: "exact-sequence-and-session-time";
cameraToLidar: "accepted-e6-nearest-host-arrival-best-effort";
clockBasis: "recorded-host-monotonic-arrival";
maximumLidarCameraDeltaMs: number;
maximumPosePointDeltaMs: number;
physicalSynchronizationProven: false;
};
taxonomy: readonly E47SemanticClass[];
metrics: {
frames: {
total: number;
maskAvailable: number;
sourceAvailable: number;
};
points: {
total: number;
projected: number;
labeled: number;
ambiguous: number;
unprojected: number;
absent: number;
};
observations: {
total: number;
labeled: number;
ambiguous: number;
unprojected: number;
absent: number;
};
runtime: {
elapsedMs: number;
framesPerSecond: number;
};
};
acceptance: {
artifactContractPassed: boolean;
frameAccountingPassed: boolean;
pointAccountingPassed: boolean;
observationBindingPassed: boolean;
temporalBindingPassed: boolean;
independentSemanticTruthPassed: false;
providerPromoted: false;
};
limitations: readonly string[];
}
export interface E47SemanticTimelineFrame {
sequence: number;
sourcePointCount: number;
classIds: readonly number[];
statusCodes: readonly number[];
counts: {
labeled: number;
ambiguous: number;
unprojected: number;
absent: number;
};
}
export interface E47SemanticTimelineChunk {
resultId: string;
startSequence: number;
frameCount: number;
nextSequence: number | null;
frames: readonly E47SemanticTimelineFrame[];
}
type LaboratoryFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
export class E47SemanticSlamContractError extends Error {}
const object = (value: unknown, label: string): Record<string, unknown> => {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new E47SemanticSlamContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
};
const array = (value: unknown, label: string): readonly unknown[] => {
if (!Array.isArray(value)) {
throw new E47SemanticSlamContractError(`${label}: ожидался массив.`);
}
return value;
};
const text = (value: unknown, label: string): string => {
if (typeof value !== "string" || !value.trim()) {
throw new E47SemanticSlamContractError(`${label}: ожидалась строка.`);
}
return value;
};
const finite = (value: unknown, label: string): number => {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new E47SemanticSlamContractError(`${label}: ожидалось число.`);
}
return value;
};
const integer = (value: unknown, label: string): number => {
const parsed = finite(value, label);
if (!Number.isInteger(parsed) || parsed < 0) {
throw new E47SemanticSlamContractError(`${label}: ожидалось неотрицательное целое.`);
}
return parsed;
};
const exact = <T extends string | number | boolean>(
value: unknown,
expected: T,
label: string,
): T => {
if (value !== expected) {
throw new E47SemanticSlamContractError(`${label}: нарушен контракт.`);
}
return expected;
};
function resultId(value: unknown): string {
const parsed = text(value, "E47 result id");
if (!/^e47-semantic-slam-[a-f0-9]{64}$/.test(parsed)) {
throw new E47SemanticSlamContractError("E47 result id: нарушена идентичность.");
}
return parsed;
}
function m4ResultId(value: unknown): string {
const parsed = text(value, "E47 base M4 result id");
if (!/^m4-threat-replay-[a-f0-9]{64}$/.test(parsed)) {
throw new E47SemanticSlamContractError("E47 base M4 result id: нарушена идентичность.");
}
return parsed;
}
function sha256(value: unknown, label: string): string {
const parsed = text(value, label);
if (!/^[a-f0-9]{64}$/.test(parsed)) {
throw new E47SemanticSlamContractError(`${label}: ожидался SHA-256.`);
}
return parsed;
}
function taxonomy(value: unknown): readonly E47SemanticClass[] {
const classes = array(value, "E47 taxonomy").map((raw) => {
const item = object(raw, "E47 semantic class");
const classId = integer(item.class_id, "E47 class id");
if (classId > 255) {
throw new E47SemanticSlamContractError("E47 class id: вышел за uint8.");
}
const disposition = text(item.disposition, "E47 class disposition");
if (disposition !== "labeled" && disposition !== "ambiguous") {
throw new E47SemanticSlamContractError("E47 class disposition: неизвестное значение.");
}
const rgb = array(item.color_rgb, "E47 class color").map(
(channel) => integer(channel, "E47 color channel"),
);
if (rgb.length !== 3 || rgb.some((channel) => channel > 255)) {
throw new E47SemanticSlamContractError("E47 class color: нарушен RGB-контракт.");
}
return {
classId,
label: text(item.label, "E47 class label"),
disposition: disposition as E47SemanticDisposition,
colorRgb: [rgb[0]!, rgb[1]!, rgb[2]!] as const,
};
});
if (!classes.length || new Set(classes.map((item) => item.classId)).size !== classes.length) {
throw new E47SemanticSlamContractError("E47 taxonomy: классы отсутствуют или дублируются.");
}
return classes;
}
function parseResult(value: unknown): E47SemanticSlamResult {
const item = object(value, "E47 result");
exact(item.schema_version, "missioncore.e47-semantic-slam-view/v1", "E47 view schema");
exact(item.status, "diagnostic-semantic-slam-shadow", "E47 status");
exact(item.ground_truth, false, "E47 ground truth");
exact(item.semantic_authority, "diagnostic-only", "E47 semantic authority");
exact(item.navigation_or_safety_accepted, false, "E47 safety authority");
exact(item.actuation_allowed, false, "E47 actuation authority");
const provider = object(item.provider, "E47 provider");
const temporalBinding = object(item.temporal_binding, "E47 temporal binding");
const metrics = object(item.metrics, "E47 metrics");
const frames = object(metrics.frames, "E47 frame metrics");
const points = object(metrics.points, "E47 point metrics");
const observations = object(metrics.observations, "E47 observation metrics");
const runtime = object(metrics.runtime, "E47 runtime metrics");
const acceptance = object(item.acceptance, "E47 acceptance");
const frameMetrics = {
total: exact(frames.total, 4489, "E47 frame total"),
maskAvailable: integer(frames.mask_available, "E47 mask frames"),
sourceAvailable: integer(frames.source_available, "E47 source frames"),
};
if (
frameMetrics.maskAvailable > frameMetrics.total
|| frameMetrics.sourceAvailable > frameMetrics.total
) {
throw new E47SemanticSlamContractError("E47 frame accounting: нарушен контракт.");
}
const pointMetrics = {
total: integer(points.total, "E47 total points"),
projected: integer(points.projected, "E47 projected points"),
labeled: integer(points.labeled, "E47 labeled points"),
ambiguous: integer(points.ambiguous, "E47 ambiguous points"),
unprojected: integer(points.unprojected, "E47 unprojected points"),
absent: integer(points.absent, "E47 absent points"),
};
if (
pointMetrics.projected !== pointMetrics.labeled + pointMetrics.ambiguous
|| pointMetrics.total !== pointMetrics.projected
+ pointMetrics.unprojected
+ pointMetrics.absent
) {
throw new E47SemanticSlamContractError("E47 point accounting: нарушен контракт.");
}
const observationMetrics = {
total: integer(observations.total, "E47 total observations"),
labeled: integer(observations.labeled, "E47 labeled observations"),
ambiguous: integer(observations.ambiguous, "E47 ambiguous observations"),
unprojected: integer(observations.unprojected, "E47 unprojected observations"),
absent: integer(observations.absent, "E47 absent observations"),
};
if (
observationMetrics.total !== observationMetrics.labeled
+ observationMetrics.ambiguous
+ observationMetrics.unprojected
+ observationMetrics.absent
) {
throw new E47SemanticSlamContractError("E47 observation accounting: нарушен контракт.");
}
const runtimeMetrics = {
elapsedMs: finite(runtime.elapsed_ms, "E47 elapsed"),
framesPerSecond: finite(runtime.frames_per_second, "E47 FPS"),
};
if (runtimeMetrics.elapsedMs <= 0 || runtimeMetrics.framesPerSecond <= 0) {
throw new E47SemanticSlamContractError("E47 runtime accounting: нарушен контракт.");
}
return {
resultId: resultId(item.result_id),
createdAtUtc: text(item.created_at_utc, "E47 created at"),
status: "diagnostic-semantic-slam-shadow",
profileId: text(item.profile_id, "E47 profile"),
baseM4ResultId: m4ResultId(item.base_m4_result_id),
semanticResultId: text(item.semantic_result_id, "E47 semantic source"),
geometryResultId: text(item.geometry_result_id, "E47 geometry source"),
sourcePackId: text(item.source_pack_id, "E47 source pack"),
calibrationContentSha256: sha256(item.calibration_content_sha256, "E47 calibration"),
provider: {
providerId: text(provider.provider_id, "E47 provider id"),
modelId: text(provider.model_id, "E47 model id"),
modelRevision: text(provider.model_revision, "E47 model revision"),
modelWeightsSha256: sha256(provider.model_weights_sha256, "E47 model weights"),
preprocessId: text(provider.preprocess_id, "E47 preprocess id"),
},
temporalBinding: {
semanticToCamera: exact(
temporalBinding.semantic_to_camera,
"exact-sequence-and-session-time",
"E47 semantic/camera binding",
),
cameraToLidar: exact(
temporalBinding.camera_to_lidar,
"accepted-e6-nearest-host-arrival-best-effort",
"E47 camera/LiDAR binding",
),
clockBasis: exact(
temporalBinding.clock_basis,
"recorded-host-monotonic-arrival",
"E47 clock basis",
),
maximumLidarCameraDeltaMs: finite(
temporalBinding.maximum_lidar_camera_delta_ms,
"E47 maximum camera/LiDAR delta",
),
maximumPosePointDeltaMs: finite(
temporalBinding.maximum_pose_point_delta_ms,
"E47 maximum pose/point delta",
),
physicalSynchronizationProven: exact(
temporalBinding.physical_synchronization_proven,
false,
"E47 physical synchronization",
),
},
taxonomy: taxonomy(item.taxonomy),
metrics: {
frames: frameMetrics,
points: pointMetrics,
observations: observationMetrics,
runtime: runtimeMetrics,
},
acceptance: {
artifactContractPassed: exact(
acceptance.artifact_contract_passed,
true,
"E47 artifact contract",
),
frameAccountingPassed: exact(
acceptance.frame_accounting_passed,
true,
"E47 frame accounting",
),
pointAccountingPassed: exact(
acceptance.point_accounting_passed,
true,
"E47 point accounting",
),
observationBindingPassed: exact(
acceptance.observation_binding_passed,
true,
"E47 observation binding",
),
temporalBindingPassed: exact(
acceptance.temporal_binding_passed,
true,
"E47 temporal binding",
),
independentSemanticTruthPassed: exact(
acceptance.independent_semantic_truth_passed,
false,
"E47 independent truth",
),
providerPromoted: exact(
acceptance.provider_promoted,
false,
"E47 provider promotion",
),
},
limitations: array(item.limitations, "E47 limitations").map(
(entry) => text(entry, "E47 limitation"),
),
};
}
export async function fetchE47SemanticSlamResult({
fetcher = fetch,
signal,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
} = {}): Promise<E47SemanticSlamResult | null> {
const response = await fetcher("/api/v1/laboratory/e47-semantic-slam/results?limit=1", {
headers: { Accept: "application/json" },
signal,
});
if (!response.ok) {
throw new E47SemanticSlamContractError(`E47 LAB недоступен: HTTP ${response.status}.`);
}
const payload = object(await response.json(), "E47 catalog");
exact(
payload.schema_version,
"missioncore.e47-semantic-slam-catalog/v1",
"E47 catalog schema",
);
const items = array(payload.items, "E47 catalog items");
return items.length ? parseResult(items[0]) : null;
}
function parseFrame(
value: unknown,
expectedSequence: number,
declaredTaxonomy: readonly E47SemanticClass[] | undefined,
): E47SemanticTimelineFrame {
const item = object(value, "E47 semantic frame");
exact(item.schema_version, "missioncore.e47-semantic-slam-frame/v1", "E47 frame schema");
const sequence = integer(item.sequence, "E47 frame sequence");
if (sequence !== expectedSequence) {
throw new E47SemanticSlamContractError("E47 frame sequence: нарушен порядок.");
}
const sourcePointCount = integer(item.source_point_count, "E47 frame source points");
const classIds = array(item.class_ids, "E47 frame classes").map((entry) => {
const parsed = finite(entry, "E47 frame class");
if (!Number.isInteger(parsed) || parsed < -1 || parsed > 255) {
throw new E47SemanticSlamContractError("E47 frame class: вышел за контракт.");
}
return parsed;
});
const statusCodes = array(item.status_codes, "E47 frame statuses").map((entry) => {
const parsed = integer(entry, "E47 frame status");
if (parsed > 3) {
throw new E47SemanticSlamContractError("E47 frame status: неизвестное значение.");
}
return parsed;
});
if (classIds.length !== sourcePointCount || statusCodes.length !== sourcePointCount) {
throw new E47SemanticSlamContractError("E47 frame point accounting: нарушен контракт.");
}
if (classIds.some((classId, index) => {
const status = statusCodes[index];
return status === 0 || status === 1 ? classId !== -1 : classId < 0;
})) {
throw new E47SemanticSlamContractError("E47 frame class/status binding: нарушен контракт.");
}
if (declaredTaxonomy) {
const classesById = new Map(declaredTaxonomy.map((item) => [item.classId, item]));
if (classIds.some((classId, index) => {
const status = statusCodes[index];
if (status !== 2 && status !== 3) return false;
const semanticClass = classesById.get(classId);
return !semanticClass
|| (status === 2 && semanticClass.disposition !== "ambiguous")
|| (status === 3 && semanticClass.disposition !== "labeled");
})) {
throw new E47SemanticSlamContractError("E47 frame taxonomy binding: нарушен контракт.");
}
}
const counts = object(item.counts, "E47 frame counts");
const parsedCounts = {
labeled: integer(counts.labeled, "E47 frame labeled"),
ambiguous: integer(counts.ambiguous, "E47 frame ambiguous"),
unprojected: integer(counts.unprojected, "E47 frame unprojected"),
absent: integer(counts.absent, "E47 frame absent"),
};
if (Object.values(parsedCounts).reduce((sum, count) => sum + count, 0) !== sourcePointCount) {
throw new E47SemanticSlamContractError("E47 frame status accounting: нарушен контракт.");
}
const actualCounts = {
labeled: statusCodes.filter((status) => status === 3).length,
ambiguous: statusCodes.filter((status) => status === 2).length,
unprojected: statusCodes.filter((status) => status === 1).length,
absent: statusCodes.filter((status) => status === 0).length,
};
if (Object.keys(actualCounts).some(
(key) => actualCounts[key as keyof typeof actualCounts]
!== parsedCounts[key as keyof typeof parsedCounts],
)) {
throw new E47SemanticSlamContractError("E47 frame status histogram: нарушен контракт.");
}
return { sequence, sourcePointCount, classIds, statusCodes, counts: parsedCounts };
}
export async function fetchE47SemanticTimelineChunk(
result: string,
startSequence: number,
frameCount: number,
{
fetcher = fetch,
signal,
taxonomy,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
taxonomy?: readonly E47SemanticClass[];
} = {},
): Promise<E47SemanticTimelineChunk> {
resultId(result);
const parameters = new URLSearchParams({
start: String(startSequence),
count: String(frameCount),
});
const response = await fetcher(
`/api/v1/laboratory/e47-semantic-slam/results/${result}/timeline/chunk?${parameters}`,
{ headers: { Accept: "application/json" }, signal },
);
if (!response.ok) {
throw new E47SemanticSlamContractError(`E47 timeline chunk: HTTP ${response.status}.`);
}
const payload = object(await response.json(), "E47 semantic chunk");
exact(payload.schema_version, "missioncore.e47-semantic-slam-chunk/v1", "E47 chunk schema");
exact(payload.result_id, result, "E47 chunk result");
const parsedStart = integer(payload.start_sequence, "E47 chunk start");
if (parsedStart !== startSequence) {
throw new E47SemanticSlamContractError("E47 chunk start: нарушен контракт.");
}
const frames = array(payload.frames, "E47 chunk frames").map(
(frame, offset) => parseFrame(frame, parsedStart + offset, taxonomy),
);
const parsedCount = integer(payload.frame_count, "E47 chunk count");
if (parsedCount !== frames.length || parsedCount > frameCount) {
throw new E47SemanticSlamContractError("E47 chunk frame count: нарушен контракт.");
}
return {
resultId: result,
startSequence: parsedStart,
frameCount: parsedCount,
nextSequence: payload.next_sequence === null
? null
: integer(payload.next_sequence, "E47 next sequence"),
frames,
};
}
export function e47SemanticMaskUrl(result: string, sequence: number): string {
resultId(result);
if (!Number.isInteger(sequence) || sequence < 0 || sequence >= 4489) {
throw new E47SemanticSlamContractError("E47 mask sequence: вне recorded replay.");
}
return `/api/v1/laboratory/e47-semantic-slam/results/${result}/masks/${sequence}`;
}
@@ -88,6 +88,24 @@
inset: 0;
}
.recorded-evidence-semantic-mask-overlay__error {
position: absolute;
z-index: 4;
top: 4.9rem;
left: 50%;
max-width: min(32rem, calc(100% - 2rem));
border: 1px solid rgb(var(--nodedc-warning-rgb) / 0.44);
border-radius: var(--nodedc-radius-control-compact);
background: var(--nodedc-floating-surface);
padding: 0.42rem 0.58rem;
color: rgb(var(--nodedc-warning-rgb));
font-size: 0.54rem;
text-align: center;
backdrop-filter: blur(var(--nodedc-blur-control));
pointer-events: none;
transform: translateX(-50%);
}
.m4-replay-threat-evidence-viewer .laboratory-metric-evidence-scene__legend,
.m4-replay-threat-evidence-viewer .m4-replay-threat-visual__overlay {
bottom: 6.2rem;
@@ -56,6 +56,7 @@
.m4-replay-threat-evidence-viewer .laboratory-evidence-viewer__controls {
width: calc(100% - 1.2rem);
gap: 0.3rem;
}
.m4-replay-threat-evidence-viewer .l3-visual-audit__actions {
@@ -67,7 +68,7 @@
}
.m4-replay-threat-visual__layer-controls .nodedc-segmented__item {
padding-inline: 0.72rem;
padding-inline: 0.3rem;
}
.m4-replay-threat-evidence-viewer .laboratory-evidence-viewer__transport {
@@ -138,7 +139,7 @@
width: 0.38rem;
height: 0.38rem;
border-radius: 50%;
background: var(--nodedc-text-muted);
background: var(--laboratory-metric-legend-color, var(--nodedc-text-muted));
content: "";
}
@@ -39,6 +39,7 @@ import { E46GRectifiedDetectorBakeoffResultView } from "./E46GRectifiedDetectorB
import { E46HFullRectifiedFrontReplayResultView } from "./E46HFullRectifiedFrontReplayResult";
import { E46IGroundingDinoFullReplayResultView } from "./E46IGroundingDinoFullReplayResult";
import { E46JRawFisheyeRealtimeResultView } from "./E46JRawFisheyeRealtimeResult";
import { E47SemanticSlamResultView } from "./E47SemanticSlamResult";
import { M4ReplayThreatResultView } from "./M4ReplayThreatResult";
export { isAdvancedLaboratoryWorkId };
@@ -85,6 +86,9 @@ export function AdvancedLaboratoryResult({
if (workId === "m4-replay-threat" && results.m4Threat) {
return <M4ReplayThreatResultView rigLabel={rigLabel} result={results.m4Threat} />;
}
if (workId === "e47-semantic-slam-shadow" && results.e47) {
return <E47SemanticSlamResultView rigLabel={rigLabel} result={results.e47} />;
}
if (workId === "l3-pointpillars-visual-audit" && results.l3) {
return <L3PointPillarsResult result={results.l3} />;
}
@@ -0,0 +1,156 @@
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import type { E47SemanticSlamResult } from "../../core/laboratory/e47SemanticSlam";
import { formatNumber } from "../../presentation";
import { M4ReplayThreatVisual } from "./M4ReplayThreatVisual";
export function E47SemanticSlamResultView({
rigLabel,
result,
}: {
rigLabel: string;
result: E47SemanticSlamResult;
}) {
const pointProjectedCoverage = result.metrics.points.total
? result.metrics.points.projected / result.metrics.points.total
: 0;
const pointLabeledCoverage = result.metrics.points.total
? result.metrics.points.labeled / result.metrics.points.total
: 0;
const observationLabeledCoverage = result.metrics.observations.total
? result.metrics.observations.labeled / result.metrics.observations.total
: 0;
return (
<LaboratoryWorkTemplate
summary={(
<LaboratorySummary
title="E47 · semantic mask → KB4 → SLAM shadow"
description="Зафиксированные EoMT-маски проецируются заводской KB4-калибровкой на исходные точки SLAM/LiDAR и отдельно агрегируются по уже существующим геометрическим наблюдениям. Это диагностический слой: он не меняет occupancy, motion, threat или safe/unknown."
status="Diagnostic contract passed · provider quality gate open"
statusTone="warning"
facts={[
{
label: "Конфигурация",
value: `${rigLabel} · RIGHT camera + registered SLAM cloud · recorded replay`,
},
{
label: "Semantic control",
value: `${result.provider.modelId} · exact revision ${result.provider.modelRevision.slice(0, 12)}`,
},
{
label: "Проекция",
value: `factory KB4 · ${result.calibrationContentSha256.slice(0, 12)} · frame-local point IDs`,
},
{
label: "Синхрон",
value: `semantic↔camera exact ledger · camera↔LiDAR E6 best-effort ≤${formatNumber(result.temporalBinding.maximumLidarCameraDeltaMs, 0)} ms · HW sync: нет`,
},
{
label: "Покрытие",
value: `${result.metrics.frames.maskAvailable}/${result.metrics.frames.total} masks · ${formatNumber(pointProjectedCoverage * 100, 1)}% projected · ${formatNumber(pointLabeledCoverage * 100, 1)}% unambiguous`,
},
{
label: "Визуал",
value: "4489-frame VIDEO/CAMERA/3D/PLAN · один recorded clock · semantic layer",
},
]}
brief={{
question: "Можно ли добавить плотную семантику камеры к сильной SLAM/LiDAR-геометрии, не превратив классификацию в источник ложного свободного пространства?",
approach: "Для всех 4489 кадров переиспользованы неизменяемые EoMT masks, factory KB4 extrinsic/intrinsic и тот же source point index space, на котором построен M4.6. Semantic↔camera сверяется fail-closed по sequence и session-time; camera↔LiDAR сохраняет исходный bounded nearest-arrival E6 contract, а не выдаётся за hardware-sync. Каждая точка получает labeled, ambiguous, unprojected или absent.",
principalResult: `${result.metrics.points.labeled.toLocaleString("ru-RU")} точек получили однозначный класс, ${result.metrics.points.ambiguous.toLocaleString("ru-RU")} остались semantic-ambiguous, ${result.metrics.points.unprojected.toLocaleString("ru-RU")} не спроецировались. Из ${result.metrics.observations.total.toLocaleString("ru-RU")} неизменённых geometry observations однозначный класс получили ${formatNumber(observationLabeledCoverage * 100, 1)}%.`,
limitation: "EoMT здесь — фиксированный control provider, а не выбранная production-модель. Physical camera↔LiDAR hardware-sync не доказан; принят только E6 nearest-host-arrival best-effort в пределах 100 мс. Semantic/instance truth, obstacle recall и fisheye-specific качество независимо не размечены; отсутствие класса никогда не означает free.",
}}
method={{
completeness: "complete",
executionClass: "hybrid",
pipelineId: "semantic-slam-diagnostic-shadow/v1",
components: [
{
kind: "source",
name: result.semanticResultId,
version: result.provider.modelRevision,
role: "sealed full-route uint8 semantic masks",
identitySha256: result.provider.modelWeightsSha256,
},
{
kind: "source",
name: result.sourcePackId,
version: "registered map increments + vendor SLAM pose",
role: "точный frame-local point index space",
identitySha256: result.sourcePackId.split("-").at(-1) ?? null,
},
{
kind: "source",
name: result.geometryResultId,
version: "immutable M4 geometry observations",
role: "неизменяемые obstacle IDs, occupancy и metric geometry",
identitySha256: result.geometryResultId.split("-").at(-1) ?? null,
},
{
kind: "algorithm",
name: "semantic diagnostic fusion",
version: result.profileId,
role: "KB4 mask projection + point/observation accounting без safety authority",
identitySha256: result.resultId.split("-").at(-1) ?? null,
},
],
}}
/>
)}
evidence={(
<LaboratoryEvidence
eyebrow="E47 VISUAL EVIDENCE · VIDEO / CAMERA / 3D / PLAN"
title="Синхронный контроль маски, semantic-точек, геометрии и коридора"
kind="diagnostic-model"
resizable
>
<M4ReplayThreatVisual
resultId={result.baseM4ResultId}
semantic={{
resultId: result.resultId,
taxonomy: result.taxonomy,
}}
/>
</LaboratoryEvidence>
)}
result={(
<LaboratoryResultSummary
title="Semantic/SLAM seam принят; качество provider ещё не принято"
status="Жёлтый: артефакты и проекция доказаны, independent semantic truth отсутствует"
statusTone="warning"
metrics={[
{
label: "Semantic masks",
value: `${result.metrics.frames.maskAvailable}/${result.metrics.frames.total}`,
hint: "exact immutable full-route archive",
},
{
label: "Point labels",
value: result.metrics.points.labeled.toLocaleString("ru-RU"),
hint: `${result.metrics.points.unprojected.toLocaleString("ru-RU")} unprojected · ${result.metrics.points.ambiguous.toLocaleString("ru-RU")} ambiguous`,
},
{
label: "Observation labels",
value: result.metrics.observations.labeled.toLocaleString("ru-RU"),
hint: `${result.metrics.observations.ambiguous.toLocaleString("ru-RU")} ambiguous`,
},
{
label: "Derivative build",
value: `${formatNumber(result.metrics.runtime.framesPerSecond, 1)} FPS`,
hint: `${formatNumber(result.metrics.runtime.elapsedMs / 1000, 1)} s offline`,
},
]}
conclusion={{
proved: "Одна каноническая модель-независимая форма принимает sealed semantic mask, привязывает её к исходному кадру и к factory KB4, маркирует полный frame-local point space и публикует проверяемое semantic evidence для существующих geometry observations. Текущий M4.6 при этом не изменён.",
notProved: "Не доказаны physical hardware-sync, class accuracy, instance separation, удержание отдельных объектов, obstacle recall, перенос на другой маршрут/provider и production latency на Worker 006. Semantic evidence не имеет navigation/safety authority.",
decision: "Оставить EoMT как контрольную ветку. Следующий честный A/B — NVIDIA CitySemSegFormer на замороженном truth-island через тот же provider contract; после ручного GT сравнивать качество, а не интерфейс или цвет overlay.",
}}
/>
)}
/>
);
}
@@ -9,11 +9,20 @@ import {
} from "../../components/laboratory/LaboratoryMetricEvidenceScene";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
import { RecordedEvidenceImageScene } from "../../components/laboratory/RecordedEvidenceImageScene";
import type {
RecordedEvidenceSemanticClass,
RecordedEvidenceSemanticOverlay,
RecordedEvidenceSemanticPaletteEntry,
} from "../../components/laboratory/RecordedEvidenceSemanticMaskOverlay";
import {
RecordedEvidenceVideoScene,
type RecordedEvidenceBox,
} from "../../components/laboratory/RecordedEvidenceVideoScene";
import { useRecordedEvidencePlayback } from "../../components/laboratory/useRecordedEvidencePlayback";
import {
e47SemanticMaskUrl,
type E47SemanticClass,
} from "../../core/laboratory/e47SemanticSlam";
import type {
M4ThreatCameraProposal,
M4ThreatTimelineFrame,
@@ -26,6 +35,7 @@ import {
useM4ThreatTimelineFrame,
useM4ThreatTimelineMetadata,
} from "./useM4ThreatTimeline";
import { useE47SemanticTimelineFrame } from "./useE47SemanticTimeline";
type M4ThreatViewMode = "video" | "camera" | LaboratoryMetricSceneMode;
@@ -65,12 +75,24 @@ function SpatialState({ message: text }: { message: string }) {
);
}
export function M4ReplayThreatVisual({ resultId }: { resultId: string }) {
export interface M4ReplayThreatSemanticLayer {
resultId: string;
taxonomy: readonly E47SemanticClass[];
}
export function M4ReplayThreatVisual({
resultId,
semantic,
}: {
resultId: string;
semantic?: M4ReplayThreatSemanticLayer;
}) {
const [mode, setMode] = useState<M4ThreatViewMode>("video");
const [spatialMode, setSpatialMode] = useState<LaboratoryMetricSceneMode>("3d");
const [showCurrentIncrement, setShowCurrentIncrement] = useState(true);
const [showLocalSurface, setShowLocalSurface] = useState(true);
const [showRollingMap, setShowRollingMap] = useState(true);
const [showSemantic, setShowSemantic] = useState(true);
const [expanded, setExpanded] = useState(false);
const metricSceneRef = useRef<LaboratoryMetricEvidenceSceneHandle | null>(null);
const metadata = useM4ThreatTimelineMetadata(resultId);
@@ -140,6 +162,12 @@ export function M4ReplayThreatVisual({ resultId }: { resultId: string }) {
}, [resultId]);
if (timelineFrame.activeFrame) lastFrameRef.current = timelineFrame.activeFrame;
const frame = timelineFrame.activeFrame ?? lastFrameRef.current;
const semanticTimeline = useE47SemanticTimelineFrame({
resultId: semantic?.resultId ?? null,
activeSequence: frame?.sequence ?? timelineFrame.activeSequence,
frameCount: metadata.timeline?.frameCount ?? 0,
taxonomy: semantic?.taxonomy ?? [],
});
const displayingBufferedFrame = Boolean(
frame
&& timelineFrame.activeSequence !== null
@@ -173,6 +201,57 @@ export function M4ReplayThreatVisual({ resultId }: { resultId: string }) {
pointLimit: 20_000,
},
), [frame, metadata.timeline, timelineFrame.availableFrames]);
const semanticClasses = useMemo<readonly RecordedEvidenceSemanticClass[]>(
() => semantic?.taxonomy.map((item) => ({
id: item.classId,
label: `semantic: ${item.label}`,
})) ?? [],
[semantic?.taxonomy],
);
const semanticPalette = useMemo<readonly RecordedEvidenceSemanticPaletteEntry[]>(
() => semantic?.taxonomy.map((item) => ({
classId: item.classId,
color: item.disposition === "ambiguous"
? { kind: "token" as const, token: "--nodedc-warning-rgb" as const }
: { kind: "diagnostic" as const, rgb: item.colorRgb },
opacity: item.disposition === "ambiguous" ? 0.22 : 0.56,
})) ?? [],
[semantic?.taxonomy],
);
const semanticFrame = semanticTimeline.activeFrame?.sequence === frame?.sequence
? semanticTimeline.activeFrame
: null;
const semanticIntegrityError = semantic && frame?.spatialAvailable && semanticFrame && (
semanticFrame.sourcePointCount !== frame.pointCloudSourceCount
|| frame.pointCloudSampleCount !== frame.pointCloudSourceCount
|| frame.pointCloudBodyXyzM.length !== frame.pointCloudSourceCount
)
? "E47 semantic point index space не совпал с exact current increment M4.6."
: null;
const alignedSemanticPointIds = useMemo<readonly (number | null)[] | undefined>(() => {
if (
!semantic
|| !showSemantic
|| !frame
|| !frame.spatialAvailable
|| !semanticFrame
|| semanticIntegrityError
) return undefined;
return semanticFrame.classIds.map((classId, index) => {
const status = semanticFrame.statusCodes[index];
return status === 2 || status === 3 ? classId : null;
});
}, [frame, semantic, semanticFrame, semanticIntegrityError, showSemantic]);
const semanticOverlay: RecordedEvidenceSemanticOverlay | undefined =
semantic && showSemantic && frame
? {
src: e47SemanticMaskUrl(semantic.resultId, frame.sequence),
classes: semanticClasses,
palette: semanticPalette,
opacity: 0.48,
ariaLabel: `E47 semantic mask frame ${frame.sequence + 1}`,
}
: undefined;
const seek = (seconds: number) => playbackController.seek(seconds);
const handleModeChange = (next: M4ThreatViewMode) => {
@@ -205,12 +284,14 @@ export function M4ReplayThreatVisual({ resultId }: { resultId: string }) {
<Icon name="chevron-right" size={16} />
</IconButton>
</div>
{mode === "3d" || mode === "plan" ? (
{mode === "3d" || mode === "plan" || semantic ? (
<div
className="nodedc-segmented m4-replay-threat-visual__layer-controls"
role="group"
aria-label="Слои пространственного evidence"
>
{mode === "3d" || mode === "plan" ? (
<>
<button
type="button"
className="nodedc-segmented__item"
@@ -239,6 +320,19 @@ export function M4ReplayThreatVisual({ resultId }: { resultId: string }) {
>
ROLLING
</button>
</>
) : null}
{semantic ? (
<button
type="button"
className="nodedc-segmented__item"
data-active={showSemantic ? "true" : undefined}
aria-pressed={showSemantic}
onClick={() => setShowSemantic((visible) => !visible)}
>
SEMANTICS
</button>
) : null}
</div>
) : null}
</div>
@@ -274,6 +368,9 @@ export function M4ReplayThreatVisual({ resultId }: { resultId: string }) {
{frame.spatialAvailable
? `${frame.pointCloudSampleCount}/${frame.pointCloudSourceCount} exact · ${localSurface.pointsBodyXyzM.length} local SLAM / ${localSurface.sourceFrameCount} frames`
: "body frame / current increment unavailable"}
{semantic && semanticFrame
? ` · semantic L ${semanticFrame.counts.labeled} · A ${semanticFrame.counts.ambiguous} · U ${semanticFrame.counts.unprojected} · Ø ${semanticFrame.counts.absent}`
: semantic ? " · semantic buffer" : ""}
</small>
</div>
<div>
@@ -314,6 +411,7 @@ export function M4ReplayThreatVisual({ resultId }: { resultId: string }) {
imageWidth={timeline.imageWidth}
imageHeight={timeline.imageHeight}
boxes={activeBoxes}
semanticOverlay={mode === "video" ? semanticOverlay : undefined}
ariaLabel={`M4.6 recorded-realtime frame ${frame?.sequence ?? 0}: ${activeBoxes.length} proposals`}
interactive={false}
/>
@@ -337,6 +435,7 @@ export function M4ReplayThreatVisual({ resultId }: { resultId: string }) {
imageWidth={timeline.imageWidth}
imageHeight={timeline.imageHeight}
boxes={activeBoxes}
semanticOverlay={mode === "camera" ? semanticOverlay : undefined}
ariaLabel={`M4.6 exact camera frame ${frame.sequence}: ${activeBoxes.length} proposals`}
/>
) : null}
@@ -360,6 +459,9 @@ export function M4ReplayThreatVisual({ resultId }: { resultId: string }) {
showCurrentIncrement={showCurrentIncrement}
showLocalSurface={showLocalSurface}
showRollingMap={showRollingMap}
pointSemanticClassIds={alignedSemanticPointIds}
semanticClasses={semanticClasses}
semanticPalette={semanticPalette}
/>
) : null}
</div>
@@ -375,6 +477,24 @@ export function M4ReplayThreatVisual({ resultId }: { resultId: string }) {
<span>{timelineFrame.error}</span>
</div>
) : null}
{semantic && semanticTimeline.loading ? (
<div className="m4-replay-threat-visual__buffering" role="status">
<span className="busy-indicator" aria-hidden="true" />
<span>Догружаем semantic-point evidence E47</span>
</div>
) : null}
{semanticTimeline.error ? (
<div className="m4-replay-threat-visual__buffering" role="alert">
<Icon name="alert" size={16} />
<span>{semanticTimeline.error}</span>
</div>
) : null}
{semanticIntegrityError ? (
<div className="m4-replay-threat-visual__buffering" role="alert">
<Icon name="alert" size={16} />
<span>{semanticIntegrityError}</span>
</div>
) : null}
{frame && !frame.spatialAvailable && (mode === "3d" || mode === "plan") ? (
<div className="m4-replay-threat-visual__buffering" role="status">
На этом кадре нет квалифицированного body frame; сцена сохранена.
@@ -409,7 +529,9 @@ export function M4ReplayThreatVisual({ resultId }: { resultId: string }) {
return (
<div className="l3-visual-audit m4-replay-threat-visual">
<LaboratoryEvidenceViewer
label="M4.6 dual-evidence recorded-realtime replay"
label={semantic
? "E47 semantic + SLAM diagnostic replay"
: "M4.6 dual-evidence recorded-realtime replay"}
className="m4-replay-threat-evidence-viewer"
mode={mode}
modes={[
@@ -64,6 +64,13 @@ const KNOWN_WORKS: Readonly<Record<Exclude<LaboratoryWorkId, `session:${string}`
experimentName: "RAVNOVES00 dual-evidence threat qualification",
variantName: "M4.6 · virtual corridor replay · VIDEO/CAMERA/3D",
},
"e47-semantic-slam-shadow": {
profileId: "rig-dual-evidence-virtual-corridor-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · Camera + LiDAR dual evidence`,
experimentId: "ravnoves00-semantic-slam-shadow-r1",
experimentName: "RAVNOVES00 semantic mask → KB4 → SLAM diagnostic shadow",
variantName: "E47 · EoMT control · full semantic point projection",
},
"e28-local-surface": {
profileId: "rig-camera-local-surface-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · Camera-first + local-surface LiDAR`,
@@ -43,6 +43,7 @@ function mergeResults(
e46h: next.e46h ?? current.e46h,
e46i: next.e46i ?? current.e46i,
e46j: next.e46j ?? current.e46j,
e47: next.e47 ?? current.e47,
l34: next.l34 ?? current.l34,
l34a: next.l34a ?? current.l34a,
l34b: next.l34b ?? current.l34b,
@@ -0,0 +1,113 @@
import { useEffect, useMemo, useRef, useState } from "react";
import {
fetchE47SemanticTimelineChunk,
type E47SemanticClass,
type E47SemanticTimelineChunk,
type E47SemanticTimelineFrame,
} from "../../core/laboratory/e47SemanticSlam";
const CHUNK_SIZE = 24;
const RETAINED_CHUNK_COUNT = 8;
const PREFETCH_CHUNKS_AHEAD = 2;
function chunkWindowStarts(activeStart: number, frameCount: number): readonly number[] {
return Array.from(
{ length: PREFETCH_CHUNKS_AHEAD + 2 },
(_, index) => activeStart + (index - 1) * CHUNK_SIZE,
).filter((start) => start >= 0 && start < frameCount);
}
function errorMessage(error: unknown): string {
return error instanceof Error && error.message.trim()
? error.message
: "Semantic point evidence E47 недоступен.";
}
export function useE47SemanticTimelineFrame({
resultId,
activeSequence,
frameCount,
taxonomy,
}: {
resultId: string | null;
activeSequence: number | null;
frameCount: number;
taxonomy: readonly E47SemanticClass[];
}) {
const [chunks, setChunks] = useState<ReadonlyMap<number, E47SemanticTimelineChunk>>(
() => new Map(),
);
const [error, setError] = useState<string | null>(null);
const chunksRef = useRef(chunks);
const inFlight = useRef(new Map<number, AbortController>());
const activeStartRef = useRef<number | null>(null);
chunksRef.current = chunks;
useEffect(() => {
for (const controller of inFlight.current.values()) controller.abort();
inFlight.current.clear();
const empty = new Map<number, E47SemanticTimelineChunk>();
chunksRef.current = empty;
setChunks(empty);
setError(null);
return () => {
for (const controller of inFlight.current.values()) controller.abort();
inFlight.current.clear();
};
}, [resultId]);
const activeStart = activeSequence === null
? null
: Math.floor(activeSequence / CHUNK_SIZE) * CHUNK_SIZE;
activeStartRef.current = activeStart;
useEffect(() => {
if (!resultId || activeStart === null || frameCount < 1) return;
for (const start of chunkWindowStarts(activeStart, frameCount)) {
if (chunksRef.current.has(start) || inFlight.current.has(start)) continue;
const controller = new AbortController();
inFlight.current.set(start, controller);
void fetchE47SemanticTimelineChunk(resultId, start, CHUNK_SIZE, {
signal: controller.signal,
taxonomy,
})
.then((chunk) => {
if (controller.signal.aborted) return;
setChunks((current) => {
const next = new Map(current);
next.set(start, chunk);
const center = activeStartRef.current ?? start;
const retained = [...next.keys()]
.sort((left, right) => Math.abs(left - center) - Math.abs(right - center))
.slice(0, RETAINED_CHUNK_COUNT);
const bounded = new Map(retained.map((key) => [key, next.get(key)!]));
chunksRef.current = bounded;
return bounded;
});
if (start === activeStartRef.current) setError(null);
})
.catch((caught: unknown) => {
if (!controller.signal.aborted && start === activeStartRef.current) {
setError(errorMessage(caught));
}
})
.finally(() => {
if (inFlight.current.get(start) === controller) inFlight.current.delete(start);
});
}
}, [activeStart, frameCount, resultId, taxonomy]);
const activeFrame: E47SemanticTimelineFrame | null = useMemo(() => {
if (activeSequence === null || activeStart === null) return null;
return chunks.get(activeStart)?.frames.find(
(frame) => frame.sequence === activeSequence,
) ?? null;
}, [activeSequence, activeStart, chunks]);
return {
activeFrame,
loading: Boolean(resultId) && activeSequence !== null && !activeFrame && !error,
error,
};
}
@@ -0,0 +1,192 @@
import assert from "node:assert/strict";
import { after, before, test } from "node:test";
import { createServer } from "vite";
let server;
let fetchE47SemanticSlamResult;
let fetchE47SemanticTimelineChunk;
let e47SemanticMaskUrl;
const resultId = `e47-semantic-slam-${"a".repeat(64)}`;
const baseM4ResultId = `m4-threat-replay-${"b".repeat(64)}`;
before(async () => {
server = await createServer({
appType: "custom",
logLevel: "silent",
server: { middlewareMode: true },
});
({
fetchE47SemanticSlamResult,
fetchE47SemanticTimelineChunk,
e47SemanticMaskUrl,
} = await server.ssrLoadModule("/src/core/laboratory/e47SemanticSlam.ts"));
});
after(async () => {
await server?.close();
});
function resultView(overrides = {}) {
return {
schema_version: "missioncore.e47-semantic-slam-view/v1",
result_id: resultId,
created_at_utc: "2026-08-06T10:00:00.000Z",
status: "diagnostic-semantic-slam-shadow",
profile_id: "ravnoves00-eomt-kb4-slam-shadow/v1",
base_m4_result_id: baseM4ResultId,
semantic_result_id: `result-${"c".repeat(64)}`,
geometry_result_id: `m4-geometry-replay-${"d".repeat(64)}`,
source_pack_id: `e10-lidar-pack-${"e".repeat(64)}`,
calibration_content_sha256: "f".repeat(64),
provider: {
provider_id: "eomt-cityscapes-semantic-control/v1",
model_id: "tue-mps/cityscapes_semantic_eomt_large_1024",
model_revision: "revision-1",
model_weights_sha256: "1".repeat(64),
preprocess_id: "raw-kb4-valid-fov-semantic/v1",
},
temporal_binding: {
semantic_to_camera: "exact-sequence-and-session-time",
camera_to_lidar: "accepted-e6-nearest-host-arrival-best-effort",
clock_basis: "recorded-host-monotonic-arrival",
maximum_lidar_camera_delta_ms: 100,
maximum_pose_point_delta_ms: 100,
physical_synchronization_proven: false,
},
taxonomy: [
{
class_id: 0,
label: "outside_valid_fov",
disposition: "ambiguous",
color_rgb: [0, 0, 0],
},
{
class_id: 7,
label: "paved_road",
disposition: "labeled",
color_rgb: [128, 64, 128],
},
],
metrics: {
frames: { total: 4489, mask_available: 4489, source_available: 4489 },
points: {
total: 4,
projected: 2,
labeled: 1,
ambiguous: 1,
unprojected: 2,
absent: 0,
},
observations: {
total: 2,
labeled: 1,
ambiguous: 0,
unprojected: 1,
absent: 0,
},
runtime: { elapsed_ms: 1000, frames_per_second: 4.489 },
},
acceptance: {
artifact_contract_passed: true,
frame_accounting_passed: true,
point_accounting_passed: true,
observation_binding_passed: true,
temporal_binding_passed: true,
independent_semantic_truth_passed: false,
provider_promoted: false,
},
limitations: ["diagnostic only"],
ground_truth: false,
semantic_authority: "diagnostic-only",
navigation_or_safety_accepted: false,
actuation_allowed: false,
...overrides,
};
}
test("E47 accepts only a fully accounted diagnostic semantic/SLAM view", async () => {
const result = await fetchE47SemanticSlamResult({
fetcher: async () => new Response(JSON.stringify({
schema_version: "missioncore.e47-semantic-slam-catalog/v1",
items: [resultView()],
})),
});
assert.equal(result.resultId, resultId);
assert.equal(result.baseM4ResultId, baseM4ResultId);
assert.equal(result.metrics.points.projected, 2);
assert.equal(result.acceptance.independentSemanticTruthPassed, false);
assert.equal(result.temporalBinding.physicalSynchronizationProven, false);
assert.equal(result.temporalBinding.maximumLidarCameraDeltaMs, 100);
assert.equal(result.acceptance.temporalBindingPassed, true);
assert.equal(result.taxonomy[0].disposition, "ambiguous");
assert.equal(
e47SemanticMaskUrl(resultId, 14),
`/api/v1/laboratory/e47-semantic-slam/results/${resultId}/masks/14`,
);
});
test("E47 rejects result-level point accounting drift", async () => {
await assert.rejects(
fetchE47SemanticSlamResult({
fetcher: async () => new Response(JSON.stringify({
schema_version: "missioncore.e47-semantic-slam-catalog/v1",
items: [resultView({
metrics: {
...resultView().metrics,
points: {
...resultView().metrics.points,
total: 5,
},
},
})],
})),
}),
/point accounting/,
);
});
test("E47 chunk preserves unavailable sentinel and verifies the status histogram", async () => {
const validFrame = {
schema_version: "missioncore.e47-semantic-slam-frame/v1",
sequence: 14,
source_point_count: 4,
class_ids: [7, 0, -1, -1],
status_codes: [3, 2, 1, 1],
counts: { labeled: 1, ambiguous: 1, unprojected: 2, absent: 0 },
};
const chunk = await fetchE47SemanticTimelineChunk(resultId, 14, 1, {
taxonomy: [
{ classId: 0, label: "outside_valid_fov", disposition: "ambiguous", colorRgb: [0, 0, 0] },
{ classId: 7, label: "paved_road", disposition: "labeled", colorRgb: [128, 64, 128] },
],
fetcher: async () => new Response(JSON.stringify({
schema_version: "missioncore.e47-semantic-slam-chunk/v1",
result_id: resultId,
start_sequence: 14,
frame_count: 1,
next_sequence: 15,
frames: [validFrame],
})),
});
assert.deepEqual(chunk.frames[0].classIds, [7, 0, -1, -1]);
await assert.rejects(
fetchE47SemanticTimelineChunk(resultId, 14, 1, {
taxonomy: [
{ classId: 0, label: "outside_valid_fov", disposition: "ambiguous", colorRgb: [0, 0, 0] },
{ classId: 7, label: "paved_road", disposition: "labeled", colorRgb: [128, 64, 128] },
],
fetcher: async () => new Response(JSON.stringify({
schema_version: "missioncore.e47-semantic-slam-chunk/v1",
result_id: resultId,
start_sequence: 14,
frame_count: 1,
next_sequence: 15,
frames: [{ ...validFrame, class_ids: [7, 0, 7, -1] }],
})),
}),
/class\/status binding/,
);
});
@@ -0,0 +1,69 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import { test } from "node:test";
const component = (name) => new URL(`../src/components/laboratory/${name}`, import.meta.url);
test("semantic evidence mask is decoded, cached, cancelled and object-contained client-side", async () => {
const source = await readFile(component("RecordedEvidenceSemanticMaskOverlay.tsx"), "utf8");
assert.match(source, /decodedMaskCache/);
assert.match(source, /pendingMaskCache/);
assert.match(source, /AbortController/);
assert.match(source, /getImageData/);
assert.match(source, /8-bit grayscale class-id PNG/);
assert.match(source, /ResizeObserver/);
assert.match(source, /Math\.min\(width \/ imageWidth, height \/ imageHeight\)/);
assert.match(source, /decoded\.key !== expectedKey/);
assert.match(source, /mask\?\.key === expectedKey/);
assert.match(source, /необъявленный class ID/);
assert.match(source, /recorded-evidence-semantic-mask-overlay__error/);
assert.match(source, /role="alert"/);
});
test("shared recorded scenes place optional semantic masks beneath boxes", async () => {
const [imageScene, videoScene] = await Promise.all([
readFile(component("RecordedEvidenceImageScene.tsx"), "utf8"),
readFile(component("RecordedEvidenceVideoScene.tsx"), "utf8"),
]);
for (const source of [imageScene, videoScene]) {
assert.match(source, /semanticOverlay\?: RecordedEvidenceSemanticOverlay/);
assert.ok(
source.indexOf("<RecordedEvidenceSemanticMaskOverlay")
< source.indexOf("<RecordedEvidenceBoxOverlay"),
);
}
});
test("metric evidence keeps missing semantic assignments as context and exposes a taxonomy legend", async () => {
const source = await readFile(component("LaboratoryMetricEvidenceScene.tsx"), "utf8");
assert.match(source, /pointSemanticClassIds\?: readonly \(number \| null\)\[\]/);
assert.match(source, /pointSemanticClassIds\.length === pointCloudBodyXyzM\.length/);
assert.match(source, /classId === null \? undefined : colorsByClassId\.get\(classId\)/);
assert.match(source, /data-decision="semantic"/);
assert.match(source, /recordedEvidenceSemanticCssColor/);
});
test("semantic point alignment is enforced only when M4 exposes the exact spatial increment", async () => {
const source = await readFile(
new URL("../src/workspaces/laboratory/M4ReplayThreatVisual.tsx", import.meta.url),
"utf8",
);
assert.match(source, /semantic && frame\?\.spatialAvailable && semanticFrame/);
assert.match(source, /\|\| !frame\.spatialAvailable\s*\|\| !semanticFrame/);
assert.match(source, /semanticFrame\.sourcePointCount !== frame\.pointCloudSourceCount/);
});
test("M4 mounts semantic mask overlays only for the active VIDEO or CAMERA layer", async () => {
const source = await readFile(
new URL("../src/workspaces/laboratory/M4ReplayThreatVisual.tsx", import.meta.url),
"utf8",
);
assert.match(
source,
/semanticOverlay=\{mode === "video" \? semanticOverlay : undefined\}/,
);
assert.match(
source,
/semanticOverlay=\{mode === "camera" \? semanticOverlay : undefined\}/,
);
});