Files
NODEDC_MISSION_CORE/apps/control-station/test/m48ObjectCentricQuality.test.mjs

726 lines
32 KiB
JavaScript

import assert from "node:assert/strict";
import { readFileSync, readdirSync } from "node:fs";
import { after, before, test } from "node:test";
import { createServer } from "vite";
let server;
let decodeM48GateStatus;
let decodeM48ReviewSourceCatalog;
let decodeM48CorrectionSession;
let decodeM48SpatialFrame;
let decodeM48QualityResult;
let decodeM48FailureAtlas;
let decodeM48FailureCase;
let assertM48BlindPayload;
let interpolateM48Extent;
let createM48Tracklet;
let nextM48ObjectId;
let laboratoryMetricLegendEntries;
let nearestLaboratoryRecordedClipFrame;
let laboratoryRecordedClipEndExclusiveNs;
let laboratoryRecordedClipClockGate;
let m48SpatialPlaybackWindow;
let trimM48SpatialPlaybackCache;
let nextM48CameraVisibility;
let nextM48SpatialMode;
const packId = `m48-object-quality-pack-${"a".repeat(64)}`;
before(async () => {
server = await createServer({ appType: "custom", logLevel: "silent", server: { middlewareMode: true } });
({
decodeM48GateStatus,
decodeM48ReviewSourceCatalog,
decodeM48CorrectionSession,
decodeM48SpatialFrame,
decodeM48QualityResult,
decodeM48FailureAtlas,
decodeM48FailureCase,
assertM48BlindPayload,
} = await server.ssrLoadModule("/src/core/laboratory/m48ObjectCentricQuality.ts"));
({
interpolateM48Extent,
createM48Tracklet,
nextM48ObjectId,
} = await server.ssrLoadModule("/src/workspaces/laboratory/annotation/M48BlindClipPlayer.tsx"));
({
nearestLaboratoryRecordedClipFrame,
laboratoryRecordedClipEndExclusiveNs,
laboratoryRecordedClipClockGate,
} = await server.ssrLoadModule(
"/src/components/laboratory/LaboratoryRecordedClipPlayer.tsx",
));
({ laboratoryMetricLegendEntries } = await server.ssrLoadModule(
"/src/components/laboratory/LaboratoryMetricEvidenceScene.tsx",
));
({
m48SpatialPlaybackWindow,
trimM48SpatialPlaybackCache,
} = await server.ssrLoadModule(
"/src/workspaces/laboratory/annotation/useM48SpatialClipPlayback.ts",
));
({
nextM48CameraVisibility,
nextM48SpatialMode,
} = await server.ssrLoadModule(
"/src/workspaces/laboratory/annotation/M48EvidenceModeControls.tsx",
));
});
after(async () => server?.close());
function authority() {
return {
mode: "replay-simulated",
physical_live: false,
commands_enabled: false,
actuation_allowed: false,
navigation_or_safety_accepted: false,
};
}
function source() {
const clips = Array.from({ length: 20 }, (_, index) => {
const start = index * 2 + 1;
return {
clip_id: `clip-${String(index + 1).padStart(2, "0")}`,
start_sequence: start,
end_sequence: start + 1,
frames: [start, start + 1].map((sequence) => ({
sequence,
source_time_ns: (sequence - 1) * 100_000_000,
camera_fragment_sha256: String(index % 10).repeat(64),
camera_url: `/api/v1/laboratory/m48/packs/${packId}/source/clips/clip-${String(index + 1).padStart(2, "0")}/frames/${sequence}/camera`,
spatial_url: `/api/v1/laboratory/m48/packs/${packId}/source/clips/clip-${String(index + 1).padStart(2, "0")}/frames/${sequence}/spatial`,
})),
};
});
return {
schema_version: "missioncore.m48-neutral-object-review-source/v2",
pack_id: packId,
state: "prediction-blind-neutral-source-projection",
contract: {
contract_id: "m48-class-free-object-tracklet/v1",
label_unit: "clip-local-object-tracklet",
semantic_classes_allowed: false,
extent: "normalized-xyxy-sparse-keyframes",
extent_interpolation: "linear-between-bounding-keyframes",
visibility: ["occluded", "partial", "visible"],
state_segments: {
coverage: "contiguous-full-tracklet-lifetime",
geometry_association: ["associated", "ineligible", "unavailable", "unknown"],
freshness: ["current", "held", "stale", "unavailable"],
motion: ["moving", "static", "unknown", "unsupported"],
threat: ["not-threat", "threat", "unknown"],
critical_corridor_obstacle: "boolean",
},
},
camera_playback: {
schema_version: "missioncore.laboratory-recorded-clip-camera/v1",
source_id: "recorded.camera.test",
label: "Записанная RIGHT камера",
manifest_url: "/api/v1/observation-sessions/recorded-session/media/recorded-video-test/manifest",
manifest_generation_sha256: "f".repeat(64),
byte_length: 123456,
media_type: "video/mp4",
timeline_start_seconds: 0,
timeline_end_seconds: 4,
segment_count: 40,
seekable: true,
synchronization: "host-arrival-best-effort",
transport: "recorded-fmp4-manifest",
fragment_binding: "pack-frozen-sha256-verified",
},
clips,
clip_count: clips.length,
frame_count: 40,
strata_included: false,
split_included: false,
candidate_identity_included: false,
frozen_predictions_included: false,
model_scores_included: false,
semantic_class_task_included: false,
evidence_capabilities: {
state: "prediction-free-spatial-evidence-available",
camera_epoch_time: true,
current_point_cloud_body_xyz_m: true,
rig: true,
virtual_corridor: true,
raw_lidar: false,
graph_output: false,
graph_boxes_ids_scores: false,
label_authority: {
obstacle_presence_and_extent: true,
geometry_association: true,
freshness: true,
motion: true,
threat: true,
critical_corridor_obstacle: true,
},
fail_closed_reason: null,
},
access: "prediction-free-strata-free-source-read-only",
};
}
test("M4.8 pack status stays fail-closed and command-free", () => {
const result = decodeM48GateStatus({
schema_version: "missioncore.m48-object-quality-pack-status/v1",
pack_id: packId,
created_at_utc: "2026-08-24T10:00:00Z",
state: "prepared",
metrics: { clip_count: 20, frame_count: 1020, seed_object_count: 5236, correction_state: "not-started", correction_reviewed_clip_count: 0, correction_complete: false, review_slot_count: 0, frozen_reviewer_count: 0, required_frozen_reviewer_count: 2 },
decision: { review_collection_ready: true, two_distinct_reviews_frozen: false, adjudication_unlocked: false, adjudication_frozen: false, evaluated: false, next_action: "freeze two reviews" },
truth_seal_id: null,
quality_result_id: null,
blindness: { candidate_identity_included: false, frozen_predictions_included: false, model_scores_included: false, semantic_class_task_included: false, strata_included: false },
authority: authority(),
access: "neutral-workflow-status-read-only",
});
assert.equal(result.packId, packId);
assert.equal(result.authority.commandsEnabled, false);
assert.equal(result.correctionState, "not-started");
assert.equal(result.seedObjectCount, 5236);
assert.equal(result.requiredFrozenReviewerCount, 2);
});
test("blind source admits fragment identity and structurally rejects candidate material", () => {
const result = decodeM48ReviewSourceCatalog(source());
assert.equal(result.clips.length, 20);
assert.equal(result.cameraPlayback.segmentCount, 40);
assert.equal(result.cameraPlayback.manifestGenerationSha256, "f".repeat(64));
assert.equal(result.clips[0].frames[0].cameraFragmentSha256.length, 64);
assert.equal(result.evidenceCapabilities.currentPointCloudBodyXyzM, true);
for (const leak of [
{ predictions: [] },
{ strata: ["no-object"] },
{ model: { id: "candidate" } },
{ graph: [] },
{ split: "validation" },
{ semantic_class: "car" },
]) assert.throws(() => assertM48BlindPayload(leak), /blind|candidate|stratum/i);
const staleName = source();
staleName.clips[0].frames[0].camera_frame_sha256 = staleName.clips[0].frames[0].camera_fragment_sha256;
delete staleName.clips[0].frames[0].camera_fragment_sha256;
assert.throws(() => decodeM48ReviewSourceCatalog(staleName), /состав полей/);
});
test("candidate-assisted correction admits frozen boxes without claiming independent truth", () => {
const candidate = {
schema_version: "missioncore.m48-assisted-object-correction-session/v1",
pack_id: packId,
session_id: `m48-correction-session-${"b".repeat(64)}`,
title: "Worker 006 correction",
revision: 0,
state: "draft",
created_at_utc: "2026-08-24T12:00:00Z",
updated_at_utc: "2026-08-24T12:00:00Z",
clips: [{
clip_id: "clip-01",
start_sequence: 1,
end_sequence: 2,
review_state: "pending",
no_object: null,
tracklets: [{
object_id: "proposal-0-0",
first_sequence: 1,
last_sequence: 1,
keyframes: [{ sequence: 1, extent_xyxy: [0.1, 0.2, 0.3, 0.4], visibility: "visible" }],
state_segments: [{ start_sequence: 1, end_sequence: 1, geometry_association: "associated", freshness: "current", motion: "unknown", threat: "unknown", critical_corridor_obstacle: false }],
notes: null,
}],
notes: null,
}],
progress: { reviewed_clip_count: 0, clip_count: 1, complete: false },
seed_summary: { worker_id: "006", clip_count: 1, frame_count: 2, object_count: 1, prediction_rows_sha256: "c".repeat(64) },
evidence_summary: null,
reviewer_id: null,
submitted_at_utc: null,
submission_sha256: null,
assistance: { mode: "frozen-candidate-seeded", candidate_predictions_seen: true, model_scores_seen: false, semantic_class_task_seen: false, independent_truth_eligible: false },
authority: authority(),
access: "capability-protected-candidate-assisted-correction",
};
const decoded = decodeM48CorrectionSession(candidate);
assert.equal(decoded.seedWorkerId, "006");
assert.equal(decoded.seedObjectCount, 1);
assert.equal(decoded.clips[0].tracklets[0].objectId, "proposal-0-0");
candidate.assistance.independent_truth_eligible = true;
assert.throws(() => decodeM48CorrectionSession(candidate), /assistance|truth/i);
});
test("spatial frame accepts only current points, virtual rig and corridor", () => {
const result = decodeM48SpatialFrame({
schema_version: "missioncore.m48-neutral-object-review-spatial-frame/v1",
pack_id: packId,
clip_id: "clip-01",
sequence: 1,
source_time_ns: 0,
point_cloud_body_xyz_m: [[1, 0, 0.25]],
rig: { profile_id: "virtual-rig", length_m: 1, width_m: 0.6, lidar_reference: "rear", nominal_sensor_height_m: 1.25, physical_mount_claimed: false },
corridor: { profile_id: "corridor", forward_length_m: 8, rear_margin_m: 0.2, lateral_clearance_m: 0.25, half_width_m: 0.55, prediction_horizon_seconds: 5 },
occupied_voxel_size_m: 0.2,
source_available: true,
body_frame_available: true,
candidate_identity_included: false,
graph_boxes_ids_scores_included: false,
frozen_predictions_included: false,
strata_included: false,
authority: authority(),
access: "prediction-free-current-spatial-evidence-read-only",
});
assert.deepEqual(result.pointCloudBodyXyzM, [[1, 0, 0.25]]);
assert.equal(result.sourceAvailable, true);
assert.equal(result.bodyFrameAvailable, true);
assert.equal(result.corridor.forwardLengthM, 8);
const unavailable = {
schema_version: "missioncore.m48-neutral-object-review-spatial-frame/v1",
pack_id: packId,
clip_id: "clip-01",
sequence: 1,
source_time_ns: 0,
point_cloud_body_xyz_m: [[1, 0, 0.25]],
rig: { profile_id: "virtual-rig", length_m: 1, width_m: 0.6, lidar_reference: "rear", nominal_sensor_height_m: 1.25, physical_mount_claimed: false },
corridor: { profile_id: "corridor", forward_length_m: 8, rear_margin_m: 0.2, lateral_clearance_m: 0.25, half_width_m: 0.55, prediction_horizon_seconds: 5 },
occupied_voxel_size_m: 0.2,
source_available: true,
body_frame_available: false,
candidate_identity_included: false,
graph_boxes_ids_scores_included: false,
frozen_predictions_included: false,
strata_included: false,
authority: authority(),
access: "prediction-free-current-spatial-evidence-read-only",
};
assert.throws(() => decodeM48SpatialFrame(unavailable), /unavailable spatial frame/);
});
test("tracklet extents interpolate on the shared clip timeline", () => {
const tracklet = {
objectId: "object-01",
firstSequence: 10,
lastSequence: 20,
keyframes: [
{ sequence: 10, extentXyxy: [0.1, 0.2, 0.3, 0.4], visibility: "visible" },
{ sequence: 20, extentXyxy: [0.2, 0.3, 0.4, 0.5], visibility: "visible" },
],
stateSegments: [],
notes: null,
};
const midpoint = interpolateM48Extent(tracklet, 15);
assert.ok(midpoint.every((value, index) => Math.abs(value - [0.15, 0.25, 0.35, 0.45][index]) < 1e-12));
assert.equal(interpolateM48Extent(tracklet, 9), null);
});
test("manual correction is frame-local, fails closed without spatial authority and never reuses a deleted id", () => {
const clip = decodeM48ReviewSourceCatalog(source()).clips[0];
const tracklet = createM48Tracklet("object-01", clip, [0.1, 0.2, 0.3, 0.4], false, clip.endSequence);
assert.equal(tracklet.firstSequence, clip.endSequence);
assert.equal(tracklet.lastSequence, clip.endSequence);
assert.equal(tracklet.stateSegments[0].startSequence, clip.endSequence);
assert.equal(tracklet.stateSegments[0].endSequence, clip.endSequence);
assert.equal(interpolateM48Extent(tracklet, clip.startSequence), null);
assert.deepEqual(interpolateM48Extent(tracklet, clip.endSequence), [0.1, 0.2, 0.3, 0.4]);
assert.equal(tracklet.stateSegments[0].geometryAssociation, "unavailable");
assert.equal(tracklet.stateSegments[0].motion, "unsupported");
assert.equal(nextM48ObjectId([
tracklet,
{ ...tracklet, objectId: "object-03" },
]), "object-02");
});
test("shared recorded clip clock selects exact frames and one stable loop boundary", () => {
const frames = [
{ sequence: 11, sourceTimeNs: 1_000_000_000 },
{ sequence: 12, sourceTimeNs: 1_100_000_000 },
{ sequence: 13, sourceTimeNs: 1_200_000_000 },
];
assert.equal(nearestLaboratoryRecordedClipFrame(frames, 1_049_000_000).sequence, 11);
assert.equal(nearestLaboratoryRecordedClipFrame(frames, 1_051_000_000).sequence, 12);
assert.equal(laboratoryRecordedClipEndExclusiveNs(frames), 1_300_000_000);
});
test("shared recorded clip rejects stale media callbacks until an explicit seek lands", () => {
assert.deepEqual(laboratoryRecordedClipClockGate(1, 123), {
accept: false,
pendingSequence: 1,
});
assert.deepEqual(laboratoryRecordedClipClockGate(1, 1), {
accept: true,
pendingSequence: null,
});
assert.deepEqual(laboratoryRecordedClipClockGate(null, 124), {
accept: true,
pendingSequence: null,
});
});
test("M4.8 camera and spatial visibility are independent without an empty viewer", () => {
assert.equal(nextM48CameraVisibility("camera", true), true);
assert.equal(nextM48CameraVisibility("3d", true), false);
assert.equal(nextM48CameraVisibility("3d", false), true);
assert.equal(nextM48SpatialMode("camera", true, "3d"), "3d");
assert.equal(nextM48SpatialMode("3d", true, "3d"), "camera");
assert.equal(nextM48SpatialMode("3d", false, "3d"), "3d");
assert.equal(nextM48SpatialMode("3d", false, "plan"), "plan");
});
test("M4.8 spatial playback prefetches across the loop and remains bounded", () => {
const frames = [11, 12, 13, 14, 15].map((sequence) => ({ sequence }));
assert.deepEqual(m48SpatialPlaybackWindow(frames, 14, 4), [14, 15, 11, 12]);
const cache = new Map(Array.from({ length: 30 }, (_, index) => [index + 1, index]));
trimM48SpatialPlaybackCache(cache, [28, 29, 30, 1], 24);
assert.equal(cache.size, 24);
for (const sequence of [28, 29, 30, 1]) assert.equal(cache.has(sequence), true);
});
test("post-seal result and atlas reveal bounded graph material only after evaluation", () => {
const resultId = `m48-object-quality-result-${"b".repeat(64)}`;
const truthId = `m48-object-truth-seal-${"c".repeat(64)}`;
const metricNames = [
"terminal_outcome_accounting",
"false_free_space_claims",
"obstacle_presence_precision",
"obstacle_presence_recall",
"critical_corridor_obstacle_recall",
"geometry_association_correctness",
"freshness_correctness",
"motion_decision_correctness",
"critical_threat_not_threat",
"unknown_prediction_count",
"failure_case_count",
];
const metrics = Object.fromEntries(metricNames.map((name) => [name, name.endsWith("_count") || name === "false_free_space_claims" ? 0 : 1]));
const quality = decodeM48QualityResult({
schema_version: "missioncore.m48-object-centric-quality-result-view/v1",
result_id: resultId,
pack_id: packId,
truth_seal_id: truthId,
created_at_utc: "2026-08-24T12:00:00Z",
status: "accepted-object-centric-source-quality",
accepted: true,
metrics,
gates: { obstacle_presence_precision: true },
unknown_causes: {},
prediction_material_release: "post-adjudication-seal-evaluation-only",
authority: authority(),
ground_truth: false,
access: "evaluated-object-quality-summary-read-only",
});
assert.equal(quality.accepted, true);
const caseId = `m48-failure-${"d".repeat(64)}`;
const atlas = decodeM48FailureAtlas({
schema_version: "missioncore.m48-object-quality-failure-atlas-view/v1",
result_id: resultId,
cases: [{ schema_version: "missioncore.m48-object-quality-failure/v1", failure_case_id: caseId, clip_id: "clip-01", split: "validation", sequence: 1, causes: ["presence-false-negative"], severity: "high", terminal_outcome: "delivered", unmatched_prediction_ids: [], unmatched_truth_object_ids: ["object-01"] }],
case_count: 1,
prediction_material_release: "post-adjudication-seal-evaluation-only",
authority: authority(),
access: "evaluated-bounded-failure-atlas-read-only",
});
assert.equal(atlas[0].caseId, caseId);
assert.equal(atlas[0].split, "validation");
const failure = decodeM48FailureCase({
schema_version: "missioncore.m48-object-quality-failure-case-view/v1",
result_id: resultId,
case: { failure_case_id: caseId, clip_id: "clip-01", split: "validation", sequence: 1, causes: ["presence-false-negative"], severity: "high" },
frame: { sequence: 1, source_time_ns: 0, camera_fragment_sha256: "e".repeat(64), camera_url: `/api/v1/laboratory/m48/packs/${packId}/source/clips/clip-01/frames/1/camera`, spatial_url: null },
truth: [{ object_id: "object-01", extent_xyxy: [0.1, 0.1, 0.3, 0.4], geometry_association: "associated", freshness: "current", motion: "static", threat: "threat" }],
graph: [],
prediction_material_release: "post-adjudication-seal-evaluation-only",
authority: authority(),
access: "evaluated-failure-case-read-only",
});
assert.equal(failure.truth[0].objectId, "object-01");
assert.equal(failure.frame.cameraFragmentSha256, "e".repeat(64));
});
test("M4.8 evidence keeps the shared viewer stage stretched over the visual frame", () => {
const stylesheet = readFileSync(
new URL("../src/styles/laboratory-recorded-clip-player.css", import.meta.url),
"utf8",
);
const player = readFileSync(
new URL("../src/workspaces/laboratory/annotation/M48BlindClipPlayer.tsx", import.meta.url),
"utf8",
);
const shared = readFileSync(
new URL("../src/components/laboratory/LaboratoryRecordedClipPlayer.tsx", import.meta.url),
"utf8",
);
const visual = readFileSync(
new URL("../src/workspaces/laboratory/M48FailureAtlasVisual.tsx", import.meta.url),
"utf8",
);
const evidenceViewer = readFileSync(
new URL("../src/components/laboratory/LaboratoryEvidenceViewer.tsx", import.meta.url),
"utf8",
);
const laboratoryStyles = readFileSync(
new URL("../src/styles/laboratory-evidence-viewer.css", import.meta.url),
"utf8",
);
const modeControls = readFileSync(
new URL("../src/workspaces/laboratory/annotation/M48EvidenceModeControls.tsx", import.meta.url),
"utf8",
);
assert.match(
stylesheet,
/\.laboratory-recorded-clip-player__camera\s*>\s*\.recorded-media-player\s*\{[^}]*position:\s*absolute/s,
);
assert.match(player, /<LaboratoryRecordedClipPlayer/);
assert.doesNotMatch(player, /fetch\(|image\/jpeg|createM48LatestFrameLoader|Следующий camera frame/);
assert.match(shared, /missioncore\.laboratory-recorded-clip-viewer\/v1/);
assert.match(shared, /<RecordedFmp4Player/);
assert.match(shared, /<SplitPane/);
assert.equal((shared.match(/<RecordedFmp4Player/g) ?? []).length, 1);
assert.match(shared, /orientation="vertical"/);
assert.match(shared, /resizable=\{companionVisible\}/);
assert.match(shared, /separatorLabel="Изменить размер 3D\/ПЛАН и правой камеры"/);
assert.match(shared, /segmentSequence=\{frame\?\.sequence\}/);
assert.match(player, /cameraPresentation=\{spatialVisible/);
assert.match(player, /effectiveCameraVisible \? "companion" : "hidden"/);
assert.match(player, /cameraVisible: boolean/);
assert.match(player, /continuousPlayback/);
assert.doesNotMatch(player, /continuousPlayback=\{mode === "camera"\}/);
assert.match(visual, /chromeLayout="stacked"/);
assert.match(visual, /modeControlsVisible=\{false\}/);
assert.match(visual, /<M48EvidenceModeRail/);
assert.match(modeControls, /<GlassSurface/);
assert.match(modeControls, /className="m48-evidence-mode-rail"/);
assert.match(modeControls, /radius="pill"/);
assert.match(modeControls, /padding="sm"/);
assert.match(modeControls, /materialRim=\{false\}/);
assert.match(modeControls, /<Icon name="video" size=\{16\}/);
assert.match(modeControls, /<Icon name="plan" size=\{16\}/);
assert.match(modeControls, />3D<\/span>/);
assert.doesNotMatch(modeControls, /<SegmentedControl|<Button/);
assert.doesNotMatch(modeControls, /value: "camera"[\s\S]*value: "3d"/);
assert.match(evidenceViewer, /chromeLayout\?: "overlay" \| "stacked"/);
assert.match(evidenceViewer, /laboratory-evidence-viewer__header/);
assert.match(
laboratoryStyles,
/data-chrome-layout="stacked"[\s\S]*?grid-template-rows:\s*auto minmax\(0, 1fr\) auto/,
);
assert.match(
stylesheet,
/data-chrome-layout="stacked"[\s\S]*?laboratory-recorded-clip-player\s*\{[\s\S]*?gap:\s*0/,
);
assert.doesNotMatch(stylesheet, /grid-template-columns:[^;]*31%/);
assert.doesNotMatch(
stylesheet,
/border-(?:left|top):\s*1px solid var\(--nodedc-glass-outline\)/,
);
assert.match(
stylesheet,
/nodedc-split-pane__separator::before\s*\{[^}]*background:\s*transparent/s,
);
assert.match(
laboratoryStyles,
/laboratory-evidence-viewer__header\s*\{[^}]*border:\s*0/s,
);
});
test("LAB workspaces cannot fork the frozen recorded clip transport", () => {
const root = new URL("../src/workspaces/laboratory/", import.meta.url);
const sourceFiles = readdirSync(root, { recursive: true })
.filter((name) => /\.(?:ts|tsx)$/.test(String(name)));
for (const name of sourceFiles) {
const sourceText = readFileSync(new URL(String(name), root), "utf8");
assert.doesNotMatch(
sourceText,
/new\s+MediaSource|requestVideoFrameCallback|URL\.createObjectURL|image\/jpeg|setTimeout\s*\(|<RecordedFmp4Player/,
`${name} forks missioncore.laboratory-recorded-clip-viewer/v1`,
);
}
});
test("M4.8 review and adjudication share one focus-owning workspace frame", () => {
const review = readFileSync(
new URL("../src/workspaces/laboratory/annotation/M48BlindReviewWorkspace.tsx", import.meta.url),
"utf8",
);
const adjudication = readFileSync(
new URL("../src/workspaces/laboratory/annotation/M48AdjudicationWorkspace.tsx", import.meta.url),
"utf8",
);
const frame = readFileSync(
new URL("../src/components/laboratory/LaboratoryReviewWorkspaceFrame.tsx", import.meta.url),
"utf8",
);
const frameStyles = readFileSync(
new URL("../src/styles/laboratory-review-workspace.css", import.meta.url),
"utf8",
);
const m48Styles = readFileSync(
new URL("../src/styles/m48-object-centric-quality.css", import.meta.url),
"utf8",
);
const recordedStyles = readFileSync(
new URL("../src/styles/laboratory-recorded-clip-player.css", import.meta.url),
"utf8",
);
for (const workspace of [review, adjudication]) {
assert.match(workspace, /LaboratoryReviewWorkspaceFrame/);
assert.doesNotMatch(workspace, /createPortal|className="m48-review-workspace"/);
assert.doesNotMatch(
workspace,
/@rerun-io|RerunViewer|Blueprint|view_id|blueprint_id/,
"M4.8 must reuse shared viewers instead of defining a per-LAB Rerun layout",
);
}
assert.match(frame, /document\.body\.style\.overflow = "hidden"/);
assert.match(frame, /event\.key === "Escape"/);
assert.match(frame, /keepFocusInside/);
assert.match(frame, /returnFocusTarget\?\.isConnected/);
assert.match(frame, /requestAnimationFrame\(\(\) => target\?\.focus\(\)\)/);
const player = readFileSync(
new URL("../src/workspaces/laboratory/annotation/M48BlindClipPlayer.tsx", import.meta.url),
"utf8",
);
assert.match(player, /spatialFrame\.bodyFrameAvailable/);
assert.match(player, /LiDAR в системе координат корпуса для этого кадра недоступен/);
assert.match(player, /Number\(effectiveCameraVisible\) \+ Number\(spatialVisible\)/);
assert.match(player, /data-spatial-sequence=\{spatialReady \? sequence : undefined\}/);
assert.match(review, /<M48EvidenceModeRail/);
assert.match(adjudication, /<M48EvidenceModeRail/);
assert.match(review, /<FieldFrame label="Выбор клипа"/);
assert.match(review, /label="Предыдущий клип"/);
assert.match(review, /label="Следующий клип"/);
assert.match(review, /<IconButton\s+label="Добавить объект"/);
assert.doesNotMatch(review, />\s*Добавить объект\s*<\/Button>/);
assert.ok(review.indexOf('label="Предыдущий клип"') < review.indexOf('<FieldFrame label="Выбор клипа"'));
assert.ok(review.indexOf('className="m48-review-workspace__clip-reviewed"') < review.indexOf('className="m48-review-workspace__topbar-end"'));
assert.doesNotMatch(review, /m48-review-workspace__topbar-center/);
assert.ok(review.indexOf('label="Добавить объект"') < review.indexOf('label="Закрыть проверку M4.8"'));
assert.doesNotMatch(review, /label="Сохранить всю проверку"/);
assert.match(review, /void save\(nextDrafts, "clip-status", \{ drafts, dirty \}\)/);
assert.match(review, /title: reason === "clip-status" \? "Статус клипа сохранён"/);
assert.match(review, /setDrafts\(rollback\.drafts\);\s*setDirty\(rollback\.dirty\);/);
assert.match(review, /className="m48-review-workspace__stage-shell"/);
assert.match(review, /className="m48-review-workspace__source-sticker"/);
assert.match(review, /materialRim=\{false\}/);
assert.match(review, /label="Добавить ещё один объект"/);
assert.match(review, /label="Сохранить изменения объекта"/);
assert.match(review, /<FieldFrame label="Проезд"/);
assert.match(review, /label="Объезд или запас"/);
assert.match(review, /<FieldFrame label="Непосредственная опасность"/);
assert.match(review, /label="Закрыть редактор объекта"/);
assert.doesNotMatch(review, /className="m48-review-workspace__(?:reviewbar|review-actions|source-state)"/);
assert.doesNotMatch(review, />\s*Сохранить изменения\s*<\/Button>/);
assert.doesNotMatch(review, /Выбранный объект|Рамка здесь|Начало здесь|Конец здесь|trimM48Tracklet|рамка переносится по клипу/);
assert.match(review, /sequence < selectedTracklet\.firstSequence \|\| sequence > selectedTracklet\.lastSequence/);
assert.match(player, /interactionMoved\([\s\S]*?boxInteraction\.startClient,[\s\S]*?event\.clientX/);
assert.match(player, /extentsDiffer\(boxInteraction\.originalExtent, extent\)/);
assert.match(player, /firstSequence: sequence/);
assert.match(player, /lastSequence: sequence/);
assert.doesNotMatch(review, /inspector=\{/);
assert.match(frame, /\{inspector \? <footer/);
assert.doesNotMatch(frameStyles, /border-(?:top|bottom):/);
assert.match(m48Styles, /\.m48-clip-player__pane-label\s*\{[^}]*border:\s*0/s);
assert.match(m48Styles, /\.m48-review-workspace__object-tools\s*\{[^}]*justify-content:\s*flex-start/s);
assert.match(m48Styles, /\.m48-review-workspace__topbar\s*\{[^}]*display:\s*grid[^}]*grid-template-columns:\s*minmax\(0, 1fr\) auto/s);
assert.match(m48Styles, /\.m48-evidence-mode-rail\s*\{[^}]*position:\s*absolute[^}]*left:\s*var\(--nodedc-space-4\)[^}]*translateY\(-50%\)/s);
assert.match(m48Styles, /\.m48-evidence-mode-controls\s*\{[^}]*flex-direction:\s*column[^}]*gap:\s*var\(--nodedc-space-2\)/s);
assert.match(m48Styles, /\.m48-evidence-mode-controls__text\s*\{[^}]*font-size:\s*var\(--nodedc-font-size-xs\)/s);
assert.match(m48Styles, /\.m48-review-workspace__clip-field\s*\{[^}]*19vw/s);
assert.match(m48Styles, /\.m48-review-workspace__clip-reviewed\s*\{[^}]*16vw/s);
assert.match(m48Styles, /\.m48-review-workspace__passage-field\s*\{[^}]*18vw/s);
assert.match(m48Styles, /\.m48-review-workspace__source-sticker\s*\{[^}]*position:\s*absolute[^}]*pointer-events:\s*none/s);
assert.match(
recordedStyles,
/\.laboratory-recorded-clip-player__timeline\.observation-timeline\s*\{[^}]*border:\s*0/s,
);
assert.match(
recordedStyles,
/grid-template-columns:\s*auto auto auto minmax\(12rem, 1fr\) auto/,
);
});
test("spatial legend exposes only layers present in the current evidence contract", () => {
const sourceOnly = laboratoryMetricLegendEntries({
pointCloudCount: 128,
localSurfaceCount: 0,
obstacles: [],
showCurrentIncrement: true,
showLocalSurface: false,
showRollingMap: false,
});
assert.deepEqual(sourceOnly, [{ id: "context", label: "Текущий кадр" }]);
const threat = laboratoryMetricLegendEntries({
pointCloudCount: 128,
localSurfaceCount: 64,
obstacles: [{
id: "obstacle-1",
decision: "threat",
state: "retained",
centroidBodyXyzM: [1, 0, 0],
cellCentersBodyXyzM: [[1, 0, 0]],
}],
showCurrentIncrement: true,
showLocalSurface: true,
showRollingMap: true,
});
assert.deepEqual(threat.map(({ id }) => id), [
"threat",
"context",
"local-surface",
"rolling",
]);
});
test("M4.8 full-screen workflow suspends the background evidence owner", () => {
const capability = readFileSync(
new URL("../src/workspaces/laboratory/annotation/useM48ReviewCapability.tsx", import.meta.url),
"utf8",
);
const archive = readFileSync(
new URL("../src/workspaces/laboratory/LaboratoryArchiveWorkspace.tsx", import.meta.url),
"utf8",
);
assert.match(capability, /\{ workspace: ReactNode; active: boolean \}/);
assert.match(archive, /!m48Review\.active \? <div className="laboratory-work-output">/);
assert.match(archive, /\{m48Review\.workspace\}/);
});
test("metric evidence keeps one WebGL renderer while frame labels advance", () => {
const source = readFileSync(
new URL("../src/components/laboratory/LaboratoryMetricEvidenceScene.tsx", import.meta.url),
"utf8",
);
assert.doesNotMatch(source, /renderer\.domElement\.setAttribute\("aria-label", label\)/);
assert.match(source, /renderer\.dispose\(\);[\s\S]{0,600}?\}, \[\]\);/);
assert.match(
source,
/querySelector\("canvas"\)[\s\S]*?setAttribute\("aria-label", label\)[\s\S]*?\}, \[label\]\);/,
);
});
test("metric evidence renders on demand while preserving damped camera interaction", () => {
const source = readFileSync(
new URL("../src/components/laboratory/LaboratoryMetricEvidenceScene.tsx", import.meta.url),
"utf8",
);
assert.match(source, /const requestRenderRef = useRef/);
assert.match(source, /controls\.addEventListener\("change", requestRender\)/);
assert.match(source, /requestRenderRef\.current\(\)/);
assert.doesNotMatch(
source,
/const render = \(\) => \{\s*animationFrame = window\.requestAnimationFrame\(render\)/,
);
});