feat(perception): add E34 temporal occupied layer

This commit is contained in:
DCCONSTRUCTIONS
2026-07-27 16:55:40 +03:00
parent 95c6540691
commit 621084fcd6
22 changed files with 4732 additions and 104 deletions
@@ -93,6 +93,7 @@ export interface AdvancedLaboratoryResults {
e31: E31LaboratoryResult | null;
e32: E32LaboratoryResult | null;
e33: E33LaboratoryResult | null;
e34: E34TemporalLayerResult | null;
}
export class AdvancedLaboratoryContractError extends Error {
@@ -371,10 +372,15 @@ export async function fetchAdvancedLaboratoryResults({
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
} = {}): Promise<AdvancedLaboratoryResults> {
const [e31, e32, e33] = await Promise.all([
const [e31, e32, e33, e34] = await Promise.all([
fetchOne("/api/v1/laboratory/e31/results?limit=1", parseE31, fetcher, signal),
fetchOne("/api/v1/laboratory/e32/results?limit=1", parseE32, fetcher, signal),
fetchOne("/api/v1/laboratory/e33/results?limit=1", parseE33, fetcher, signal),
fetchE34TemporalLayerResult({ fetcher, signal }),
]);
return { e31, e32, e33 };
return { e31, e32, e33, e34 };
}
import {
fetchE34TemporalLayerResult,
type E34TemporalLayerResult,
} from "./e34TemporalLayer";
@@ -0,0 +1,480 @@
export type E34TemporalState = "current" | "held" | "expired";
export type E34OccupancyState = "occupied" | "unknown";
export type E34OwnerKind = "camera-track" | "geometry-cluster";
export type E34Point3 = readonly [number, number, number];
export interface E34TemporalHistoryPoint {
frameIndex: number;
sessionSeconds: number;
centroidMapXyzM: E34Point3;
}
export interface E34TemporalComponent {
temporalId: number;
state: E34TemporalState;
occupancyState: E34OccupancyState;
ownerKind: E34OwnerKind;
centroidMapXyzM: E34Point3;
lastObservedAgeSeconds: number;
associationReason: string;
history: readonly E34TemporalHistoryPoint[];
}
export interface E34TemporalReviewFrame {
frameIndex: number;
sessionSeconds: number;
sourceAvailable: boolean;
layerState: "current" | "held" | "unknown";
counts: {
current: number;
held: number;
expired: number;
};
cellCentersMapXyzM: readonly E34Point3[];
components: readonly E34TemporalComponent[];
}
export interface E34TemporalLayerResult {
resultId: string;
createdAtUtc: string | null;
sourceSessionId: string;
status: "accepted-bounded-occupied-unknown-temporal-layer";
e32ResultId: string;
e33ResultId: string;
profileId: string;
pipelineId: string;
coordinateFrame: "map";
configuration: {
voxelSizeM: number;
occupiedTtlSeconds: number;
maximumActiveComponents: number;
maximumCellsPerComponent: number;
geometryMaximumGapSeconds: number;
geometryMaximumCentroidDistanceM: number;
};
metrics: {
sourceFrames: number;
processedFrames: number;
framesWithCurrentLayer: number;
heldOnlyFrames: number;
unknownEmptyFrames: number;
createdComponents: number;
cameraCreatedComponents: number;
geometryCreatedComponents: number;
geometryReassociatedComponents: number;
exactCameraAssociations: number;
geometrySpatialReassociations: number;
heldPublications: number;
expiredComponents: number;
peakActiveComponents: number;
maximumObservedSpanSeconds: number;
continuityFraction: number;
geometryOnlyReassociationFraction: number;
e32CurrentPointRows: number;
e34ConsumedCurrentPointRows: number;
currentCellRows: number;
heldCellRows: number;
storedCellRows: number;
freeCellRows: number;
peakCellsPerComponent: number;
maximumHeldAgeSeconds: number;
maximumExpiryDelaySeconds: number;
maximumReplayMaterializationDelaySeconds: number;
mapFrameJumpCandidates: number;
frameProcessingP95Ms: number;
buildElapsedMs: number;
};
reviewFrames: readonly E34TemporalReviewFrame[];
access: "read-only";
}
type LaboratoryFetch = (
input: RequestInfo | URL,
init?: RequestInit,
) => Promise<Response>;
class E34TemporalLayerContractError extends Error {
constructor(message: string) {
super(message);
this.name = "E34TemporalLayerContractError";
}
}
function record(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new E34TemporalLayerContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function list(value: unknown, label: string): readonly unknown[] {
if (!Array.isArray(value)) {
throw new E34TemporalLayerContractError(`${label}: ожидался массив.`);
}
return value;
}
function stringValue(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new E34TemporalLayerContractError(`${label}: ожидалась строка.`);
}
return value;
}
function optionalString(value: unknown, label: string): string | null {
return value === null ? null : stringValue(value, label);
}
function finiteNumber(value: unknown, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new E34TemporalLayerContractError(`${label}: ожидалось число.`);
}
return value;
}
function numberValue(value: unknown, label: string): number {
const parsed = finiteNumber(value, label);
if (parsed < 0) {
throw new E34TemporalLayerContractError(`${label}: число отрицательно.`);
}
return parsed;
}
function integerValue(value: unknown, label: string): number {
const parsed = numberValue(value, label);
if (!Number.isSafeInteger(parsed)) {
throw new E34TemporalLayerContractError(`${label}: ожидалось целое число.`);
}
return parsed;
}
function booleanValue(value: unknown, label: string): boolean {
if (typeof value !== "boolean") {
throw new E34TemporalLayerContractError(`${label}: ожидался boolean.`);
}
return value;
}
function exactString<T extends string>(
value: unknown,
expected: T,
label: string,
): T {
if (value !== expected) {
throw new E34TemporalLayerContractError(`${label}: неверное значение.`);
}
return expected;
}
function oneOf<T extends string>(
value: unknown,
expected: readonly T[],
label: string,
): T {
if (typeof value !== "string" || !expected.includes(value as T)) {
throw new E34TemporalLayerContractError(`${label}: неверное значение.`);
}
return value as T;
}
function contentId(value: unknown, prefix: string, label: string): string {
const parsed = stringValue(value, label);
if (!new RegExp(`^${prefix}-[a-f0-9]{64}$`).test(parsed)) {
throw new E34TemporalLayerContractError(`${label}: неверный content id.`);
}
return parsed;
}
function point(value: unknown, label: string): E34Point3 {
const coordinates = list(value, label);
if (coordinates.length !== 3) {
throw new E34TemporalLayerContractError(`${label}: ожидались XYZ.`);
}
return [
finiteNumber(coordinates[0], `${label}[0]`),
finiteNumber(coordinates[1], `${label}[1]`),
finiteNumber(coordinates[2], `${label}[2]`),
];
}
function parseHistory(value: unknown, label: string): E34TemporalHistoryPoint {
const item = record(value, label);
return {
frameIndex: integerValue(item.frame_index, `${label}.frame_index`),
sessionSeconds: numberValue(
item.session_seconds,
`${label}.session_seconds`,
),
centroidMapXyzM: point(
item.centroid_map_xyz_m,
`${label}.centroid_map_xyz_m`,
),
};
}
function parseComponent(value: unknown, label: string): E34TemporalComponent {
const item = record(value, label);
return {
temporalId: integerValue(item.temporal_id, `${label}.temporal_id`),
state: oneOf(
item.state,
["current", "held", "expired"] as const,
`${label}.state`,
),
occupancyState: oneOf(
item.occupancy_state,
["occupied", "unknown"] as const,
`${label}.occupancy_state`,
),
ownerKind: oneOf(
item.owner_kind,
["camera-track", "geometry-cluster"] as const,
`${label}.owner_kind`,
),
centroidMapXyzM: point(
item.centroid_map_xyz_m,
`${label}.centroid_map_xyz_m`,
),
lastObservedAgeSeconds: numberValue(
item.last_observed_age_seconds,
`${label}.last_observed_age_seconds`,
),
associationReason: stringValue(
item.association_reason,
`${label}.association_reason`,
),
history: list(item.history_tail, `${label}.history_tail`).map(
(entry, index) => parseHistory(entry, `${label}.history_tail[${index}]`),
),
};
}
function parseReviewFrame(
value: unknown,
label: string,
): E34TemporalReviewFrame {
const item = record(value, label);
const counts = record(item.counts, `${label}.counts`);
return {
frameIndex: integerValue(item.frame_index, `${label}.frame_index`),
sessionSeconds: numberValue(
item.session_seconds,
`${label}.session_seconds`,
),
sourceAvailable: booleanValue(
item.source_available,
`${label}.source_available`,
),
layerState: oneOf(
item.layer_state,
["current", "held", "unknown"] as const,
`${label}.layer_state`,
),
counts: {
current: integerValue(counts.current, `${label}.counts.current`),
held: integerValue(counts.held, `${label}.counts.held`),
expired: integerValue(counts.expired, `${label}.counts.expired`),
},
cellCentersMapXyzM: list(
item.cell_centers_map_xyz_m,
`${label}.cell_centers_map_xyz_m`,
).map((entry, index) => point(
entry,
`${label}.cell_centers_map_xyz_m[${index}]`,
)),
components: list(item.components, `${label}.components`).map(
(entry, index) => parseComponent(
entry,
`${label}.components[${index}]`,
),
),
};
}
function diagnosticAuthority(value: unknown, label: string): void {
const authority = record(value, label);
if (
authority.commands_enabled !== false
|| authority.navigation_or_safety_accepted !== false
) {
throw new E34TemporalLayerContractError(
`${label}: запрещённые полномочия.`,
);
}
}
function metricRecord(
value: Record<string, unknown>,
): E34TemporalLayerResult["metrics"] {
const metric = (key: string) => numberValue(
value[key],
`E34.metrics.${key}`,
);
const integer = (key: string) => integerValue(
value[key],
`E34.metrics.${key}`,
);
return {
sourceFrames: integer("source_frames"),
processedFrames: integer("processed_frames"),
framesWithCurrentLayer: integer("frames_with_current_layer"),
heldOnlyFrames: integer("held_only_frames"),
unknownEmptyFrames: integer("unknown_empty_frames"),
createdComponents: integer("created_components"),
cameraCreatedComponents: integer("camera_created_components"),
geometryCreatedComponents: integer("geometry_created_components"),
geometryReassociatedComponents: integer("geometry_reassociated_components"),
exactCameraAssociations: integer("exact_camera_associations"),
geometrySpatialReassociations: integer("geometry_spatial_reassociations"),
heldPublications: integer("held_publications"),
expiredComponents: integer("expired_components"),
peakActiveComponents: integer("peak_active_components"),
maximumObservedSpanSeconds: metric("maximum_observed_span_seconds"),
continuityFraction: metric("continuity_fraction"),
geometryOnlyReassociationFraction: metric(
"geometry_only_reassociation_fraction",
),
e32CurrentPointRows: integer("e32_current_point_rows"),
e34ConsumedCurrentPointRows: integer("e34_consumed_current_point_rows"),
currentCellRows: integer("current_cell_rows"),
heldCellRows: integer("held_cell_rows"),
storedCellRows: integer("stored_cell_rows"),
freeCellRows: integer("free_cell_rows"),
peakCellsPerComponent: integer("peak_cells_per_component"),
maximumHeldAgeSeconds: metric("maximum_held_age_seconds"),
maximumExpiryDelaySeconds: metric("maximum_expiry_delay_seconds"),
maximumReplayMaterializationDelaySeconds: metric(
"maximum_replay_materialization_delay_seconds",
),
mapFrameJumpCandidates: integer("map_frame_jump_candidates"),
frameProcessingP95Ms: metric("frame_processing_p95_ms"),
buildElapsedMs: metric("build_elapsed_ms"),
};
}
function parseResult(value: unknown): E34TemporalLayerResult {
const item = record(value, "E34");
const configuration = record(item.configuration, "E34.configuration");
const review = record(item.review, "E34.review");
const acceptance = record(item.acceptance, "E34.acceptance");
diagnosticAuthority(item.authority, "E34.authority");
if (acceptance.accepted !== true) {
throw new E34TemporalLayerContractError("E34.acceptance: результат отклонён.");
}
exactString(
review.schema_version,
"missioncore.e34-temporal-review-timeline/v1",
"E34.review.schema_version",
);
return {
resultId: contentId(
item.result_id,
"e34-temporal-occupied",
"E34.result_id",
),
createdAtUtc: optionalString(item.created_at_utc, "E34.created_at_utc"),
sourceSessionId: stringValue(
item.source_session_id,
"E34.source_session_id",
),
status: exactString(
item.status,
"accepted-bounded-occupied-unknown-temporal-layer",
"E34.status",
),
e32ResultId: contentId(
item.e32_result_id,
"e32-track-geometry",
"E34.e32_result_id",
),
e33ResultId: contentId(
item.e33_result_id,
"e33-worker-shadow",
"E34.e33_result_id",
),
profileId: stringValue(item.profile_id, "E34.profile_id"),
pipelineId: stringValue(item.pipeline_id, "E34.pipeline_id"),
coordinateFrame: exactString(
item.coordinate_frame,
"map",
"E34.coordinate_frame",
),
configuration: {
voxelSizeM: numberValue(
configuration.voxel_size_m,
"E34.configuration.voxel_size_m",
),
occupiedTtlSeconds: numberValue(
configuration.occupied_ttl_seconds,
"E34.configuration.occupied_ttl_seconds",
),
maximumActiveComponents: integerValue(
configuration.maximum_active_components,
"E34.configuration.maximum_active_components",
),
maximumCellsPerComponent: integerValue(
configuration.maximum_cells_per_component,
"E34.configuration.maximum_cells_per_component",
),
geometryMaximumGapSeconds: numberValue(
configuration.geometry_maximum_gap_seconds,
"E34.configuration.geometry_maximum_gap_seconds",
),
geometryMaximumCentroidDistanceM: numberValue(
configuration.geometry_maximum_centroid_distance_m,
"E34.configuration.geometry_maximum_centroid_distance_m",
),
},
metrics: metricRecord(record(item.metrics, "E34.metrics")),
reviewFrames: list(review.frames, "E34.review.frames").map(
(entry, index) => parseReviewFrame(
entry,
`E34.review.frames[${index}]`,
),
),
access: exactString(item.access, "read-only", "E34.access"),
};
}
export async function fetchE34TemporalLayerResult({
fetcher = fetch,
signal,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
} = {}): Promise<E34TemporalLayerResult | null> {
const response = await fetcher(
"/api/v1/laboratory/e34/results?limit=1",
{
method: "GET",
headers: { Accept: "application/json" },
signal,
},
);
if (!response.ok) {
throw new E34TemporalLayerContractError(
`Каталог LAB E34 недоступен: HTTP ${response.status}.`,
);
}
const catalog = record(await response.json(), "Каталог LAB E34");
exactString(
catalog.schema_version,
"missioncore.laboratory-advanced-catalog/v1",
"Каталог LAB E34.schema_version",
);
booleanValue(catalog.configured, "Каталог LAB E34.configured");
integerValue(catalog.candidate_total, "Каталог LAB E34.candidate_total");
integerValue(catalog.invalid_total, "Каталог LAB E34.invalid_total");
exactString(
catalog.access,
"read-only",
"Каталог LAB E34.access",
);
const items = list(catalog.items, "Каталог LAB E34.items");
if (items.length > 1) {
throw new E34TemporalLayerContractError(
"Каталог LAB E34: нарушен limit=1.",
);
}
return items.length ? parseResult(items[0]) : null;
}
+1
View File
@@ -3,6 +3,7 @@
@import "./styles/workspaces.css";
@import "./styles/laboratory.css";
@import "./styles/laboratory-reporting.css";
@import "./styles/e34-temporal-layer.css";
@import "./styles/e30-human-review.css";
@import "./styles/spatial.css";
@import "./styles/device.css";
@@ -0,0 +1,169 @@
.e34-temporal-evidence,
.e34-temporal-scene {
width: 100%;
height: 100%;
min-width: 0;
min-height: 0;
}
.e34-temporal-scene {
position: relative;
overflow: hidden;
background: var(--nodedc-canvas);
}
.e34-temporal-scene__viewport {
position: absolute;
inset: 0;
}
.e34-temporal-scene__viewport canvas {
display: block;
width: 100%;
height: 100%;
cursor: grab;
touch-action: none;
}
.e34-temporal-scene__viewport canvas:active {
cursor: grabbing;
}
.e34-temporal-scene__toolbar {
position: absolute;
z-index: 3;
top: 0.6rem;
left: 0.6rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.e34-temporal-scene__toolbar .nodedc-button {
background: var(--nodedc-floating-surface);
backdrop-filter: blur(var(--nodedc-blur-control));
}
.e34-temporal-scene__gestures,
.e34-temporal-scene__legend,
.e34-temporal-evidence__telemetry {
border-radius: var(--nodedc-radius-control-compact);
background: var(--nodedc-floating-surface);
color: var(--nodedc-text-secondary);
backdrop-filter: blur(var(--nodedc-blur-control));
}
.e34-temporal-scene__gestures {
display: flex;
gap: 0.55rem;
padding: 0.43rem 0.55rem;
}
.e34-temporal-scene__gestures span,
.e34-temporal-scene__legend span,
.e34-temporal-evidence__telemetry dt,
.e34-temporal-evidence__telemetry dd {
font-size: 0.5rem;
}
.e34-temporal-scene__legend {
position: absolute;
right: 0.6rem;
bottom: 0.6rem;
display: flex;
flex-wrap: wrap;
gap: 0.55rem;
padding: 0.42rem 0.55rem;
}
.e34-temporal-scene__legend span {
display: inline-flex;
align-items: center;
gap: 0.25rem;
}
.e34-temporal-scene__legend span::before {
width: 0.38rem;
height: 0.38rem;
border-radius: 50%;
background: var(--nodedc-text-muted);
content: "";
}
.e34-temporal-scene__legend span[data-state="current"]::before {
background: rgb(var(--nodedc-accent-rgb));
}
.e34-temporal-scene__legend span[data-state="held"]::before {
background: rgb(var(--nodedc-warning-rgb));
}
.e34-temporal-scene__legend span[data-state="expired"]::before {
background: rgb(var(--nodedc-danger-rgb));
}
.e34-temporal-evidence__telemetry {
position: absolute;
z-index: 3;
left: 0.6rem;
bottom: 0.6rem;
display: grid;
width: min(13rem, calc(100% - 1.2rem));
gap: 0.35rem;
margin: 0;
padding: 0.55rem 0.65rem;
pointer-events: none;
}
.e34-temporal-evidence__telemetry > div {
display: grid;
gap: 0.12rem;
}
.e34-temporal-evidence__telemetry dt,
.e34-temporal-evidence__telemetry dd {
margin: 0;
}
.e34-temporal-evidence__telemetry dt {
color: var(--nodedc-text-muted);
}
.e34-temporal-evidence__telemetry dd {
overflow: hidden;
color: var(--nodedc-text-primary);
font-weight: 650;
text-overflow: ellipsis;
white-space: nowrap;
}
.e34-temporal-evidence .laboratory-evidence-viewer__controls
.nodedc-select-anchor {
width: clamp(12rem, 24vw, 20rem);
}
.e34-temporal-scene__error {
position: absolute;
inset: 0;
display: grid;
place-items: center;
color: var(--nodedc-text-muted);
font-size: 0.62rem;
}
@media (max-width: 900px) {
.e34-temporal-scene__gestures {
display: none;
}
.e34-temporal-scene__legend {
left: 0.6rem;
right: auto;
bottom: 5.7rem;
}
.e34-temporal-evidence .laboratory-evidence-viewer__controls
.nodedc-select-anchor {
width: 10rem;
}
}
@@ -0,0 +1,146 @@
import type { ComponentType } from "react";
import type { LaboratoryOption } from "../../components/laboratory/LaboratoryPresentation";
import type { AdvancedLaboratoryResults } from "../../core/laboratory/advancedResults";
import type { ObservationSessionSummary } from "../../core/observation/sessionArchive";
import type { WorkspaceRendererProps } from "../contracts";
import { E31Result } from "./E31Result";
import { E32Result } from "./E32Result";
import { E33Result } from "./E33Result";
import { E34Result } from "./E34Result";
import { RecordedReplayEvidence } from "./RecordedReplayEvidence";
export type AdvancedLaboratoryWorkId =
| "e31-source-binding"
| "e32-track-geometry"
| "e33-worker-shadow"
| "e34-temporal-layer";
type LaboratoryWorkspaceProps = WorkspaceRendererProps & {
SpatialView: ComponentType<WorkspaceRendererProps>;
};
export function isAdvancedLaboratoryWorkId(
value: string,
): value is AdvancedLaboratoryWorkId {
return (
value === "e31-source-binding"
|| value === "e32-track-geometry"
|| value === "e33-worker-shadow"
|| value === "e34-temporal-layer"
);
}
export function advancedLaboratoryWorkOptions(
results: AdvancedLaboratoryResults,
sourceSessions: ReadonlyMap<string, ObservationSessionSummary>,
): readonly LaboratoryOption<AdvancedLaboratoryWorkId>[] {
const options: LaboratoryOption<AdvancedLaboratoryWorkId>[] = [];
if (results.e31 && sourceSessions.has(results.e31.sourceSessionId)) {
options.push({
id: "e31-source-binding",
label: "LAB E31 · source binding",
});
}
if (results.e32 && sourceSessions.has(results.e32.sourceSessionId)) {
options.push({
id: "e32-track-geometry",
label: "LAB E32 · TrackGeometry v1",
});
}
if (results.e33 && sourceSessions.has(results.e33.sourceSessionId)) {
options.push({
id: "e33-worker-shadow",
label: "LAB E33 · worker shadow 1×",
});
}
if (results.e34) {
options.push({
id: "e34-temporal-layer",
label: "LAB E34 · temporal occupied/unknown",
});
}
return options;
}
export function advancedLaboratorySourceSession(
workId: AdvancedLaboratoryWorkId,
results: AdvancedLaboratoryResults,
sourceSessions: ReadonlyMap<string, ObservationSessionSummary>,
): ObservationSessionSummary | null {
const sourceSessionId = workId === "e31-source-binding"
? results.e31?.sourceSessionId
: workId === "e32-track-geometry"
? results.e32?.sourceSessionId
: workId === "e33-worker-shadow"
? results.e33?.sourceSessionId
: null;
return sourceSessionId ? sourceSessions.get(sourceSessionId) ?? null : null;
}
export function AdvancedLaboratoryResult({
props,
rigLabel,
workId,
results,
sourceSessions,
replayingSessionId,
failedSessionId,
replayError,
}: {
props: LaboratoryWorkspaceProps;
rigLabel: string;
workId: AdvancedLaboratoryWorkId;
results: AdvancedLaboratoryResults;
sourceSessions: ReadonlyMap<string, ObservationSessionSummary>;
replayingSessionId: string | null;
failedSessionId: string | null;
replayError: string | null;
}) {
if (workId === "e34-temporal-layer" && results.e34) {
return <E34Result rigLabel={rigLabel} result={results.e34} />;
}
const sourceSession = advancedLaboratorySourceSession(
workId,
results,
sourceSessions,
);
if (!sourceSession) return null;
const evidence = (
<RecordedReplayEvidence
props={props}
sourceSession={sourceSession}
loading={replayingSessionId === sourceSession.id}
error={failedSessionId === sourceSession.id ? replayError : null}
/>
);
if (workId === "e31-source-binding" && results.e31) {
return (
<E31Result
rigLabel={rigLabel}
result={results.e31}
evidence={evidence}
/>
);
}
if (workId === "e32-track-geometry" && results.e32) {
return (
<E32Result
rigLabel={rigLabel}
result={results.e32}
evidence={evidence}
/>
);
}
if (workId === "e33-worker-shadow" && results.e33) {
return (
<E33Result
rigLabel={rigLabel}
result={results.e33}
evidence={evidence}
/>
);
}
return null;
}
@@ -0,0 +1,236 @@
import { useMemo, useState } from "react";
import { Select } from "@nodedc/ui-react";
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
import type {
E34TemporalLayerResult,
E34TemporalReviewFrame,
} from "../../core/laboratory/e34TemporalLayer";
import { formatNumber } from "../../presentation";
import {
E34TemporalLayerScene,
type E34TemporalViewMode,
} from "./E34TemporalLayerScene";
function percent(value: number): string {
return `${(value * 100).toLocaleString("ru-RU", {
maximumFractionDigits: 1,
})}%`;
}
function defaultFrame(
frames: readonly E34TemporalReviewFrame[],
): E34TemporalReviewFrame | null {
return frames.find((frame) => (
frame.counts.current > 0 && frame.counts.held > 0
)) ?? frames.find((frame) => frame.counts.expired > 0) ?? frames[0] ?? null;
}
function frameLabel(frame: E34TemporalReviewFrame): string {
const state = frame.counts.expired
? `истекло ${frame.counts.expired}`
: frame.counts.held
? `удерживается ${frame.counts.held}`
: `наблюдается ${frame.counts.current}`;
return `Кадр ${formatNumber(frame.frameIndex, 0)} · ${state}`;
}
function E34Evidence({
result,
}: {
result: E34TemporalLayerResult;
}) {
const initial = useMemo(
() => defaultFrame(result.reviewFrames),
[result.reviewFrames],
);
const [frameIndex, setFrameIndex] = useState(initial?.frameIndex ?? 0);
const [mode, setMode] = useState<E34TemporalViewMode>("3d");
const [expanded, setExpanded] = useState(false);
const frame = result.reviewFrames.find(
(item) => item.frameIndex === frameIndex,
) ?? initial;
if (!frame) {
return (
<div className="laboratory-result-pending" role="status">
Контрольные состояния временного слоя не опубликованы.
</div>
);
}
return (
<div className="e34-temporal-evidence">
<LaboratoryEvidenceViewer
label="Временной occupied/unknown слой E34"
mode={mode}
modes={[
{ value: "3d", label: "3D" },
{ value: "plan", label: "План" },
]}
expanded={expanded}
onModeChange={setMode}
onExpandedChange={setExpanded}
actions={(
<Select
label="Контрольное состояние E34"
value={String(frame.frameIndex)}
options={result.reviewFrames.map((item) => ({
value: String(item.frameIndex),
label: frameLabel(item),
}))}
variant="split"
menuWidth="anchor"
onChange={(value) => setFrameIndex(Number(value))}
/>
)}
overlay={(
<dl className="e34-temporal-evidence__telemetry">
<div>
<dt>Кадр / время</dt>
<dd>
{formatNumber(frame.frameIndex, 0)}
{" · "}
{frame.sessionSeconds.toLocaleString("ru-RU", {
maximumFractionDigits: 3,
})}
{" с"}
</dd>
</div>
<div>
<dt>Вход</dt>
<dd>{frame.sourceAvailable ? "Есть текущие точки" : "Текущих точек нет"}</dd>
</div>
<div>
<dt>Состояние слоя</dt>
<dd>
{frame.counts.current} current · {frame.counts.held} held · {frame.counts.expired} expired
</dd>
</div>
</dl>
)}
>
<E34TemporalLayerScene frame={frame} mode={mode} />
</LaboratoryEvidenceViewer>
</div>
);
}
export function E34Result({
rigLabel,
result,
}: {
rigLabel: string;
result: E34TemporalLayerResult;
}) {
const metrics = result.metrics;
const config = result.configuration;
return (
<LaboratoryWorkTemplate
summary={(
<LaboratorySummary
title="LAB E34 · короткоживущий occupied/unknown слой"
description="Проверяли, может ли TrackGeometry поддерживать ограниченную во времени карту занятости: подтверждённые точки публикуются как occupied, исчезнувшее наблюдение кратко удерживается как unknown и затем удаляется строго по TTL."
status="Временной слой принят"
statusTone="success"
facts={[
{ label: "Источник", value: `${rigLabel} · TrackGeometry E32` },
{
label: "Объём проверки",
value: `${formatNumber(metrics.processedFrames, 0)} / ${formatNumber(metrics.sourceFrames, 0)} кадров`,
},
{
label: "Параметры слоя",
value: `${config.voxelSizeM.toLocaleString("ru-RU")} м · TTL ${config.occupiedTtlSeconds.toLocaleString("ru-RU")} с`,
},
{ label: "Полномочия", value: "Диагностика · команды и safety выключены" },
]}
brief={{
question: "Можно ли сохранить краткую пространственную непрерывность объектов между наблюдениями, не объявляя ненаблюдаемое пространство свободным и не загрязняя постоянную карту?",
approach: "Полная запись E32 воспроизведена через hit-only voxel layer в map-frame. Camera-track связывались только по точной идентичности, geometry-only компоненты — по ограниченному пространственно-временному сопоставлению. Первый immutable replay был отклонён; после исправления логических часов TTL и детектора глобального сдвига тот же замороженный профиль выполнен повторно.",
principalResult: `Да, в диагностическом контуре. Обработаны все ${formatNumber(metrics.processedFrames, 0)} кадров и все ${formatNumber(metrics.e34ConsumedCurrentPointRows, 0)} текущих точек; непрерывность наблюдений составила ${percent(metrics.continuityFraction)}, задержка логического истечения — ${metrics.maximumExpiryDelaySeconds.toLocaleString("ru-RU", { maximumFractionDigits: 3 })} с.`,
limitation: "Слой не вычисляет свободное пространство, не назначает dynamic/static, не меняет persistent reconstruction и не имеет навигационных или safety-полномочий. Непрерывность — диагностическая метрика, не ground truth.",
}}
method={{
completeness: "complete",
executionClass: "deterministic",
pipelineId: "TrackGeometry → short-TTL hit-only occupied/unknown layer",
components: [
{
kind: "source",
name: "Принятый TrackGeometry из LAB E32",
version: `${formatNumber(metrics.e32CurrentPointRows, 0)} квалифицированных точек`,
role: "неизменяемый map-frame вход с явной принадлежностью точек",
identitySha256: null,
},
{
kind: "algorithm",
name: "Bounded temporal component association",
version: `voxel ${config.voxelSizeM} м · TTL ${config.occupiedTtlSeconds} с`,
role: "точное camera-связывание, geometry-only reassociation, hold и независимое expiry",
identitySha256: null,
},
{
kind: "runtime",
name: "Fail-closed diagnostic replay",
version: `${metrics.frameProcessingP95Ms.toLocaleString("ru-RU", { maximumFractionDigits: 3 })} мс/frame p95`,
role: "полный учёт кадров, bounds, контроль map-frame и неизменность upstream",
identitySha256: null,
},
],
}}
/>
)}
evidence={(
<LaboratoryEvidence
eyebrow="ВРЕМЕННОЕ ДОКАЗАТЕЛЬСТВО"
title="Контрольные состояния: наблюдение, удержание unknown и точное истечение"
kind="diagnostic-model"
resizable
>
<E34Evidence result={result} />
</LaboratoryEvidence>
)}
result={(
<LaboratoryResultSummary
title="Краткая непрерывность доказана; свободное пространство намеренно не выводится"
status="12 / 12 gate"
statusTone="success"
metrics={[
{
label: "Полный replay",
value: `${formatNumber(metrics.processedFrames, 0)} / ${formatNumber(metrics.sourceFrames, 0)}`,
hint: "все входные точки учтены",
},
{
label: "Связность компонентов",
value: percent(metrics.continuityFraction),
hint: `${formatNumber(metrics.exactCameraAssociations + metrics.geometrySpatialReassociations, 0)} повторных связей`,
},
{
label: "TTL / истечение",
value: `${metrics.maximumHeldAgeSeconds.toLocaleString("ru-RU", { maximumFractionDigits: 2 })} с`,
hint: `${metrics.maximumExpiryDelaySeconds.toLocaleString("ru-RU", { maximumFractionDigits: 3 })} с задержка`,
},
{
label: "Ограниченность",
value: `${formatNumber(metrics.peakActiveComponents, 0)} / ${formatNumber(config.maximumActiveComponents, 0)}`,
hint: `${formatNumber(metrics.peakCellsPerComponent, 0)} / ${formatNumber(config.maximumCellsPerComponent, 0)} ячеек`,
},
]}
conclusion={{
proved: "На данной полной записи hit-backed компоненты можно детерминированно удерживать до 0,75 с и удалять по независимому deadline, сохраняя bounds, полный учёт и неизменность E32/E33.",
notProved: "Работа не доказывает свободное пространство, динамический класс, качество планирования, переносимость порогов на другой сенсорный риг или корректность при реальном глобальном скачке map-frame.",
decision: "E34 принимается как read-only диагностический слой. Следующий критический gate — E35: детерминированно проверить деградацию и восстановление при потере источника, нарушении сроков и map-frame fault.",
}}
/>
)}
/>
);
}
@@ -0,0 +1,363 @@
import { useEffect, useMemo, useRef, useState } from "react";
import { Button, Icon } from "@nodedc/ui-react";
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import type {
E34Point3,
E34TemporalComponent,
E34TemporalReviewFrame,
} from "../../core/laboratory/e34TemporalLayer";
export type E34TemporalViewMode = "3d" | "plan";
function tokenColor(
host: HTMLElement,
token: string,
fallback: readonly [number, number, number],
): THREE.Color {
const value = getComputedStyle(host).getPropertyValue(token).trim();
if (value.startsWith("#")) {
return new THREE.Color(value);
}
const channels = value.match(/[\d.]+/g)?.slice(0, 3).map(Number);
const [red, green, blue] = channels?.length === 3
? channels
: fallback;
return new THREE.Color(red / 255, green / 255, blue / 255);
}
function disposeRenderable(object: THREE.Object3D): void {
const renderable = object as THREE.Object3D & {
geometry?: THREE.BufferGeometry;
material?: THREE.Material | THREE.Material[];
};
renderable.geometry?.dispose();
const materials = Array.isArray(renderable.material)
? renderable.material
: renderable.material
? [renderable.material]
: [];
materials.forEach((material) => material.dispose());
}
function scenePoint(
point: E34Point3,
origin: E34Point3,
): readonly [number, number, number] {
return [
point[0] - origin[0],
point[2] - origin[2],
-(point[1] - origin[1]),
];
}
function positions(
points: readonly E34Point3[],
origin: E34Point3,
): Float32Array {
const result = new Float32Array(points.length * 3);
points.forEach((point, index) => {
const [x, y, z] = scenePoint(point, origin);
const offset = index * 3;
result[offset] = x;
result[offset + 1] = y;
result[offset + 2] = z;
});
return result;
}
function median(values: readonly number[]): number {
if (!values.length) return 0;
const sorted = [...values].sort((left, right) => left - right);
return sorted[Math.floor(sorted.length / 2)] ?? 0;
}
function frameOrigin(frame: E34TemporalReviewFrame): E34Point3 {
const anchors = frame.components.length
? frame.components.map((component) => component.centroidMapXyzM)
: frame.cellCentersMapXyzM;
return [
median(anchors.map((point) => point[0])),
median(anchors.map((point) => point[1])),
median(anchors.map((point) => point[2])),
];
}
function componentColor(
host: HTMLElement,
component: E34TemporalComponent,
): THREE.Color {
if (component.state === "expired") {
return tokenColor(host, "--nodedc-danger-rgb", [255, 104, 112]);
}
if (component.state === "held") {
return tokenColor(host, "--nodedc-warning-rgb", [255, 209, 102]);
}
return tokenColor(host, "--nodedc-accent-rgb", [247, 248, 244]);
}
function fitRadius(frame: E34TemporalReviewFrame, origin: E34Point3): number {
const points = [
...frame.cellCentersMapXyzM,
...frame.components.map((component) => component.centroidMapXyzM),
];
if (!points.length) return 4;
const distances = points
.map((point) => {
const [x, y, z] = scenePoint(point, origin);
return Math.hypot(x, y, z);
})
.sort((left, right) => left - right);
const p95 = distances[Math.floor((distances.length - 1) * 0.95)] ?? 4;
return THREE.MathUtils.clamp(p95, 3, 32);
}
export function E34TemporalLayerScene({
frame,
mode,
}: {
frame: E34TemporalReviewFrame;
mode: E34TemporalViewMode;
}) {
const hostRef = useRef<HTMLDivElement | null>(null);
const sceneRef = useRef<THREE.Scene | null>(null);
const cameraRef = useRef<THREE.PerspectiveCamera | null>(null);
const controlsRef = useRef<OrbitControls | null>(null);
const contentRef = useRef<THREE.Group | null>(null);
const viewRadiusRef = useRef(5);
const [renderError, setRenderError] = useState<string | null>(null);
const origin = useMemo(() => frameOrigin(frame), [frame]);
useEffect(() => {
const host = hostRef.current;
if (!host) return;
let renderer: THREE.WebGLRenderer;
try {
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: false,
powerPreference: "high-performance",
});
} catch {
setRenderError("Браузер не смог создать 3D-сцену временного слоя.");
return;
}
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.setClearColor(
tokenColor(host, "--nodedc-canvas", [5, 5, 6]),
1,
);
renderer.domElement.setAttribute(
"aria-label",
"Интерактивная 3D-сцена временного occupied/unknown слоя E34",
);
renderer.domElement.setAttribute("role", "img");
host.prepend(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(48, 1, 0.01, 500);
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.08;
controls.enablePan = true;
controls.enableZoom = true;
controls.screenSpacePanning = true;
controls.minDistance = 0.5;
controls.maxDistance = 160;
controls.target.set(0, 0, 0);
const content = new THREE.Group();
scene.add(content);
sceneRef.current = scene;
cameraRef.current = camera;
controlsRef.current = controls;
contentRef.current = content;
const resize = () => {
const width = Math.max(host.clientWidth, 1);
const height = Math.max(host.clientHeight, 1);
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height, false);
};
const observer = new ResizeObserver(resize);
observer.observe(host);
resize();
let animationFrame = 0;
const render = () => {
animationFrame = window.requestAnimationFrame(render);
controls.update();
renderer.render(scene, camera);
};
render();
return () => {
window.cancelAnimationFrame(animationFrame);
observer.disconnect();
controls.dispose();
scene.traverse(disposeRenderable);
renderer.dispose();
renderer.domElement.remove();
sceneRef.current = null;
cameraRef.current = null;
controlsRef.current = null;
contentRef.current = null;
};
}, []);
useEffect(() => {
const host = hostRef.current;
const content = contentRef.current;
if (!host || !content) return;
while (content.children.length) {
const child = content.children[0];
if (!child) continue;
content.remove(child);
child.traverse(disposeRenderable);
}
const contextGeometry = new THREE.BufferGeometry();
contextGeometry.setAttribute(
"position",
new THREE.BufferAttribute(
positions(frame.cellCentersMapXyzM, origin),
3,
),
);
const context = new THREE.Points(
contextGeometry,
new THREE.PointsMaterial({
color: tokenColor(host, "--nodedc-text-muted", [147, 151, 159]),
size: 2.4,
sizeAttenuation: false,
transparent: true,
opacity: 0.32,
depthWrite: false,
}),
);
content.add(context);
for (const component of frame.components) {
const color = componentColor(host, component);
const markerGeometry = new THREE.BufferGeometry();
markerGeometry.setAttribute(
"position",
new THREE.BufferAttribute(
positions([component.centroidMapXyzM], origin),
3,
),
);
const marker = new THREE.Points(
markerGeometry,
new THREE.PointsMaterial({
color,
size: component.state === "expired" ? 8 : 6,
sizeAttenuation: false,
transparent: true,
opacity: component.state === "expired" ? 0.72 : 1,
depthWrite: false,
}),
);
content.add(marker);
if (component.history.length > 1) {
const trailGeometry = new THREE.BufferGeometry();
trailGeometry.setAttribute(
"position",
new THREE.BufferAttribute(
positions(
component.history.map((item) => item.centroidMapXyzM),
origin,
),
3,
),
);
content.add(new THREE.Line(
trailGeometry,
new THREE.LineBasicMaterial({
color,
transparent: true,
opacity: component.state === "expired" ? 0.28 : 0.54,
}),
));
}
}
const radius = fitRadius(frame, origin);
viewRadiusRef.current = radius;
const grid = new THREE.GridHelper(
radius * 2.4,
20,
tokenColor(host, "--nodedc-text-muted", [96, 99, 106]),
tokenColor(host, "--nodedc-glass-outline", [48, 50, 56]),
);
const materials = Array.isArray(grid.material)
? grid.material
: [grid.material];
materials.forEach((material) => {
material.transparent = true;
material.opacity = 0.15;
material.depthWrite = false;
});
content.add(grid);
}, [frame, origin]);
const resetView = () => {
const camera = cameraRef.current;
const controls = controlsRef.current;
if (!camera || !controls) return;
const radius = viewRadiusRef.current;
controls.target.set(0, 0, 0);
if (mode === "plan") {
camera.position.set(0, radius * 2.8, 0.001);
camera.up.set(0, 0, -1);
} else {
camera.position.set(radius * 1.35, radius * 0.9, radius * 1.35);
camera.up.set(0, 1, 0);
}
camera.near = Math.max(radius / 2_000, 0.005);
camera.far = Math.max(radius * 20, 120);
camera.updateProjectionMatrix();
controls.maxDistance = Math.max(radius * 8, 40);
controls.update();
};
useEffect(resetView, [frame, mode]);
return (
<div className="e34-temporal-scene">
<div ref={hostRef} className="e34-temporal-scene__viewport">
{renderError ? (
<p className="e34-temporal-scene__error">{renderError}</p>
) : null}
</div>
<div className="e34-temporal-scene__toolbar">
<Button
variant="secondary"
size="compact"
icon={<Icon name="refresh" size={14} />}
onClick={resetView}
>
Сбросить ракурс
</Button>
<div className="e34-temporal-scene__gestures">
<span>ЛКМ · вращение</span>
<span>Колесо · масштаб</span>
<span>ПКМ · панорама</span>
</div>
</div>
<div className="e34-temporal-scene__legend">
<span data-state="cells">
Ячейки · {frame.cellCentersMapXyzM.length}
</span>
<span data-state="current">Наблюдается · {frame.counts.current}</span>
<span data-state="held">Удерживается · {frame.counts.held}</span>
<span data-state="expired">Истекло · {frame.counts.expired}</span>
</div>
</div>
);
}
@@ -29,9 +29,7 @@ import {
} from "../../core/laboratory/e30Review";
import {
fetchAdvancedLaboratoryResults,
type E31LaboratoryResult,
type E32LaboratoryResult,
type E33LaboratoryResult,
type AdvancedLaboratoryResults,
} from "../../core/laboratory/advancedResults";
import {
fetchLidarLocalSurfaces,
@@ -41,14 +39,17 @@ import { formatNumber } from "../../presentation";
import { E30ReviewWorkspace } from "../E30ReviewWorkspace";
import { LidarQualityWorkspace } from "../LidarQualityWorkspace";
import type { WorkspaceRendererProps } from "../contracts";
import { E31Result } from "./E31Result";
import { E32Result } from "./E32Result";
import { E33Result } from "./E33Result";
import {
AdvancedLaboratoryResult,
advancedLaboratorySourceSession,
advancedLaboratoryWorkOptions,
isAdvancedLaboratoryWorkId,
type AdvancedLaboratoryWorkId,
} from "./AdvancedLaboratoryResult";
import {
e28LaboratoryBrief, e29LaboratoryBrief,
e30LaboratoryBrief, PUBLISHED_LABORATORY_BRIEF,
} from "./laboratoryArchiveBriefs";
import { RecordedReplayEvidence } from "./RecordedReplayEvidence";
type LaboratoryWorkspaceProps = WorkspaceRendererProps & {
SpatialView: ComponentType<WorkspaceRendererProps>;
};
@@ -58,11 +59,16 @@ type LaboratoryWorkId =
| "e28-local-surface"
| "e29-camera-geometry"
| "e30-evidence-review"
| "e31-source-binding"
| "e32-track-geometry"
| "e33-worker-shadow"
| AdvancedLaboratoryWorkId
| `session:${string}`;
const EMPTY_ADVANCED_RESULTS: AdvancedLaboratoryResults = {
e31: null,
e32: null,
e33: null,
e34: null,
};
function digestFromContentId(value: string | null | undefined): string | null {
const digest = value?.split("-").at(-1) ?? "";
return /^[a-f0-9]{64}$/.test(digest) ? digest : null;
@@ -554,9 +560,9 @@ export function LaboratoryArchiveWorkspace(props: LaboratoryWorkspaceProps) {
const [e28Model, setE28Model] = useState<LidarLocalSurfaceModel | null>(null);
const [e29Result, setE29Result] = useState<E29EvidenceResult | null>(null);
const [e30Result, setE30Result] = useState<E30ReviewResult | null>(null);
const [e31Result, setE31Result] = useState<E31LaboratoryResult | null>(null);
const [e32Result, setE32Result] = useState<E32LaboratoryResult | null>(null);
const [e33Result, setE33Result] = useState<E33LaboratoryResult | null>(null);
const [advancedResults, setAdvancedResults] = useState(
EMPTY_ADVANCED_RESULTS,
);
const [evidenceLoading, setEvidenceLoading] = useState(true);
const [evidenceError, setEvidenceError] = useState<string | null>(null);
const sessions = useObservationSessions({
@@ -598,14 +604,12 @@ export function LaboratoryArchiveWorkspace(props: LaboratoryWorkspaceProps) {
setE28Model(nextE28);
setE29Result(nextE29);
setE30Result(nextE30);
setE31Result(nextAdvanced?.e31 ?? null);
setE32Result(nextAdvanced?.e32 ?? null);
setE33Result(nextAdvanced?.e33 ?? null);
setAdvancedResults(nextAdvanced ?? EMPTY_ADVANCED_RESULTS);
const failures = [
e28.status === "rejected" ? "E28" : null,
e29.status === "rejected" ? "E29" : null,
e30.status === "rejected" ? "E30" : null,
advanced.status === "rejected" ? "E31–E33" : null,
advanced.status === "rejected" ? "E31–E34" : null,
].filter(Boolean);
setEvidenceError(
failures.length
@@ -651,32 +655,16 @@ export function LaboratoryArchiveWorkspace(props: LaboratoryWorkspaceProps) {
label: "LAB E30 · evidence review A2",
});
}
if (e31Result && sourceSessions.has(e31Result.sourceSessionId)) {
items.push({
id: "e31-source-binding",
label: "LAB E31 · source binding",
});
}
if (e32Result && sourceSessions.has(e32Result.sourceSessionId)) {
items.push({
id: "e32-track-geometry",
label: "LAB E32 · TrackGeometry v1",
});
}
if (e33Result && sourceSessions.has(e33Result.sourceSessionId)) {
items.push({
id: "e33-worker-shadow",
label: "LAB E33 · worker shadow 1×",
});
}
items.push(...advancedLaboratoryWorkOptions(
advancedResults,
sourceSessions,
));
return items;
}, [
advancedResults,
e28Model,
e29Result,
e30Result,
e31Result,
e32Result,
e33Result,
sourceSessions,
]);
const profiles = useMemo(() => {
@@ -714,15 +702,6 @@ export function LaboratoryArchiveWorkspace(props: LaboratoryWorkspaceProps) {
const e30SourceSession = e30Result
? sourceSessions.get(e30Result.sourceSessionId) ?? null
: null;
const e31SourceSession = e31Result
? sourceSessions.get(e31Result.sourceSessionId) ?? null
: null;
const e32SourceSession = e32Result
? sourceSessions.get(e32Result.sourceSessionId) ?? null
: null;
const e33SourceSession = e33Result
? sourceSessions.get(e33Result.sourceSessionId) ?? null
: null;
useEffect(() => {
if (
@@ -786,13 +765,13 @@ export function LaboratoryArchiveWorkspace(props: LaboratoryWorkspaceProps) {
void sessions.replay(e30SourceSession.id);
return;
}
const advancedSession = next === "e31-source-binding"
? e31SourceSession
: next === "e32-track-geometry"
? e32SourceSession
: next === "e33-worker-shadow"
? e33SourceSession
: null;
const advancedSession = isAdvancedLaboratoryWorkId(next)
? advancedLaboratorySourceSession(
next,
advancedResults,
sourceSessions,
)
: null;
if (advancedSession) {
void sessions.replay(advancedSession.id);
}
@@ -940,44 +919,16 @@ export function LaboratoryArchiveWorkspace(props: LaboratoryWorkspaceProps) {
result={e30Result}
sourceSession={e30SourceSession}
/>
) : workId === "e31-source-binding" && e31Result && e31SourceSession ? (
<E31Result
) : isAdvancedLaboratoryWorkId(workId) ? (
<AdvancedLaboratoryResult
props={props}
rigLabel={rigLabel}
result={e31Result}
evidence={(
<RecordedReplayEvidence
props={props}
sourceSession={e31SourceSession}
loading={sessions.replayingSessionId === e31SourceSession.id}
error={sessions.failedSessionId === e31SourceSession.id ? sessions.error : null}
/>
)}
/>
) : workId === "e32-track-geometry" && e32Result && e32SourceSession ? (
<E32Result
rigLabel={rigLabel}
result={e32Result}
evidence={(
<RecordedReplayEvidence
props={props}
sourceSession={e32SourceSession}
loading={sessions.replayingSessionId === e32SourceSession.id}
error={sessions.failedSessionId === e32SourceSession.id ? sessions.error : null}
/>
)}
/>
) : workId === "e33-worker-shadow" && e33Result && e33SourceSession ? (
<E33Result
rigLabel={rigLabel}
result={e33Result}
evidence={(
<RecordedReplayEvidence
props={props}
sourceSession={e33SourceSession}
loading={sessions.replayingSessionId === e33SourceSession.id}
error={sessions.failedSessionId === e33SourceSession.id ? sessions.error : null}
/>
)}
workId={workId}
results={advancedResults}
sourceSessions={sourceSessions}
replayingSessionId={sessions.replayingSessionId}
failedSessionId={sessions.failedSessionId}
replayError={sessions.error}
/>
) : selectedSession ? (
<PublishedLaboratoryResult
@@ -123,6 +123,89 @@ function e33() {
};
}
function e34() {
return {
result_id: `e34-temporal-occupied-${"4".repeat(64)}`,
created_at_utc: "2026-07-27T13:28:30Z",
source_session_id: "20260720T065719Z_viewer_live",
status: "accepted-bounded-occupied-unknown-temporal-layer",
e32_result_id: e32().result_id,
e33_result_id: e33().result_id,
profile_id: "e34-short-ttl-hit-only-occupied-unknown/v1",
pipeline_id: "lidar-local-map/short-ttl-hit-only/v1",
coordinate_frame: "map",
configuration: {
voxel_size_m: 0.45,
occupied_ttl_seconds: 0.75,
maximum_active_components: 256,
maximum_cells_per_component: 4096,
geometry_maximum_gap_seconds: 0.35,
geometry_maximum_centroid_distance_m: 0.9,
},
metrics: {
source_frames: 4489,
processed_frames: 4489,
frames_with_current_layer: 3928,
held_only_frames: 540,
unknown_empty_frames: 21,
created_components: 4912,
camera_created_components: 1027,
geometry_created_components: 3885,
geometry_reassociated_components: 2366,
exact_camera_associations: 5168,
geometry_spatial_reassociations: 17433,
held_publications: 39664,
expired_components: 4895,
peak_active_components: 37,
maximum_observed_span_seconds: 13.394,
continuity_fraction: 0.821466,
geometry_only_reassociation_fraction: 0.81776,
e32_current_point_rows: 2119302,
e34_consumed_current_point_rows: 2119302,
current_cell_rows: 834478,
held_cell_rows: 531581,
stored_cell_rows: 1366059,
free_cell_rows: 0,
peak_cells_per_component: 324,
maximum_held_age_seconds: 0.75,
maximum_expiry_delay_seconds: 0,
maximum_replay_materialization_delay_seconds: 0.283,
map_frame_jump_candidates: 0,
frame_processing_p95_ms: 1.483,
build_elapsed_ms: 4317,
},
review: {
schema_version: "missioncore.e34-temporal-review-timeline/v1",
result_id: `e34-temporal-occupied-${"4".repeat(64)}`,
frames: [{
frame_index: 238,
session_seconds: 59.206,
source_available: false,
layer_state: "unknown",
counts: { current: 0, held: 0, expired: 1 },
cell_centers_map_xyz_m: [],
components: [{
temporal_id: 136,
state: "expired",
occupancy_state: "unknown",
owner_kind: "camera-track",
centroid_map_xyz_m: [20.25, -2.83, 0.21],
last_observed_age_seconds: 0.75,
association_reason: "independent-ttl-deadline",
history_tail: [{
frame_index: 230,
session_seconds: 58.456,
centroid_map_xyz_m: [20.25, -2.83, 0.21],
}],
}],
}],
},
acceptance: { accepted: true },
authority,
access: "read-only",
};
}
before(async () => {
server = await createServer({
appType: "custom",
@@ -139,9 +222,9 @@ after(async () => {
await server?.close();
});
test("decodes E31, E32 and E33 from separate read-only catalogs", async () => {
test("decodes E31–E34 from separate read-only catalogs", async () => {
const requests = [];
const items = [e31(), e32(), e33()];
const items = [e31(), e32(), e33(), e34()];
const decoded = await fetchAdvancedLaboratoryResults({
fetcher: async (input, init) => {
requests.push({ input: String(input), method: init?.method });
@@ -156,10 +239,13 @@ test("decodes E31, E32 and E33 from separate read-only catalogs", async () => {
assert.equal(decoded.e33.metrics.deliveredFrames, 4489);
assert.equal(decoded.e33.metrics.sourceAvailableFrames, 3928);
assert.equal(decoded.e33.metrics.resultAgeMaxMs, 13.638);
assert.equal(decoded.e34.metrics.processedFrames, 4489);
assert.equal(decoded.e34.reviewFrames[0].components[0].state, "expired");
assert.deepEqual(requests, [
{ input: "/api/v1/laboratory/e31/results?limit=1", method: "GET" },
{ input: "/api/v1/laboratory/e32/results?limit=1", method: "GET" },
{ input: "/api/v1/laboratory/e33/results?limit=1", method: "GET" },
{ input: "/api/v1/laboratory/e34/results?limit=1", method: "GET" },
]);
});
@@ -172,7 +258,8 @@ test("rejects authority escalation in an accepted-looking result", async () => {
() => fetchAdvancedLaboratoryResults({
fetcher: async (input) => new Response(JSON.stringify(catalog(
String(input).includes("/e31/") ? forged
: String(input).includes("/e32/") ? e32() : e33(),
: String(input).includes("/e32/") ? e32()
: String(input).includes("/e33/") ? e33() : e34(),
)), { status: 200 }),
}),
AdvancedLaboratoryContractError,
@@ -22,6 +22,14 @@ const e33ResultUrl = new URL(
"../src/workspaces/laboratory/E33Result.tsx",
import.meta.url,
);
const e34ResultUrl = new URL(
"../src/workspaces/laboratory/E34Result.tsx",
import.meta.url,
);
const advancedLaboratoryResultUrl = new URL(
"../src/workspaces/laboratory/AdvancedLaboratoryResult.tsx",
import.meta.url,
);
const workspacesUrl = new URL(
"../src/workspaces/Workspaces.tsx",
import.meta.url,
@@ -136,6 +144,23 @@ test("E33 explains the experiment, its method and its retained limits", async ()
assert.doesNotMatch(e33Source, /name: result\.e32ResultId/);
});
test("E34 keeps temporal evidence inside the canonical LAB and viewer contracts", async () => {
const [e34Source, advancedSource] = await Promise.all([
readFile(e34ResultUrl, "utf8"),
readFile(advancedLaboratoryResultUrl, "utf8"),
]);
assert.match(e34Source, /<LaboratoryWorkTemplate/);
assert.match(e34Source, /<LaboratoryEvidenceViewer/);
assert.match(e34Source, /\{ value: "3d", label: "3D" \}/);
assert.match(e34Source, /\{ value: "plan", label: "План" \}/);
assert.match(e34Source, /Первый immutable replay был отклонён/);
assert.match(e34Source, /Слой не вычисляет свободное пространство/);
assert.match(e34Source, /Следующий критический gate — E35/);
assert.match(advancedSource, /id: "e34-temporal-layer"/);
assert.match(advancedSource, /<E34Result/);
});
test("the primary point-cloud viewer restores from fullscreen on Escape", async () => {
const workspacesSource = await readFile(workspacesUrl, "utf8");