feat(observatory): ship modular AI inference labs

This commit is contained in:
DCCONSTRUCTIONS
2026-09-04 17:59:05 +03:00
parent eff60e490a
commit cada687173
145 changed files with 17651 additions and 1667 deletions
@@ -125,7 +125,7 @@ test("Observatory owns a bounded explicit-open lifecycle around the shared recor
assert.match(observatory, /data-observatory-authority="observation-only"/);
assert.match(
observatory,
/data-observatory-viewer={replay\.kind === "ready" \? "attached" : "detached"}/,
/data-observatory-viewer={replay\.kind === "ready" \|\| replay\.kind === "composition" \? "attached" : "detached"}/,
);
assert.doesNotMatch(
`${observatory}\n${observatoryCore}`,
@@ -144,8 +144,8 @@ test("Observatory owns a bounded explicit-open lifecycle around the shared recor
);
assert.equal(sharedReplay.match(/<RerunViewport\b/g)?.length, 1);
assert.match(sharedReplay, /recordedSessionRerunProfile/);
assert.match(sharedReplay, /useState<0 \| 1>\(costmap \? 1 : 0\)/);
assert.match(sharedReplay, /costmap \? 0\.000001 : 0/);
assert.match(sharedReplay, /useState<0 \| 1>\(hasTgs \? 1 : 0\)/);
assert.match(sharedReplay, /hasTgs \? 0\.000001 : 0/);
for (const adapter of ["CanonicalVegetationRerunReplay", "PortableResultReplay"]) {
const source = await read(`components/laboratory/${adapter}.tsx`);
assert.match(source, /<CanonicalResultRerunReplay/);
@@ -133,9 +133,12 @@ test("canonical LAB resolves one generation-bound merged RRD", async () => {
});
});
test("portable TGS resolves Core cache for the exact result and base, never a legacy LAB", async () => {
for (const [prefix, sourceKind] of [
["m49-tgs-portable-review", "portable-tgs"],
["lab-v1-eomt-ddrnet", "portable-semantic"],
]) test(`${sourceKind} resolves Core cache for the exact result and base, never a legacy LAB`, async () => {
const base = "a".repeat(64), generation = "b".repeat(64);
const resultId = `m49-tgs-portable-review-${"c".repeat(64)}`;
const resultId = `${prefix}-${"c".repeat(64)}`;
const launch = { sourceUrl: "/api/v1/observation-sessions/source/recording.rrd",
viewerSourceUrl: `/api/v1/observation-sessions/source/recording.rrd?generation=${base}`,
sha256: base };
@@ -147,11 +150,14 @@ test("portable TGS resolves Core cache for the exact result and base, never a le
return new Response(null, { headers: { "Content-Type": "application/vnd.rerun.rrd",
"Content-Length": "100", "ETag": `"${generation}"`, "X-Rerun-Format": "RRF2" } });
};
const options = { sourceKind: "portable-tgs", origin: "http://mission-core.test", fetcher };
const options = { sourceKind, origin: "http://mission-core.test", fetcher };
const replay = await resolveCanonicalLabReplay(resultId, launch, options);
assert.equal(replay.viewerSourceUrl, `${replay.sourceUrl}?generation=${generation}`);
assert.equal(replay.blueprintSourceUrl, "/api/v1/observation-sessions/source/blueprint.rrd");
await assert.rejects(resolveCanonicalLabReplay(`lab-v1-vegetation-shadow-${"c".repeat(64)}`, launch, options));
await assert.rejects(resolveCanonicalLabReplay(resultId, launch, {
...options, sourceKind: sourceKind === "portable-tgs" ? "portable-semantic" : "portable-tgs",
}));
await assert.rejects(resolveCanonicalLabReplay(resultId, { ...launch, sha256: "d".repeat(64) }, options));
assert.equal(calls, 1);
});
@@ -0,0 +1,97 @@
import assert from "node:assert/strict";
import { after, before, test } from "node:test";
import { createServer } from "vite";
let server, fetchLabViewProfile, saveLabViewProfile, normalizeLabSceneSettings;
before(async () => {
server = await createServer({ appType: "custom", logLevel: "silent",
server: { middlewareMode: true } });
({ fetchLabViewProfile, saveLabViewProfile } = await server.ssrLoadModule(
"/src/core/observatory/labViewProfile.ts"));
({ normalizeLabSceneSettings } = await server.ssrLoadModule(
"/src/components/laboratory/CanonicalResultRerunReplay.tsx"));
});
after(async () => { await server?.close(); });
const serverProfile = {
schema_version: "missioncore.observatory-lab-view-profile/v1",
result_id: "result-1",
scene_settings: {
point_size: 4.7,
accumulation_seconds: 8,
color_mode: "height",
palette: "viridis",
show_grid: false,
show_labels: true,
show_camera_frustums: false,
},
updated_at_utc: "2026-09-04T09:30:00.000Z",
};
test("LAB view profile round-trips through the result-scoped server endpoint", async () => {
const originalFetch = globalThis.fetch;
const requests = [];
globalThis.fetch = async (url, init = {}) => {
requests.push({ url, init });
return new Response(JSON.stringify(serverProfile), {
status: 200, headers: { "Content-Type": "application/json" },
});
};
try {
const settings = await fetchLabViewProfile("result-1");
assert.equal(settings.pointSize, 4.7);
assert.equal(settings.palette, "viridis");
const saved = await saveLabViewProfile("result-1", settings);
assert.equal(saved.accumulationSeconds, 8);
assert.equal(requests[0].url, "/api/v1/observatory/lab-view-profiles/result-1");
assert.equal(requests[1].init.method, "PUT");
assert.deepEqual(JSON.parse(requests[1].init.body).scene_settings, serverProfile.scene_settings);
} finally {
globalThis.fetch = originalFetch;
}
});
test("LAB view profile fails closed on a foreign result identity", async () => {
const originalFetch = globalThis.fetch;
globalThis.fetch = async () => new Response(JSON.stringify({
...serverProfile, result_id: "foreign-result",
}), { status: 200, headers: { "Content-Type": "application/json" } });
try {
await assert.rejects(fetchLabViewProfile("result-1"), /другой LAB/);
} finally {
globalThis.fetch = originalFetch;
}
});
test("LAB scene settings normalize typed values before persistence", () => {
assert.deepEqual(
normalizeLabSceneSettings({
pointSize: 28,
accumulationSeconds: 305,
colorMode: "height",
palette: "viridis",
customColor: "#ffffff",
showGrid: false,
showPoints: true,
showTrajectory: true,
showLabels: true,
showCameraFrustums: false,
projection: "3d",
}),
{
pointSize: 28,
accumulationSeconds: 305,
colorMode: "height",
palette: "viridis",
customColor: "#ffffff",
showGrid: false,
showPoints: true,
showTrajectory: true,
showLabels: true,
showCameraFrustums: false,
projection: "3d",
},
);
});
@@ -886,7 +886,7 @@ test("M4.6 viewer keeps media and spatial panes on one playback clock", async ()
assert.match(canonical, /primary=\{mediaPane\}/);
assert.match(canonical, /secondary=\{spatialPane/);
assert.match(canonical, /primarySize=\{splitView \? splitPrimarySize : mediaMode !== "none" \? 100 : 0\}/);
assert.match(canonical, /resizable=\{splitView && !unifiedContent\}/);
assert.match(canonical, /resizable=\{splitView\}/);
assert.match(canonical, /separatorLabel="Изменить размер видео\/камеры и 3D\/плана"/);
assert.match(canonical, /secondaryMode=\{\{/);
assert.match(visual, /playback=\{playbackController\.playback\}/);
@@ -40,6 +40,7 @@ let formatAccumulationDuration;
let resolveRerunSourceUrl;
let resolveRecordedBlueprintUrl;
let fetchRecordedBlueprintRrd;
let recordedCameraJournalContract;
let resolveRecordedPerceptionUrl;
let fetchRecordedPerceptionRrd;
let resolveRecordedPerceptionViewerSourceUrl;
@@ -110,6 +111,7 @@ before(async () => {
resolveRerunSourceUrl,
resolveRecordedBlueprintUrl,
fetchRecordedBlueprintRrd,
recordedCameraJournalContract,
resolveRecordedPerceptionUrl,
fetchRecordedPerceptionRrd,
resolveRecordedPerceptionViewerSourceUrl,
@@ -118,7 +120,7 @@ before(async () => {
fetchRecordedPointColorsRrd,
recordedPointColorKey,
isRecordedPlaybackFullyBuffered,
} = await server.ssrLoadModule(
} = await server.ssrLoadModule(
"/src/components/RerunViewport.tsx",
));
({ recordedObservationSources } = await server.ssrLoadModule(
@@ -129,6 +131,25 @@ before(async () => {
));
});
test("follow toggles retain the current recorded camera journal", () => {
const before = recordedCameraJournalContract({
activeView: "spatial", viewResetGeneration: 0, planView: false, followTrajectory: false,
});
const afterFollowToggle = recordedCameraJournalContract({
activeView: "spatial", viewResetGeneration: 0, planView: false, followTrajectory: true,
});
const afterPlanToggle = recordedCameraJournalContract({
activeView: "spatial", viewResetGeneration: 0, planView: true, followTrajectory: true,
});
const afterReset = recordedCameraJournalContract({
activeView: "spatial", viewResetGeneration: 1, planView: false, followTrajectory: false,
});
assert.equal(afterFollowToggle, before);
assert.notEqual(afterPlanToggle, before);
assert.notEqual(afterReset, before);
});
test("observation camera windows tile from the bottom-right above the live timeline", () => {
const bounds = { width: 1280, height: 720 };
const left = initialObservationWindowRect(0, 2, bounds);
@@ -444,6 +465,7 @@ test("recorded replay becomes ready only after the complete declared timeline is
test("recorded blueprint fetch is bounded, strict and sends only display settings", async () => {
const calls = [];
const cameraLimits = [];
const payload = Uint8Array.from([0x52, 0x52, 0x46, 0x32, 0x01]);
const result = await fetchRecordedBlueprintRrd(
"http://127.0.0.1:5174/api/v1/observation-sessions/session-1/blueprint.rrd",
@@ -464,6 +486,9 @@ test("recorded blueprint fetch is bounded, strict and sends only display setting
activeView: "perception3d",
viewResetGeneration: 1,
followTrajectory: true,
currentTimeNs: 39_215_263_458,
onCameraMaxOrbitalRadius: value => cameraLimits.push(value),
unifiedCameraShare: 0.73,
perceptionLayers: {
enabled: true,
detections2d: true,
@@ -474,7 +499,10 @@ test("recorded blueprint fetch is bounded, strict and sends only display setting
calls.push({ input: String(input), init, body: JSON.parse(String(init.body)) });
return new Response(payload, {
status: 200,
headers: { "Content-Type": "application/vnd.rerun.rrd" },
headers: {
"Content-Type": "application/vnd.rerun.rrd",
"X-MissionCore-Camera-Max-Orbital-Radius": "686.024231",
},
});
},
},
@@ -497,13 +525,20 @@ test("recorded blueprint fetch is bounded, strict and sends only display setting
active_view: "perception3d",
view_reset_generation: 1,
follow_trajectory: true,
current_time_ns: 39_215_263_458,
unified_perception: true,
unified_camera_share: 0.73,
semantic_layer: null,
plan_view: false,
eye_position: null,
eye_look_target: null,
eye_up: null,
show_camera_image: true,
show_detections_2d: true,
show_segmentation: false,
show_cuboids_3d: true,
});
assert.deepEqual(cameraLimits, [686.024231]);
await fetchRecordedBlueprintRrd(
"http://127.0.0.1:5174/api/v1/observation-sessions/session-1/blueprint.rrd",
@@ -537,6 +572,7 @@ test("recorded blueprint fetch is bounded, strict and sends only display setting
},
);
assert.equal(calls[1].body.unified_perception, false);
assert.equal(calls[1].body.unified_camera_share, 0.46);
assert.equal(calls[1].body.show_cuboids_3d, true);
await assert.rejects(
@@ -63,14 +63,31 @@ function evidence(id, sourceSessionId, publishedAtUtc) {
labId: `LAB-${id}`,
sourceSessionId,
resultKind: "recorded-evidence",
resultId: `result-${id}`,
resultId: id,
sourceResultId: null,
configSha256: "a".repeat(64),
runCreatedAtUtc: publishedAtUtc,
publishedAtUtc,
replayCapability: null,
replayCapability: {
schemaVersion: "missioncore.observation-lab-replay-capability/v2",
kind: "portable-result-review", viewerProfile: "portable-result",
timeline: "result-defined", activation: "explicit", commandsEnabled: false,
},
calculationProfile: null,
provenance: { verdict: "must-not-be-inferred" },
provenance: {
schema_version: "missioncore.observatory-portable-result-publication/v1",
authority: { commands_enabled: false },
calculation_profile: null, calculation_profile_sha256: "1".repeat(64),
job: {}, method: {}, storage: {},
source: { session_id: sourceSessionId },
run_definition: { definition_sha256: "a".repeat(64) },
result_package: { manifest_sha256: "b".repeat(64), artifact_manifest_id: "c".repeat(64) },
replay_capability: {
schema_version: "missioncore.observation-lab-replay-capability/v2",
kind: "portable-result-review", viewer_profile: "portable-result",
timeline: "result-defined", activation: "explicit", commands_enabled: false,
},
},
},
};
}
@@ -154,7 +171,9 @@ test("Observatory fetches disjoint read-only source and laboratory projections",
const calls = [];
const fetcher = async (input, init) => {
calls.push({ input: String(input), method: init?.method });
return new Response(JSON.stringify({ items: [] }), {
return new Response(JSON.stringify({
schema_version: "missioncore.observation-session-page/v1", items: [], next_cursor: null,
}), {
status: 200,
headers: { "Content-Type": "application/json" },
});
@@ -175,8 +194,8 @@ test("Observatory fetches disjoint read-only source and laboratory projections",
assert.deepEqual(
calls.map(({ input }) => input).sort(),
[
"/api/v1/observation-sessions?limit=50&scope=laboratory&lab_contract=v3",
"/api/v1/observation-sessions?limit=50&scope=source",
"/api/v1/observation-sessions?limit=50&scope=laboratory&lab_contract=v3&pagination=cursor-v1",
"/api/v1/observation-sessions?limit=50&scope=source&pagination=cursor-v1",
],
);
assert.deepEqual(new Set(calls.map(({ method }) => method)), new Set(["GET"]));
@@ -203,7 +222,7 @@ test("Observatory exposes bounded-window uncertainty without inventing a broken
);
});
test("Observatory projects a typed canonical run only through its exact sourceSessionId", () => {
test("historical canonical replay stays out of Observatory without modifying its archive record", () => {
const canonicalResultId = `lab-v1-vegetation-shadow-${"8".repeat(64)}`;
const canonical = evidence(
canonicalResultId,
@@ -227,20 +246,144 @@ test("Observatory projects a typed canonical run only through its exact sourceSe
commandsEnabled: false,
},
};
const before = structuredClone(canonical);
const catalog = buildObservatoryCatalog(
[source("20260828T130511Z_viewer_live", "2026-08-28T13:05:11Z")],
[canonical],
);
assert.deepEqual(catalog.items[0].evidence[0].recordedRun, {
kind: "canonical-recorded-rerun",
evidenceSessionId: canonicalResultId,
sourceSessionId: "20260828T130511Z_viewer_live",
resultId: canonicalResultId,
viewerProfile: "recorded-session",
timeline: "session_time",
activation: "explicit",
assert.deepEqual(catalog.items[0].evidence, []);
assert.equal(catalog.window.laboratoryCount, 0);
assert.deepEqual(canonical, before);
});
test("only admitted portable evidence is visible regardless of LAB label, age or installed version", () => {
const legacy = evidence("legacy", "source", "2026-09-03T13:00:00Z");
legacy.lab.labId = "LAB M4.9T5";
legacy.lab.replayCapability = null;
const historical = evidence("old-portable", "source", "2026-07-20T10:00:00Z");
historical.lab.labId = "LAB E24"; // Labels must never become an allow/deny list.
const current = evidence("new-portable", "source", "2026-09-03T12:00:00Z");
const catalog = buildObservatoryCatalog(
[source("source", "2026-07-19T10:00:00Z")], [legacy, historical, current],
);
assert.deepEqual(catalog.items[0].evidence.map((item) => item.sessionId), ["new-portable", "old-portable"]);
assert.equal(catalog.window.laboratoryCount, 2);
assert.equal(findObservatoryEvidence(catalog, "legacy"), null);
});
test("a malformed portable binding errors instead of silently disappearing as legacy", () => {
const broken = evidence("broken", "source", "2026-09-03T12:00:00Z");
broken.lab.provenance.source.session_id = "another-source";
assert.throws(() => buildObservatoryCatalog(
[source("source", "2026-07-19T10:00:00Z")], [broken],
), /identity/);
});
function wire(item) {
const lab = item.lab;
return {
id: item.id, label: item.label, started_at_utc: item.startedAtUtc,
completed_at_utc: item.completedAtUtc, status: item.status,
modalities: item.modalities, duration_seconds: item.durationSeconds, replayable: item.replayable,
...(lab ? { lab: {
lab_id: "LAB M4.9T5", source_session_id: lab.sourceSessionId, result_kind: lab.resultKind,
result_id: lab.resultId, source_result_id: lab.sourceResultId, config_sha256: lab.configSha256,
run_created_at_utc: lab.runCreatedAtUtc, published_at_utc: lab.publishedAtUtc,
provenance: lab.provenance, replay_capability: lab.replayCapability ? lab.provenance.replay_capability : null,
calculation_profile: null,
} } : {}),
};
}
function pageResponse(items, nextCursor = null) {
return new Response(JSON.stringify({
schema_version: "missioncore.observation-session-page/v1",
items: items.map(wire), next_cursor: nextCursor,
}), { headers: { "Content-Type": "application/json" } });
}
test("500 sources and later-page results are searchable metadata, with no archive or viewer requests", async () => {
const sources = Array.from({ length: 500 }, (_, index) => source(`source-${index}`, "2026-08-29T10:00:00Z"));
const labs = Array.from({ length: 101 }, (_, index) => {
const item = evidence(`legacy-${index}`, sources[0].id, "2026-08-29T11:00:00Z");
item.lab.replayCapability = null;
return item;
});
labs.push(evidence("portable-after-legacy", sources[499].id, "2026-08-29T12:00:00Z"));
const calls = [];
const catalog = await fetchObservatoryCatalog({ fetcher: async (input, init) => {
const url = new URL(String(input), "http://localhost");
calls.push(url);
assert.equal(init.method, "GET");
assert.equal(url.pathname, "/api/v1/observation-sessions");
const rows = url.searchParams.get("scope") === "source" ? sources : labs;
const cursor = url.searchParams.get("cursor");
const start = cursor === null ? 0 : rows.findIndex((item) => item.id === cursor) + 1;
const page = rows.slice(start, start + 100);
return pageResponse(page, start + page.length < rows.length ? page.at(-1).id : null);
} });
assert.equal(calls.length, 7);
assert.equal(catalog.items.length, 500);
assert.equal(catalog.window.laboratoryCount, 1);
assert.equal(catalog.window.sourceLimitReached, false);
assert.equal(catalog.window.laboratoryLimitReached, false);
assert.equal(catalog.items.find((item) => item.source.id === "source-499").evidence[0].sessionId, "portable-after-legacy");
assert.deepEqual(catalog.unresolvedEvidence, []);
});
test("exactly one full page with no cursor is complete, not a guessed truncated window", async () => {
const catalog = await fetchObservatoryCatalog({ limit: 1, fetcher: async (url) =>
String(url).includes("scope=source")
? pageResponse([source("source", "2026-08-29T10:00:00Z")])
: pageResponse([evidence("result", "source", "2026-08-29T11:00:00Z")]),
});
assert.equal(catalog.window.sourceLimitReached, false);
assert.equal(catalog.window.laboratoryLimitReached, false);
});
test("paging rejects repeated records, cursor cycles, unsafe cursors and unversioned responses", async () => {
for (const failure of ["duplicate", "cycle", "unsafe", "unversioned", "too-large"]) {
let index = 0;
await assert.rejects(fetchObservatoryCatalog({ limit: 1, fetcher: async (url) => {
if (String(url).includes("scope=laboratory")) return pageResponse([]);
index += 1;
if (failure === "unversioned") return new Response(JSON.stringify({ items: [] }));
if (failure === "unsafe") return pageResponse([], "../../private");
const id = failure === "duplicate" ? "same" : `source-${index}`;
const rows = [source(id, "2026-08-29T10:00:00Z")];
if (failure === "too-large") rows.push(source("extra", "2026-08-29T10:00:00Z"));
return pageResponse(rows, failure === "cycle" ? "cycle" : id);
} }));
assert.ok(index <= 2);
}
});
test("the traversal cap is explicit and cannot confirm absence of a result", async () => {
let count = 0;
const catalog = await fetchObservatoryCatalog({ limit: 1, fetcher: async (url) => {
if (String(url).includes("scope=source")) return pageResponse([]);
count += 1;
return pageResponse([], `cursor-${count}`);
} });
assert.equal(count, 256);
assert.equal(catalog.window.laboratoryLimitReached, true);
assert.equal(observatoryCatalogConfirmsEvidenceDeletion(catalog, "missing"), false);
});
test("failure aborts the sibling traversal and an already aborted caller starts no requests", async () => {
const signals = [];
await assert.rejects(fetchObservatoryCatalog({ fetcher: async (url, init) => {
signals.push(init.signal);
if (String(url).includes("scope=source")) return new Response("unavailable", { status: 503 });
return pageResponse([]);
} }), /503/);
assert.ok(signals.every((signal) => signal.aborted));
const request = new AbortController();
request.abort();
await assert.rejects(fetchObservatoryCatalog({ signal: request.signal, fetcher: async () => {
assert.fail("aborted catalog cannot start a request");
} }), { name: "AbortError" });
});
test("Observatory applies an exact rename locally without mutating canonical identity", () => {
@@ -68,6 +68,10 @@ test("missing and stale telemetry show no fabricated advancement", () => {
assert.equal(label(job, null), "Ожидаем данные расчёта");
assert.equal(label(job, decode({ ...view(), age_seconds: 30 }, job)), "Ожидаем обновление прогресса");
});
test("terminal jobs keep their real state when progress telemetry is absent", () => {
assert.equal(label({ ...job, state: "failed" }, null), "Ошибка расчёта");
assert.equal(label({ ...job, state: "succeeded" }, null), "Расчёт завершён");
});
test("progress is a read-only request bound to the active job", async () => {
const calls = [];
const progress = await fetchProgress(job, { fetcher: async (path, init) => {
@@ -35,9 +35,9 @@ test("Observatory is the third independent Polygon workspace", () => {
id: "observatory",
root: "polygon",
label: "Обсерватория",
title: "Проверка компьютерного зрения",
title: "AI inference",
eyebrow: "ТЕСТОВЫЙ КОНТУР / ОБСЕРВАТОРИЯ",
description: "Сессии и квалификация компьютерного зрения без доступа к управлению.",
description: "Сессии и квалификация без доступа к управлению.",
icon: "eye",
kind: "observatory",
groups: [],
@@ -73,9 +73,15 @@ test("Observatory mounts the one shared canonical replay only after explicit adm
assert.doesNotMatch(app, /\["recordings", "lab-archive", "observatory"\]/);
assert.match(workspaceHub, /case "observatory":[\s\S]*<ObservatoryWorkspace/);
assert.match(workspace, /useObservatoryCatalog/);
assert.doesNotMatch(workspace, /observatory-lead/);
assert.doesNotMatch(workspace, /Расчёт записанных маршрутов выбранными профилями/);
assert.match(workspace, /Связанных результатов нет/);
assert.match(workspace, /не является выводом о качестве/);
assert.match(workspace, /presentedEvidence = selectedSession\?\.evidence \?\? \[\]/);
assert.match(workspace, /function compositionProducedEvidence\(/);
assert.match(workspace, /publishedAt >= compositionCreatedAt/);
assert.match(workspace, /const presentedCompositionRuns = aiJobsController\.runs/);
assert.match(workspace, /presentedEvidence = \(selectedSession\?\.evidence \?\? \[\]\)\.filter/);
assert.match(workspace, /!presentedCompositionRuns\.some\([\s\S]*compositionProducedEvidence\(run, evidence\)/);
assert.doesNotMatch(workspace, /MAX_PRESENTED_EVIDENCE|\.evidence\.slice\(/);
assert.match(workspace, /Полнота исторических/);
assert.match(workspace, /вне текущего загруженного среза/);
@@ -86,12 +92,13 @@ test("Observatory mounts the one shared canonical replay only after explicit adm
assert.match(workspace, /observatory-evidence-card__copy/);
assert.match(
workspace,
/function evidenceResultSubtitle[\s\S]*calculationProfile\?\.displayName[\s\S]*`\$\{evidence\.label\} · \$\{profileName\}`[\s\S]*: evidence\.label/,
/function evidenceConfigurationLabel[\s\S]*calculationProfile\?\.displayName[\s\S]*return profileName \?\? evidence\.label/,
);
assert.match(
workspace,
/<span>\{evidenceResultSubtitle\(evidence\)\}<\/span>/,
/<strong>\{evidenceConfigurationLabel\([\s\S]*evidence,[\s\S]*aiJobsController\.moduleLabelsBySetup,[\s\S]*aiJobsController\.jobs,[\s\S]*\)\}<\/strong>/,
);
assert.doesNotMatch(workspace, /evidenceConfigurationLabel\([^)]*,\s*selectedSession\.source\.label/);
assert.match(
workspace,
/evidence\.recordedRun \? \([\s\S]*name="trash"[\s\S]*name="edit"[\s\S]*Открыть визуальный разбор:[\s\S]*name="eye"/,
@@ -101,9 +108,15 @@ test("Observatory mounts the one shared canonical replay only after explicit adm
assert.match(workspace, /replay\.kind === "ready"[\s\S]*<CanonicalVegetationRerunReplay/);
assert.match(workspace, /<PortableResultReplay review=\{replay\.review\}/);
const portable = await read("components/laboratory/PortableResultReplay.tsx");
assert.match(portable, /<summary>Документ результата<\/summary>/);
assert.doesNotMatch(portable, /Документ результата|JSON.stringify|StatusBadge/);
assert.match(portable, /missioncore.recorded-eomt-ddrnet-review\/v2/);
assert.match(portable, /sourceKind: "portable-semantic"/);
assert.match(portable, /sourceKind: "portable-objects"/);
assert.match(portable, /costmap=\{tgs\} semantics=\{semantics\}/);
assert.match(portable, /detections=\{objects\}/);
assert.match(portable, /<CanonicalResultRerunReplay/);
assert.doesNotMatch(workspace, /UNIVERSAL VIEWER|content-addressed artifacts/);
assert.doesNotMatch(workspace, /evidence\.lab\.resultKind|без запуска тяжёлого/);
assert.match(workspace, /Проверяем точную связь результата/);
assert.match(workspace, /role="alert"/);
assert.match(workspace, /Повторить/);
@@ -112,7 +125,10 @@ test("Observatory mounts the one shared canonical replay only after explicit adm
workspace,
/const returnToOverview[\s\S]*closeReplay\(\);[\s\S]*scrollIntoView\(\{ block: "start" \}\)/,
);
assert.match(workspace, /<h3>\{replayEvidence\?\.label \?\? replay\.binding\.resultId\}<\/h3>/);
assert.match(
workspace,
/<h3>\{replayEvidence && selectedSession[\s\S]*evidenceConfigurationLabel\([\s\S]*replayEvidence,[\s\S]*aiJobsController\.moduleLabelsBySetup,[\s\S]*aiJobsController\.jobs,[\s\S]*\)[\s\S]*replay\.binding\.resultId\}<\/h3>/,
);
assert.doesNotMatch(workspace, /RAVNOVES004TREE · полный маршрут восприятия/);
assert.match(workspace, /const selectSession[\s\S]*closeReplay\(\);[\s\S]*setSelectedSessionId/);
assert.match(workspace, /data-observatory-authority="observation-only"/);
@@ -186,6 +202,10 @@ test("Observatory rename and delete use admitted projection mutations and canoni
assert.match(workspace, /<WindowFooterActions>[\s\S]*Сохранить/);
assert.match(workspace, /<ConfirmationModal[\s\S]*title="Удалить результат из Обсерватории\?"/);
assert.match(workspace, /Исходная сессия, запечатанный лабораторный результат и файлы доказательств/);
assert.match(workspace, /renameAICompositionRunProjection/);
assert.match(workspace, /deleteAICompositionRunProjection/);
assert.match(workspace, /openCompositionRename\(run\)/);
assert.match(workspace, /openCompositionDelete\(run\)/);
assert.match(
workspace,
/const result = await renameObservatoryLabProjection\([\s\S]*controller\.applyEvidenceRename\(result\.sessionId, result\.displayName\);[\s\S]*setRenameTarget\(null\);[\s\S]*void controller\.refresh\(\);/,
@@ -196,9 +216,12 @@ test("Observatory rename and delete use admitted projection mutations and canoni
);
const deleteFlowStart = workspace.indexOf("const confirmDelete = useCallback");
const deleteFlowEnd = workspace.indexOf("}, [closeReplay, controller", deleteFlowStart);
const deleteFlowEnd = workspace.indexOf("}, [aiJobsController, closeReplay", deleteFlowStart);
assert.ok(deleteFlowStart >= 0 && deleteFlowEnd > deleteFlowStart);
const deleteFlow = workspace.slice(deleteFlowStart, deleteFlowEnd);
const wholeDeleteFlow = workspace.slice(deleteFlowStart, deleteFlowEnd);
const evidenceFlowStart = wholeDeleteFlow.indexOf("if (!deleteTarget?.recordedRun");
assert.ok(evidenceFlowStart >= 0);
const deleteFlow = wholeDeleteFlow.slice(evidenceFlowStart);
const teardown = deleteFlow.indexOf("closeReplay();");
const remove = deleteFlow.indexOf("await deleteObservatoryLabProjection");
const tombstone = deleteFlow.indexOf("controller.applyEvidenceDeletion");
@@ -219,77 +242,128 @@ test("Observatory rename and delete use admitted projection mutations and canoni
assert.match(hook, /applyObservatoryCatalogMutationOverlay\(/);
});
test("Observatory keeps one compact selector axis without the obsolete setup detail", async () => {
const [workspace, styles, setupHook, jobsHook] = await Promise.all([
test("Observatory configures independent AI modules and shows queued evidence progress", async () => {
const [workspace, styles, configWindow, jobsHook, sharedReplay] = await Promise.all([
read("workspaces/observatory/ObservatoryWorkspace.tsx"),
read("styles/observatory.css"),
read("core/observatory/useObservatoryLaboratorySetups.ts"),
read("core/observatory/useObservatoryRecordedJobs.ts"),
read("components/observatory/AIConfigurationWindow.tsx"),
read("core/observatory/useAICompositionJobs.ts"),
read("components/laboratory/CanonicalResultRerunReplay.tsx"),
]);
assert.match(workspace, /className="observatory-catalog-bar" padding="sm"/);
assert.match(workspace, /label="Выбрать сохранённую сессию"/);
assert.match(workspace, /label="Выбрать сетап лаборатории"/);
assert.match(workspace, /setupController\.error \? \(/);
assert.match(workspace, />\s*Сконфигурировать AI-слой\s*<\/Button>/);
assert.doesNotMatch(workspace, /label="Выбрать сетап лаборатории"/);
assert.doesNotMatch(workspace, />\s*Обновить\s*<\/Button>/);
assert.doesNotMatch(workspace, /Выбор меняет только читаемую карточку/);
assert.doesNotMatch(workspace, /ObservatorySetupDetail|observatory-setup-detail/);
assert.doesNotMatch(styles, /observatory-setup-detail|observatory-setup-results/);
assert.match(workspace, /useObservatoryRecordedJobs/);
assert.doesNotMatch(workspace, /useObservatoryRecordedJobs|useObservatoryLaboratorySetups/);
assert.match(configWindow, /title="Сконфигурировать AI-слой"/);
assert.match(configWindow, /label: "Сегментация"/);
assert.match(configWindow, /Зависимые блоки подключаются явно/);
assert.match(configWindow, /label: "Облако точек \/ TGS"/);
assert.match(configWindow, /label: "Дистанция"/);
assert.match(configWindow, /Дистанция использует рамки детектора и синхронное облако точек/);
assert.match(configWindow, /TGS обрабатывает LiDAR независимо от сегментации и детектора/);
assert.match(configWindow, /providers\.length === 1/);
assert.match(
workspace,
/runPreflight\?\.outcome === "queueable"[\s\S]*runPreflight\.submissionAllowed/,
configWindow,
/<WindowFooterActions>[\s\S]*"Отправляем на Worker…" : "Рассчитать"/,
);
assert.match(configWindow, /className=\{state === "saving" \? "observatory-ai-config__calculate--saving"/);
assert.match(configWindow, /aria-busy=\{state === "saving"\}/);
assert.match(styles, /@keyframes observatory-ai-calculate-pulse/);
assert.match(
workspace,
/showCalculate \? \([\s\S]*>\s*Рассчитать\s*<\/Button>/,
styles,
/\.observatory-ai-config__calculate--saving:disabled \{[\s\S]*animation: observatory-ai-calculate-pulse 1\.8s ease-in-out infinite;[\s\S]*opacity: 1;/,
);
assert.doesNotMatch(workspace, /recordedJobStatus|presentedJobStatus|Расчёт завершён|Результат опубликован|Вычислено ·/);
assert.match(workspace, /setupController\.selectableSetups\.map/);
assert.match(workspace, /showCalculate = setupController\.selectedSetup !== null/);
assert.match(workspace, /disabled=\{!canSubmitRecordedJob\}/);
assert.match(workspace, /aria-busy=\{calculationPending\}/);
const runBar = workspace.slice(workspace.indexOf('<div className="observatory-catalog-bar__run"'), workspace.indexOf("{queueStatusError ?"));
assert.doesNotMatch(runBar, /StatusBadge/);
assert.match(setupHook, /fetchObservatoryPortableLaboratorySetups/);
assert.doesNotMatch(setupHook, /fetchObservatoryLaboratorySetups|mergeSetupCatalogs|legacyResult/);
assert.match(setupHook, /selectableObservatorySetups\(next\)/);
assert.match(setupHook, /selectable\[0\]\?\.setupId \?\? ""/);
assert.match(setupHook, /selectedSetupId, sourceSessionId, selectedDefinitionSha256/);
assert.match(workspace, /useAICompositionJobs\(selectedSessionId\)/);
assert.match(workspace, /observatory-evidence-card--progress/);
assert.match(workspace, /const active = run\.state === "running"/);
assert.match(workspace, /\{active \? \([\s\S]*observatory-evidence-card__progress/);
assert.match(workspace, /run\.state === "ready" \? <Icon name="clipboard"/);
assert.match(workspace, /function aiJobStage\(/);
assert.match(workspace, /return "Инициализация"/);
assert.match(workspace, /return "Передача на сервер"/);
assert.match(workspace, /return "Просчёт"/);
assert.match(workspace, /Собираем видеоряд для модели/);
assert.match(workspace, /измеренная скорость Worker 006: 17–18 FPS/);
assert.doesNotMatch(workspace, /phaseElapsedSeconds \/ progress\.completed/);
assert.match(workspace, /AI_PROGRESS_PHASE_RANGES/);
assert.match(workspace, /"source-preparation": \[5, 25\]/);
assert.match(workspace, /computing: \[25, 90\]/);
assert.match(workspace, /Подготавливаем камеру и LiDAR/);
assert.match(workspace, /прошло.*formatDuration\(progress\.elapsedSeconds\)/s);
assert.match(workspace, /Не удалось запустить модель/);
assert.doesNotMatch(workspace, /job\.terminalMessage|job\.publication\.error/);
assert.doesNotMatch(workspace, /function aiModuleLabel\(/);
assert.match(workspace, /aiJobsController\.moduleLabelsBySetup\[job\.setupId\] \?\? job\.setupId/);
assert.match(jobsHook, /fetchAIModuleCatalog\(request\.signal\)/);
assert.match(jobsHook, /AI_MODULE_SETUP_IDS\[module\.moduleId\]/);
assert.match(jobsHook, /run\.presentation\.modules\.flatMap/);
assert.match(jobsHook, /Object\.fromEntries\(\[\.\.\.catalogLabels, \.\.\.projectedLabels\]\)/);
assert.match(workspace, /<strong>\{moduleLabel\}<\/strong>/);
assert.match(workspace, /<strong>\{run\.displayName \?\? run\.presentation\.configurationLabel\}<\/strong>/);
assert.doesNotMatch(workspace, /<strong>\{selectedSession\.source\.label\} ·/);
assert.match(workspace, /type EvidenceSortDirection = "newest" \| "oldest"/);
assert.match(workspace, /const presentedRows = useMemo<readonly ObservatoryEvidenceRow\[]>/);
assert.match(workspace, /direction \* \(Date\.parse\(left\.timestamp\) - Date\.parse\(right\.timestamp\)\)/);
assert.match(workspace, /className="observatory-evidence__sort"/);
assert.match(workspace, /data-direction=\{evidenceSortDirection\}/);
assert.match(workspace, /formatTimestamp\(row\.timestamp\)/);
assert.match(styles, /\.observatory-evidence-card\s*\{[\s\S]*grid-template-columns:\s*auto minmax\(0, 1fr\) auto auto/);
assert.match(styles, /\.observatory-evidence__sort\s*\{[\s\S]*border:\s*0;[\s\S]*outline:\s*0/);
assert.match(
workspace,
/preflightCandidate\.definitionSha256[\s\S]*selectedSetup\?\.runDefinition\?\.definitionSha256/,
styles,
/\.observatory-evidence-card__copy strong \{[\s\S]*font-size: var\(--nodedc-font-size-sm\);/,
);
assert.match(sharedReplay, /LAB_SCENE_SETTINGS_STORAGE_PREFIX/);
assert.match(sharedReplay, /window\.localStorage\.getItem/);
assert.match(sharedReplay, /window\.localStorage\.setItem/);
assert.match(sharedReplay, /unifiedCameraShare: blueprintSplitPrimarySize \/ 100/);
assert.match(sharedReplay, /setSettingsOpen\(false\);[\s\S]*void saveLabViewProfile/);
assert.match(
jobsHook,
/OPEN_STATES[\s\S]*"preemption-pending",[\s\S]*"reconciliation-required"/,
sharedReplay,
/showPoints: spatialMode !== null && \(showSourcePoints \|\| showLocalSlam\)/,
);
assert.match(jobsHook, /return `observatory-ui:\$\{entropy\}`;/);
assert.match(jobsHook, /JSON\.stringify\(\[sourceSessionId, setupId, definitionSha256\]\)/);
assert.match(jobsHook, /snapshot\.selectionKey === selectionKey \? snapshot : null/);
assert.doesNotMatch(jobsHook, /observatory-ui:[^`]*sourceSessionId|\.slice\(0, 160\)/);
assert.match(workspace, /recordedJobsController\.refresh\(\)/);
assert.match(jobsHook, /POLL_INTERVAL_MS = 1_500/);
assert.match(sharedReplay, /showTrajectory: spatialMode !== null && showLocalSlam/);
assert.match(
sharedReplay,
/accumulationSeconds: showLocalSlam \? sceneDraft\.accumulationSeconds : 0/,
);
assert.match(workspace, /<AICompositionReplay run=\{replay\.run\} \/>/);
assert.match(workspace, /function presentAIJob\(job: ObservatoryRecordedJob\)/);
assert.match(workspace, /if \(job\.state === "failed"\) return true/);
assert.match(workspace, /return job\.state !== "succeeded"/);
assert.match(workspace, /function presentLatestAIJobs/);
assert.match(workspace, /seenSetups\.has\(job\.setupId\)/);
assert.match(configWindow, /fetchAICompositionJobs\(sourceSessionId, request\.signal\)/);
assert.match(
configWindow,
/if \(job\.state === "failed" \|\| job\.publication\.state === "failed"\) continue;/,
);
assert.match(configWindow, /Текущая конфигурация уже рассчитана/);
assert.match(configWindow, /configurationAlreadyExists/);
assert.match(configWindow, /Готовый результат будет использован без повторного расчёта/);
assert.doesNotMatch(configWindow, /disabled: existingByModule\.has\(module\.moduleId\)/);
assert.doesNotMatch(configWindow, /selections\.some\(\(\{ module \}\) => existingByModule/);
assert.match(jobsHook, /const OPEN = new Set/);
assert.match(jobsHook, /1_500/);
assert.match(jobsHook, /globalThis\.setTimeout/);
assert.doesNotMatch(jobsHook, /setInterval/);
assert.match(workspace, /job\.publication\.state === "published"/);
assert.match(workspace, /void controller\.refresh\(\);[\s\S]*setupController\.refresh\(\);/);
assert.match(workspace, /onCalculated=\{\(\) => aiJobsController\.refresh\(\)\}/);
assert.match(
styles,
/\.observatory-catalog-bar__controls \{[\s\S]*min-width: 0;[\s\S]*flex: 1 1 auto;[\s\S]*flex-wrap: nowrap;[\s\S]*justify-content: flex-end;/,
);
assert.match(
styles,
/\.observatory-catalog-bar__run \{[\s\S]*display: flex;[\s\S]*align-items: center;/,
);
assert.match(styles, /\.observatory-evidence-card__progress \{/);
assert.match(styles, /\.observatory-ai-module-group > header \{/);
assert.match(styles, /\.observatory-ai-module-group \{[\s\S]*border: 0;[\s\S]*outline: 0;/);
assert.match(
styles,
/@container observatory-workspace \(max-width: 920px\)[\s\S]*\.observatory-catalog-bar,[\s\S]*\.observatory-catalog-bar__controls,[\s\S]*flex-direction: column;/,
);
assert.match(setupHook, /catalog\?\.sourceSessionId === sourceSessionId/);
assert.match(setupHook, /preflightRequest\.current\?\.abort\(\)/);
assert.match(setupHook, /preflightRequest\.current !== request/);
assert.match(
setupHook,
/state !== "ready" \|\| !selectedSetup \|\| preflight\.kind !== "idle"[\s\S]*void check\(\)/,
);
});
@@ -0,0 +1,101 @@
import assert from "node:assert/strict";
import { after, before, test } from "node:test";
import { createServer } from "vite";
let server, createRecordedRerunCameraJournal, initialEye;
before(async () => {
server = await createServer({ appType: "custom", logLevel: "silent",
server: { middlewareMode: true } });
const module = await server.ssrLoadModule(
"/src/components/rerun/recordedRerunCameraJournal.ts");
createRecordedRerunCameraJournal = module.createRecordedRerunCameraJournal;
initialEye = module.RECORDED_RERUN_ORBITAL_EYE;
});
after(async () => { await server?.close(); });
class FakeEvent {
constructor(type, init = {}) { this.type = type; Object.assign(this, init); }
}
class FakeTarget {
listeners = new Map();
emitted = [];
addEventListener(type, listener) {
const listeners = this.listeners.get(type) ?? [];
listeners.push(listener); this.listeners.set(type, listeners);
}
removeEventListener(type, listener) {
this.listeners.set(type, (this.listeners.get(type) ?? []).filter(item => item !== listener));
}
dispatchEvent(event) {
this.emitted.push(event);
for (const listener of this.listeners.get(event.type) ?? []) listener(event);
return true;
}
}
const radius = (eye) => Math.hypot(
eye.position[0] - eye.lookTarget[0],
eye.position[1] - eye.lookTarget[1],
eye.position[2] - eye.lookTarget[2],
);
test("recorded orbital eye tracks only 3D viewport navigation", () => {
const scope = new FakeTarget();
const timers = [];
Object.assign(scope, {
WheelEvent: FakeEvent,
requestAnimationFrame(callback) { callback(); return 1; },
setTimeout(callback) { timers.push(callback); return timers.length; },
});
const canvas = new FakeTarget();
Object.assign(canvas, {
clientHeight: 200,
isConnected: true,
getBoundingClientRect: () => ({
left: 100, right: 500, top: 50, bottom: 250, width: 400, height: 200,
}),
});
const journal = createRecordedRerunCameraJournal(canvas, scope);
journal.configure(initialEye, 0.46);
const pointer = (type, x, y, buttons) => new FakeEvent(type, {
clientX: x, clientY: y, button: 0, buttons, pointerId: 7, pointerType: "mouse",
altKey: false, ctrlKey: false, metaKey: false, shiftKey: false,
});
canvas.dispatchEvent(pointer("pointerdown", 200, 100, 1));
scope.dispatchEvent(pointer("pointermove", 240, 120, 1));
scope.dispatchEvent(pointer("pointerup", 240, 120, 0));
assert.deepEqual(journal.current(), initialEye);
canvas.dispatchEvent(pointer("pointerdown", 380, 100, 1));
scope.dispatchEvent(pointer("pointermove", 420, 120, 1));
scope.dispatchEvent(pointer("pointerup", 420, 120, 0));
const rotated = journal.current();
assert.notDeepEqual(rotated.position, initialEye.position);
assert.ok(Math.abs(radius(rotated) - radius(initialEye)) < 1e-9);
journal.setMaxOrbitalRadius(40);
scope.dispatchEvent(new FakeEvent("wheel", {
clientX: 380, clientY: 130, deltaX: 0, deltaY: 2_000, deltaMode: 0,
altKey: false, ctrlKey: false, metaKey: false, shiftKey: false,
}));
assert.ok(Math.abs(radius(journal.current()) - 40) < 1e-9);
journal.configure(rotated, 0.46);
scope.dispatchEvent(new FakeEvent("wheel", {
clientX: 0, clientY: 0, deltaX: 0, deltaY: -20, deltaMode: 0,
altKey: false, ctrlKey: false, metaKey: false, shiftKey: false,
}));
const zoomed = journal.current();
assert.ok(Math.abs(radius(zoomed) - radius(rotated) * Math.exp(-20 / 200)) < 1e-9);
const snapshot = journal.current();
journal.setSpatialViewportStart(0.8);
scope.dispatchEvent(new FakeEvent("wheel", {
clientX: 380, clientY: 130, deltaX: 0, deltaY: -20, deltaMode: 0,
}));
assert.deepEqual(journal.current(), snapshot);
journal.dispose();
});
@@ -0,0 +1,261 @@
import assert from "node:assert/strict";
import { after, before, test } from "node:test";
import { createServer } from "vite";
let server, createIsolatedRerunHost, keepRecordedBlueprintSession, createRecordedRerunFacade, createRecordedRerunOwner, relayIsolatedRerunInput;
before(async () => {
server = await createServer({ appType: "custom", logLevel: "silent",
server: { middlewareMode: true } });
({ createIsolatedRerunHost } = await server.ssrLoadModule(
"/src/components/rerun/isolatedRerunHost.ts"));
({ keepRecordedBlueprintSession } = await server.ssrLoadModule(
"/src/core/observation/recordedBlueprintLifecycle.ts"));
({ createRecordedRerunFacade } = await server.ssrLoadModule(
"/src/components/rerun/recordedRerunFacade.ts"));
({ createRecordedRerunOwner } = await server.ssrLoadModule(
"/src/components/rerun/recordedRerunOwner.ts"));
({ relayIsolatedRerunInput } = await server.ssrLoadModule(
"/src/components/rerun/isolatedRerunInput.ts"));
});
after(async () => { await server?.close(); });
function fixture(t, { stopFails = false } = {}) {
const timers = new Map();
let timerId = 0, creates = 0, stops = 0, removals = 0;
t.mock.method(globalThis, "setTimeout", (fn) => {
timers.set(++timerId, fn); return timerId;
});
t.mock.method(globalThis, "clearTimeout", (id) => timers.delete(id));
const listeners = new Map();
const viewer = { stop() { stops++; if (stopFails) throw new Error("partial start"); } };
const mount = {};
const frame = {
contentWindow: {
missionCoreRerun: createRecordedRerunOwner(() => { creates++; return viewer; }, mount),
addEventListener: (name, fn) => listeners.set(name, fn),
removeEventListener: (name) => listeners.delete(name),
},
remove() { removals++; },
};
const host = { dataset: {}, ownerDocument: { createElement(name) {
assert.equal(name, "iframe"); return frame;
} }, append(child) { assert.equal(child, frame); } };
return { host, frame, viewer, mount, timers, listeners,
counts: () => ({ creates, stops, removals }) };
}
function bridge(native, mount = {}) {
const child = createRecordedRerunOwner(() => native, mount);
const parent = createRecordedRerunFacade((request, bytes) => {
assert.equal(typeof request, "string");
const result = child.invoke(request, bytes);
assert.equal(typeof result, "string");
return result;
});
child.connect(message => {
assert.equal(typeof message, "string");
parent.notify(message);
});
return parent;
}
test("recorded realm terminates on close even if upstream stop throws", async (t) => {
const f = fixture(t, { stopFails: true });
const scope = createIsolatedRerunHost(f.host);
assert.equal(f.frame.src, "/rerun-runtime.html");
f.frame.onload();
const ready = await scope.ready;
assert.notEqual(ready.viewer, f.viewer);
assert.equal(ready.mount, f.host);
assert.equal(f.timers.size, 0);
assert.equal(f.listeners.size, 1);
scope.dispose(); scope.dispose();
assert.deepEqual(f.counts(), { creates: 1, stops: 1, removals: 1 });
assert.equal(f.listeners.size, 0);
assert.equal(f.frame.onload, null);
assert.equal(ready.viewer.ready, false);
});
test("native frame input relays Escape and leaves pointer ownership inside Rerun", () => {
const listeners = new Map(), events = [];
class ParentEvent { constructor(type, init) { this.type = type; Object.assign(this, init); } }
const host = { dataset: {}, ownerDocument: { defaultView: {
KeyboardEvent: ParentEvent, PointerEvent: ParentEvent,
} }, dispatchEvent: event => events.push(event) };
const child = {
addEventListener: (name, fn) => listeners.set(name, fn),
removeEventListener: name => listeners.delete(name),
};
const release = relayIsolatedRerunInput(host, child, null);
assert.equal(listeners.has("pointerdown"), false);
listeners.get("keydown")({ key: "Escape", defaultPrevented: false });
assert.equal(events[0].key, "Escape");
release(); release();
assert.equal(listeners.size, 0); assert.equal(events.length, 1);
});
test("disposed facade, channel and unsubscribe reject native use and copy event values", async () => {
let eventCallback, unsubscribed = 0, sends = 0, starts = 0;
const range = { min: 1, max: 2 }, mount = {};
const native = {
ready: true, stop() {},
async start(source, parent) { assert.equal(parent, mount); starts++; },
get_time_range: () => range,
on(event, callback) { eventCallback = callback; return () => { unsubscribed++; }; },
open_channel: () => ({ ready: true, send_rrd() { sends++; }, close() {} }),
};
const { facade, dispose } = bridge(native, mount);
await facade.start(null, {}, null);
assert.equal(starts, 1);
let event;
const unsubscribe = facade.on("time_update", value => { event = value; });
eventCallback(range);
assert.deepEqual(event, range); assert.notEqual(event, range);
assert.notEqual(facade.get_time_range(), range);
const channel = facade.open_channel();
channel.send_rrd(new Uint8Array());
dispose(); dispose(); unsubscribe();
assert.equal(unsubscribed, 1);
assert.equal(channel.ready, false);
channel.send_rrd(new Uint8Array());
eventCallback({ min: 3, max: 4 });
assert.deepEqual(event, range);
assert.equal(sends, 1);
await assert.rejects(facade.start(null, {}, null), /disposed/);
});
test("close before frame load cancels startup and cannot reopen", async (t) => {
const f = fixture(t);
const scope = createIsolatedRerunHost(f.host);
const lateLoad = f.frame.onload;
const rejected = assert.rejects(scope.ready, /disposed/);
scope.dispose(); lateLoad();
await rejected;
assert.deepEqual(f.counts(), { creates: 0, stops: 0, removals: 1 });
assert.equal(f.timers.size, 0);
});
test("closing during SDK startup also releases a late successful start", async () => {
let finish, stops = 0;
const owner = bridge({
start: () => new Promise(resolve => { finish = resolve; }),
stop() { stops++; },
}, {});
const pending = owner.facade.start(null, {}, null);
const rejected = assert.rejects(pending, /disposed/);
owner.dispose();
assert.equal(stops, 1);
finish();
await rejected;
await Promise.resolve();
assert.equal(stops, 2);
assert.equal(owner.facade.ready, false);
});
test("late failed startup cannot revive a disposed parent and still finishes cleanup", async () => {
let fail, stops = 0;
const owner = bridge({
start: () => new Promise((_resolve, reject) => { fail = reject; }),
stop() { stops++; if (stops > 1) throw new Error("already gone"); },
}, {});
const pending = owner.facade.start(null, {}, null);
const rejected = assert.rejects(pending, /disposed/);
owner.dispose();
fail(new Error("startup failed"));
await rejected;
await Promise.resolve();
assert.equal(stops, 2);
assert.equal(owner.facade.ready, false);
});
test("primitive bridge preserves all public clock/control arguments and errors", async () => {
const calls = [];
const methods = {
open: [["one.rrd", "two.rrd"]], close: ["one.rrd"],
override_panel_state: ["time", "hidden"],
get_active_recording_id: [], get_active_timeline: ["recording"],
get_current_time: ["recording", "session_time"], get_playing: ["recording"],
get_time_range: ["recording", "session_time"],
set_active_timeline: ["recording", "session_time"],
set_current_time: ["recording", "session_time", 123.456],
set_playing: ["recording", false],
};
const native = { stop() {}, start: async () => { throw new Error("native startup failure"); } };
for (const name of Object.keys(methods)) native[name] = (...args) => { calls.push([name, args]); return 1; };
const { facade, dispose } = bridge(native);
for (const [name, args] of Object.entries(methods)) facade[name](...args);
assert.deepEqual(calls, Object.entries(methods));
await assert.rejects(facade.start(null, {}, null), /native startup failure/);
dispose();
});
test("sync native failures become parent-owned errors and optional channel names stay undefined", () => {
const nativeError = new Error("native failure");
let channelName = "unobserved";
const { facade, dispose } = bridge({
stop() {},
open() { throw nativeError; },
open_channel(name) { channelName = name; return { close() {} }; },
});
assert.throws(() => facade.open("rrd"), error => error !== nativeError && /native failure/.test(error.message));
facade.open_channel();
assert.equal(channelName, undefined);
dispose();
});
test("dispose closes every auxiliary channel even if one channel fails", () => {
const closed = [];
let stops = 0;
const owner = bridge({
stop() { stops++; },
open_channel: name => ({ close() {
closed.push(name);
if (name === "first") throw new Error("already gone");
} }),
}, {});
const first = owner.facade.open_channel("first");
const second = owner.facade.open_channel("second");
owner.dispose(); owner.dispose(); first.close(); second.close();
assert.deepEqual(closed, ["first", "second"]);
assert.equal(stops, 1);
});
test("host load timeout removes the frame and clears its timer", async (t) => {
const f = fixture(t);
const scope = createIsolatedRerunHost(f.host);
const rejected = assert.rejects(scope.ready, /timed out/);
[...f.timers.values()][0]();
await rejected;
assert.equal(f.counts().removals, 1);
assert.equal(f.timers.size, 0);
});
test("blueprint termination cancels renewal and sends one bounded keepalive release", async () => {
let tick, canceled = 0;
const requests = [];
const release = keepRecordedBlueprintSession({
endpointUrl: "/api/v1/observation-sessions/source/blueprint.rrd",
origin: "http://core.test", applicationId: "nodedc_mission_core_recorded",
recordingId: "recording", ownerId: "a".repeat(32),
fetcher: async (url, init) => { requests.push({ url, ...init }); return {}; },
schedule(fn) { tick = fn; return 1; }, cancel() { canceled++; },
});
tick();
const renewal = requests[0];
release(); release(); tick();
assert.equal(canceled, 1);
assert.equal(requests.length, 2);
assert.equal(renewal.signal.aborted, true);
assert.equal(requests[1].keepalive, true);
assert.equal(requests[1].url, "http://core.test/api/v1/observation-sessions/source/blueprint-lifecycle");
assert.deepEqual(JSON.parse(requests[1].body), {
action: "release", application_id: "nodedc_mission_core_recorded",
recording_id: "recording", blueprint_session_id: "a".repeat(32),
});
});
test("blueprint lease refuses a foreign endpoint", () => {
assert.throws(() => keepRecordedBlueprintSession({ endpointUrl: "https://other.test/blueprint.rrd",
origin: "http://core.test", applicationId: "app", recordingId: "recording", ownerId: "owner",
}), /Unsafe/);
});
@@ -90,8 +90,15 @@ test("one canonical LAB replay generation reaches the same native receiver", ()
);
});
test("portable replay uses recorded admission and the shared source blueprint", () => {
const source = `/api/v1/observatory/portable-results/m49-tgs-portable-review-${"c".repeat(64)}/replays/${"d".repeat(64)}/recording.rrd`;
for (const prefix of [
"m49-tgs-portable-review",
"lab-v1-eomt-ddrnet",
"ai-layer-ddrnet",
"ai-layer-eomt",
"ai-layer-rf-detr",
"ai-layer-object-distance",
]) test(`${prefix} replay uses recorded admission and the shared source blueprint`, () => {
const source = `/api/v1/observatory/portable-results/${prefix}-${"c".repeat(64)}/replays/${"d".repeat(64)}/recording.rrd`;
const blueprint = "/api/v1/observation-sessions/source/blueprint.rrd";
assert.equal(isRecordedRrdSource(source), true);
assert.equal(isRecordedRrdSource(source.replace("/replays/", "/untrusted/")), false);
@@ -97,7 +97,7 @@ test("M4 keeps independent semantic controls in media and spatial panes", async
assert.match(source, /aria-label="Слои 3D и плана"/);
assert.match(canonical, /data-pane-mode="media"/);
assert.match(canonical, /data-pane-mode="spatial"/);
assert.match(canonical, /modeControlsVisible=\{!splitView\}/);
assert.match(canonical, /modeControlsVisible=\{!splitView && !paneToolbarsAlwaysVisible\}/);
assert.match(source, /semanticOverlay=\{mediaMode === "video" \? semanticOverlay : undefined\}/);
});
@@ -497,14 +497,17 @@ test("vegetation realtime LAB uses one upstream Rerun clock and keeps archival r
assert.match(rerunSource, /resolveReplay\(resultId, value/);
assert.doesNotMatch(rerunSource, /canonical-overlay\.rrd/);
assert.match(rerunSource, /unifiedPerception: splitView/);
assert.match(rerunSource, /lockPerceptionCameraInteraction: mediaMode !== null/);
assert.match(rerunSource, /!splitView[\s\S]*event\.button !== 0/);
assert.match(rerunSource, /if \(!splitView\) stopNativeSplitTracking\(\)/);
assert.match(rerunSource, /event\.target instanceof HTMLCanvasElement/);
assert.match(rerunSource, /--canonical-rerun-camera-pane/);
assert.match(rerunSource, /onPointerDownCapture=\{splitView \? trackNativeSplit : undefined\}/);
assert.match(rerunSource, /lockPerceptionCameraInteraction: false/);
assert.match(rerunSource, /&& !semantics[\s\S]*&& !detections[\s\S]*&& !costmap/);
assert.doesNotMatch(rerunSource, /trackNativeSplit|rerunNativeCursor|rerunNativeInput/);
assert.match(canonicalSource, /resizable=\{splitView\}/);
assert.match(rerunSource, /unifiedCameraShare: blueprintSplitPrimarySize \/ 100/);
assert.match(rerunSource, /data-split-view=\{splitView \? "true" : undefined\}/);
assert.match(rerunSource, /mediaMode === null[\s\S]*\? 0[\s\S]*: splitView[\s\S]*\? nativeSplitPercentRef\.current[\s\S]*: 100/);
assert.match(rerunSource, /max=\{500\}/);
assert.match(rerunSource, /exactValueBounds=\{\{ min: 0 \}\}/);
assert.match(replayStyles, /m4-replay-threat-visual__pane-toolbar \{[\s\S]*flex-wrap: wrap;/);
assert.match(replayStyles, /m4-replay-threat-visual__spatial-toolbar-end \{[\s\S]*display: contents;/);
assert.match(rerunSource, /<ToastStack items=\{toasts\}/);
assert.match(rerunSource, /isRecordedPlaybackPresentationReady\(viewerStatus, playback\)/);
assert.match(rerunSource, /data-presentation-state=\{presentationState\}/);
assert.match(rerunSource, /<ActivityIndicator label="Загружаем синхронизированную запись"/);
@@ -517,22 +520,14 @@ test("vegetation realtime LAB uses one upstream Rerun clock and keeps archival r
replayStyles,
/canonical-vegetation-rerun-replay__timeline \.observation-timeline__playback \{[\s\S]*grid-template-columns: auto auto minmax\(0, 1fr\) auto;/,
);
assert.match(
replayStyles,
/canonical-vegetation-rerun-replay__viewport-lock[\s\S]*rerun-viewport__camera-lock \{[\s\S]*width: var\(--canonical-rerun-camera-pane, 100%\);/,
);
assert.match(
replayStyles,
/canonical-vegetation-rerun-replay__viewport-lock\[data-split-view="true"\][\s\S]*width: calc\(var\(--canonical-rerun-camera-pane, 46%\) - 0\.75rem\);/,
);
assert.doesNotMatch(rerunSource, /LaboratoryRecordedClipPlayer|LaboratoryMetricEvidenceScene/);
assert.match(resultSource, /point-aligned 3D semantics пока не запечатаны/);
assert.match(rerunSource, /value: "tgs", label: "TGS", disabled: !costmap/);
assert.match(rerunSource, /hasTgs \? toggle\("TGS", showTgs/);
assert.match(canonicalSource, /primary=\{mediaPane\}/);
assert.match(canonicalSource, /secondary=\{spatialPane/);
assert.match(canonicalSource, /missioncore\.canonical-recorded-lab-replay\/v1/);
assert.match(canonicalSource, /separatorLabel="Изменить размер видео\/камеры и 3D\/плана"/);
assert.match(canonicalSource, /resizable=\{splitView && !unifiedContent\}/);
assert.match(canonicalSource, /resizable=\{splitView\}/);
assert.match(resultSource, /linked canonical M4\.9 TGS evidence/);
assert.match(resultSource, /linkedTgsResultId/);
assert.match(benchmarkSource, /M48MaskComparisonVisual/);