feat(lab): separate evidence catalogs and record methods

This commit is contained in:
DCCONSTRUCTIONS
2026-07-26 17:56:23 +03:00
parent e9ff29297b
commit 4db00b53f7
17 changed files with 670 additions and 24 deletions
@@ -1204,6 +1204,117 @@ interface LaboratoryOption<T extends string> {
label: string;
}
type LaboratoryExecutionClass = "deterministic" | "ai-inference" | "hybrid";
type LaboratoryMethodCompleteness = "complete" | "legacy-partial";
type LaboratoryEvidenceKind = "recorded-replay" | "diagnostic-model";
interface LaboratoryMethodComponent {
kind: "source" | "tool" | "model" | "algorithm" | "runtime";
name: string;
version: string;
role: string;
identitySha256: string | null;
}
interface LaboratoryMethod {
completeness: LaboratoryMethodCompleteness;
executionClass: LaboratoryExecutionClass;
pipelineId: string;
components: readonly LaboratoryMethodComponent[];
}
function digestFromContentId(value: string | null | undefined): string | null {
const digest = value?.split("-").at(-1) ?? "";
return /^[a-f0-9]{64}$/.test(digest) ? digest : null;
}
function publishedLaboratoryMethod(
session: ObservationSessionSummary,
): LaboratoryMethod {
const method = session.lab?.provenance.method;
if (method && typeof method === "object" && !Array.isArray(method)) {
const value = method as Record<string, unknown>;
const rawComponents = Array.isArray(value.components) ? value.components : [];
const components: LaboratoryMethodComponent[] = rawComponents.flatMap((component) => {
if (!component || typeof component !== "object" || Array.isArray(component)) return [];
const item = component as Record<string, unknown>;
const kind = item.kind;
if (
kind !== "source"
&& kind !== "tool"
&& kind !== "model"
&& kind !== "algorithm"
&& kind !== "runtime"
) return [];
if (
typeof item.name !== "string"
|| typeof item.version !== "string"
|| typeof item.role !== "string"
) return [];
return [{
kind: kind as LaboratoryMethodComponent["kind"],
name: item.name,
version: item.version,
role: item.role,
identitySha256: typeof item.identity_sha256 === "string"
? item.identity_sha256
: null,
}];
});
const executionClass = value.execution_class;
const completeness = value.completeness;
if (
components.length
&& typeof value.pipeline_id === "string"
&& (
executionClass === "deterministic"
|| executionClass === "ai-inference"
|| executionClass === "hybrid"
)
&& (completeness === "complete" || completeness === "legacy-partial")
) {
return {
completeness,
executionClass,
pipelineId: value.pipeline_id,
components,
};
}
}
const resultKind = session.lab?.resultKind ?? "unknown";
const algorithmNames: Record<string, string> = {
"e10-integrated-perception": "Camera semantics + LiDAR metric fusion",
"e21-realtime-envelope": "Bounded real-time perception replay",
"e22-temporal-stability": "Temporal 2D/3D/semantic stabilization",
"e23-inline-temporal-stability": "Inline warm-worker stabilization",
"e24-world-motion": "World-frame motion tracking",
"e25-persistent-support-motion": "Persistent occupied-support tracking",
"e26-camera-ego-motion-fusion": "KB4 ego-motion + persistent LiDAR support",
};
return {
completeness: "legacy-partial",
executionClass: "hybrid",
pipelineId: resultKind,
components: [
{
kind: "source",
name: session.lab?.sourceResultId ?? session.lab?.sourceSessionId ?? session.id,
version: "immutable source evidence",
role: "read-only input",
identitySha256: digestFromContentId(session.lab?.sourceResultId),
},
{
kind: "algorithm",
name: algorithmNames[resultKind] ?? resultKind,
version: resultKind,
role: "laboratory derivative",
identitySha256: session.lab?.configSha256 ?? null,
},
],
};
}
function LaboratorySelector<T extends string>({
eyebrow,
title,
@@ -1285,17 +1396,20 @@ function LaboratoryTask({
function LaboratoryEvidence({
eyebrow,
title,
kind,
resizable = false,
children,
}: {
eyebrow: string;
title: string;
kind: LaboratoryEvidenceKind;
resizable?: boolean;
children: ReactNode;
}) {
return (
<section
className="lab-result-surface"
data-evidence-kind={kind}
data-resizable={resizable ? "true" : undefined}
>
<header>
@@ -1309,13 +1423,69 @@ function LaboratoryEvidence({
);
}
function LaboratoryMethodCard({ method }: { method: LaboratoryMethod }) {
const complete = method.completeness === "complete";
const executionLabels: Record<LaboratoryExecutionClass, string> = {
deterministic: "Детерминированный",
"ai-inference": "AI inference",
hybrid: "Гибридный",
};
return (
<section className="laboratory-method">
<header>
<div>
<span className="section-eyebrow">МЕТОД И ВОСПРОИЗВОДИМОСТЬ</span>
<h2>{method.pipelineId}</h2>
<p>
Зафиксированы вычислительный класс, инструменты, модели и алгоритмы.
{complete
? " Идентичности достаточны для повторного запуска."
: " Это legacy-прогон: отсутствующие исторические версии не восстановлены задним числом."}
</p>
</div>
<StatusBadge tone={complete ? "success" : "warning"}>
{complete ? "Метод полный" : "Legacy · частично"}
</StatusBadge>
</header>
<div className="laboratory-method__summary">
<div>
<span>Класс вычисления</span>
<strong>{executionLabels[method.executionClass]}</strong>
</div>
<div>
<span>Компонентов</span>
<strong>{method.components.length}</strong>
</div>
</div>
<ul>
{method.components.map((component, index) => (
<li key={`${component.kind}:${component.name}:${index}`}>
<span>{component.kind}</span>
<div>
<strong>{component.name}</strong>
<small>{component.role} · {component.version}</small>
</div>
<code>
{component.identitySha256
? component.identitySha256.slice(0, 12)
: "identity не зафиксирована"}
</code>
</li>
))}
</ul>
</section>
);
}
function LaboratoryWorkTemplate({
task,
method,
evidence,
result = null,
details = null,
}: {
task: ReactNode;
method: ReactNode;
evidence: ReactNode;
result?: ReactNode;
details?: ReactNode;
@@ -1323,6 +1493,7 @@ function LaboratoryWorkTemplate({
return (
<div className="laboratory-work-template">
{task}
{method}
{evidence}
{result}
{details}
@@ -1406,10 +1577,45 @@ function E29LaboratoryResult({
]}
/>
)}
method={(
<LaboratoryMethodCard
method={{
completeness: "complete",
executionClass: "hybrid",
pipelineId: result.identity.profileId,
components: [
{
kind: "source",
name: result.linkedEvidence.sourceResultId,
version: "camera-first semantic observations",
role: "semantic identity and class",
identitySha256: digestFromContentId(result.linkedEvidence.sourceResultId),
},
{
kind: "algorithm",
name: "Camera/LiDAR local-surface validation",
version: result.identity.profileId,
role: "range, occupied support and conflict classification",
identitySha256: result.identity.producerSha256,
},
{
kind: "model",
name: result.linkedEvidence.localSurfaceModelId,
version: "L2.6 local surface",
role: "independent metric geometry",
identitySha256: digestFromContentId(
result.linkedEvidence.localSurfaceModelId,
),
},
],
}}
/>
)}
evidence={(
<LaboratoryEvidence
eyebrow="ИСХОДНЫЕ ДАННЫЕ"
title="LiDAR, траектория и камера RAVNOVES00"
kind="recorded-replay"
resizable
>
{replayReady ? (
@@ -1592,10 +1798,14 @@ function PublishedLaboratoryResult({
]}
/>
)}
method={(
<LaboratoryMethodCard method={publishedLaboratoryMethod(session)} />
)}
evidence={(
<LaboratoryEvidence
eyebrow="ВИЗУАЛЬНОЕ ДОКАЗАТЕЛЬСТВО"
title="Исходная запись выбранной лабораторной работы"
kind="recorded-replay"
resizable
>
{replayReady ? (
@@ -1900,10 +2110,43 @@ function LabArchiveWorkspace(props: WorkspaceRendererProps) {
]}
/>
)}
method={(
<LaboratoryMethodCard
method={{
completeness: "complete",
executionClass: e28Model?.method.executionClass ?? "deterministic",
pipelineId: e28Model?.method.pipelineId ?? "local-surface/unavailable",
components: [
{
kind: "source",
name: e28Model?.sourcePackId ?? "Источник не загружен",
version: "immutable vendor MAP + pose",
role: "read-only LiDAR evidence",
identitySha256: digestFromContentId(e28Model?.sourcePackId),
},
{
kind: "algorithm",
name: e28Model?.method.algorithm ?? "Rolling local surface",
version: e28Model?.method.pipelineId ?? "—",
role: "robust local plane, occupancy and temporal residuals",
identitySha256: e28Model?.method.producerSha256 ?? null,
},
{
kind: "runtime",
name: "Mission Core worker D",
version: "recorded-source-paced shadow",
role: "bounded passive replay",
identitySha256: null,
},
],
}}
/>
)}
evidence={(
<LaboratoryEvidence
eyebrow="ВИЗУАЛЬНОЕ ДОКАЗАТЕЛЬСТВО"
title="Диагностическая поверхность и кадры LAB E28"
kind="diagnostic-model"
>
<LidarQualityWorkspace
embedded