feat(lab): canonize versioned report template

This commit is contained in:
DCCONSTRUCTIONS
2026-07-27 16:05:32 +03:00
parent 2c016b117d
commit 95c6540691
20 changed files with 582 additions and 128 deletions
@@ -219,6 +219,16 @@ function SpatialWorkspace({
const pointCloudFocused = Boolean(
pointCloudSource && observationLayout.focusedSourceId === pointCloudSource.id,
);
useEffect(() => {
if (!pointCloudFocused) return;
const onKeyDown = (event: KeyboardEvent) => {
if (event.key !== "Escape") return;
event.preventDefault();
observationLayout.setFocusedSourceId(null);
};
window.addEventListener("keydown", onKeyDown);
return () => window.removeEventListener("keydown", onKeyDown);
}, [observationLayout.setFocusedSourceId, pointCloudFocused]);
const floatingSourceMaximized = !unifiedPerception &&
Boolean(observationLayout.maximizedFloatingSourceId);
const timeline = state?.observationTimeline;
@@ -1,8 +1,8 @@
import type { ReactNode } from "react";
import { StatusBadge } from "@nodedc/ui-react";
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
@@ -36,6 +36,12 @@ export function E31Result({
},
{ label: "Контур", value: "Source-scoped · read-only" },
]}
brief={{
question: "Можно ли однозначно связать camera, LiDAR и pose всей записи RAVNOVES00 без подбора соответствий вручную?",
approach: "Для каждого кадра проверены временная привязка, чувствительность к offset, доступность pose и self-mask на одном неизменяемом источнике.",
principalResult: `${formatNumber(metrics.availableBindingCount, 0)} из ${formatNumber(metrics.frameCount, 0)} кадров получили воспроизводимую source-scoped привязку.`,
limitation: "Время основано на host-arrival, профиль действителен только для этой записи и этого монтажа сенсоров.",
}}
method={{
completeness: "complete",
executionClass: "deterministic",
@@ -70,38 +76,38 @@ export function E31Result({
</LaboratoryEvidence>
)}
result={(
<section className="laboratory-result-summary">
<header>
<div>
<span className="section-eyebrow">РЕЗУЛЬТАТ И ВЫВОД</span>
<h2>Привязка воспроизводима в границах этого источника</h2>
</div>
<StatusBadge tone="success">Source profile принят</StatusBadge>
</header>
<div className="laboratory-result-metrics">
<div>
<span>Camera LiDAR p95</span>
<strong>{metrics.lidarCameraP95Ms.toLocaleString("ru-RU", { maximumFractionDigits: 2 })} мс</strong>
<small>host-arrival binding</small>
</div>
<div>
<span>Pose point p95</span>
<strong>{metrics.posePointP95Ms.toLocaleString("ru-RU", { maximumFractionDigits: 2 })} мс</strong>
<small>по доступным кадрам</small>
</div>
<div>
<span>Поддержка соответствий</span>
<strong>{(metrics.supportedFraction * 100).toLocaleString("ru-RU", { maximumFractionDigits: 1 })}%</strong>
<small>{formatNumber(metrics.correspondenceCount, 0)} соответствий</small>
</div>
<div>
<span>Self-mask</span>
<strong>{formatNumber(metrics.semanticSelfSampleCount, 0)} образцов</strong>
<small>{formatNumber(metrics.semanticSelfCollateralCount, 0)} collateral</small>
</div>
</div>
<p>Результат диагностический: он не переносится на другой монтаж сенсоров и не выдаёт командных полномочий.</p>
</section>
<LaboratoryResultSummary
title="Привязка воспроизводима в границах этого источника"
status="Source profile принят"
statusTone="success"
metrics={[
{
label: "Camera ↔ LiDAR p95",
value: `${metrics.lidarCameraP95Ms.toLocaleString("ru-RU", { maximumFractionDigits: 2 })} мс`,
hint: "host-arrival binding",
},
{
label: "Pose ↔ point p95",
value: `${metrics.posePointP95Ms.toLocaleString("ru-RU", { maximumFractionDigits: 2 })} мс`,
hint: "по доступным кадрам",
},
{
label: "Поддержка соответствий",
value: `${(metrics.supportedFraction * 100).toLocaleString("ru-RU", { maximumFractionDigits: 1 })}%`,
hint: `${formatNumber(metrics.correspondenceCount, 0)} соответствий`,
},
{
label: "Self-mask",
value: `${formatNumber(metrics.semanticSelfSampleCount, 0)} образцов`,
hint: `${formatNumber(metrics.semanticSelfCollateralCount, 0)} collateral`,
},
]}
conclusion={{
proved: "Camera, LiDAR и pose воспроизводимо связаны для неизменяемой записи RAVNOVES00 в заявленных границах source profile.",
notProved: "Не доказаны аппаратная синхронизация, переносимость профиля на другую запись или другой монтаж и пригодность результата для управления.",
decision: "Принять source-scoped профиль как диагностический вход E32; команды, навигация и safety остаются запрещены.",
}}
/>
)}
/>
);
@@ -1,8 +1,8 @@
import type { ReactNode } from "react";
import { StatusBadge } from "@nodedc/ui-react";
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
@@ -33,6 +33,12 @@ export function E32Result({
{ label: "Кадры", value: formatNumber(metrics.framesTotal, 0) },
{ label: "Контур", value: "Diagnostic replay · read-only" },
]}
brief={{
question: "Можно ли детерминированно превратить полную принятую запись E29/E31 в покадровый TrackGeometry без повторной настройки порогов?",
approach: "Для каждого кадра применены один source profile, единый арбитраж перекрывающихся наблюдений, self-mask и запрет публикации диапазонов без квалифицированного источника.",
principalResult: `TrackGeometry построен для ${formatNumber(metrics.framesTotal, 0)} кадров; опубликовано ${formatNumber(metrics.qualifiedPointsPublished, 0)} квалифицированных точек.`,
limitation: "Результат наследует качество входных наблюдений и не считает отсутствие LiDAR-точек доказательством свободного пространства.",
}}
method={{
completeness: "complete",
executionClass: "deterministic",
@@ -67,38 +73,38 @@ export function E32Result({
</LaboratoryEvidence>
)}
result={(
<section className="laboratory-result-summary">
<header>
<div>
<span className="section-eyebrow">РЕЗУЛЬТАТ И ВЫВОД</span>
<h2>TrackGeometry построен без повторной настройки порогов</h2>
</div>
<StatusBadge tone="success">Детерминированный replay</StatusBadge>
</header>
<div className="laboratory-result-metrics">
<div>
<span>Опубликовано точек</span>
<strong>{formatNumber(metrics.qualifiedPointsPublished, 0)}</strong>
<small>{formatNumber(metrics.qualifiedPointsWithheld, 0)} withheld</small>
</div>
<div>
<span>Арбитраж наблюдений</span>
<strong>{formatNumber(metrics.observationsArbitrated, 0)}</strong>
<small>{formatNumber(metrics.overlappingClaimsRemoved, 0)} overlap снято</small>
</div>
<div>
<span>Semantic</span>
<strong>{formatNumber(metrics.semanticPublished, 0)}</strong>
<small>{formatNumber(metrics.semanticMasked, 0)} self-mask</small>
</div>
<div>
<span>Frame processing p95</span>
<strong>{metrics.frameProcessingP95Ms.toLocaleString("ru-RU", { maximumFractionDigits: 3 })} мс</strong>
<small>{(metrics.buildElapsedMs / 1000).toLocaleString("ru-RU", { maximumFractionDigits: 2 })} с build</small>
</div>
</div>
<p>Отсутствие LiDAR-точек по-прежнему не трактуется как свободное пространство; результат не является навигационным ground truth.</p>
</section>
<LaboratoryResultSummary
title="TrackGeometry построен без повторной настройки порогов"
status="Детерминированный replay"
statusTone="success"
metrics={[
{
label: "Опубликовано точек",
value: formatNumber(metrics.qualifiedPointsPublished, 0),
hint: `${formatNumber(metrics.qualifiedPointsWithheld, 0)} withheld`,
},
{
label: "Арбитраж наблюдений",
value: formatNumber(metrics.observationsArbitrated, 0),
hint: `${formatNumber(metrics.overlappingClaimsRemoved, 0)} overlap снято`,
},
{
label: "Semantic",
value: formatNumber(metrics.semanticPublished, 0),
hint: `${formatNumber(metrics.semanticMasked, 0)} self-mask`,
},
{
label: "Frame processing p95",
value: `${metrics.frameProcessingP95Ms.toLocaleString("ru-RU", { maximumFractionDigits: 3 })} мс`,
hint: `${(metrics.buildElapsedMs / 1000).toLocaleString("ru-RU", { maximumFractionDigits: 2 })} с build`,
},
]}
conclusion={{
proved: "Принятый source profile воспроизводимо материализует покадровый TrackGeometry с единственным владельцем перекрывающихся LiDAR-точек.",
notProved: "Не доказаны полнота препятствий, свободное пространство при отсутствии точек и навигационная точность геометрии.",
decision: "Принять TrackGeometry как неизменяемый диагностический вход worker shadow; не использовать как navigation ground truth.",
}}
/>
)}
/>
);
@@ -1,8 +1,8 @@
import type { ReactNode } from "react";
import { StatusBadge } from "@nodedc/ui-react";
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
@@ -23,33 +23,52 @@ export function E33Result({
<LaboratoryWorkTemplate
summary={(
<LaboratorySummary
title="LAB E33 · worker shadow 1×"
description=олная запись TrackGeometry выполнена отдельным bounded worker в темпе исходника 1×. Проверены очереди, доставка каждого кадра, задержка результата и ресурсы без подключения командного канала."
status="Worker gate пройден"
title="LAB E33 · проверка TrackGeometry worker в реальном темпе"
description=роверяли не качество распознавания объектов, а способность отдельного worker принять уже готовый TrackGeometry и вернуть явный результат по каждому кадру без накопления задержки и скрытых потерь."
status="Принят как worker shadow"
statusTone="success"
facts={[
{ label: "Конфигурация", value: `${rigLabel} · worker shadow` },
{ label: "Worker", value: result.worker.node },
{ label: "Доставка", value: `${formatNumber(metrics.deliveredFrames, 0)} / ${formatNumber(metrics.sourceFrames, 0)} кадров` },
{ label: "Контур", value: "Recorded-source-paced · 1×" },
{ label: "Источник", value: `${rigLabel} · RAVNOVES00` },
{
label: "Объём проверки",
value: `${formatNumber(metrics.sourceFrames, 0)} кадров · ${metrics.sourceSpanSeconds.toLocaleString("ru-RU", { maximumFractionDigits: 1 })} с`,
},
{
label: "Геометрия доступна",
value: `${formatNumber(metrics.sourceAvailableFrames, 0)} / ${formatNumber(metrics.sourceFrames, 0)} кадров`,
},
{ label: "Полномочия", value: "Read-only · команды и safety выключены" },
]}
brief={{
question: "Сможет ли отдельный worker обработать всю запись в исходном темпе 1×, не теряя кадры и не раздувая очереди?",
approach: "Готовый TrackGeometry из E32 выпускался по исходным временным меткам через отдельные work/result очереди ёмкостью 2. Для каждого кадра фиксировался один терминальный исход, задержка и использование памяти.",
principalResult: `Да. Доставлены все ${formatNumber(metrics.deliveredFrames, 0)} исходов, замен и deadline miss не было; p95 возраста результата — ${metrics.resultAgeP95Ms.toLocaleString("ru-RU", { maximumFractionDigits: 3 })} мс.`,
limitation: `Детектор, semantic-модели и качество геометрии здесь не пересчитывались. В ${formatNumber(metrics.sourceUnavailableFrames, 0)} кадрах источник уже не содержал квалифицированной геометрии.`,
}}
method={{
completeness: "complete",
executionClass: "deterministic",
pipelineId: result.pipelineId,
pipelineId: "TrackGeometry → recorded-paced bounded worker",
components: [
{
kind: "source",
name: result.e32ResultId,
version: "accepted TrackGeometry replay",
role: "immutable frame stream",
identitySha256: result.e32ResultId.split("-").at(-1) ?? null,
name: "Принятый TrackGeometry из LAB E32",
version: `${formatNumber(metrics.sourceFrames, 0)} кадров · неизменяемая запись`,
role: "готовый вход; detector и semantic inference не запускались повторно",
identitySha256: null,
},
{
kind: "algorithm",
name: "Recorded pacing + bounded latest-wins queues",
version: `1× · очереди ${metrics.workQueueCapacity}/${metrics.resultQueueCapacity}`,
role: "выпуск по исходному времени, закрытый учёт исходов и deadline-контроль",
identitySha256: null,
},
{
kind: "runtime",
name: result.worker.node,
version: `${result.worker.python} · NumPy ${result.worker.numpy}`,
role: "bounded worker, work/result queues 2/2",
name: "Изолированный worker",
version: `${result.worker.node} · Python ${result.worker.python} · NumPy ${result.worker.numpy}`,
role: "проверка структуры TrackGeometry, SHA-256 диапазонов PointSlab и публикация результата",
identitySha256: null,
},
],
@@ -59,7 +78,7 @@ export function E33Result({
evidence={(
<LaboratoryEvidence
eyebrow="ИСХОДНОЕ ДОКАЗАТЕЛЬСТВО"
title="Запись, доставленная worker без потерь"
title="Контрольная запись: кадры и TrackGeometry, которые доставлял worker"
kind="recorded-replay"
resizable
>
@@ -67,40 +86,38 @@ export function E33Result({
</LaboratoryEvidence>
)}
result={(
<section className="laboratory-result-summary">
<header>
<div>
<span className="section-eyebrow">РЕЗУЛЬТАТ И ВЫВОД</span>
<h2>Полный replay выдержан в темпе источника</h2>
</div>
<StatusBadge tone="success">
{formatNumber(metrics.deliveredFrames, 0)} / {formatNumber(metrics.sourceFrames, 0)}
</StatusBadge>
</header>
<div className="laboratory-result-metrics">
<div>
<span>Эффективная частота</span>
<strong>{metrics.effectiveDeliveryFps.toLocaleString("ru-RU", { maximumFractionDigits: 3 })} FPS</strong>
<small>скорость источника 1×</small>
</div>
<div>
<span>Result age p95</span>
<strong>{metrics.resultAgeP95Ms.toLocaleString("ru-RU", { maximumFractionDigits: 3 })} мс</strong>
<small>{(metrics.deadlineMissFraction * 100).toLocaleString("ru-RU", { maximumFractionDigits: 2 })}% deadline miss</small>
</div>
<div>
<span>Processing p95</span>
<strong>{metrics.processingP95Ms.toLocaleString("ru-RU", { maximumFractionDigits: 3 })} мс</strong>
<small>{metrics.releaseLagP95Ms.toLocaleString("ru-RU", { maximumFractionDigits: 3 })} мс release lag</small>
</div>
<div>
<span>Ресурсы p95</span>
<strong>{metrics.processRssP95Mib.toLocaleString("ru-RU", { maximumFractionDigits: 1 })} MiB</strong>
<small>GPU {metrics.gpuUtilizationP95Percent.toLocaleString("ru-RU", { maximumFractionDigits: 1 })}%</small>
</div>
</div>
<p>Очереди остались bounded, пропусков нет. Результат подтверждает только worker shadow и не даёт навигационных или safety-полномочий.</p>
</section>
<LaboratoryResultSummary
title="Worker выдержал запись; качество восприятия этим прогоном не проверялось"
status="Worker shadow принят"
statusTone="success"
metrics={[
{
label: "Доставка исходов",
value: `${formatNumber(metrics.deliveredFrames, 0)} / ${formatNumber(metrics.sourceFrames, 0)}`,
hint: "0 замен · 0 пропусков",
},
{
label: "Темп обработки",
value: `${metrics.effectiveDeliveryFps.toLocaleString("ru-RU", { maximumFractionDigits: 3 })} FPS`,
hint: "исходная скорость 10 FPS · 1×",
},
{
label: "Возраст результата",
value: `${metrics.resultAgeP95Ms.toLocaleString("ru-RU", { maximumFractionDigits: 3 })} мс p95`,
hint: `${metrics.resultAgeMaxMs.toLocaleString("ru-RU", { maximumFractionDigits: 3 })} мс max · лимит ${formatNumber(metrics.resultDeadlineMs, 0)} мс`,
},
{
label: "Память процесса",
value: `${metrics.processRssMaxMib.toLocaleString("ru-RU", { maximumFractionDigits: 1 })} MiB max`,
hint: `лимит ${formatNumber(metrics.processRssLimitMib, 0)} MiB`,
},
]}
conclusion={{
proved: "Финальная стадия публикации TrackGeometry на этом worker успевает за данной записью: очереди ограничены, каждый кадр учтён, задержка остаётся ниже заявленного deadline.",
notProved: "Прогон не измеряет точность машин, кубов или сегментации, не запускает заново detector/semantic pipeline и не доказывает переносимость на другую запись или монтаж сенсоров.",
decision: "Результат принимается только как диагностический worker shadow. Он допускает дальнейшие лабораторные испытания временного occupied/unknown слоя, но не включает команды, навигацию или safety.",
}}
/>
)}
/>
);
@@ -44,8 +44,11 @@ import type { WorkspaceRendererProps } from "../contracts";
import { E31Result } from "./E31Result";
import { E32Result } from "./E32Result";
import { E33Result } from "./E33Result";
import {
e28LaboratoryBrief, e29LaboratoryBrief,
e30LaboratoryBrief, PUBLISHED_LABORATORY_BRIEF,
} from "./laboratoryArchiveBriefs";
import { RecordedReplayEvidence } from "./RecordedReplayEvidence";
type LaboratoryWorkspaceProps = WorkspaceRendererProps & {
SpatialView: ComponentType<WorkspaceRendererProps>;
};
@@ -227,6 +230,7 @@ function E29LaboratoryResult({
value: "Read-only · hash verified",
},
]}
brief={e29LaboratoryBrief(formatNumber(semantic.total, 0))}
method={{
completeness: "complete",
executionClass: "hybrid",
@@ -411,6 +415,7 @@ function E30LaboratoryResult({
{ label: "Кейсов", value: formatNumber(result.itemCount, 0) },
{ label: "Контур", value: "Read-only · без LAB publish" },
]}
brief={e30LaboratoryBrief(formatNumber(result.itemCount, 0))}
/>
)}
evidence={(
@@ -483,6 +488,7 @@ function PublishedLaboratoryResult({
{ label: "Конфигурация", value: lab?.configSha256?.slice(0, 16) ?? "Не зафиксирована" },
{ label: "Контур", value: "Диагностика · без команд" },
]}
brief={PUBLISHED_LABORATORY_BRIEF}
method={publishedLaboratoryMethod(session)}
/>
)}
@@ -867,6 +873,10 @@ export function LaboratoryArchiveWorkspace(props: LaboratoryWorkspaceProps) {
{ label: "Режим", value: "Recorded-source-paced shadow" },
{ label: "Контур", value: "Read-only · hash verified" },
]}
brief={e28LaboratoryBrief(
formatNumber(e28Model?.metrics.frames.valid ?? 0, 0),
formatNumber(e28Model?.metrics.frames.total ?? 0, 0),
)}
method={{
completeness: "complete",
executionClass: e28Model?.method.executionClass ?? "deterministic",
@@ -0,0 +1,44 @@
import type {
LaboratoryBrief,
} from "../../components/laboratory/LaboratoryPresentation";
export function e28LaboratoryBrief(
validFrames: string,
totalFrames: string,
): LaboratoryBrief {
return {
question: "Можно ли устойчиво оценивать локальную поверхность и временные остатки на полной записи без изменения источника?",
approach: "LiDAR и pose воспроизводятся через bounded recorded-source-paced shadow; rolling local plane оценивает поверхность, препятствия и temporal residuals.",
principalResult: `${validFrames} из ${totalFrames} кадров прошли диагностическую обработку L2.6.`,
limitation: "Модель проверена только на RAVNOVES00 и не получает командных, навигационных или safety-полномочий.",
};
}
export function e29LaboratoryBrief(
observationCount: string,
): LaboratoryBrief {
return {
question: "Совпадают ли camera-семантика объекта и независимая LiDAR-геометрия в одном точном кадре?",
approach: "Camera сохраняет класс и идентичность, а локальная поверхность L2.6 отдельно проверяет дальность, occupied support и конфликт принадлежности точек.",
principalResult: `${observationCount} наблюдений разложены по согласованию camera и geometry без объявления пустого LiDAR свободным пространством.`,
limitation: "Результат зависит от точности исходной camera-семантики и локальной поверхности; он остаётся диагностическим и read-only.",
};
}
export function e30LaboratoryBrief(
itemCount: string,
): LaboratoryBrief {
return {
question: "Можно ли человеку проверить спорный camera/LiDAR-кейс по понятному совмещённому доказательству, а не по изолированному облаку точек?",
approach: "Каждый кейс связан с точным camera frame, переключаемой LiDAR-проекцией, frame-local индексами и синхронным 3D в одном viewer.",
principalResult: `${itemCount} кейсов доступны для camera-first проверки и отдельного разбора только неоднозначных исключений.`,
limitation: "Рабочее место не меняет исходный A2 и не превращает решение оператора в navigation или safety ground truth.",
};
}
export const PUBLISHED_LABORATORY_BRIEF: LaboratoryBrief = {
question: "Что было зафиксировано опубликованной лабораторной работой на её неизменяемом источнике?",
approach: "Mission Core открывает связанный source replay и проецирует только опубликованные method/provenance и метрики без реконструкции отсутствующих данных.",
principalResult: "Оператор получает исходное визуальное доказательство и зафиксированный результат одного LAB run в общей продуктовой форме.",
limitation: "Для legacy-результатов часть идентичностей и метрик могла не публиковаться; приложение их не выдумывает задним числом.",
};