feat(lab): present chronological perception evidence

This commit is contained in:
DCCONSTRUCTIONS
2026-08-05 07:49:49 +03:00
parent 12c8a2d74c
commit 7a72a206f1
102 changed files with 20122 additions and 222 deletions
+4 -2
View File
@@ -72,8 +72,8 @@ import {
} from "./sceneSettings";
import { DeviceWorkspace } from "./workspaces/DeviceWorkspace";
import { WorkspaceRenderer } from "./workspaces/Workspaces";
import { useLaboratoryAnnotationHeader } from "./components/laboratory/useLaboratoryAnnotationHeader";
import "./styles/scene-windows.css";
type SceneToolWindowId = "sources" | "display" | "layers";
const viewerSettingsQuietPeriodMs = 750;
@@ -179,6 +179,7 @@ export default function App() {
const [layerInspectorOpen, setLayerInspectorOpen] = useState(false);
const [sceneWindowOrder, setSceneWindowOrder] = useState<SceneToolWindowId[]>([]);
const [layoutSaveNotice, setLayoutSaveNotice] = useState<string | null>(null);
const laboratoryAnnotation = useLaboratoryAnnotationHeader();
const [sceneSettings, setSceneSettings] = useState<SceneSettings>(defaultSceneSettings);
const [displayDraft, setDisplayDraft] = useState<SceneSettings>(defaultSceneSettings);
const [livePerceptionLayers, setLivePerceptionLayers] = useState<LivePerceptionLayers>(
@@ -811,7 +812,7 @@ export default function App() {
) : activeDefinition.kind === "datasets" ? (
<StatusBadge tone="neutral">Offline evaluation</StatusBadge>
) : activeDefinition.kind === "lab-archive" ? (
null
laboratoryAnnotation.control
) : activeDefinition.root === "system" ? (
<SystemWorkspaceSelector
value={activeDefinition.id}
@@ -861,6 +862,7 @@ export default function App() {
settleRecordedReplaySwitch(outcome),
onDeleteBegin: releaseRecordedReplayForDelete,
}}
onLaboratoryAnnotationActionChange={laboratoryAnnotation.setAction}
navigation={{
openView,
openSource,
@@ -234,21 +234,27 @@ async function mountRecordedEpochStream(
export function RecordedFmp4Player({
source,
playback,
interactive = false,
prepare = true,
sessionGate = "ready",
admissionKey = null,
onAdmissionChange,
onPlaybackChange,
}: {
source: ObservationSourceDescriptor;
playback?: RecordedObservationPlayback | null;
interactive?: boolean;
prepare?: boolean;
sessionGate?: RecordedAdmissionPhase;
admissionKey?: string | null;
onAdmissionChange?: (state: RecordedCameraAdmissionState) => void;
onPlaybackChange?: (playback: RecordedObservationPlayback) => void;
}) {
const videoRef = useRef<HTMLVideoElement>(null);
const onAdmissionChangeRef = useRef(onAdmissionChange);
onAdmissionChangeRef.current = onAdmissionChange;
const onPlaybackChangeRef = useRef(onPlaybackChange);
onPlaybackChangeRef.current = onPlaybackChange;
const workerRef = useRef<{ admissionKey: string | null; generation: number } | null>(null);
if (!workerRef.current || workerRef.current.admissionKey !== admissionKey) {
recordedMediaWorkerGeneration += 1;
@@ -461,15 +467,49 @@ export function RecordedFmp4Player({
}
}, [archive?.byteLength, bufferRevision, currentSeconds, epoch, playback?.playing, visualState]);
useEffect(() => {
const video = videoRef.current;
if (!interactive || !video || !epoch || visualState !== "ready") return;
let videoFrameRequest: number | null = null;
const emitPlayback = () => {
onPlaybackChangeRef.current?.({
currentSeconds: epoch.timelineStartSeconds + video.currentTime,
playing: !video.paused && !video.ended,
});
};
const scheduleVideoFrame = () => {
if (typeof video.requestVideoFrameCallback !== "function") return;
videoFrameRequest = video.requestVideoFrameCallback(() => {
emitPlayback();
scheduleVideoFrame();
});
};
const events = ["play", "pause", "seeking", "seeked", "timeupdate", "ended"] as const;
for (const event of events) video.addEventListener(event, emitPlayback);
scheduleVideoFrame();
emitPlayback();
return () => {
for (const event of events) video.removeEventListener(event, emitPlayback);
if (
videoFrameRequest !== null &&
typeof video.cancelVideoFrameCallback === "function"
) {
video.cancelVideoFrameCallback(videoFrameRequest);
}
};
}, [epoch, interactive, visualState]);
return (
<div
className="recorded-media-player"
data-state={visualState}
data-interactive={interactive ? "true" : undefined}
aria-busy={visualState === "loading"}
>
<video
ref={videoRef}
className="observation-media__asset"
controls={interactive}
muted
playsInline
preload="auto"
@@ -15,7 +15,10 @@ export interface LaboratoryEvidenceViewerMode<T extends string> {
label: string;
}
export function LaboratoryEvidenceViewer<T extends string>({
export function LaboratoryEvidenceViewer<
T extends string,
U extends string = string,
>({
label,
mode,
modes,
@@ -23,6 +26,7 @@ export function LaboratoryEvidenceViewer<T extends string>({
onModeChange,
onExpandedChange,
actions,
secondaryMode,
overlay,
children,
}: {
@@ -33,6 +37,12 @@ export function LaboratoryEvidenceViewer<T extends string>({
onModeChange: (mode: T) => void;
onExpandedChange: (expanded: boolean) => void;
actions?: ReactNode;
secondaryMode?: {
value: U;
modes: readonly LaboratoryEvidenceViewerMode<U>[];
label: string;
onChange: (mode: U) => void;
};
overlay?: ReactNode;
children: ReactNode;
}) {
@@ -62,6 +72,14 @@ export function LaboratoryEvidenceViewer<T extends string>({
{overlay}
<div className="laboratory-evidence-viewer__controls">
{actions}
{secondaryMode ? (
<SegmentedControl
value={secondaryMode.value}
items={[...secondaryMode.modes]}
label={secondaryMode.label}
onChange={secondaryMode.onChange}
/>
) : null}
<SegmentedControl
value={mode}
items={[...modes]}
@@ -75,6 +75,7 @@ export function LaboratorySelector<T extends string>({
value,
options,
disabled = false,
searchable = false,
onChange,
}: {
eyebrow: string;
@@ -84,6 +85,7 @@ export function LaboratorySelector<T extends string>({
value: T;
options: readonly LaboratoryOption<T>[];
disabled?: boolean;
searchable?: boolean;
onChange: (value: T) => void;
}) {
return (
@@ -105,6 +107,8 @@ export function LaboratorySelector<T extends string>({
variant="split"
menuWidth="anchor"
disabled={disabled}
searchable={searchable}
searchPlaceholder={`Найти: ${label.toLocaleLowerCase("ru-RU")}`}
onChange={(next) => onChange(next)}
/>
</div>
@@ -0,0 +1,26 @@
import { useState, type ReactNode } from "react";
import { Button, Icon } from "@nodedc/ui-react";
import type { LaboratoryAnnotationAction } from "../../workspaces/contracts";
export function useLaboratoryAnnotationHeader(): {
control: ReactNode;
setAction: (action: LaboratoryAnnotationAction | null) => void;
} {
const [action, setAction] = useState<LaboratoryAnnotationAction | null>(null);
return {
setAction,
control: action ? (
<Button
size="compact"
shape="pill"
variant="accent"
icon={<Icon name="edit" size={16} />}
disabled={action.disabled}
onClick={action.onClick}
>
{action.label}
</Button>
) : null,
};
}
@@ -15,10 +15,32 @@ import { fetchE35DegradationRecoveryResult } from "./e35DegradationRecovery";
import { fetchE40ProductGateResult } from "./e40ProductGate";
import { fetchL3PointPillarsVisualAudit } from "./l3PointPillarsVisualAudit";
import { fetchL31PointPillarsRavnoves } from "./l31PointPillarsRavnoves";
import { fetchL32PointPillarsCameraReview } from "./l32PointPillarsCameraReview";
import { fetchL33CameraFirstDetectorReview } from "./l33CameraFirstDetectorReview";
import { fetchL34RightYoloxTruthIsland } from "./l34RightYoloxTruthIsland";
import { fetchL34AAssistedYoloxErrorAudit } from "./l34aAssistedYoloxErrorAudit";
import { fetchL34BNestedBoxConsolidation } from "./l34bNestedBoxConsolidation";
import { fetchL34CTileSeamStitch } from "./l34cTileSeamStitch";
import { fetchL34DCumulativePostprocessing } from "./l34dCumulativePostprocessing";
import { fetchL34ESelfReviewDiagnostic } from "./l34eSelfReviewDiagnostic";
import { fetchL34FFrozenResult } from "./l34fAdjudication";
import { fetchE46BlindReviewResult } from "./e46BlindReview";
import { fetchE46AAiEngineeringPreannotation } from "./e46aAiEngineeringPreannotation";
import { fetchE46BTemporalMotion } from "./e46bTemporalMotion";
import { fetchE46CFullReplayWorldTracks } from "./e46cFullReplayWorldTracks";
import { fetchE46DTemporalFailureAudit } from "./e46dTemporalFailureAudit";
import { fetchE46EReadyStack } from "./e46eReadyStack";
import { fetchE46FDashCamBakeoff } from "./e46fDashCamBakeoff";
import { fetchE46GRectifiedDetectorBakeoff } from "./e46gRectifiedDetectorBakeoff";
import { fetchE46HFullRectifiedFrontReplay } from "./e46hFullRectifiedFrontReplay";
import { fetchE46IGroundingDinoFullReplay } from "./e46iGroundingDinoFullReplay";
import { fetchE46JRawFisheyeRealtime } from "./e46jRawFisheyeRealtime";
export type AdvancedLaboratoryWorkId =
| "l3-pointpillars-visual-audit"
| "l31-pointpillars-ravnoves"
| "l32-pointpillars-camera-review"
| "l33-camera-first-detector-review"
| "e31-source-binding"
| "e32-track-geometry"
| "e33-worker-shadow"
@@ -27,7 +49,25 @@ export type AdvancedLaboratoryWorkId =
| "e37-ravnoves-acceptance"
| "e38-perception-baseline"
| "e39-perception-refinement"
| "e40-perception-product-gate";
| "e40-perception-product-gate"
| "e46-detector-truth-island"
| "e46a-ai-engineering-preannotation"
| "e46b-temporal-motion"
| "e46c-full-replay-world-tracks"
| "e46d-temporal-failure-audit"
| "e46e-ready-stack"
| "e46f-dashcam-bakeoff"
| "e46g-rectified-detector-bakeoff"
| "e46h-full-rectified-front-replay"
| "e46i-grounding-dino-full-replay"
| "e46j-raw-fisheye-realtime"
| "l34-right-yolox-truth-island-freeze"
| "l34a-assisted-yolox-error-audit"
| "l34b-nested-box-consolidation-shadow"
| "l34c-tile-seam-stitch-shadow"
| "l34d-cumulative-postprocessing-candidate"
| "l34e-self-review-diagnostic"
| "l34f-adjudicated-reference";
export interface AdvancedLaboratoryIndexItem {
workId: AdvancedLaboratoryWorkId;
@@ -38,6 +78,8 @@ export interface AdvancedLaboratoryIndexItem {
const WORK_IDS: readonly AdvancedLaboratoryWorkId[] = [
"l3-pointpillars-visual-audit",
"l31-pointpillars-ravnoves",
"l32-pointpillars-camera-review",
"l33-camera-first-detector-review",
"e31-source-binding",
"e32-track-geometry",
"e33-worker-shadow",
@@ -47,11 +89,31 @@ const WORK_IDS: readonly AdvancedLaboratoryWorkId[] = [
"e38-perception-baseline",
"e39-perception-refinement",
"e40-perception-product-gate",
"e46-detector-truth-island",
"e46a-ai-engineering-preannotation",
"e46b-temporal-motion",
"e46c-full-replay-world-tracks",
"e46d-temporal-failure-audit",
"e46e-ready-stack",
"e46f-dashcam-bakeoff",
"e46g-rectified-detector-bakeoff",
"e46h-full-rectified-front-replay",
"e46i-grounding-dino-full-replay",
"e46j-raw-fisheye-realtime",
"l34-right-yolox-truth-island-freeze",
"l34a-assisted-yolox-error-audit",
"l34b-nested-box-consolidation-shadow",
"l34c-tile-seam-stitch-shadow",
"l34d-cumulative-postprocessing-candidate",
"l34e-self-review-diagnostic",
"l34f-adjudicated-reference",
];
const RESULT_PREFIX: Readonly<Record<AdvancedLaboratoryWorkId, string>> = {
"l3-pointpillars-visual-audit": "l3-pointpillars-visual-audit",
"l31-pointpillars-ravnoves": "l31-pointpillars-ravnoves",
"l32-pointpillars-camera-review": "l32-pointpillars-camera-review",
"l33-camera-first-detector-review": "l33-camera-first-detector-review",
"e31-source-binding": "e31-source-qualification",
"e32-track-geometry": "e32-track-geometry",
"e33-worker-shadow": "e33-worker-shadow",
@@ -61,6 +123,24 @@ const RESULT_PREFIX: Readonly<Record<AdvancedLaboratoryWorkId, string>> = {
"e38-perception-baseline": "e38-perception-baseline",
"e39-perception-refinement": "e39-perception-refinement",
"e40-perception-product-gate": "e40-perception-product-gate",
"e46-detector-truth-island": "e46-detector-truth-island",
"e46a-ai-engineering-preannotation": "e46a-ai-engineering-preannotation",
"e46b-temporal-motion": "e46b-temporal-motion",
"e46c-full-replay-world-tracks": "e46c-full-replay-world-tracks",
"e46d-temporal-failure-audit": "e46d-temporal-failure-audit",
"e46e-ready-stack": "e46e-ready-stack",
"e46f-dashcam-bakeoff": "e46f-dashcam-bakeoff",
"e46g-rectified-detector-bakeoff": "e46g-rectified-detector-bakeoff",
"e46h-full-rectified-front-replay": "e46h-full-rectified-front-replay",
"e46i-grounding-dino-full-replay": "e46i-grounding-dino-full-replay",
"e46j-raw-fisheye-realtime": "e46j-raw-fisheye-realtime",
"l34-right-yolox-truth-island-freeze": "l34-right-yolox-truth-island-freeze",
"l34a-assisted-yolox-error-audit": "l34a-assisted-yolox-error-audit",
"l34b-nested-box-consolidation-shadow": "l34b-nested-box-consolidation-shadow",
"l34c-tile-seam-stitch-shadow": "l34c-tile-seam-stitch-shadow",
"l34d-cumulative-postprocessing-candidate": "l34d-cumulative-postprocessing-candidate",
"l34e-self-review-diagnostic": "l34e-self-review-diagnostic",
"l34f-adjudicated-reference": "l34f-adjudicated-reference",
};
export function isAdvancedLaboratoryWorkId(
@@ -73,6 +153,8 @@ export function emptyAdvancedLaboratoryResults(): AdvancedLaboratoryResults {
return {
l3: null,
l31: null,
l32: null,
l33: null,
e31: null,
e32: null,
e33: null,
@@ -82,6 +164,24 @@ export function emptyAdvancedLaboratoryResults(): AdvancedLaboratoryResults {
e38: null,
e39: null,
e40: null,
e46: null,
e46a: null,
e46b: null,
e46c: null,
e46d: null,
e46e: null,
e46f: null,
e46g: null,
e46h: null,
e46i: null,
e46j: null,
l34: null,
l34a: null,
l34b: null,
l34c: null,
l34d: null,
l34e: null,
l34f: null,
};
}
@@ -175,6 +275,8 @@ export function advancedLaboratoryResultAvailable(
): boolean {
return workId === "l3-pointpillars-visual-audit" ? results.l3 !== null
: workId === "l31-pointpillars-ravnoves" ? results.l31 !== null
: workId === "l32-pointpillars-camera-review" ? results.l32 !== null
: workId === "l33-camera-first-detector-review" ? results.l33 !== null
: workId === "e31-source-binding" ? results.e31 !== null
: workId === "e32-track-geometry" ? results.e32 !== null
: workId === "e33-worker-shadow" ? results.e33 !== null
@@ -183,7 +285,25 @@ export function advancedLaboratoryResultAvailable(
: workId === "e37-ravnoves-acceptance" ? results.e37 !== null
: workId === "e38-perception-baseline" ? results.e38 !== null
: workId === "e39-perception-refinement" ? results.e39 !== null
: results.e40 !== null;
: workId === "e40-perception-product-gate" ? results.e40 !== null
: workId === "e46-detector-truth-island" ? results.e46 !== null
: workId === "e46a-ai-engineering-preannotation" ? results.e46a !== null
: workId === "e46b-temporal-motion" ? results.e46b !== null
: workId === "e46c-full-replay-world-tracks" ? results.e46c !== null
: workId === "e46d-temporal-failure-audit" ? results.e46d !== null
: workId === "e46e-ready-stack" ? results.e46e !== null
: workId === "e46f-dashcam-bakeoff" ? results.e46f !== null
: workId === "e46g-rectified-detector-bakeoff" ? results.e46g !== null
: workId === "e46h-full-rectified-front-replay" ? results.e46h !== null
: workId === "e46i-grounding-dino-full-replay" ? results.e46i !== null
: workId === "e46j-raw-fisheye-realtime" ? results.e46j !== null
: workId === "l34-right-yolox-truth-island-freeze" ? results.l34 !== null
: workId === "l34a-assisted-yolox-error-audit" ? results.l34a !== null
: workId === "l34b-nested-box-consolidation-shadow" ? results.l34b !== null
: workId === "l34c-tile-seam-stitch-shadow" ? results.l34c !== null
: workId === "l34d-cumulative-postprocessing-candidate" ? results.l34d !== null
: workId === "l34e-self-review-diagnostic" ? results.l34e !== null
: results.l34f !== null;
}
export async function fetchAdvancedLaboratoryResult(
@@ -201,6 +321,10 @@ export async function fetchAdvancedLaboratoryResult(
results.l3 = await fetchL3PointPillarsVisualAudit({ fetcher, signal });
} else if (workId === "l31-pointpillars-ravnoves") {
results.l31 = await fetchL31PointPillarsRavnoves({ fetcher, signal });
} else if (workId === "l32-pointpillars-camera-review") {
results.l32 = await fetchL32PointPillarsCameraReview({ fetcher, signal });
} else if (workId === "l33-camera-first-detector-review") {
results.l33 = await fetchL33CameraFirstDetectorReview({ fetcher, signal });
} else if (workId === "e31-source-binding") {
results.e31 = await fetchOne(
"/api/v1/laboratory/e31/results?limit=1",
@@ -247,8 +371,44 @@ export async function fetchAdvancedLaboratoryResult(
fetcher,
signal,
);
} else {
} else if (workId === "e40-perception-product-gate") {
results.e40 = await fetchE40ProductGateResult({ fetcher, signal });
} else if (workId === "e46-detector-truth-island") {
results.e46 = await fetchE46BlindReviewResult({ fetcher, signal });
} else if (workId === "e46a-ai-engineering-preannotation") {
results.e46a = await fetchE46AAiEngineeringPreannotation({ fetcher, signal });
} else if (workId === "e46b-temporal-motion") {
results.e46b = await fetchE46BTemporalMotion({ fetcher, signal });
} else if (workId === "e46c-full-replay-world-tracks") {
results.e46c = await fetchE46CFullReplayWorldTracks({ fetcher, signal });
} else if (workId === "e46d-temporal-failure-audit") {
results.e46d = await fetchE46DTemporalFailureAudit({ fetcher, signal });
} else if (workId === "e46e-ready-stack") {
results.e46e = await fetchE46EReadyStack({ fetcher, signal });
} else if (workId === "e46f-dashcam-bakeoff") {
results.e46f = await fetchE46FDashCamBakeoff({ fetcher, signal });
} else if (workId === "e46g-rectified-detector-bakeoff") {
results.e46g = await fetchE46GRectifiedDetectorBakeoff({ fetcher, signal });
} else if (workId === "e46h-full-rectified-front-replay") {
results.e46h = await fetchE46HFullRectifiedFrontReplay({ fetcher, signal });
} else if (workId === "e46i-grounding-dino-full-replay") {
results.e46i = await fetchE46IGroundingDinoFullReplay({ fetcher, signal });
} else if (workId === "e46j-raw-fisheye-realtime") {
results.e46j = await fetchE46JRawFisheyeRealtime({ fetcher, signal });
} else if (workId === "l34-right-yolox-truth-island-freeze") {
results.l34 = await fetchL34RightYoloxTruthIsland({ fetcher, signal });
} else if (workId === "l34a-assisted-yolox-error-audit") {
results.l34a = await fetchL34AAssistedYoloxErrorAudit({ fetcher, signal });
} else if (workId === "l34b-nested-box-consolidation-shadow") {
results.l34b = await fetchL34BNestedBoxConsolidation({ fetcher, signal });
} else if (workId === "l34c-tile-seam-stitch-shadow") {
results.l34c = await fetchL34CTileSeamStitch({ fetcher, signal });
} else if (workId === "l34d-cumulative-postprocessing-candidate") {
results.l34d = await fetchL34DCumulativePostprocessing({ fetcher, signal });
} else if (workId === "l34e-self-review-diagnostic") {
results.l34e = await fetchL34ESelfReviewDiagnostic({ fetcher, signal });
} else {
results.l34f = await fetchL34FFrozenResult({ fetcher, signal });
}
if (!advancedLaboratoryResultAvailable(workId, results)) {
throw new AdvancedLaboratoryContractError(
@@ -11,10 +11,32 @@ import type { E35DegradationRecoveryResult } from "./e35DegradationRecovery";
import type { E40PerceptionProductGateResult } from "./e40ProductGate";
import type { L3PointPillarsVisualAuditResult } from "./l3PointPillarsVisualAudit";
import type { L31PointPillarsRavnovesResult } from "./l31PointPillarsRavnoves";
import type { L32PointPillarsCameraReviewResult } from "./l32PointPillarsCameraReview";
import type { L33CameraFirstDetectorReviewResult } from "./l33CameraFirstDetectorReview";
import type { L34RightYoloxTruthIslandResult } from "./l34RightYoloxTruthIsland";
import type { L34AAssistedYoloxErrorAuditResult } from "./l34aAssistedYoloxErrorAudit";
import type { L34BResult } from "./l34bNestedBoxConsolidation";
import type { L34CResult } from "./l34cTileSeamStitch";
import type { L34DResult } from "./l34dCumulativePostprocessing";
import type { L34EResult } from "./l34eSelfReviewDiagnostic";
import type { L34FFrozenResult } from "./l34fAdjudication";
import type { E46BlindReviewResult } from "./e46BlindReview";
import type { E46AAiEngineeringPreannotationResult } from "./e46aAiEngineeringPreannotation";
import type { E46BTemporalMotionResult } from "./e46bTemporalMotion";
import type { E46CFullReplayWorldTracksResult } from "./e46cFullReplayWorldTracks";
import type { E46DTemporalFailureAuditResult } from "./e46dTemporalFailureAudit";
import type { E46EReadyStackResult } from "./e46eReadyStack";
import type { E46FDashCamBakeoffResult } from "./e46fDashCamBakeoff";
import type { E46GRectifiedDetectorBakeoffResult } from "./e46gRectifiedDetectorBakeoff";
import type { E46HFullRectifiedFrontReplayResult } from "./e46hFullRectifiedFrontReplay";
import type { E46IGroundingDinoFullReplayResult } from "./e46iGroundingDinoFullReplay";
import type { E46JRawFisheyeRealtimeResult } from "./e46jRawFisheyeRealtime";
export interface AdvancedLaboratoryResults {
l3: L3PointPillarsVisualAuditResult | null;
l31: L31PointPillarsRavnovesResult | null;
l32: L32PointPillarsCameraReviewResult | null;
l33: L33CameraFirstDetectorReviewResult | null;
e31: E31LaboratoryResult | null;
e32: E32LaboratoryResult | null;
e33: E33LaboratoryResult | null;
@@ -24,4 +46,22 @@ export interface AdvancedLaboratoryResults {
e38: E38PerceptionBaselineResult | null;
e39: E39PerceptionRefinementResult | null;
e40: E40PerceptionProductGateResult | null;
e46: E46BlindReviewResult | null;
e46a: E46AAiEngineeringPreannotationResult | null;
e46b: E46BTemporalMotionResult | null;
e46c: E46CFullReplayWorldTracksResult | null;
e46d: E46DTemporalFailureAuditResult | null;
e46e: E46EReadyStackResult | null;
e46f: E46FDashCamBakeoffResult | null;
e46g: E46GRectifiedDetectorBakeoffResult | null;
e46h: E46HFullRectifiedFrontReplayResult | null;
e46i: E46IGroundingDinoFullReplayResult | null;
e46j: E46JRawFisheyeRealtimeResult | null;
l34: L34RightYoloxTruthIslandResult | null;
l34a: L34AAssistedYoloxErrorAuditResult | null;
l34b: L34BResult | null;
l34c: L34CResult | null;
l34d: L34DResult | null;
l34e: L34EResult | null;
l34f: L34FFrozenResult | null;
}
@@ -1,16 +1,9 @@
import {
fetchE34TemporalLayerResult,
} from "./e34TemporalLayer";
import {
fetchE35DegradationRecoveryResult,
} from "./e35DegradationRecovery";
import {
fetchE40ProductGateResult,
} from "./e40ProductGate";
import { fetchE34TemporalLayerResult } from "./e34TemporalLayer";
import { fetchE35DegradationRecoveryResult } from "./e35DegradationRecovery";
import { fetchE40ProductGateResult } from "./e40ProductGate";
import { settledCatalogValue } from "./catalogTransport";
import type { AdvancedLaboratoryResults } from "./advancedLaboratoryResults";
export type { AdvancedLaboratoryResults } from "./advancedLaboratoryResults";
export interface E31LaboratoryResult {
resultId: string;
createdAtUtc: string | null;
@@ -947,7 +940,6 @@ export async function fetchOne<T>(
}
return parseCatalog(await response.json(), parser);
}
export async function fetchAdvancedLaboratoryResults({
fetcher = fetch,
signal,
@@ -976,8 +968,9 @@ export async function fetchAdvancedLaboratoryResults({
const e39 = settledCatalogValue(settled[7]);
const e40 = settledCatalogValue(settled[8]);
return {
l3: null,
l31: null,
l3: null, l31: null,
l32: null,
l33: null,
e31,
e32,
e33,
@@ -987,5 +980,20 @@ export async function fetchAdvancedLaboratoryResults({
e38,
e39,
e40,
e46: null,
e46a: null,
e46b: null,
e46c: null,
e46d: null,
e46e: null,
e46f: null,
e46g: null, e46h: null, e46i: null, e46j: null,
l34: null,
l34a: null,
l34b: null,
l34c: null,
l34d: null,
l34e: null,
l34f: null,
};
}
@@ -0,0 +1,243 @@
export interface E46BlindReviewSubmissionSummary {
resultId: string;
reviewerId: string;
annotationSessionId: string;
state: "completed-e48-review-input-not-truth";
frameCount: number;
objectCount: number;
}
export interface E46BlindReviewResult {
resultId: string;
createdAtUtc: string;
status: "prepared-awaiting-independent-human-review";
sourceSessionId: string;
cameraSourceId: "sensor.camera.right";
metrics: {
frameCount: 32;
anchorCount: 16;
temporalFrameCount: 16;
temporalGroupCount: 4;
reviewSessionCount: number;
completedSubmissionCount: number;
requiredSubmissionCount: 2;
};
submissions: readonly E46BlindReviewSubmissionSummary[];
decision: {
collectionReady: true;
twoIndependentReviewsComplete: boolean;
adjudicationReady: boolean;
e48Sealed: false;
l35Open: false;
nextAction: string;
};
access: "review-collection-read-only-summary";
}
type LaboratoryFetch = (
input: RequestInfo | URL,
init?: RequestInit,
) => Promise<Response>;
class E46BlindReviewContractError extends Error {}
function object(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new E46BlindReviewContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function array(value: unknown, label: string): readonly unknown[] {
if (!Array.isArray(value)) {
throw new E46BlindReviewContractError(`${label}: ожидался массив.`);
}
return value;
}
function string(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new E46BlindReviewContractError(`${label}: ожидалась строка.`);
}
return value;
}
function integer(value: unknown, label: string): number {
if (!Number.isInteger(value) || Number(value) < 0) {
throw new E46BlindReviewContractError(`${label}: ожидалось целое число.`);
}
return Number(value);
}
function boolean(value: unknown, label: string): boolean {
if (typeof value !== "boolean") {
throw new E46BlindReviewContractError(`${label}: ожидался boolean.`);
}
return value;
}
function exact<T extends string | boolean | number>(
value: unknown,
expected: T,
label: string,
): T {
if (value !== expected) {
throw new E46BlindReviewContractError(`${label}: нарушен контракт.`);
}
return expected;
}
function parseSubmission(value: unknown): E46BlindReviewSubmissionSummary {
const item = object(value, "E46 submission");
const resultId = string(item.result_id, "E46 submission id");
if (!/^e46-lab-review-submission-[a-f0-9]{64}$/.test(resultId)) {
throw new E46BlindReviewContractError("E46 submission: нарушена identity.");
}
return {
resultId,
reviewerId: string(item.reviewer_id, "E46 reviewer"),
annotationSessionId: string(item.annotation_session_id, "E46 session"),
state: exact(
item.state,
"completed-e48-review-input-not-truth",
"E46 submission state",
),
frameCount: integer(item.frame_count, "E46 submitted frames"),
objectCount: integer(item.object_count, "E46 submitted objects"),
};
}
export async function fetchE46BlindReviewResult({
fetcher = fetch,
signal,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
} = {}): Promise<E46BlindReviewResult | null> {
const response = await fetcher("/api/v1/laboratory/e46/results?limit=1", {
method: "GET",
headers: { Accept: "application/json" },
signal,
});
if (!response.ok) {
throw new E46BlindReviewContractError(`E46 LAB недоступен: HTTP ${response.status}.`);
}
const catalog = object(await response.json(), "E46 catalog");
exact(
catalog.schema_version,
"missioncore.e46-detector-truth-island-catalog/v1",
"E46 catalog schema",
);
const items = array(catalog.items, "E46 results");
if (!items.length) return null;
if (items.length !== 1) {
throw new E46BlindReviewContractError("E46 catalog: нарушен размер.");
}
const item = object(items[0], "E46 result");
const resultId = string(item.result_id, "E46 result id");
if (!/^e46-detector-truth-island-[a-f0-9]{64}$/.test(resultId)) {
throw new E46BlindReviewContractError("E46 result: нарушена identity.");
}
const metrics = object(item.metrics, "E46 metrics");
const decision = object(item.decision, "E46 decision");
const blindness = object(item.blindness, "E46 blindness");
const authority = object(item.authority, "E46 authority");
const submissions = array(item.submissions, "E46 submissions").map(parseSubmission);
const reviewSessions = array(item.review_sessions, "E46 review sessions");
const completedSubmissionCount = integer(
metrics.completed_submission_count,
"E46 completed submissions",
);
if (
completedSubmissionCount !== submissions.length
|| completedSubmissionCount > 2
|| integer(metrics.review_session_count, "E46 session count") !== reviewSessions.length
|| reviewSessions.length > 2
|| submissions.some((submission) => submission.frameCount !== 32)
) {
throw new E46BlindReviewContractError("E46 review collection: нарушен размер.");
}
for (const key of (
[
"candidate_identity_included",
"model_prelabels_included",
"model_predictions_included",
"model_scores_included",
] as const
)) exact(blindness[key], false, `E46 blindness ${key}`);
for (const key of (
[
"ground_truth",
"independent_truth",
"metric_grade_reference",
"candidate_accepted",
"commands_enabled",
"navigation_or_safety_accepted",
] as const
)) exact(authority[key], false, `E46 authority ${key}`);
const twoComplete = boolean(
decision.two_independent_reviews_complete,
"E46 two review state",
);
const adjudicationReady = boolean(
decision.adjudication_ready,
"E46 adjudication state",
);
if (twoComplete !== (completedSubmissionCount === 2) || adjudicationReady !== twoComplete) {
throw new E46BlindReviewContractError("E46 decision: нарушен review gate.");
}
return {
resultId,
createdAtUtc: string(item.created_at_utc, "E46 created time"),
status: exact(
item.status,
"prepared-awaiting-independent-human-review",
"E46 status",
),
sourceSessionId: string(item.source_session_id, "E46 source session"),
cameraSourceId: exact(
item.camera_source_id,
"sensor.camera.right",
"E46 camera source",
),
metrics: {
frameCount: exact(metrics.frame_count, 32, "E46 frame count"),
anchorCount: exact(metrics.anchor_count, 16, "E46 anchors"),
temporalFrameCount: exact(
metrics.temporal_frame_count,
16,
"E46 temporal frames",
),
temporalGroupCount: exact(
metrics.temporal_group_count,
4,
"E46 temporal groups",
),
reviewSessionCount: integer(metrics.review_session_count, "E46 sessions"),
completedSubmissionCount,
requiredSubmissionCount: exact(
metrics.required_submission_count,
2,
"E46 required submissions",
),
},
submissions,
decision: {
collectionReady: exact(
decision.collection_ready,
true,
"E46 collection ready",
),
twoIndependentReviewsComplete: twoComplete,
adjudicationReady,
e48Sealed: exact(decision.e48_sealed, false, "E48 seal state"),
l35Open: exact(decision.l35_open, false, "L3.5 state"),
nextAction: string(decision.next_action, "E46 next action"),
},
access: exact(
item.access,
"review-collection-read-only-summary",
"E46 access",
),
};
}
@@ -0,0 +1,269 @@
export interface E46AObject {
objectId: string;
category: string;
displayCategory: string;
boxXyxy: readonly [number, number, number, number];
occluded: boolean;
truncated: boolean;
}
export interface E46ACase {
resultId: string;
truthIslandSequence: number;
imageId: number;
frameIndex: number;
groupId: string;
sessionSeconds: number;
sourceImageSha256: string;
objects: readonly E46AObject[];
objectCount: number;
cameraUrl: string;
groundTruth: false;
independentReview: false;
}
export interface E46AAiEngineeringPreannotationResult {
resultId: string;
createdAtUtc: string;
sourceSessionId: "RAVNOVES00";
cameraSourceId: "sensor.camera.right";
sourceE46ResultId: string;
metrics: {
frameCount: 32;
reviewedFrameCount: 32;
sourceObjectCount: number;
objectCount: number;
customClassRelabelCount: number;
deletedFalseBoxCount: number;
geometrySnappedObjectCount: number;
sourceGeometryRetainedObjectCount: number;
categoryCorrectedObjectCount: number;
hardNegativeFrameCount: number;
independentReviewSubmissionCount: 0;
classCounts: Readonly<Record<string, number>>;
};
taxonomy: {
classes: readonly string[];
customClasses: readonly ["laptop", "stroller"];
unresolvedClassCount: 0;
};
decision: {
preannotationAvailable: true;
visualReviewComplete: true;
independentReviewCountAffected: false;
e48TruthSealOpen: false;
l35AcceptanceOpen: false;
nextAction: string;
};
limitations: readonly string[];
groundTruth: false;
access: "read-only-ai-engineering-preannotation-not-truth";
}
type LaboratoryFetch = (
input: RequestInfo | URL,
init?: RequestInit,
) => Promise<Response>;
class E46AContractError extends Error {}
function object(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new E46AContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function array(value: unknown, label: string): readonly unknown[] {
if (!Array.isArray(value)) throw new E46AContractError(`${label}: ожидался массив.`);
return value;
}
function string(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new E46AContractError(`${label}: ожидалась строка.`);
}
return value;
}
function number(value: unknown, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new E46AContractError(`${label}: ожидалось число.`);
}
return value;
}
function integer(value: unknown, label: string): number {
const parsed = number(value, label);
if (!Number.isInteger(parsed) || parsed < 0) {
throw new E46AContractError(`${label}: ожидалось целое число.`);
}
return parsed;
}
function optionalInteger(value: unknown, fallback: number, label: string): number {
return value === undefined ? fallback : integer(value, label);
}
function exact<T extends string | boolean | number>(
value: unknown,
expected: T,
label: string,
): T {
if (value !== expected) throw new E46AContractError(`${label}: нарушен контракт.`);
return expected;
}
function boolean(value: unknown, label: string): boolean {
if (typeof value !== "boolean") {
throw new E46AContractError(`${label}: ожидался boolean.`);
}
return value;
}
function sha256(value: unknown, label: string): string {
const parsed = string(value, label);
if (!/^[a-f0-9]{64}$/.test(parsed)) throw new E46AContractError(`${label}: invalid hash.`);
return parsed;
}
function parseClassCounts(value: unknown): Readonly<Record<string, number>> {
const raw = object(value, "E46A class counts");
const parsed: Record<string, number> = {};
for (const [key, count] of Object.entries(raw)) {
if (!key.trim()) throw new E46AContractError("E46A class name: пустое значение.");
parsed[key] = integer(count, `E46A ${key} count`);
}
return parsed;
}
function parseObject(value: unknown): E46AObject {
const item = object(value, "E46A object");
const box = array(item.box_xyxy, "E46A box").map((coordinate) => number(coordinate, "E46A coordinate"));
if (box.length !== 4 || box[2] <= box[0] || box[3] <= box[1]) {
throw new E46AContractError("E46A box: invalid geometry.");
}
const category = string(item.category, "E46A category");
if (category === "unmapped") throw new E46AContractError("E46A category unresolved.");
return {
objectId: string(item.object_id, "E46A object id"),
category,
displayCategory: category === "stroller" ? "Коляска"
: category === "laptop" ? "Ноутбук"
: category,
boxXyxy: box as [number, number, number, number],
occluded: boolean(item.occluded, "E46A occluded"),
truncated: boolean(item.truncated, "E46A truncated"),
};
}
export async function fetchE46AAiEngineeringPreannotation({
fetcher = fetch,
signal,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
} = {}): Promise<E46AAiEngineeringPreannotationResult | null> {
const response = await fetcher("/api/v1/laboratory/e46a/results?limit=1", {
method: "GET",
headers: { Accept: "application/json" },
signal,
});
if (!response.ok) throw new E46AContractError(`E46A LAB недоступен: HTTP ${response.status}.`);
const catalog = object(await response.json(), "E46A catalog");
exact(catalog.schema_version, "missioncore.e46a-ai-engineering-preannotation-catalog/v1", "E46A catalog schema");
const items = array(catalog.items, "E46A results");
if (!items.length) return null;
if (items.length !== 1) throw new E46AContractError("E46A catalog: нарушен размер.");
const item = object(items[0], "E46A result");
exact(item.schema_version, "missioncore.e46a-ai-engineering-preannotation-view/v1", "E46A result schema");
const resultId = string(item.result_id, "E46A result id");
const sourceE46ResultId = string(item.source_e46_result_id, "E46A source id");
if (!/^e46a-ai-engineering-preannotation-[a-f0-9]{64}$/.test(resultId)
|| !/^e46-detector-truth-island-[a-f0-9]{64}$/.test(sourceE46ResultId)) {
throw new E46AContractError("E46A result: нарушена identity.");
}
const metrics = object(item.metrics, "E46A metrics");
const taxonomy = object(item.taxonomy, "E46A taxonomy");
const decision = object(item.decision, "E46A decision");
const classes = array(taxonomy.classes, "E46A classes").map((value) => string(value, "E46A class"));
const custom = array(taxonomy.custom_classes, "E46A custom classes").map((value) => string(value, "E46A custom class"));
const objectCount = integer(metrics.object_count, "E46A object count");
if (custom.length !== 2 || custom[0] !== "laptop" || custom[1] !== "stroller") {
throw new E46AContractError("E46A custom taxonomy changed.");
}
return {
resultId,
createdAtUtc: string(item.created_at_utc, "E46A created time"),
sourceSessionId: exact(item.source_session_id, "RAVNOVES00", "E46A source session"),
cameraSourceId: exact(item.camera_source_id, "sensor.camera.right", "E46A camera source"),
sourceE46ResultId,
metrics: {
frameCount: exact(metrics.frame_count, 32, "E46A frame count"),
reviewedFrameCount: exact(metrics.reviewed_frame_count, 32, "E46A reviewed frames"),
sourceObjectCount: optionalInteger(metrics.source_object_count, objectCount, "E46A source object count"),
objectCount,
customClassRelabelCount: integer(metrics.custom_class_relabel_count, "E46A relabel count"),
deletedFalseBoxCount: optionalInteger(metrics.deleted_false_box_count, 0, "E46A deleted false boxes"),
geometrySnappedObjectCount: optionalInteger(metrics.geometry_snapped_object_count, 0, "E46A snapped geometry"),
sourceGeometryRetainedObjectCount: optionalInteger(metrics.source_geometry_retained_object_count, objectCount, "E46A retained geometry"),
categoryCorrectedObjectCount: optionalInteger(metrics.category_corrected_object_count, 0, "E46A category corrections"),
hardNegativeFrameCount: integer(metrics.hard_negative_frame_count, "E46A hard negatives"),
independentReviewSubmissionCount: exact(metrics.independent_review_submission_count, 0, "E46A independent reviews"),
classCounts: parseClassCounts(metrics.class_counts),
},
taxonomy: {
classes,
customClasses: ["laptop", "stroller"],
unresolvedClassCount: exact(taxonomy.unresolved_class_count, 0, "E46A unresolved classes"),
},
decision: {
preannotationAvailable: exact(decision.preannotation_available, true, "E46A available"),
visualReviewComplete: exact(decision.visual_review_complete, true, "E46A visual review"),
independentReviewCountAffected: exact(decision.independent_review_count_affected, false, "E46A reviewer isolation"),
e48TruthSealOpen: exact(decision.e48_truth_seal_open, false, "E46A E48 state"),
l35AcceptanceOpen: exact(decision.l35_acceptance_open, false, "E46A L3.5 state"),
nextAction: string(decision.next_action, "E46A next action"),
},
limitations: array(item.limitations, "E46A limitations").map((value) => string(value, "E46A limitation")),
groundTruth: exact(item.ground_truth, false, "E46A truth state"),
access: exact(item.access, "read-only-ai-engineering-preannotation-not-truth", "E46A access"),
};
}
export async function fetchE46ACase(
resultId: string,
sequence: number,
{ fetcher = fetch, signal }: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
): Promise<E46ACase> {
if (!/^e46a-ai-engineering-preannotation-[a-f0-9]{64}$/.test(resultId)
|| !Number.isInteger(sequence) || sequence < 1 || sequence > 32) {
throw new E46AContractError("E46A case request invalid.");
}
const response = await fetcher(`/api/v1/laboratory/e46a/results/${resultId}/cases/${sequence}`, {
method: "GET",
headers: { Accept: "application/json" },
signal,
});
if (!response.ok) throw new E46AContractError(`E46A frame недоступен: HTTP ${response.status}.`);
const item = object(await response.json(), "E46A case");
exact(item.schema_version, "missioncore.e46a-ai-engineering-preannotation-case/v1", "E46A case schema");
const objects = array(item.objects, "E46A objects").map(parseObject);
if (integer(item.object_count, "E46A object count") !== objects.length) {
throw new E46AContractError("E46A case object count mismatch.");
}
return {
resultId: exact(item.result_id, resultId, "E46A case result"),
truthIslandSequence: exact(item.truth_island_sequence, sequence, "E46A case sequence"),
imageId: integer(item.image_id, "E46A image id"),
frameIndex: integer(item.frame_index, "E46A frame index"),
groupId: string(item.group_id, "E46A group"),
sessionSeconds: number(item.session_seconds, "E46A session seconds"),
sourceImageSha256: sha256(item.source_image_sha256, "E46A source hash"),
objects,
objectCount: objects.length,
cameraUrl: string(item.camera_url, "E46A camera URL"),
groundTruth: exact(item.ground_truth, false, "E46A case truth"),
independentReview: exact(item.independent_review, false, "E46A independent review"),
};
}
@@ -0,0 +1,170 @@
export type E46BMotionState = "dynamic" | "static" | "unknown";
export interface E46BObject {
objectId: string;
trackId: string;
trackIndex: number;
category: string;
displayCategory: string;
boxXyxy: readonly [number, number, number, number];
motionState: E46BMotionState;
motionConfidence: number;
motionEvidenceObservationCount: number;
trailCentersXy: readonly (readonly [number, number])[];
}
export interface E46BCase {
resultId: string;
truthIslandSequence: number;
frameIndex: number;
groupId: string;
sessionSeconds: number;
sourceImageSha256: string;
objects: readonly E46BObject[];
objectCount: number;
motionCounts: Readonly<Record<E46BMotionState, number>>;
cameraUrl: string;
}
export interface E46BTemporalMotionResult {
resultId: string;
createdAtUtc: string;
sourceE46AResultId: string;
sourceE26ResultId: string;
metrics: {
frameCount: 16;
temporalGroupCount: 4;
objectObservationCount: 136;
trackCount: 34;
matchedMotionObservationCount: number;
unmatchedMotionObservationCount: number;
dynamicTrackCount: number;
staticTrackCount: number;
unknownTrackCount: number;
visuallyReviewedFrameCount: 16;
};
decision: { stableIdsAvailable: true; motionStateAvailable: true; metricVelocityAvailable: false; nextAction: string };
limitations: readonly string[];
}
type LaboratoryFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
class ContractError extends Error {}
const object = (value: unknown, label: string): Record<string, unknown> => {
if (!value || typeof value !== "object" || Array.isArray(value)) throw new ContractError(`${label}: объект.`);
return value as Record<string, unknown>;
};
const array = (value: unknown, label: string): readonly unknown[] => {
if (!Array.isArray(value)) throw new ContractError(`${label}: массив.`);
return value;
};
const string = (value: unknown, label: string): string => {
if (typeof value !== "string" || !value.trim()) throw new ContractError(`${label}: строка.`);
return value;
};
const number = (value: unknown, label: string): number => {
if (typeof value !== "number" || !Number.isFinite(value)) throw new ContractError(`${label}: число.`);
return value;
};
const integer = (value: unknown, label: string): number => {
const parsed = number(value, label);
if (!Number.isInteger(parsed) || parsed < 0) throw new ContractError(`${label}: целое.`);
return parsed;
};
const exact = <T extends string | boolean | number>(value: unknown, expected: T, label: string): T => {
if (value !== expected) throw new ContractError(`${label}: контракт.`);
return expected;
};
const motionState = (value: unknown): E46BMotionState => {
if (value !== "dynamic" && value !== "static" && value !== "unknown") throw new ContractError("E46B motion state.");
return value;
};
const displayCategory = (category: string): string => category === "stroller" ? "Коляска"
: category === "laptop" ? "Ноутбук"
: category === "heavy_vehicle" ? "Тяжёлый транспорт"
: category === "person" ? "Человек"
: category === "car" ? "Авто" : category;
export async function fetchE46BTemporalMotion({ fetcher = fetch, signal }: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {}): Promise<E46BTemporalMotionResult | null> {
const response = await fetcher("/api/v1/laboratory/e46b/results?limit=1", { headers: { Accept: "application/json" }, signal });
if (!response.ok) throw new ContractError(`E46B LAB недоступен: HTTP ${response.status}.`);
const catalog = object(await response.json(), "E46B catalog");
exact(catalog.schema_version, "missioncore.e46b-temporal-motion-catalog/v1", "E46B catalog schema");
const items = array(catalog.items, "E46B results");
if (!items.length) return null;
if (items.length !== 1) throw new ContractError("E46B catalog size.");
const item = object(items[0], "E46B result");
exact(item.schema_version, "missioncore.e46b-temporal-motion-view/v1", "E46B view schema");
const resultId = string(item.result_id, "E46B result id");
if (!/^e46b-temporal-motion-[a-f0-9]{64}$/.test(resultId)) throw new ContractError("E46B identity.");
const metrics = object(item.metrics, "E46B metrics");
const decision = object(item.decision, "E46B decision");
return {
resultId,
createdAtUtc: string(item.created_at_utc, "E46B created"),
sourceE46AResultId: string(item.source_e46a_result_id, "E46B E46A source"),
sourceE26ResultId: string(item.source_e26_result_id, "E46B E26 source"),
metrics: {
frameCount: exact(metrics.frame_count, 16, "E46B frames"),
temporalGroupCount: exact(metrics.temporal_group_count, 4, "E46B groups"),
objectObservationCount: exact(metrics.object_observation_count, 136, "E46B observations"),
trackCount: exact(metrics.track_count, 34, "E46B tracks"),
matchedMotionObservationCount: integer(metrics.matched_motion_observation_count, "E46B matched"),
unmatchedMotionObservationCount: integer(metrics.unmatched_motion_observation_count, "E46B unmatched"),
dynamicTrackCount: integer(metrics.dynamic_track_count, "E46B dynamic"),
staticTrackCount: integer(metrics.static_track_count, "E46B static"),
unknownTrackCount: integer(metrics.unknown_track_count, "E46B unknown"),
visuallyReviewedFrameCount: exact(metrics.visually_reviewed_frame_count, 16, "E46B reviewed"),
},
decision: {
stableIdsAvailable: exact(decision.stable_ids_available, true, "E46B stable IDs"),
motionStateAvailable: exact(decision.motion_state_available, true, "E46B motion"),
metricVelocityAvailable: exact(decision.metric_velocity_available, false, "E46B velocity"),
nextAction: string(decision.next_action, "E46B next action"),
},
limitations: array(item.limitations, "E46B limitations").map((value) => string(value, "E46B limitation")),
};
}
export async function fetchE46BCase(resultId: string, sequence: number, { fetcher = fetch, signal }: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {}): Promise<E46BCase> {
if (!/^e46b-temporal-motion-[a-f0-9]{64}$/.test(resultId)) throw new ContractError("E46B result id.");
const response = await fetcher(`/api/v1/laboratory/e46b/results/${resultId}/cases/${sequence}`, { headers: { Accept: "application/json" }, signal });
if (!response.ok) throw new ContractError(`E46B frame недоступен: HTTP ${response.status}.`);
const item = object(await response.json(), "E46B case");
exact(item.schema_version, "missioncore.e46b-temporal-motion-case/v1", "E46B case schema");
const objects = array(item.objects, "E46B objects").map((raw): E46BObject => {
const value = object(raw, "E46B object");
const box = array(value.box_xyxy, "E46B box").map((coordinate) => number(coordinate, "E46B coordinate"));
const trail = array(value.trail_centers_xy, "E46B trail").map((rawPoint) => {
const point = array(rawPoint, "E46B point").map((coordinate) => number(coordinate, "E46B point coordinate"));
if (point.length !== 2) throw new ContractError("E46B point geometry.");
return point as [number, number];
});
if (box.length !== 4 || box[2] <= box[0] || box[3] <= box[1]) throw new ContractError("E46B box geometry.");
const category = string(value.category, "E46B category");
return {
objectId: string(value.object_id, "E46B object id"),
trackId: string(value.track_id, "E46B track id"),
trackIndex: integer(value.track_index, "E46B track index"),
category,
displayCategory: displayCategory(category),
boxXyxy: box as [number, number, number, number],
motionState: motionState(value.motion_state),
motionConfidence: number(value.motion_confidence, "E46B motion confidence"),
motionEvidenceObservationCount: integer(value.motion_evidence_observation_count, "E46B evidence count"),
trailCentersXy: trail,
};
});
const rawCounts = object(item.motion_counts, "E46B motion counts");
return {
resultId: exact(item.result_id, resultId, "E46B case result"),
truthIslandSequence: integer(item.truth_island_sequence, "E46B sequence"),
frameIndex: integer(item.frame_index, "E46B frame"),
groupId: string(item.group_id, "E46B group"),
sessionSeconds: number(item.session_seconds, "E46B time"),
sourceImageSha256: string(item.source_image_sha256, "E46B image hash"),
objects,
objectCount: integer(item.object_count, "E46B object count"),
motionCounts: { dynamic: integer(rawCounts.dynamic, "E46B dynamic count"), static: integer(rawCounts.static, "E46B static count"), unknown: integer(rawCounts.unknown, "E46B unknown count") },
cameraUrl: string(item.camera_url, "E46B camera URL"),
};
}
@@ -0,0 +1,281 @@
export type E46CMotionState = "dynamic" | "static" | "unknown";
export interface E46CCameraObject {
objectId: string;
category: string;
displayCategory: string;
boxXyxy: readonly [number, number, number, number];
routeTrackId: number | null;
worldTrackId: number | null;
motionState: E46CMotionState;
}
export interface E46CWorldObject {
worldTrackId: number;
routeTrackId: number;
category: string;
motionState: E46CMotionState;
positionMapM: readonly [number, number, number];
occupancyFootprintMapXy: readonly (readonly [number, number])[];
occupancyEvidenceCurrent: boolean;
occupancyCellCount: number;
}
export interface E46CCase {
resultId: string;
truthIslandSequence: number;
frameIndex: number;
groupId: string;
sessionSeconds: number;
sourceImageSha256: string;
fusionState: string;
objects: readonly E46CCameraObject[];
objectCount: number;
matchedRouteObjectCount: number;
worldObjects: readonly E46CWorldObject[];
worldObjectCount: number;
cameraUrl: string;
}
export interface E46CFullReplayWorldTracksResult {
resultId: string;
createdAtUtc: string;
sourceE46AResultId: string;
sourceE26ResultId: string;
metrics: {
routeFrameCount: 4489;
routeSpanSeconds: number;
fusionObservationCount: number;
sourceTrackCount: number;
worldTrackCandidateCount: number;
worldTrackCount: number;
sourceTrackWorldBoundCount: number;
worldFrameCount: number;
worldObservationCount: number;
worldCurrentObservationCount: number;
worldHeldObservationCount: number;
visualSampleFrameCount: 32;
sampleObjectCount: number;
sampleMatchedRouteObjectCount: number;
sampleUnmatchedObjectCount: number;
sampleWorldFrameCount: number;
motionObservationCounts: Readonly<Record<E46CMotionState, number>>;
};
acceptance: { benchmarkPassedEvents: 11; benchmarkTotalEvents: 11 };
decision: { routeTrackLayerAvailable: true; worldOccupiedLayerAvailable: true; unknownRemainsOccupied: true; freeSpaceAvailable: false; nextAction: string };
limitations: readonly string[];
}
export interface E46CVideoObject {
boxXyxy: readonly [number, number, number, number];
category: string;
displayCategory: string;
score: number;
routeTrackId: number;
worldTrackId: number | null;
motionState: E46CMotionState;
motionConfidence: number;
trackHits: number;
trackAgeSeconds: number;
cameraEvidenceCurrent: boolean;
worldEvidenceCurrent: boolean;
}
export interface E46CVideoFrame {
frameIndex: number;
sessionSeconds: number;
fusionState: string;
objects: readonly E46CVideoObject[];
}
export interface E46CVideoReviewWindow {
id: string;
kind: string;
classGroup: string;
startSeconds: number;
endSeconds: number;
targetSourceTrackIds: readonly number[];
}
export interface E46CVideoOverlay {
resultId: string;
recordedSourceSessionId: string;
recordedSourceId: "sensor.camera.right";
inputSha256: string;
imageWidth: 800;
imageHeight: 600;
timelineStartSeconds: number;
timelineEndSeconds: number;
frameCount: 4489;
frames: readonly E46CVideoFrame[];
reviewWindows: readonly E46CVideoReviewWindow[];
}
type LaboratoryFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
class ContractError extends Error {}
const object = (value: unknown, label: string): Record<string, unknown> => { if (!value || typeof value !== "object" || Array.isArray(value)) throw new ContractError(`${label}: объект.`); return value as Record<string, unknown>; };
const array = (value: unknown, label: string): readonly unknown[] => { if (!Array.isArray(value)) throw new ContractError(`${label}: массив.`); return value; };
const string = (value: unknown, label: string): string => { if (typeof value !== "string" || !value.trim()) throw new ContractError(`${label}: строка.`); return value; };
const number = (value: unknown, label: string): number => { if (typeof value !== "number" || !Number.isFinite(value)) throw new ContractError(`${label}: число.`); return value; };
const integer = (value: unknown, label: string): number => { const parsed = number(value, label); if (!Number.isInteger(parsed) || parsed < 0) throw new ContractError(`${label}: целое.`); return parsed; };
const optionalInteger = (value: unknown, label: string): number | null => value === null ? null : integer(value, label);
const exact = <T extends string | boolean | number>(value: unknown, expected: T, label: string): T => { if (value !== expected) throw new ContractError(`${label}: контракт.`); return expected; };
const motionState = (value: unknown): E46CMotionState => { if (value !== "dynamic" && value !== "static" && value !== "unknown") throw new ContractError("E46C motion state."); return value; };
const displayCategory = (category: string): string => category === "stroller" ? "Коляска" : category === "laptop" ? "Ноутбук" : category === "heavy_vehicle" || category === "truck" || category === "bus" ? "Тяжёлый транспорт" : category === "person" ? "Человек" : category === "car" ? "Авто" : category;
const vector = (value: unknown, length: number, label: string): number[] => { const parsed = array(value, label).map((item) => number(item, label)); if (parsed.length !== length) throw new ContractError(`${label}: geometry.`); return parsed; };
export async function fetchE46CFullReplayWorldTracks({ fetcher = fetch, signal }: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {}): Promise<E46CFullReplayWorldTracksResult | null> {
const response = await fetcher("/api/v1/laboratory/e46c/results?limit=1", { headers: { Accept: "application/json" }, signal });
if (!response.ok) throw new ContractError(`E46C LAB недоступен: HTTP ${response.status}.`);
const catalog = object(await response.json(), "E46C catalog"); exact(catalog.schema_version, "missioncore.e46c-full-replay-world-tracks-catalog/v1", "E46C catalog schema");
const items = array(catalog.items, "E46C results"); if (!items.length) return null; if (items.length !== 1) throw new ContractError("E46C catalog size.");
const item = object(items[0], "E46C result"); exact(item.schema_version, "missioncore.e46c-full-replay-world-tracks-view/v1", "E46C view schema");
const resultId = string(item.result_id, "E46C result id"); if (!/^e46c-full-replay-world-tracks-[a-f0-9]{64}$/.test(resultId)) throw new ContractError("E46C identity.");
const metrics = object(item.metrics, "E46C metrics"); const counts = object(metrics.motion_observation_counts, "E46C motion counts"); const acceptance = object(item.acceptance, "E46C acceptance"); const decision = object(item.decision, "E46C decision");
return { resultId, createdAtUtc: string(item.created_at_utc, "E46C created"), sourceE46AResultId: string(item.source_e46a_result_id, "E46C E46A"), sourceE26ResultId: string(item.source_e26_result_id, "E46C E26"), metrics: { routeFrameCount: exact(metrics.route_frame_count, 4489, "E46C route frames"), routeSpanSeconds: number(metrics.route_span_seconds, "E46C span"), fusionObservationCount: integer(metrics.fusion_observation_count, "E46C observations"), sourceTrackCount: integer(metrics.source_track_count, "E46C source tracks"), worldTrackCandidateCount: integer(metrics.world_track_candidate_count, "E46C world candidates"), worldTrackCount: integer(metrics.world_track_count, "E46C world tracks"), sourceTrackWorldBoundCount: integer(metrics.source_track_world_bound_count, "E46C bound tracks"), worldFrameCount: integer(metrics.world_frame_count, "E46C world frames"), worldObservationCount: integer(metrics.world_observation_count, "E46C world observations"), worldCurrentObservationCount: integer(metrics.world_current_observation_count, "E46C current world"), worldHeldObservationCount: integer(metrics.world_held_observation_count, "E46C held world"), visualSampleFrameCount: exact(metrics.visual_sample_frame_count, 32, "E46C samples"), sampleObjectCount: integer(metrics.sample_object_count, "E46C sample objects"), sampleMatchedRouteObjectCount: integer(metrics.sample_matched_route_object_count, "E46C matched"), sampleUnmatchedObjectCount: integer(metrics.sample_unmatched_object_count, "E46C unmatched"), sampleWorldFrameCount: integer(metrics.sample_world_frame_count, "E46C sample world frames"), motionObservationCounts: { dynamic: integer(counts.dynamic, "E46C dynamic"), static: integer(counts.static, "E46C static"), unknown: integer(counts.unknown, "E46C unknown") } }, acceptance: { benchmarkPassedEvents: exact(acceptance.benchmark_passed_events, 11, "E46C passed events"), benchmarkTotalEvents: exact(acceptance.benchmark_total_events, 11, "E46C total events") }, decision: { routeTrackLayerAvailable: exact(decision.route_track_layer_available, true, "E46C route layer"), worldOccupiedLayerAvailable: exact(decision.world_occupied_layer_available, true, "E46C world layer"), unknownRemainsOccupied: exact(decision.unknown_remains_occupied, true, "E46C unknown"), freeSpaceAvailable: exact(decision.free_space_available, false, "E46C free space"), nextAction: string(decision.next_action, "E46C next") }, limitations: array(item.limitations, "E46C limitations").map((value) => string(value, "E46C limitation")) };
}
export async function fetchE46CCase(resultId: string, sequence: number, { fetcher = fetch, signal }: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {}): Promise<E46CCase> {
const response = await fetcher(`/api/v1/laboratory/e46c/results/${resultId}/cases/${sequence}`, { headers: { Accept: "application/json" }, signal }); if (!response.ok) throw new ContractError(`E46C frame недоступен: HTTP ${response.status}.`);
const item = object(await response.json(), "E46C case"); exact(item.schema_version, "missioncore.e46c-full-replay-world-track-case/v1", "E46C case schema");
const objects = array(item.objects, "E46C objects").map((raw): E46CCameraObject => { const value = object(raw, "E46C object"); const category = string(value.category, "E46C category"); const box = vector(value.box_xyxy, 4, "E46C box") as [number, number, number, number]; return { objectId: string(value.object_id, "E46C object id"), category, displayCategory: displayCategory(category), boxXyxy: box, routeTrackId: optionalInteger(value.route_track_id, "E46C route track"), worldTrackId: optionalInteger(value.world_track_id, "E46C world track"), motionState: motionState(value.motion_state) }; });
const worldObjects = array(item.world_objects, "E46C world objects").map((raw): E46CWorldObject => { const value = object(raw, "E46C world object"); return { worldTrackId: integer(value.world_track_id, "E46C world id"), routeTrackId: integer(value.route_track_id, "E46C route id"), category: string(value.category, "E46C world category"), motionState: motionState(value.motion_state), positionMapM: vector(value.position_map_m, 3, "E46C position") as [number, number, number], occupancyFootprintMapXy: array(value.occupancy_footprint_map_xy, "E46C footprint").map((point) => vector(point, 2, "E46C footprint point") as [number, number]), occupancyEvidenceCurrent: exact(value.occupancy_evidence_current, Boolean(value.occupancy_evidence_current), "E46C current") as boolean, occupancyCellCount: integer(value.occupancy_cell_count, "E46C cells") }; });
return { resultId: exact(item.result_id, resultId, "E46C case result"), truthIslandSequence: integer(item.truth_island_sequence, "E46C sequence"), frameIndex: integer(item.frame_index, "E46C frame"), groupId: string(item.group_id, "E46C group"), sessionSeconds: number(item.session_seconds, "E46C time"), sourceImageSha256: string(item.source_image_sha256, "E46C image hash"), fusionState: string(item.fusion_state, "E46C fusion"), objects, objectCount: integer(item.object_count, "E46C object count"), matchedRouteObjectCount: integer(item.matched_route_object_count, "E46C matched count"), worldObjects, worldObjectCount: integer(item.world_object_count, "E46C world count"), cameraUrl: string(item.camera_url, "E46C camera URL") };
}
export async function fetchE46CVideoOverlay(
resultId: string,
{ fetcher = fetch, signal }: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
): Promise<E46CVideoOverlay> {
const response = await fetcher(
`/api/v1/laboratory/e46c/results/${resultId}/video-overlay`,
{ headers: { Accept: "application/json" }, signal },
);
if (!response.ok) {
throw new ContractError(`E46C video overlay недоступен: HTTP ${response.status}.`);
}
const payload = object(await response.json(), "E46C video overlay");
exact(
payload.schema_version,
"missioncore.e46c-recorded-video-overlay/v1",
"E46C video overlay schema",
);
exact(payload.result_id, resultId, "E46C video result");
exact(payload.ground_truth, false, "E46C video truth");
const recordedSource = object(payload.recorded_source, "E46C recorded source");
exact(recordedSource.source_id, "sensor.camera.right", "E46C video source");
exact(
recordedSource.synchronization,
"host-arrival-best-effort",
"E46C video synchronization",
);
const inputSha256 = string(recordedSource.input_sha256, "E46C video input hash");
if (!/^[a-f0-9]{64}$/.test(inputSha256)) throw new ContractError("E46C video input hash.");
const frames = array(payload.frames, "E46C video frames").map(
(raw, frameSequence): E46CVideoFrame => {
const frame = object(raw, "E46C video frame");
const frameIndex = integer(frame.frame_index, "E46C video frame index");
if (frameIndex !== frameSequence) throw new ContractError("E46C video frame order.");
return {
frameIndex,
sessionSeconds: number(frame.session_seconds, "E46C video frame time"),
fusionState: string(frame.fusion_state, "E46C video fusion state"),
objects: array(frame.objects, "E46C video objects").map((rawObject) => {
const item = object(rawObject, "E46C video object");
const category = string(item.category, "E46C video category");
return {
boxXyxy: vector(item.bbox_xyxy, 4, "E46C video box") as [number, number, number, number],
category,
displayCategory: displayCategory(category),
score: number(item.score, "E46C video score"),
routeTrackId: integer(item.route_track_id, "E46C video route track"),
worldTrackId: optionalInteger(item.world_track_id, "E46C video world track"),
motionState: motionState(item.motion_state),
motionConfidence: number(item.motion_confidence, "E46C video motion confidence"),
trackHits: integer(item.track_hits, "E46C video track hits"),
trackAgeSeconds: number(item.track_age_seconds, "E46C video track age"),
cameraEvidenceCurrent: exact(
item.camera_evidence_current,
Boolean(item.camera_evidence_current),
"E46C camera evidence",
) as boolean,
worldEvidenceCurrent: exact(
item.world_evidence_current,
Boolean(item.world_evidence_current),
"E46C world evidence",
) as boolean,
};
}),
};
},
);
const reviewWindows = array(payload.review_windows, "E46C video review windows").map(
(raw): E46CVideoReviewWindow => {
const window = object(raw, "E46C video review window");
return {
id: string(window.id, "E46C video review id"),
kind: string(window.kind, "E46C video review kind"),
classGroup: string(window.class_group, "E46C video review class"),
startSeconds: number(window.start_seconds, "E46C video review start"),
endSeconds: number(window.end_seconds, "E46C video review end"),
targetSourceTrackIds: array(
window.target_source_track_ids,
"E46C video review targets",
).map((value) => integer(value, "E46C video review target")),
};
},
);
const timelineStartSeconds = number(
payload.timeline_start_seconds,
"E46C video timeline start",
);
const timelineEndSeconds = number(
payload.timeline_end_seconds,
"E46C video timeline end",
);
if (
frames.length !== 4489 ||
exact(payload.frame_count, 4489, "E46C video frame count") !== frames.length ||
frames[0]?.sessionSeconds !== timelineStartSeconds ||
frames.at(-1)?.sessionSeconds !== timelineEndSeconds
) {
throw new ContractError("E46C video timeline coverage.");
}
return {
resultId,
recordedSourceSessionId: string(
recordedSource.session_id,
"E46C recorded source session",
),
recordedSourceId: "sensor.camera.right",
inputSha256,
imageWidth: exact(payload.image_width, 800, "E46C video width"),
imageHeight: exact(payload.image_height, 600, "E46C video height"),
timelineStartSeconds,
timelineEndSeconds,
frameCount: 4489,
frames,
reviewWindows,
};
}
export function selectE46CVideoFrame(
frames: readonly E46CVideoFrame[],
sessionSeconds: number,
): E46CVideoFrame | null {
if (!frames.length || !Number.isFinite(sessionSeconds)) return null;
let low = 0;
let high = frames.length - 1;
while (low <= high) {
const middle = Math.floor((low + high) / 2);
const frame = frames[middle];
if (!frame) break;
if (frame.sessionSeconds <= sessionSeconds) low = middle + 1;
else high = middle - 1;
}
const before = frames[Math.max(0, high)] ?? null;
const after = frames[Math.min(frames.length - 1, low)] ?? null;
if (!before) return after;
if (!after) return before;
return Math.abs(before.sessionSeconds - sessionSeconds) <=
Math.abs(after.sessionSeconds - sessionSeconds)
? before
: after;
}
@@ -0,0 +1,291 @@
export type E46DTemporalSignalKind =
| "layer-blackout"
| "camera-evidence-hold"
| "route-layer-gap"
| "route-id-rebirth-candidate"
| "bbox-jump"
| "motion-state-flap"
| "world-binding-flap"
| "short-track-burst";
export type E46DTemporalPriority = "critical" | "high" | "medium";
export interface E46DReviewClip {
clipId: string;
rank: number;
priority: E46DTemporalPriority;
kind: E46DTemporalSignalKind;
signalId: string;
startSeconds: number;
eventStartSeconds: number;
eventEndSeconds: number;
endSeconds: number;
startFrame: number;
endFrame: number;
routeTrackIds: readonly number[];
worldTrackIds: readonly number[];
evidence: Readonly<Record<string, string | number | boolean>>;
}
export interface E46DTemporalFailureAuditResult {
resultId: string;
createdAtUtc: string;
sourceE46CResultId: string;
sourceE26ResultId: string;
metrics: {
routeFrameCount: 4489;
routeSpanSeconds: number;
objectObservationCount: number;
routeTrackCount: number;
zeroObjectFrameCount: number;
zeroObjectFrameFraction: number;
cameraHeldObservationCount: number;
cameraHeldObservationFraction: number;
detectorHoldEpisodeCount: number;
layerBlackoutEpisodeCount: number;
routeLayerGapEpisodeCount: number;
routeIdRebirthCandidateCount: number;
bboxJumpEpisodeCount: number;
motionStateFlapEpisodeCount: number;
worldBindingFlapEpisodeCount: number;
shortRouteTrackCount: number;
shortRouteTrackFraction: number;
shortTrackBurstEpisodeCount: number;
failureSignalCount: number;
reviewClipCount: number;
temporalContinuityPassed: boolean;
};
acceptance: {
fullRouteAccounted: true;
temporalContinuityPassed: boolean;
independentTruthAvailable: false;
navigationOrSafetyAccepted: false;
};
decision: {
temporalRegressionConfirmed: boolean;
detectorGapVisible: boolean;
routeFragmentationVisible: boolean;
worldBindingInstabilityVisible: boolean;
nextAction: string;
};
limitations: readonly string[];
reviewClips: readonly E46DReviewClip[];
}
type LaboratoryFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
class ContractError extends Error {}
function object(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new ContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function array(value: unknown, label: string): readonly unknown[] {
if (!Array.isArray(value)) throw new ContractError(`${label}: ожидался массив.`);
return value;
}
function string(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new ContractError(`${label}: ожидалась строка.`);
}
return value;
}
function number(value: unknown, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new ContractError(`${label}: ожидалось число.`);
}
return value;
}
function integer(value: unknown, label: string): number {
const parsed = number(value, label);
if (!Number.isInteger(parsed) || parsed < 0) {
throw new ContractError(`${label}: ожидалось неотрицательное целое.`);
}
return parsed;
}
function exact<T extends string | number | boolean>(
value: unknown,
expected: T,
label: string,
): T {
if (value !== expected) throw new ContractError(`${label}: нарушен контракт.`);
return expected;
}
function boolean(value: unknown, label: string): boolean {
if (typeof value !== "boolean") throw new ContractError(`${label}: ожидался boolean.`);
return value;
}
function priority(value: unknown): E46DTemporalPriority {
if (value !== "critical" && value !== "high" && value !== "medium") {
throw new ContractError("E46D priority: неизвестное значение.");
}
return value;
}
function kind(value: unknown): E46DTemporalSignalKind {
if (
value !== "layer-blackout"
&& value !== "camera-evidence-hold"
&& value !== "route-layer-gap"
&& value !== "route-id-rebirth-candidate"
&& value !== "bbox-jump"
&& value !== "motion-state-flap"
&& value !== "world-binding-flap"
&& value !== "short-track-burst"
) {
throw new ContractError("E46D signal kind: неизвестное значение.");
}
return value;
}
function shaResult(value: unknown, prefix: string, label: string): string {
const parsed = string(value, label);
if (!new RegExp(`^${prefix}-[a-f0-9]{64}$`).test(parsed)) {
throw new ContractError(`${label}: нарушена идентичность.`);
}
return parsed;
}
function evidence(value: unknown): Readonly<Record<string, string | number | boolean>> {
const raw = object(value, "E46D clip evidence");
const parsed: Record<string, string | number | boolean> = {};
for (const [key, item] of Object.entries(raw)) {
if (
typeof item !== "string"
&& typeof item !== "number"
&& typeof item !== "boolean"
) {
throw new ContractError("E46D clip evidence: неизвестное значение.");
}
parsed[key] = item;
}
return parsed;
}
function parseClip(value: unknown, expectedRank: number): E46DReviewClip {
const item = object(value, "E46D review clip");
exact(item.schema_version, "missioncore.e46d-temporal-review-clip/v1", "E46D clip schema");
const rank = integer(item.rank, "E46D clip rank");
if (rank !== expectedRank) throw new ContractError("E46D clip order: нарушен контракт.");
const clipId = string(item.clip_id, "E46D clip id");
const signalId = string(item.signal_id, "E46D signal id");
if (!/^e46d-clip-\d{2}-[a-f0-9]{20}$/.test(clipId) || !/^e46d-signal-[a-f0-9]{20}$/.test(signalId)) {
throw new ContractError("E46D clip identity: нарушен контракт.");
}
const startSeconds = number(item.start_seconds, "E46D clip start");
const eventStartSeconds = number(item.event_start_seconds, "E46D event start");
const eventEndSeconds = number(item.event_end_seconds, "E46D event end");
const endSeconds = number(item.end_seconds, "E46D clip end");
if (!(startSeconds <= eventStartSeconds && eventStartSeconds <= eventEndSeconds && eventEndSeconds <= endSeconds)) {
throw new ContractError("E46D clip timeline: нарушен контракт.");
}
return {
clipId,
rank,
priority: priority(item.priority),
kind: kind(item.kind),
signalId,
startSeconds,
eventStartSeconds,
eventEndSeconds,
endSeconds,
startFrame: integer(item.start_frame, "E46D clip start frame"),
endFrame: integer(item.end_frame, "E46D clip end frame"),
routeTrackIds: array(item.route_track_ids, "E46D clip route tracks").map((entry) => integer(entry, "E46D route track")),
worldTrackIds: array(item.world_track_ids, "E46D clip world tracks").map((entry) => integer(entry, "E46D world track")),
evidence: evidence(item.evidence),
};
}
export async function fetchE46DTemporalFailureAudit({
fetcher = fetch,
signal,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
} = {}): Promise<E46DTemporalFailureAuditResult | null> {
const response = await fetcher("/api/v1/laboratory/e46d/results?limit=1", {
headers: { Accept: "application/json" },
signal,
});
if (!response.ok) throw new ContractError(`E46D LAB недоступен: HTTP ${response.status}.`);
const catalog = object(await response.json(), "E46D catalog");
exact(
catalog.schema_version,
"missioncore.e46d-temporal-failure-audit-catalog/v1",
"E46D catalog schema",
);
const items = array(catalog.items, "E46D results");
if (!items.length) return null;
if (items.length !== 1) throw new ContractError("E46D catalog size: нарушен контракт.");
const item = object(items[0], "E46D result");
exact(
item.schema_version,
"missioncore.e46d-temporal-failure-audit-view/v1",
"E46D view schema",
);
exact(item.ground_truth, false, "E46D truth authority");
const metrics = object(item.metrics, "E46D metrics");
const acceptance = object(item.acceptance, "E46D acceptance");
const decision = object(item.decision, "E46D decision");
const clips = array(item.review_clips, "E46D review clips").map((clip, index) => (
parseClip(clip, index + 1)
));
const result: E46DTemporalFailureAuditResult = {
resultId: shaResult(item.result_id, "e46d-temporal-failure-audit", "E46D result id"),
createdAtUtc: string(item.created_at_utc, "E46D created"),
sourceE46CResultId: shaResult(item.source_e46c_result_id, "e46c-full-replay-world-tracks", "E46D E46C source"),
sourceE26ResultId: shaResult(item.source_e26_result_id, "e10-integrated-perception", "E46D E26 source"),
metrics: {
routeFrameCount: exact(metrics.route_frame_count, 4489, "E46D route frames"),
routeSpanSeconds: number(metrics.route_span_seconds, "E46D route span"),
objectObservationCount: integer(metrics.object_observation_count, "E46D observations"),
routeTrackCount: integer(metrics.route_track_count, "E46D route tracks"),
zeroObjectFrameCount: integer(metrics.zero_object_frame_count, "E46D zero frames"),
zeroObjectFrameFraction: number(metrics.zero_object_frame_fraction, "E46D zero fraction"),
cameraHeldObservationCount: integer(metrics.camera_held_observation_count, "E46D held observations"),
cameraHeldObservationFraction: number(metrics.camera_held_observation_fraction, "E46D held fraction"),
detectorHoldEpisodeCount: integer(metrics.detector_hold_episode_count, "E46D detector holds"),
layerBlackoutEpisodeCount: integer(metrics.layer_blackout_episode_count, "E46D blackouts"),
routeLayerGapEpisodeCount: integer(metrics.route_layer_gap_episode_count, "E46D route gaps"),
routeIdRebirthCandidateCount: integer(metrics.route_id_rebirth_candidate_count, "E46D ID candidates"),
bboxJumpEpisodeCount: integer(metrics.bbox_jump_episode_count, "E46D bbox jumps"),
motionStateFlapEpisodeCount: integer(metrics.motion_state_flap_episode_count, "E46D motion flaps"),
worldBindingFlapEpisodeCount: integer(metrics.world_binding_flap_episode_count, "E46D world flaps"),
shortRouteTrackCount: integer(metrics.short_route_track_count, "E46D short tracks"),
shortRouteTrackFraction: number(metrics.short_route_track_fraction, "E46D short fraction"),
shortTrackBurstEpisodeCount: integer(metrics.short_track_burst_episode_count, "E46D short bursts"),
failureSignalCount: integer(metrics.failure_signal_count, "E46D failure signals"),
reviewClipCount: integer(metrics.review_clip_count, "E46D review clips"),
temporalContinuityPassed: boolean(metrics.temporal_continuity_passed, "E46D continuity"),
},
acceptance: {
fullRouteAccounted: exact(acceptance.full_route_accounted, true, "E46D full route"),
temporalContinuityPassed: boolean(acceptance.temporal_continuity_passed, "E46D acceptance continuity"),
independentTruthAvailable: exact(acceptance.independent_truth_available, false, "E46D truth"),
navigationOrSafetyAccepted: exact(acceptance.navigation_or_safety_accepted, false, "E46D safety"),
},
decision: {
temporalRegressionConfirmed: boolean(decision.temporal_regression_confirmed, "E46D regression"),
detectorGapVisible: boolean(decision.detector_gap_visible, "E46D detector gap"),
routeFragmentationVisible: boolean(decision.route_fragmentation_visible, "E46D fragmentation"),
worldBindingInstabilityVisible: boolean(decision.world_binding_instability_visible, "E46D world binding"),
nextAction: string(decision.next_action, "E46D next action"),
},
limitations: array(item.limitations, "E46D limitations").map((value) => string(value, "E46D limitation")),
reviewClips: clips,
};
if (result.metrics.reviewClipCount !== clips.length) {
throw new ContractError("E46D review clip accounting: нарушен контракт.");
}
return result;
}
@@ -0,0 +1,219 @@
export interface E46ELaboratoryMethod {
completeness: "complete";
executionClass: "hybrid";
pipelineId: string;
components: readonly {
kind: "source" | "tool" | "model" | "algorithm" | "runtime";
name: string;
version: string;
role: string;
identitySha256: string;
}[];
}
export interface E46EReadyStackResult {
resultId: string;
createdAtUtc: string;
sourceSessionId: string;
cameraSourceId: "sensor.camera.right";
metrics: {
frameCount: 4489;
routeDurationSeconds: number;
detectionObservationCount: number;
trackObservationCount: number;
detectionBoxClippedCount: number;
trackBoxClippedCount: number;
uniqueTrackCount: number;
meanTrackedObjectsPerFrame: number;
zeroDetectionFrameCount: number;
zeroTrackFrameCount: number;
trackerRecoveredFrameCount: number;
fullLayerBlackoutEventCount: number;
routeIdGapEventCount: number;
shortTrackCount: number;
shortTrackFraction: number;
trackClassSwitchCount: number;
};
acceptance: {
fullRouteAccounted: true;
stockDetectorTrackerExecuted: true;
visualOverlayAvailable: true;
independentTruthAvailable: false;
navigationOrSafetyAccepted: false;
};
decision: {
readyStackBaselineAvailable: true;
customTemporalLogicUsed: false;
nextAction: string;
};
method: E46ELaboratoryMethod;
limitations: readonly string[];
video: {
url: string;
mediaType: "video/mp4";
byteLength: number;
sha256: string;
width: 800;
height: 600;
};
}
type LaboratoryFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
class ContractError extends Error {}
function object(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new ContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function array(value: unknown, label: string): readonly unknown[] {
if (!Array.isArray(value)) throw new ContractError(`${label}: ожидался массив.`);
return value;
}
function string(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new ContractError(`${label}: ожидалась строка.`);
}
return value;
}
function number(value: unknown, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new ContractError(`${label}: ожидалось число.`);
}
return value;
}
function integer(value: unknown, label: string): number {
const parsed = number(value, label);
if (!Number.isSafeInteger(parsed) || parsed < 0) {
throw new ContractError(`${label}: ожидалось неотрицательное целое.`);
}
return parsed;
}
function exact<T extends string | number | boolean>(
value: unknown,
expected: T,
label: string,
): T {
if (value !== expected) throw new ContractError(`${label}: нарушен контракт.`);
return expected;
}
function sha(value: unknown, label: string): string {
const parsed = string(value, label);
if (!/^[a-f0-9]{64}$/.test(parsed)) {
throw new ContractError(`${label}: нарушена SHA-256 идентичность.`);
}
return parsed;
}
function method(value: unknown): E46ELaboratoryMethod {
const raw = object(value, "E46E method");
exact(raw.schema_version, "missioncore.laboratory-method/v1", "E46E method schema");
exact(raw.completeness, "complete", "E46E method completeness");
exact(raw.execution_class, "hybrid", "E46E execution class");
const components = array(raw.components, "E46E method components").map((entry) => {
const component = object(entry, "E46E method component");
const kind = string(component.kind, "E46E component kind");
if (!(["source", "tool", "model", "algorithm", "runtime"] as const).includes(
kind as "source",
)) {
throw new ContractError("E46E component kind: неизвестное значение.");
}
return {
kind: kind as "source" | "tool" | "model" | "algorithm" | "runtime",
name: string(component.name, "E46E component name"),
version: string(component.version, "E46E component version"),
role: string(component.role, "E46E component role"),
identitySha256: sha(component.identity_sha256, "E46E component identity"),
};
});
return {
completeness: "complete",
executionClass: "hybrid",
pipelineId: string(raw.pipeline_id, "E46E pipeline"),
components,
};
}
export async function fetchE46EReadyStack({
fetcher = fetch,
signal,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
} = {}): Promise<E46EReadyStackResult | null> {
const response = await fetcher("/api/v1/laboratory/e46e/results?limit=1", {
headers: { Accept: "application/json" },
signal,
});
if (!response.ok) throw new ContractError(`E46E LAB недоступен: HTTP ${response.status}.`);
const catalog = object(await response.json(), "E46E catalog");
exact(catalog.schema_version, "missioncore.e46e-ready-stack-catalog/v1", "E46E catalog schema");
const items = array(catalog.items, "E46E results");
if (!items.length) return null;
if (items.length !== 1) throw new ContractError("E46E catalog size: нарушен контракт.");
const item = object(items[0], "E46E result");
exact(item.schema_version, "missioncore.e46e-ready-stack-view/v1", "E46E view schema");
exact(item.ground_truth, false, "E46E truth authority");
const metrics = object(item.metrics, "E46E metrics");
const acceptance = object(item.acceptance, "E46E acceptance");
const decision = object(item.decision, "E46E decision");
const video = object(item.video, "E46E video");
const resultId = string(item.result_id, "E46E result id");
if (!/^e46e-ready-stack-[a-f0-9]{64}$/.test(resultId)) {
throw new ContractError("E46E result identity: нарушен контракт.");
}
return {
resultId,
createdAtUtc: string(item.created_at_utc, "E46E created"),
sourceSessionId: string(item.source_session_id, "E46E source session"),
cameraSourceId: exact(item.camera_source_id, "sensor.camera.right", "E46E camera"),
metrics: {
frameCount: exact(metrics.frame_count, 4489, "E46E frames"),
routeDurationSeconds: number(metrics.route_duration_seconds, "E46E duration"),
detectionObservationCount: integer(metrics.detection_observation_count, "E46E detections"),
trackObservationCount: integer(metrics.track_observation_count, "E46E track observations"),
detectionBoxClippedCount: integer(metrics.detection_box_clipped_count, "E46E clipped detections"),
trackBoxClippedCount: integer(metrics.track_box_clipped_count, "E46E clipped tracks"),
uniqueTrackCount: integer(metrics.unique_track_count, "E46E tracks"),
meanTrackedObjectsPerFrame: number(metrics.mean_tracked_objects_per_frame, "E46E mean objects"),
zeroDetectionFrameCount: integer(metrics.zero_detection_frame_count, "E46E zero detections"),
zeroTrackFrameCount: integer(metrics.zero_track_frame_count, "E46E zero tracks"),
trackerRecoveredFrameCount: integer(metrics.tracker_recovered_frame_count, "E46E recovered frames"),
fullLayerBlackoutEventCount: integer(metrics.full_layer_blackout_event_count, "E46E blackouts"),
routeIdGapEventCount: integer(metrics.route_id_gap_event_count, "E46E ID gaps"),
shortTrackCount: integer(metrics.short_track_count, "E46E short tracks"),
shortTrackFraction: number(metrics.short_track_fraction, "E46E short fraction"),
trackClassSwitchCount: integer(metrics.track_class_switch_count, "E46E class switches"),
},
acceptance: {
fullRouteAccounted: exact(acceptance.full_route_accounted, true, "E46E full route"),
stockDetectorTrackerExecuted: exact(acceptance.stock_detector_tracker_executed, true, "E46E ready stack"),
visualOverlayAvailable: exact(acceptance.visual_overlay_available, true, "E46E visual"),
independentTruthAvailable: exact(acceptance.independent_truth_available, false, "E46E truth"),
navigationOrSafetyAccepted: exact(acceptance.navigation_or_safety_accepted, false, "E46E safety"),
},
decision: {
readyStackBaselineAvailable: exact(decision.ready_stack_baseline_available, true, "E46E baseline"),
customTemporalLogicUsed: exact(decision.custom_temporal_logic_used, false, "E46E custom logic"),
nextAction: string(decision.next_action, "E46E next action"),
},
method: method(item.method),
limitations: array(item.limitations, "E46E limitations").map((entry) => string(entry, "E46E limitation")),
video: {
url: string(video.url, "E46E video URL"),
mediaType: exact(video.media_type, "video/mp4", "E46E media type"),
byteLength: integer(video.byte_length, "E46E video bytes"),
sha256: sha(video.sha256, "E46E video SHA"),
width: exact(video.width, 800, "E46E video width"),
height: exact(video.height, 600, "E46E video height"),
},
};
}
@@ -0,0 +1,289 @@
import type { E46ELaboratoryMethod } from "./e46eReadyStack";
export interface E46FMetrics {
frameCount: 4489;
routeDurationSeconds: number;
detectionObservationCount: number;
trackObservationCount: number;
detectionBoxClippedCount: number;
trackBoxClippedCount: number;
uniqueTrackCount: number;
meanTrackedObjectsPerFrame: number;
zeroDetectionFrameCount: number;
zeroTrackFrameCount: number;
trackerRecoveredFrameCount: number;
fullLayerBlackoutEventCount: number;
routeIdGapEventCount: number;
shortTrackCount: number;
shortTrackFraction: number;
trackClassSwitchCount: number;
}
export interface E46FLargeBoxTriage {
observationCount: number;
frameCount: number;
trackIdCount: number;
classObservations: Readonly<Record<string, number>>;
}
export interface E46FDashCamBakeoffResult {
resultId: string;
createdAtUtc: string;
sourceSessionId: string;
cameraSourceId: "sensor.camera.right";
metrics: E46FMetrics;
acceptance: {
fullRouteAccounted: true;
stockDetectorTrackerExecuted: true;
controlledDetectorOnlyChange: true;
visualOverlayAvailable: true;
independentTruthAvailable: false;
navigationOrSafetyAccepted: false;
};
method: E46ELaboratoryMethod;
limitations: readonly string[];
comparison: {
baselineResultId: string;
baselineMetrics: E46FMetrics;
delta: {
zeroTrackFrameCount: number;
fullLayerBlackoutEventCount: number;
uniqueTrackCount: number;
shortTrackFraction: number;
};
largeBoxVisualTriage: {
areaRatioThreshold: 0.2;
candidate: E46FLargeBoxTriage;
baseline: E46FLargeBoxTriage;
interpretation: string;
};
visualReview: {
status: "rejected-semantic-regression";
sampleVideoSeconds: readonly number[];
finding: string;
nextAction: string;
};
verdict: "reject-dashcamnet-on-unrectified-fisheye";
};
video: {
url: string;
mediaType: "video/mp4";
byteLength: number;
sha256: string;
width: 800;
height: 600;
};
}
type LaboratoryFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
class ContractError extends Error {}
function object(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new ContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function array(value: unknown, label: string): readonly unknown[] {
if (!Array.isArray(value)) throw new ContractError(`${label}: ожидался массив.`);
return value;
}
function string(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new ContractError(`${label}: ожидалась строка.`);
}
return value;
}
function number(value: unknown, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new ContractError(`${label}: ожидалось число.`);
}
return value;
}
function integer(value: unknown, label: string): number {
const parsed = number(value, label);
if (!Number.isSafeInteger(parsed) || parsed < 0) {
throw new ContractError(`${label}: ожидалось неотрицательное целое.`);
}
return parsed;
}
function exact<T extends string | number | boolean>(
value: unknown,
expected: T,
label: string,
): T {
if (value !== expected) throw new ContractError(`${label}: нарушен контракт.`);
return expected;
}
function sha(value: unknown, label: string): string {
const parsed = string(value, label);
if (!/^[a-f0-9]{64}$/.test(parsed)) {
throw new ContractError(`${label}: нарушена SHA-256 идентичность.`);
}
return parsed;
}
function signedNumber(value: unknown, label: string): number {
return number(value, label);
}
function metrics(value: unknown, label: string): E46FMetrics {
const raw = object(value, label);
return {
frameCount: exact(raw.frame_count, 4489, `${label} frames`),
routeDurationSeconds: number(raw.route_duration_seconds, `${label} duration`),
detectionObservationCount: integer(raw.detection_observation_count, `${label} detections`),
trackObservationCount: integer(raw.track_observation_count, `${label} track observations`),
detectionBoxClippedCount: integer(raw.detection_box_clipped_count, `${label} clipped detections`),
trackBoxClippedCount: integer(raw.track_box_clipped_count, `${label} clipped tracks`),
uniqueTrackCount: integer(raw.unique_track_count, `${label} tracks`),
meanTrackedObjectsPerFrame: number(raw.mean_tracked_objects_per_frame, `${label} mean objects`),
zeroDetectionFrameCount: integer(raw.zero_detection_frame_count, `${label} zero detections`),
zeroTrackFrameCount: integer(raw.zero_track_frame_count, `${label} zero tracks`),
trackerRecoveredFrameCount: integer(raw.tracker_recovered_frame_count, `${label} recovered`),
fullLayerBlackoutEventCount: integer(raw.full_layer_blackout_event_count, `${label} blackouts`),
routeIdGapEventCount: integer(raw.route_id_gap_event_count, `${label} ID gaps`),
shortTrackCount: integer(raw.short_track_count, `${label} short tracks`),
shortTrackFraction: number(raw.short_track_fraction, `${label} short fraction`),
trackClassSwitchCount: integer(raw.track_class_switch_count, `${label} class switches`),
};
}
function method(value: unknown): E46ELaboratoryMethod {
const raw = object(value, "E46F method");
exact(raw.schema_version, "missioncore.laboratory-method/v1", "E46F method schema");
exact(raw.completeness, "complete", "E46F method completeness");
exact(raw.execution_class, "hybrid", "E46F execution class");
const components = array(raw.components, "E46F method components").map((entry) => {
const component = object(entry, "E46F method component");
const kind = string(component.kind, "E46F component kind");
if (!(["source", "tool", "model", "algorithm", "runtime"] as const).includes(
kind as "source",
)) {
throw new ContractError("E46F component kind: неизвестное значение.");
}
return {
kind: kind as "source" | "tool" | "model" | "algorithm" | "runtime",
name: string(component.name, "E46F component name"),
version: string(component.version, "E46F component version"),
role: string(component.role, "E46F component role"),
identitySha256: sha(component.identity_sha256, "E46F component identity"),
};
});
return {
completeness: "complete",
executionClass: "hybrid",
pipelineId: string(raw.pipeline_id, "E46F pipeline"),
components,
};
}
function classObservations(value: unknown, label: string): Readonly<Record<string, number>> {
const raw = object(value, label);
return Object.fromEntries(
Object.entries(raw).map(([key, entry]) => [key, integer(entry, `${label}.${key}`)]),
);
}
function triage(value: unknown, label: string): E46FLargeBoxTriage {
const raw = object(value, label);
return {
observationCount: integer(raw.observation_count, `${label} observations`),
frameCount: integer(raw.frame_count, `${label} frames`),
trackIdCount: integer(raw.track_id_count, `${label} tracks`),
classObservations: classObservations(raw.class_observations, `${label} classes`),
};
}
export async function fetchE46FDashCamBakeoff({
fetcher = fetch,
signal,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
} = {}): Promise<E46FDashCamBakeoffResult | null> {
const response = await fetcher("/api/v1/laboratory/e46f/results?limit=1", {
headers: { Accept: "application/json" },
signal,
});
if (!response.ok) throw new ContractError(`E46F LAB недоступен: HTTP ${response.status}.`);
const catalog = object(await response.json(), "E46F catalog");
exact(catalog.schema_version, "missioncore.e46f-dashcam-bakeoff-catalog/v1", "E46F catalog schema");
const items = array(catalog.items, "E46F results");
if (!items.length) return null;
if (items.length !== 1) throw new ContractError("E46F catalog size: нарушен контракт.");
const item = object(items[0], "E46F result");
exact(item.schema_version, "missioncore.e46f-dashcam-bakeoff-view/v1", "E46F view schema");
exact(item.ground_truth, false, "E46F truth authority");
const resultId = string(item.result_id, "E46F result id");
if (!/^e46f-dashcam-bakeoff-[a-f0-9]{64}$/.test(resultId)) {
throw new ContractError("E46F result identity: нарушен контракт.");
}
const acceptance = object(item.acceptance, "E46F acceptance");
const comparison = object(item.comparison, "E46F comparison");
exact(comparison.controlled_change, "detector-only", "E46F controlled change");
const delta = object(comparison.delta, "E46F delta");
const largeBox = object(comparison.large_box_visual_triage, "E46F large-box triage");
const visualReview = object(comparison.visual_review, "E46F visual review");
const video = object(item.video, "E46F video");
const baselineResultId = string(comparison.baseline_result_id, "E46F baseline id");
if (!/^e46e-ready-stack-[a-f0-9]{64}$/.test(baselineResultId)) {
throw new ContractError("E46F baseline identity: нарушен контракт.");
}
return {
resultId,
createdAtUtc: string(item.created_at_utc, "E46F created"),
sourceSessionId: string(item.source_session_id, "E46F source session"),
cameraSourceId: exact(item.camera_source_id, "sensor.camera.right", "E46F camera"),
metrics: metrics(item.metrics, "E46F metrics"),
acceptance: {
fullRouteAccounted: exact(acceptance.full_route_accounted, true, "E46F full route"),
stockDetectorTrackerExecuted: exact(acceptance.stock_detector_tracker_executed, true, "E46F stack"),
controlledDetectorOnlyChange: exact(acceptance.controlled_detector_only_change, true, "E46F controlled change"),
visualOverlayAvailable: exact(acceptance.visual_overlay_available, true, "E46F visual"),
independentTruthAvailable: exact(acceptance.independent_truth_available, false, "E46F truth"),
navigationOrSafetyAccepted: exact(acceptance.navigation_or_safety_accepted, false, "E46F safety"),
},
method: method(item.method),
limitations: array(item.limitations, "E46F limitations").map((entry) => string(entry, "E46F limitation")),
comparison: {
baselineResultId,
baselineMetrics: metrics(comparison.baseline_metrics, "E46E baseline metrics"),
delta: {
zeroTrackFrameCount: signedNumber(delta.zero_track_frame_count, "E46F zero-track delta"),
fullLayerBlackoutEventCount: signedNumber(delta.full_layer_blackout_event_count, "E46F blackout delta"),
uniqueTrackCount: signedNumber(delta.unique_track_count, "E46F track delta"),
shortTrackFraction: signedNumber(delta.short_track_fraction, "E46F short-track delta"),
},
largeBoxVisualTriage: {
areaRatioThreshold: exact(largeBox.area_ratio_threshold, 0.2, "E46F triage threshold"),
candidate: triage(largeBox.candidate, "E46F candidate triage"),
baseline: triage(largeBox.baseline, "E46E baseline triage"),
interpretation: string(largeBox.interpretation, "E46F triage interpretation"),
},
visualReview: {
status: exact(visualReview.status, "rejected-semantic-regression", "E46F visual status"),
sampleVideoSeconds: array(visualReview.sample_video_seconds, "E46F review timestamps")
.map((entry) => number(entry, "E46F review timestamp")),
finding: string(visualReview.finding, "E46F visual finding"),
nextAction: string(visualReview.next_action, "E46F next action"),
},
verdict: exact(comparison.verdict, "reject-dashcamnet-on-unrectified-fisheye", "E46F verdict"),
},
video: {
url: string(video.url, "E46F video URL"),
mediaType: exact(video.media_type, "video/mp4", "E46F media type"),
byteLength: integer(video.byte_length, "E46F video bytes"),
sha256: sha(video.sha256, "E46F video SHA"),
width: exact(video.width, 800, "E46F video width"),
height: exact(video.height, 600, "E46F video height"),
},
};
}
@@ -0,0 +1,327 @@
import type { E46ELaboratoryMethod } from "./e46eReadyStack";
export type E46GCandidate = "trafficcamnet" | "dashcamnet";
export type E46GView = "left" | "front" | "right";
export interface E46GViewMetrics {
frameCount: 600;
detectionObservationCount: number;
trackObservationCount: number;
uniqueTrackCount: number;
meanTrackedObjectsPerFrame: number;
zeroDetectionFrameCount: number;
zeroTrackFrameCount: number;
fullLayerBlackoutEventCount: number;
shortTrackFraction: number;
largeTrackObservationCount: number;
largeTrackFraction: number;
}
export interface E46GCandidateMetrics {
sourceFrameCount: 600;
viewFrameCount: 1800;
detectionObservationCount: number;
trackObservationCount: number;
uniqueTrackCount: number;
largeTrackObservationCount: number;
largeTrackFraction: number;
views: Readonly<Record<E46GView, E46GViewMetrics>>;
}
export interface E46GVideo {
url: string;
mediaType: "video/mp4";
byteLength: number;
sha256: string;
width: 2880;
height: 544;
durationSeconds: 60;
viewOrder: readonly ["left", "front", "right"];
}
export interface E46GRectifiedDetectorBakeoffResult {
resultId: string;
createdAtUtc: string;
sourceSessionId: string;
cameraSourceId: "sensor.camera.right";
selection: {
firstSourceFrameIndex: 1000;
lastSourceFrameIndex: 1599;
frameCount: 600;
};
rectification: {
provider: "NVIDIA Gst-nvdewarper";
providerVersion: string;
outputResolution: readonly [960, 544];
horizontalFovDegrees: 100;
retainedSourceFrameIndexRange: readonly [0, 4487];
excludedSourceTailFrameCount: 1;
viewOrder: readonly ["left", "front", "right"];
};
metrics: Readonly<Record<E46GCandidate, E46GCandidateMetrics>>;
acceptance: {
exactRecordedRightSourceBound: true;
factoryCalibrationBound: true;
officialNvidiaDewarperExecuted: true;
stockDetectorTrackerExecuted: true;
sameViewsAndFramesForBothCandidates: true;
visualComparisonVideosAvailable: true;
independentTruthAvailable: false;
candidateAccepted: false;
navigationOrSafetyAccepted: false;
};
method: E46ELaboratoryMethod;
limitations: readonly string[];
comparison: {
visualReview: {
status: "selected-for-next-diagnostic";
reviewedVideoSeconds: readonly number[];
selectedCandidate: "trafficcamnet";
selectedView: "front";
excludedViews: readonly ["left", "right"];
finding: string;
risk: string;
nextAction: string;
};
verdict: "select-trafficcamnet-front-only-for-e46h";
};
videos: Readonly<Record<E46GCandidate, E46GVideo>>;
}
type LaboratoryFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
class ContractError extends Error {}
function object(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new ContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function array(value: unknown, label: string): readonly unknown[] {
if (!Array.isArray(value)) throw new ContractError(`${label}: ожидался массив.`);
return value;
}
function string(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new ContractError(`${label}: ожидалась строка.`);
}
return value;
}
function number(value: unknown, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new ContractError(`${label}: ожидалось число.`);
}
return value;
}
function integer(value: unknown, label: string): number {
const parsed = number(value, label);
if (!Number.isSafeInteger(parsed) || parsed < 0) {
throw new ContractError(`${label}: ожидалось неотрицательное целое.`);
}
return parsed;
}
function exact<T extends string | number | boolean>(
value: unknown,
expected: T,
label: string,
): T {
if (value !== expected) throw new ContractError(`${label}: нарушен контракт.`);
return expected;
}
function sha(value: unknown, label: string): string {
const parsed = string(value, label);
if (!/^[a-f0-9]{64}$/.test(parsed)) {
throw new ContractError(`${label}: нарушена SHA-256 идентичность.`);
}
return parsed;
}
function tuple<T extends readonly unknown[]>(
value: unknown,
expected: T,
label: string,
): T {
const parsed = array(value, label);
if (parsed.length !== expected.length || parsed.some((entry, index) => entry !== expected[index])) {
throw new ContractError(`${label}: нарушен контракт.`);
}
return expected;
}
function viewMetrics(value: unknown, label: string): E46GViewMetrics {
const raw = object(value, label);
return {
frameCount: exact(raw.frame_count, 600, `${label} frames`),
detectionObservationCount: integer(raw.detection_observation_count, `${label} detections`),
trackObservationCount: integer(raw.track_observation_count, `${label} tracks`),
uniqueTrackCount: integer(raw.unique_track_count, `${label} identities`),
meanTrackedObjectsPerFrame: number(raw.mean_tracked_objects_per_frame, `${label} mean`),
zeroDetectionFrameCount: integer(raw.zero_detection_frame_count, `${label} zero detections`),
zeroTrackFrameCount: integer(raw.zero_track_frame_count, `${label} zero tracks`),
fullLayerBlackoutEventCount: integer(raw.full_layer_blackout_event_count, `${label} blackouts`),
shortTrackFraction: number(raw.short_track_fraction, `${label} short fraction`),
largeTrackObservationCount: integer(raw.large_track_observation_count, `${label} large tracks`),
largeTrackFraction: number(raw.large_track_fraction, `${label} large fraction`),
};
}
function candidateMetrics(value: unknown, label: string): E46GCandidateMetrics {
const raw = object(value, label);
const views = object(raw.views, `${label} views`);
return {
sourceFrameCount: exact(raw.source_frame_count, 600, `${label} source frames`),
viewFrameCount: exact(raw.view_frame_count, 1800, `${label} view frames`),
detectionObservationCount: integer(raw.detection_observation_count, `${label} detections`),
trackObservationCount: integer(raw.track_observation_count, `${label} tracks`),
uniqueTrackCount: integer(raw.unique_track_count, `${label} identities`),
largeTrackObservationCount: integer(raw.large_track_observation_count, `${label} large tracks`),
largeTrackFraction: number(raw.large_track_fraction, `${label} large fraction`),
views: {
left: viewMetrics(views.left, `${label} left`),
front: viewMetrics(views.front, `${label} front`),
right: viewMetrics(views.right, `${label} right`),
},
};
}
function method(value: unknown): E46ELaboratoryMethod {
const raw = object(value, "E46G method");
exact(raw.schema_version, "missioncore.laboratory-method/v1", "E46G method schema");
exact(raw.completeness, "complete", "E46G method completeness");
exact(raw.execution_class, "hybrid", "E46G execution class");
const components = array(raw.components, "E46G method components").map((entry) => {
const component = object(entry, "E46G method component");
const kind = string(component.kind, "E46G component kind");
if (!("source tool model algorithm runtime".split(" ")).includes(kind)) {
throw new ContractError("E46G component kind: неизвестное значение.");
}
return {
kind: kind as "source" | "tool" | "model" | "algorithm" | "runtime",
name: string(component.name, "E46G component name"),
version: string(component.version, "E46G component version"),
role: string(component.role, "E46G component role"),
identitySha256: sha(component.identity_sha256, "E46G component identity"),
};
});
return {
completeness: "complete",
executionClass: "hybrid",
pipelineId: string(raw.pipeline_id, "E46G pipeline"),
components,
};
}
function video(value: unknown, label: string): E46GVideo {
const raw = object(value, label);
return {
url: string(raw.url, `${label} URL`),
mediaType: exact(raw.media_type, "video/mp4", `${label} media`),
byteLength: integer(raw.byte_length, `${label} bytes`),
sha256: sha(raw.sha256, `${label} SHA`),
width: exact(raw.width, 2880, `${label} width`),
height: exact(raw.height, 544, `${label} height`),
durationSeconds: exact(raw.duration_seconds, 60, `${label} duration`),
viewOrder: tuple(raw.view_order, ["left", "front", "right"] as const, `${label} views`),
};
}
export async function fetchE46GRectifiedDetectorBakeoff({
fetcher = fetch,
signal,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
} = {}): Promise<E46GRectifiedDetectorBakeoffResult | null> {
const response = await fetcher("/api/v1/laboratory/e46g/results?limit=1", {
headers: { Accept: "application/json" },
signal,
});
if (!response.ok) throw new ContractError(`E46G LAB недоступен: HTTP ${response.status}.`);
const catalog = object(await response.json(), "E46G catalog");
exact(catalog.schema_version, "missioncore.e46g-rectified-detector-bakeoff-catalog/v1", "E46G catalog schema");
const items = array(catalog.items, "E46G results");
if (!items.length) return null;
if (items.length !== 1) throw new ContractError("E46G catalog size: нарушен контракт.");
const item = object(items[0], "E46G result");
exact(item.schema_version, "missioncore.e46g-rectified-detector-bakeoff-view/v1", "E46G view schema");
exact(item.status, "selected-for-next-diagnostic-full-route", "E46G status");
exact(item.ground_truth, false, "E46G truth authority");
const resultId = string(item.result_id, "E46G result id");
if (!/^e46g-rectified-detector-bakeoff-[a-f0-9]{64}$/.test(resultId)) {
throw new ContractError("E46G result identity: нарушен контракт.");
}
const selection = object(item.selection, "E46G selection");
const rectification = object(item.rectification, "E46G rectification");
const metrics = object(item.metrics, "E46G metrics");
const acceptance = object(item.acceptance, "E46G acceptance");
const comparison = object(item.comparison, "E46G comparison");
const review = object(comparison.visual_review, "E46G visual review");
const videos = object(item.videos, "E46G videos");
const authority = object(item.authority, "E46G authority");
exact(authority.ground_truth, false, "E46G authority ground truth");
exact(authority.independent_truth, false, "E46G authority independent truth");
exact(authority.candidate_accepted, false, "E46G authority candidate");
exact(authority.commands_enabled, false, "E46G authority commands");
exact(authority.navigation_or_safety_accepted, false, "E46G authority safety");
return {
resultId,
createdAtUtc: string(item.created_at_utc, "E46G created"),
sourceSessionId: string(item.source_session_id, "E46G source session"),
cameraSourceId: exact(item.camera_source_id, "sensor.camera.right", "E46G camera"),
selection: {
firstSourceFrameIndex: exact(selection.first_source_frame_index, 1000, "E46G first frame"),
lastSourceFrameIndex: exact(selection.last_source_frame_index, 1599, "E46G last frame"),
frameCount: exact(selection.frame_count, 600, "E46G frame count"),
},
rectification: {
provider: exact(rectification.provider, "NVIDIA Gst-nvdewarper", "E46G dewarper"),
providerVersion: string(rectification.provider_version, "E46G dewarper version"),
outputResolution: tuple(rectification.output_resolution, [960, 544] as const, "E46G output"),
horizontalFovDegrees: exact(rectification.horizontal_fov_degrees, 100, "E46G FOV"),
retainedSourceFrameIndexRange: tuple(rectification.retained_source_frame_index_range, [0, 4487] as const, "E46G retained source"),
excludedSourceTailFrameCount: exact(rectification.excluded_source_tail_frame_count, 1, "E46G excluded tail"),
viewOrder: tuple(rectification.view_order, ["left", "front", "right"] as const, "E46G views"),
},
metrics: {
trafficcamnet: candidateMetrics(metrics.trafficcamnet, "TrafficCamNet"),
dashcamnet: candidateMetrics(metrics.dashcamnet, "DashCamNet"),
},
acceptance: {
exactRecordedRightSourceBound: exact(acceptance.exact_recorded_right_source_bound, true, "E46G source"),
factoryCalibrationBound: exact(acceptance.factory_calibration_bound, true, "E46G calibration"),
officialNvidiaDewarperExecuted: exact(acceptance.official_nvidia_dewarper_executed, true, "E46G dewarper execution"),
stockDetectorTrackerExecuted: exact(acceptance.stock_detector_tracker_executed, true, "E46G stock stack"),
sameViewsAndFramesForBothCandidates: exact(acceptance.same_views_and_frames_for_both_candidates, true, "E46G controlled A/B"),
visualComparisonVideosAvailable: exact(acceptance.visual_comparison_videos_available, true, "E46G videos"),
independentTruthAvailable: exact(acceptance.independent_truth_available, false, "E46G truth"),
candidateAccepted: exact(acceptance.candidate_accepted, false, "E46G acceptance authority"),
navigationOrSafetyAccepted: exact(acceptance.navigation_or_safety_accepted, false, "E46G safety"),
},
method: method(item.method),
limitations: array(item.limitations, "E46G limitations").map((entry) => string(entry, "E46G limitation")),
comparison: {
visualReview: {
status: exact(review.status, "selected-for-next-diagnostic", "E46G review status"),
reviewedVideoSeconds: array(review.reviewed_video_seconds, "E46G review seconds").map((entry) => number(entry, "E46G review second")),
selectedCandidate: exact(review.selected_candidate, "trafficcamnet", "E46G candidate"),
selectedView: exact(review.selected_view, "front", "E46G selected view"),
excludedViews: tuple(review.excluded_views, ["left", "right"] as const, "E46G excluded views"),
finding: string(review.finding, "E46G finding"),
risk: string(review.risk, "E46G risk"),
nextAction: string(review.next_action, "E46G next action"),
},
verdict: exact(comparison.verdict, "select-trafficcamnet-front-only-for-e46h", "E46G verdict"),
},
videos: {
trafficcamnet: video(videos.trafficcamnet, "TrafficCamNet video"),
dashcamnet: video(videos.dashcamnet, "DashCamNet video"),
},
};
}
@@ -0,0 +1,325 @@
import type { E46ELaboratoryMethod } from "./e46eReadyStack";
export interface E46HReviewWindow {
id: string;
label: string;
startSeconds: number;
endSeconds: number;
sourceTrackId: number | null;
verdict: "semantic-false-positive" | "empty-scene-expected";
}
export interface E46HFullRectifiedFrontReplayResult {
resultId: string;
createdAtUtc: string;
sourceSessionId: string;
cameraSourceId: "sensor.camera.right";
baselineResultId: string;
selection: {
firstSourceFrameIndex: 0;
lastSourceFrameIndex: 4487;
frameCount: 4488;
excludedSourceTailFrameCount: 1;
};
rectification: {
provider: "NVIDIA Gst-nvdewarper";
providerVersion: string;
projection: "fisheye-to-perspective";
view: "front";
outputResolution: readonly [960, 544];
horizontalFovDegrees: 100;
};
metrics: {
frameCount: 4488;
routeDurationSeconds: number;
detectionObservationCount: number;
trackObservationCount: number;
uniqueTrackCount: number;
meanTrackedObjectsPerFrame: number;
zeroDetectionFrameCount: number;
zeroTrackFrameCount: number;
trackerRecoveredFrameCount: number;
fullLayerBlackoutEventCount: number;
routeIdGapEventCount: number;
shortTrackCount: number;
shortTrackFraction: number;
trackClassSwitchCount: number;
largeTrackObservationCount: number;
largeTrackFraction: number;
};
acceptance: {
exactRecordedRightSourceBound: true;
factoryCalibrationBound: true;
officialNvidiaDewarperExecuted: true;
selectedStockDetectorTrackerExecuted: true;
retainedRouteAccounted: true;
terminalSourceFrameExcluded: true;
fullVisualReviewCompleted: false;
independentTruthAvailable: false;
candidateAccepted: false;
navigationOrSafetyAccepted: false;
};
decision: {
selectedProvider: "front-trafficcamnet-stock-nvdcf";
customDetectorOrTrackerLogicUsed: false;
providerPromoted: false;
nextAction: string;
};
method: E46ELaboratoryMethod;
limitations: readonly string[];
visualReview: {
status: "full-continuous-and-targeted-review-completed";
completeVideoReviewed: true;
reviewedVideoRangeSeconds: readonly [0, 448.8];
verdict: "useful-front-continuity-but-semantic-regression-blocks-promotion";
reviewWindows: readonly E46HReviewWindow[];
finding: string;
blackoutInterpretation: string;
nextAction: string;
};
video: {
url: string;
mediaType: "video/mp4";
byteLength: number;
sha256: string;
width: 960;
height: 544;
durationSeconds: 448.8;
};
}
type LaboratoryFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
class ContractError extends Error {}
function object(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new ContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function array(value: unknown, label: string): readonly unknown[] {
if (!Array.isArray(value)) throw new ContractError(`${label}: ожидался массив.`);
return value;
}
function string(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new ContractError(`${label}: ожидалась строка.`);
}
return value;
}
function number(value: unknown, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new ContractError(`${label}: ожидалось число.`);
}
return value;
}
function integer(value: unknown, label: string): number {
const parsed = number(value, label);
if (!Number.isSafeInteger(parsed) || parsed < 0) {
throw new ContractError(`${label}: ожидалось неотрицательное целое.`);
}
return parsed;
}
function exact<T extends string | number | boolean>(
value: unknown,
expected: T,
label: string,
): T {
if (value !== expected) throw new ContractError(`${label}: нарушен контракт.`);
return expected;
}
function sha(value: unknown, label: string): string {
const parsed = string(value, label);
if (!/^[a-f0-9]{64}$/.test(parsed)) {
throw new ContractError(`${label}: нарушена SHA-256 идентичность.`);
}
return parsed;
}
function method(value: unknown): E46ELaboratoryMethod {
const raw = object(value, "E46H method");
exact(raw.schema_version, "missioncore.laboratory-method/v1", "E46H method schema");
exact(raw.completeness, "complete", "E46H method completeness");
exact(raw.execution_class, "hybrid", "E46H execution class");
const components = array(raw.components, "E46H method components").map((entry) => {
const component = object(entry, "E46H method component");
const kind = string(component.kind, "E46H component kind");
if (!("source tool model algorithm runtime".split(" ")).includes(kind)) {
throw new ContractError("E46H component kind: неизвестное значение.");
}
return {
kind: kind as "source" | "tool" | "model" | "algorithm" | "runtime",
name: string(component.name, "E46H component name"),
version: string(component.version, "E46H component version"),
role: string(component.role, "E46H component role"),
identitySha256: sha(component.identity_sha256, "E46H component identity"),
};
});
return {
completeness: "complete",
executionClass: "hybrid",
pipelineId: string(raw.pipeline_id, "E46H pipeline"),
components,
};
}
function reviewWindow(value: unknown): E46HReviewWindow {
const raw = object(value, "E46H review window");
const sourceTrackId = raw.source_track_id === null
? null
: integer(raw.source_track_id, "E46H review track");
const verdict = raw.verdict;
if (verdict !== "semantic-false-positive" && verdict !== "empty-scene-expected") {
throw new ContractError("E46H review verdict: неизвестное значение.");
}
const startSeconds = number(raw.start_seconds, "E46H review start");
const endSeconds = number(raw.end_seconds, "E46H review end");
if (startSeconds < 0 || endSeconds <= startSeconds || endSeconds > 448.8) {
throw new ContractError("E46H review window: нарушен диапазон.");
}
return {
id: string(raw.id, "E46H review id"),
label: string(raw.label, "E46H review label"),
startSeconds,
endSeconds,
sourceTrackId,
verdict,
};
}
export async function fetchE46HFullRectifiedFrontReplay({
fetcher = fetch,
signal,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
} = {}): Promise<E46HFullRectifiedFrontReplayResult | null> {
const response = await fetcher("/api/v1/laboratory/e46h/results?limit=1", {
headers: { Accept: "application/json" },
signal,
});
if (!response.ok) throw new ContractError(`E46H LAB недоступен: HTTP ${response.status}.`);
const catalog = object(await response.json(), "E46H catalog");
exact(catalog.schema_version, "missioncore.e46h-full-rectified-front-replay-catalog/v1", "E46H catalog schema");
const items = array(catalog.items, "E46H results");
if (!items.length) return null;
if (items.length !== 1) throw new ContractError("E46H catalog size: нарушен контракт.");
const item = object(items[0], "E46H result");
exact(item.schema_version, "missioncore.e46h-full-rectified-front-replay-view/v1", "E46H view schema");
exact(item.status, "diagnostic-regression-large-semantic-false-tracks", "E46H status");
exact(item.ground_truth, false, "E46H truth authority");
const resultId = string(item.result_id, "E46H result id");
if (!/^e46h-full-rectified-front-replay-[a-f0-9]{64}$/.test(resultId)) {
throw new ContractError("E46H result identity: нарушен контракт.");
}
const selection = object(item.selection, "E46H selection");
const rectification = object(item.rectification, "E46H rectification");
const metrics = object(item.metrics, "E46H metrics");
const acceptance = object(item.acceptance, "E46H acceptance");
const decision = object(item.decision, "E46H decision");
const visualReview = object(item.visual_review, "E46H visual review");
const video = object(item.video, "E46H video");
const authority = object(item.authority, "E46H authority");
exact(authority.ground_truth, false, "E46H authority ground truth");
exact(authority.independent_truth, false, "E46H authority independent truth");
exact(authority.candidate_accepted, false, "E46H authority candidate");
exact(authority.commands_enabled, false, "E46H authority commands");
exact(authority.navigation_or_safety_accepted, false, "E46H authority safety");
return {
resultId,
createdAtUtc: string(item.created_at_utc, "E46H created"),
sourceSessionId: string(item.source_session_id, "E46H source session"),
cameraSourceId: exact(item.camera_source_id, "sensor.camera.right", "E46H camera"),
baselineResultId: string(item.baseline_result_id, "E46H baseline"),
selection: {
firstSourceFrameIndex: exact(selection.first_source_frame_index, 0, "E46H first frame"),
lastSourceFrameIndex: exact(selection.last_source_frame_index, 4487, "E46H last frame"),
frameCount: exact(selection.frame_count, 4488, "E46H frame count"),
excludedSourceTailFrameCount: exact(selection.excluded_source_tail_frame_count, 1, "E46H excluded tail"),
},
rectification: {
provider: exact(rectification.provider, "NVIDIA Gst-nvdewarper", "E46H dewarper"),
providerVersion: string(rectification.provider_version, "E46H dewarper version"),
projection: exact(rectification.projection, "fisheye-to-perspective", "E46H projection"),
view: exact(rectification.view, "front", "E46H view"),
outputResolution: (() => {
const parsed = array(rectification.output_resolution, "E46H output");
if (parsed.length !== 2 || parsed[0] !== 960 || parsed[1] !== 544) {
throw new ContractError("E46H output: нарушен контракт.");
}
return [960, 544] as const;
})(),
horizontalFovDegrees: exact(rectification.horizontal_fov_degrees, 100, "E46H FOV"),
},
metrics: {
frameCount: exact(metrics.frame_count, 4488, "E46H metric frames"),
routeDurationSeconds: number(metrics.route_duration_seconds, "E46H source duration"),
detectionObservationCount: integer(metrics.detection_observation_count, "E46H detections"),
trackObservationCount: integer(metrics.track_observation_count, "E46H tracks"),
uniqueTrackCount: integer(metrics.unique_track_count, "E46H identities"),
meanTrackedObjectsPerFrame: number(metrics.mean_tracked_objects_per_frame, "E46H mean"),
zeroDetectionFrameCount: integer(metrics.zero_detection_frame_count, "E46H zero detections"),
zeroTrackFrameCount: integer(metrics.zero_track_frame_count, "E46H zero tracks"),
trackerRecoveredFrameCount: integer(metrics.tracker_recovered_frame_count, "E46H recovered"),
fullLayerBlackoutEventCount: integer(metrics.full_layer_blackout_event_count, "E46H blackouts"),
routeIdGapEventCount: integer(metrics.route_id_gap_event_count, "E46H id gaps"),
shortTrackCount: integer(metrics.short_track_count, "E46H short tracks"),
shortTrackFraction: number(metrics.short_track_fraction, "E46H short fraction"),
trackClassSwitchCount: integer(metrics.track_class_switch_count, "E46H class switches"),
largeTrackObservationCount: integer(metrics.large_track_observation_count, "E46H large tracks"),
largeTrackFraction: number(metrics.large_track_fraction, "E46H large fraction"),
},
acceptance: {
exactRecordedRightSourceBound: exact(acceptance.exact_recorded_right_source_bound, true, "E46H source"),
factoryCalibrationBound: exact(acceptance.factory_calibration_bound, true, "E46H calibration"),
officialNvidiaDewarperExecuted: exact(acceptance.official_nvidia_dewarper_executed, true, "E46H dewarper execution"),
selectedStockDetectorTrackerExecuted: exact(acceptance.selected_stock_detector_tracker_executed, true, "E46H stock stack"),
retainedRouteAccounted: exact(acceptance.retained_route_accounted, true, "E46H retained route"),
terminalSourceFrameExcluded: exact(acceptance.terminal_source_frame_excluded, true, "E46H tail"),
fullVisualReviewCompleted: exact(acceptance.full_visual_review_completed, false, "E46H visual review authority"),
independentTruthAvailable: exact(acceptance.independent_truth_available, false, "E46H truth"),
candidateAccepted: exact(acceptance.candidate_accepted, false, "E46H candidate"),
navigationOrSafetyAccepted: exact(acceptance.navigation_or_safety_accepted, false, "E46H safety"),
},
decision: {
selectedProvider: exact(decision.selected_provider, "front-trafficcamnet-stock-nvdcf", "E46H provider"),
customDetectorOrTrackerLogicUsed: exact(decision.custom_detector_or_tracker_logic_used, false, "E46H custom logic"),
providerPromoted: exact(decision.provider_promoted, false, "E46H promotion"),
nextAction: string(decision.next_action, "E46H decision"),
},
method: method(item.method),
limitations: array(item.limitations, "E46H limitations").map((entry) => string(entry, "E46H limitation")),
visualReview: {
status: exact(visualReview.status, "full-continuous-and-targeted-review-completed", "E46H review status"),
completeVideoReviewed: exact(visualReview.complete_video_reviewed, true, "E46H complete video review"),
reviewedVideoRangeSeconds: (() => {
const parsed = array(visualReview.reviewed_video_range_seconds, "E46H reviewed video range");
if (parsed.length !== 2 || parsed[0] !== 0 || parsed[1] !== 448.8) {
throw new ContractError("E46H reviewed video range: нарушен контракт.");
}
return [0, 448.8] as const;
})(),
verdict: exact(visualReview.verdict, "useful-front-continuity-but-semantic-regression-blocks-promotion", "E46H review verdict"),
reviewWindows: array(visualReview.review_windows, "E46H review windows").map(reviewWindow),
finding: string(visualReview.finding, "E46H finding"),
blackoutInterpretation: string(visualReview.blackout_interpretation, "E46H blackout interpretation"),
nextAction: string(visualReview.next_action, "E46H next action"),
},
video: {
url: string(video.url, "E46H video URL"),
mediaType: exact(video.media_type, "video/mp4", "E46H video media"),
byteLength: integer(video.byte_length, "E46H video bytes"),
sha256: sha(video.sha256, "E46H video SHA"),
width: exact(video.width, 960, "E46H video width"),
height: exact(video.height, 544, "E46H video height"),
durationSeconds: exact(video.duration_seconds, 448.8, "E46H video duration"),
},
};
}
@@ -0,0 +1,350 @@
import type { E46ELaboratoryMethod } from "./e46eReadyStack";
export interface E46IReviewWindow {
id: string;
label: string;
startSeconds: number;
endSeconds: number;
verdict:
| "legacy-background-false-positive-suppressed"
| "empty-scene-mostly-preserved";
}
export interface E46IGroundingDinoFullReplayResult {
resultId: string;
createdAtUtc: string;
baselineResultId: string;
source: {
cameraSourceId: "sensor.camera.right";
sessionId: string;
view: "front";
resolution: readonly [960, 544];
frameCount: 4488;
frameRate: 10;
durationSeconds: 448.8;
};
provider: {
name: "NVIDIA TAO Grounding DINO Swin-Tiny Commercial";
version: "1.0";
toolkit: "NVIDIA TAO Toolkit Deploy 7.0.1";
precision: "FP16";
};
inference: {
captions: readonly ["car", "person", "bicycle", "road sign"];
confidenceThreshold: 0.5;
meanFramesPerSecond: number;
};
metrics: {
frameCount: 4488;
detectionObservationCount: number;
classObservationCounts: Readonly<Record<string, number>>;
meanDetectionsPerFrame: number;
maxDetectionsPerFrame: number;
zeroDetectionFrameCount: number;
longestZeroDetectionRunFrames: number;
confidenceMean: number;
largeBoxObservationCount: number;
largeBoxObservationFraction: number;
workerElapsedSeconds: number;
workerMeanFramesPerSecond: number;
};
shadowGate: {
legacyCases: 5;
suppressedCases: 5;
positiveCases: 5;
positiveCasesWithRelevantDetection: 5;
limitations: readonly string[];
};
visualReview: {
completeVideoPlaybackCompleted: true;
reviewedRangeSeconds: readonly [0, 448.8];
verdict: "material-semantic-progress-not-yet-complete-perception";
reviewWindows: readonly E46IReviewWindow[];
finding: string;
knownError: string;
};
decision: {
selectedProvider: "nvidia-grounding-dino-swin-tiny-commercial-v1.0";
providerSemanticProgress: true;
providerPromoted: false;
nextAction: string;
};
method: E46ELaboratoryMethod;
limitations: readonly string[];
video: {
url: string;
mediaType: "video/mp4";
byteLength: number;
sha256: string;
width: 960;
height: 544;
frameRate: 10;
frameCount: 4488;
durationSeconds: 448.8;
};
visuals: {
shadowGate: VisualArtifact;
fullRoute: VisualArtifact;
targetedWindows: VisualArtifact;
};
}
interface VisualArtifact {
url: string;
mediaType: "image/png";
byteLength: number;
sha256: string;
}
type LaboratoryFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
class ContractError extends Error {}
function object(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new ContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function array(value: unknown, label: string): readonly unknown[] {
if (!Array.isArray(value)) throw new ContractError(`${label}: ожидался массив.`);
return value;
}
function text(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new ContractError(`${label}: ожидалась строка.`);
}
return value;
}
function number(value: unknown, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new ContractError(`${label}: ожидалось число.`);
}
return value;
}
function integer(value: unknown, label: string): number {
const parsed = number(value, label);
if (!Number.isSafeInteger(parsed) || parsed < 0) {
throw new ContractError(`${label}: ожидалось неотрицательное целое.`);
}
return parsed;
}
function exact<T extends string | number | boolean>(
value: unknown,
expected: T,
label: string,
): T {
if (value !== expected) throw new ContractError(`${label}: нарушен контракт.`);
return expected;
}
function sha(value: unknown, label: string): string {
const parsed = text(value, label);
if (!/^[a-f0-9]{64}$/.test(parsed)) {
throw new ContractError(`${label}: нарушена SHA-256 идентичность.`);
}
return parsed;
}
function pair<const T extends readonly [number, number]>(
value: unknown,
expected: T,
label: string,
): T {
const parsed = array(value, label);
if (parsed.length !== 2 || parsed[0] !== expected[0] || parsed[1] !== expected[1]) {
throw new ContractError(`${label}: нарушен диапазон.`);
}
return expected;
}
function method(value: unknown): E46ELaboratoryMethod {
const raw = object(value, "E46I method");
exact(raw.schema_version, "missioncore.laboratory-method/v1", "E46I method schema");
exact(raw.completeness, "complete", "E46I method completeness");
exact(raw.execution_class, "hybrid", "E46I method execution");
return {
completeness: "complete",
executionClass: "hybrid",
pipelineId: text(raw.pipeline_id, "E46I pipeline"),
components: array(raw.components, "E46I components").map((entry) => {
const component = object(entry, "E46I component");
const kind = text(component.kind, "E46I component kind");
if (!("source tool model algorithm runtime".split(" ")).includes(kind)) {
throw new ContractError("E46I component kind: неизвестное значение.");
}
return {
kind: kind as "source" | "tool" | "model" | "algorithm" | "runtime",
name: text(component.name, "E46I component name"),
version: text(component.version, "E46I component version"),
role: text(component.role, "E46I component role"),
identitySha256: sha(component.identity_sha256, "E46I component identity"),
};
}),
};
}
function visual(value: unknown, label: string): VisualArtifact {
const raw = object(value, label);
return {
url: text(raw.url, `${label} URL`),
mediaType: exact(raw.media_type, "image/png", `${label} media`),
byteLength: integer(raw.byte_length, `${label} bytes`),
sha256: sha(raw.sha256, `${label} SHA`),
};
}
function reviewWindow(value: unknown): E46IReviewWindow {
const raw = object(value, "E46I review window");
const verdict = raw.verdict;
if (
verdict !== "legacy-background-false-positive-suppressed"
&& verdict !== "empty-scene-mostly-preserved"
) {
throw new ContractError("E46I review verdict: неизвестное значение.");
}
const startSeconds = number(raw.start_seconds, "E46I review start");
const endSeconds = number(raw.end_seconds, "E46I review end");
if (startSeconds < 0 || endSeconds <= startSeconds || endSeconds > 448.8) {
throw new ContractError("E46I review window: нарушен диапазон.");
}
return {
id: text(raw.id, "E46I review id"),
label: text(raw.label, "E46I review label"),
startSeconds,
endSeconds,
verdict,
};
}
export async function fetchE46IGroundingDinoFullReplay({
fetcher = fetch,
signal,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
} = {}): Promise<E46IGroundingDinoFullReplayResult | null> {
const response = await fetcher("/api/v1/laboratory/e46i/results?limit=1", {
headers: { Accept: "application/json" },
signal,
});
if (!response.ok) throw new ContractError(`E46I LAB недоступен: HTTP ${response.status}.`);
const catalog = object(await response.json(), "E46I catalog");
exact(catalog.schema_version, "missioncore.e46i-grounding-dino-full-replay-catalog/v1", "E46I catalog schema");
const items = array(catalog.items, "E46I results");
if (!items.length) return null;
if (items.length !== 1) throw new ContractError("E46I catalog size: нарушен контракт.");
const item = object(items[0], "E46I result");
exact(item.schema_version, "missioncore.e46i-grounding-dino-full-replay-view/v1", "E46I view schema");
exact(item.status, "semantic-regression-suppressed-awaiting-temporal-layer", "E46I status");
exact(item.ground_truth, false, "E46I truth authority");
const authority = object(item.authority, "E46I authority");
exact(authority.ground_truth, false, "E46I authority truth");
exact(authority.independent_truth, false, "E46I independent truth");
exact(authority.candidate_accepted, false, "E46I acceptance");
exact(authority.commands_enabled, false, "E46I commands");
exact(authority.navigation_or_safety_accepted, false, "E46I safety");
const resultId = text(item.result_id, "E46I result id");
if (!/^e46i-grounding-dino-full-replay-[a-f0-9]{64}$/.test(resultId)) {
throw new ContractError("E46I result identity: нарушен контракт.");
}
const source = object(item.source, "E46I source");
const provider = object(item.provider, "E46I provider");
const inference = object(item.inference, "E46I inference");
const metrics = object(item.metrics, "E46I metrics");
const shadow = object(item.shadow_gate, "E46I shadow gate");
const review = object(item.visual_review, "E46I visual review");
const decision = object(item.decision, "E46I decision");
const video = object(item.video, "E46I video");
const visuals = object(item.visuals, "E46I visuals");
const captions = array(inference.captions, "E46I captions");
if (captions.join("|") !== "car|person|bicycle|road sign") {
throw new ContractError("E46I captions: нарушен контракт.");
}
const classCounts = object(metrics.class_observation_counts, "E46I class counts");
return {
resultId,
createdAtUtc: text(item.created_at_utc, "E46I created"),
baselineResultId: text(item.baseline_result_id, "E46I baseline"),
source: {
cameraSourceId: exact(source.camera_source_id, "sensor.camera.right", "E46I camera"),
sessionId: text(source.session_id, "E46I session"),
view: exact(source.view, "front", "E46I view"),
resolution: pair(source.projection_resolution, [960, 544], "E46I resolution"),
frameCount: exact(source.video_frame_count, 4488, "E46I frames"),
frameRate: exact(source.video_frame_rate, 10, "E46I fps"),
durationSeconds: exact(source.video_duration_seconds, 448.8, "E46I duration"),
},
provider: {
name: exact(provider.name, "NVIDIA TAO Grounding DINO Swin-Tiny Commercial", "E46I provider"),
version: exact(provider.version, "1.0", "E46I provider version"),
toolkit: exact(provider.deployment_toolkit, "NVIDIA TAO Toolkit Deploy 7.0.1", "E46I toolkit"),
precision: exact(provider.engine_precision, "FP16", "E46I precision"),
},
inference: {
captions: ["car", "person", "bicycle", "road sign"],
confidenceThreshold: exact(inference.confidence_threshold, 0.5, "E46I threshold"),
meanFramesPerSecond: number(inference.mean_frames_per_second, "E46I inference fps"),
},
metrics: {
frameCount: exact(metrics.frame_count, 4488, "E46I metric frames"),
detectionObservationCount: integer(metrics.detection_observation_count, "E46I detections"),
classObservationCounts: Object.fromEntries(
Object.entries(classCounts).map(([key, value]) => [key, integer(value, `E46I ${key}`)]),
),
meanDetectionsPerFrame: number(metrics.mean_detections_per_frame, "E46I mean"),
maxDetectionsPerFrame: integer(metrics.max_detections_per_frame, "E46I max"),
zeroDetectionFrameCount: integer(metrics.zero_detection_frame_count, "E46I zero"),
longestZeroDetectionRunFrames: integer(metrics.longest_zero_detection_run_frames, "E46I zero run"),
confidenceMean: number(metrics.confidence_mean, "E46I confidence"),
largeBoxObservationCount: integer(metrics.large_box_observation_count, "E46I large boxes"),
largeBoxObservationFraction: number(metrics.large_box_observation_fraction, "E46I large fraction"),
workerElapsedSeconds: number(metrics.worker_elapsed_seconds, "E46I elapsed"),
workerMeanFramesPerSecond: number(metrics.worker_mean_frames_per_second, "E46I worker fps"),
},
shadowGate: {
legacyCases: exact(shadow.legacy_large_false_background_cases, 5, "E46I legacy cases"),
suppressedCases: exact(shadow.legacy_large_false_background_cases_suppressed, 5, "E46I suppressed"),
positiveCases: exact(shadow.positive_anchor_cases, 5, "E46I positives"),
positiveCasesWithRelevantDetection: exact(shadow.positive_anchor_cases_with_relevant_detection, 5, "E46I retained positives"),
limitations: array(shadow.known_semantic_limitations, "E46I gate limitations").map((entry) => text(entry, "E46I gate limitation")),
},
visualReview: {
completeVideoPlaybackCompleted: exact(review.complete_video_playback_completed, true, "E46I playback"),
reviewedRangeSeconds: pair(review.reviewed_video_range_seconds, [0, 448.8], "E46I reviewed range"),
verdict: exact(review.verdict, "material-semantic-progress-not-yet-complete-perception", "E46I verdict"),
reviewWindows: array(review.review_windows, "E46I review windows").map(reviewWindow),
finding: text(review.finding, "E46I finding"),
knownError: text(review.known_error, "E46I known error"),
},
decision: {
selectedProvider: exact(decision.selected_provider, "nvidia-grounding-dino-swin-tiny-commercial-v1.0", "E46I selected provider"),
providerSemanticProgress: exact(decision.provider_semantic_progress, true, "E46I progress"),
providerPromoted: exact(decision.provider_promoted, false, "E46I promotion"),
nextAction: text(decision.next_action, "E46I next action"),
},
method: method(item.method),
limitations: array(item.limitations, "E46I limitations").map((entry) => text(entry, "E46I limitation")),
video: {
url: text(video.url, "E46I video URL"),
mediaType: exact(video.media_type, "video/mp4", "E46I video media"),
byteLength: integer(video.byte_length, "E46I video bytes"),
sha256: sha(video.sha256, "E46I video SHA"),
width: exact(video.width, 960, "E46I video width"),
height: exact(video.height, 544, "E46I video height"),
frameRate: exact(video.frame_rate, 10, "E46I video fps"),
frameCount: exact(video.frame_count, 4488, "E46I video frames"),
durationSeconds: exact(video.duration_seconds, 448.8, "E46I video duration"),
},
visuals: {
shadowGate: visual(visuals.shadow_gate, "E46I shadow visual"),
fullRoute: visual(visuals.full_route, "E46I route visual"),
targetedWindows: visual(visuals.targeted_windows, "E46I targeted visual"),
},
};
}
@@ -0,0 +1,338 @@
import type { E46ELaboratoryMethod } from "./e46eReadyStack";
export interface E46JReviewWindow {
id: string;
label: string;
startSeconds: number;
endSeconds: number;
verdict:
| "legacy-background-false-positive-suppressed"
| "operator-shadow-person-false-positive-observed";
}
interface VisualArtifact {
url: string;
mediaType: "image/png";
byteLength: number;
sha256: string;
}
export interface E46JRawFisheyeRealtimeResult {
resultId: string;
createdAtUtc: string;
source: {
cameraSourceId: "sensor.camera.right";
sessionId: string;
resolution: readonly [800, 600];
frameCount: 4489;
frameRate: number;
durationSeconds: 448.723;
calibrationModel: "KB4";
};
detector: {
architecture: "YOLOX-S";
source: string;
license: "Apache-2.0";
runtime: string;
};
detection: {
minimumScore: 0.5;
nmsIouThreshold: 0.45;
};
metrics: {
frameCount: 4489;
failedFrameCount: 0;
detectionObservationCount: number;
classObservationCounts: Readonly<Record<string, number>>;
meanDetectionsPerFrame: number;
maxDetectionsPerFrame: number;
zeroDetectionFrameCount: number;
longestZeroDetectionRunFrames: number;
coreCapacityFps: number;
corePathMeanMs: number;
corePathP95Ms: number;
inferenceRequestMeanMs: number;
inferenceRequestP95Ms: number;
gpuUtilizationMeanPercent: number;
operatorShadowWindowFrameCount: number;
operatorShadowPersonFrameCount: number;
};
visualReview: {
reviewedRangeSeconds: readonly [0, 448.723];
verdict: "realtime-detector-progress-with-known-shadow-exception";
reviewWindows: readonly E46JReviewWindow[];
finding: string;
knownError: string;
};
decision: {
selectedProvider: "megvii-yolox-s-0.1.1rc0";
realtimeCapacityPassed: true;
readyForTemporalBakeoff: true;
providerPromoted: false;
nextAction: string;
};
method: E46ELaboratoryMethod;
limitations: readonly string[];
video: {
url: string;
mediaType: "video/mp4";
byteLength: number;
sha256: string;
width: 800;
height: 600;
frameRate: number;
frameCount: 4489;
durationSeconds: 448.723;
};
visuals: {
fullRoute: VisualArtifact;
targetedWindows: VisualArtifact;
operatorShadow: VisualArtifact;
};
}
type LaboratoryFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
class ContractError extends Error {}
function object(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new ContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function array(value: unknown, label: string): readonly unknown[] {
if (!Array.isArray(value)) throw new ContractError(`${label}: ожидался массив.`);
return value;
}
function text(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new ContractError(`${label}: ожидалась строка.`);
}
return value;
}
function number(value: unknown, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new ContractError(`${label}: ожидалось число.`);
}
return value;
}
function integer(value: unknown, label: string): number {
const parsed = number(value, label);
if (!Number.isSafeInteger(parsed) || parsed < 0) {
throw new ContractError(`${label}: ожидалось неотрицательное целое.`);
}
return parsed;
}
function exact<T extends string | number | boolean>(
value: unknown,
expected: T,
label: string,
): T {
if (value !== expected) throw new ContractError(`${label}: нарушен контракт.`);
return expected;
}
function sha(value: unknown, label: string): string {
const parsed = text(value, label);
if (!/^[a-f0-9]{64}$/.test(parsed)) {
throw new ContractError(`${label}: нарушена SHA-256 идентичность.`);
}
return parsed;
}
function pair<const T extends readonly [number, number]>(
value: unknown,
expected: T,
label: string,
): T {
const parsed = array(value, label);
if (parsed.length !== 2 || parsed[0] !== expected[0] || parsed[1] !== expected[1]) {
throw new ContractError(`${label}: нарушен диапазон.`);
}
return expected;
}
function visual(value: unknown, label: string): VisualArtifact {
const raw = object(value, label);
return {
url: text(raw.url, `${label} URL`),
mediaType: exact(raw.media_type, "image/png", `${label} media`),
byteLength: integer(raw.byte_length, `${label} bytes`),
sha256: sha(raw.sha256, `${label} SHA`),
};
}
function method(value: unknown): E46ELaboratoryMethod {
const raw = object(value, "E46J method");
exact(raw.schema_version, "missioncore.laboratory-method/v1", "E46J method schema");
exact(raw.completeness, "complete", "E46J method completeness");
exact(raw.execution_class, "hybrid", "E46J method execution");
return {
completeness: "complete",
executionClass: "hybrid",
pipelineId: text(raw.pipeline_id, "E46J pipeline"),
components: array(raw.components, "E46J components").map((value) => {
const component = object(value, "E46J component");
const kind = text(component.kind, "E46J component kind");
if (!("source tool model algorithm runtime".split(" ")).includes(kind)) {
throw new ContractError("E46J component kind: неизвестное значение.");
}
return {
kind: kind as "source" | "tool" | "model" | "algorithm" | "runtime",
name: text(component.name, "E46J component name"),
version: text(component.version, "E46J component version"),
role: text(component.role, "E46J component role"),
identitySha256: sha(component.identity_sha256, "E46J component identity"),
};
}),
};
}
function reviewWindow(value: unknown): E46JReviewWindow {
const raw = object(value, "E46J review window");
const verdict = raw.verdict;
if (
verdict !== "legacy-background-false-positive-suppressed"
&& verdict !== "operator-shadow-person-false-positive-observed"
) {
throw new ContractError("E46J review verdict: неизвестное значение.");
}
const startSeconds = number(raw.start_seconds, "E46J review start");
const endSeconds = number(raw.end_seconds, "E46J review end");
if (startSeconds < 0 || endSeconds <= startSeconds || endSeconds > 448.723) {
throw new ContractError("E46J review window: нарушен диапазон.");
}
return {
id: text(raw.id, "E46J review id"),
label: text(raw.label, "E46J review label"),
startSeconds,
endSeconds,
verdict,
};
}
export async function fetchE46JRawFisheyeRealtime({
fetcher = fetch,
signal,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
} = {}): Promise<E46JRawFisheyeRealtimeResult | null> {
const response = await fetcher("/api/v1/laboratory/e46j/results?limit=1", {
headers: { Accept: "application/json" },
signal,
});
if (!response.ok) throw new ContractError(`E46J LAB недоступен: HTTP ${response.status}.`);
const catalog = object(await response.json(), "E46J catalog");
exact(catalog.schema_version, "missioncore.e46j-raw-fisheye-realtime-catalog/v1", "E46J catalog schema");
const items = array(catalog.items, "E46J results");
if (!items.length) return null;
if (items.length !== 1) throw new ContractError("E46J catalog size: нарушен контракт.");
const item = object(items[0], "E46J result");
exact(item.schema_version, "missioncore.e46j-raw-fisheye-realtime-view/v1", "E46J view schema");
exact(item.status, "realtime-capacity-passed-awaiting-temporal-layer", "E46J status");
exact(item.ground_truth, false, "E46J truth authority");
const authority = object(item.authority, "E46J authority");
exact(authority.ground_truth, false, "E46J authority truth");
exact(authority.provider_promoted, false, "E46J promotion");
exact(authority.commands_enabled, false, "E46J commands");
exact(authority.navigation_or_safety_accepted, false, "E46J safety");
const resultId = text(item.result_id, "E46J result id");
if (!/^e46j-raw-fisheye-realtime-[a-f0-9]{64}$/.test(resultId)) {
throw new ContractError("E46J result identity: нарушен контракт.");
}
const source = object(item.source, "E46J source");
const detector = object(item.detector, "E46J detector");
const detection = object(item.detection, "E46J detection");
const metrics = object(item.metrics, "E46J metrics");
const review = object(item.visual_review, "E46J visual review");
const decision = object(item.decision, "E46J decision");
const video = object(item.video, "E46J video");
const visuals = object(item.visuals, "E46J visuals");
const classCounts = object(metrics.class_observation_counts, "E46J class counts");
const acceptance = object(item.acceptance, "E46J acceptance");
exact(acceptance.ten_hz_capacity_gate_passed, true, "E46J capacity gate");
exact(acceptance.latency_gate_passed, true, "E46J latency gate");
exact(acceptance.full_raw_fisheye_retained, true, "E46J FOV gate");
return {
resultId,
createdAtUtc: text(item.created_at_utc, "E46J created"),
source: {
cameraSourceId: exact(source.camera_source_id, "sensor.camera.right", "E46J camera"),
sessionId: text(source.session_id, "E46J session"),
resolution: pair(source.resolution, [800, 600], "E46J resolution"),
frameCount: exact(source.frame_count, 4489, "E46J frames"),
frameRate: number(source.frame_rate, "E46J fps"),
durationSeconds: 448.723,
calibrationModel: exact(source.calibration_model, "KB4", "E46J calibration"),
},
detector: {
architecture: exact(detector.architecture, "YOLOX-S", "E46J architecture"),
source: text(detector.source, "E46J detector source"),
license: exact(detector.license, "Apache-2.0", "E46J license"),
runtime: text(detector.runtime, "E46J runtime"),
},
detection: {
minimumScore: exact(detection.minimum_score, 0.5, "E46J score"),
nmsIouThreshold: exact(detection.nms_iou_threshold, 0.45, "E46J NMS"),
},
metrics: {
frameCount: exact(metrics.frame_count, 4489, "E46J metric frames"),
failedFrameCount: exact(metrics.failed_frame_count, 0, "E46J failures"),
detectionObservationCount: integer(metrics.detection_observation_count, "E46J detections"),
classObservationCounts: Object.fromEntries(
Object.entries(classCounts).map(([key, value]) => [key, integer(value, `E46J ${key}`)]),
),
meanDetectionsPerFrame: number(metrics.mean_detections_per_frame, "E46J mean"),
maxDetectionsPerFrame: integer(metrics.max_detections_per_frame, "E46J max"),
zeroDetectionFrameCount: integer(metrics.zero_detection_frame_count, "E46J zero"),
longestZeroDetectionRunFrames: integer(metrics.longest_zero_detection_run_frames, "E46J zero run"),
coreCapacityFps: number(metrics.core_capacity_fps, "E46J capacity"),
corePathMeanMs: number(metrics.core_path_mean_ms, "E46J core mean"),
corePathP95Ms: number(metrics.core_path_p95_ms, "E46J core p95"),
inferenceRequestMeanMs: number(metrics.inference_request_mean_ms, "E46J infer mean"),
inferenceRequestP95Ms: number(metrics.inference_request_p95_ms, "E46J infer p95"),
gpuUtilizationMeanPercent: number(metrics.gpu_utilization_mean_percent, "E46J GPU"),
operatorShadowWindowFrameCount: integer(metrics.operator_shadow_window_frame_count, "E46J shadow window"),
operatorShadowPersonFrameCount: integer(metrics.operator_shadow_person_frame_count, "E46J shadow false frames"),
},
visualReview: {
reviewedRangeSeconds: pair(review.reviewed_video_range_seconds, [0, 448.723], "E46J reviewed range"),
verdict: exact(review.verdict, "realtime-detector-progress-with-known-shadow-exception", "E46J verdict"),
reviewWindows: array(review.review_windows, "E46J review windows").map(reviewWindow),
finding: text(review.finding, "E46J finding"),
knownError: text(review.known_error, "E46J known error"),
},
decision: {
selectedProvider: exact(decision.selected_provider, "megvii-yolox-s-0.1.1rc0", "E46J provider"),
realtimeCapacityPassed: exact(decision.realtime_capacity_passed, true, "E46J realtime"),
readyForTemporalBakeoff: exact(decision.ready_for_temporal_bakeoff, true, "E46J temporal readiness"),
providerPromoted: exact(decision.provider_promoted, false, "E46J provider promotion"),
nextAction: text(decision.next_action, "E46J next action"),
},
method: method(item.method),
limitations: array(item.limitations, "E46J limitations").map((value) => text(value, "E46J limitation")),
video: {
url: text(video.url, "E46J video URL"),
mediaType: exact(video.media_type, "video/mp4", "E46J video media"),
byteLength: integer(video.byte_length, "E46J video bytes"),
sha256: sha(video.sha256, "E46J video SHA"),
width: exact(video.width, 800, "E46J video width"),
height: exact(video.height, 600, "E46J video height"),
frameRate: number(video.frame_rate, "E46J video fps"),
frameCount: exact(video.frame_count, 4489, "E46J video frames"),
durationSeconds: exact(video.duration_seconds, 448.723, "E46J video duration"),
},
visuals: {
fullRoute: visual(visuals.full_route, "E46J route visual"),
targetedWindows: visual(visuals.targeted_windows, "E46J targeted visual"),
operatorShadow: visual(visuals.operator_shadow, "E46J shadow visual"),
},
};
}
@@ -0,0 +1,358 @@
import {
AdvancedLaboratoryContractError,
type LaboratoryFetch,
} from "./advancedResults";
import type { L3VisualBox } from "./l3PointPillarsVisualAudit";
import type { L31ClassCounts } from "./l31PointPillarsRavnoves";
export interface L32FrameSummary {
frameId: string;
frameIndex: number;
sessionSeconds: number;
sourcePointCount: number;
predictionCount: number;
classCounts: L31ClassCounts;
inferenceMs: number;
cameraSourceFrameIndex: number;
cameraSessionSeconds: number;
cameraDeltaMs: number;
}
export interface L32PointPillarsCameraReviewResult {
resultId: string;
createdAtUtc: string;
status: "camera-bound-review-rejects-current-candidate";
sourceSessionId: string;
sourceL31ResultId: string;
sourceE10PackId: string;
metrics: {
frameCount: number;
predictionCount: number;
classCounts: L31ClassCounts;
inferenceLatencyMs: { p50: number; p95: number; maximum: number };
reviewFrameCount: number;
reviewPredictionCount: number;
reviewVisibleProjectedBoxCount: number;
reviewProjectedPointCount: number;
scoreBelow025Fraction: number;
scoreBelow050Fraction: number;
cameraBindingAbsoluteDeltaMs: { p95: number; maximum: number };
};
frames: readonly L32FrameSummary[];
limitations: readonly string[];
}
export interface L32ProjectedBox {
modelClass: string;
score: number;
segmentsXyxy: readonly number[];
}
export interface L32VisualFrame {
frameId: string;
summary: L32FrameSummary;
cameraUrl: string;
cameraWidth: number;
cameraHeight: number;
modelRangePointCount: number;
pointsXyzi: readonly number[];
predictionBoxes: readonly L3VisualBox[];
projectedPointsXyd: readonly number[];
projectedBoxes: readonly L32ProjectedBox[];
}
export const L32_REVIEW_SCORE_THRESHOLD = 0.25;
const RESULT_ID = /^l32-pointpillars-camera-review-[a-f0-9]{64}$/;
const L31_RESULT_ID = /^l31-pointpillars-ravnoves-[a-f0-9]{64}$/;
const PACK_ID = /^e10-lidar-pack-[a-f0-9]{64}$/;
const FRAME_ID = /^[0-9]{6}$/;
function record(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new AdvancedLaboratoryContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function array(value: unknown, label: string): readonly unknown[] {
if (!Array.isArray(value)) {
throw new AdvancedLaboratoryContractError(`${label}: ожидался массив.`);
}
return value;
}
function string(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new AdvancedLaboratoryContractError(`${label}: ожидалась строка.`);
}
return value;
}
function exact<T extends string>(value: unknown, expected: T, label: string): T {
if (value !== expected) {
throw new AdvancedLaboratoryContractError(`${label}: нарушен контракт.`);
}
return expected;
}
function number(value: unknown, label: string, minimum = 0): number {
if (typeof value !== "number" || !Number.isFinite(value) || value < minimum) {
throw new AdvancedLaboratoryContractError(`${label}: неверное число.`);
}
return value;
}
function integer(value: unknown, label: string): number {
const parsed = number(value, label);
if (!Number.isInteger(parsed)) {
throw new AdvancedLaboratoryContractError(`${label}: ожидалось целое.`);
}
return parsed;
}
function counts(value: unknown, label: string): L31ClassCounts {
const item = record(value, label);
if (Object.keys(item).sort().join(",") !== "Cyclist,Pedestrian,Vehicle") {
throw new AdvancedLaboratoryContractError(`${label}: классы изменились.`);
}
return {
Vehicle: integer(item.Vehicle, `${label}.Vehicle`),
Pedestrian: integer(item.Pedestrian, `${label}.Pedestrian`),
Cyclist: integer(item.Cyclist, `${label}.Cyclist`),
};
}
function distribution(value: unknown, label: string) {
const item = record(value, label);
return {
p50: number(item.p50, `${label}.p50`),
p95: number(item.p95, `${label}.p95`),
maximum: number(item.maximum, `${label}.maximum`),
};
}
function parseSummary(value: unknown): L32FrameSummary {
const item = record(value, "L3.2 frame");
const frameId = string(item.frame_id, "L3.2 frame_id");
if (!FRAME_ID.test(frameId)) {
throw new AdvancedLaboratoryContractError("L3.2 frame_id: неверный формат.");
}
return {
frameId,
frameIndex: integer(item.frame_index, "L3.2 frame index"),
sessionSeconds: number(item.session_seconds, "L3.2 session seconds"),
sourcePointCount: integer(item.source_point_count, "L3.2 source points"),
predictionCount: integer(item.prediction_count, "L3.2 predictions"),
classCounts: counts(item.class_counts, "L3.2 classes"),
inferenceMs: number(item.inference_ms, "L3.2 inference", Number.MIN_VALUE),
cameraSourceFrameIndex: integer(
item.camera_source_frame_index,
"L3.2 camera frame",
),
cameraSessionSeconds: number(
item.camera_session_seconds,
"L3.2 camera seconds",
),
cameraDeltaMs: number(item.camera_delta_ms, "L3.2 camera delta", -100),
};
}
function parseBox(value: unknown): L3VisualBox {
const box = record(value, "L3.2 box");
const modelClass = string(box.model_class, "L3.2 box class");
return {
benchmarkClass: modelClass,
centerXyzM: [
number(box.x_m, "L3.2 box x", -Infinity),
number(box.y_m, "L3.2 box y", -Infinity),
number(box.z_m, "L3.2 box z", -Infinity),
],
sizeLwhM: [
number(box.length_m, "L3.2 box length", Number.MIN_VALUE),
number(box.width_m, "L3.2 box width", Number.MIN_VALUE),
number(box.height_m, "L3.2 box height", Number.MIN_VALUE),
],
yawRad: number(box.yaw_rad, "L3.2 box yaw", -Infinity),
status: "model-prediction",
score: number(box.score, "L3.2 box score"),
};
}
function parseResult(value: unknown): L32PointPillarsCameraReviewResult {
const item = record(value, "L3.2 result");
exact(
item.schema_version,
"missioncore.l32-pointpillars-camera-review-result/v1",
"L3.2 schema",
);
exact(item.access, "read-only", "L3.2 access");
const resultId = string(item.result_id, "L3.2 result id");
const sourceL31ResultId = string(item.source_l31_result_id, "L3.2 L3.1 id");
const sourceE10PackId = string(item.source_e10_pack_id, "L3.2 E10 id");
if (
!RESULT_ID.test(resultId)
|| !L31_RESULT_ID.test(sourceL31ResultId)
|| !PACK_ID.test(sourceE10PackId)
) {
throw new AdvancedLaboratoryContractError("L3.2: неверная identity.");
}
const metrics = record(item.metrics, "L3.2 metrics");
const cameraDelta = distribution(
metrics.camera_binding_absolute_delta_ms,
"L3.2 camera delta",
);
const frames = array(item.frames, "L3.2 frames").map(parseSummary);
if (!frames.length || frames.length > 18) {
throw new AdvancedLaboratoryContractError("L3.2 frames: нарушен bound.");
}
return {
resultId,
createdAtUtc: string(item.created_at_utc, "L3.2 created at"),
status: exact(
item.status,
"camera-bound-review-rejects-current-candidate",
"L3.2 status",
),
sourceSessionId: exact(
item.source_session_id,
"20260720T065719Z_viewer_live",
"L3.2 session",
),
sourceL31ResultId,
sourceE10PackId,
metrics: {
frameCount: integer(metrics.frame_count, "L3.2 frame count"),
predictionCount: integer(metrics.prediction_count, "L3.2 predictions"),
classCounts: counts(metrics.class_counts, "L3.2 metric classes"),
inferenceLatencyMs: distribution(
metrics.inference_latency_ms,
"L3.2 inference latency",
),
reviewFrameCount: integer(metrics.review_frame_count, "L3.2 review frames"),
reviewPredictionCount: integer(
metrics.review_prediction_count,
"L3.2 review predictions",
),
reviewVisibleProjectedBoxCount: integer(
metrics.review_visible_projected_box_count,
"L3.2 visible boxes",
),
reviewProjectedPointCount: integer(
metrics.review_projected_point_count,
"L3.2 projected points",
),
scoreBelow025Fraction: number(
metrics.score_below_0_25_fraction,
"L3.2 low score 025",
),
scoreBelow050Fraction: number(
metrics.score_below_0_50_fraction,
"L3.2 low score 050",
),
cameraBindingAbsoluteDeltaMs: cameraDelta,
},
frames,
limitations: array(item.limitations, "L3.2 limitations").map(
(entry) => string(entry, "L3.2 limitation"),
),
};
}
export async function fetchL32PointPillarsCameraReview({
fetcher = fetch,
signal,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
} = {}): Promise<L32PointPillarsCameraReviewResult | null> {
const response = await fetcher(
"/api/v1/laboratory/l32/pointpillars-camera-review/results?limit=1",
{ method: "GET", headers: { Accept: "application/json" }, signal },
);
if (!response.ok) {
throw new AdvancedLaboratoryContractError(
`L3.2 RAVNOVES недоступен: HTTP ${response.status}.`,
);
}
const catalog = record(await response.json(), "L3.2 catalog");
exact(
catalog.schema_version,
"missioncore.l32-pointpillars-camera-review-catalog-results/v1",
"L3.2 catalog schema",
);
const items = array(catalog.items, "L3.2 catalog items");
return items.length ? parseResult(items[0]) : null;
}
export async function fetchL32PointPillarsCameraReviewFrame(
resultId: string,
frameId: string,
{
fetcher = fetch,
signal,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
} = {},
): Promise<L32VisualFrame> {
if (!RESULT_ID.test(resultId) || !FRAME_ID.test(frameId)) {
throw new AdvancedLaboratoryContractError("L3.2 frame: неверная identity.");
}
const response = await fetcher(
`/api/v1/laboratory/l32/pointpillars-camera-review/${resultId}/frames/${frameId}`,
{ method: "GET", headers: { Accept: "application/json" }, signal },
);
if (!response.ok) {
throw new AdvancedLaboratoryContractError(
`L3.2 frame недоступен: HTTP ${response.status}.`,
);
}
const item = record(await response.json(), "L3.2 visual frame");
exact(
item.schema_version,
"missioncore.l32-pointpillars-camera-review-frame/v1",
"L3.2 frame schema",
);
const summary = parseSummary(item.summary);
const camera = record(item.camera, "L3.2 camera");
const points = record(item.points, "L3.2 points");
exact(points.layout, "flat-xyzi", "L3.2 point layout");
const values = array(points.values, "L3.2 point values").map(
(entry, index) => number(entry, `L3.2 points[${index}]`, -Infinity),
);
const projection = record(item.camera_projection, "L3.2 projection");
exact(projection.point_layout, "flat-xy-depth-m", "L3.2 projection layout");
const projected = array(projection.point_values, "L3.2 projected points").map(
(entry, index) => number(entry, `L3.2 projected[${index}]`, -Infinity),
);
if (projected.length !== integer(projection.point_count, "L3.2 point count") * 3) {
throw new AdvancedLaboratoryContractError("L3.2 projection: нарушен размер.");
}
return {
frameId,
summary,
cameraUrl: `/api/v1/laboratory/l32/pointpillars-camera-review/${resultId}/frames/${frameId}/camera`,
cameraWidth: integer(camera.width, "L3.2 camera width"),
cameraHeight: integer(camera.height, "L3.2 camera height"),
modelRangePointCount: integer(
points.model_range_point_count,
"L3.2 model range points",
),
pointsXyzi: values,
predictionBoxes: array(item.prediction_boxes, "L3.2 boxes").map(parseBox),
projectedPointsXyd: projected,
projectedBoxes: array(projection.boxes, "L3.2 projected boxes").map(
(entry) => {
const box = record(entry, "L3.2 projected box");
return {
modelClass: string(box.model_class, "L3.2 projected class"),
score: number(box.score, "L3.2 projected score"),
segmentsXyxy: array(box.segments_xyxy, "L3.2 projected segments").map(
(value, index) => number(value, `L3.2 segment[${index}]`, -Infinity),
),
};
},
),
};
}
@@ -0,0 +1,396 @@
import {
AdvancedLaboratoryContractError,
type LaboratoryFetch,
} from "./advancedResults";
import type { L3VisualBox } from "./l3PointPillarsVisualAudit";
export type L33SemanticProvenance =
| "recorded-current"
| "bounded-track-interpolation"
| "rectified-detector-current"
| "e23-temporal-hold";
export interface L33FrameSummary {
frameId: string;
frameIndex: number;
sessionSeconds: number;
cameraSourceFrameIndex: number;
cameraSessionSeconds: number;
cameraDeltaMs: number;
sourcePointCount: number;
detectionCount: number;
rangedDetectionCount: number;
cameraOnlyDetectionCount: number;
cuboidCount: number;
semanticProvenance: L33SemanticProvenance;
}
export interface L33CameraFirstDetectorReviewResult {
resultId: string;
createdAtUtc: string;
status: "camera-first-boundary-restored-shadow-only";
sourceSessionId: string;
sourceL32ResultId: string;
sourceE26ResultId: string;
sourceE29ResultId: string;
sourceE10PackId: string;
sourceWorldStateResultId: string;
detector: {
id: string;
architecture: string;
classes: string;
modelSha256: string;
inputShape: readonly number[];
};
metrics: {
reviewFrameCount: number;
recordedCurrentFrameCount: number;
recoveredCameraFrameCount: number;
semanticFrameCoverageFraction: number;
detectionCount: number;
rangedDetectionCount: number;
cameraOnlyDetectionCount: number;
rangeCoverageFraction: number;
minimumDetectorScore: number;
cuboidCount: number;
routeDetectionCount: number;
routeDetectionFrameCount: number;
framesProcessed: number;
effectiveComposedFps: number;
composedP95Ms: number;
semanticEffectiveFps: number;
lidarFusedFrames: number;
rawAcceptedCuboids: number;
duplicateSupportRejections: number;
temporalAcceptedCuboids: number;
worldObjectObservations: number;
classCounts: Readonly<Record<string, number>>;
};
frames: readonly L33FrameSummary[];
limitations: readonly string[];
}
export interface L33Detection {
sourceTrackId: number;
label: string;
associationGroup: string;
score: number;
bboxXyxy: readonly [number, number, number, number];
semanticProvenance: L33SemanticProvenance;
geometryStatus: string;
geometryReason: string;
rangeM: number | null;
sourceE29OpticalDepthM: number | null;
support: {
projectedPoints: number;
occupiedPoints: number;
connectedOccupiedPoints: number;
};
geometryAnchorXyzM: readonly [number, number, number] | null;
cameraRayLidar: {
originXyzM: readonly [number, number, number];
directionXyz: readonly [number, number, number];
};
}
export interface L33VisualFrame {
frameId: string;
summary: L33FrameSummary;
cameraUrl: string;
cameraWidth: number;
cameraHeight: number;
pointsXyzi: readonly number[];
projectedPointsXyd: readonly number[];
detections: readonly L33Detection[];
predictionBoxes: readonly L3VisualBox[];
}
const RESULT_ID = /^l33-camera-first-detector-review-[a-f0-9]{64}$/;
const SOURCE_ID = /^(?:l32-pointpillars-camera-review|e10-integrated-perception|e29-camera-geometry|e10-lidar-pack|rectified-camera-world-state)-[a-f0-9]{64}$/;
const FRAME_ID = /^[0-9]{6}$/;
function record(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new AdvancedLaboratoryContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function array(value: unknown, label: string): readonly unknown[] {
if (!Array.isArray(value)) {
throw new AdvancedLaboratoryContractError(`${label}: ожидался массив.`);
}
return value;
}
function string(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new AdvancedLaboratoryContractError(`${label}: ожидалась строка.`);
}
return value;
}
function exact<T extends string>(value: unknown, expected: T, label: string): T {
if (value !== expected) {
throw new AdvancedLaboratoryContractError(`${label}: нарушен контракт.`);
}
return expected;
}
function number(value: unknown, label: string, minimum = 0): number {
if (typeof value !== "number" || !Number.isFinite(value) || value < minimum) {
throw new AdvancedLaboratoryContractError(`${label}: неверное число.`);
}
return value;
}
function integer(value: unknown, label: string): number {
const parsed = number(value, label);
if (!Number.isInteger(parsed)) {
throw new AdvancedLaboratoryContractError(`${label}: ожидалось целое.`);
}
return parsed;
}
function provenance(value: unknown, label: string): L33SemanticProvenance {
if (
value !== "recorded-current"
&& value !== "bounded-track-interpolation"
&& value !== "rectified-detector-current"
&& value !== "e23-temporal-hold"
) {
throw new AdvancedLaboratoryContractError(`${label}: неизвестный источник.`);
}
return value;
}
function tuple(
value: unknown,
length: number,
label: string,
minimum = -Infinity,
): readonly number[] {
const values = array(value, label).map((item, index) => (
number(item, `${label}[${index}]`, minimum)
));
if (values.length !== length) {
throw new AdvancedLaboratoryContractError(`${label}: неверная размерность.`);
}
return values;
}
function sourceId(value: unknown, label: string): string {
const parsed = string(value, label);
if (!SOURCE_ID.test(parsed)) {
throw new AdvancedLaboratoryContractError(`${label}: неверная identity.`);
}
return parsed;
}
function parseSummary(value: unknown): L33FrameSummary {
const item = record(value, "L3.3 frame");
const frameId = string(item.frame_id, "L3.3 frame id");
if (!FRAME_ID.test(frameId)) {
throw new AdvancedLaboratoryContractError("L3.3 frame id: неверный формат.");
}
return {
frameId,
frameIndex: integer(item.frame_index, "L3.3 frame index"),
sessionSeconds: number(item.session_seconds, "L3.3 session seconds"),
cameraSourceFrameIndex: integer(item.camera_source_frame_index, "L3.3 camera frame"),
cameraSessionSeconds: number(item.camera_session_seconds, "L3.3 camera seconds"),
cameraDeltaMs: number(item.camera_delta_ms, "L3.3 camera delta", -100),
sourcePointCount: integer(item.source_point_count, "L3.3 source points"),
detectionCount: integer(item.detection_count, "L3.3 detections"),
rangedDetectionCount: integer(item.ranged_detection_count, "L3.3 ranged detections"),
cameraOnlyDetectionCount: integer(item.camera_only_detection_count, "L3.3 camera-only detections"),
cuboidCount: item.cuboid_count === undefined
? 0
: integer(item.cuboid_count, "L3.3 cuboids"),
semanticProvenance: provenance(item.semantic_provenance, "L3.3 provenance"),
};
}
function parsePredictionBox(value: unknown): L3VisualBox {
const item = record(value, "L3.3 cuboid");
exact(item.status, "model-prediction", "L3.3 cuboid status");
return {
benchmarkClass: string(item.benchmark_class, "L3.3 cuboid class"),
centerXyzM: tuple(item.center_xyz_m, 3, "L3.3 cuboid center", -Infinity) as [number, number, number],
sizeLwhM: tuple(item.size_lwh_m, 3, "L3.3 cuboid size", Number.MIN_VALUE) as [number, number, number],
yawRad: number(item.yaw_rad, "L3.3 cuboid yaw", -Infinity),
status: "model-prediction",
score: number(item.score, "L3.3 cuboid score"),
temporalStatus: string(item.temporal_status, "L3.3 cuboid temporal status"),
};
}
function parseDetection(value: unknown): L33Detection {
const item = record(value, "L3.3 detection");
const support = record(item.support, "L3.3 support");
const cameraRay = record(item.camera_ray_lidar, "L3.3 camera ray");
return {
sourceTrackId: integer(item.source_track_id, "L3.3 track"),
label: string(item.label, "L3.3 label"),
associationGroup: string(item.association_group, "L3.3 group"),
score: number(item.score, "L3.3 score", 0.25),
bboxXyxy: tuple(item.bbox_xyxy, 4, "L3.3 bbox", -Infinity) as [number, number, number, number],
semanticProvenance: provenance(item.semantic_provenance, "L3.3 detection provenance"),
geometryStatus: string(item.geometry_status, "L3.3 geometry status"),
geometryReason: string(item.geometry_reason, "L3.3 geometry reason"),
rangeM: item.range_m === null ? null : number(item.range_m, "L3.3 range"),
sourceE29OpticalDepthM: item.source_e29_optical_depth_m === null
? null
: number(item.source_e29_optical_depth_m, "L3.3 E29 optical depth"),
support: {
projectedPoints: integer(support.projected_points, "L3.3 projected support"),
occupiedPoints: integer(support.occupied_points, "L3.3 occupied support"),
connectedOccupiedPoints: integer(support.connected_occupied_points, "L3.3 connected support"),
},
geometryAnchorXyzM: item.geometry_anchor_xyz_m === null
? null
: tuple(item.geometry_anchor_xyz_m, 3, "L3.3 geometry anchor", -Infinity) as [number, number, number],
cameraRayLidar: {
originXyzM: tuple(cameraRay.origin_xyz_m, 3, "L3.3 ray origin", -Infinity) as [number, number, number],
directionXyz: tuple(cameraRay.direction_xyz, 3, "L3.3 ray direction", -Infinity) as [number, number, number],
},
};
}
function parseResult(value: unknown): L33CameraFirstDetectorReviewResult {
const item = record(value, "L3.3 result");
exact(item.schema_version, "missioncore.l33-camera-first-detector-review-result/v1", "L3.3 schema");
exact(item.access, "read-only", "L3.3 access");
const resultId = string(item.result_id, "L3.3 result id");
if (!RESULT_ID.test(resultId)) {
throw new AdvancedLaboratoryContractError("L3.3 result: неверная identity.");
}
const detector = record(item.detector, "L3.3 detector");
const metrics = record(item.metrics, "L3.3 metrics");
const classCounts = record(metrics.class_counts, "L3.3 class counts");
const frames = array(item.frames, "L3.3 frames").map(parseSummary);
if (!frames.length || frames.length > 18) {
throw new AdvancedLaboratoryContractError("L3.3 frames: нарушен bound.");
}
return {
resultId,
createdAtUtc: string(item.created_at_utc, "L3.3 created at"),
status: exact(item.status, "camera-first-boundary-restored-shadow-only", "L3.3 status"),
sourceSessionId: exact(item.source_session_id, "20260720T065719Z_viewer_live", "L3.3 session"),
sourceL32ResultId: sourceId(item.source_l32_result_id, "L3.3 L3.2 id"),
sourceE26ResultId: sourceId(item.source_e26_result_id, "L3.3 E26 id"),
sourceE29ResultId: sourceId(item.source_e29_result_id, "L3.3 E29 id"),
sourceE10PackId: sourceId(item.source_e10_pack_id, "L3.3 E10 id"),
sourceWorldStateResultId: sourceId(item.source_world_state_result_id, "L3.3 world-state id"),
detector: {
id: string(detector.id, "L3.3 detector id"),
architecture: string(detector.architecture, "L3.3 detector architecture"),
classes: string(detector.classes, "L3.3 detector classes"),
modelSha256: string(detector.model_sha256, "L3.3 detector sha"),
inputShape: tuple(detector.input_shape, 4, "L3.3 detector shape"),
},
metrics: {
reviewFrameCount: integer(metrics.review_frame_count, "L3.3 review frames"),
recordedCurrentFrameCount: integer(metrics.recorded_current_frame_count, "L3.3 current frames"),
recoveredCameraFrameCount: integer(metrics.recovered_camera_frame_count, "L3.3 recovered frames"),
semanticFrameCoverageFraction: number(metrics.semantic_frame_coverage_fraction, "L3.3 semantic coverage"),
detectionCount: integer(metrics.detection_count, "L3.3 detections"),
rangedDetectionCount: integer(metrics.ranged_detection_count, "L3.3 ranged"),
cameraOnlyDetectionCount: integer(metrics.camera_only_detection_count, "L3.3 camera only"),
rangeCoverageFraction: number(metrics.range_coverage_fraction, "L3.3 range coverage"),
minimumDetectorScore: number(metrics.minimum_detector_score, "L3.3 threshold"),
cuboidCount: integer(metrics.cuboid_count, "L3.3 selected cuboids"),
routeDetectionCount: integer(metrics.route_detection_count, "L3.3 route detections"),
routeDetectionFrameCount: integer(metrics.route_detection_frame_count, "L3.3 route detection frames"),
framesProcessed: integer(metrics.frames_processed, "L3.3 processed frames"),
effectiveComposedFps: number(metrics.effective_composed_fps, "L3.3 composed FPS", Number.MIN_VALUE),
composedP95Ms: number(metrics.composed_p95_ms, "L3.3 composed p95", Number.MIN_VALUE),
semanticEffectiveFps: number(metrics.semantic_effective_fps, "L3.3 semantic FPS", Number.MIN_VALUE),
lidarFusedFrames: integer(metrics.lidar_fused_frames, "L3.3 fused frames"),
rawAcceptedCuboids: integer(metrics.raw_accepted_cuboids, "L3.3 raw cuboids"),
duplicateSupportRejections: integer(
metrics.duplicate_support_rejections,
"L3.3 duplicate support rejections",
),
temporalAcceptedCuboids: integer(metrics.temporal_accepted_cuboids, "L3.3 temporal cuboids"),
worldObjectObservations: integer(metrics.world_object_observations, "L3.3 world objects"),
classCounts: Object.fromEntries(
Object.entries(classCounts).map(([key, entry]) => [key, integer(entry, `L3.3 class ${key}`)]),
),
},
frames,
limitations: array(item.limitations, "L3.3 limitations").map((entry) => string(entry, "L3.3 limitation")),
};
}
export async function fetchL33CameraFirstDetectorReview({
fetcher = fetch,
signal,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
} = {}): Promise<L33CameraFirstDetectorReviewResult | null> {
const response = await fetcher(
"/api/v1/laboratory/l33/camera-first-detector-review/results?limit=1",
{ method: "GET", headers: { Accept: "application/json" }, signal },
);
if (!response.ok) {
throw new AdvancedLaboratoryContractError(`L3.3 недоступен: HTTP ${response.status}.`);
}
const catalog = record(await response.json(), "L3.3 catalog");
exact(catalog.schema_version, "missioncore.l33-camera-first-detector-review-catalog-results/v1", "L3.3 catalog schema");
const items = array(catalog.items, "L3.3 catalog items");
return items.length ? parseResult(items[0]) : null;
}
export async function fetchL33CameraFirstDetectorReviewFrame(
resultId: string,
frameId: string,
{
fetcher = fetch,
signal,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
} = {},
): Promise<L33VisualFrame> {
if (!RESULT_ID.test(resultId) || !FRAME_ID.test(frameId)) {
throw new AdvancedLaboratoryContractError("L3.3 frame: неверная identity.");
}
const response = await fetcher(
`/api/v1/laboratory/l33/camera-first-detector-review/${resultId}/frames/${frameId}`,
{ method: "GET", headers: { Accept: "application/json" }, signal },
);
if (!response.ok) {
throw new AdvancedLaboratoryContractError(`L3.3 frame недоступен: HTTP ${response.status}.`);
}
const item = record(await response.json(), "L3.3 visual frame");
exact(item.schema_version, "missioncore.l33-camera-first-detector-review-frame/v1", "L3.3 frame schema");
const summary = parseSummary(item.summary);
const camera = record(item.camera, "L3.3 camera");
const points = record(item.points, "L3.3 points");
exact(points.layout, "flat-xyzi", "L3.3 point layout");
const projection = record(item.camera_projection, "L3.3 projection");
exact(projection.point_layout, "flat-xy-depth-m", "L3.3 projection layout");
const projected = array(projection.point_values, "L3.3 projected points").map(
(entry, index) => number(entry, `L3.3 projected[${index}]`, -Infinity),
);
if (projected.length !== integer(projection.point_count, "L3.3 projected count") * 3) {
throw new AdvancedLaboratoryContractError("L3.3 projection: нарушен размер.");
}
const detections = array(item.detections, "L3.3 detections").map(parseDetection);
const predictionBoxes = array(item.prediction_boxes, "L3.3 cuboids").map(parsePredictionBox);
return {
frameId,
summary,
cameraUrl: `/api/v1/laboratory/l33/camera-first-detector-review/${resultId}/frames/${frameId}/camera`,
cameraWidth: integer(camera.width, "L3.3 camera width"),
cameraHeight: integer(camera.height, "L3.3 camera height"),
pointsXyzi: array(points.values, "L3.3 point values").map(
(entry, index) => number(entry, `L3.3 points[${index}]`, -Infinity),
),
projectedPointsXyd: projected,
detections,
predictionBoxes,
};
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,436 @@
export interface L34FrameSummary {
truthIslandSequence: number;
imageId: number;
frameIndex: number;
groupId: string;
predictionCount: number;
maximumScore: number;
}
export type L34PredictionLabel =
| "person"
| "bicycle"
| "motorcycle"
| "car"
| "heavy_vehicle"
| "static_obstacle"
| "animal";
export interface L34Prediction {
label: L34PredictionLabel;
score: number;
bboxXyxy: readonly [number, number, number, number];
}
export interface L34VisualFrame {
resultId: string;
truthIslandSequence: number;
imageId: number;
frameIndex: number;
groupId: string;
role: "anchor" | "temporal";
sessionSeconds: number;
cameraUrl: string;
cameraWidth: number;
cameraHeight: number;
cameraSha256: string;
predictionRowsSha256: string;
predictions: readonly L34Prediction[];
truthLabelsRead: false;
}
export interface L34RightYoloxTruthIslandResult {
resultId: string;
createdAtUtc: string;
status: "predictions-frozen-awaiting-independent-truth";
profileId: "RAVNOVES00_RIGHT_YOLOX_TRUTH_ISLAND_V1";
pipelineId: string;
sourceSessionId: string;
cameraSourceId: "sensor.camera.right";
candidate: {
architecture: "YOLOX-S";
modelSha256: string;
minimumScore: number;
l33ResultId: string;
};
truthIsland: {
resultId: string;
truthState: "labels-unavailable";
};
metrics: {
frameCount: number;
temporalGroupCount: number;
predictionCount: number;
framesWithPredictions: number;
classCounts: Readonly<Record<string, number>>;
accuracyMetricsAvailable: false;
};
frames: readonly L34FrameSummary[];
decision: {
candidatePredictionsFrozen: true;
truthLabelsRead: false;
candidateAccepted: false;
modelRetrainingAuthorized: false;
nextGate: string;
};
limitations: readonly string[];
access: "read-only";
}
class L34ContractError extends Error {}
type LaboratoryFetch = (
input: RequestInfo | URL,
init?: RequestInit,
) => Promise<Response>;
function object(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new L34ContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function array(value: unknown, label: string): readonly unknown[] {
if (!Array.isArray(value)) {
throw new L34ContractError(`${label}: ожидался массив.`);
}
return value;
}
function string(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new L34ContractError(`${label}: ожидалась строка.`);
}
return value;
}
function integer(value: unknown, label: string): number {
if (!Number.isInteger(value) || Number(value) < 0) {
throw new L34ContractError(`${label}: ожидалось целое число.`);
}
return Number(value);
}
function finiteNumber(
value: unknown,
label: string,
minimum = 0,
): number {
if (typeof value !== "number" || !Number.isFinite(value) || value < minimum) {
throw new L34ContractError(`${label}: ожидалось конечное число.`);
}
return value;
}
function exact<T extends string | boolean>(
value: unknown,
expected: T,
label: string,
): T {
if (value !== expected) throw new L34ContractError(`${label}: нарушен контракт.`);
return expected;
}
function predictionLabel(value: unknown, label: string): L34PredictionLabel {
if (
value !== "person"
&& value !== "bicycle"
&& value !== "motorcycle"
&& value !== "car"
&& value !== "heavy_vehicle"
&& value !== "static_obstacle"
&& value !== "animal"
) {
throw new L34ContractError(`${label}: неизвестный класс.`);
}
return value;
}
function parseFrameSummary(value: unknown): L34FrameSummary {
const item = object(value, "L3.4 frame summary");
const maximumScore = finiteNumber(
item.maximum_score,
"L3.4 frame maximum score",
);
if (maximumScore > 1) {
throw new L34ContractError("L3.4 frame maximum score: нарушен диапазон.");
}
return {
truthIslandSequence: integer(
item.truth_island_sequence,
"L3.4 frame sequence",
),
imageId: integer(item.image_id, "L3.4 frame image"),
frameIndex: integer(item.frame_index, "L3.4 frame index"),
groupId: string(item.group_id, "L3.4 frame group"),
predictionCount: integer(
item.prediction_count,
"L3.4 frame predictions",
),
maximumScore,
};
}
function parse(itemValue: unknown): L34RightYoloxTruthIslandResult {
const item = object(itemValue, "L3.4");
const candidate = object(item.candidate, "L3.4.candidate");
const truthIsland = object(item.truth_island, "L3.4.truth_island");
const metrics = object(item.metrics, "L3.4.metrics");
const classCounts = object(metrics.class_counts, "L3.4.metrics.class_counts");
const decision = object(item.decision, "L3.4.decision");
const frames = array(item.frames, "L3.4.frames").map(parseFrameSummary);
const resultId = string(item.result_id, "L3.4.result_id");
const modelSha256 = string(candidate.model_sha256, "L3.4.candidate.model_sha256");
if (
!/^l34-right-yolox-truth-island-freeze-[a-f0-9]{64}$/.test(resultId)
|| !/^[a-f0-9]{64}$/.test(modelSha256)
) {
throw new L34ContractError("L3.4: нарушена content identity.");
}
const frameCount = integer(metrics.frame_count, "L3.4.metrics.frame_count");
const predictionCount = integer(
metrics.prediction_count,
"L3.4.metrics.prediction_count",
);
const framesWithPredictions = integer(
metrics.frames_with_predictions,
"L3.4.metrics.frames_with_predictions",
);
if (
!frames.length
|| frames.length > 32
|| frames.length !== frameCount
|| new Set(frames.map(({ truthIslandSequence }) => truthIslandSequence)).size
!== frames.length
|| frames.reduce((total, frame) => total + frame.predictionCount, 0)
!== predictionCount
|| frames.filter(({ predictionCount: count }) => count > 0).length
!== framesWithPredictions
) {
throw new L34ContractError("L3.4.frames: нарушена визуальная связность.");
}
const counts = Object.fromEntries(Object.entries(classCounts).map(([key, value]) => (
[key, integer(value, `L3.4.metrics.class_counts.${key}`)]
)));
const limitations = item.limitations;
if (!Array.isArray(limitations) || limitations.some((value) => typeof value !== "string")) {
throw new L34ContractError("L3.4.limitations: ожидался список строк.");
}
const minimumScore = candidate.minimum_score;
if (typeof minimumScore !== "number" || minimumScore !== 0.25) {
throw new L34ContractError("L3.4.candidate.minimum_score: нарушен контракт.");
}
return {
resultId,
createdAtUtc: string(item.created_at_utc, "L3.4.created_at_utc"),
status: exact(
item.status,
"predictions-frozen-awaiting-independent-truth",
"L3.4.status",
),
profileId: exact(
item.profile_id,
"RAVNOVES00_RIGHT_YOLOX_TRUTH_ISLAND_V1",
"L3.4.profile_id",
),
pipelineId: string(item.pipeline_id, "L3.4.pipeline_id"),
sourceSessionId: string(item.source_session_id, "L3.4.source_session_id"),
cameraSourceId: exact(
item.camera_source_id,
"sensor.camera.right",
"L3.4.camera_source_id",
),
candidate: {
architecture: exact(candidate.architecture, "YOLOX-S", "L3.4.architecture"),
modelSha256,
minimumScore,
l33ResultId: string(candidate.l33_result_id, "L3.4.candidate.l33_result_id"),
},
truthIsland: {
resultId: string(truthIsland.result_id, "L3.4.truth_island.result_id"),
truthState: exact(
truthIsland.truth_state,
"labels-unavailable",
"L3.4.truth_island.truth_state",
),
},
metrics: {
frameCount,
temporalGroupCount: integer(
metrics.temporal_group_count,
"L3.4.metrics.temporal_group_count",
),
predictionCount,
framesWithPredictions,
classCounts: counts,
accuracyMetricsAvailable: exact(
metrics.accuracy_metrics_available,
false,
"L3.4.metrics.accuracy_metrics_available",
),
},
frames,
decision: {
candidatePredictionsFrozen: exact(
decision.candidate_predictions_frozen,
true,
"L3.4.decision.candidate_predictions_frozen",
),
truthLabelsRead: exact(
decision.truth_labels_read,
false,
"L3.4.decision.truth_labels_read",
),
candidateAccepted: exact(
decision.candidate_accepted,
false,
"L3.4.decision.candidate_accepted",
),
modelRetrainingAuthorized: exact(
decision.model_retraining_authorized,
false,
"L3.4.decision.model_retraining_authorized",
),
nextGate: string(decision.next_gate, "L3.4.decision.next_gate"),
},
limitations: limitations as string[],
access: exact(item.access, "read-only", "L3.4.access"),
};
}
function parseVisualFrame(
value: unknown,
resultId: string,
sequence: number,
): L34VisualFrame {
const item = object(value, "L3.4 visual frame");
exact(
item.schema_version,
"missioncore.l34-right-yolox-truth-island-frame/v1",
"L3.4 frame schema",
);
exact(item.access, "read-only", "L3.4 frame access");
exact(item.truth_labels_read, false, "L3.4 frame truth state");
if (
item.result_id !== resultId
|| item.truth_island_sequence !== sequence
|| !Number.isInteger(sequence)
|| sequence < 1
|| sequence > 32
) {
throw new L34ContractError("L3.4 visual frame: нарушена identity.");
}
const camera = object(item.camera, "L3.4 frame camera");
const cameraWidth = integer(camera.width, "L3.4 camera width");
const cameraHeight = integer(camera.height, "L3.4 camera height");
const cameraSha256 = string(camera.sha256, "L3.4 camera sha256");
const predictionRowsSha256 = string(
item.prediction_rows_sha256,
"L3.4 prediction rows sha256",
);
if (
!/^[a-f0-9]{64}$/.test(cameraSha256)
|| !/^[a-f0-9]{64}$/.test(predictionRowsSha256)
) {
throw new L34ContractError("L3.4 visual frame: нарушена content identity.");
}
const predictions = array(item.predictions, "L3.4 frame predictions").map(
(value, index): L34Prediction => {
const prediction = object(value, `L3.4 prediction ${index}`);
const score = finiteNumber(prediction.score, `L3.4 prediction ${index} score`, 0.25);
const bbox = array(prediction.bbox_xyxy, `L3.4 prediction ${index} bbox`).map(
(entry, coordinate) => finiteNumber(
entry,
`L3.4 prediction ${index} bbox ${coordinate}`,
),
);
if (
score > 1
|| bbox.length !== 4
|| !(bbox[0] < bbox[2] && bbox[2] <= cameraWidth)
|| !(bbox[1] < bbox[3] && bbox[3] <= cameraHeight)
) {
throw new L34ContractError(`L3.4 prediction ${index}: нарушена геометрия.`);
}
return {
label: predictionLabel(prediction.label, `L3.4 prediction ${index} label`),
score,
bboxXyxy: bbox as [number, number, number, number],
};
},
);
const role = item.role;
if (role !== "anchor" && role !== "temporal") {
throw new L34ContractError("L3.4 frame role: нарушен контракт.");
}
return {
resultId,
truthIslandSequence: sequence,
imageId: integer(item.image_id, "L3.4 frame image"),
frameIndex: integer(item.frame_index, "L3.4 frame index"),
groupId: string(item.group_id, "L3.4 frame group"),
role,
sessionSeconds: finiteNumber(item.session_seconds, "L3.4 frame time"),
cameraUrl: `/api/v1/laboratory/l34/results/${resultId}/frames/${sequence}/camera`,
cameraWidth,
cameraHeight,
cameraSha256,
predictionRowsSha256,
predictions,
truthLabelsRead: false,
};
}
export async function fetchL34RightYoloxTruthIsland({
fetcher = fetch,
signal,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
} = {}): Promise<L34RightYoloxTruthIslandResult | null> {
const response = await fetcher("/api/v1/laboratory/l34/results?limit=1", {
method: "GET",
headers: { Accept: "application/json" },
signal,
});
if (!response.ok) throw new L34ContractError(`L3.4 недоступен: HTTP ${response.status}.`);
const catalog = object(await response.json(), "L3.4 catalog");
exact(
catalog.schema_version,
"missioncore.l34-right-yolox-truth-island-catalog/v1",
"L3.4 catalog.schema_version",
);
exact(catalog.access, "read-only", "L3.4 catalog.access");
if (!Array.isArray(catalog.items) || catalog.items.length > 1) {
throw new L34ContractError("L3.4 catalog.items: нарушен размер.");
}
return catalog.items.length ? parse(catalog.items[0]) : null;
}
export async function fetchL34RightYoloxTruthIslandFrame(
resultId: string,
sequence: number,
{
fetcher = fetch,
signal,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
} = {},
): Promise<L34VisualFrame> {
if (!/^l34-right-yolox-truth-island-freeze-[a-f0-9]{64}$/.test(resultId)) {
throw new L34ContractError("L3.4 visual frame: неверный result id.");
}
if (!Number.isInteger(sequence) || sequence < 1 || sequence > 32) {
throw new L34ContractError("L3.4 visual frame: неверный sequence.");
}
const response = await fetcher(
`/api/v1/laboratory/l34/results/${resultId}/frames/${sequence}`,
{ method: "GET", headers: { Accept: "application/json" }, signal },
);
if (!response.ok) {
throw new L34ContractError(`L3.4 visual frame недоступен: HTTP ${response.status}.`);
}
return parseVisualFrame(await response.json(), resultId, sequence);
}
@@ -0,0 +1,466 @@
export type L34APredictionVerdict =
| "true_positive"
| "class_mismatch"
| "duplicate_false_positive"
| "false_positive";
export type L34AAnnotationVerdict =
| "true_positive"
| "class_mismatch"
| "false_negative";
export interface L34ACaseSummaryMetrics {
predictionCount: number;
referenceCount: number;
truePositive: number;
falsePositive: number;
falseNegative: number;
classMismatch: number;
duplicateFalsePositive: number;
unmatchedFalsePositive: number;
unmatchedFalseNegative: number;
severityScore: number;
}
export interface L34ACaseSummary {
truthIslandSequence: number;
imageId: number;
frameIndex: number;
groupId: string;
sessionSeconds: number;
sourceImageSha256: string;
summary: L34ACaseSummaryMetrics;
}
export interface L34AClassMetric {
referenceCount: number;
truePositive: number;
falsePositive: number;
falseNegative: number;
precisionIou50: number;
recallIou50: number;
}
export interface L34AAssistedYoloxErrorAuditResult {
resultId: string;
createdAtUtc: string;
status: "completed-assisted-candidate-error-audit-not-truth";
profileId: string;
pipelineId: string;
sourceSessionId: string;
cameraSourceId: "sensor.camera.right";
assistedAnnotation: {
sessionId: string;
sessionSha256: string;
revision: number;
updatedAtUtc: string;
independentTruthEligible: false;
};
metrics: {
frameCount: number;
predictionCount: number;
referenceCount: number;
truePositive: number;
falsePositive: number;
falseNegative: number;
classMismatch: number;
duplicateFalsePositive: number;
unmatchedFalsePositive: number;
unmatchedFalseNegative: number;
precisionIou50: number;
recallIou50: number;
f1Iou50: number;
errorCaseCount: number;
customReferenceCount: number;
perClass: Readonly<Record<string, L34AClassMetric>>;
};
cases: readonly L34ACaseSummary[];
caseOrder: readonly number[];
decision: {
assistedAlignmentAvailable: true;
blindAccuracyAvailable: false;
postprocessingIssueConfirmed: boolean;
ontologyGapConfirmed: boolean;
candidateAccepted: false;
modelRetrainingAuthorized: false;
l35BlindGateOpen: false;
nextAction: string;
};
limitations: readonly string[];
groundTruth: false;
access: "read-only";
}
export interface L34AAuditPrediction {
predictionIndex: number;
category: string;
score: number;
boxXyxy: readonly [number, number, number, number];
verdict: L34APredictionVerdict;
matchedObjectId: string | null;
matchIou: number | null;
}
export interface L34AAuditAnnotation {
objectId: string;
category: string;
proposedLabel: string | null;
displayCategory: string;
origin: "manual" | "frozen_candidate_seed";
boxXyxy: readonly [number, number, number, number];
occluded: boolean;
truncated: boolean;
verdict: L34AAnnotationVerdict;
matchedPredictionIndex: number | null;
matchIou: number | null;
}
export interface L34AAuditCase {
resultId: string;
truthIslandSequence: number;
imageId: number;
frameIndex: number;
groupId: string;
sessionSeconds: number;
sourceImageSha256: string;
cameraWidth: number;
cameraHeight: number;
cameraUrl: string;
predictions: readonly L34AAuditPrediction[];
annotations: readonly L34AAuditAnnotation[];
summary: L34ACaseSummaryMetrics;
groundTruth: false;
access: "read-only";
}
export type L34ALaboratoryFetch = (
input: RequestInfo | URL,
init?: RequestInit,
) => Promise<Response>;
export class L34AContractError extends Error {
constructor(message: string) {
super(message);
this.name = "L34AContractError";
}
}
function object(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new L34AContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function string(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new L34AContractError(`${label}: ожидалась строка.`);
}
return value;
}
function exact<T>(value: unknown, expected: T, label: string): T {
if (value !== expected) {
throw new L34AContractError(`${label}: нарушен контракт.`);
}
return expected;
}
function integer(value: unknown, label: string): number {
if (!Number.isInteger(value) || Number(value) < 0) {
throw new L34AContractError(`${label}: ожидалось целое число.`);
}
return Number(value);
}
function finite(value: unknown, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new L34AContractError(`${label}: ожидалось число.`);
}
return value;
}
function boolean(value: unknown, label: string): boolean {
if (typeof value !== "boolean") {
throw new L34AContractError(`${label}: ожидалось логическое значение.`);
}
return value;
}
function ratio(value: unknown, label: string): number {
const parsed = finite(value, label);
if (parsed < 0 || parsed > 1) {
throw new L34AContractError(`${label}: нарушен диапазон.`);
}
return parsed;
}
function sha(value: unknown, label: string): string {
const parsed = string(value, label);
if (!/^[a-f0-9]{64}$/.test(parsed)) {
throw new L34AContractError(`${label}: нарушен SHA-256.`);
}
return parsed;
}
function box(value: unknown, label: string): readonly [number, number, number, number] {
if (!Array.isArray(value) || value.length !== 4) {
throw new L34AContractError(`${label}: ожидалась рамка xyxy.`);
}
const parsed = value.map((coordinate) => finite(coordinate, label));
if (!(0 <= parsed[0] && parsed[0] < parsed[2] && parsed[2] <= 800
&& 0 <= parsed[1] && parsed[1] < parsed[3] && parsed[3] <= 600)) {
throw new L34AContractError(`${label}: нарушена геометрия.`);
}
return parsed as [number, number, number, number];
}
function summary(value: unknown, label: string): L34ACaseSummaryMetrics {
const item = object(value, label);
return {
predictionCount: integer(item.prediction_count, `${label}.prediction_count`),
referenceCount: integer(item.reference_count, `${label}.reference_count`),
truePositive: integer(item.true_positive, `${label}.true_positive`),
falsePositive: integer(item.false_positive, `${label}.false_positive`),
falseNegative: integer(item.false_negative, `${label}.false_negative`),
classMismatch: integer(item.class_mismatch, `${label}.class_mismatch`),
duplicateFalsePositive: integer(item.duplicate_false_positive, `${label}.duplicate_false_positive`),
unmatchedFalsePositive: integer(item.unmatched_false_positive, `${label}.unmatched_false_positive`),
unmatchedFalseNegative: integer(item.unmatched_false_negative, `${label}.unmatched_false_negative`),
severityScore: integer(item.severity_score, `${label}.severity_score`),
};
}
function parseCaseSummary(value: unknown): L34ACaseSummary {
const item = object(value, "L3.4A case summary");
const sequence = integer(item.truth_island_sequence, "L3.4A case sequence");
if (sequence < 1 || sequence > 32) {
throw new L34AContractError("L3.4A case sequence: нарушен диапазон.");
}
return {
truthIslandSequence: sequence,
imageId: integer(item.image_id, "L3.4A case image"),
frameIndex: integer(item.frame_index, "L3.4A case frame"),
groupId: string(item.group_id, "L3.4A case group"),
sessionSeconds: finite(item.session_seconds, "L3.4A case time"),
sourceImageSha256: sha(item.source_image_sha256, "L3.4A case source"),
summary: summary(item.summary, "L3.4A case summary.metrics"),
};
}
function parseClassMetrics(value: unknown): Readonly<Record<string, L34AClassMetric>> {
const metrics = object(value, "L3.4A per-class metrics");
return Object.fromEntries(Object.entries(metrics).map(([category, raw]) => {
const item = object(raw, `L3.4A class ${category}`);
return [category, {
referenceCount: integer(item.reference_count, `${category}.reference_count`),
truePositive: integer(item.true_positive, `${category}.true_positive`),
falsePositive: integer(item.false_positive, `${category}.false_positive`),
falseNegative: integer(item.false_negative, `${category}.false_negative`),
precisionIou50: ratio(item.precision_iou50, `${category}.precision_iou50`),
recallIou50: ratio(item.recall_iou50, `${category}.recall_iou50`),
} satisfies L34AClassMetric];
}));
}
function parseResult(value: unknown): L34AAssistedYoloxErrorAuditResult {
const item = object(value, "L3.4A");
const resultId = string(item.result_id, "L3.4A.result_id");
if (!/^l34a-assisted-yolox-error-audit-[a-f0-9]{64}$/.test(resultId)) {
throw new L34AContractError("L3.4A.result_id: нарушена идентичность.");
}
const assisted = object(item.assisted_annotation, "L3.4A assisted annotation");
const metrics = object(item.metrics, "L3.4A metrics");
const decision = object(item.decision, "L3.4A decision");
const cases = Array.isArray(item.cases) ? item.cases.map(parseCaseSummary) : [];
if (cases.length !== 32 || new Set(cases.map((entry) => entry.truthIslandSequence)).size !== 32) {
throw new L34AContractError("L3.4A cases: требуется 32 уникальных кадра.");
}
if (!Array.isArray(item.case_order) || item.case_order.length !== 32) {
throw new L34AContractError("L3.4A case order: нарушен размер.");
}
const caseOrder = item.case_order.map((entry) => integer(entry, "L3.4A case order"));
if (new Set(caseOrder).size !== 32 || caseOrder.some((entry) => entry < 1 || entry > 32)) {
throw new L34AContractError("L3.4A case order: нарушено покрытие.");
}
if (!Array.isArray(item.limitations) || item.limitations.some((entry) => typeof entry !== "string")) {
throw new L34AContractError("L3.4A limitations: ожидался список строк.");
}
const sessionId = string(assisted.session_id, "L3.4A annotation session");
if (!/^l34-annotation-session-[a-f0-9]{64}$/.test(sessionId)) {
throw new L34AContractError("L3.4A annotation session: нарушена идентичность.");
}
return {
resultId,
createdAtUtc: string(item.created_at_utc, "L3.4A.created_at_utc"),
status: exact(item.status, "completed-assisted-candidate-error-audit-not-truth", "L3.4A.status"),
profileId: string(item.profile_id, "L3.4A.profile_id"),
pipelineId: string(item.pipeline_id, "L3.4A.pipeline_id"),
sourceSessionId: string(item.source_session_id, "L3.4A.source_session_id"),
cameraSourceId: exact(item.camera_source_id, "sensor.camera.right", "L3.4A.camera_source_id"),
assistedAnnotation: {
sessionId,
sessionSha256: sha(assisted.session_sha256, "L3.4A annotation sha"),
revision: integer(assisted.revision, "L3.4A annotation revision"),
updatedAtUtc: string(assisted.updated_at_utc, "L3.4A annotation time"),
independentTruthEligible: exact(assisted.independent_truth_eligible, false, "L3.4A independent truth"),
},
metrics: {
frameCount: integer(metrics.frame_count, "L3.4A frame count"),
predictionCount: integer(metrics.prediction_count, "L3.4A prediction count"),
referenceCount: integer(metrics.reference_count, "L3.4A reference count"),
truePositive: integer(metrics.true_positive, "L3.4A TP"),
falsePositive: integer(metrics.false_positive, "L3.4A FP"),
falseNegative: integer(metrics.false_negative, "L3.4A FN"),
classMismatch: integer(metrics.class_mismatch, "L3.4A mismatch"),
duplicateFalsePositive: integer(metrics.duplicate_false_positive, "L3.4A duplicates"),
unmatchedFalsePositive: integer(metrics.unmatched_false_positive, "L3.4A unmatched FP"),
unmatchedFalseNegative: integer(metrics.unmatched_false_negative, "L3.4A unmatched FN"),
precisionIou50: ratio(metrics.precision_iou50, "L3.4A precision"),
recallIou50: ratio(metrics.recall_iou50, "L3.4A recall"),
f1Iou50: ratio(metrics.f1_iou50, "L3.4A F1"),
errorCaseCount: integer(metrics.error_case_count, "L3.4A error cases"),
customReferenceCount: integer(metrics.custom_reference_count, "L3.4A custom references"),
perClass: parseClassMetrics(metrics.per_class),
},
cases,
caseOrder,
decision: {
assistedAlignmentAvailable: exact(decision.assisted_alignment_available, true, "L3.4A assisted alignment"),
blindAccuracyAvailable: exact(decision.blind_accuracy_available, false, "L3.4A blind accuracy"),
postprocessingIssueConfirmed: boolean(decision.postprocessing_issue_confirmed, "L3.4A postprocessing issue"),
ontologyGapConfirmed: boolean(decision.ontology_gap_confirmed, "L3.4A ontology gap"),
candidateAccepted: exact(decision.candidate_accepted, false, "L3.4A candidate acceptance"),
modelRetrainingAuthorized: exact(decision.model_retraining_authorized, false, "L3.4A retraining authority"),
l35BlindGateOpen: exact(decision.l35_blind_gate_open, false, "L3.5 gate"),
nextAction: string(decision.next_action, "L3.4A next action"),
},
limitations: item.limitations as string[],
groundTruth: exact(item.ground_truth, false, "L3.4A ground truth"),
access: exact(item.access, "read-only", "L3.4A access"),
};
}
const PREDICTION_VERDICTS: readonly L34APredictionVerdict[] = [
"true_positive",
"class_mismatch",
"duplicate_false_positive",
"false_positive",
];
const ANNOTATION_VERDICTS: readonly L34AAnnotationVerdict[] = [
"true_positive",
"class_mismatch",
"false_negative",
];
function nullableRatio(value: unknown, label: string): number | null {
return value === null ? null : ratio(value, label);
}
function parseCase(value: unknown, resultId: string, sequence: number): L34AAuditCase {
const item = object(value, "L3.4A visual case");
exact(item.schema_version, "missioncore.l34a-assisted-yolox-error-case/v1", "L3.4A case schema");
exact(item.result_id, resultId, "L3.4A case result");
exact(item.truth_island_sequence, sequence, "L3.4A case sequence");
const camera = object(item.camera, "L3.4A camera");
const rawPredictions = Array.isArray(item.predictions) ? item.predictions : [];
const rawAnnotations = Array.isArray(item.annotations) ? item.annotations : [];
const predictions = rawPredictions.map((raw, index): L34AAuditPrediction => {
const prediction = object(raw, `L3.4A prediction ${index + 1}`);
const verdict = string(prediction.verdict, "L3.4A prediction verdict") as L34APredictionVerdict;
if (!PREDICTION_VERDICTS.includes(verdict)) throw new L34AContractError("L3.4A prediction verdict: неизвестное значение.");
return {
predictionIndex: integer(prediction.prediction_index, "L3.4A prediction index"),
category: string(prediction.category, "L3.4A prediction category"),
score: ratio(prediction.score, "L3.4A prediction score"),
boxXyxy: box(prediction.box_xyxy, "L3.4A prediction box"),
verdict,
matchedObjectId: prediction.matched_object_id === null ? null : string(prediction.matched_object_id, "L3.4A matched object"),
matchIou: nullableRatio(prediction.match_iou, "L3.4A prediction IoU"),
};
});
const annotations = rawAnnotations.map((raw, index): L34AAuditAnnotation => {
const annotation = object(raw, `L3.4A annotation ${index + 1}`);
const verdict = string(annotation.verdict, "L3.4A annotation verdict") as L34AAnnotationVerdict;
if (!ANNOTATION_VERDICTS.includes(verdict)) throw new L34AContractError("L3.4A annotation verdict: неизвестное значение.");
return {
objectId: string(annotation.object_id, "L3.4A annotation id"),
category: string(annotation.category, "L3.4A annotation category"),
proposedLabel: annotation.proposed_label === null ? null : string(annotation.proposed_label, "L3.4A proposed label"),
displayCategory: string(annotation.display_category, "L3.4A display category"),
origin: exact(
annotation.origin,
annotation.origin === "manual" ? "manual" : "frozen_candidate_seed",
"L3.4A annotation origin",
),
boxXyxy: box(annotation.box_xyxy, "L3.4A annotation box"),
occluded: boolean(annotation.occluded, "L3.4A annotation occluded"),
truncated: boolean(annotation.truncated, "L3.4A annotation truncated"),
verdict,
matchedPredictionIndex: annotation.matched_prediction_index === null ? null : integer(annotation.matched_prediction_index, "L3.4A matched prediction"),
matchIou: nullableRatio(annotation.match_iou, "L3.4A annotation IoU"),
};
});
return {
resultId,
truthIslandSequence: sequence,
imageId: integer(item.image_id, "L3.4A case image"),
frameIndex: integer(item.frame_index, "L3.4A case frame"),
groupId: string(item.group_id, "L3.4A case group"),
sessionSeconds: finite(item.session_seconds, "L3.4A case time"),
sourceImageSha256: sha(item.source_image_sha256, "L3.4A case source"),
cameraWidth: integer(camera.width, "L3.4A camera width"),
cameraHeight: integer(camera.height, "L3.4A camera height"),
cameraUrl: `/api/v1/laboratory/l34a/results/${resultId}/cases/${sequence}/camera`,
predictions,
annotations,
summary: summary(item.summary, "L3.4A visual summary"),
groundTruth: exact(item.ground_truth, false, "L3.4A visual ground truth"),
access: exact(item.access, "read-only", "L3.4A visual access"),
};
}
export async function fetchL34AAssistedYoloxErrorAudit({
fetcher = fetch,
signal,
}: {
fetcher?: L34ALaboratoryFetch;
signal?: AbortSignal;
} = {}): Promise<L34AAssistedYoloxErrorAuditResult> {
const response = await fetcher("/api/v1/laboratory/l34a/results?limit=1", {
method: "GET",
headers: { Accept: "application/json" },
signal,
});
if (!response.ok) throw new L34AContractError(`L3.4A недоступен: HTTP ${response.status}.`);
const catalog = object(await response.json(), "L3.4A catalog");
exact(catalog.schema_version, "missioncore.l34a-assisted-yolox-error-catalog/v1", "L3.4A catalog schema");
exact(catalog.access, "read-only", "L3.4A catalog access");
if (!Array.isArray(catalog.items) || catalog.items.length !== 1) {
throw new L34AContractError("L3.4A catalog: ожидался один результат.");
}
return parseResult(catalog.items[0]);
}
export async function fetchL34AAuditCase(
resultId: string,
sequence: number,
{
fetcher = fetch,
signal,
}: {
fetcher?: L34ALaboratoryFetch;
signal?: AbortSignal;
} = {},
): Promise<L34AAuditCase> {
if (!/^l34a-assisted-yolox-error-audit-[a-f0-9]{64}$/.test(resultId) || !Number.isInteger(sequence) || sequence < 1 || sequence > 32) {
throw new L34AContractError("L3.4A visual case: неверная идентичность.");
}
const response = await fetcher(`/api/v1/laboratory/l34a/results/${resultId}/cases/${sequence}`, {
method: "GET",
headers: { Accept: "application/json" },
signal,
});
if (!response.ok) throw new L34AContractError(`L3.4A visual case недоступен: HTTP ${response.status}.`);
return parseCase(await response.json(), resultId, sequence);
}
@@ -0,0 +1,366 @@
import type {
L34AAuditAnnotation,
L34AAuditPrediction,
L34ACaseSummaryMetrics,
L34ALaboratoryFetch,
} from "./l34aAssistedYoloxErrorAudit";
export interface L34BMetricSet {
frameCount: number;
predictionCount: number;
referenceCount: number;
truePositive: number;
falsePositive: number;
falseNegative: number;
classMismatch: number;
duplicateFalsePositive: number;
unmatchedFalsePositive: number;
unmatchedFalseNegative: number;
precisionIou50: number;
recallIou50: number;
f1Iou50: number;
errorCaseCount: number;
customReferenceCount: number;
}
export interface L34BCaseSummary {
truthIslandSequence: number;
imageId: number;
frameIndex: number;
groupId: string;
sessionSeconds: number;
sourceImageSha256: string;
beforeSummary: L34ACaseSummaryMetrics;
afterSummary: L34ACaseSummaryMetrics;
consolidationCount: number;
}
export interface L34BResult {
resultId: string;
createdAtUtc: string;
status: "completed-nested-box-consolidation-shadow-not-truth";
profile: {
profileId: string;
categoryPolicy: string;
overlapMetric: string;
overlapThreshold: number;
geometryPolicy: string;
scorePolicy: string;
scope: string;
};
pipelineId: string;
sourceSessionId: string;
cameraSourceId: "sensor.camera.right";
metrics: {
before: L34BMetricSet;
after: L34BMetricSet;
delta: Readonly<Record<string, number>>;
consolidationCount: number;
affectedCaseCount: number;
assistedRegressionFree: boolean;
};
cases: readonly L34BCaseSummary[];
caseOrder: readonly number[];
decision: {
shadowPolicyAccepted: boolean;
classicIouNmsFixRejected: boolean;
remainingL34aDuplicateSignals: number;
nextAction: string;
};
limitations: readonly string[];
groundTruth: false;
access: "read-only";
}
export interface L34BPrediction extends L34AAuditPrediction {
sourcePredictionIndices: readonly number[];
}
export interface L34BConsolidation {
category: string;
sourcePredictionIndices: readonly number[];
sourceScores: readonly number[];
sourceBoxesXyxy: readonly (readonly [number, number, number, number])[];
mergedScore: number;
mergedBoxXyxy: readonly [number, number, number, number];
minimumOverlapOverSmaller: number;
outputPredictionIndex: number;
}
export interface L34BCase {
resultId: string;
truthIslandSequence: number;
imageId: number;
frameIndex: number;
groupId: string;
sessionSeconds: number;
sourceImageSha256: string;
cameraWidth: number;
cameraHeight: number;
cameraUrl: string;
beforePredictions: readonly L34AAuditPrediction[];
afterPredictions: readonly L34BPrediction[];
annotations: readonly L34AAuditAnnotation[];
consolidations: readonly L34BConsolidation[];
beforeSummary: L34ACaseSummaryMetrics;
afterSummary: L34ACaseSummaryMetrics;
groundTruth: false;
access: "read-only";
}
export class L34BContractError extends Error {
constructor(message: string) {
super(message);
this.name = "L34BContractError";
}
}
function object(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new L34BContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function text(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) throw new L34BContractError(`${label}: ожидалась строка.`);
return value;
}
function exact<T>(value: unknown, expected: T, label: string): T {
if (value !== expected) throw new L34BContractError(`${label}: нарушен контракт.`);
return expected;
}
function integer(value: unknown, label: string): number {
if (!Number.isInteger(value) || Number(value) < 0) throw new L34BContractError(`${label}: ожидалось целое число.`);
return Number(value);
}
function finite(value: unknown, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value)) throw new L34BContractError(`${label}: ожидалось число.`);
return value;
}
function ratio(value: unknown, label: string): number {
const parsed = finite(value, label);
if (parsed < 0 || parsed > 1) throw new L34BContractError(`${label}: нарушен диапазон.`);
return parsed;
}
function bool(value: unknown, label: string): boolean {
if (typeof value !== "boolean") throw new L34BContractError(`${label}: ожидалось логическое значение.`);
return value;
}
function box(value: unknown, label: string): readonly [number, number, number, number] {
if (!Array.isArray(value) || value.length !== 4) throw new L34BContractError(`${label}: ожидалась рамка.`);
const parsed = value.map((entry) => finite(entry, label));
if (!(0 <= parsed[0] && parsed[0] < parsed[2] && parsed[2] <= 800
&& 0 <= parsed[1] && parsed[1] < parsed[3] && parsed[3] <= 600)) {
throw new L34BContractError(`${label}: нарушена геометрия.`);
}
return parsed as [number, number, number, number];
}
function summary(value: unknown, label: string): L34ACaseSummaryMetrics {
const item = object(value, label);
return {
predictionCount: integer(item.prediction_count, `${label}.prediction_count`),
referenceCount: integer(item.reference_count, `${label}.reference_count`),
truePositive: integer(item.true_positive, `${label}.true_positive`),
falsePositive: integer(item.false_positive, `${label}.false_positive`),
falseNegative: integer(item.false_negative, `${label}.false_negative`),
classMismatch: integer(item.class_mismatch, `${label}.class_mismatch`),
duplicateFalsePositive: integer(item.duplicate_false_positive, `${label}.duplicate_false_positive`),
unmatchedFalsePositive: integer(item.unmatched_false_positive, `${label}.unmatched_false_positive`),
unmatchedFalseNegative: integer(item.unmatched_false_negative, `${label}.unmatched_false_negative`),
severityScore: integer(item.severity_score, `${label}.severity_score`),
};
}
function metrics(value: unknown, label: string): L34BMetricSet {
const item = object(value, label);
return {
frameCount: integer(item.frame_count, `${label}.frame_count`),
predictionCount: integer(item.prediction_count, `${label}.prediction_count`),
referenceCount: integer(item.reference_count, `${label}.reference_count`),
truePositive: integer(item.true_positive, `${label}.true_positive`),
falsePositive: integer(item.false_positive, `${label}.false_positive`),
falseNegative: integer(item.false_negative, `${label}.false_negative`),
classMismatch: integer(item.class_mismatch, `${label}.class_mismatch`),
duplicateFalsePositive: integer(item.duplicate_false_positive, `${label}.duplicate_false_positive`),
unmatchedFalsePositive: integer(item.unmatched_false_positive, `${label}.unmatched_false_positive`),
unmatchedFalseNegative: integer(item.unmatched_false_negative, `${label}.unmatched_false_negative`),
precisionIou50: ratio(item.precision_iou50, `${label}.precision_iou50`),
recallIou50: ratio(item.recall_iou50, `${label}.recall_iou50`),
f1Iou50: ratio(item.f1_iou50, `${label}.f1_iou50`),
errorCaseCount: integer(item.error_case_count, `${label}.error_case_count`),
customReferenceCount: integer(item.custom_reference_count, `${label}.custom_reference_count`),
};
}
function parseResult(value: unknown): L34BResult {
const item = object(value, "L3.4B");
const resultId = text(item.result_id, "L3.4B.result_id");
if (!/^l34b-nested-box-consolidation-shadow-[a-f0-9]{64}$/.test(resultId)) throw new L34BContractError("L3.4B.result_id: нарушена идентичность.");
const profile = object(item.profile, "L3.4B.profile");
const rawMetrics = object(item.metrics, "L3.4B.metrics");
const rawDecision = object(item.decision, "L3.4B.decision");
const cases = Array.isArray(item.cases) ? item.cases.map((raw): L34BCaseSummary => {
const current = object(raw, "L3.4B.case summary");
return {
truthIslandSequence: integer(current.truth_island_sequence, "L3.4B.case.sequence"),
imageId: integer(current.image_id, "L3.4B.case.image"),
frameIndex: integer(current.frame_index, "L3.4B.case.frame"),
groupId: text(current.group_id, "L3.4B.case.group"),
sessionSeconds: finite(current.session_seconds, "L3.4B.case.time"),
sourceImageSha256: text(current.source_image_sha256, "L3.4B.case.source"),
beforeSummary: summary(current.before_summary, "L3.4B.case.before"),
afterSummary: summary(current.after_summary, "L3.4B.case.after"),
consolidationCount: integer(current.consolidation_count, "L3.4B.case.consolidations"),
};
}) : [];
if (cases.length !== 32 || !Array.isArray(item.case_order) || item.case_order.length !== 32) throw new L34BContractError("L3.4B: требуется 32 кадра.");
const delta = object(rawMetrics.delta, "L3.4B.metrics.delta");
return {
resultId,
createdAtUtc: text(item.created_at_utc, "L3.4B.created_at"),
status: exact(item.status, "completed-nested-box-consolidation-shadow-not-truth", "L3.4B.status"),
profile: {
profileId: text(profile.profile_id, "L3.4B.profile.id"),
categoryPolicy: text(profile.category_policy, "L3.4B.profile.category"),
overlapMetric: text(profile.overlap_metric, "L3.4B.profile.metric"),
overlapThreshold: ratio(profile.overlap_threshold, "L3.4B.profile.threshold"),
geometryPolicy: text(profile.geometry_policy, "L3.4B.profile.geometry"),
scorePolicy: text(profile.score_policy, "L3.4B.profile.score"),
scope: text(profile.scope, "L3.4B.profile.scope"),
},
pipelineId: text(item.pipeline_id, "L3.4B.pipeline"),
sourceSessionId: text(item.source_session_id, "L3.4B.session"),
cameraSourceId: exact(item.camera_source_id, "sensor.camera.right", "L3.4B.camera"),
metrics: {
before: metrics(rawMetrics.before, "L3.4B.before"),
after: metrics(rawMetrics.after, "L3.4B.after"),
delta: Object.fromEntries(Object.entries(delta).map(([key, raw]) => [key, finite(raw, `L3.4B.delta.${key}`)])),
consolidationCount: integer(rawMetrics.consolidation_count, "L3.4B.consolidation_count"),
affectedCaseCount: integer(rawMetrics.affected_case_count, "L3.4B.affected_case_count"),
assistedRegressionFree: bool(rawMetrics.assisted_regression_free, "L3.4B.regression_free"),
},
cases,
caseOrder: item.case_order.map((entry) => integer(entry, "L3.4B.case_order")),
decision: {
shadowPolicyAccepted: bool(rawDecision.shadow_policy_accepted, "L3.4B.shadow accepted"),
classicIouNmsFixRejected: bool(rawDecision.classic_iou_nms_fix_rejected, "L3.4B.NMS rejected"),
remainingL34aDuplicateSignals: integer(rawDecision.remaining_l34a_duplicate_signals, "L3.4B.remaining signals"),
nextAction: text(rawDecision.next_action, "L3.4B.next action"),
},
limitations: Array.isArray(item.limitations) ? item.limitations.map((entry) => text(entry, "L3.4B.limitation")) : [],
groundTruth: exact(item.ground_truth, false, "L3.4B.truth"),
access: exact(item.access, "read-only", "L3.4B.access"),
};
}
const PREDICTION_VERDICTS = new Set(["true_positive", "class_mismatch", "duplicate_false_positive", "false_positive"]);
const ANNOTATION_VERDICTS = new Set(["true_positive", "class_mismatch", "false_negative"]);
function parsePrediction(raw: unknown, label: string, withSources: boolean): L34BPrediction {
const item = object(raw, label);
const verdict = text(item.verdict, `${label}.verdict`);
if (!PREDICTION_VERDICTS.has(verdict)) throw new L34BContractError(`${label}: неизвестный verdict.`);
return {
predictionIndex: integer(item.prediction_index, `${label}.index`),
category: text(item.category, `${label}.category`),
score: ratio(item.score, `${label}.score`),
boxXyxy: box(item.box_xyxy, `${label}.box`),
verdict: verdict as L34AAuditPrediction["verdict"],
matchedObjectId: item.matched_object_id === null ? null : text(item.matched_object_id, `${label}.match`),
matchIou: item.match_iou === null ? null : ratio(item.match_iou, `${label}.iou`),
sourcePredictionIndices: withSources && Array.isArray(item.source_prediction_indices)
? item.source_prediction_indices.map((entry) => integer(entry, `${label}.source`))
: [integer(item.prediction_index, `${label}.index`)],
};
}
function parseAnnotation(raw: unknown, label: string): L34AAuditAnnotation {
const item = object(raw, label);
const verdict = text(item.verdict, `${label}.verdict`);
if (!ANNOTATION_VERDICTS.has(verdict)) throw new L34BContractError(`${label}: неизвестный verdict.`);
return {
objectId: text(item.object_id, `${label}.id`),
category: text(item.category, `${label}.category`),
proposedLabel: item.proposed_label === null ? null : text(item.proposed_label, `${label}.proposed`),
displayCategory: text(item.display_category, `${label}.display`),
origin: item.origin === "manual" ? "manual" : "frozen_candidate_seed",
boxXyxy: box(item.box_xyxy, `${label}.box`),
occluded: bool(item.occluded, `${label}.occluded`),
truncated: bool(item.truncated, `${label}.truncated`),
verdict: verdict as L34AAuditAnnotation["verdict"],
matchedPredictionIndex: item.matched_prediction_index === null ? null : integer(item.matched_prediction_index, `${label}.match`),
matchIou: item.match_iou === null ? null : ratio(item.match_iou, `${label}.iou`),
};
}
function parseCase(value: unknown, resultId: string, sequence: number): L34BCase {
const item = object(value, "L3.4B.case");
exact(item.schema_version, "missioncore.l34b-nested-box-consolidation-case/v1", "L3.4B.case.schema");
exact(item.result_id, resultId, "L3.4B.case.result");
exact(item.truth_island_sequence, sequence, "L3.4B.case.sequence");
const camera = object(item.camera, "L3.4B.case.camera");
const consolidations = Array.isArray(item.consolidations) ? item.consolidations.map((raw): L34BConsolidation => {
const current = object(raw, "L3.4B.consolidation");
return {
category: text(current.category, "L3.4B.consolidation.category"),
sourcePredictionIndices: Array.isArray(current.source_prediction_indices) ? current.source_prediction_indices.map((entry) => integer(entry, "L3.4B.consolidation.source")) : [],
sourceScores: Array.isArray(current.source_scores) ? current.source_scores.map((entry) => ratio(entry, "L3.4B.consolidation.score")) : [],
sourceBoxesXyxy: Array.isArray(current.source_boxes_xyxy) ? current.source_boxes_xyxy.map((entry) => box(entry, "L3.4B.consolidation.source box")) : [],
mergedScore: ratio(current.merged_score, "L3.4B.consolidation.merged score"),
mergedBoxXyxy: box(current.merged_box_xyxy, "L3.4B.consolidation.merged box"),
minimumOverlapOverSmaller: ratio(current.minimum_overlap_over_smaller, "L3.4B.consolidation.overlap"),
outputPredictionIndex: integer(current.output_prediction_index, "L3.4B.consolidation.output"),
};
}) : [];
return {
resultId,
truthIslandSequence: sequence,
imageId: integer(item.image_id, "L3.4B.case.image"),
frameIndex: integer(item.frame_index, "L3.4B.case.frame"),
groupId: text(item.group_id, "L3.4B.case.group"),
sessionSeconds: finite(item.session_seconds, "L3.4B.case.time"),
sourceImageSha256: text(item.source_image_sha256, "L3.4B.case.source"),
cameraWidth: integer(camera.width, "L3.4B.case.width"),
cameraHeight: integer(camera.height, "L3.4B.case.height"),
cameraUrl: `/api/v1/laboratory/l34b/results/${resultId}/cases/${sequence}/camera`,
beforePredictions: Array.isArray(item.before_predictions) ? item.before_predictions.map((raw, index) => parsePrediction(raw, `L3.4B.before.${index}`, false)) : [],
afterPredictions: Array.isArray(item.after_predictions) ? item.after_predictions.map((raw, index) => parsePrediction(raw, `L3.4B.after.${index}`, true)) : [],
annotations: Array.isArray(item.annotations) ? item.annotations.map((raw, index) => parseAnnotation(raw, `L3.4B.annotation.${index}`)) : [],
consolidations,
beforeSummary: summary(item.before_summary, "L3.4B.case.before summary"),
afterSummary: summary(item.after_summary, "L3.4B.case.after summary"),
groundTruth: exact(item.ground_truth, false, "L3.4B.case.truth"),
access: exact(item.access, "read-only", "L3.4B.case.access"),
};
}
export async function fetchL34BNestedBoxConsolidation({
fetcher = fetch,
signal,
}: { fetcher?: L34ALaboratoryFetch; signal?: AbortSignal } = {}): Promise<L34BResult> {
const response = await fetcher("/api/v1/laboratory/l34b/results?limit=1", { method: "GET", headers: { Accept: "application/json" }, signal });
if (!response.ok) throw new L34BContractError(`L3.4B недоступен: HTTP ${response.status}.`);
const catalog = object(await response.json(), "L3.4B.catalog");
exact(catalog.schema_version, "missioncore.l34b-nested-box-consolidation-catalog/v1", "L3.4B.catalog.schema");
if (!Array.isArray(catalog.items) || catalog.items.length !== 1) throw new L34BContractError("L3.4B.catalog: ожидался один результат.");
return parseResult(catalog.items[0]);
}
export async function fetchL34BCase(
resultId: string,
sequence: number,
{ fetcher = fetch, signal }: { fetcher?: L34ALaboratoryFetch; signal?: AbortSignal } = {},
): Promise<L34BCase> {
if (!/^l34b-nested-box-consolidation-shadow-[a-f0-9]{64}$/.test(resultId) || !Number.isInteger(sequence) || sequence < 1 || sequence > 32) throw new L34BContractError("L3.4B.case: неверная идентичность.");
const response = await fetcher(`/api/v1/laboratory/l34b/results/${resultId}/cases/${sequence}`, { method: "GET", headers: { Accept: "application/json" }, signal });
if (!response.ok) throw new L34BContractError(`L3.4B.case недоступен: HTTP ${response.status}.`);
return parseCase(await response.json(), resultId, sequence);
}
@@ -0,0 +1,462 @@
import type {
L34AAuditAnnotation,
L34AAuditPrediction,
L34ACaseSummaryMetrics,
L34ALaboratoryFetch,
} from "./l34aAssistedYoloxErrorAudit";
import type { L34BMetricSet } from "./l34bNestedBoxConsolidation";
export type L34CRectificationTile = "front" | "left" | "right";
export interface L34CCaseSummary {
truthIslandSequence: number;
imageId: number;
frameIndex: number;
groupId: string;
sessionSeconds: number;
sourceImageSha256: string;
beforeSummary: L34ACaseSummaryMetrics;
afterSummary: L34ACaseSummaryMetrics;
stitchCount: number;
}
export interface L34CResult {
resultId: string;
createdAtUtc: string;
status: "completed-temporal-tile-seam-stitch-shadow-not-truth";
profile: {
profileId: string;
categoryPolicy: string;
tilePolicy: string;
horizontalOverlapThreshold: number;
verticalOverlapThreshold: number;
minimumUnionAreaFraction: number;
temporalUnionIouThreshold: number;
minimumConsecutiveFrames: number;
geometryPolicy: string;
scorePolicy: string;
scope: string;
};
pipelineId: string;
sourceSessionId: string;
cameraSourceId: "sensor.camera.right";
metrics: {
before: L34BMetricSet;
after: L34BMetricSet;
delta: Readonly<Record<string, number>>;
staticCandidateCount: number;
temporallyRejectedCandidateCount: number;
stitchCount: number;
affectedCaseCount: number;
temporalRunCount: number;
assistedRegressionFree: boolean;
};
cases: readonly L34CCaseSummary[];
caseOrder: readonly number[];
decision: {
shadowPolicyAccepted: boolean;
predictionProvenancePreserved: boolean;
globalNmsUnchanged: boolean;
remainingL34aDuplicateSignals: number;
nextAction: string;
};
limitations: readonly string[];
groundTruth: false;
access: "read-only";
}
export interface L34CBeforePrediction extends L34AAuditPrediction {
rectificationTile: L34CRectificationTile;
rawLabel: string;
classId: number;
rawCenterXy: readonly [number, number];
}
export interface L34CAfterPrediction extends L34AAuditPrediction {
sourcePredictionIndices: readonly number[];
sourceRectificationTiles: readonly L34CRectificationTile[];
}
export interface L34CStitch {
category: string;
sourcePredictionIndices: readonly number[];
sourceTiles: readonly L34CRectificationTile[];
sourceScores: readonly number[];
sourceBoxesXyxy: readonly (readonly [number, number, number, number])[];
mergedScore: number;
mergedBoxXyxy: readonly [number, number, number, number];
horizontalOverlapOverSmaller: number;
verticalOverlapOverSmaller: number;
sourceIou: number;
unionAreaFraction: number;
temporalRunId: string;
temporalRunLength: number;
}
export interface L34CCase {
resultId: string;
truthIslandSequence: number;
imageId: number;
frameIndex: number;
groupId: string;
sessionSeconds: number;
sourceImageSha256: string;
cameraWidth: number;
cameraHeight: number;
cameraUrl: string;
beforePredictions: readonly L34CBeforePrediction[];
afterPredictions: readonly L34CAfterPrediction[];
annotations: readonly L34AAuditAnnotation[];
stitches: readonly L34CStitch[];
beforeSummary: L34ACaseSummaryMetrics;
afterSummary: L34ACaseSummaryMetrics;
groundTruth: false;
access: "read-only";
}
export class L34CContractError extends Error {
constructor(message: string) {
super(message);
this.name = "L34CContractError";
}
}
function object(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new L34CContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function text(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new L34CContractError(`${label}: ожидалась строка.`);
}
return value;
}
function exact<T>(value: unknown, expected: T, label: string): T {
if (value !== expected) {
throw new L34CContractError(`${label}: нарушен контракт.`);
}
return expected;
}
function integer(value: unknown, label: string): number {
if (!Number.isInteger(value) || Number(value) < 0) {
throw new L34CContractError(`${label}: ожидалось целое число.`);
}
return Number(value);
}
function finite(value: unknown, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new L34CContractError(`${label}: ожидалось число.`);
}
return value;
}
function ratio(value: unknown, label: string): number {
const parsed = finite(value, label);
if (parsed < 0 || parsed > 1) {
throw new L34CContractError(`${label}: нарушен диапазон.`);
}
return parsed;
}
function bool(value: unknown, label: string): boolean {
if (typeof value !== "boolean") {
throw new L34CContractError(`${label}: ожидалось логическое значение.`);
}
return value;
}
function box(
value: unknown,
label: string,
): readonly [number, number, number, number] {
if (!Array.isArray(value) || value.length !== 4) {
throw new L34CContractError(`${label}: ожидалась рамка.`);
}
const parsed = value.map((entry) => finite(entry, label));
if (!(0 <= parsed[0] && parsed[0] < parsed[2] && parsed[2] <= 800
&& 0 <= parsed[1] && parsed[1] < parsed[3] && parsed[3] <= 600)) {
throw new L34CContractError(`${label}: нарушена геометрия.`);
}
return parsed as [number, number, number, number];
}
function point(value: unknown, label: string): readonly [number, number] {
if (!Array.isArray(value) || value.length !== 2) {
throw new L34CContractError(`${label}: ожидалась точка.`);
}
return [finite(value[0], label), finite(value[1], label)];
}
function tile(value: unknown, label: string): L34CRectificationTile {
if (value !== "front" && value !== "left" && value !== "right") {
throw new L34CContractError(`${label}: неизвестный rectification tile.`);
}
return value;
}
function summary(value: unknown, label: string): L34ACaseSummaryMetrics {
const item = object(value, label);
return {
predictionCount: integer(item.prediction_count, `${label}.prediction_count`),
referenceCount: integer(item.reference_count, `${label}.reference_count`),
truePositive: integer(item.true_positive, `${label}.true_positive`),
falsePositive: integer(item.false_positive, `${label}.false_positive`),
falseNegative: integer(item.false_negative, `${label}.false_negative`),
classMismatch: integer(item.class_mismatch, `${label}.class_mismatch`),
duplicateFalsePositive: integer(item.duplicate_false_positive, `${label}.duplicate_false_positive`),
unmatchedFalsePositive: integer(item.unmatched_false_positive, `${label}.unmatched_false_positive`),
unmatchedFalseNegative: integer(item.unmatched_false_negative, `${label}.unmatched_false_negative`),
severityScore: integer(item.severity_score, `${label}.severity_score`),
};
}
function metrics(value: unknown, label: string): L34BMetricSet {
const item = object(value, label);
return {
frameCount: integer(item.frame_count, `${label}.frame_count`),
predictionCount: integer(item.prediction_count, `${label}.prediction_count`),
referenceCount: integer(item.reference_count, `${label}.reference_count`),
truePositive: integer(item.true_positive, `${label}.true_positive`),
falsePositive: integer(item.false_positive, `${label}.false_positive`),
falseNegative: integer(item.false_negative, `${label}.false_negative`),
classMismatch: integer(item.class_mismatch, `${label}.class_mismatch`),
duplicateFalsePositive: integer(item.duplicate_false_positive, `${label}.duplicate_false_positive`),
unmatchedFalsePositive: integer(item.unmatched_false_positive, `${label}.unmatched_false_positive`),
unmatchedFalseNegative: integer(item.unmatched_false_negative, `${label}.unmatched_false_negative`),
precisionIou50: ratio(item.precision_iou50, `${label}.precision_iou50`),
recallIou50: ratio(item.recall_iou50, `${label}.recall_iou50`),
f1Iou50: ratio(item.f1_iou50, `${label}.f1_iou50`),
errorCaseCount: integer(item.error_case_count, `${label}.error_case_count`),
customReferenceCount: integer(item.custom_reference_count, `${label}.custom_reference_count`),
};
}
function parseResult(value: unknown): L34CResult {
const item = object(value, "L3.4C");
const resultId = text(item.result_id, "L3.4C.result_id");
if (!/^l34c-tile-seam-stitch-shadow-[a-f0-9]{64}$/.test(resultId)) {
throw new L34CContractError("L3.4C.result_id: нарушена идентичность.");
}
const profile = object(item.profile, "L3.4C.profile");
const rawMetrics = object(item.metrics, "L3.4C.metrics");
const rawDecision = object(item.decision, "L3.4C.decision");
const cases = Array.isArray(item.cases) ? item.cases.map((raw): L34CCaseSummary => {
const current = object(raw, "L3.4C.case summary");
return {
truthIslandSequence: integer(current.truth_island_sequence, "L3.4C.case.sequence"),
imageId: integer(current.image_id, "L3.4C.case.image"),
frameIndex: integer(current.frame_index, "L3.4C.case.frame"),
groupId: text(current.group_id, "L3.4C.case.group"),
sessionSeconds: finite(current.session_seconds, "L3.4C.case.time"),
sourceImageSha256: text(current.source_image_sha256, "L3.4C.case.source"),
beforeSummary: summary(current.before_summary, "L3.4C.case.before"),
afterSummary: summary(current.after_summary, "L3.4C.case.after"),
stitchCount: integer(current.stitch_count, "L3.4C.case.stitches"),
};
}) : [];
if (cases.length !== 32 || !Array.isArray(item.case_order) || item.case_order.length !== 32) {
throw new L34CContractError("L3.4C: требуется 32 кадра.");
}
const delta = object(rawMetrics.delta, "L3.4C.metrics.delta");
return {
resultId,
createdAtUtc: text(item.created_at_utc, "L3.4C.created_at"),
status: exact(item.status, "completed-temporal-tile-seam-stitch-shadow-not-truth", "L3.4C.status"),
profile: {
profileId: text(profile.profile_id, "L3.4C.profile.id"),
categoryPolicy: text(profile.category_policy, "L3.4C.profile.category"),
tilePolicy: text(profile.tile_policy, "L3.4C.profile.tile"),
horizontalOverlapThreshold: ratio(profile.horizontal_overlap_over_smaller_threshold, "L3.4C.profile.horizontal"),
verticalOverlapThreshold: ratio(profile.vertical_overlap_over_smaller_threshold, "L3.4C.profile.vertical"),
minimumUnionAreaFraction: ratio(profile.minimum_union_area_fraction, "L3.4C.profile.area"),
temporalUnionIouThreshold: ratio(profile.temporal_union_iou_threshold, "L3.4C.profile.temporal"),
minimumConsecutiveFrames: integer(profile.minimum_consecutive_frames, "L3.4C.profile.frames"),
geometryPolicy: text(profile.geometry_policy, "L3.4C.profile.geometry"),
scorePolicy: text(profile.score_policy, "L3.4C.profile.score"),
scope: text(profile.scope, "L3.4C.profile.scope"),
},
pipelineId: text(item.pipeline_id, "L3.4C.pipeline"),
sourceSessionId: text(item.source_session_id, "L3.4C.session"),
cameraSourceId: exact(item.camera_source_id, "sensor.camera.right", "L3.4C.camera"),
metrics: {
before: metrics(rawMetrics.before, "L3.4C.before"),
after: metrics(rawMetrics.after, "L3.4C.after"),
delta: Object.fromEntries(Object.entries(delta).map(([key, raw]) => [key, finite(raw, `L3.4C.delta.${key}`)])),
staticCandidateCount: integer(rawMetrics.static_candidate_count, "L3.4C.static candidates"),
temporallyRejectedCandidateCount: integer(rawMetrics.temporally_rejected_candidate_count, "L3.4C.rejected candidates"),
stitchCount: integer(rawMetrics.stitch_count, "L3.4C.stitch count"),
affectedCaseCount: integer(rawMetrics.affected_case_count, "L3.4C.affected cases"),
temporalRunCount: integer(rawMetrics.temporal_run_count, "L3.4C.run count"),
assistedRegressionFree: bool(rawMetrics.assisted_regression_free, "L3.4C.regression free"),
},
cases,
caseOrder: item.case_order.map((entry) => integer(entry, "L3.4C.case_order")),
decision: {
shadowPolicyAccepted: bool(rawDecision.shadow_policy_accepted, "L3.4C.shadow accepted"),
predictionProvenancePreserved: bool(rawDecision.prediction_provenance_preserved, "L3.4C.provenance"),
globalNmsUnchanged: bool(rawDecision.global_nms_unchanged, "L3.4C.NMS"),
remainingL34aDuplicateSignals: integer(rawDecision.remaining_l34a_duplicate_signals, "L3.4C.remaining signals"),
nextAction: text(rawDecision.next_action, "L3.4C.next action"),
},
limitations: Array.isArray(item.limitations) ? item.limitations.map((entry) => text(entry, "L3.4C.limitation")) : [],
groundTruth: exact(item.ground_truth, false, "L3.4C.truth"),
access: exact(item.access, "read-only", "L3.4C.access"),
};
}
const PREDICTION_VERDICTS = new Set(["true_positive", "class_mismatch", "duplicate_false_positive", "false_positive"]);
const ANNOTATION_VERDICTS = new Set(["true_positive", "class_mismatch", "false_negative"]);
function predictionBase(raw: unknown, label: string): L34AAuditPrediction {
const item = object(raw, label);
const verdict = text(item.verdict, `${label}.verdict`);
if (!PREDICTION_VERDICTS.has(verdict)) {
throw new L34CContractError(`${label}: неизвестный verdict.`);
}
return {
predictionIndex: integer(item.prediction_index, `${label}.index`),
category: text(item.category, `${label}.category`),
score: ratio(item.score, `${label}.score`),
boxXyxy: box(item.box_xyxy, `${label}.box`),
verdict: verdict as L34AAuditPrediction["verdict"],
matchedObjectId: item.matched_object_id === null ? null : text(item.matched_object_id, `${label}.match`),
matchIou: item.match_iou === null ? null : ratio(item.match_iou, `${label}.iou`),
};
}
function parseBeforePrediction(raw: unknown, label: string): L34CBeforePrediction {
const item = object(raw, label);
return {
...predictionBase(item, label),
rectificationTile: tile(item.rectification_tile, `${label}.tile`),
rawLabel: text(item.raw_label, `${label}.raw label`),
classId: integer(item.class_id, `${label}.class id`),
rawCenterXy: point(item.raw_center_xy, `${label}.raw center`),
};
}
function parseAfterPrediction(raw: unknown, label: string): L34CAfterPrediction {
const item = object(raw, label);
return {
...predictionBase(item, label),
sourcePredictionIndices: Array.isArray(item.source_prediction_indices)
? item.source_prediction_indices.map((entry) => integer(entry, `${label}.source`))
: [],
sourceRectificationTiles: Array.isArray(item.source_rectification_tiles)
? item.source_rectification_tiles.map((entry) => tile(entry, `${label}.tile`))
: [],
};
}
function parseAnnotation(raw: unknown, label: string): L34AAuditAnnotation {
const item = object(raw, label);
const verdict = text(item.verdict, `${label}.verdict`);
if (!ANNOTATION_VERDICTS.has(verdict)) {
throw new L34CContractError(`${label}: неизвестный verdict.`);
}
return {
objectId: text(item.object_id, `${label}.id`),
category: text(item.category, `${label}.category`),
proposedLabel: item.proposed_label === null ? null : text(item.proposed_label, `${label}.proposed`),
displayCategory: text(item.display_category, `${label}.display`),
origin: item.origin === "manual" ? "manual" : "frozen_candidate_seed",
boxXyxy: box(item.box_xyxy, `${label}.box`),
occluded: bool(item.occluded, `${label}.occluded`),
truncated: bool(item.truncated, `${label}.truncated`),
verdict: verdict as L34AAuditAnnotation["verdict"],
matchedPredictionIndex: item.matched_prediction_index === null ? null : integer(item.matched_prediction_index, `${label}.match`),
matchIou: item.match_iou === null ? null : ratio(item.match_iou, `${label}.iou`),
};
}
function parseStitch(raw: unknown): L34CStitch {
const item = object(raw, "L3.4C.stitch");
return {
category: text(item.category, "L3.4C.stitch.category"),
sourcePredictionIndices: Array.isArray(item.source_prediction_indices) ? item.source_prediction_indices.map((entry) => integer(entry, "L3.4C.stitch.source")) : [],
sourceTiles: Array.isArray(item.source_tiles) ? item.source_tiles.map((entry) => tile(entry, "L3.4C.stitch.tile")) : [],
sourceScores: Array.isArray(item.source_scores) ? item.source_scores.map((entry) => ratio(entry, "L3.4C.stitch.score")) : [],
sourceBoxesXyxy: Array.isArray(item.source_boxes_xyxy) ? item.source_boxes_xyxy.map((entry) => box(entry, "L3.4C.stitch.source box")) : [],
mergedScore: ratio(item.merged_score, "L3.4C.stitch.merged score"),
mergedBoxXyxy: box(item.merged_box_xyxy, "L3.4C.stitch.merged box"),
horizontalOverlapOverSmaller: ratio(item.horizontal_overlap_over_smaller, "L3.4C.stitch.horizontal"),
verticalOverlapOverSmaller: ratio(item.vertical_overlap_over_smaller, "L3.4C.stitch.vertical"),
sourceIou: ratio(item.source_iou, "L3.4C.stitch.iou"),
unionAreaFraction: ratio(item.union_area_fraction, "L3.4C.stitch.area"),
temporalRunId: text(item.temporal_run_id, "L3.4C.stitch.run"),
temporalRunLength: integer(item.temporal_run_length, "L3.4C.stitch.run length"),
};
}
function parseCase(value: unknown, resultId: string, sequence: number): L34CCase {
const item = object(value, "L3.4C.case");
exact(item.schema_version, "missioncore.l34c-tile-seam-stitch-case/v1", "L3.4C.case.schema");
exact(item.result_id, resultId, "L3.4C.case.result");
exact(item.truth_island_sequence, sequence, "L3.4C.case.sequence");
const camera = object(item.camera, "L3.4C.case.camera");
return {
resultId,
truthIslandSequence: sequence,
imageId: integer(item.image_id, "L3.4C.case.image"),
frameIndex: integer(item.frame_index, "L3.4C.case.frame"),
groupId: text(item.group_id, "L3.4C.case.group"),
sessionSeconds: finite(item.session_seconds, "L3.4C.case.time"),
sourceImageSha256: text(item.source_image_sha256, "L3.4C.case.source"),
cameraWidth: integer(camera.width, "L3.4C.case.width"),
cameraHeight: integer(camera.height, "L3.4C.case.height"),
cameraUrl: `/api/v1/laboratory/l34c/results/${resultId}/cases/${sequence}/camera`,
beforePredictions: Array.isArray(item.before_predictions) ? item.before_predictions.map((raw, index) => parseBeforePrediction(raw, `L3.4C.before.${index}`)) : [],
afterPredictions: Array.isArray(item.after_predictions) ? item.after_predictions.map((raw, index) => parseAfterPrediction(raw, `L3.4C.after.${index}`)) : [],
annotations: Array.isArray(item.annotations) ? item.annotations.map((raw, index) => parseAnnotation(raw, `L3.4C.annotation.${index}`)) : [],
stitches: Array.isArray(item.stitches) ? item.stitches.map(parseStitch) : [],
beforeSummary: summary(item.before_summary, "L3.4C.case.before summary"),
afterSummary: summary(item.after_summary, "L3.4C.case.after summary"),
groundTruth: exact(item.ground_truth, false, "L3.4C.case.truth"),
access: exact(item.access, "read-only", "L3.4C.case.access"),
};
}
export async function fetchL34CTileSeamStitch({
fetcher = fetch,
signal,
}: { fetcher?: L34ALaboratoryFetch; signal?: AbortSignal } = {}): Promise<L34CResult> {
const response = await fetcher("/api/v1/laboratory/l34c/results?limit=1", {
method: "GET",
headers: { Accept: "application/json" },
signal,
});
if (!response.ok) {
throw new L34CContractError(`L3.4C недоступен: HTTP ${response.status}.`);
}
const catalog = object(await response.json(), "L3.4C.catalog");
exact(catalog.schema_version, "missioncore.l34c-tile-seam-stitch-catalog/v1", "L3.4C.catalog.schema");
if (!Array.isArray(catalog.items) || catalog.items.length !== 1) {
throw new L34CContractError("L3.4C.catalog: ожидался один результат.");
}
return parseResult(catalog.items[0]);
}
export async function fetchL34CCase(
resultId: string,
sequence: number,
{ fetcher = fetch, signal }: { fetcher?: L34ALaboratoryFetch; signal?: AbortSignal } = {},
): Promise<L34CCase> {
if (!/^l34c-tile-seam-stitch-shadow-[a-f0-9]{64}$/.test(resultId)
|| !Number.isInteger(sequence) || sequence < 1 || sequence > 32) {
throw new L34CContractError("L3.4C.case: неверная идентичность.");
}
const response = await fetcher(
`/api/v1/laboratory/l34c/results/${resultId}/cases/${sequence}`,
{ method: "GET", headers: { Accept: "application/json" }, signal },
);
if (!response.ok) {
throw new L34CContractError(`L3.4C.case недоступен: HTTP ${response.status}.`);
}
return parseCase(await response.json(), resultId, sequence);
}
@@ -0,0 +1,535 @@
import type {
L34AAuditAnnotation,
L34AAuditPrediction,
L34ACaseSummaryMetrics,
L34ALaboratoryFetch,
} from "./l34aAssistedYoloxErrorAudit";
import type { L34BMetricSet } from "./l34bNestedBoxConsolidation";
import type {
L34CBeforePrediction,
L34CRectificationTile,
} from "./l34cTileSeamStitch";
export type L34DOperationType =
| "nested-box-consolidation"
| "temporal-tile-seam-stitch";
export interface L34DCaseSummary {
truthIslandSequence: number;
imageId: number;
frameIndex: number;
groupId: string;
sessionSeconds: number;
sourceImageSha256: string;
beforeSummary: L34ACaseSummaryMetrics;
afterSummary: L34ACaseSummaryMetrics;
operationCount: number;
operationTypes: readonly L34DOperationType[];
}
export interface L34DResult {
resultId: string;
createdAtUtc: string;
status: "completed-cumulative-postprocessing-candidate-freeze-not-truth";
profile: {
profileId: string;
operationOrder: string;
conflictPolicy: string;
geometryPolicy: string;
scorePolicy: string;
scope: string;
};
pipelineId: string;
sourceSessionId: string;
cameraSourceId: "sensor.camera.right";
metrics: {
before: L34BMetricSet;
after: L34BMetricSet;
delta: Readonly<Record<string, number>>;
nestedConsolidationCount: number;
temporalStitchCount: number;
cumulativeOperationCount: number;
affectedCaseCount: number;
operationConflictCount: number;
countEffectsAdditive: boolean;
assistedRegressionFree: boolean;
};
cases: readonly L34DCaseSummary[];
caseOrder: readonly number[];
decision: {
cumulativeShadowAccepted: boolean;
candidateFrozen: boolean;
candidateAccepted: boolean;
predictionProvenancePreserved: boolean;
operationSetsDisjoint: boolean;
globalNmsUnchanged: boolean;
independentTruthAvailable: boolean;
l35BlindGateOpen: boolean;
nextAction: string;
};
limitations: readonly string[];
groundTruth: false;
access: "read-only";
}
export interface L34DAfterPrediction extends L34AAuditPrediction {
sourcePredictionIndices: readonly number[];
sourceRectificationTiles: readonly L34CRectificationTile[];
operationTypes: readonly L34DOperationType[];
}
export interface L34DOperation {
operationType: L34DOperationType;
category: string;
sourcePredictionIndices: readonly number[];
sourceTiles: readonly L34CRectificationTile[];
sourceScores: readonly number[];
sourceBoxesXyxy: readonly (readonly [number, number, number, number])[];
mergedScore: number;
mergedBoxXyxy: readonly [number, number, number, number];
temporalRunId: string | null;
temporalRunLength: number | null;
}
export interface L34DCase {
resultId: string;
truthIslandSequence: number;
imageId: number;
frameIndex: number;
groupId: string;
sessionSeconds: number;
sourceImageSha256: string;
cameraWidth: number;
cameraHeight: number;
cameraUrl: string;
beforePredictions: readonly L34CBeforePrediction[];
afterPredictions: readonly L34DAfterPrediction[];
annotations: readonly L34AAuditAnnotation[];
operations: readonly L34DOperation[];
beforeSummary: L34ACaseSummaryMetrics;
afterSummary: L34ACaseSummaryMetrics;
groundTruth: false;
access: "read-only";
}
export class L34DContractError extends Error {
constructor(message: string) {
super(message);
this.name = "L34DContractError";
}
}
function object(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new L34DContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function text(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new L34DContractError(`${label}: ожидалась строка.`);
}
return value;
}
function exact<T>(value: unknown, expected: T, label: string): T {
if (value !== expected) {
throw new L34DContractError(`${label}: нарушен контракт.`);
}
return expected;
}
function integer(value: unknown, label: string): number {
if (!Number.isInteger(value) || Number(value) < 0) {
throw new L34DContractError(`${label}: ожидалось целое число.`);
}
return Number(value);
}
function finite(value: unknown, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new L34DContractError(`${label}: ожидалось число.`);
}
return value;
}
function ratio(value: unknown, label: string): number {
const parsed = finite(value, label);
if (parsed < 0 || parsed > 1) {
throw new L34DContractError(`${label}: нарушен диапазон.`);
}
return parsed;
}
function bool(value: unknown, label: string): boolean {
if (typeof value !== "boolean") {
throw new L34DContractError(`${label}: ожидалось логическое значение.`);
}
return value;
}
function box(
value: unknown,
label: string,
): readonly [number, number, number, number] {
if (!Array.isArray(value) || value.length !== 4) {
throw new L34DContractError(`${label}: ожидалась рамка.`);
}
const parsed = value.map((entry) => finite(entry, label));
if (!(0 <= parsed[0] && parsed[0] < parsed[2] && parsed[2] <= 800
&& 0 <= parsed[1] && parsed[1] < parsed[3] && parsed[3] <= 600)) {
throw new L34DContractError(`${label}: нарушена геометрия.`);
}
return parsed as [number, number, number, number];
}
function point(value: unknown, label: string): readonly [number, number] {
if (!Array.isArray(value) || value.length !== 2) {
throw new L34DContractError(`${label}: ожидалась точка.`);
}
return [finite(value[0], label), finite(value[1], label)];
}
function tile(value: unknown, label: string): L34CRectificationTile {
if (value !== "front" && value !== "left" && value !== "right") {
throw new L34DContractError(`${label}: неизвестный rectification tile.`);
}
return value;
}
function operationType(value: unknown, label: string): L34DOperationType {
if (value !== "nested-box-consolidation"
&& value !== "temporal-tile-seam-stitch") {
throw new L34DContractError(`${label}: неизвестная операция.`);
}
return value;
}
function summary(value: unknown, label: string): L34ACaseSummaryMetrics {
const item = object(value, label);
return {
predictionCount: integer(item.prediction_count, `${label}.prediction_count`),
referenceCount: integer(item.reference_count, `${label}.reference_count`),
truePositive: integer(item.true_positive, `${label}.true_positive`),
falsePositive: integer(item.false_positive, `${label}.false_positive`),
falseNegative: integer(item.false_negative, `${label}.false_negative`),
classMismatch: integer(item.class_mismatch, `${label}.class_mismatch`),
duplicateFalsePositive: integer(item.duplicate_false_positive, `${label}.duplicate_false_positive`),
unmatchedFalsePositive: integer(item.unmatched_false_positive, `${label}.unmatched_false_positive`),
unmatchedFalseNegative: integer(item.unmatched_false_negative, `${label}.unmatched_false_negative`),
severityScore: integer(item.severity_score, `${label}.severity_score`),
};
}
function metrics(value: unknown, label: string): L34BMetricSet {
const item = object(value, label);
return {
frameCount: integer(item.frame_count, `${label}.frame_count`),
predictionCount: integer(item.prediction_count, `${label}.prediction_count`),
referenceCount: integer(item.reference_count, `${label}.reference_count`),
truePositive: integer(item.true_positive, `${label}.true_positive`),
falsePositive: integer(item.false_positive, `${label}.false_positive`),
falseNegative: integer(item.false_negative, `${label}.false_negative`),
classMismatch: integer(item.class_mismatch, `${label}.class_mismatch`),
duplicateFalsePositive: integer(item.duplicate_false_positive, `${label}.duplicate_false_positive`),
unmatchedFalsePositive: integer(item.unmatched_false_positive, `${label}.unmatched_false_positive`),
unmatchedFalseNegative: integer(item.unmatched_false_negative, `${label}.unmatched_false_negative`),
precisionIou50: ratio(item.precision_iou50, `${label}.precision_iou50`),
recallIou50: ratio(item.recall_iou50, `${label}.recall_iou50`),
f1Iou50: ratio(item.f1_iou50, `${label}.f1_iou50`),
errorCaseCount: integer(item.error_case_count, `${label}.error_case_count`),
customReferenceCount: integer(item.custom_reference_count, `${label}.custom_reference_count`),
};
}
const PREDICTION_VERDICTS = new Set([
"true_positive",
"class_mismatch",
"duplicate_false_positive",
"false_positive",
]);
const ANNOTATION_VERDICTS = new Set([
"true_positive",
"class_mismatch",
"false_negative",
]);
function predictionBase(raw: unknown, label: string): L34AAuditPrediction {
const item = object(raw, label);
const verdict = text(item.verdict, `${label}.verdict`);
if (!PREDICTION_VERDICTS.has(verdict)) {
throw new L34DContractError(`${label}: неизвестный verdict.`);
}
return {
predictionIndex: integer(item.prediction_index, `${label}.index`),
category: text(item.category, `${label}.category`),
score: ratio(item.score, `${label}.score`),
boxXyxy: box(item.box_xyxy, `${label}.box`),
verdict: verdict as L34AAuditPrediction["verdict"],
matchedObjectId: item.matched_object_id === null
? null
: text(item.matched_object_id, `${label}.match`),
matchIou: item.match_iou === null
? null
: ratio(item.match_iou, `${label}.iou`),
};
}
function parseBeforePrediction(raw: unknown, label: string): L34CBeforePrediction {
const item = object(raw, label);
return {
...predictionBase(item, label),
rectificationTile: tile(item.rectification_tile, `${label}.tile`),
rawLabel: text(item.raw_label, `${label}.raw label`),
classId: integer(item.class_id, `${label}.class id`),
rawCenterXy: point(item.raw_center_xy, `${label}.raw center`),
};
}
function parseAfterPrediction(raw: unknown, label: string): L34DAfterPrediction {
const item = object(raw, label);
return {
...predictionBase(item, label),
sourcePredictionIndices: Array.isArray(item.source_prediction_indices)
? item.source_prediction_indices.map((entry) => integer(entry, `${label}.source`))
: [],
sourceRectificationTiles: Array.isArray(item.source_rectification_tiles)
? item.source_rectification_tiles.map((entry) => tile(entry, `${label}.tile`))
: [],
operationTypes: Array.isArray(item.operation_types)
? item.operation_types.map((entry) => operationType(entry, `${label}.operation`))
: [],
};
}
function parseAnnotation(raw: unknown, label: string): L34AAuditAnnotation {
const item = object(raw, label);
const verdict = text(item.verdict, `${label}.verdict`);
if (!ANNOTATION_VERDICTS.has(verdict)) {
throw new L34DContractError(`${label}: неизвестный verdict.`);
}
return {
objectId: text(item.object_id, `${label}.id`),
category: text(item.category, `${label}.category`),
proposedLabel: item.proposed_label === null
? null
: text(item.proposed_label, `${label}.proposed`),
displayCategory: text(item.display_category, `${label}.display`),
origin: item.origin === "manual" ? "manual" : "frozen_candidate_seed",
boxXyxy: box(item.box_xyxy, `${label}.box`),
occluded: bool(item.occluded, `${label}.occluded`),
truncated: bool(item.truncated, `${label}.truncated`),
verdict: verdict as L34AAuditAnnotation["verdict"],
matchedPredictionIndex: item.matched_prediction_index === null
? null
: integer(item.matched_prediction_index, `${label}.match`),
matchIou: item.match_iou === null
? null
: ratio(item.match_iou, `${label}.iou`),
};
}
function parseOperation(raw: unknown): L34DOperation {
const item = object(raw, "L3.4D.operation");
const type = operationType(item.operation_type, "L3.4D.operation.type");
return {
operationType: type,
category: text(item.category, "L3.4D.operation.category"),
sourcePredictionIndices: Array.isArray(item.source_prediction_indices)
? item.source_prediction_indices.map((entry) => integer(entry, "L3.4D.operation.source"))
: [],
sourceTiles: Array.isArray(item.source_tiles)
? item.source_tiles.map((entry) => tile(entry, "L3.4D.operation.tile"))
: [],
sourceScores: Array.isArray(item.source_scores)
? item.source_scores.map((entry) => ratio(entry, "L3.4D.operation.score"))
: [],
sourceBoxesXyxy: Array.isArray(item.source_boxes_xyxy)
? item.source_boxes_xyxy.map((entry) => box(entry, "L3.4D.operation.box"))
: [],
mergedScore: ratio(item.merged_score, "L3.4D.operation.merged score"),
mergedBoxXyxy: box(item.merged_box_xyxy, "L3.4D.operation.merged box"),
temporalRunId: type === "temporal-tile-seam-stitch"
? text(item.temporal_run_id, "L3.4D.operation.run")
: null,
temporalRunLength: type === "temporal-tile-seam-stitch"
? integer(item.temporal_run_length, "L3.4D.operation.run length")
: null,
};
}
function parseResult(value: unknown): L34DResult {
const item = object(value, "L3.4D");
const resultId = text(item.result_id, "L3.4D.result_id");
if (!/^l34d-cumulative-postprocessing-candidate-[a-f0-9]{64}$/.test(resultId)) {
throw new L34DContractError("L3.4D.result_id: нарушена идентичность.");
}
const profile = object(item.profile, "L3.4D.profile");
const rawMetrics = object(item.metrics, "L3.4D.metrics");
const rawDecision = object(item.decision, "L3.4D.decision");
const cases = Array.isArray(item.cases)
? item.cases.map((raw): L34DCaseSummary => {
const current = object(raw, "L3.4D.case summary");
return {
truthIslandSequence: integer(current.truth_island_sequence, "L3.4D.case.sequence"),
imageId: integer(current.image_id, "L3.4D.case.image"),
frameIndex: integer(current.frame_index, "L3.4D.case.frame"),
groupId: text(current.group_id, "L3.4D.case.group"),
sessionSeconds: finite(current.session_seconds, "L3.4D.case.time"),
sourceImageSha256: text(current.source_image_sha256, "L3.4D.case.source"),
beforeSummary: summary(current.before_summary, "L3.4D.case.before"),
afterSummary: summary(current.after_summary, "L3.4D.case.after"),
operationCount: integer(current.operation_count, "L3.4D.case.operations"),
operationTypes: Array.isArray(current.operation_types)
? current.operation_types.map((entry) => operationType(entry, "L3.4D.case.operation"))
: [],
};
})
: [];
if (cases.length !== 32 || !Array.isArray(item.case_order)
|| item.case_order.length !== 32) {
throw new L34DContractError("L3.4D: требуется 32 кадра.");
}
const delta = object(rawMetrics.delta, "L3.4D.metrics.delta");
return {
resultId,
createdAtUtc: text(item.created_at_utc, "L3.4D.created_at"),
status: exact(
item.status,
"completed-cumulative-postprocessing-candidate-freeze-not-truth",
"L3.4D.status",
),
profile: {
profileId: text(profile.profile_id, "L3.4D.profile.id"),
operationOrder: text(profile.operation_order, "L3.4D.profile.order"),
conflictPolicy: text(profile.conflict_policy, "L3.4D.profile.conflict"),
geometryPolicy: text(profile.geometry_policy, "L3.4D.profile.geometry"),
scorePolicy: text(profile.score_policy, "L3.4D.profile.score"),
scope: text(profile.scope, "L3.4D.profile.scope"),
},
pipelineId: text(item.pipeline_id, "L3.4D.pipeline"),
sourceSessionId: text(item.source_session_id, "L3.4D.session"),
cameraSourceId: exact(item.camera_source_id, "sensor.camera.right", "L3.4D.camera"),
metrics: {
before: metrics(rawMetrics.before, "L3.4D.before"),
after: metrics(rawMetrics.after, "L3.4D.after"),
delta: Object.fromEntries(
Object.entries(delta).map(([key, raw]) => [key, finite(raw, `L3.4D.delta.${key}`)]),
),
nestedConsolidationCount: integer(rawMetrics.nested_consolidation_count, "L3.4D.nested"),
temporalStitchCount: integer(rawMetrics.temporal_stitch_count, "L3.4D.stitches"),
cumulativeOperationCount: integer(rawMetrics.cumulative_operation_count, "L3.4D.operations"),
affectedCaseCount: integer(rawMetrics.affected_case_count, "L3.4D.affected"),
operationConflictCount: integer(rawMetrics.operation_conflict_count, "L3.4D.conflicts"),
countEffectsAdditive: bool(rawMetrics.count_effects_additive, "L3.4D.additive"),
assistedRegressionFree: bool(rawMetrics.assisted_regression_free, "L3.4D.regression"),
},
cases,
caseOrder: item.case_order.map((entry) => integer(entry, "L3.4D.case_order")),
decision: {
cumulativeShadowAccepted: bool(rawDecision.cumulative_shadow_accepted, "L3.4D.shadow"),
candidateFrozen: bool(rawDecision.candidate_frozen, "L3.4D.frozen"),
candidateAccepted: bool(rawDecision.candidate_accepted, "L3.4D.accepted"),
predictionProvenancePreserved: bool(rawDecision.prediction_provenance_preserved, "L3.4D.provenance"),
operationSetsDisjoint: bool(rawDecision.operation_sets_disjoint, "L3.4D.disjoint"),
globalNmsUnchanged: bool(rawDecision.global_nms_unchanged, "L3.4D.NMS"),
independentTruthAvailable: bool(rawDecision.independent_truth_available, "L3.4D.truth available"),
l35BlindGateOpen: bool(rawDecision.l35_blind_gate_open, "L3.4D.gate"),
nextAction: text(rawDecision.next_action, "L3.4D.next action"),
},
limitations: Array.isArray(item.limitations)
? item.limitations.map((entry) => text(entry, "L3.4D.limitation"))
: [],
groundTruth: exact(item.ground_truth, false, "L3.4D.truth"),
access: exact(item.access, "read-only", "L3.4D.access"),
};
}
function parseCase(value: unknown, resultId: string, sequence: number): L34DCase {
const item = object(value, "L3.4D.case");
exact(item.schema_version, "missioncore.l34d-cumulative-postprocessing-case/v1", "L3.4D.case.schema");
exact(item.result_id, resultId, "L3.4D.case.result");
exact(item.truth_island_sequence, sequence, "L3.4D.case.sequence");
const camera = object(item.camera, "L3.4D.case.camera");
return {
resultId,
truthIslandSequence: sequence,
imageId: integer(item.image_id, "L3.4D.case.image"),
frameIndex: integer(item.frame_index, "L3.4D.case.frame"),
groupId: text(item.group_id, "L3.4D.case.group"),
sessionSeconds: finite(item.session_seconds, "L3.4D.case.time"),
sourceImageSha256: text(item.source_image_sha256, "L3.4D.case.source"),
cameraWidth: integer(camera.width, "L3.4D.case.width"),
cameraHeight: integer(camera.height, "L3.4D.case.height"),
cameraUrl: `/api/v1/laboratory/l34d/results/${resultId}/cases/${sequence}/camera`,
beforePredictions: Array.isArray(item.before_predictions)
? item.before_predictions.map((raw, index) => parseBeforePrediction(raw, `L3.4D.before.${index}`))
: [],
afterPredictions: Array.isArray(item.after_predictions)
? item.after_predictions.map((raw, index) => parseAfterPrediction(raw, `L3.4D.after.${index}`))
: [],
annotations: Array.isArray(item.annotations)
? item.annotations.map((raw, index) => parseAnnotation(raw, `L3.4D.annotation.${index}`))
: [],
operations: Array.isArray(item.operations)
? item.operations.map(parseOperation)
: [],
beforeSummary: summary(item.before_summary, "L3.4D.case.before summary"),
afterSummary: summary(item.after_summary, "L3.4D.case.after summary"),
groundTruth: exact(item.ground_truth, false, "L3.4D.case.truth"),
access: exact(item.access, "read-only", "L3.4D.case.access"),
};
}
export async function fetchL34DCumulativePostprocessing({
fetcher = fetch,
signal,
}: {
fetcher?: L34ALaboratoryFetch;
signal?: AbortSignal;
} = {}): Promise<L34DResult> {
const response = await fetcher("/api/v1/laboratory/l34d/results?limit=1", {
method: "GET",
headers: { Accept: "application/json" },
signal,
});
if (!response.ok) {
throw new L34DContractError(`L3.4D недоступен: HTTP ${response.status}.`);
}
const catalog = object(await response.json(), "L3.4D.catalog");
exact(
catalog.schema_version,
"missioncore.l34d-cumulative-postprocessing-catalog/v1",
"L3.4D.catalog.schema",
);
if (!Array.isArray(catalog.items) || catalog.items.length !== 1) {
throw new L34DContractError("L3.4D.catalog: ожидался один результат.");
}
return parseResult(catalog.items[0]);
}
export async function fetchL34DCase(
resultId: string,
sequence: number,
{
fetcher = fetch,
signal,
}: {
fetcher?: L34ALaboratoryFetch;
signal?: AbortSignal;
} = {},
): Promise<L34DCase> {
if (!/^l34d-cumulative-postprocessing-candidate-[a-f0-9]{64}$/.test(resultId)
|| !Number.isInteger(sequence) || sequence < 1 || sequence > 32) {
throw new L34DContractError("L3.4D.case: неверная идентичность.");
}
const response = await fetcher(
`/api/v1/laboratory/l34d/results/${resultId}/cases/${sequence}`,
{ method: "GET", headers: { Accept: "application/json" }, signal },
);
if (!response.ok) {
throw new L34DContractError(`L3.4D.case недоступен: HTTP ${response.status}.`);
}
return parseCase(await response.json(), resultId, sequence);
}
@@ -0,0 +1,554 @@
import type { L34ALaboratoryFetch } from "./l34aAssistedYoloxErrorAudit";
export type L34EDiagnosticVerdict =
| "strict_alignment"
| "strict_class_mismatch"
| "localization_disagreement"
| "class_and_localization_disagreement"
| "prediction_only"
| "reference_only";
export interface L34EStrictCaseSummary {
predictionCount: number;
referenceCount: number;
truePositive: number;
falsePositive: number;
falseNegative: number;
classMismatch: number;
duplicateFalsePositive: number;
unmatchedFalsePositive: number;
unmatchedFalseNegative: number;
severityScore: number;
}
export interface L34EDiagnosticSummary {
predictionCount: number;
referenceCount: number;
associatedPairCount: number;
strictAlignment: number;
strictClassMismatch: number;
localizationDisagreement: number;
classAndLocalizationDisagreement: number;
predictionOnly: number;
referenceOnly: number;
candidateAssociationCoverage: number;
referenceAssociationCoverage: number;
severityScore: number;
}
export interface L34ECaseSummary {
truthIslandSequence: number;
imageId: number;
frameIndex: number;
groupId: string;
sessionSeconds: number;
sourceImageSha256: string;
strictSummary: L34EStrictCaseSummary;
diagnosticSummary: L34EDiagnosticSummary;
}
export interface L34EPrediction {
predictionIndex: number;
category: string;
score: number;
boxXyxy: readonly [number, number, number, number];
strictVerdict: string;
diagnosticVerdict: L34EDiagnosticVerdict;
associatedObjectId: string | null;
associationIou: number | null;
associationOverlapOverSmaller: number | null;
sourcePredictionIndices: readonly number[];
sourceRectificationTiles: readonly string[];
operationTypes: readonly string[];
}
export interface L34EReference {
objectId: string;
category: string;
displayCategory: string;
proposedLabel: string | null;
boxXyxy: readonly [number, number, number, number];
occluded: boolean;
truncated: boolean;
strictVerdict: string;
diagnosticVerdict: L34EDiagnosticVerdict;
associatedPredictionIndex: number | null;
associationIou: number | null;
associationOverlapOverSmaller: number | null;
}
export interface L34EAssociation {
predictionIndex: number;
objectId: string;
predictionCategory: string;
referenceCategory: string;
iou: number;
overlapOverSmaller: number;
classification: Exclude<
L34EDiagnosticVerdict,
"prediction_only" | "reference_only"
>;
}
export interface L34ECase {
resultId: string;
truthIslandSequence: number;
imageId: number;
frameIndex: number;
groupId: string;
sessionSeconds: number;
sourceImageSha256: string;
cameraWidth: number;
cameraHeight: number;
cameraUrl: string;
predictions: readonly L34EPrediction[];
references: readonly L34EReference[];
associations: readonly L34EAssociation[];
strictSummary: L34EStrictCaseSummary;
diagnosticSummary: L34EDiagnosticSummary;
groundTruth: false;
access: "read-only";
}
export interface L34EResult {
resultId: string;
createdAtUtc: string;
status: "completed-self-review-diagnostic-not-truth";
profile: {
profileId: string;
strictMatcher: string;
diagnosticMatcher: string;
classPolicy: string;
referencePolicy: string;
scope: string;
};
pipelineId: string;
sourceSessionId: string;
cameraSourceId: "sensor.camera.right";
metrics: {
strictIou50: {
frameCount: number;
predictionCount: number;
referenceCount: number;
truePositive: number;
falsePositive: number;
falseNegative: number;
classMismatch: number;
duplicateFalsePositive: number;
unmatchedFalsePositive: number;
unmatchedFalseNegative: number;
precisionIou50: number;
recallIou50: number;
f1Iou50: number;
errorCaseCount: number;
customReferenceCount: number;
};
diagnosticAssociation: Omit<L34EDiagnosticSummary, "severityScore"> & {
errorCaseCount: number;
};
frameCountDisagreement: {
candidateSurplusLowerBound: number;
referenceSurplusLowerBound: number;
equalCountFrameCount: number;
};
referenceQuality: "not-metric-grade-self-review";
};
cases: readonly L34ECaseSummary[];
caseOrder: readonly number[];
decision: {
selfReviewComplete: boolean;
diagnosticAlignmentAvailable: boolean;
metricGradeReferenceAvailable: boolean;
independentTruthAvailable: boolean;
detectorRetuningAuthorized: boolean;
candidateAccepted: boolean;
l35BlindGateOpen: boolean;
nextAction: string;
};
limitations: readonly string[];
groundTruth: false;
access: "read-only";
}
export class L34EContractError extends Error {
constructor(message: string) {
super(message);
this.name = "L34EContractError";
}
}
function object(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new L34EContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function text(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new L34EContractError(`${label}: ожидалась строка.`);
}
return value;
}
function exact<T>(value: unknown, expected: T, label: string): T {
if (value !== expected) {
throw new L34EContractError(`${label}: нарушен контракт.`);
}
return expected;
}
function finite(value: unknown, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new L34EContractError(`${label}: ожидалось число.`);
}
return value;
}
function integer(value: unknown, label: string): number {
const parsed = finite(value, label);
if (!Number.isInteger(parsed) || parsed < 0) {
throw new L34EContractError(`${label}: ожидалось целое число.`);
}
return parsed;
}
function ratio(value: unknown, label: string): number {
const parsed = finite(value, label);
if (parsed < 0 || parsed > 1) {
throw new L34EContractError(`${label}: нарушен диапазон.`);
}
return parsed;
}
function bool(value: unknown, label: string): boolean {
if (typeof value !== "boolean") {
throw new L34EContractError(`${label}: ожидалось логическое значение.`);
}
return value;
}
function box(
value: unknown,
label: string,
): readonly [number, number, number, number] {
if (!Array.isArray(value) || value.length !== 4) {
throw new L34EContractError(`${label}: ожидалась рамка.`);
}
const parsed = value.map((entry) => finite(entry, label));
if (!(0 <= parsed[0] && parsed[0] < parsed[2] && parsed[2] <= 800
&& 0 <= parsed[1] && parsed[1] < parsed[3] && parsed[3] <= 600)) {
throw new L34EContractError(`${label}: нарушена геометрия.`);
}
return parsed as [number, number, number, number];
}
const DIAGNOSTIC_VERDICTS = new Set<L34EDiagnosticVerdict>([
"strict_alignment",
"strict_class_mismatch",
"localization_disagreement",
"class_and_localization_disagreement",
"prediction_only",
"reference_only",
]);
function diagnosticVerdict(value: unknown, label: string): L34EDiagnosticVerdict {
const parsed = text(value, label) as L34EDiagnosticVerdict;
if (!DIAGNOSTIC_VERDICTS.has(parsed)) {
throw new L34EContractError(`${label}: неизвестный verdict.`);
}
return parsed;
}
function nullableText(value: unknown, label: string): string | null {
return value === null ? null : text(value, label);
}
function nullableRatio(value: unknown, label: string): number | null {
return value === null ? null : ratio(value, label);
}
function strictSummary(value: unknown, label: string): L34EStrictCaseSummary {
const item = object(value, label);
return {
predictionCount: integer(item.prediction_count, `${label}.prediction_count`),
referenceCount: integer(item.reference_count, `${label}.reference_count`),
truePositive: integer(item.true_positive, `${label}.true_positive`),
falsePositive: integer(item.false_positive, `${label}.false_positive`),
falseNegative: integer(item.false_negative, `${label}.false_negative`),
classMismatch: integer(item.class_mismatch, `${label}.class_mismatch`),
duplicateFalsePositive: integer(item.duplicate_false_positive, `${label}.duplicate_false_positive`),
unmatchedFalsePositive: integer(item.unmatched_false_positive, `${label}.unmatched_false_positive`),
unmatchedFalseNegative: integer(item.unmatched_false_negative, `${label}.unmatched_false_negative`),
severityScore: integer(item.severity_score, `${label}.severity_score`),
};
}
function diagnosticSummary(value: unknown, label: string): L34EDiagnosticSummary {
const item = object(value, label);
return {
predictionCount: integer(item.prediction_count, `${label}.prediction_count`),
referenceCount: integer(item.reference_count, `${label}.reference_count`),
associatedPairCount: integer(item.associated_pair_count, `${label}.pairs`),
strictAlignment: integer(item.strict_alignment, `${label}.aligned`),
strictClassMismatch: integer(item.strict_class_mismatch, `${label}.class`),
localizationDisagreement: integer(item.localization_disagreement, `${label}.localization`),
classAndLocalizationDisagreement: integer(item.class_and_localization_disagreement, `${label}.class_localization`),
predictionOnly: integer(item.prediction_only, `${label}.prediction_only`),
referenceOnly: integer(item.reference_only, `${label}.reference_only`),
candidateAssociationCoverage: ratio(item.candidate_association_coverage, `${label}.candidate_coverage`),
referenceAssociationCoverage: ratio(item.reference_association_coverage, `${label}.reference_coverage`),
severityScore: integer(item.severity_score, `${label}.severity`),
};
}
function aggregateStrict(value: unknown, label: string) {
const item = object(value, label);
return {
frameCount: integer(item.frame_count, `${label}.frames`),
predictionCount: integer(item.prediction_count, `${label}.predictions`),
referenceCount: integer(item.reference_count, `${label}.references`),
truePositive: integer(item.true_positive, `${label}.tp`),
falsePositive: integer(item.false_positive, `${label}.fp`),
falseNegative: integer(item.false_negative, `${label}.fn`),
classMismatch: integer(item.class_mismatch, `${label}.class`),
duplicateFalsePositive: integer(item.duplicate_false_positive, `${label}.duplicate`),
unmatchedFalsePositive: integer(item.unmatched_false_positive, `${label}.unmatched_fp`),
unmatchedFalseNegative: integer(item.unmatched_false_negative, `${label}.unmatched_fn`),
precisionIou50: ratio(item.precision_iou50, `${label}.precision`),
recallIou50: ratio(item.recall_iou50, `${label}.recall`),
f1Iou50: ratio(item.f1_iou50, `${label}.f1`),
errorCaseCount: integer(item.error_case_count, `${label}.error_cases`),
customReferenceCount: integer(item.custom_reference_count, `${label}.custom`),
};
}
function parseResult(value: unknown): L34EResult {
const item = object(value, "L3.4E");
const resultId = text(item.result_id, "L3.4E.result_id");
if (!/^l34e-self-review-diagnostic-[a-f0-9]{64}$/.test(resultId)) {
throw new L34EContractError("L3.4E.result_id: нарушена идентичность.");
}
const profile = object(item.profile, "L3.4E.profile");
const metrics = object(item.metrics, "L3.4E.metrics");
const diagnostic = object(metrics.diagnostic_association, "L3.4E.diagnostic");
const counts = object(metrics.frame_count_disagreement, "L3.4E.counts");
const decision = object(item.decision, "L3.4E.decision");
const cases = Array.isArray(item.cases)
? item.cases.map((raw): L34ECaseSummary => {
const current = object(raw, "L3.4E.case summary");
return {
truthIslandSequence: integer(current.truth_island_sequence, "L3.4E.case.sequence"),
imageId: integer(current.image_id, "L3.4E.case.image"),
frameIndex: integer(current.frame_index, "L3.4E.case.frame"),
groupId: text(current.group_id, "L3.4E.case.group"),
sessionSeconds: finite(current.session_seconds, "L3.4E.case.time"),
sourceImageSha256: text(current.source_image_sha256, "L3.4E.case.source"),
strictSummary: strictSummary(current.strict_summary, "L3.4E.case.strict"),
diagnosticSummary: diagnosticSummary(current.diagnostic_summary, "L3.4E.case.diagnostic"),
};
})
: [];
if (cases.length !== 32 || !Array.isArray(item.case_order)
|| item.case_order.length !== 32) {
throw new L34EContractError("L3.4E: требуется 32 кадра.");
}
return {
resultId,
createdAtUtc: text(item.created_at_utc, "L3.4E.created_at"),
status: exact(item.status, "completed-self-review-diagnostic-not-truth", "L3.4E.status"),
profile: {
profileId: text(profile.profile_id, "L3.4E.profile.id"),
strictMatcher: text(profile.strict_matcher, "L3.4E.profile.strict"),
diagnosticMatcher: text(profile.diagnostic_matcher, "L3.4E.profile.diagnostic"),
classPolicy: text(profile.class_policy, "L3.4E.profile.class"),
referencePolicy: text(profile.reference_policy, "L3.4E.profile.reference"),
scope: text(profile.scope, "L3.4E.profile.scope"),
},
pipelineId: text(item.pipeline_id, "L3.4E.pipeline"),
sourceSessionId: text(item.source_session_id, "L3.4E.session"),
cameraSourceId: exact(item.camera_source_id, "sensor.camera.right", "L3.4E.camera"),
metrics: {
strictIou50: aggregateStrict(metrics.strict_iou50, "L3.4E.strict"),
diagnosticAssociation: {
predictionCount: integer(diagnostic.prediction_count, "L3.4E.diagnostic.predictions"),
referenceCount: integer(diagnostic.reference_count, "L3.4E.diagnostic.references"),
associatedPairCount: integer(diagnostic.associated_pair_count, "L3.4E.diagnostic.pairs"),
strictAlignment: integer(diagnostic.strict_alignment, "L3.4E.diagnostic.aligned"),
strictClassMismatch: integer(diagnostic.strict_class_mismatch, "L3.4E.diagnostic.class"),
localizationDisagreement: integer(diagnostic.localization_disagreement, "L3.4E.diagnostic.localization"),
classAndLocalizationDisagreement: integer(diagnostic.class_and_localization_disagreement, "L3.4E.diagnostic.class_localization"),
predictionOnly: integer(diagnostic.prediction_only, "L3.4E.diagnostic.prediction_only"),
referenceOnly: integer(diagnostic.reference_only, "L3.4E.diagnostic.reference_only"),
candidateAssociationCoverage: ratio(diagnostic.candidate_association_coverage, "L3.4E.diagnostic.candidate_coverage"),
referenceAssociationCoverage: ratio(diagnostic.reference_association_coverage, "L3.4E.diagnostic.reference_coverage"),
errorCaseCount: integer(diagnostic.error_case_count, "L3.4E.diagnostic.error_cases"),
},
frameCountDisagreement: {
candidateSurplusLowerBound: integer(counts.candidate_surplus_lower_bound, "L3.4E.counts.candidate"),
referenceSurplusLowerBound: integer(counts.reference_surplus_lower_bound, "L3.4E.counts.reference"),
equalCountFrameCount: integer(counts.equal_count_frame_count, "L3.4E.counts.equal"),
},
referenceQuality: exact(metrics.reference_quality, "not-metric-grade-self-review", "L3.4E.reference quality"),
},
cases,
caseOrder: item.case_order.map((entry) => integer(entry, "L3.4E.case_order")),
decision: {
selfReviewComplete: bool(decision.self_review_complete, "L3.4E.decision.review"),
diagnosticAlignmentAvailable: bool(decision.diagnostic_alignment_available, "L3.4E.decision.alignment"),
metricGradeReferenceAvailable: bool(decision.metric_grade_reference_available, "L3.4E.decision.metric"),
independentTruthAvailable: bool(decision.independent_truth_available, "L3.4E.decision.truth"),
detectorRetuningAuthorized: bool(decision.detector_retuning_authorized, "L3.4E.decision.retuning"),
candidateAccepted: bool(decision.candidate_accepted, "L3.4E.decision.accepted"),
l35BlindGateOpen: bool(decision.l35_blind_gate_open, "L3.4E.decision.gate"),
nextAction: text(decision.next_action, "L3.4E.decision.next"),
},
limitations: Array.isArray(item.limitations)
? item.limitations.map((entry) => text(entry, "L3.4E.limitation"))
: [],
groundTruth: exact(item.ground_truth, false, "L3.4E.truth"),
access: exact(item.access, "read-only", "L3.4E.access"),
};
}
function parsePrediction(raw: unknown, label: string): L34EPrediction {
const item = object(raw, label);
return {
predictionIndex: integer(item.prediction_index, `${label}.index`),
category: text(item.category, `${label}.category`),
score: ratio(item.score, `${label}.score`),
boxXyxy: box(item.box_xyxy, `${label}.box`),
strictVerdict: text(item.verdict, `${label}.strict`),
diagnosticVerdict: diagnosticVerdict(item.diagnostic_verdict, `${label}.diagnostic`),
associatedObjectId: nullableText(item.associated_object_id, `${label}.object`),
associationIou: nullableRatio(item.association_iou, `${label}.iou`),
associationOverlapOverSmaller: nullableRatio(item.association_overlap_over_smaller, `${label}.overlap`),
sourcePredictionIndices: Array.isArray(item.source_prediction_indices)
? item.source_prediction_indices.map((entry) => integer(entry, `${label}.source`))
: [],
sourceRectificationTiles: Array.isArray(item.source_rectification_tiles)
? item.source_rectification_tiles.map((entry) => text(entry, `${label}.tile`))
: [],
operationTypes: Array.isArray(item.operation_types)
? item.operation_types.map((entry) => text(entry, `${label}.operation`))
: [],
};
}
function parseReference(raw: unknown, label: string): L34EReference {
const item = object(raw, label);
return {
objectId: text(item.object_id, `${label}.id`),
category: text(item.category, `${label}.category`),
displayCategory: text(item.display_category, `${label}.display`),
proposedLabel: nullableText(item.proposed_label, `${label}.proposed`),
boxXyxy: box(item.box_xyxy, `${label}.box`),
occluded: bool(item.occluded, `${label}.occluded`),
truncated: bool(item.truncated, `${label}.truncated`),
strictVerdict: text(item.verdict, `${label}.strict`),
diagnosticVerdict: diagnosticVerdict(item.diagnostic_verdict, `${label}.diagnostic`),
associatedPredictionIndex: item.associated_prediction_index === null
? null
: integer(item.associated_prediction_index, `${label}.prediction`),
associationIou: nullableRatio(item.association_iou, `${label}.iou`),
associationOverlapOverSmaller: nullableRatio(item.association_overlap_over_smaller, `${label}.overlap`),
};
}
function parseAssociation(raw: unknown, label: string): L34EAssociation {
const item = object(raw, label);
const classification = diagnosticVerdict(item.classification, `${label}.class`);
if (classification === "prediction_only" || classification === "reference_only") {
throw new L34EContractError(`${label}: ожидалась парная ассоциация.`);
}
return {
predictionIndex: integer(item.prediction_index, `${label}.prediction`),
objectId: text(item.object_id, `${label}.object`),
predictionCategory: text(item.prediction_category, `${label}.prediction_category`),
referenceCategory: text(item.reference_category, `${label}.reference_category`),
iou: ratio(item.iou, `${label}.iou`),
overlapOverSmaller: ratio(item.overlap_over_smaller, `${label}.overlap`),
classification,
};
}
function parseCase(value: unknown, resultId: string, sequence: number): L34ECase {
const item = object(value, "L3.4E.case");
exact(item.schema_version, "missioncore.l34e-self-review-diagnostic-case/v1", "L3.4E.case.schema");
exact(item.result_id, resultId, "L3.4E.case.result");
exact(item.truth_island_sequence, sequence, "L3.4E.case.sequence");
const camera = object(item.camera, "L3.4E.case.camera");
return {
resultId,
truthIslandSequence: sequence,
imageId: integer(item.image_id, "L3.4E.case.image"),
frameIndex: integer(item.frame_index, "L3.4E.case.frame"),
groupId: text(item.group_id, "L3.4E.case.group"),
sessionSeconds: finite(item.session_seconds, "L3.4E.case.time"),
sourceImageSha256: text(item.source_image_sha256, "L3.4E.case.source"),
cameraWidth: integer(camera.width, "L3.4E.case.width"),
cameraHeight: integer(camera.height, "L3.4E.case.height"),
cameraUrl: `/api/v1/laboratory/l34e/results/${resultId}/cases/${sequence}/camera`,
predictions: Array.isArray(item.predictions)
? item.predictions.map((raw, index) => parsePrediction(raw, `L3.4E.prediction.${index}`))
: [],
references: Array.isArray(item.references)
? item.references.map((raw, index) => parseReference(raw, `L3.4E.reference.${index}`))
: [],
associations: Array.isArray(item.associations)
? item.associations.map((raw, index) => parseAssociation(raw, `L3.4E.association.${index}`))
: [],
strictSummary: strictSummary(item.strict_summary, "L3.4E.case.strict"),
diagnosticSummary: diagnosticSummary(item.diagnostic_summary, "L3.4E.case.diagnostic"),
groundTruth: exact(item.ground_truth, false, "L3.4E.case.truth"),
access: exact(item.access, "read-only", "L3.4E.case.access"),
};
}
export async function fetchL34ESelfReviewDiagnostic({
fetcher = fetch,
signal,
}: {
fetcher?: L34ALaboratoryFetch;
signal?: AbortSignal;
} = {}): Promise<L34EResult> {
const response = await fetcher("/api/v1/laboratory/l34e/results?limit=1", {
method: "GET",
headers: { Accept: "application/json" },
signal,
});
if (!response.ok) {
throw new L34EContractError(`L3.4E недоступен: HTTP ${response.status}.`);
}
const catalog = object(await response.json(), "L3.4E.catalog");
exact(catalog.schema_version, "missioncore.l34e-self-review-diagnostic-catalog/v1", "L3.4E.catalog.schema");
if (!Array.isArray(catalog.items) || catalog.items.length !== 1) {
throw new L34EContractError("L3.4E.catalog: ожидался один результат.");
}
return parseResult(catalog.items[0]);
}
export async function fetchL34ECase(
resultId: string,
sequence: number,
{
fetcher = fetch,
signal,
}: {
fetcher?: L34ALaboratoryFetch;
signal?: AbortSignal;
} = {},
): Promise<L34ECase> {
if (!/^l34e-self-review-diagnostic-[a-f0-9]{64}$/.test(resultId)
|| !Number.isInteger(sequence) || sequence < 1 || sequence > 32) {
throw new L34EContractError("L3.4E.case: неверная идентичность.");
}
const response = await fetcher(
`/api/v1/laboratory/l34e/results/${resultId}/cases/${sequence}`,
{ method: "GET", headers: { Accept: "application/json" }, signal },
);
if (!response.ok) {
throw new L34EContractError(`L3.4E.case недоступен: HTTP ${response.status}.`);
}
return parseCase(await response.json(), resultId, sequence);
}
@@ -0,0 +1,495 @@
import {
annotationOperationKey,
type L34AnnotationCategory,
} from "./l34Annotation";
import type { L34EDiagnosticVerdict } from "./l34eSelfReviewDiagnostic";
export interface L34FCandidateObject {
candidateId: string;
predictionIndex: number;
category: string;
boxXyxy: readonly [number, number, number, number];
diagnosticVerdict: L34EDiagnosticVerdict;
associatedObjectId: string | null;
}
export interface L34FCase {
diagnosticResultId: string;
truthIslandSequence: number;
imageId: number;
frameIndex: number;
groupId: string;
sessionSeconds: number;
sourceImageSha256: string;
cameraWidth: number;
cameraHeight: number;
cameraUrl: string;
candidateObjects: readonly L34FCandidateObject[];
severityScore: number;
modelScoresIncluded: false;
groundTruth: false;
}
export interface L34FObject {
objectId: string;
category: L34AnnotationCategory;
proposedLabel: string | null;
origin: "self_review_seed" | "adjudicated_manual";
boxXyxy: readonly [number, number, number, number];
occluded: boolean;
truncated: boolean;
}
export interface L34FFrame {
truthIslandSequence: number;
imageId: number;
frameIndex: number;
sourceSha256: string;
reviewed: boolean;
objects: readonly L34FObject[];
}
export interface L34FProgress {
reviewedFrameCount: number;
frameCount: 32;
objectCount: number;
complete: boolean;
}
export interface L34FSessionSummary {
sessionId: string;
diagnosticResultId: string;
title: string;
reviewerSlot: number;
revision: number;
state: "draft" | "saved";
createdAtUtc: string;
updatedAtUtc: string;
progress: L34FProgress;
}
export interface L34FSession extends L34FSessionSummary {
frames: readonly L34FFrame[];
}
export interface L34FFrozenResult {
resultId: string;
createdAtUtc: string;
sourceSessionId: "RAVNOVES00";
diagnosticResultId: string;
adjudicationSessionId: string;
status: "completed-candidate-visible-adjudication-not-truth";
metrics: {
frameCount: number;
sourceReferenceCount: number;
adjudicatedReferenceCount: number;
unchangedObjectCount: number;
geometryChangedObjectCount: number;
classChangedObjectCount: number;
attributeChangedObjectCount: number;
addedObjectCount: number;
deletedObjectCount: number;
changedFrameCount: number;
};
decision: {
adjudicationComplete: boolean;
engineeringReferenceAvailable: boolean;
metricGradeReferenceAvailable: boolean;
independentTruthAvailable: boolean;
candidateAccepted: boolean;
detectorRetuningAuthorized: boolean;
l35BlindGateOpen: boolean;
nextAction: string;
};
limitations: readonly string[];
groundTruth: false;
}
type LaboratoryFetch = (
input: RequestInfo | URL,
init?: RequestInit,
) => Promise<Response>;
export class L34FContractError extends Error {}
function record(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new L34FContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function list(value: unknown, label: string): readonly unknown[] {
if (!Array.isArray(value)) throw new L34FContractError(`${label}: ожидался массив.`);
return value;
}
function text(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new L34FContractError(`${label}: ожидалась строка.`);
}
return value;
}
function integer(value: unknown, label: string): number {
if (!Number.isInteger(value) || Number(value) < 0) {
throw new L34FContractError(`${label}: ожидалось целое число.`);
}
return Number(value);
}
function finite(value: unknown, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new L34FContractError(`${label}: ожидалось число.`);
}
return value;
}
function boolean(value: unknown, label: string): boolean {
if (typeof value !== "boolean") throw new L34FContractError(`${label}: ожидался boolean.`);
return value;
}
function exact<T>(value: unknown, expected: T, label: string): T {
if (value !== expected) throw new L34FContractError(`${label}: нарушен контракт.`);
return expected;
}
function box(value: unknown, label: string): readonly [number, number, number, number] {
if (!Array.isArray(value) || value.length !== 4) {
throw new L34FContractError(`${label}: ожидалась рамка.`);
}
const parsed = value.map((entry) => finite(entry, label));
if (!(0 <= parsed[0] && parsed[0] < parsed[2] && parsed[2] <= 800
&& 0 <= parsed[1] && parsed[1] < parsed[3] && parsed[3] <= 600)) {
throw new L34FContractError(`${label}: нарушена геометрия.`);
}
return parsed as [number, number, number, number];
}
function category(value: unknown, label: string): L34AnnotationCategory {
if (![
"person", "bicycle", "motorcycle", "car", "heavy_vehicle",
"static_obstacle", "animal", "unmapped",
].includes(String(value))) {
throw new L34FContractError(`${label}: неизвестный класс.`);
}
return value as L34AnnotationCategory;
}
function verdict(value: unknown, label: string): L34EDiagnosticVerdict {
if (![
"strict_alignment", "strict_class_mismatch", "localization_disagreement",
"class_and_localization_disagreement", "prediction_only", "reference_only",
].includes(String(value))) {
throw new L34FContractError(`${label}: неизвестный verdict.`);
}
return value as L34EDiagnosticVerdict;
}
function parseObject(value: unknown, label: string): L34FObject {
const item = record(value, label);
const parsedCategory = category(item.category, `${label}.category`);
const proposedLabel = item.proposed_label;
if (parsedCategory === "unmapped") text(proposedLabel, `${label}.proposed_label`);
if (parsedCategory !== "unmapped" && proposedLabel !== null) {
throw new L34FContractError(`${label}: unexpected proposed label.`);
}
const origin = item.origin;
if (origin !== "self_review_seed" && origin !== "adjudicated_manual") {
throw new L34FContractError(`${label}: неизвестное происхождение.`);
}
return {
objectId: text(item.object_id, `${label}.id`),
category: parsedCategory,
proposedLabel: proposedLabel === null ? null : text(proposedLabel, `${label}.label`),
origin,
boxXyxy: box(item.box_xyxy, `${label}.box`),
occluded: boolean(item.occluded, `${label}.occluded`),
truncated: boolean(item.truncated, `${label}.truncated`),
};
}
function parseProgress(value: unknown): L34FProgress {
const item = record(value, "L3.4F.progress");
const frameCount = integer(item.frame_count, "L3.4F.frame_count");
if (frameCount !== 32) throw new L34FContractError("L3.4F: ожидались 32 кадра.");
return {
reviewedFrameCount: integer(item.reviewed_frame_count, "L3.4F.reviewed"),
frameCount: 32,
objectCount: integer(item.object_count, "L3.4F.objects"),
complete: boolean(item.complete, "L3.4F.complete"),
};
}
function parseSummary(value: unknown): L34FSessionSummary {
const item = record(value, "L3.4F.session");
exact(item.schema_version, "missioncore.l34f-adjudication-session/v1", "L3.4F.schema");
exact(item.contract_id, "l34e-candidate-visible-adjudication/v1", "L3.4F.contract");
const authority = record(item.authority, "L3.4F.authority");
exact(authority.ground_truth, false, "L3.4F.truth");
exact(authority.independent_truth, false, "L3.4F.independent");
exact(authority.metric_grade_reference, false, "L3.4F.metric_grade");
exact(authority.candidate_accepted, false, "L3.4F.accepted");
const assistance = record(item.assistance, "L3.4F.assistance");
exact(assistance.mode, "candidate-visible-human-adjudication", "L3.4F.mode");
exact(assistance.independent_truth_eligible, false, "L3.4F.eligible");
const state = item.state;
if (state !== "draft" && state !== "saved") {
throw new L34FContractError("L3.4F.state: нарушен контракт.");
}
return {
sessionId: text(item.session_id, "L3.4F.session_id"),
diagnosticResultId: text(item.diagnostic_result_id, "L3.4F.result_id"),
title: text(item.title, "L3.4F.title"),
reviewerSlot: integer(item.reviewer_slot, "L3.4F.reviewer"),
revision: integer(item.revision, "L3.4F.revision"),
state,
createdAtUtc: text(item.created_at_utc, "L3.4F.created"),
updatedAtUtc: text(item.updated_at_utc, "L3.4F.updated"),
progress: parseProgress(item.progress),
};
}
function parseSession(value: unknown): L34FSession {
const item = record(value, "L3.4F.session");
const summary = parseSummary(item);
const frames = list(item.frames, "L3.4F.frames").map((raw, index) => {
const frame = record(raw, `L3.4F.frame.${index}`);
return {
truthIslandSequence: integer(frame.truth_island_sequence, `L3.4F.frame.${index}.sequence`),
imageId: integer(frame.image_id, `L3.4F.frame.${index}.image`),
frameIndex: integer(frame.frame_index, `L3.4F.frame.${index}.frame`),
sourceSha256: text(frame.source_sha256, `L3.4F.frame.${index}.source`),
reviewed: boolean(frame.reviewed, `L3.4F.frame.${index}.reviewed`),
objects: list(frame.objects, `L3.4F.frame.${index}.objects`).map(
(object, objectIndex) => parseObject(object, `L3.4F.object.${objectIndex}`),
),
};
});
if (frames.length !== 32) throw new L34FContractError("L3.4F: ожидались 32 кадра.");
return { ...summary, frames };
}
async function responseJson(response: Response, label: string): Promise<unknown> {
if (response.ok) return response.json();
let detail = "";
try {
const payload = record(await response.json(), label);
detail = typeof payload.detail === "string" ? `: ${payload.detail}` : "";
} catch {
detail = "";
}
throw new L34FContractError(`${label} недоступен: HTTP ${response.status}${detail}.`);
}
function assertDiagnosticId(resultId: string): void {
if (!/^l34e-self-review-diagnostic-[a-f0-9]{64}$/.test(resultId)) {
throw new L34FContractError("L3.4F: неверный diagnostic result id.");
}
}
function base(resultId: string): string {
assertDiagnosticId(resultId);
return `/api/v1/laboratory/l34f/diagnostics/${resultId}`;
}
export async function fetchL34FCase(
resultId: string,
sequence: number,
{ fetcher = fetch, signal }: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
): Promise<L34FCase> {
const response = await fetcher(`${base(resultId)}/cases/${sequence}`, {
method: "GET", headers: { Accept: "application/json" }, signal,
});
const item = record(await responseJson(response, "Кадр L3.4F"), "L3.4F.case");
exact(item.schema_version, "missioncore.l34f-adjudication-case/v1", "L3.4F.case.schema");
exact(item.diagnostic_result_id, resultId, "L3.4F.case.result");
exact(item.truth_island_sequence, sequence, "L3.4F.case.sequence");
exact(item.model_scores_included, false, "L3.4F.case.scores");
exact(item.ground_truth, false, "L3.4F.case.truth");
const camera = record(item.camera, "L3.4F.camera");
const summary = record(item.diagnostic_summary, "L3.4F.summary");
return {
diagnosticResultId: resultId,
truthIslandSequence: sequence,
imageId: integer(item.image_id, "L3.4F.case.image"),
frameIndex: integer(item.frame_index, "L3.4F.case.frame"),
groupId: text(item.group_id, "L3.4F.case.group"),
sessionSeconds: finite(item.session_seconds, "L3.4F.case.time"),
sourceImageSha256: text(item.source_image_sha256, "L3.4F.case.source"),
cameraWidth: integer(camera.width, "L3.4F.case.width"),
cameraHeight: integer(camera.height, "L3.4F.case.height"),
cameraUrl: text(item.camera_url, "L3.4F.case.camera_url"),
candidateObjects: list(item.candidate_objects, "L3.4F.candidates").map((raw, index) => {
const object = record(raw, `L3.4F.candidate.${index}`);
return {
candidateId: text(object.candidate_id, `L3.4F.candidate.${index}.id`),
predictionIndex: integer(object.prediction_index, `L3.4F.candidate.${index}.index`),
category: text(object.category, `L3.4F.candidate.${index}.category`),
boxXyxy: box(object.box_xyxy, `L3.4F.candidate.${index}.box`),
diagnosticVerdict: verdict(object.diagnostic_verdict, `L3.4F.candidate.${index}.verdict`),
associatedObjectId: object.associated_object_id === null
? null
: text(object.associated_object_id, `L3.4F.candidate.${index}.association`),
};
}),
severityScore: integer(summary.severity_score, "L3.4F.case.severity"),
modelScoresIncluded: false,
groundTruth: false,
};
}
export async function fetchL34FSessions(
resultId: string,
{ fetcher = fetch, signal }: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
): Promise<readonly L34FSessionSummary[]> {
const response = await fetcher(`${base(resultId)}/sessions`, {
method: "GET", headers: { Accept: "application/json" }, signal,
});
const catalog = record(await responseJson(response, "Сессии L3.4F"), "L3.4F.catalog");
exact(catalog.schema_version, "missioncore.l34f-adjudication-session-catalog/v1", "L3.4F.catalog.schema");
exact(catalog.diagnostic_result_id, resultId, "L3.4F.catalog.result");
return list(catalog.items, "L3.4F.catalog.items").map(parseSummary);
}
export async function fetchL34FSession(
resultId: string,
sessionId: string,
{ fetcher = fetch, signal }: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
): Promise<L34FSession> {
const response = await fetcher(`${base(resultId)}/sessions/${sessionId}`, {
method: "GET", headers: { Accept: "application/json" }, signal,
});
return parseSession(await responseJson(response, "Сессия L3.4F"));
}
export async function createL34FSession(
resultId: string,
{ fetcher = fetch }: { fetcher?: LaboratoryFetch } = {},
): Promise<L34FSession> {
const response = await fetcher(`${base(resultId)}/sessions`, {
method: "POST",
headers: { Accept: "application/json", "Content-Type": "application/json" },
body: JSON.stringify({ idempotency_key: annotationOperationKey("create-l34f") }),
});
return parseSession(await responseJson(response, "Создание сессии L3.4F"));
}
export async function saveL34FSession(
session: L34FSession,
title: string,
frames: readonly L34FFrame[],
{ fetcher = fetch }: { fetcher?: LaboratoryFetch } = {},
): Promise<L34FSession> {
const response = await fetcher(
`${base(session.diagnosticResultId)}/sessions/${session.sessionId}`,
{
method: "PUT",
headers: { Accept: "application/json", "Content-Type": "application/json" },
body: JSON.stringify({
expected_revision: session.revision,
idempotency_key: annotationOperationKey("save-l34f"),
title,
frames: frames.map((frame) => ({
truth_island_sequence: frame.truthIslandSequence,
reviewed: frame.reviewed,
objects: frame.objects.map((object) => ({
object_id: object.objectId,
category: object.category,
proposed_label: object.proposedLabel,
origin: object.origin,
box_xyxy: [...object.boxXyxy],
occluded: object.occluded,
truncated: object.truncated,
})),
})),
}),
},
);
return parseSession(await responseJson(response, "Сохранение сессии L3.4F"));
}
export async function freezeL34FSession(
session: L34FSession,
{ fetcher = fetch }: { fetcher?: LaboratoryFetch } = {},
): Promise<L34FFrozenResult> {
const response = await fetcher(
`${base(session.diagnosticResultId)}/sessions/${session.sessionId}/freeze`,
{
method: "POST",
headers: { Accept: "application/json", "Content-Type": "application/json" },
body: JSON.stringify({ expected_revision: session.revision }),
},
);
return parseFrozenResult(await responseJson(response, "Freeze L3.4F"));
}
export async function fetchL34FFrozenResult({
fetcher = fetch,
signal,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
} = {}): Promise<L34FFrozenResult> {
const response = await fetcher("/api/v1/laboratory/l34f/results?limit=1", {
method: "GET",
headers: { Accept: "application/json" },
signal,
});
const catalog = record(await responseJson(response, "L3.4F"), "L3.4F.catalog");
exact(
catalog.schema_version,
"missioncore.l34f-adjudicated-reference-catalog/v1",
"L3.4F.catalog.schema",
);
const items = list(catalog.items, "L3.4F.catalog.items");
if (items.length !== 1) {
throw new L34FContractError("L3.4F.catalog: ожидался один результат.");
}
return parseFrozenResult(items[0]);
}
export function parseFrozenResult(value: unknown): L34FFrozenResult {
const item = record(value, "L3.4F.result");
const metrics = record(item.metrics, "L3.4F.metrics");
const decision = record(item.decision, "L3.4F.decision");
const resultId = text(item.result_id, "L3.4F.result_id");
if (!/^l34f-adjudicated-reference-[a-f0-9]{64}$/.test(resultId)) {
throw new L34FContractError("L3.4F.result_id: нарушена идентичность.");
}
const diagnosticResultId = text(item.diagnostic_result_id, "L3.4F.diagnostic_result_id");
assertDiagnosticId(diagnosticResultId);
return {
resultId,
createdAtUtc: text(item.created_at_utc, "L3.4F.created_at"),
sourceSessionId: exact(item.source_session_id, "RAVNOVES00", "L3.4F.source_session_id"),
diagnosticResultId,
adjudicationSessionId: text(item.adjudication_session_id, "L3.4F.adjudication_session_id"),
status: exact(item.status, "completed-candidate-visible-adjudication-not-truth", "L3.4F.status"),
metrics: {
frameCount: integer(metrics.frame_count, "L3.4F.frames"),
sourceReferenceCount: integer(metrics.source_reference_count, "L3.4F.source_references"),
adjudicatedReferenceCount: integer(metrics.adjudicated_reference_count, "L3.4F.references"),
unchangedObjectCount: integer(metrics.unchanged_object_count, "L3.4F.unchanged"),
geometryChangedObjectCount: integer(metrics.geometry_changed_object_count, "L3.4F.geometry"),
classChangedObjectCount: integer(metrics.class_changed_object_count, "L3.4F.class"),
attributeChangedObjectCount: integer(metrics.attribute_changed_object_count, "L3.4F.attributes"),
addedObjectCount: integer(metrics.added_object_count, "L3.4F.added"),
deletedObjectCount: integer(metrics.deleted_object_count, "L3.4F.deleted"),
changedFrameCount: integer(metrics.changed_frame_count, "L3.4F.changed_frames"),
},
decision: {
adjudicationComplete: boolean(decision.adjudication_complete, "L3.4F.complete"),
engineeringReferenceAvailable: boolean(decision.engineering_reference_available, "L3.4F.engineering"),
metricGradeReferenceAvailable: boolean(decision.metric_grade_reference_available, "L3.4F.metric_grade"),
independentTruthAvailable: boolean(decision.independent_truth_available, "L3.4F.independent"),
candidateAccepted: boolean(decision.candidate_accepted, "L3.4F.accepted"),
detectorRetuningAuthorized: boolean(decision.detector_retuning_authorized, "L3.4F.retuning"),
l35BlindGateOpen: boolean(decision.l35_blind_gate_open, "L3.4F.l35"),
nextAction: text(decision.next_action, "L3.4F.next"),
},
limitations: list(item.limitations, "L3.4F.limitations").map((raw, index) => text(raw, `L3.4F.limitation.${index}`)),
groundTruth: exact(item.ground_truth, false, "L3.4F.truth"),
};
}
@@ -28,6 +28,7 @@ export interface L3VisualBox {
| "false-positive"
| "model-prediction";
score: number | null;
temporalStatus?: string;
}
export interface L3VisualFrame {
+1
View File
@@ -5,6 +5,7 @@
@import "./styles/laboratory.css";
@import "./styles/e40-case-review.css";
@import "./styles/l3-pointpillars-visual-audit.css";
@import "./styles/l34-annotation.css";
@import "./styles/laboratory-reporting.css";
@import "./styles/e34-temporal-layer.css";
@import "./styles/e35-degradation-recovery.css";
@@ -23,6 +23,68 @@
height: 100%;
}
.l32-camera-scene {
position: relative;
width: 100%;
height: 100%;
min-height: 0;
overflow: hidden;
background: var(--nodedc-canvas);
}
.l32-camera-scene canvas {
display: block;
width: 100%;
height: 100%;
}
.l32-camera-scene > img {
display: block;
width: 100%;
height: 100%;
object-fit: contain;
user-select: none;
}
.e46c-video-scene {
position: relative;
width: 100%;
height: 100%;
min-height: 0;
overflow: hidden;
background: var(--nodedc-canvas);
}
.e46c-video-scene > .recorded-media-player {
position: absolute;
inset: 0;
}
.e46c-video-scene > canvas {
position: absolute;
z-index: 2;
inset: 0;
display: block;
width: 100%;
height: 100%;
pointer-events: none;
}
.e46e-ready-stack-video {
width: 100%;
height: 100%;
min-height: 0;
background: var(--nodedc-canvas);
}
.e46e-ready-stack-video > video,
.e46e-ready-stack-video > img {
display: block;
width: 100%;
height: 100%;
object-fit: contain;
}
.l3-visual-audit__state {
display: flex;
width: 100%;
@@ -52,7 +114,7 @@
.l3-visual-audit__actions .nodedc-select-anchor,
.l3-visual-audit__actions .nodedc-select {
width: clamp(17rem, 34vw, 31rem);
width: clamp(12rem, 23vw, 22rem);
}
.l3-visual-audit
@@ -97,6 +159,10 @@
font-size: 0.6rem;
}
.l3-visual-audit__overlay--video {
bottom: 3.55rem;
}
.l3-visual-audit__legend {
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.35rem 0.6rem;
@@ -135,6 +201,10 @@
background: rgb(var(--nodedc-accent-rgb));
}
.l3-visual-audit__legend span[data-tone="warning"]::before {
background: #ffc447;
}
@media (max-width: 900px) {
.l3-visual-audit__overlay {
grid-template-columns: repeat(2, minmax(0, 1fr));
@@ -0,0 +1,300 @@
.l34-annotation-workspace {
position: fixed;
z-index: var(--nodedc-layer-overlay);
inset: 0;
display: grid;
min-width: 0;
min-height: 0;
grid-template-rows: auto minmax(0, 1fr) auto;
background: var(--nodedc-canvas);
color: var(--nodedc-text-primary);
}
.l34-annotation-workspace__toolbar {
z-index: 6;
display: flex;
min-width: 0;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
border-bottom: 1px solid var(--station-hairline);
background: var(--nodedc-floating-surface);
padding: 0.55rem 0.7rem;
backdrop-filter: blur(var(--nodedc-blur-control));
}
.l34-annotation-workspace__session-tools,
.l34-annotation-workspace__frame-tools,
.l34-annotation-workspace__object-tools {
display: flex;
min-width: 0;
align-items: center;
gap: 0.45rem;
}
.l34-annotation-workspace__session-tools > .nodedc-select-anchor {
width: clamp(14rem, 25vw, 24rem);
}
.l34-annotation-workspace__frame-tools > .nodedc-select-anchor {
width: clamp(15rem, 26vw, 25rem);
}
.l34-annotation-workspace__stage {
position: relative;
min-width: 0;
min-height: 0;
overflow: hidden;
background: var(--nodedc-canvas);
}
.l34-annotation-workspace__state {
display: flex;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
gap: 0.55rem;
color: var(--nodedc-text-secondary);
}
.l34-annotation-canvas {
display: grid;
width: 100%;
height: 100%;
place-items: center;
overflow: hidden;
background: var(--nodedc-canvas);
}
.l34-annotation-canvas__plane {
position: relative;
max-width: 100%;
max-height: 100%;
overflow: visible;
user-select: none;
}
.l34-annotation-canvas__plane > img,
.l34-annotation-canvas__plane > svg {
position: absolute;
inset: 0;
display: block;
width: 100%;
height: 100%;
}
.l34-annotation-canvas__plane > img {
object-fit: fill;
pointer-events: none;
}
.l34-annotation-canvas__plane > svg {
z-index: 1;
touch-action: none;
}
.l34-annotation-canvas__plane > svg[data-drawing="true"] {
cursor: crosshair;
}
.l34-annotation-canvas rect {
fill: none;
stroke: rgb(var(--nodedc-accent-rgb));
stroke-width: 2;
vector-effect: non-scaling-stroke;
pointer-events: all;
}
.l34-annotation-canvas rect[data-selected="true"] {
fill: none;
stroke-width: 3;
}
.l34-annotation-canvas .l34-annotation-canvas__comparison {
stroke: rgb(var(--nodedc-warning-rgb));
stroke-dasharray: 7 5;
stroke-width: 2;
pointer-events: none;
}
.l34-annotation-canvas svg:not([data-drawing="true"]) rect:not(.l34-annotation-canvas__draft) {
cursor: move;
}
.l34-annotation-canvas__resize-handle {
fill: var(--nodedc-floating-surface);
stroke: rgb(var(--nodedc-accent-rgb));
stroke-width: 2;
vector-effect: non-scaling-stroke;
pointer-events: all;
}
.l34-annotation-canvas__resize-handle[data-handle="nw"],
.l34-annotation-canvas__resize-handle[data-handle="se"] {
cursor: nwse-resize;
}
.l34-annotation-canvas__resize-handle[data-handle="ne"],
.l34-annotation-canvas__resize-handle[data-handle="sw"] {
cursor: nesw-resize;
}
.l34-annotation-canvas .l34-annotation-canvas__draft {
fill: none;
stroke-dasharray: 8 5;
pointer-events: none;
}
.l34-annotation-canvas__label {
position: absolute;
z-index: 3;
width: clamp(9rem, 13vw, 13rem);
max-width: 42%;
}
.l34-annotation-canvas__comparison-label {
position: absolute;
z-index: 2;
overflow: hidden;
max-width: 10rem;
border-radius: var(--nodedc-radius-option);
background: rgb(var(--nodedc-warning-rgb) / 0.92);
color: var(--nodedc-canvas);
font-size: 0.52rem;
font-weight: 800;
line-height: 1;
padding: 0.3rem 0.4rem;
pointer-events: none;
text-overflow: ellipsis;
white-space: nowrap;
}
.l34f-adjudication-workspace__legend {
display: flex;
align-items: center;
gap: 0.75rem;
color: var(--nodedc-text-muted);
font-size: 0.54rem;
}
.l34f-adjudication-workspace__legend span::before {
display: inline-block;
width: 0.65rem;
height: 0.12rem;
margin-right: 0.3rem;
background: rgb(var(--nodedc-accent-rgb));
content: "";
vertical-align: middle;
}
.l34f-adjudication-workspace__legend span:first-child::before {
background: rgb(var(--nodedc-warning-rgb));
}
.l34-annotation-canvas__label:not([data-selected="true"]) {
width: auto;
max-width: 10rem;
pointer-events: none;
}
.l34-annotation-canvas__label:not([data-selected="true"]) > span {
display: block;
overflow: hidden;
border-radius: var(--nodedc-radius-option);
background: var(--nodedc-floating-surface);
color: var(--nodedc-text-primary);
font-size: 0.58rem;
font-weight: 700;
line-height: 1;
max-width: 10rem;
padding: 0.35rem 0.45rem;
text-overflow: ellipsis;
white-space: nowrap;
}
.l34-annotation-canvas__label[data-unclassified="true"]
.nodedc-select-trigger,
.l34-annotation-canvas__label[data-unmapped="true"]
.nodedc-select-trigger {
box-shadow: inset 0 0 0 1px rgb(var(--nodedc-warning-rgb));
}
.l34-annotation-workspace__inspector {
z-index: 5;
display: flex;
min-width: 0;
align-items: center;
justify-content: space-between;
gap: 1rem;
border-top: 1px solid var(--station-hairline);
background: var(--nodedc-floating-surface);
padding: 0.55rem 0.7rem;
backdrop-filter: blur(var(--nodedc-blur-control));
}
.l34-annotation-workspace__source-state {
display: grid;
min-width: 13rem;
gap: 0.1rem;
}
.l34-annotation-workspace__source-state span,
.l34-annotation-workspace__source-state small,
.l34-annotation-workspace__hint {
color: var(--nodedc-text-muted);
font-size: 0.52rem;
}
.l34-annotation-workspace__source-state strong {
font-size: 0.62rem;
}
.l34-annotation-save-form {
display: grid;
gap: 1rem;
}
.l34-annotation-save-form__summary {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 0.55rem 1rem;
border-radius: var(--nodedc-radius-option);
background: var(--station-panel-soft);
padding: 0.8rem;
}
.l34-annotation-save-form__summary span,
.l34-annotation-save-form p {
margin: 0;
color: var(--nodedc-text-secondary);
font-size: 0.65rem;
line-height: 1.55;
}
.l34-annotation-save-form__summary strong {
font-size: 0.7rem;
}
.l34-annotation-save-form p[role="alert"] {
color: rgb(var(--nodedc-warning-rgb));
}
@media (max-width: 980px) {
.l34-annotation-workspace__toolbar,
.l34-annotation-workspace__inspector {
align-items: stretch;
flex-direction: column;
}
.l34-annotation-workspace__session-tools,
.l34-annotation-workspace__frame-tools,
.l34-annotation-workspace__object-tools {
flex-wrap: wrap;
}
.l34-annotation-workspace__session-tools > .nodedc-select-anchor,
.l34-annotation-workspace__frame-tools > .nodedc-select-anchor {
width: min(100%, 24rem);
}
}
@@ -23,6 +23,12 @@ export interface WorkspaceNavigation {
activateAutomaticSpatialSource: () => void;
}
export interface LaboratoryAnnotationAction {
label: string;
disabled?: boolean;
onClick: () => void;
}
export interface WorkspaceRendererProps {
definition: WorkspaceDefinition;
state: MissionRuntimeState | null;
@@ -56,4 +62,7 @@ export interface WorkspaceRendererProps {
disabled: boolean;
blockedReason: string | null;
};
onLaboratoryAnnotationActionChange: (
action: LaboratoryAnnotationAction | null,
) => void;
}
@@ -1,9 +1,7 @@
import type { ComponentType } from "react";
import type { LaboratoryOption } from "../../components/laboratory/LaboratoryPresentation";
import {
isAdvancedLaboratoryWorkId,
type AdvancedLaboratoryIndexItem,
type AdvancedLaboratoryWorkId,
} from "../../core/laboratory/advancedIndex";
import type { AdvancedLaboratoryResults } from "../../core/laboratory/advancedResults";
@@ -20,7 +18,27 @@ import { E39Result } from "./E39Result";
import { E40Result } from "./E40Result";
import { L3PointPillarsResult } from "./L3PointPillarsResult";
import { L31PointPillarsRavnovesResult } from "./L31PointPillarsRavnovesResult";
import { L32PointPillarsCameraReviewResult } from "./L32PointPillarsCameraReviewResult";
import { L33CameraFirstDetectorReviewResult } from "./L33CameraFirstDetectorReviewResult";
import { L34RightYoloxTruthIslandResultView } from "./L34RightYoloxTruthIslandResult";
import { L34AAssistedYoloxErrorResultView } from "./L34AAssistedYoloxErrorResult";
import { L34BNestedBoxConsolidationResultView } from "./L34BNestedBoxConsolidationResult";
import { L34CTileSeamStitchResultView } from "./L34CTileSeamStitchResult";
import { L34DCumulativePostprocessingResultView } from "./L34DCumulativePostprocessingResult";
import { L34ESelfReviewDiagnosticResultView } from "./L34ESelfReviewDiagnosticResult";
import { L34FAdjudicatedReferenceResultView } from "./L34FAdjudicatedReferenceResult";
import { RecordedReplayEvidence } from "./RecordedReplayEvidence";
import { E46BlindReviewResultView } from "./E46BlindReviewResult";
import { E46AAiEngineeringPreannotationResultView } from "./E46AAiEngineeringPreannotationResult";
import { E46BTemporalMotionResultView } from "./E46BTemporalMotionResult";
import { E46CFullReplayWorldTracksResultView } from "./E46CFullReplayWorldTracksResult";
import { E46DTemporalFailureAuditResultView } from "./E46DTemporalFailureAuditResult";
import { E46EReadyStackResultView } from "./E46EReadyStackResult";
import { E46FDashCamBakeoffResultView } from "./E46FDashCamBakeoffResult";
import { E46GRectifiedDetectorBakeoffResultView } from "./E46GRectifiedDetectorBakeoffResult";
import { E46HFullRectifiedFrontReplayResultView } from "./E46HFullRectifiedFrontReplayResult";
import { E46IGroundingDinoFullReplayResultView } from "./E46IGroundingDinoFullReplayResult";
import { E46JRawFisheyeRealtimeResultView } from "./E46JRawFisheyeRealtimeResult";
export { isAdvancedLaboratoryWorkId };
export type { AdvancedLaboratoryWorkId };
@@ -29,35 +47,6 @@ type LaboratoryWorkspaceProps = WorkspaceRendererProps & {
SpatialView: ComponentType<WorkspaceRendererProps>;
};
export function advancedLaboratoryWorkOptions(
index: readonly AdvancedLaboratoryIndexItem[],
): readonly LaboratoryOption<AdvancedLaboratoryWorkId>[] {
const available = new Set(index.map(({ workId }) => workId));
const options: readonly LaboratoryOption<AdvancedLaboratoryWorkId>[] = [
{
id: "l31-pointpillars-ravnoves",
label: "L3.1 · PointPillars на RAVNOVES00",
},
{
id: "l3-pointpillars-visual-audit",
label: "L3 · KITTI · внешний PointPillars benchmark",
},
{ id: "e31-source-binding", label: "LAB E31 · source binding" },
{ id: "e32-track-geometry", label: "LAB E32 · TrackGeometry v1" },
{ id: "e33-worker-shadow", label: "LAB E33 · worker shadow 1×" },
{ id: "e34-temporal-layer", label: "LAB E34 · temporal occupied/unknown" },
{ id: "e35-degradation-recovery", label: "LAB E35 · degradation recovery" },
{ id: "e37-ravnoves-acceptance", label: "LAB E37 · RAVNOVES00 acceptance R0" },
{ id: "e38-perception-baseline", label: "LAB E38 · perception quality R1" },
{ id: "e39-perception-refinement", label: "LAB E39 · perception refinement R1" },
{
id: "e40-perception-product-gate",
label: "LAB E40 · historical visible engineering evaluation",
},
];
return options.filter(({ id }) => available.has(id));
}
export function advancedLaboratorySourceSession(
workId: AdvancedLaboratoryWorkId,
results: AdvancedLaboratoryResults,
@@ -98,9 +87,69 @@ export function AdvancedLaboratoryResult({
if (workId === "l31-pointpillars-ravnoves" && results.l31) {
return <L31PointPillarsRavnovesResult result={results.l31} />;
}
if (workId === "l32-pointpillars-camera-review" && results.l32) {
return <L32PointPillarsCameraReviewResult result={results.l32} />;
}
if (workId === "l33-camera-first-detector-review" && results.l33) {
return <L33CameraFirstDetectorReviewResult result={results.l33} />;
}
if (workId === "e40-perception-product-gate" && results.e40) {
return <E40Result rigLabel={rigLabel} result={results.e40} />;
}
if (workId === "e46-detector-truth-island" && results.e46) {
return <E46BlindReviewResultView rigLabel={rigLabel} result={results.e46} />;
}
if (workId === "e46a-ai-engineering-preannotation" && results.e46a) {
return <E46AAiEngineeringPreannotationResultView rigLabel={rigLabel} result={results.e46a} />;
}
if (workId === "e46b-temporal-motion" && results.e46b) {
return <E46BTemporalMotionResultView rigLabel={rigLabel} result={results.e46b} />;
}
if (workId === "e46c-full-replay-world-tracks" && results.e46c) {
return <E46CFullReplayWorldTracksResultView rigLabel={rigLabel} result={results.e46c} />;
}
if (workId === "e46d-temporal-failure-audit" && results.e46d) {
return <E46DTemporalFailureAuditResultView rigLabel={rigLabel} result={results.e46d} />;
}
if (workId === "e46e-ready-stack" && results.e46e) {
return <E46EReadyStackResultView rigLabel={rigLabel} result={results.e46e} />;
}
if (workId === "e46f-dashcam-bakeoff" && results.e46f) {
return <E46FDashCamBakeoffResultView rigLabel={rigLabel} result={results.e46f} />;
}
if (workId === "e46g-rectified-detector-bakeoff" && results.e46g) {
return <E46GRectifiedDetectorBakeoffResultView rigLabel={rigLabel} result={results.e46g} />;
}
if (workId === "e46h-full-rectified-front-replay" && results.e46h) {
return <E46HFullRectifiedFrontReplayResultView rigLabel={rigLabel} result={results.e46h} />;
}
if (workId === "e46i-grounding-dino-full-replay" && results.e46i) {
return <E46IGroundingDinoFullReplayResultView rigLabel={rigLabel} result={results.e46i} />;
}
if (workId === "e46j-raw-fisheye-realtime" && results.e46j) {
return <E46JRawFisheyeRealtimeResultView rigLabel={rigLabel} result={results.e46j} />;
}
if (workId === "l34-right-yolox-truth-island-freeze" && results.l34) {
return <L34RightYoloxTruthIslandResultView rigLabel={rigLabel} result={results.l34} />;
}
if (workId === "l34a-assisted-yolox-error-audit" && results.l34a) {
return <L34AAssistedYoloxErrorResultView rigLabel={rigLabel} result={results.l34a} />;
}
if (workId === "l34b-nested-box-consolidation-shadow" && results.l34b) {
return <L34BNestedBoxConsolidationResultView rigLabel={rigLabel} result={results.l34b} />;
}
if (workId === "l34c-tile-seam-stitch-shadow" && results.l34c) {
return <L34CTileSeamStitchResultView rigLabel={rigLabel} result={results.l34c} />;
}
if (workId === "l34d-cumulative-postprocessing-candidate" && results.l34d) {
return <L34DCumulativePostprocessingResultView rigLabel={rigLabel} result={results.l34d} />;
}
if (workId === "l34e-self-review-diagnostic" && results.l34e) {
return <L34ESelfReviewDiagnosticResultView rigLabel={rigLabel} result={results.l34e} />;
}
if (workId === "l34f-adjudicated-reference" && results.l34f) {
return <L34FAdjudicatedReferenceResultView rigLabel={rigLabel} result={results.l34f} />;
}
if (workId === "e39-perception-refinement" && results.e39) {
return <E39Result rigLabel={rigLabel} result={results.e39} />;
}
@@ -0,0 +1,75 @@
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import type { E46AAiEngineeringPreannotationResult } from "../../core/laboratory/e46aAiEngineeringPreannotation";
import { formatNumber } from "../../presentation";
import { E46AAiEngineeringPreannotationVisual } from "./E46AAiEngineeringPreannotationVisual";
export function E46AAiEngineeringPreannotationResultView({
rigLabel,
result,
}: {
rigLabel: string;
result: E46AAiEngineeringPreannotationResult;
}) {
const counts = result.metrics.classCounts;
return (
<LaboratoryWorkTemplate
summary={<LaboratorySummary
title="LAB E46A · object-level preannotation QA"
description="Все 32 exact E46 RIGHT-camera кадра повторно проверены на уровне отдельных объектов. Ложные рамки удалены, а грубая геометрия привязана к замороженному детекторному кандидату и затем просмотрена на исходных кадрах."
status={`32/32 object-level QA · ${result.metrics.deletedFalseBoxCount} false boxes · not truth`}
statusTone="warning"
facts={[
{ label: "Конфигурация", value: `${rigLabel} · RIGHT camera · recorded replay` },
{ label: "Разметка", value: `${result.metrics.frameCount}/32 кадров · ${formatNumber(result.metrics.objectCount, 0)} объектов` },
{ label: "QA", value: `${formatNumber(result.metrics.geometrySnappedObjectCount, 0)} geometry snaps · ${formatNumber(result.metrics.deletedFalseBoxCount, 0)} удалено · ${formatNumber(result.metrics.categoryCorrectedObjectCount, 0)} класс исправлен` },
{ label: "Полномочия", value: "Reviewer A/B не затронуты · E48 closed · L3.5 closed" },
]}
brief={{
question: "Можно ли убрать очевидную дрочню из ручной разметки и оставить человеку только спорные correction/approval?",
approach: "Исходные 260 candidate-visible рамок сопоставлены с замороженной Mask R-CNN геометрией на тех же hash-bound кадрах. После NMS и one-to-one snap все 32 кадра просмотрены целиком; ложные фоновые рамки удалены, custom-классы сохранены.",
principalResult: `${formatNumber(result.metrics.sourceObjectCount, 0)} исходных рамок → ${formatNumber(result.metrics.objectCount, 0)} после QA: ${formatNumber(counts.car ?? 0, 0)} авто, ${formatNumber(counts.heavy_vehicle ?? 0, 0)} тяжёлых ТС, ${formatNumber(counts.person ?? 0, 0)} людей, ${formatNumber(counts.stroller ?? 0, 0)} коляски, ${formatNumber(counts.laptop ?? 0, 0)} ноутбука и ${formatNumber(counts.bicycle ?? 0, 0)} велосипед.`,
limitation: "Наличие объекта и финальное решение всё ещё candidate-visible и AI-assisted. Это инженерная предразметка, не independent truth и не основание считать AP/precision/recall.",
}}
method={{
completeness: "complete",
executionClass: "hybrid",
pipelineId: "e46a-object-level-preannotation-qa/v2",
components: [
{ kind: "source", name: result.sourceE46ResultId, version: "E46 immutable 32-frame source", role: "hash-bound sensor.camera.right frames", identitySha256: result.sourceE46ResultId.split("-").at(-1) ?? null },
{ kind: "model", name: "Mask R-CNN geometry candidate", version: "valid-fov-fill frozen", role: "geometry-only snap; no truth authority", identitySha256: null },
{ kind: "tool", name: "AI object-level visual audit", version: "all-32/v2", role: "remove false boxes, verify one instance per frame", identitySha256: null },
],
}}
/>}
evidence={<LaboratoryEvidence
eyebrow="E46A VISUAL EVIDENCE · PREANNOTATION / SOURCE"
title="Все 32 кадра открываются с рамками и без них"
kind="diagnostic-model"
resizable
>
<E46AAiEngineeringPreannotationVisual resultId={result.resultId} />
</LaboratoryEvidence>}
result={<LaboratoryResultSummary
title="Очевидные ошибки предразметки сняты; ручная работа оставлена только для спорных случаев"
status="Object-level QA complete · independent gate unchanged"
statusTone="warning"
metrics={[
{ label: "Reviewed frames", value: "32/32", hint: "Exact E46 source" },
{ label: "Geometry snapped", value: formatNumber(result.metrics.geometrySnappedObjectCount, 0), hint: "Frozen candidate · visually checked" },
{ label: "False boxes removed", value: formatNumber(result.metrics.deletedFalseBoxCount, 0), hint: `${formatNumber(result.metrics.sourceObjectCount, 0)}${formatNumber(result.metrics.objectCount, 0)} objects` },
{ label: "Independent truth", value: "0/2", hint: "E46 Reviewer A/B untouched" },
]}
conclusion={{
proved: `На всех 32 кадрах выполнен object-level engineering QA: ${formatNumber(result.metrics.deletedFalseBoxCount, 0)} фоновых/дублирующих рамок удалены, ${formatNumber(result.metrics.geometrySnappedObjectCount, 0)} рамок геометрически уточнены, один SUV исправлен из heavy_vehicle в car.`,
notProved: "Не доказаны независимость, correctness неоднозначных дальних объектов, detector AP/precision/recall, движение/статика, live/hardware или safety-пригодность. Количество рамок не является метрикой качества модели.",
decision: "Использовать обновлённый E46A как assisted correction/approval. Следующий архитектурный шаг — temporal motion-state для размеченных объектов; независимый benchmark остаётся отдельным Reviewer A/B → adjudication → E48 seal.",
}}
/>}
/>
);
}
@@ -0,0 +1,120 @@
import { useEffect, useRef, useState } from "react";
import type { E46ACase } from "../../core/laboratory/e46aAiEngineeringPreannotation";
export type E46ASceneMode = "preannotation" | "source";
function color(
host: HTMLElement,
token: string,
fallback: readonly [number, number, number],
alpha = 1,
): string {
const value = getComputedStyle(host).getPropertyValue(token).trim();
const channels = value.match(/[\d.]+/g)?.slice(0, 3).map(Number);
const [red, green, blue] = channels?.length === 3 ? channels : fallback;
return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
}
function objectColor(host: HTMLElement, category: string): string {
if (category === "person" || category === "stroller" || category === "laptop") {
return color(host, "--nodedc-accent-rgb", [232, 56, 126]);
}
if (category === "heavy_vehicle") {
return color(host, "--nodedc-warning-rgb", [255, 197, 92]);
}
if (category === "bicycle" || category === "motorcycle") {
return color(host, "--nodedc-success-rgb", [181, 255, 90]);
}
return color(host, "--nodedc-foreground-rgb", [245, 245, 245]);
}
export function E46AAiEngineeringPreannotationScene({
frame,
mode,
}: {
frame: E46ACase;
mode: E46ASceneMode;
}) {
const hostRef = useRef<HTMLDivElement | null>(null);
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const [image, setImage] = useState<HTMLImageElement | null>(null);
const [failed, setFailed] = useState(false);
useEffect(() => {
const next = new Image();
next.decoding = "async";
next.onload = () => { setImage(next); setFailed(false); };
next.onerror = () => { setImage(null); setFailed(true); };
next.src = frame.cameraUrl;
return () => { next.onload = null; next.onerror = null; };
}, [frame.cameraUrl]);
useEffect(() => {
const host = hostRef.current;
const canvas = canvasRef.current;
if (!host || !canvas || !image) return;
const context = canvas.getContext("2d");
if (!context) return;
const render = () => {
const width = Math.max(host.clientWidth, 1);
const height = Math.max(host.clientHeight, 1);
const pixelRatio = Math.min(window.devicePixelRatio, 1.5);
canvas.width = Math.round(width * pixelRatio);
canvas.height = Math.round(height * pixelRatio);
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
context.fillStyle = color(host, "--nodedc-canvas-rgb", [5, 5, 6]);
context.fillRect(0, 0, width, height);
const scale = Math.min(width / 800, height / 600);
const drawWidth = 800 * scale;
const drawHeight = 600 * scale;
const offsetX = (width - drawWidth) / 2;
const offsetY = (height - drawHeight) / 2;
context.drawImage(image, offsetX, offsetY, drawWidth, drawHeight);
if (mode === "source") return;
frame.objects.forEach((item) => {
const [left, top, right, bottom] = item.boxXyxy;
const x = offsetX + left * scale;
const y = offsetY + top * scale;
const boxWidth = (right - left) * scale;
const boxHeight = (bottom - top) * scale;
const stroke = objectColor(host, item.category);
context.strokeStyle = stroke;
context.lineWidth = Math.max(1.5, 2 * scale);
context.strokeRect(x, y, boxWidth, boxHeight);
const label = item.displayCategory;
const fontSize = Math.max(9, 10 * scale);
context.font = `600 ${fontSize}px Inter, system-ui, sans-serif`;
const labelWidth = context.measureText(label).width + 8;
const labelHeight = fontSize + 6;
const labelX = Math.min(offsetX + drawWidth - labelWidth, Math.max(offsetX, x));
const labelY = Math.max(offsetY, y - labelHeight);
context.fillStyle = color(host, "--nodedc-canvas-rgb", [5, 5, 6], 0.9);
context.fillRect(labelX, labelY, labelWidth, labelHeight);
context.fillStyle = stroke;
context.fillText(label, labelX + 4, labelY + fontSize + 1);
});
};
const observer = new ResizeObserver(render);
observer.observe(host);
render();
return () => observer.disconnect();
}, [frame, image, mode]);
return (
<div className="l32-camera-scene" ref={hostRef}>
<canvas
ref={canvasRef}
role="img"
aria-label={`E46A frame ${frame.frameIndex}: ${mode}, ${frame.objectCount} preannotations`}
/>
{failed ? (
<div className="l3-visual-audit__state" role="status">
Точный source-кадр E46A недоступен.
</div>
) : null}
</div>
);
}
@@ -0,0 +1,120 @@
import { useEffect, useState } from "react";
import { Icon, IconButton, Select } from "@nodedc/ui-react";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
import {
fetchE46ACase,
type E46ACase,
} from "../../core/laboratory/e46aAiEngineeringPreannotation";
import {
E46AAiEngineeringPreannotationScene,
type E46ASceneMode,
} from "./E46AAiEngineeringPreannotationScene";
const SEQUENCES = Array.from({ length: 32 }, (_, index) => index + 1);
export function E46AAiEngineeringPreannotationVisual({
resultId,
}: {
resultId: string;
}) {
const [selectedSequence, setSelectedSequence] = useState(1);
const [frame, setFrame] = useState<E46ACase | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [mode, setMode] = useState<E46ASceneMode>("preannotation");
const [expanded, setExpanded] = useState(false);
useEffect(() => {
const controller = new AbortController();
setLoading(true);
setError(null);
setFrame(null);
void fetchE46ACase(resultId, selectedSequence, { signal: controller.signal })
.then((next) => { if (!controller.signal.aborted) setFrame(next); })
.catch((caught: unknown) => {
if (!controller.signal.aborted) {
setError(caught instanceof Error ? caught.message : "E46A frame недоступен.");
}
})
.finally(() => { if (!controller.signal.aborted) setLoading(false); });
return () => controller.abort();
}, [resultId, selectedSequence]);
const navigate = (offset: -1 | 1) => {
setSelectedSequence((current) => ((current - 1 + offset + 32) % 32) + 1);
};
const actions = (
<div className="l3-visual-audit__actions">
<div className="l3-visual-audit__pagination">
<IconButton label="Предыдущий кадр E46A" onClick={() => navigate(-1)}>
<Icon name="chevron-left" size={16} />
</IconButton>
<IconButton label="Следующий кадр E46A" onClick={() => navigate(1)}>
<Icon name="chevron-right" size={16} />
</IconButton>
</div>
<Select
label="Выбрать кадр E46A"
value={String(selectedSequence)}
options={SEQUENCES.map((sequence) => ({
value: String(sequence),
label: frame?.truthIslandSequence === sequence
? `${sequence}/32 · frame ${frame.frameIndex} · ${frame.groupId}`
: `${sequence}/32 · E46 source frame`,
}))}
variant="split"
menuWidth="anchor"
searchable
searchPlaceholder="Найти номер кадра"
onChange={(value) => setSelectedSequence(Number(value))}
/>
</div>
);
const overlay = frame ? (
<div className="l3-visual-audit__overlay">
<div>
<span>RAVNOVES00 · sensor.camera.right</span>
<strong>{frame.sessionSeconds.toLocaleString("ru-RU", { maximumFractionDigits: 1 })} с · frame {frame.frameIndex}</strong>
<small>Sequence {frame.truthIslandSequence}/32 · {frame.groupId}</small>
</div>
<div>
<span>E46A · AI engineering preannotation</span>
<strong>{frame.objectCount} объектов · визуально проверено</strong>
<small>Not independent · not truth · image {frame.sourceImageSha256.slice(0, 12)}</small>
</div>
</div>
) : undefined;
return (
<div className="l3-visual-audit">
<LaboratoryEvidenceViewer
label="E46A AI engineering preannotation and exact source"
mode={mode}
modes={[
{ value: "preannotation", label: "PREANNOTATION" },
{ value: "source", label: "SOURCE" },
]}
expanded={expanded}
onModeChange={setMode}
onExpandedChange={setExpanded}
actions={actions}
overlay={overlay}
>
{loading ? (
<div className="l3-visual-audit__state" role="status">
<span className="busy-indicator" aria-hidden="true" />
<span>Открываем hash-bound E46A preannotation</span>
</div>
) : error || !frame ? (
<div className="l3-visual-audit__state" role="status">
<Icon name="alert" size={18} />
<span>{error ?? "E46A frame недоступен."}</span>
</div>
) : (
<E46AAiEngineeringPreannotationScene frame={frame} mode={mode} />
)}
</LaboratoryEvidenceViewer>
</div>
);
}
@@ -0,0 +1,12 @@
import { LaboratoryEvidence, LaboratoryResultSummary, LaboratorySummary, LaboratoryWorkTemplate } from "../../components/laboratory/LaboratoryPresentation";
import type { E46BTemporalMotionResult } from "../../core/laboratory/e46bTemporalMotion";
import { E46BTemporalMotionVisual } from "./E46BTemporalMotionVisual";
export function E46BTemporalMotionResultView({ rigLabel, result }: { rigLabel: string; result: E46BTemporalMotionResult }) {
const metrics = result.metrics;
return <LaboratoryWorkTemplate
summary={<LaboratorySummary title="LAB E46B · RIGHT temporal tracks + motion-state" description="Четыре записанных temporal-клипа E46A связаны в устойчивые source-scoped ID. Состояние движения публикуется только при решающем E26 ego-motion/LiDAR evidence; остальные объекты честно остаются unknown." status={`16/16 visual evidence · ${metrics.trackCount} tracks · recorded RIGHT`} statusTone="warning" facts={[{ label: "Конфигурация", value: `${rigLabel} · RIGHT camera · recorded replay` }, { label: "Temporal", value: `${metrics.temporalGroupCount} клипа · ${metrics.frameCount} кадров · ${metrics.objectObservationCount} наблюдений` }, { label: "Motion", value: `${metrics.dynamicTrackCount} dynamic · ${metrics.staticTrackCount} static · ${metrics.unknownTrackCount} unknown` }, { label: "Полномочия", value: "Engineering evidence · no metric velocity · no safety" }]} brief={{ question: "Можно ли превратить отдельные рамки в стабильные объекты и различить движение/статику на записи?", approach: "Внутри каждого фиксированного четырёхкадрового клипа объекты связаны по классу и пространственному порядку. Каждая рамка отдельно сопоставлена с hash-bound E26 KB4 ego-motion/LiDAR fusion на том же source frame; конфликт или отсутствие evidence даёт unknown.", principalResult: `${metrics.trackCount} устойчивых source-scoped ID: ${metrics.dynamicTrackCount} движется, ${metrics.staticTrackCount} стоит, ${metrics.unknownTrackCount} остаются неизвестными. Визуал показывает рамку, ID и накопленный след на всех 16 кадрах.`, limitation: "ID действуют только внутри четырёх коротких клипов. Это не route-global tracking, не метрическая скорость, не truth и не acceptance детектора." }} method={{ completeness: "complete", executionClass: "hybrid", pipelineId: "e46b-source-scoped-temporal-motion/v1", components: [{ kind: "source", name: result.sourceE46AResultId, version: "E46A immutable", role: "object geometry on exact RIGHT frames", identitySha256: result.sourceE46AResultId.split("-").at(-1) ?? null }, { kind: "algorithm", name: "Source-scoped temporal association", version: "category-x-rank/v1", role: "stable IDs within four-frame clips", identitySha256: null }, { kind: "algorithm", name: result.sourceE26ResultId, version: "KB4 ego-motion + LiDAR fusion", role: "conservative dynamic/static/unknown evidence", identitySha256: result.sourceE26ResultId.split("-").at(-1) ?? null }] }} />}
evidence={<LaboratoryEvidence eyebrow="E46B VISUAL EVIDENCE · TRACKS / SOURCE" title="Устойчивый ID, след и motion-state на каждом temporal-кадре" kind="diagnostic-model" resizable><E46BTemporalMotionVisual resultId={result.resultId} /></LaboratoryEvidence>}
result={<LaboratoryResultSummary title="Объектный слой получил temporal identity и консервативный motion-state" status="Source-scoped temporal layer complete · route-global tracking next" statusTone="warning" metrics={[{ label: "Temporal frames", value: "16/16", hint: "4 fixed clips" }, { label: "Stable IDs", value: String(metrics.trackCount), hint: `${metrics.objectObservationCount} observations` }, { label: "Decisive motion", value: String(metrics.dynamicTrackCount + metrics.staticTrackCount), hint: `${metrics.dynamicTrackCount} dynamic · ${metrics.staticTrackCount} static` }, { label: "Unknown", value: String(metrics.unknownTrackCount), hint: "no guess without evidence" }]} conclusion={{ proved: `На 16 recorded RIGHT кадрах опубликованы ${metrics.trackCount} стабильных ID с визуальными следами; ${metrics.dynamicTrackCount + metrics.staticTrackCount} треков имеют непротиворечивое E26 motion evidence.`, notProved: "Не доказаны route-global identity, метрическая скорость для camera-only объектов, detector AP/recall, live/hardware или safety-пригодность.", decision: "Принять E46B как recorded temporal object layer. Следующий архитектурный шаг — расширить ассоциацию с коротких truth-island клипов на полный recorded replay и затем связать confirmed tracks с temporal occupied/unknown world layer." }} />}
/>;
}
@@ -0,0 +1,65 @@
import { useEffect, useRef, useState } from "react";
import type { E46BCase, E46BMotionState } from "../../core/laboratory/e46bTemporalMotion";
export type E46BSceneMode = "tracks" | "source";
function color(host: HTMLElement, token: string, fallback: readonly [number, number, number], alpha = 1): string {
const channels = getComputedStyle(host).getPropertyValue(token).trim().match(/[\d.]+/g)?.slice(0, 3).map(Number);
const [red, green, blue] = channels?.length === 3 ? channels : fallback;
return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
}
function stateColor(host: HTMLElement, state: E46BMotionState): string {
return state === "dynamic" ? color(host, "--nodedc-accent-rgb", [232, 56, 126])
: state === "static" ? color(host, "--nodedc-success-rgb", [181, 255, 90])
: color(host, "--nodedc-warning-rgb", [255, 197, 92]);
}
const stateLabel = (state: E46BMotionState) => state === "dynamic" ? "движется" : state === "static" ? "стоит" : "неизвестно";
export function E46BTemporalMotionScene({ frame, mode }: { frame: E46BCase; mode: E46BSceneMode }) {
const hostRef = useRef<HTMLDivElement | null>(null);
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const [image, setImage] = useState<HTMLImageElement | null>(null);
const [failed, setFailed] = useState(false);
useEffect(() => {
const next = new Image(); next.decoding = "async";
next.onload = () => { setImage(next); setFailed(false); };
next.onerror = () => { setImage(null); setFailed(true); };
next.src = frame.cameraUrl;
return () => { next.onload = null; next.onerror = null; };
}, [frame.cameraUrl]);
useEffect(() => {
const host = hostRef.current; const canvas = canvasRef.current;
if (!host || !canvas || !image) return;
const context = canvas.getContext("2d"); if (!context) return;
const render = () => {
const width = Math.max(host.clientWidth, 1); const height = Math.max(host.clientHeight, 1);
const pixelRatio = Math.min(window.devicePixelRatio, 1.5);
canvas.width = Math.round(width * pixelRatio); canvas.height = Math.round(height * pixelRatio);
canvas.style.width = `${width}px`; canvas.style.height = `${height}px`;
context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
context.fillStyle = color(host, "--nodedc-canvas-rgb", [5, 5, 6]); context.fillRect(0, 0, width, height);
const scale = Math.min(width / 800, height / 600); const drawWidth = 800 * scale; const drawHeight = 600 * scale;
const offsetX = (width - drawWidth) / 2; const offsetY = (height - drawHeight) / 2;
context.drawImage(image, offsetX, offsetY, drawWidth, drawHeight);
if (mode === "source") return;
frame.objects.forEach((item) => {
const stroke = stateColor(host, item.motionState);
if (item.trailCentersXy.length > 1) {
context.beginPath(); context.strokeStyle = stroke; context.lineWidth = Math.max(1.5, 2 * scale);
item.trailCentersXy.forEach(([px, py], index) => index === 0 ? context.moveTo(offsetX + px * scale, offsetY + py * scale) : context.lineTo(offsetX + px * scale, offsetY + py * scale));
context.stroke();
item.trailCentersXy.forEach(([px, py]) => { context.beginPath(); context.fillStyle = stroke; context.arc(offsetX + px * scale, offsetY + py * scale, Math.max(2, 3 * scale), 0, Math.PI * 2); context.fill(); });
}
const [left, top, right, bottom] = item.boxXyxy; const x = offsetX + left * scale; const y = offsetY + top * scale;
context.strokeStyle = stroke; context.lineWidth = Math.max(1.5, 2 * scale); context.strokeRect(x, y, (right - left) * scale, (bottom - top) * scale);
const label = `#${String(item.trackIndex).padStart(2, "0")} · ${item.displayCategory} · ${stateLabel(item.motionState)}`;
const fontSize = Math.max(9, 10 * scale); context.font = `600 ${fontSize}px Inter, system-ui, sans-serif`;
const labelWidth = context.measureText(label).width + 8; const labelHeight = fontSize + 6;
const labelX = Math.min(offsetX + drawWidth - labelWidth, Math.max(offsetX, x)); const labelY = Math.max(offsetY, y - labelHeight);
context.fillStyle = color(host, "--nodedc-canvas-rgb", [5, 5, 6], 0.9); context.fillRect(labelX, labelY, labelWidth, labelHeight);
context.fillStyle = stroke; context.fillText(label, labelX + 4, labelY + fontSize + 1);
});
};
const observer = new ResizeObserver(render); observer.observe(host); render(); return () => observer.disconnect();
}, [frame, image, mode]);
return <div className="l32-camera-scene" ref={hostRef}><canvas ref={canvasRef} role="img" aria-label={`E46B frame ${frame.frameIndex}: ${frame.objectCount} temporal tracks`} />{failed ? <div className="l3-visual-audit__state" role="status">Точный source-кадр E46B недоступен.</div> : null}</div>;
}
@@ -0,0 +1,21 @@
import { useEffect, useState } from "react";
import { Icon, IconButton, Select } from "@nodedc/ui-react";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
import { fetchE46BCase, type E46BCase } from "../../core/laboratory/e46bTemporalMotion";
import { E46BTemporalMotionScene, type E46BSceneMode } from "./E46BTemporalMotionScene";
const SEQUENCES = [5, 6, 7, 8, 14, 15, 16, 17, 23, 24, 25, 26, 29, 30, 31, 32] as const;
export function E46BTemporalMotionVisual({ resultId }: { resultId: string }) {
const [selectedSequence, setSelectedSequence] = useState<number>(5); const [frame, setFrame] = useState<E46BCase | null>(null);
const [loading, setLoading] = useState(true); const [error, setError] = useState<string | null>(null);
const [mode, setMode] = useState<E46BSceneMode>("tracks"); const [expanded, setExpanded] = useState(false);
useEffect(() => {
const controller = new AbortController(); setLoading(true); setError(null); setFrame(null);
void fetchE46BCase(resultId, selectedSequence, { signal: controller.signal }).then((next) => { if (!controller.signal.aborted) setFrame(next); }).catch((caught: unknown) => { if (!controller.signal.aborted) setError(caught instanceof Error ? caught.message : "E46B frame недоступен."); }).finally(() => { if (!controller.signal.aborted) setLoading(false); });
return () => controller.abort();
}, [resultId, selectedSequence]);
const navigate = (offset: -1 | 1) => { const index = SEQUENCES.indexOf(selectedSequence as typeof SEQUENCES[number]); setSelectedSequence(SEQUENCES[(index + offset + SEQUENCES.length) % SEQUENCES.length]); };
const actions = <div className="l3-visual-audit__actions"><div className="l3-visual-audit__pagination"><IconButton label="Предыдущий temporal кадр" onClick={() => navigate(-1)}><Icon name="chevron-left" size={16} /></IconButton><IconButton label="Следующий temporal кадр" onClick={() => navigate(1)}><Icon name="chevron-right" size={16} /></IconButton></div><Select label="Выбрать temporal кадр" value={String(selectedSequence)} options={SEQUENCES.map((sequence, index) => ({ value: String(sequence), label: `${index + 1}/16 · E46 seq ${sequence}${frame?.truthIslandSequence === sequence ? ` · frame ${frame.frameIndex}` : ""}` }))} variant="split" menuWidth="anchor" onChange={(value) => setSelectedSequence(Number(value))} /></div>;
const overlay = frame ? <div className="l3-visual-audit__overlay"><div><span>RAVNOVES00 · sensor.camera.right</span><strong>{frame.groupId} · frame {frame.frameIndex}</strong><small>Sequence {frame.truthIslandSequence} · {frame.sessionSeconds.toLocaleString("ru-RU", { maximumFractionDigits: 1 })} с</small></div><div><span>E46B · temporal tracks</span><strong>{frame.objectCount} ID · {frame.motionCounts.dynamic} движется · {frame.motionCounts.static} стоит</strong><small>{frame.motionCounts.unknown} неизвестно · image {frame.sourceImageSha256.slice(0, 12)}</small></div></div> : undefined;
return <div className="l3-visual-audit"><LaboratoryEvidenceViewer label="E46B temporal tracks and exact source" mode={mode} modes={[{ value: "tracks", label: "TRACKS" }, { value: "source", label: "SOURCE" }]} expanded={expanded} onModeChange={setMode} onExpandedChange={setExpanded} actions={actions} overlay={overlay}>{loading ? <div className="l3-visual-audit__state" role="status"><span className="busy-indicator" aria-hidden="true" /><span>Открываем E46B temporal evidence</span></div> : error || !frame ? <div className="l3-visual-audit__state" role="status"><Icon name="alert" size={18} /><span>{error ?? "E46B frame недоступен."}</span></div> : <E46BTemporalMotionScene frame={frame} mode={mode} />}</LaboratoryEvidenceViewer></div>;
}
@@ -0,0 +1,79 @@
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import type { E46BlindReviewResult } from "../../core/laboratory/e46BlindReview";
import { E46BlindReviewVisual } from "./E46BlindReviewVisual";
export function E46BlindReviewResultView({
rigLabel,
result,
}: {
rigLabel: string;
result: E46BlindReviewResult;
}) {
const metrics = result.metrics;
const complete = result.decision.twoIndependentReviewsComplete;
return (
<LaboratoryWorkTemplate
summary={<LaboratorySummary
title="LAB E46 · independent source-only detector review"
description="Два раздельных reviewer-слота получают те же 32 immutable кадра RAVNOVES00 без identity YOLOX-кандидата, predictions, scores, prelabels и разметки другого рецензента."
status={complete ? "2/2 frozen · нужна adjudication" : `${metrics.completedSubmissionCount}/2 independent reviews frozen`}
statusTone="warning"
facts={[
{ label: "Конфигурация", value: `${rigLabel} · RIGHT camera · recorded replay` },
{ label: "Источник", value: `${metrics.frameCount} кадров · ${metrics.anchorCount} anchors + ${metrics.temporalFrameCount} temporal` },
{ label: "Reviewer slots", value: `${metrics.reviewSessionCount}/2 созданы · ${metrics.completedSubmissionCount}/2 frozen` },
{ label: "Полномочия", value: "Not truth · E48 unsealed · L3.5 closed" },
]}
brief={{
question: "Как собрать две реально независимые разметки E46, не подсказывая людям результат модели или первого reviewer?",
approach: "Каждый рецензент получает приватный capability-bound слот, фиксированную семиклассовую taxonomy и 32 exact source frame. Чужой слот сервер не раскрывает; freeze разрешён только после 32/32 и трёх явных attestations о независимости и model blindness.",
principalResult: `Контур готов; заморожено ${metrics.completedSubmissionCount} из 2 обязательных review. До второго независимого freeze никаких AP/recall или выводов о качестве детектора не существует.`,
limitation: "Интерфейс и криптографическая привязка источника контролируют данные, но утверждение о том, что reviewer физически не видел модель вне системы, остаётся человеческой аттестацией.",
}}
method={{
completeness: "complete",
executionClass: "deterministic",
pipelineId: "e46-source-only-independent-review/v1",
components: [
{ kind: "source", name: result.resultId, version: "E46 immutable references", role: "32 hash-bound right-camera frames", identitySha256: result.resultId.split("-").at(-1) ?? null },
{ kind: "algorithm", name: "Capability-separated reviewer slots", version: "2-slot/v1", role: "prevent cross-review label access", identitySha256: null },
{ kind: "tool", name: "Fullscreen canonical box editor", version: "source-only", role: "manual move, resize, add, delete and classify", identitySha256: null },
],
}}
/>}
evidence={<LaboratoryEvidence
eyebrow="E46 VISUAL SOURCE · CANDIDATE IDENTITY EXCLUDED"
title="Все 32 кадра доступны как source-only evidence"
kind="diagnostic-model"
resizable
>
<E46BlindReviewVisual resultId={result.resultId} />
</LaboratoryEvidence>}
result={<LaboratoryResultSummary
title={complete
? "Два независимых входа собраны; seal всё ещё закрыт до adjudication"
: "Контур готов; независимые человеческие проходы ещё не собраны"}
status={complete ? "Adjudication required before E48" : "Reviewer A + Reviewer B required"}
statusTone="warning"
metrics={[
{ label: "Source frames", value: "32/32", hint: "16 anchors · 4 temporal groups" },
{ label: "Reviewer slots", value: `${metrics.reviewSessionCount}/2`, hint: "Capability separated" },
{ label: "Frozen inputs", value: `${metrics.completedSubmissionCount}/2`, hint: "E48 inputs · not truth" },
{ label: "Model material", value: "0", hint: "No candidate id, boxes, scores or prelabels" },
]}
conclusion={{
proved: "Источник для независимого контроля воспроизводим, визуально доступен и отделён от model material и чужих reviewer labels. Сервер не примет неполный, assisted или произвольный-class проход.",
notProved: "Пока не доказаны correctness разметки, межэкспертное согласие, качество YOLOX, AP/recall, metric-grade truth или пригодность системы к live/hardware/safety.",
decision: complete
? "Выполнить отдельную adjudication двух immutable reviews; только затем разрешить E48 truth seal и один acceptance-прогон L3.5."
: "Передать Reviewer A и Reviewer B их отдельные слоты. Не показывать им L3.4/L3.4D/L3.4F до freeze собственного прохода.",
}}
/>}
/>
);
}
@@ -0,0 +1,147 @@
import { useEffect, useState } from "react";
import { Icon, IconButton, Select } from "@nodedc/ui-react";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
import {
fetchL34AnnotationSourceCatalog,
fetchL34AnnotationSourceFrame,
type L34AnnotationSourceCatalog,
type L34AnnotationSourceFrame,
} from "../../core/laboratory/l34Annotation";
function frameLabel(frame: L34AnnotationSourceCatalog["frames"][number]): string {
return `${frame.truthIslandSequence}/32 · frame ${frame.frameIndex} · ${frame.groupId}`;
}
export function E46BlindReviewVisual({ resultId }: { resultId: string }) {
const [catalog, setCatalog] = useState<L34AnnotationSourceCatalog | null>(null);
const [selectedSequence, setSelectedSequence] = useState(1);
const [frame, setFrame] = useState<L34AnnotationSourceFrame | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [expanded, setExpanded] = useState(false);
useEffect(() => {
const controller = new AbortController();
setLoading(true);
setError(null);
void fetchL34AnnotationSourceCatalog(resultId, {
signal: controller.signal,
}).then((next) => {
if (controller.signal.aborted) return;
setCatalog(next);
setSelectedSequence(next.frames[0]?.truthIslandSequence ?? 1);
}).catch((caught: unknown) => {
if (!controller.signal.aborted) {
setError(caught instanceof Error ? caught.message : "E46 source недоступен.");
}
}).finally(() => {
if (!controller.signal.aborted) setLoading(false);
});
return () => controller.abort();
}, [resultId]);
useEffect(() => {
if (!catalog) return;
const controller = new AbortController();
setLoading(true);
setFrame(null);
void fetchL34AnnotationSourceFrame(resultId, selectedSequence, {
signal: controller.signal,
}).then((next) => {
if (!controller.signal.aborted) setFrame(next);
}).catch((caught: unknown) => {
if (!controller.signal.aborted) {
setError(caught instanceof Error ? caught.message : "E46 frame недоступен.");
}
}).finally(() => {
if (!controller.signal.aborted) setLoading(false);
});
return () => controller.abort();
}, [catalog, resultId, selectedSequence]);
const selectedIndex = catalog?.frames.findIndex(
(item) => item.truthIslandSequence === selectedSequence,
) ?? -1;
const navigate = (offset: -1 | 1) => {
if (!catalog || selectedIndex < 0) return;
const next = (
selectedIndex + offset + catalog.frames.length
) % catalog.frames.length;
setSelectedSequence(catalog.frames[next].truthIslandSequence);
};
const actions = catalog ? (
<div className="l3-visual-audit__actions">
<div className="l3-visual-audit__pagination">
<IconButton label="Предыдущий source-only кадр E46" onClick={() => navigate(-1)}>
<Icon name="chevron-left" size={16} />
</IconButton>
<IconButton label="Следующий source-only кадр E46" onClick={() => navigate(1)}>
<Icon name="chevron-right" size={16} />
</IconButton>
</div>
<Select
label="Выбрать source-only кадр E46"
value={String(selectedSequence)}
options={catalog.frames.map((item) => ({
value: String(item.truthIslandSequence),
label: frameLabel(item),
}))}
variant="split"
menuWidth="anchor"
searchable
searchPlaceholder="Найти кадр или группу"
onChange={(value) => setSelectedSequence(Number(value))}
/>
</div>
) : null;
const overlay = frame ? (
<div className="l3-visual-audit__overlay">
<div>
<span>RAVNOVES00 · sensor.camera.right</span>
<strong>{frame.sessionSeconds.toLocaleString("ru-RU", { maximumFractionDigits: 1 })} с · frame {frame.frameIndex}</strong>
<small>Sequence {frame.truthIslandSequence}/32 · {frame.groupId}</small>
</div>
<div>
<span>E46 independent review source</span>
<strong>0 boxes · 0 labels · 0 scores</strong>
<small>Candidate identity не включена · image {frame.cameraSha256.slice(0, 12)}</small>
</div>
</div>
) : undefined;
return (
<div className="l3-visual-audit">
<LaboratoryEvidenceViewer
label="E46 source-only independent reviewer evidence"
mode="source"
modes={[{ value: "source", label: "SOURCE ONLY" }]}
expanded={expanded}
onModeChange={() => undefined}
onExpandedChange={setExpanded}
actions={actions}
overlay={overlay}
>
{loading ? (
<div className="l3-visual-audit__state" role="status">
<span className="busy-indicator" aria-hidden="true" />
<span>Открываем hash-bound E46 source без model material</span>
</div>
) : error || !frame ? (
<div className="l3-visual-audit__state" role="status">
<Icon name="alert" size={18} />
<span>{error ?? "E46 source frame недоступен."}</span>
</div>
) : (
<div className="l32-camera-scene">
<img
src={frame.cameraUrl}
alt={`E46 source-only кадр ${frame.frameIndex} без model overlay`}
draggable={false}
/>
</div>
)}
</LaboratoryEvidenceViewer>
</div>
);
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,24 @@
import { useEffect, useRef, useState } from "react";
import type { E46CCase, E46CMotionState } from "../../core/laboratory/e46cFullReplayWorldTracks";
export type E46CSceneMode = "camera" | "world";
function color(host: HTMLElement, token: string, fallback: readonly [number, number, number], alpha = 1): string { const channels = getComputedStyle(host).getPropertyValue(token).trim().match(/[\d.]+/g)?.slice(0, 3).map(Number); const [red, green, blue] = channels?.length === 3 ? channels : fallback; return `rgba(${red}, ${green}, ${blue}, ${alpha})`; }
function stateColor(host: HTMLElement, state: E46CMotionState): string { return state === "dynamic" ? color(host, "--nodedc-accent-rgb", [232, 56, 126]) : state === "static" ? color(host, "--nodedc-success-rgb", [181, 255, 90]) : color(host, "--nodedc-warning-rgb", [255, 197, 92]); }
const stateLabel = (state: E46CMotionState) => state === "dynamic" ? "движется" : state === "static" ? "стоит" : "unknown";
export function E46CFullReplayWorldTracksScene({ frame, mode }: { frame: E46CCase; mode: E46CSceneMode }) {
const hostRef = useRef<HTMLDivElement | null>(null); const canvasRef = useRef<HTMLCanvasElement | null>(null); const [image, setImage] = useState<HTMLImageElement | null>(null); const [failed, setFailed] = useState(false);
useEffect(() => { const next = new Image(); next.decoding = "async"; next.onload = () => { setImage(next); setFailed(false); }; next.onerror = () => { setImage(null); setFailed(true); }; next.src = frame.cameraUrl; return () => { next.onload = null; next.onerror = null; }; }, [frame.cameraUrl]);
useEffect(() => {
const host = hostRef.current; const canvas = canvasRef.current; if (!host || !canvas || (mode === "camera" && !image)) return; const context = canvas.getContext("2d"); if (!context) return;
const render = () => { const width = Math.max(host.clientWidth, 1); const height = Math.max(host.clientHeight, 1); const pixelRatio = Math.min(window.devicePixelRatio, 1.5); canvas.width = Math.round(width * pixelRatio); canvas.height = Math.round(height * pixelRatio); canvas.style.width = `${width}px`; canvas.style.height = `${height}px`; context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0); context.fillStyle = color(host, "--nodedc-canvas-rgb", [5, 5, 6]); context.fillRect(0, 0, width, height);
if (mode === "camera" && image) { const scale = Math.min(width / 800, height / 600); const drawWidth = 800 * scale; const drawHeight = 600 * scale; const offsetX = (width - drawWidth) / 2; const offsetY = (height - drawHeight) / 2; context.drawImage(image, offsetX, offsetY, drawWidth, drawHeight); frame.objects.forEach((item) => { const [left, top, right, bottom] = item.boxXyxy; const x = offsetX + left * scale; const y = offsetY + top * scale; const stroke = stateColor(host, item.motionState); context.strokeStyle = stroke; context.lineWidth = Math.max(1.5, 2 * scale); context.strokeRect(x, y, (right - left) * scale, (bottom - top) * scale); const identity = item.routeTrackId === null ? "S—" : `S${item.routeTrackId}${item.worldTrackId === null ? "" : `→W${item.worldTrackId}`}`; const label = `${identity} · ${item.displayCategory} · ${stateLabel(item.motionState)}`; const fontSize = Math.max(9, 10 * scale); context.font = `600 ${fontSize}px Inter, system-ui, sans-serif`; const labelWidth = context.measureText(label).width + 8; const labelHeight = fontSize + 6; const labelX = Math.min(offsetX + drawWidth - labelWidth, Math.max(offsetX, x)); const labelY = Math.max(offsetY, y - labelHeight); context.fillStyle = color(host, "--nodedc-canvas-rgb", [5, 5, 6], 0.9); context.fillRect(labelX, labelY, labelWidth, labelHeight); context.fillStyle = stroke; context.fillText(label, labelX + 4, labelY + fontSize + 1); }); return; }
const footprints = frame.worldObjects.flatMap((item) => item.occupancyFootprintMapXy); if (!footprints.length) { context.fillStyle = color(host, "--nodedc-foreground-rgb", [245, 245, 245], 0.64); context.font = "600 14px Inter, system-ui, sans-serif"; context.textAlign = "center"; context.fillText("На этом sample-кадре world-state отсутствует", width / 2, height / 2); context.textAlign = "start"; return; }
const xs = footprints.map((point) => point[0]); const ys = footprints.map((point) => point[1]); const minX = Math.min(...xs); const maxX = Math.max(...xs); const minY = Math.min(...ys); const maxY = Math.max(...ys); const padding = 56; const spanX = Math.max(maxX - minX, 8); const spanY = Math.max(maxY - minY, 8); const scale = Math.min((width - padding * 2) / spanX, (height - padding * 2) / spanY); const project = ([x, y]: readonly [number, number]) => [padding + (x - minX) * scale, height - padding - (y - minY) * scale] as const;
context.strokeStyle = color(host, "--nodedc-foreground-rgb", [245, 245, 245], 0.1); context.lineWidth = 1; for (let gx = Math.floor(minX / 5) * 5; gx <= maxX; gx += 5) { const [x] = project([gx, minY]); context.beginPath(); context.moveTo(x, padding); context.lineTo(x, height - padding); context.stroke(); } for (let gy = Math.floor(minY / 5) * 5; gy <= maxY; gy += 5) { const [, y] = project([minX, gy]); context.beginPath(); context.moveTo(padding, y); context.lineTo(width - padding, y); context.stroke(); }
frame.worldObjects.forEach((item) => { const stroke = stateColor(host, item.motionState); const projected = item.occupancyFootprintMapXy.map(project); context.beginPath(); projected.forEach(([x, y], index) => index === 0 ? context.moveTo(x, y) : context.lineTo(x, y)); context.closePath(); context.fillStyle = item.occupancyEvidenceCurrent ? stroke.replace(", 1)", ", 0.28)") : stroke.replace(", 1)", ", 0.12)"); context.fill(); context.strokeStyle = stroke; context.setLineDash(item.occupancyEvidenceCurrent ? [] : [5, 4]); context.lineWidth = 2; context.stroke(); context.setLineDash([]); const [labelX, labelY] = project([item.positionMapM[0], item.positionMapM[1]]); const label = `S${item.routeTrackId}→W${item.worldTrackId} · ${stateLabel(item.motionState)} · ${item.occupancyEvidenceCurrent ? "current" : "held"}`; context.font = "600 11px Inter, system-ui, sans-serif"; const labelWidth = context.measureText(label).width + 8; const safeLabelX = Math.min(width - labelWidth - 18, Math.max(18, labelX + 5)); const safeLabelY = Math.min(height - 42, Math.max(22, labelY - 16)); context.fillStyle = color(host, "--nodedc-canvas-rgb", [5, 5, 6], 0.88); context.fillRect(safeLabelX, safeLabelY, labelWidth, 18); context.fillStyle = stroke; context.fillText(label, safeLabelX + 4, safeLabelY + 13); });
context.fillStyle = color(host, "--nodedc-foreground-rgb", [245, 245, 245], 0.58); context.font = "600 11px Inter, system-ui, sans-serif"; context.fillText("world map · occupied footprints · solid=current · dashed=held", 18, height - 18);
}; const observer = new ResizeObserver(render); observer.observe(host); render(); return () => observer.disconnect();
}, [frame, image, mode]);
return <div className="l32-camera-scene" ref={hostRef}><canvas ref={canvasRef} role="img" aria-label={`E46C frame ${frame.frameIndex}: ${mode}, ${frame.objectCount} camera objects, ${frame.worldObjectCount} world objects`} />{failed && mode === "camera" ? <div className="l3-visual-audit__state" role="status">Точный source-кадр E46C недоступен.</div> : null}</div>;
}
@@ -0,0 +1,358 @@
import { useEffect, useMemo, useState } from "react";
import { Icon, IconButton, Select } from "@nodedc/ui-react";
import type { RecordedObservationPlayback } from "../../components/RecordedFmp4Player";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
import {
fetchE46CCase,
fetchE46CVideoOverlay,
selectE46CVideoFrame,
type E46CCase,
type E46CVideoOverlay,
} from "../../core/laboratory/e46cFullReplayWorldTracks";
import { recordedObservationSources } from "../../core/observation/recordedObservationSources";
import { replayObservationSession } from "../../core/observation/sessionArchive";
import type { ObservationSourceDescriptor } from "../../core/runtime/contracts";
import { E46CRecordedVideoScene } from "./E46CRecordedVideoScene";
import {
E46CFullReplayWorldTracksScene,
type E46CSceneMode,
} from "./E46CFullReplayWorldTracksScene";
const SEQUENCES = Array.from({ length: 32 }, (_, index) => index + 1);
type E46CViewMode = "video" | E46CSceneMode;
export interface E46CTemporalAuditWindow {
id: string;
label: string;
startSeconds: number;
endSeconds: number;
title: string;
detail: string;
}
function reviewWindowLabel(
window: E46CVideoOverlay["reviewWindows"][number],
): string {
const kind = window.kind === "static-control" ? "статика" : "динамика";
const group = window.classGroup === "vehicle"
? "транспорт"
: window.classGroup === "person"
? "люди"
: window.classGroup === "vulnerable_road_user"
? "люди / велосипеды"
: window.classGroup;
const tracks = window.targetSourceTrackIds.length
? ` · S${window.targetSourceTrackIds.join(", S")}`
: "";
return `${window.startSeconds.toFixed(0)}${window.endSeconds.toFixed(0)} с · ${kind} · ${group}${tracks}`;
}
export function E46CFullReplayWorldTracksVisual({
resultId,
auditWindows,
}: {
resultId: string;
auditWindows?: readonly E46CTemporalAuditWindow[];
}) {
const [selectedSequence, setSelectedSequence] = useState(1);
const [frame, setFrame] = useState<E46CCase | null>(null);
const [sampleLoading, setSampleLoading] = useState(true);
const [sampleError, setSampleError] = useState<string | null>(null);
const [mode, setMode] = useState<E46CViewMode>("video");
const [expanded, setExpanded] = useState(false);
const [videoOverlay, setVideoOverlay] = useState<E46CVideoOverlay | null>(null);
const [videoSource, setVideoSource] = useState<ObservationSourceDescriptor | null>(null);
const [videoLoading, setVideoLoading] = useState(false);
const [videoError, setVideoError] = useState<string | null>(null);
const [selectedWindow, setSelectedWindow] = useState(auditWindows?.[0]?.id ?? "");
const [videoPlayback, setVideoPlayback] = useState<RecordedObservationPlayback>({
currentSeconds: 0,
playing: false,
});
useEffect(() => {
const controller = new AbortController();
setSampleLoading(true);
setSampleError(null);
setFrame(null);
void fetchE46CCase(resultId, selectedSequence, { signal: controller.signal })
.then((next) => {
if (!controller.signal.aborted) setFrame(next);
})
.catch((caught: unknown) => {
if (!controller.signal.aborted) {
setSampleError(caught instanceof Error ? caught.message : "E46C frame недоступен.");
}
})
.finally(() => {
if (!controller.signal.aborted) setSampleLoading(false);
});
return () => controller.abort();
}, [resultId, selectedSequence]);
useEffect(() => {
if (mode !== "video" || (videoOverlay && videoSource)) return;
const controller = new AbortController();
setVideoLoading(true);
setVideoError(null);
void (async () => {
const overlay = await fetchE46CVideoOverlay(resultId, {
signal: controller.signal,
});
const replay = await replayObservationSession(overlay.recordedSourceSessionId, {
signal: controller.signal,
});
if (replay.kind !== "ready") {
throw new Error("Физический RIGHT-архив ещё готовится к воспроизведению.");
}
const source = recordedObservationSources(replay.launch).find(
(candidate) =>
candidate.modality === "video" &&
candidate.semanticChannelId === "camera.video.recorded",
);
const delivery = source?.delivery?.kind === "recorded-fmp4-manifest"
? source.delivery
: null;
if (
!source ||
!delivery ||
delivery.timelineStartSeconds !== overlay.timelineStartSeconds ||
delivery.timelineEndSeconds < overlay.timelineEndSeconds
) {
throw new Error("RIGHT-видео не совпало с временным контрактом E46C.");
}
if (controller.signal.aborted) return;
setVideoOverlay(overlay);
setVideoSource(source);
const initialWindow = auditWindows?.find((item) => item.id === selectedWindow);
setVideoPlayback({
currentSeconds: initialWindow?.startSeconds ?? overlay.timelineStartSeconds,
playing: false,
});
})()
.catch((caught: unknown) => {
if (!controller.signal.aborted) {
setVideoError(
caught instanceof Error ? caught.message : "E46C video replay недоступен.",
);
}
})
.finally(() => {
if (!controller.signal.aborted) setVideoLoading(false);
});
return () => controller.abort();
}, [auditWindows, mode, resultId, selectedWindow, videoOverlay, videoSource]);
const activeVideoFrame = useMemo(
() =>
videoOverlay
? selectE46CVideoFrame(videoOverlay.frames, videoPlayback.currentSeconds)
: null,
[videoOverlay, videoPlayback.currentSeconds],
);
const activeAuditWindow = auditWindows?.find((item) => item.id === selectedWindow) ?? null;
const navigate = (offset: -1 | 1) => {
setSelectedSequence((current) => ((current - 1 + offset + 32) % 32) + 1);
};
const seekVideo = (seconds: number) => {
if (!videoOverlay) return;
setVideoPlayback({
currentSeconds: Math.min(
videoOverlay.timelineEndSeconds,
Math.max(videoOverlay.timelineStartSeconds, seconds),
),
playing: false,
});
};
const actions = mode === "video" ? (
<div className="l3-visual-audit__actions">
<div className="l3-visual-audit__pagination">
<IconButton
label="Назад на 5 секунд"
onClick={() => seekVideo(videoPlayback.currentSeconds - 5)}
>
<Icon name="chevron-left" size={16} />
</IconButton>
<IconButton
label="Вперёд на 5 секунд"
onClick={() => seekVideo(videoPlayback.currentSeconds + 5)}
>
<Icon name="chevron-right" size={16} />
</IconButton>
</div>
{auditWindows?.length || videoOverlay?.reviewWindows.length ? (
<Select
label={auditWindows?.length
? "Перейти к автоматически найденному эпизоду E46D"
: "Перейти к проверочному окну E26"}
value={selectedWindow || "full-route"}
options={[
{ value: "full-route", label: "Весь проход · с начала" },
...(auditWindows?.map((window) => ({
value: window.id,
label: window.label,
})) ?? videoOverlay?.reviewWindows.map((window) => ({
value: window.id,
label: reviewWindowLabel(window),
})) ?? []),
]}
variant="split"
menuWidth="anchor"
searchable
searchPlaceholder="Найти окно"
onChange={(value) => {
if (!videoOverlay) return;
setSelectedWindow(value);
if (value === "full-route") {
seekVideo(videoOverlay.timelineStartSeconds);
return;
}
const window = auditWindows?.find((item) => item.id === value)
?? videoOverlay.reviewWindows.find((item) => item.id === value);
if (window) seekVideo(window.startSeconds);
}}
/>
) : null}
</div>
) : (
<div className="l3-visual-audit__actions">
<div className="l3-visual-audit__pagination">
<IconButton label="Предыдущий sample E46C" onClick={() => navigate(-1)}>
<Icon name="chevron-left" size={16} />
</IconButton>
<IconButton label="Следующий sample E46C" onClick={() => navigate(1)}>
<Icon name="chevron-right" size={16} />
</IconButton>
</div>
<Select
label="Выбрать sample E46C"
value={String(selectedSequence)}
options={SEQUENCES.map((sequence) => ({
value: String(sequence),
label:
frame?.truthIslandSequence === sequence
? `${sequence}/32 · frame ${frame.frameIndex} · ${frame.groupId}`
: `${sequence}/32 · exact E46 sample`,
}))}
variant="split"
menuWidth="anchor"
searchable
searchPlaceholder="Найти sample"
onChange={(value) => setSelectedSequence(Number(value))}
/>
</div>
);
const overlay = mode === "video" && videoOverlay ? (
<div className="l3-visual-audit__overlay l3-visual-audit__overlay--video">
<div>
<span>RAVNOVES00 · recorded RIGHT video</span>
<strong>
+{Math.max(0, videoPlayback.currentSeconds - videoOverlay.timelineStartSeconds).toFixed(1)} с
{activeVideoFrame ? ` · frame ${activeVideoFrame.frameIndex}` : ""}
</strong>
<small>{videoPlayback.playing ? "воспроизведение" : "пауза / seek"}</small>
</div>
<div>
<span>E26 · temporal route tracks</span>
<strong>
{activeVideoFrame?.objects.length ?? 0} boxes
{activeVideoFrame?.objects.length
? ` · ${activeVideoFrame.objects.map((item) => `S${item.routeTrackId}`).join(", ")}`
: " · объектов нет"}
</strong>
<small>{activeVideoFrame?.fusionState ?? "ожидаем frame binding"}</small>
</div>
{activeAuditWindow ? (
<div>
<span>E46D · temporal exception</span>
<strong>{activeAuditWindow.title}</strong>
<small>{activeAuditWindow.detail}</small>
</div>
) : (
<div>
<span>Что проверяем</span>
<strong>удержание рамки · смена S-ID · потеря / повторный захват</strong>
<small>Цвет: dynamic / static / unknown · это diagnostic, не truth</small>
</div>
)}
</div>
) : frame ? (
<div className="l3-visual-audit__overlay">
<div>
<span>RAVNOVES00 · full recorded RIGHT</span>
<strong>frame {frame.frameIndex} · {frame.groupId}</strong>
<small>
Sample {frame.truthIslandSequence}/32 · {frame.sessionSeconds.toLocaleString("ru-RU", {
maximumFractionDigits: 1,
})} с
</small>
</div>
<div>
<span>E46C · route / world binding</span>
<strong>
{frame.matchedRouteObjectCount}/{frame.objectCount} route-bound · {frame.worldObjectCount} world objects
</strong>
<small>{frame.fusionState} · image {frame.sourceImageSha256.slice(0, 12)}</small>
</div>
</div>
) : undefined;
let content;
if (mode === "video") {
content = videoLoading ? (
<div className="l3-visual-audit__state" role="status">
<span className="busy-indicator" aria-hidden="true" />
<span>Связываем 4489 temporal frames с RIGHT-видео</span>
</div>
) : videoError || !videoOverlay || !videoSource ? (
<div className="l3-visual-audit__state" role="status">
<Icon name="alert" size={18} />
<span>{videoError ?? "E46C video replay недоступен."}</span>
</div>
) : (
<E46CRecordedVideoScene
source={videoSource}
overlay={videoOverlay}
playback={videoPlayback}
onPlaybackChange={setVideoPlayback}
/>
);
} else {
content = sampleLoading ? (
<div className="l3-visual-audit__state" role="status">
<span className="busy-indicator" aria-hidden="true" />
<span>Открываем E46C route/world sample</span>
</div>
) : sampleError || !frame ? (
<div className="l3-visual-audit__state" role="status">
<Icon name="alert" size={18} />
<span>{sampleError ?? "E46C frame недоступен."}</span>
</div>
) : (
<E46CFullReplayWorldTracksScene frame={frame} mode={mode} />
);
}
return (
<div className="l3-visual-audit">
<LaboratoryEvidenceViewer
label="E46C full replay temporal video, route and world evidence"
mode={mode}
modes={[
{ value: "video", label: "VIDEO" },
{ value: "camera", label: "CAMERA" },
{ value: "world", label: "WORLD" },
]}
expanded={expanded}
onModeChange={setMode}
onExpandedChange={setExpanded}
actions={actions}
overlay={overlay}
>
{content}
</LaboratoryEvidenceViewer>
</div>
);
}
@@ -0,0 +1,146 @@
import { useEffect, useMemo, useRef } from "react";
import {
RecordedFmp4Player,
type RecordedObservationPlayback,
} from "../../components/RecordedFmp4Player";
import {
selectE46CVideoFrame,
type E46CMotionState,
type E46CVideoOverlay,
} from "../../core/laboratory/e46cFullReplayWorldTracks";
import type { ObservationSourceDescriptor } from "../../core/runtime/contracts";
function color(
host: HTMLElement,
token: string,
fallback: readonly [number, number, number],
alpha = 1,
): string {
const channels = getComputedStyle(host)
.getPropertyValue(token)
.trim()
.match(/[\d.]+/g)
?.slice(0, 3)
.map(Number);
const [red, green, blue] = channels?.length === 3 ? channels : fallback;
return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
}
function stateColor(host: HTMLElement, state: E46CMotionState): string {
if (state === "dynamic") return color(host, "--nodedc-accent-rgb", [232, 56, 126]);
if (state === "static") return color(host, "--nodedc-success-rgb", [181, 255, 90]);
return color(host, "--nodedc-warning-rgb", [255, 197, 92]);
}
function stateLabel(state: E46CMotionState): string {
if (state === "dynamic") return "движется";
if (state === "static") return "стоит";
return "unknown";
}
export function E46CRecordedVideoScene({
source,
overlay,
playback,
onPlaybackChange,
}: {
source: ObservationSourceDescriptor;
overlay: E46CVideoOverlay;
playback: RecordedObservationPlayback;
onPlaybackChange: (playback: RecordedObservationPlayback) => void;
}) {
const hostRef = useRef<HTMLDivElement | null>(null);
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const frame = useMemo(
() => selectE46CVideoFrame(overlay.frames, playback.currentSeconds),
[overlay.frames, playback.currentSeconds],
);
useEffect(() => {
const host = hostRef.current;
const canvas = canvasRef.current;
if (!host || !canvas) return;
const context = canvas.getContext("2d");
if (!context) return;
const render = () => {
const width = Math.max(host.clientWidth, 1);
const height = Math.max(host.clientHeight, 1);
const pixelRatio = Math.min(window.devicePixelRatio, 1.5);
canvas.width = Math.round(width * pixelRatio);
canvas.height = Math.round(height * pixelRatio);
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
context.clearRect(0, 0, width, height);
if (!frame) return;
const scale = Math.min(
width / overlay.imageWidth,
height / overlay.imageHeight,
);
const drawWidth = overlay.imageWidth * scale;
const drawHeight = overlay.imageHeight * scale;
const offsetX = (width - drawWidth) / 2;
const offsetY = (height - drawHeight) / 2;
for (const item of frame.objects) {
const [left, top, right, bottom] = item.boxXyxy;
const x = offsetX + left * scale;
const y = offsetY + top * scale;
const boxWidth = (right - left) * scale;
const boxHeight = (bottom - top) * scale;
const stroke = stateColor(host, item.motionState);
context.strokeStyle = stroke;
context.lineWidth = Math.max(1.5, 2 * scale);
context.setLineDash(item.cameraEvidenceCurrent ? [] : [5, 4]);
context.strokeRect(x, y, boxWidth, boxHeight);
context.setLineDash([]);
const identity = `S${item.routeTrackId}${
item.worldTrackId === null ? "" : `→W${item.worldTrackId}`
}`;
const label = `${identity} · ${item.displayCategory} · ${stateLabel(
item.motionState,
)} · ${Math.round(item.score * 100)}%`;
const fontSize = Math.max(9, 10 * scale);
context.font = `650 ${fontSize}px Inter, system-ui, sans-serif`;
const labelWidth = context.measureText(label).width + 8;
const labelHeight = fontSize + 6;
const labelX = Math.min(
offsetX + drawWidth - labelWidth,
Math.max(offsetX, x),
);
const labelY = Math.max(offsetY, y - labelHeight);
context.fillStyle = color(host, "--nodedc-canvas-rgb", [5, 5, 6], 0.9);
context.fillRect(labelX, labelY, labelWidth, labelHeight);
context.fillStyle = stroke;
context.fillText(label, labelX + 4, labelY + fontSize + 1);
}
};
const observer = new ResizeObserver(render);
observer.observe(host);
render();
return () => observer.disconnect();
}, [frame, overlay.imageHeight, overlay.imageWidth]);
return (
<div className="e46c-video-scene" ref={hostRef}>
<RecordedFmp4Player
source={source}
playback={playback}
interactive
prepare
onPlaybackChange={onPlaybackChange}
/>
<canvas
ref={canvasRef}
role="img"
aria-label={
frame
? `E46C video frame ${frame.frameIndex}: ${frame.objects.length} route objects`
: "E46C recorded video overlay"
}
/>
</div>
);
}
@@ -0,0 +1,206 @@
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import type {
E46DReviewClip,
E46DTemporalFailureAuditResult,
E46DTemporalSignalKind,
} from "../../core/laboratory/e46dTemporalFailureAudit";
import { formatNumber } from "../../presentation";
import {
E46CFullReplayWorldTracksVisual,
type E46CTemporalAuditWindow,
} from "./E46CFullReplayWorldTracksVisual";
function evidenceNumber(clip: E46DReviewClip, key: string): number | null {
const value = clip.evidence[key];
return typeof value === "number" ? value : null;
}
function kindTitle(kind: E46DTemporalSignalKind): string {
if (kind === "layer-blackout") return "Объектный слой исчез";
if (kind === "camera-evidence-hold") return "Tracker держит объект без свежей рамки";
if (kind === "route-layer-gap") return "Route-track пропал и вернулся";
if (kind === "route-id-rebirth-candidate") return "Похожий объект сменил S-ID";
if (kind === "bbox-jump") return "Рамка скачком сменила положение";
if (kind === "motion-state-flap") return "Статус движения нестабилен";
if (kind === "world-binding-flap") return "Привязка к W-ID нестабильна";
return "Всплеск короткоживущих S-ID";
}
function clipTitle(clip: E46DReviewClip): string {
const tracks = clip.routeTrackIds.length
? ` · S${clip.routeTrackIds.slice(0, 4).join(", S")}${clip.routeTrackIds.length > 4 ? "…" : ""}`
: "";
if (clip.kind === "layer-blackout") {
const before = evidenceNumber(clip, "before_object_count") ?? 0;
const after = evidenceNumber(clip, "after_object_count") ?? 0;
return `${kindTitle(clip.kind)}: ${before} → 0 → ${after}`;
}
if (clip.kind === "route-id-rebirth-candidate" && clip.routeTrackIds.length >= 2) {
return `${kindTitle(clip.kind)}: S${clip.routeTrackIds[0]} → S${clip.routeTrackIds[1]}`;
}
return `${kindTitle(clip.kind)}${tracks}`;
}
function clipDetail(clip: E46DReviewClip): string {
const duration = Math.max(0, clip.eventEndSeconds - clip.eventStartSeconds);
if (clip.kind === "layer-blackout") {
return `${evidenceNumber(clip, "zero_frame_count") ?? 0} пустых кадров · ${duration.toFixed(1)} с · published layer, не truth`;
}
if (clip.kind === "route-layer-gap") {
return `${evidenceNumber(clip, "missing_frame_count") ?? 0} кадров без S-ID · тот же ID затем вернулся`;
}
if (clip.kind === "camera-evidence-hold") {
return `${evidenceNumber(clip, "held_frame_count") ?? 0} кадров без current detector evidence`;
}
if (clip.kind === "route-id-rebirth-candidate" || clip.kind === "bbox-jump") {
const overlap = evidenceNumber(clip, "bbox_iou");
return `frame ${clip.startFrame}${clip.endFrame}${overlap === null ? "" : ` · IoU ${overlap.toFixed(2)}`} · нужна визуальная проверка`;
}
if (clip.kind === "short-track-burst") {
return `${evidenceNumber(clip, "short_track_count") ?? clip.routeTrackIds.length} ID с ≤3 наблюдениями за короткое окно`;
}
return `${evidenceNumber(clip, "transition_count") ?? 0} переключения за ${Math.max(0, duration).toFixed(1)} с`;
}
function auditWindows(
clips: readonly E46DReviewClip[],
): readonly E46CTemporalAuditWindow[] {
return clips.map((clip) => ({
id: clip.clipId,
label: `#${String(clip.rank).padStart(2, "0")} · ${clip.eventStartSeconds.toFixed(1)} с · ${clipTitle(clip)}`,
startSeconds: clip.startSeconds,
endSeconds: clip.endSeconds,
title: clipTitle(clip),
detail: clipDetail(clip),
}));
}
export function E46DTemporalFailureAuditResultView({
rigLabel,
result,
}: {
rigLabel: string;
result: E46DTemporalFailureAuditResult;
}) {
const metrics = result.metrics;
const windows = auditWindows(result.reviewClips);
const zeroPercent = metrics.zeroObjectFrameFraction * 100;
const shortPercent = metrics.shortRouteTrackFraction * 100;
const heldPercent = metrics.cameraHeldObservationFraction * 100;
return (
<LaboratoryWorkTemplate
summary={(
<LaboratorySummary
title="LAB E46D · full replay temporal failure audit"
description="Полный recorded RIGHT replay автоматически просканирован на обвалы object-layer, разрывы и перерождение S-ID, скачки рамок, motion-flap и нестабильную W-привязку. Приоритетные эпизоды открываются как видео с точным временным контекстом."
status={`Temporal continuity failed · ${metrics.failureSignalCount} signals · ${metrics.reviewClipCount} clips`}
statusTone="danger"
facts={[
{
label: "Конфигурация",
value: `${rigLabel} · RIGHT camera · recorded replay · E26/E46C frozen`,
},
{
label: "Покрытие",
value: `${metrics.routeFrameCount}/4489 кадров · ${formatNumber(metrics.routeSpanSeconds, 1)} с · полный маршрут`,
},
{
label: "Continuity",
value: `${metrics.layerBlackoutEpisodeCount} обвалов слоя · ${metrics.routeLayerGapEpisodeCount} route-gap · ${metrics.routeIdRebirthCandidateCount} ID-candidates`,
},
{
label: "Визуал",
value: `${metrics.reviewClipCount} автоматически выбранных VIDEO-эпизодов · CAMERA/WORLD доступны в том же viewer`,
},
]}
brief={{
question: "Удерживает ли полный recorded perception replay рамки и идентичности во времени без скрытых провалов между удачными статичными кадрами?",
approach: "Все 4489 hash-bound E26 fusion-кадров проверены детерминированным temporal scanner. Он не пересчитывает модель: фиксирует только наблюдаемые разрывы опубликованного слоя и связывает их с исходным RIGHT-видео через ту же session-time шкалу.",
principalResult: `${metrics.zeroObjectFrameCount} кадров (${zeroPercent.toLocaleString("ru-RU", { maximumFractionDigits: 1 })}%) имеют пустой object-layer; найдено ${metrics.layerBlackoutEpisodeCount} обвалов между непустыми кадрами. ${metrics.shortRouteTrackCount} из ${metrics.routeTrackCount} S-ID (${shortPercent.toLocaleString("ru-RU", { maximumFractionDigits: 1 })}%) живут не дольше трёх наблюдений.`,
limitation: "Это честная диагностика опубликованного temporal layer, а не independent truth. Клип доказывает разрыв данных; семантический false positive или пропуск конкретного физического объекта подтверждается визуально в том же видео.",
}}
method={{
completeness: "complete",
executionClass: "deterministic",
pipelineId: "e46d-full-replay-temporal-failure-audit/v1",
components: [
{
kind: "source",
name: result.sourceE46CResultId,
version: "4489-frame full recorded RIGHT replay",
role: "video, route-track and world-binding substrate",
identitySha256: result.sourceE46CResultId.split("-").at(-1) ?? null,
},
{
kind: "source",
name: result.sourceE26ResultId,
version: "accepted diagnostic fusion",
role: "camera evidence, S-ID, W-ID and motion state",
identitySha256: result.sourceE26ResultId.split("-").at(-1) ?? null,
},
{
kind: "algorithm",
name: "Deterministic temporal exception scanner",
version: "e46d/v1",
role: "full-route detection, ranking and clip selection",
identitySha256: null,
},
],
}}
/>
)}
evidence={(
<LaboratoryEvidence
eyebrow="E46D VISUAL EVIDENCE · AUTOMATIC TEMPORAL EXCEPTIONS"
title="Приоритетные видеоэпизоды: до сбоя, сам разрыв и восстановление"
kind="diagnostic-model"
resizable
>
<E46CFullReplayWorldTracksVisual
resultId={result.sourceE46CResultId}
auditWindows={windows}
/>
</LaboratoryEvidence>
)}
result={(
<LaboratoryResultSummary
title="Полный temporal слой не прошёл continuity gate"
status="Regression confirmed in published layer · detector/tracker/world causes separated"
statusTone="danger"
metrics={[
{
label: "Empty layer",
value: `${metrics.zeroObjectFrameCount}/${metrics.routeFrameCount}`,
hint: `${zeroPercent.toLocaleString("ru-RU", { maximumFractionDigits: 1 })}% frames · ${metrics.layerBlackoutEpisodeCount} bracketed episodes`,
},
{
label: "Short S-ID",
value: `${metrics.shortRouteTrackCount}/${metrics.routeTrackCount}`,
hint: `≤3 observations · ${shortPercent.toLocaleString("ru-RU", { maximumFractionDigits: 1 })}%`,
},
{
label: "Held evidence",
value: formatNumber(metrics.cameraHeldObservationCount, 0),
hint: `${heldPercent.toLocaleString("ru-RU", { maximumFractionDigits: 1 })}% observations without fresh camera evidence`,
},
{
label: "Identity / world",
value: `${metrics.routeIdRebirthCandidateCount} / ${metrics.worldBindingFlapEpisodeCount}`,
hint: "S-ID rebirth candidates / W-binding flap episodes",
},
]}
conclusion={{
proved: `Полный маршрут учтён без пропуска входных строк, но опубликованный temporal layer нестабилен: ${metrics.layerBlackoutEpisodeCount} раз он полностью обнуляется между непустыми кадрами, ${metrics.routeLayerGapEpisodeCount} раз тот же S-ID исчезает и возвращается, а ${metrics.bboxJumpEpisodeCount} current/current перехода имеют резкий bbox-разрыв.`,
notProved: "Без независимой покадровой truth не доказано, что каждый короткий S-ID является false positive, а каждый overlap-кандидат — физический ID-switch. Не доказаны live/hardware, free-space, navigation или safety-пригодность.",
decision: "Не продвигать текущий E26/E46C temporal layer. Сначала устранить глобальные object-layer blackouts и route fragmentation, затем стабилизировать W-binding и motion-state и сравнить старый/новый pipeline на том же recorded RIGHT replay.",
}}
/>
)}
/>
);
}
@@ -0,0 +1,102 @@
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import type { E46EReadyStackResult } from "../../core/laboratory/e46eReadyStack";
import { formatNumber } from "../../presentation";
import { E46EReadyStackVisual } from "./E46EReadyStackVisual";
export function E46EReadyStackResultView({
rigLabel,
result,
}: {
rigLabel: string;
result: E46EReadyStackResult;
}) {
const metrics = result.metrics;
const zeroTrackPercent = (metrics.zeroTrackFrameCount / metrics.frameCount) * 100;
const shortPercent = metrics.shortTrackFraction * 100;
return (
<LaboratoryWorkTemplate
summary={(
<LaboratorySummary
title="LAB E46E · NVIDIA ready-stack bake-off R1"
description="Тот же полный recorded RIGHT проход пересчитан готовым NVIDIA-контуром: TrafficCamNet Transformer Lite (RT-DETR ResNet50) выдаёт детекции, штатный NvDCF — временные ID. Наш код только проверяет и публикует результат."
status={`Ready stack frozen · ${metrics.frameCount}/4489 · custom tracking 0`}
statusTone="accent"
facts={[
{
label: "Конфигурация",
value: `${rigLabel} · RIGHT camera · recorded replay · 800×600`,
},
{
label: "Модель",
value: "NVIDIA TrafficCamNet Transformer Lite · RT-DETR ResNet50 · FP16",
},
{
label: "Tracker",
value: "NVIDIA NvDCF performance profile · штатные ID · без ReID и hold/stitch Mission Core",
},
{
label: "Визуал",
value: `полное overlay-видео · ${formatNumber(result.video.byteLength / 1_048_576, 1)} MiB · hash-bound`,
},
]}
brief={{
question: "Даст ли готовый NVIDIA detector + tracker более честную и устойчивую основу компьютерного зрения, чем текущий YOLOX-контур с самописной временной склейкой?",
approach: "На Worker 006 один раз прогнан весь неизменный RIGHT-архив из 4489 кадров. DeepStream 9.1 работал без сети; RT-DETR и NvDCF не дообучались и не подстраивались под этот маршрут. Для каждого кадра опубликованы проверяемые detector/NvDCF-наблюдения, исходные LTRB-координаты и то же полноразмерное overlay-видео.",
principalResult: `${formatNumber(metrics.detectionObservationCount, 0)} detector-наблюдений превратились в ${formatNumber(metrics.trackObservationCount, 0)} NvDCF-наблюдений и ${formatNumber(metrics.uniqueTrackCount, 0)} route-local ID. Tracker пережил ${formatNumber(metrics.trackerRecoveredFrameCount, 0)} кадров без свежей детекции; зафиксировано ${metrics.fullLayerBlackoutEventCount} полных обвалов слоя.`,
limitation: "Набор классов ограничен car/person/bicycle/road_sign, а независимой покадровой truth для всего маршрута нет. Поэтому LAB доказывает воспроизводимый ready-stack и его временное поведение, но не заявляет precision/recall, motion-state, navigation или safety.",
}}
method={result.method}
/>
)}
evidence={(
<LaboratoryEvidence
eyebrow="E46E VISUAL EVIDENCE · STOCK NVIDIA FULL REPLAY"
title="Полный RIGHT-видос: рамки RT-DETR и устойчивость NvDCF ID во времени"
kind="diagnostic-model"
resizable
>
<E46EReadyStackVisual videoUrl={result.video.url} />
</LaboratoryEvidence>
)}
result={(
<LaboratoryResultSummary
title="Готовый NVIDIA-контур зафиксирован как новый сравнительный baseline"
status="Full replay available · objective continuity metrics · visual review in the same artifact"
statusTone="accent"
metrics={[
{
label: "Detector / NvDCF",
value: `${formatNumber(metrics.detectionObservationCount, 0)} / ${formatNumber(metrics.trackObservationCount, 0)}`,
hint: `mean ${formatNumber(metrics.meanTrackedObjectsPerFrame, 2)} tracked objects/frame`,
},
{
label: "Empty layer",
value: `${metrics.zeroTrackFrameCount}/${metrics.frameCount}`,
hint: `${formatNumber(zeroTrackPercent, 1)}% frames · ${metrics.fullLayerBlackoutEventCount} bracketed blackouts`,
},
{
label: "NvDCF recovery",
value: formatNumber(metrics.trackerRecoveredFrameCount, 0),
hint: "frames with track output and no current detector box",
},
{
label: "Short / gap",
value: `${metrics.shortTrackCount} / ${metrics.routeIdGapEventCount}`,
hint: `≤3 observations: ${formatNumber(shortPercent, 1)}% · ${metrics.trackBoxClippedCount} display-only box clips`,
},
]}
conclusion={{
proved: `Все ${metrics.frameCount} кадров одного immutable RIGHT-маршрута обработаны официальным NVIDIA stack; готовое видео и покадровые ID связаны одной identity. Mission Core не выполнял association, hold, stitch или detector postprocessing; ${metrics.trackBoxClippedCount} выходов рамок за границу только обрезаны для отображения с сохранением raw-координат.`,
notProved: "Без независимой full-route truth не доказаны абсолютные precision/recall и корректность каждого ID. Не реализованы классификация dynamic/static, LiDAR-range fusion, live hardware, free-space, commands, navigation или safety.",
decision: "Считать E46E готовым A/B baseline и оценивать его по полному видео и тем же continuity-метрикам против E46D. Дальнейшую работу вести через готовые модели/трекеры и адаптеры контрактов, а не через подгонку самописного tracker под один ролик.",
}}
/>
)}
/>
);
}
@@ -0,0 +1,37 @@
import { useState } from "react";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
export function E46EReadyStackVisual({
videoUrl,
label = "E46E NVIDIA ready-stack full recorded RIGHT overlay",
ariaLabel = "E46E full recorded RIGHT video with NVIDIA RT-DETR detections and NvDCF track IDs",
}: {
videoUrl: string;
label?: string;
ariaLabel?: string;
}) {
const [expanded, setExpanded] = useState(false);
return (
<div className="l3-visual-audit">
<LaboratoryEvidenceViewer
label={label}
mode="video"
modes={[{ value: "video", label: "VIDEO" }]}
expanded={expanded}
onModeChange={() => undefined}
onExpandedChange={setExpanded}
>
<div className="e46e-ready-stack-video">
<video
controls
playsInline
preload="metadata"
src={videoUrl}
aria-label={ariaLabel}
/>
</div>
</LaboratoryEvidenceViewer>
</div>
);
}
@@ -0,0 +1,114 @@
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import type { E46FDashCamBakeoffResult } from "../../core/laboratory/e46fDashCamBakeoff";
import { formatNumber } from "../../presentation";
import { E46EReadyStackVisual } from "./E46EReadyStackVisual";
export function E46FDashCamBakeoffResultView({
rigLabel,
result,
}: {
rigLabel: string;
result: E46FDashCamBakeoffResult;
}) {
const metrics = result.metrics;
const baseline = result.comparison.baselineMetrics;
const triage = result.comparison.largeBoxVisualTriage;
const candidateLargePercent = (triage.candidate.frameCount / metrics.frameCount) * 100;
const baselineLargePercent = (triage.baseline.frameCount / baseline.frameCount) * 100;
const shortPercent = metrics.shortTrackFraction * 100;
const baselineShortPercent = baseline.shortTrackFraction * 100;
const reviewSeconds = result.comparison.visualReview.sampleVideoSeconds
.map((value) => `${formatNumber(value, 1)} c`)
.join(" · ");
return (
<LaboratoryWorkTemplate
summary={(
<LaboratorySummary
title="LAB E46F · NVIDIA mobile-detector bake-off R2"
description="Контролируемая замена только детектора: тот же RIGHT-архив, DeepStream 9.1, FP16, NvDCF и 800×600. TrafficCamNet E46E заменён на готовый DashCamNet DetectNet_v2, обученный под движущуюся камеру."
status="Rejected · temporal progress, semantic regression on unrectified fisheye"
statusTone="warning"
facts={[
{
label: "Конфигурация",
value: `${rigLabel} · RIGHT recorded replay · detector-only A/B · 4489/4489`,
},
{
label: "Кандидат",
value: "NVIDIA DashCamNet · DetectNet_v2 ResNet18 · signed ONNX v1.0.4 · FP16",
},
{
label: "Контроль",
value: "NVIDIA NvDCF performance profile · без Mission Core NMS, association, hold или stitch",
},
{
label: "Предквалификация",
value: "v1.0.5 отклонён до LAB: на реальных кадрах ненулевым был только один confidence-канал",
},
]}
brief={{
question: "Станет ли готовый moving-camera DashCamNet лучшим detector drop-in для нашего сырого fisheye RIGHT-потока, если весь остальной NVIDIA-контур оставить неизменным?",
approach: "На Worker 006 весь проход пересчитан при одном контролируемом изменении — TrafficCamNet RT-DETR заменён на DashCamNet DetectNet_v2. Официальные NMS-параметры NVIDIA и точный model hash заморожены. Результат проверен не только числами: опубликован полный overlay-видос и отдельно просмотрены повторяющиеся крупные рамки на краях fisheye.",
principalResult: `Continuity немного улучшилась: пустые track-кадры ${metrics.zeroTrackFrameCount} против ${baseline.zeroTrackFrameCount}, blackout ${metrics.fullLayerBlackoutEventCount} против ${baseline.fullLayerBlackoutEventCount}, route-ID ${metrics.uniqueTrackCount} против ${baseline.uniqueTrackCount}. Но крупные рамки ≥20% кадра появились на ${triage.candidate.frameCount} кадрах против ${triage.baseline.frameCount} у E46E; визуально это повторяющиеся person-треки на чёрной кайме и краевой геометрии.`,
limitation: "Large-box triage — диагностический указатель для навигации по видео, а не precision/recall. Независимой full-route truth нет; LAB не оценивает motion-state, LiDAR range, free-space, live hardware, navigation или safety.",
}}
method={result.method}
/>
)}
evidence={(
<LaboratoryEvidence
eyebrow="E46F VISUAL EVIDENCE · DETECTOR-ONLY FULL-ROUTE A/B"
title="Полный RIGHT-видос: DashCamNet + NvDCF и семантическая регрессия на краях fisheye"
kind="diagnostic-model"
resizable
>
<E46EReadyStackVisual
videoUrl={result.video.url}
label="E46F DashCamNet detector-only full recorded RIGHT overlay"
ariaLabel="E46F full recorded RIGHT video with DashCamNet detections and NvDCF track IDs"
/>
</LaboratoryEvidence>
)}
result={(
<LaboratoryResultSummary
title="DashCamNet отклонён на сыром fisheye; следующий контракт — rectification перед detector"
status={`Visual reject · контрольные эпизоды: ${reviewSeconds}`}
statusTone="warning"
metrics={[
{
label: "Empty / blackout",
value: `${metrics.zeroTrackFrameCount} / ${metrics.fullLayerBlackoutEventCount}`,
hint: `E46E: ${baseline.zeroTrackFrameCount} / ${baseline.fullLayerBlackoutEventCount} · temporal progress`,
},
{
label: "Route ID",
value: `${metrics.uniqueTrackCount}`,
hint: `E46E: ${baseline.uniqueTrackCount} · short ${formatNumber(shortPercent, 1)}% vs ${formatNumber(baselineShortPercent, 1)}%`,
},
{
label: "Large-box frames",
value: `${triage.candidate.frameCount}/${metrics.frameCount}`,
hint: `${formatNumber(candidateLargePercent, 1)}% · E46E ${triage.baseline.frameCount} (${formatNumber(baselineLargePercent, 1)}%)`,
},
{
label: "Large-box class",
value: Object.keys(triage.candidate.classObservations).join(", "),
hint: `${triage.candidate.observationCount} observations · ${triage.candidate.trackIdCount} route IDs`,
},
]}
conclusion={{
proved: `Готовый DashCamNet действительно сокращает фрагментацию: ${metrics.uniqueTrackCount} ID вместо ${baseline.uniqueTrackCount}, ${formatNumber(shortPercent, 1)}% коротких треков вместо ${formatNumber(baselineShortPercent, 1)}%. Все ${metrics.frameCount} кадров и полное видео связаны immutable identity; E46F не содержит собственной detector/tracker-логики Mission Core.`,
notProved: `Числа continuity не означают корректное зрение. На ${triage.candidate.frameCount} кадрах видны очень крупные person-рамки, регулярно захватывающие чёрную кайму и край fisheye. Поэтому E46F не принят как perception candidate и не получает motion, free-space, navigation или safety authority.`,
decision: "Не чинить DashCamNet порогами под этот ролик. Сохранить E46E текущим готовым baseline. Следующий detector bake-off запускать только после одной calibration-derived rectification/valid-FOV projection, одинаковой для всех перспективных моделей.",
}}
/>
)}
/>
);
}
@@ -0,0 +1,108 @@
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import type { E46GRectifiedDetectorBakeoffResult } from "../../core/laboratory/e46gRectifiedDetectorBakeoff";
import { formatNumber } from "../../presentation";
import { E46GRectifiedDetectorBakeoffVisual } from "./E46GRectifiedDetectorBakeoffVisual";
export function E46GRectifiedDetectorBakeoffResultView({
rigLabel,
result,
}: {
rigLabel: string;
result: E46GRectifiedDetectorBakeoffResult;
}) {
const traffic = result.metrics.trafficcamnet;
const dash = result.metrics.dashcamnet;
const trafficFront = traffic.views.front;
const dashFront = dash.views.front;
const trafficSideLarge = traffic.views.left.largeTrackObservationCount
+ traffic.views.right.largeTrackObservationCount;
const dashSideLarge = dash.views.left.largeTrackObservationCount
+ dash.views.right.largeTrackObservationCount;
return (
<LaboratoryWorkTemplate
summary={(
<LaboratorySummary
title="LAB E46G · calibrated ready-detector bake-off R3"
description="Один физический RIGHT-поток recorded rig, factory KB4 и штатный NVIDIA nvdewarper. TrafficCamNet и DashCamNet пересчитаны на одних 600 кадрах и одних LEFT / FRONT / RIGHT проекциях с неизменным NvDCF."
status="Selected for next diagnostic · TrafficCamNet FRONT only"
statusTone="accent"
facts={[
{
label: "Конфигурация",
value: `${rigLabel} · одна RIGHT-запись · frames ${result.selection.firstSourceFrameIndex}${result.selection.lastSourceFrameIndex}`,
},
{
label: "Геометрия",
value: `Factory camera_1 KB4 · NVIDIA nvdewarper · 3 × ${result.rectification.outputResolution[0]}×${result.rectification.outputResolution[1]} · FOV ${result.rectification.horizontalFovDegrees}°`,
},
{
label: "Кандидаты",
value: "TrafficCamNet Transformer Lite RT-DETR vs DashCamNet DetectNet_v2 · FP16",
},
{
label: "Temporal",
value: "Stock NVIDIA NvDCF performance profile · identity остаётся view-local",
},
]}
brief={{
question: "Какой готовый NVIDIA-детектор лучше переносится на калиброванную перспективную проекцию нашего единственного RIGHT-потока и годится для следующего полного recorded replay?",
approach: "На Worker 006 factory KB4 подан в официальный Gst-nvdewarper. Оба детектора получили одни и те же 600 последовательных кадров, три синхронные проекции и один stock NvDCF. Решение принято по двум полным 60-секундным видео, а counts использованы только как навигация по визуалу.",
principalResult: `FRONT пригоден: TrafficCamNet удерживает видимые машины и людей заметно полнее — ${trafficFront.trackObservationCount} track-observations и ${trafficFront.zeroTrackFrameCount} пустых track-кадров против ${dashFront.trackObservationCount} и ${dashFront.zeroTrackFrameCount} у DashCamNet. LEFT/RIGHT непригодны: в них доминирует корпус крепления, на котором TrafficCamNet создаёт крупные ложные треки.`,
limitation: "Независимой исчерпывающей truth нет. У TrafficCamNet остаются дубли и stale boxes вокруг отдельных машин; E46G выбирает только следующий диагностический full-route кандидат и не принимает perception, motion-state, free-space, navigation или safety.",
}}
method={result.method}
/>
)}
evidence={(
<LaboratoryEvidence
eyebrow="E46G VISUAL EVIDENCE · CALIBRATED DETECTOR A/B"
title="Два синхронных 60-секундных видео · колонки LEFT / FRONT / RIGHT"
kind="diagnostic-model"
resizable
>
<E46GRectifiedDetectorBakeoffVisual videos={result.videos} />
</LaboratoryEvidence>
)}
result={(
<LaboratoryResultSummary
title="TrafficCamNet выбран для полного FRONT replay; боковые проекции исключены"
status="Diagnostic selection · no perception authority"
statusTone="accent"
metrics={[
{
label: "Traffic FRONT",
value: `${trafficFront.trackObservationCount} tracks`,
hint: `${trafficFront.zeroTrackFrameCount}/600 empty · ${trafficFront.uniqueTrackCount} view-local ID`,
},
{
label: "Dash FRONT",
value: `${dashFront.trackObservationCount} tracks`,
hint: `${dashFront.zeroTrackFrameCount}/600 empty · ${dashFront.uniqueTrackCount} view-local ID`,
},
{
label: "FRONT large-box",
value: `${formatNumber(trafficFront.largeTrackFraction * 100, 2)}% / ${formatNumber(dashFront.largeTrackFraction * 100, 2)}%`,
hint: "TrafficCamNet / DashCamNet · диагностический порог ≥20% plane",
},
{
label: "LEFT+RIGHT large",
value: `${trafficSideLarge} / ${dashSideLarge}`,
hint: "TrafficCamNet / DashCamNet · визуально связано с корпусом крепления",
},
]}
conclusion={{
proved: "Factory KB4 и штатный NVIDIA nvdewarper дают читаемую FRONT-перспективу из одной физической RIGHT-камеры. На одинаковом gate TrafficCamNet визуально сохраняет значительно больше реальных транспортных объектов и людей, тогда как DashCamNet часто оставляет видимые объекты без рамок.",
notProved: "Counts не являются accuracy. TrafficCamNet ещё дублирует отдельные машины, а LEFT/RIGHT содержат сильную self-occlusion корпуса. E46G не доказывает cross-view identity, динамику/статику, LiDAR range, free-space или работу на другом маршруте.",
decision: "Не тюнить модель под ролик и не использовать боковые проекции. E46H считать на полном retained route только для FRONT: готовый TrafficCamNet + stock NvDCF, затем проверить непрерывность всего видео и отдельно разобрать дубли/stale tracks.",
}}
/>
)}
/>
);
}
@@ -0,0 +1,45 @@
import { useState } from "react";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
import type {
E46GCandidate,
E46GVideo,
} from "../../core/laboratory/e46gRectifiedDetectorBakeoff";
const MODES = [
{ value: "trafficcamnet", label: "TRAFFICCAMNET" },
{ value: "dashcamnet", label: "DASHCAMNET" },
] as const;
export function E46GRectifiedDetectorBakeoffVisual({
videos,
}: {
videos: Readonly<Record<E46GCandidate, E46GVideo>>;
}) {
const [candidate, setCandidate] = useState<E46GCandidate>("trafficcamnet");
const [expanded, setExpanded] = useState(false);
const selected = videos[candidate];
return (
<div className="l3-visual-audit">
<LaboratoryEvidenceViewer
label="E46G synchronized calibrated detector comparison"
mode={candidate}
modes={MODES}
expanded={expanded}
onModeChange={setCandidate}
onExpandedChange={setExpanded}
>
<div className="e46e-ready-stack-video">
<video
key={selected.url}
controls
playsInline
preload="metadata"
src={selected.url}
aria-label={`E46G ${candidate} synchronized left front right comparison video`}
/>
</div>
</LaboratoryEvidenceViewer>
</div>
);
}
@@ -0,0 +1,106 @@
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import type {
E46HFullRectifiedFrontReplayResult,
} from "../../core/laboratory/e46hFullRectifiedFrontReplay";
import { formatNumber } from "../../presentation";
import { E46HFullRectifiedFrontReplayVisual } from "./E46HFullRectifiedFrontReplayVisual";
export function E46HFullRectifiedFrontReplayResultView({
rigLabel,
result,
}: {
rigLabel: string;
result: E46HFullRectifiedFrontReplayResult;
}) {
const metrics = result.metrics;
const falseSemanticWindows = result.visualReview.reviewWindows.filter(
({ verdict }) => verdict === "semantic-false-positive",
);
return (
<LaboratoryWorkTemplate
summary={(
<LaboratorySummary
title="LAB E46H · full calibrated FRONT replay R1"
description="Полный retained prefix единственной RIGHT-записи: factory KB4 → штатный NVIDIA nvdewarper FRONT → готовый TrafficCamNet → stock NvDCF. Без собственного детектора, трекера и route-specific постобработки."
status="Diagnostic regression · large semantic false tracks"
statusTone="warning"
facts={[
{
label: "Конфигурация",
value: `${rigLabel} · одна RIGHT-запись · frames 0…4487 из 4489`,
},
{
label: "Геометрия",
value: `FRONT ${result.rectification.outputResolution[0]}×${result.rectification.outputResolution[1]} · FOV ${result.rectification.horizontalFovDegrees}° · ${result.rectification.providerVersion}`,
},
{
label: "Provider stack",
value: "TrafficCamNet Transformer Lite RT-DETR + NVIDIA NvDCF performance profile",
},
{
label: "Длительность",
value: `448,8 с seekable overlay · ${metrics.frameCount} последовательных кадров`,
},
]}
brief={{
question: "Сохраняет ли выбранный готовый NVIDIA FRONT-stack полезные объекты на всём recorded route и можно ли после короткого bake-off продвигать его в perception?",
approach: "На Worker 006 полностью пересчитан неизменяемый retained prefix. В интерфейс опубликован весь seekable overlay, а числовые исключения связаны с конкретными окнами видео: пять крупных ложных semantic tracks и два zero-track интервала.",
principalResult: `FRONT читаем и большую часть маршрута держит видимые машины и людей. Но обнаружено ${falseSemanticWindows.length} крупных ложных car-треков на стене, кусте, дороге и террасе. Оба zero-track события в конце показывают реально пустую сцену — это не отказ object layer.`,
limitation: "Независимой полной truth нет, поэтому counts не являются precision/recall. Класс, route-local ID, physical identity, LiDAR range и dynamic/static — разные слои доказательств; E46H принимает только диагностический recorded replay.",
}}
method={result.method}
/>
)}
evidence={(
<LaboratoryEvidence
eyebrow="E46H VISUAL EVIDENCE · FULL RETAINED FRONT ROUTE"
title="Полное 448,8-секундное видео + навигация по проверенным исключениям"
kind="diagnostic-model"
resizable
>
<E46HFullRectifiedFrontReplayVisual result={result} />
</LaboratoryEvidence>
)}
result={(
<LaboratoryResultSummary
title="FRONT continuity подтверждена; текущий ready-provider не принят из-за semantic false tracks"
status="Blocked for promotion · no perception authority"
statusTone="warning"
metrics={[
{
label: "Tracked observations",
value: metrics.trackObservationCount.toLocaleString("ru-RU"),
hint: `${formatNumber(metrics.meanTrackedObjectsPerFrame, 2)} на кадр · ${metrics.uniqueTrackCount} route-local ID`,
},
{
label: "Пустые track-кадры",
value: `${metrics.zeroTrackFrameCount}/${metrics.frameCount}`,
hint: "два интервала · визуально реальная пустая сцена",
},
{
label: "Short tracks",
value: `${formatNumber(metrics.shortTrackFraction * 100, 2)}%`,
hint: `${metrics.shortTrackCount} из ${metrics.uniqueTrackCount} ID`,
},
{
label: "Large-box observations",
value: metrics.largeTrackObservationCount.toLocaleString("ru-RU"),
hint: `${formatNumber(metrics.largeTrackFraction * 100, 3)}% · пять ложных semantic tracks`,
},
]}
conclusion={{
proved: "Один физический RIGHT-поток можно штатно преобразовать в читаемый FRONT и полностью воспроизвести готовым NVIDIA detector/tracker stack. Object layer не исчезает в двух tail-интервалах: там действительно нет объектов.",
notProved: "Stack не доказал достаточную семантическую точность: длительные крупные car-треки закрепляются за фоном и местами перекрывают значительную часть кадра. E46H не принимает perception, motion-state, free-space, navigation или safety.",
decision: "Не подгонять TrafficCamNet под этот маршрут. Сохранить калиброванный FRONT adapter и full-route replay как неизменный benchmark; дальше сравнить на нём другой готовый provider или provider-level semantic suppression, затем снова проверить полное видео.",
}}
/>
)}
/>
);
}
@@ -0,0 +1,103 @@
import { useEffect, useRef, useState } from "react";
import { Icon, IconButton, Select } from "@nodedc/ui-react";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
import type {
E46HFullRectifiedFrontReplayResult,
} from "../../core/laboratory/e46hFullRectifiedFrontReplay";
const MODES = [{ value: "video", label: "VIDEO" }] as const;
export function E46HFullRectifiedFrontReplayVisual({
result,
}: {
result: E46HFullRectifiedFrontReplayResult;
}) {
const videoRef = useRef<HTMLVideoElement | null>(null);
const [selectedWindowId, setSelectedWindowId] = useState("full-route");
const [expanded, setExpanded] = useState(false);
const windows = result.visualReview.reviewWindows;
const selectedWindow = windows.find(({ id }) => id === selectedWindowId) ?? null;
useEffect(() => {
const video = videoRef.current;
if (!video || !selectedWindow) return;
video.pause();
video.currentTime = selectedWindow.startSeconds;
}, [selectedWindow]);
const navigate = (offset: -1 | 1) => {
const currentIndex = windows.findIndex(({ id }) => id === selectedWindowId);
const nextIndex = currentIndex < 0
? (offset > 0 ? 0 : windows.length - 1)
: (currentIndex + offset + windows.length) % windows.length;
setSelectedWindowId(windows[nextIndex]?.id ?? "full-route");
};
const actions = (
<div className="l3-visual-audit__actions">
<div className="l3-visual-audit__pagination">
<IconButton label="Предыдущее окно E46H" onClick={() => navigate(-1)}>
<Icon name="chevron-left" size={16} />
</IconButton>
<IconButton label="Следующее окно E46H" onClick={() => navigate(1)}>
<Icon name="chevron-right" size={16} />
</IconButton>
</div>
<Select
label="Выбрать окно полного видео E46H"
value={selectedWindowId}
options={[
{ value: "full-route", label: "Весь проход · 0.0448.8 с" },
...windows.map((window) => ({
value: window.id,
label: window.label,
})),
]}
variant="split"
menuWidth="anchor"
onChange={setSelectedWindowId}
/>
</div>
);
const overlay = selectedWindow ? (
<div className="l3-visual-audit__overlay">
<div>
<span>E46H · визуальная проверка</span>
<strong>{selectedWindow.label}</strong>
<small>
{selectedWindow.verdict === "empty-scene-expected"
? "Пустая сцена подтверждена визуально"
: `Ложный крупный semantic track ${selectedWindow.sourceTrackId ?? "—"}`}
</small>
</div>
</div>
) : null;
return (
<div className="l3-visual-audit">
<LaboratoryEvidenceViewer
label="E46H full FRONT tracked replay"
mode="video"
modes={MODES}
expanded={expanded}
onModeChange={() => undefined}
onExpandedChange={setExpanded}
actions={actions}
overlay={overlay}
>
<div className="e46e-ready-stack-video">
<video
ref={videoRef}
controls
playsInline
preload="metadata"
src={result.video.url}
aria-label="E46H full FRONT tracked replay video"
/>
</div>
</LaboratoryEvidenceViewer>
</div>
);
}
@@ -0,0 +1,101 @@
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import type { E46IGroundingDinoFullReplayResult } from "../../core/laboratory/e46iGroundingDinoFullReplay";
import { formatNumber } from "../../presentation";
import { E46IGroundingDinoFullReplayVisual } from "./E46IGroundingDinoFullReplayVisual";
export function E46IGroundingDinoFullReplayResultView({
rigLabel,
result,
}: {
rigLabel: string;
result: E46IGroundingDinoFullReplayResult;
}) {
const metrics = result.metrics;
return (
<LaboratoryWorkTemplate
summary={(
<LaboratorySummary
title="LAB E46I · Grounding DINO full FRONT shadow R1"
description="Тот же неизменяемый RIGHT → KB4 FRONT источник E46H, но другой готовый NVIDIA provider: коммерческий Grounding DINO Swin-Tiny через TAO Deploy/TensorRT FP16. Без route-specific тюнинга и собственного детекторного postprocessing."
status="Semantic progress · temporal layer pending"
statusTone="success"
facts={[
{
label: "Конфигурация",
value: `${rigLabel} · одна RIGHT-запись · FRONT 960×544 · ${result.source.frameCount} кадров`,
},
{
label: "Provider",
value: `${result.provider.name} ${result.provider.version} · ${result.provider.precision}`,
},
{
label: "Фиксированный контракт",
value: `${result.inference.captions.join(" · ")} · threshold ${result.inference.confidenceThreshold}`,
},
{
label: "Recorded throughput",
value: `${formatNumber(metrics.workerMeanFramesPerSecond, 2)} fps на RTX 4090 · ${formatNumber(metrics.workerElapsedSeconds, 0)} с`,
},
]}
brief={{
question: "Убирает ли готовый open-vocabulary NVIDIA detector крупные фоновые car-галлюцинации E46H, не теряя полезные машины и людей на полном recorded route?",
approach: "Сначала выполнен фиксированный 11-кадровый shadow gate, затем без изменения threshold пересчитаны все 4488 FRONT-кадров. В LAB опубликованы полный seekable overlay, обзор всего маршрута и отдельная сетка старых failure windows.",
principalResult: `Все ${result.shadowGate.suppressedCases}/${result.shadowGate.legacyCases} крупных фоновых случаев E46H подавлены. Все ${result.shadowGate.positiveCasesWithRelevantDetection}/${result.shadowGate.positiveCases} позитивных anchor-кадров сохранили релевантный объект. Это материальная семантическая прогрессия, а не финальное принятие perception.`,
limitation: "Выход пока покадровый: постоянных ID, dynamic/static и LiDAR range здесь нет. Caption ontology ограничена четырьмя классами; коляска названа bicycle, один частично обрезанный person пропущен. Tokenizer во время этого запуска скачивался из сети.",
}}
method={result.method}
/>
)}
evidence={(
<LaboratoryEvidence
eyebrow="E46I VISUAL EVIDENCE · FULL ROUTE + LEGACY FAILURE WINDOWS"
title="Полное 448,8-секундное видео, route overview и shadow-gate"
kind="diagnostic-model"
resizable
>
<E46IGroundingDinoFullReplayVisual result={result} />
</LaboratoryEvidence>
)}
result={(
<LaboratoryResultSummary
title="Семантическая регрессия E46H снята; следующий блокер — temporal identity"
status="Progress confirmed · not perception authority"
statusTone="success"
metrics={[
{
label: "Legacy false backgrounds",
value: `${result.shadowGate.suppressedCases}/${result.shadowGate.legacyCases} suppressed`,
hint: "стена · куст · земля · дорога · терраса",
},
{
label: "Detection observations",
value: metrics.detectionObservationCount.toLocaleString("ru-RU"),
hint: `${formatNumber(metrics.meanDetectionsPerFrame, 2)} на кадр · max ${metrics.maxDetectionsPerFrame}`,
},
{
label: "Zero-detection frames",
value: `${metrics.zeroDetectionFrameCount}/${metrics.frameCount}`,
hint: `longest run ${metrics.longestZeroDetectionRunFrames} кадров`,
},
{
label: "Large boxes ≥25%",
value: metrics.largeBoxObservationCount.toLocaleString("ru-RU"),
hint: `${formatNumber(metrics.largeBoxObservationFraction * 100, 3)}% · визуально близкие реальные машины`,
},
]}
conclusion={{
proved: "Готовый NVIDIA Grounding DINO на том же FRONT adapter принципиально чище TrafficCamNet на этом recorded route: пять известных крупных фоновых false-car случаев исчезли, а полезные объекты остались видимыми.",
notProved: "Покадровый detector ещё не доказывает стабильную identity, удержание рамки, dynamic/static, физическую природу неизвестного объекта, LiDAR range, free-space или безопасность. Полной независимой truth по маршруту нет.",
decision: "Зафиксировать Grounding DINO как прошедший semantic provider shadow, не подгонять его под ролик. Следом сделать offline-seal tokenizer и подключить готовый temporal tracker; после этого публиковать второе полное видео уже с постоянными ID и motion-state.",
}}
/>
)}
/>
);
}
@@ -0,0 +1,126 @@
import { useEffect, useRef, useState } from "react";
import { Icon, IconButton, Select } from "@nodedc/ui-react";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
import type { E46IGroundingDinoFullReplayResult } from "../../core/laboratory/e46iGroundingDinoFullReplay";
const MODES = [
{ value: "video", label: "VIDEO" },
{ value: "targeted", label: "FAILURES" },
{ value: "full-route", label: "ROUTE" },
{ value: "shadow-gate", label: "GATE" },
] as const;
type Mode = (typeof MODES)[number]["value"];
export function E46IGroundingDinoFullReplayVisual({
result,
}: {
result: E46IGroundingDinoFullReplayResult;
}) {
const videoRef = useRef<HTMLVideoElement | null>(null);
const [mode, setMode] = useState<Mode>("video");
const [selectedWindowId, setSelectedWindowId] = useState("full-route");
const [expanded, setExpanded] = useState(false);
const windows = result.visualReview.reviewWindows;
const selectedWindow = windows.find(({ id }) => id === selectedWindowId) ?? null;
useEffect(() => {
const video = videoRef.current;
if (!video || !selectedWindow) return;
video.pause();
video.currentTime = selectedWindow.startSeconds;
}, [selectedWindow]);
const navigate = (offset: -1 | 1) => {
const currentIndex = windows.findIndex(({ id }) => id === selectedWindowId);
const nextIndex = currentIndex < 0
? (offset > 0 ? 0 : windows.length - 1)
: (currentIndex + offset + windows.length) % windows.length;
setSelectedWindowId(windows[nextIndex]?.id ?? "full-route");
setMode("video");
};
const actions = mode === "video" ? (
<div className="l3-visual-audit__actions">
<div className="l3-visual-audit__pagination">
<IconButton label="Предыдущее окно E46I" onClick={() => navigate(-1)}>
<Icon name="chevron-left" size={16} />
</IconButton>
<IconButton label="Следующее окно E46I" onClick={() => navigate(1)}>
<Icon name="chevron-right" size={16} />
</IconButton>
</div>
<Select
label="Выбрать окно полного видео E46I"
value={selectedWindowId}
options={[
{ value: "full-route", label: "Весь проход · 0.0448.8 с" },
...windows.map((window) => ({ value: window.id, label: window.label })),
]}
variant="split"
menuWidth="anchor"
onChange={setSelectedWindowId}
/>
</div>
) : null;
const overlay = mode === "video" && selectedWindow ? (
<div className="l3-visual-audit__overlay">
<div>
<span>E46I · provider comparison</span>
<strong>{selectedWindow.label}</strong>
<small>
{selectedWindow.verdict === "empty-scene-mostly-preserved"
? "Пустая сцена в основном сохранена"
: "Крупная фоновая car-галлюцинация E46H подавлена"}
</small>
</div>
</div>
) : null;
const image = mode === "targeted"
? result.visuals.targetedWindows
: mode === "full-route"
? result.visuals.fullRoute
: result.visuals.shadowGate;
return (
<div className="l3-visual-audit">
<LaboratoryEvidenceViewer
label="E46I Grounding DINO full FRONT replay"
mode={mode}
modes={MODES}
expanded={expanded}
onModeChange={(value) => setMode(value as Mode)}
onExpandedChange={setExpanded}
actions={actions}
overlay={overlay}
>
<div className="e46e-ready-stack-video">
{mode === "video" ? (
<video
ref={videoRef}
controls
playsInline
preload="metadata"
src={result.video.url}
aria-label="E46I Grounding DINO full FRONT replay video"
/>
) : (
<img
src={image.url}
alt={
mode === "targeted"
? "E46I targeted legacy failure windows"
: mode === "full-route"
? "E46I full route ten-second contact sheet"
: "E46I eleven-frame semantic shadow gate"
}
/>
)}
</div>
</LaboratoryEvidenceViewer>
</div>
);
}
@@ -0,0 +1,101 @@
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import type { E46JRawFisheyeRealtimeResult } from "../../core/laboratory/e46jRawFisheyeRealtime";
import { formatNumber } from "../../presentation";
import { E46JRawFisheyeRealtimeVisual } from "./E46JRawFisheyeRealtimeVisual";
export function E46JRawFisheyeRealtimeResultView({
rigLabel,
result,
}: {
rigLabel: string;
result: E46JRawFisheyeRealtimeResult;
}) {
const metrics = result.metrics;
return (
<LaboratoryWorkTemplate
summary={(
<LaboratorySummary
title="LAB E46J · full raw fisheye realtime gate"
description="Одна физическая RIGHT-камера, один полный сырой KB4-кадр и один YOLOX-S inference. Без кропов, dewarp, виртуальных камер, тайлов, route-specific фильтров и собственного detector logic."
status="Realtime capacity passed · temporal layer pending"
statusTone="success"
facts={[
{
label: "Конфигурация",
value: `${rigLabel} · RIGHT raw KB4 800×600 · ${result.source.frameCount} кадров`,
},
{
label: "Provider",
value: `${result.detector.architecture} · ${result.detector.license} · ${result.detector.runtime}`,
},
{
label: "Фиксированный контракт",
value: `1 кадр → 1 infer · score ${result.detection.minimumScore} · NMS ${result.detection.nmsIouThreshold}`,
},
{
label: "Realtime capacity",
value: `${formatNumber(metrics.coreCapacityFps, 2)} fps · core p95 ${formatNumber(metrics.corePathP95Ms, 2)} мс`,
},
]}
brief={{
question: "Может ли готовый YOLOX-S работать одним проходом на полном сыром fisheye и сохранить исходный FOV при целевых 10 FPS?",
approach: "Все 4489 записанных RIGHT-кадров прошли через один co-located Triton inference. Видео опубликовано целиком; отдельно проверены маршрутная сетка, прежние failure windows и окно с тенью оператора.",
principalResult: `4489/4489 кадров, 0 ошибок, ${formatNumber(metrics.coreCapacityFps, 2)} fps вычислительной ёмкости. Полный fisheye сохранён; пять прежних крупных фоновых car-срабатываний в целевых окнах не наблюдаются.`,
limitation: `Это покадровый detector без ID и motion-state. В окне тени оператора person-box присутствует на ${metrics.operatorShadowPersonFrameCount}/${metrics.operatorShadowWindowFrameCount} кадров. Независимой полной truth нет.`,
}}
method={result.method}
/>
)}
evidence={(
<LaboratoryEvidence
eyebrow="E46J VISUAL EVIDENCE · FULL RAW FISHEYE + TARGETED WINDOWS"
title="Полное 448,723-секундное видео без обрезки FOV"
kind="diagnostic-model"
resizable
>
<E46JRawFisheyeRealtimeVisual result={result} />
</LaboratoryEvidence>
)}
result={(
<LaboratoryResultSummary
title="Полный fisheye проходит realtime-gate; следующий блок — temporal identity"
status="Compute accepted · detector not promoted"
statusTone="success"
metrics={[
{
label: "Core capacity",
value: `${formatNumber(metrics.coreCapacityFps, 2)} fps`,
hint: `mean ${formatNumber(metrics.corePathMeanMs, 2)} мс · p95 ${formatNumber(metrics.corePathP95Ms, 2)} мс`,
},
{
label: "Inference request",
value: `${formatNumber(metrics.inferenceRequestP95Ms, 2)} мс p95`,
hint: `mean ${formatNumber(metrics.inferenceRequestMeanMs, 2)} мс · local GPU path`,
},
{
label: "Detection observations",
value: metrics.detectionObservationCount.toLocaleString("ru-RU"),
hint: `${formatNumber(metrics.meanDetectionsPerFrame, 2)} на кадр · max ${metrics.maxDetectionsPerFrame}`,
},
{
label: "Known shadow FP",
value: `${metrics.operatorShadowPersonFrameCount}/${metrics.operatorShadowWindowFrameCount} кадров`,
hint: "419.4426.9 с · операторская тень → person",
},
]}
conclusion={{
proved: "Готовый YOLOX-S способен работать одним проходом по полному raw KB4 fisheye с запасом относительно 10 FPS. Обрезать исходный FOV или считать несколько виртуальных камер для detector-stage не требуется.",
notProved: "Покадровые рамки ещё не доказывают устойчивые ID, удержание объекта, drop/recovery, dynamic/static, LiDAR range или безопасность. Полной независимой разметки маршрута нет; известен false person на тени оператора.",
decision: result.decision.nextAction,
}}
/>
)}
/>
);
}
@@ -0,0 +1,126 @@
import { useEffect, useRef, useState } from "react";
import { Icon, IconButton, Select } from "@nodedc/ui-react";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
import type { E46JRawFisheyeRealtimeResult } from "../../core/laboratory/e46jRawFisheyeRealtime";
const MODES = [
{ value: "video", label: "VIDEO" },
{ value: "targeted", label: "FAILURES" },
{ value: "full-route", label: "ROUTE" },
{ value: "operator-shadow", label: "SHADOW" },
] as const;
type Mode = (typeof MODES)[number]["value"];
export function E46JRawFisheyeRealtimeVisual({
result,
}: {
result: E46JRawFisheyeRealtimeResult;
}) {
const videoRef = useRef<HTMLVideoElement | null>(null);
const [mode, setMode] = useState<Mode>("video");
const [selectedWindowId, setSelectedWindowId] = useState("full-route");
const [expanded, setExpanded] = useState(false);
const windows = result.visualReview.reviewWindows;
const selectedWindow = windows.find(({ id }) => id === selectedWindowId) ?? null;
useEffect(() => {
const video = videoRef.current;
if (mode !== "video" || !video || !selectedWindow) return;
video.pause();
video.currentTime = selectedWindow.startSeconds;
}, [mode, selectedWindow]);
const navigate = (offset: -1 | 1) => {
const currentIndex = windows.findIndex(({ id }) => id === selectedWindowId);
const nextIndex = currentIndex < 0
? (offset > 0 ? 0 : windows.length - 1)
: (currentIndex + offset + windows.length) % windows.length;
setSelectedWindowId(windows[nextIndex]?.id ?? "full-route");
setMode("video");
};
const actions = mode === "video" ? (
<div className="l3-visual-audit__actions">
<div className="l3-visual-audit__pagination">
<IconButton label="Предыдущее окно E46J" onClick={() => navigate(-1)}>
<Icon name="chevron-left" size={16} />
</IconButton>
<IconButton label="Следующее окно E46J" onClick={() => navigate(1)}>
<Icon name="chevron-right" size={16} />
</IconButton>
</div>
<Select
label="Выбрать окно полного видео E46J"
value={selectedWindowId}
options={[
{ value: "full-route", label: "Весь проход · 0.0448.723 с" },
...windows.map((window) => ({ value: window.id, label: window.label })),
]}
variant="split"
menuWidth="anchor"
onChange={setSelectedWindowId}
/>
</div>
) : null;
const overlay = mode === "video" && selectedWindow ? (
<div className="l3-visual-audit__overlay">
<div>
<span>E46J · raw fisheye one-pass</span>
<strong>{selectedWindow.label}</strong>
<small>
{selectedWindow.verdict === "operator-shadow-person-false-positive-observed"
? "Известное исключение · тень оператора → person"
: "Прежняя крупная фоновая car-ошибка не наблюдается"}
</small>
</div>
</div>
) : null;
const image = mode === "targeted"
? result.visuals.targetedWindows
: mode === "full-route"
? result.visuals.fullRoute
: result.visuals.operatorShadow;
return (
<div className="l3-visual-audit">
<LaboratoryEvidenceViewer
label="E46J full raw fisheye YOLOX-S realtime gate"
mode={mode}
modes={MODES}
expanded={expanded}
onModeChange={(value) => setMode(value as Mode)}
onExpandedChange={setExpanded}
actions={actions}
overlay={overlay}
>
<div className="e46e-ready-stack-video">
{mode === "video" ? (
<video
ref={videoRef}
controls
playsInline
preload="metadata"
src={result.video.url}
aria-label="E46J full raw fisheye YOLOX-S video"
/>
) : (
<img
src={image.url}
alt={
mode === "targeted"
? "E46J targeted legacy failure windows"
: mode === "full-route"
? "E46J full raw fisheye route contact sheet"
: "E46J operator shadow false-person exception"
}
/>
)}
</div>
</LaboratoryEvidenceViewer>
</div>
);
}
@@ -0,0 +1,77 @@
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import type { L32PointPillarsCameraReviewResult } from "../../core/laboratory/l32PointPillarsCameraReview";
import { L32PointPillarsCameraReviewVisual } from "./L32PointPillarsCameraReviewVisual";
function percent(value: number): string {
return `${(value * 100).toLocaleString("ru-RU", { maximumFractionDigits: 1 })}%`;
}
export function L32PointPillarsCameraReviewResult({
result,
}: {
result: L32PointPillarsCameraReviewResult;
}) {
const metrics = result.metrics;
return (
<LaboratoryWorkTemplate
summary={<LaboratorySummary
title="L3.2 · Камера + LiDAR · PointPillars на RAVNOVES00"
description="Семантическая проверка запечатанного PointPillars-прогона: точный кадр правой камеры, калиброванные точки LiDAR и те же модельные 3D-гипотезы в одном viewer."
status="Проверено · текущий кандидат отклонён"
statusTone="danger"
facts={[
{ label: "Источник", value: `RAVNOVES00 · ${metrics.reviewFrameCount} camera-bound кадров` },
{ label: "Синхронизация", value: `p95 ${metrics.cameraBindingAbsoluteDeltaMs.p95.toLocaleString("ru-RU", { maximumFractionDigits: 1 })} мс` },
{ label: "Режимы", value: "CAM / 3D / BEV · LiDAR overlay отключаемый" },
{ label: "Полномочия", value: "Shadow-only · accuracy и safety не приняты" },
]}
brief={{
question: "Соответствуют ли cross-domain гипотезы PointPillars видимым объектам RAVNOVES00?",
approach: "17 route-wide кадров старой L3.1 привязаны к ближайшему точному кадру правой камеры. Точки и 3D-боксы спроецированы неизменённой заводской KB4-калибровкой; повторный inference не выполнялся.",
principalResult: `${percent(metrics.scoreBelow025Fraction)} опубликованных гипотез имеют score ниже 0,25, ${percent(metrics.scoreBelow050Fraction)} — ниже 0,50. Камера делает семантическое несоответствие визуально проверяемым.`,
limitation: "Камера не заменяет независимый 3D ground truth, поэтому precision/recall не вычисляются. Эта работа доказательно отклоняет текущий кандидат, но не измеряет точность будущего детектора.",
}}
method={{
completeness: "complete",
executionClass: "deterministic",
pipelineId: "l32-pointpillars-camera-review/v1",
components: [
{ kind: "source", name: result.sourceL31ResultId, version: "sealed L3.1", role: "неизменённые точки и гипотезы PointPillars", identitySha256: result.sourceL31ResultId.split("-").at(-1) ?? null },
{ kind: "source", name: "sensor.camera.right", version: result.sourceSessionId, role: "семантический camera reference", identitySha256: null },
{ kind: "algorithm", name: "Factory KB4 projection", version: "camera_1 · 800×600", role: "проекция LiDAR и 3D-боксов в кадр", identitySha256: null },
{ kind: "runtime", name: "Mission Core local evidence builder", version: result.resultId, role: "последовательная immutable-материализация", identitySha256: result.resultId.split("-").at(-1) ?? null },
],
}}
/>}
evidence={<LaboratoryEvidence
eyebrow="RAVNOVES00 → CAMERA-BOUND ДОКАЗАТЕЛЬСТВО"
title="Камера, точки LiDAR и гипотезы PointPillars"
kind="diagnostic-model"
resizable
>
<L32PointPillarsCameraReviewVisual result={result} />
</LaboratoryEvidence>}
result={<LaboratoryResultSummary
title="Runtime работает, но текущая модель семантически непригодна"
status="Кандидат отклонён"
statusTone="danger"
metrics={[
{ label: "Camera-bound frames", value: metrics.reviewFrameCount.toLocaleString("ru-RU"), hint: `из 18 старых visual frames; 1 был до начала камеры` },
{ label: "Camera sync p95", value: `${metrics.cameraBindingAbsoluteDeltaMs.p95.toLocaleString("ru-RU", { maximumFractionDigits: 1 })} мс`, hint: `max ${metrics.cameraBindingAbsoluteDeltaMs.maximum.toLocaleString("ru-RU", { maximumFractionDigits: 1 })} мс` },
{ label: "Score < 0,25", value: percent(metrics.scoreBelow025Fraction), hint: `${metrics.reviewPredictionCount.toLocaleString("ru-RU")} гипотез в ревизии` },
{ label: "Видимые projected boxes", value: metrics.reviewVisibleProjectedBoxCount.toLocaleString("ru-RU"), hint: "контуры, попавшие в camera FOV" },
]}
conclusion={{
proved: "Точки сенсорного рига, правая камера и PointPillars-гипотезы воспроизводимо совмещаются в одной системе просмотра; источник и калибровка привязаны к RAVNOVES00.",
notProved: "Текущий PointPillars не доказал семантическую корректность и не получает эксплуатационных полномочий. Независимой 3D-разметки по-прежнему нет.",
decision: "Старую L3.1 убрать из продуктового каталога, сохранить как audit trail. L3.2 считать отрицательным baseline; следующий detector-кандидат проверять тем же CAM → LiDAR → target evidence-контрактом.",
}}
/>}
/>
);
}
@@ -0,0 +1,156 @@
import { useEffect, useState } from "react";
import { Icon, IconButton, Select } from "@nodedc/ui-react";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
import {
fetchL32PointPillarsCameraReviewFrame,
L32_REVIEW_SCORE_THRESHOLD,
type L32PointPillarsCameraReviewResult,
type L32VisualFrame,
} from "../../core/laboratory/l32PointPillarsCameraReview";
import { L3PointPillarsScene } from "./L3PointPillarsScene";
import { L32PointPillarsCameraScene } from "./L32PointPillarsCameraScene";
type ViewerMode = "cam" | "3d" | "bev";
type LidarMode = "on" | "off";
function frameLabel(frame: L32PointPillarsCameraReviewResult["frames"][number]) {
return `${frame.sessionSeconds.toLocaleString("ru-RU", {
maximumFractionDigits: 1,
})} с · кадр ${frame.frameId} · Vehicle ${frame.classCounts.Vehicle}`;
}
export function L32PointPillarsCameraReviewVisual({
result,
}: {
result: L32PointPillarsCameraReviewResult;
}) {
const [selectedFrameId, setSelectedFrameId] = useState(result.frames[0]?.frameId ?? "");
const [frame, setFrame] = useState<L32VisualFrame | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [mode, setMode] = useState<ViewerMode>("cam");
const [lidarMode, setLidarMode] = useState<LidarMode>("on");
const [expanded, setExpanded] = useState(false);
useEffect(() => {
if (!selectedFrameId) return;
const controller = new AbortController();
setFrame(null);
setLoading(true);
setError(null);
void fetchL32PointPillarsCameraReviewFrame(
result.resultId,
selectedFrameId,
{ signal: controller.signal },
).then((next) => {
if (!controller.signal.aborted) setFrame(next);
}).catch((caught: unknown) => {
if (!controller.signal.aborted) {
setError(caught instanceof Error ? caught.message : "Кадр L3.2 недоступен.");
}
}).finally(() => {
if (!controller.signal.aborted) setLoading(false);
});
return () => controller.abort();
}, [result.resultId, selectedFrameId]);
const selectedIndex = result.frames.findIndex(({ frameId }) => frameId === selectedFrameId);
const navigate = (offset: -1 | 1) => {
if (selectedIndex < 0) return;
const index = (selectedIndex + offset + result.frames.length) % result.frames.length;
setSelectedFrameId(result.frames[index].frameId);
};
const actions = (
<div className="l3-visual-audit__actions">
<div className="l3-visual-audit__pagination">
<IconButton label="Предыдущий кадр RAVNOVES00" onClick={() => navigate(-1)}>
<Icon name="chevron-left" size={16} />
</IconButton>
<IconButton label="Следующий кадр RAVNOVES00" onClick={() => navigate(1)}>
<Icon name="chevron-right" size={16} />
</IconButton>
</div>
<Select
label="Выбрать camera-bound кадр RAVNOVES00"
value={selectedFrameId}
options={result.frames.map((item) => ({ value: item.frameId, label: frameLabel(item) }))}
variant="split"
menuWidth="anchor"
onChange={setSelectedFrameId}
/>
</div>
);
const reviewableBoxCount = frame?.projectedBoxes.filter(
({ score }) => score >= L32_REVIEW_SCORE_THRESHOLD,
).length ?? 0;
const rejectedBoxCount = (frame?.projectedBoxes.length ?? 0) - reviewableBoxCount;
const overlay = frame ? (
<div className="l3-visual-audit__overlay">
<div>
<span>RAVNOVES00 · правая камера</span>
<strong>{frame.summary.sessionSeconds.toLocaleString("ru-RU", { maximumFractionDigits: 1 })} с · кадр {frame.frameId}</strong>
<small>Δ camera/LiDAR {Math.abs(frame.summary.cameraDeltaMs).toLocaleString("ru-RU", { maximumFractionDigits: 1 })} мс</small>
</div>
<div>
<span>PointPillars · кандидаты, не truth</span>
<strong>Vehicle {frame.summary.classCounts.Vehicle} · Pedestrian {frame.summary.classCounts.Pedestrian} · Cyclist {frame.summary.classCounts.Cyclist}</strong>
<small>{reviewableBoxCount} для ревизии · {rejectedBoxCount} ниже confidence 25%</small>
</div>
<div className="l3-visual-audit__legend">
<span data-tone="warning">Жёлтый пунктир · слабый кандидат 2550%</span>
<span data-tone="prediction">Белый контур · кандидат от 50%</span>
<span>Цветные точки · метрический LiDAR overlay</span>
</div>
</div>
) : undefined;
return (
<div className="l3-visual-audit">
<LaboratoryEvidenceViewer
label="Camera-bound PointPillars на RAVNOVES00"
mode={mode}
modes={[
{ value: "cam", label: "CAM" },
{ value: "3d", label: "3D" },
{ value: "bev", label: "BEV" },
]}
secondaryMode={mode === "cam" ? {
value: lidarMode,
modes: [
{ value: "on", label: "L on" },
{ value: "off", label: "L off" },
],
label: "LiDAR поверх камеры",
onChange: setLidarMode,
} : undefined}
expanded={expanded}
onModeChange={setMode}
onExpandedChange={setExpanded}
actions={actions}
overlay={overlay}
>
{loading ? (
<div className="l3-visual-audit__state" role="status">
<span className="busy-indicator" aria-hidden="true" />
<span>Открываем camera-bound кадр RAVNOVES00</span>
</div>
) : error || !frame ? (
<div className="l3-visual-audit__state" role="status">
<Icon name="alert" size={18} />
<span>{error ?? "Кадр L3.2 недоступен."}</span>
</div>
) : mode === "cam" ? (
<L32PointPillarsCameraScene frame={frame} lidarVisible={lidarMode === "on"} />
) : (
<L3PointPillarsScene frame={{
frameId: frame.frameId,
pointsXyzi: frame.pointsXyzi,
truthBoxes: [],
predictionBoxes: frame.predictionBoxes,
}} mode={mode} bevCenterX={0} bevHalfExtent={55} />
)}
</LaboratoryEvidenceViewer>
</div>
);
}
@@ -0,0 +1,140 @@
import { useEffect, useRef, useState } from "react";
import {
L32_REVIEW_SCORE_THRESHOLD,
type L32VisualFrame,
} from "../../core/laboratory/l32PointPillarsCameraReview";
export function L32PointPillarsCameraScene({
frame,
lidarVisible,
}: {
frame: L32VisualFrame;
lidarVisible: boolean;
}) {
const hostRef = useRef<HTMLDivElement | null>(null);
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const [image, setImage] = useState<HTMLImageElement | null>(null);
const [error, setError] = useState(false);
useEffect(() => {
const next = new Image();
next.decoding = "async";
next.onload = () => {
setError(false);
setImage(next);
};
next.onerror = () => {
setImage(null);
setError(true);
};
next.src = frame.cameraUrl;
return () => {
next.onload = null;
next.onerror = null;
};
}, [frame.cameraUrl]);
useEffect(() => {
const host = hostRef.current;
const canvas = canvasRef.current;
if (!host || !canvas || !image) return;
const context = canvas.getContext("2d");
if (!context) return;
const render = () => {
const width = Math.max(host.clientWidth, 1);
const height = Math.max(host.clientHeight, 1);
const ratio = Math.min(window.devicePixelRatio, 1.5);
canvas.width = Math.round(width * ratio);
canvas.height = Math.round(height * ratio);
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
context.setTransform(ratio, 0, 0, ratio, 0, 0);
context.fillStyle = "#050506";
context.fillRect(0, 0, width, height);
const scale = Math.min(width / frame.cameraWidth, height / frame.cameraHeight);
const drawWidth = frame.cameraWidth * scale;
const drawHeight = frame.cameraHeight * scale;
const offsetX = (width - drawWidth) / 2;
const offsetY = (height - drawHeight) / 2;
context.drawImage(image, offsetX, offsetY, drawWidth, drawHeight);
if (lidarVisible) {
for (let index = 0; index < frame.projectedPointsXyd.length; index += 3) {
const x = offsetX + frame.projectedPointsXyd[index] * scale;
const y = offsetY + frame.projectedPointsXyd[index + 1] * scale;
const depth = frame.projectedPointsXyd[index + 2];
const hue = Math.max(188, Math.min(226, 226 - depth * 1.1));
context.fillStyle = `hsla(${hue} 92% 68% / 0.72)`;
context.beginPath();
context.arc(x, y, Math.max(1.2, 1.65 * scale), 0, Math.PI * 2);
context.fill();
}
}
frame.projectedBoxes.filter(
({ score }) => score >= L32_REVIEW_SCORE_THRESHOLD,
).forEach((box) => {
const admitted = box.score >= 0.5;
context.lineWidth = Math.max(1.25, (admitted ? 1.8 : 1.55) * scale);
context.strokeStyle = admitted
? "rgba(255, 248, 232, 0.94)"
: "rgba(255, 196, 71, 0.9)";
context.setLineDash(admitted
? []
: [Math.max(4, 6 * scale), Math.max(3, 4 * scale)]);
const segments = box.segmentsXyxy;
for (let index = 0; index < segments.length; index += 4) {
context.beginPath();
context.moveTo(
offsetX + segments[index] * scale,
offsetY + segments[index + 1] * scale,
);
context.lineTo(
offsetX + segments[index + 2] * scale,
offsetY + segments[index + 3] * scale,
);
context.stroke();
}
const xValues = segments.filter((_, index) => index % 2 === 0);
const yValues = segments.filter((_, index) => index % 2 === 1);
if (xValues.length && yValues.length) {
const label = `${box.modelClass} ${(box.score * 100).toFixed(0)}%`;
const fontSize = Math.max(10, 11 * scale);
const x = offsetX + Math.min(...xValues) * scale;
const y = offsetY + Math.min(...yValues) * scale;
context.font = `600 ${fontSize}px Inter, system-ui, sans-serif`;
const labelWidth = context.measureText(label).width;
context.setLineDash([]);
context.fillStyle = "rgba(5, 5, 6, 0.82)";
context.fillRect(
x,
Math.max(offsetY, y - fontSize - 7),
labelWidth + 12,
fontSize + 7,
);
context.fillStyle = admitted ? "#fff8e8" : "#ffc447";
context.fillText(label, x + 6, Math.max(offsetY + fontSize, y - 5));
}
});
context.setLineDash([]);
};
const observer = new ResizeObserver(render);
observer.observe(host);
render();
return () => observer.disconnect();
}, [frame, image, lidarVisible]);
return (
<div className="l32-camera-scene" ref={hostRef}>
<canvas
ref={canvasRef}
role="img"
aria-label={`RAVNOVES00 camera frame ${frame.frameId}${lidarVisible ? ", LiDAR on" : ", LiDAR off"}`}
/>
{error ? (
<div className="l3-visual-audit__state" role="status">
Точный кадр правой камеры недоступен.
</div>
) : null}
</div>
);
}
@@ -0,0 +1,78 @@
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import type { L33CameraFirstDetectorReviewResult } from "../../core/laboratory/l33CameraFirstDetectorReview";
import { L33CameraFirstDetectorVisual } from "./L33CameraFirstDetectorVisual";
function percent(value: number): string {
return `${(value * 100).toLocaleString("ru-RU", { maximumFractionDigits: 1 })}%`;
}
export function L33CameraFirstDetectorReviewResult({
result,
}: {
result: L33CameraFirstDetectorReviewResult;
}) {
const metrics = result.metrics;
return (
<LaboratoryWorkTemplate
summary={<LaboratorySummary
title="L3.3 · Camera-first detector + LiDAR range · RAVNOVES00"
description="Полный записанный маршрут проходит через rectified YOLOX-S, ByteTrack, EoMT, калиброванную LiDAR-геометрию, 3D-кубоиды и ограниченную temporal-стабилизацию. Камера сохраняет объект без дальности; LiDAR добавляет только доказанную метрическую геометрию."
status="Полный replay принят · runtime ещё не подключён"
statusTone="warning"
facts={[
{ label: "Источник", value: `RAVNOVES00 · ${metrics.framesProcessed.toLocaleString("ru-RU")} кадров полного маршрута` },
{ label: "Контур", value: `${result.detector.architecture} + ByteTrack + EoMT + LiDAR + E23` },
{ label: "Производительность", value: `${metrics.effectiveComposedFps.toLocaleString("ru-RU", { maximumFractionDigits: 2 })} кадр/с · p95 ${metrics.composedP95Ms.toLocaleString("ru-RU", { maximumFractionDigits: 1 })} мс` },
{ label: "Полномочия", value: "Shadow-only · accuracy, navigation и safety не приняты" },
]}
brief={{
question: "Собирается ли из правой камеры сенсорного рига и LiDAR единый объектный world state на всём маршруте, а не только на выбранных красивых кадрах?",
approach: "Все 4 489 кадров RAVNOVES00 обработаны rectified core-3 YOLOX-S и ByteTrack. Принятый EoMT работает как multi-rate семантическая поддержка 2 Гц; калиброванный LiDAR назначает дальность и завершает кубоид, E23 ограниченно удерживает состояние между наблюдениями. Ни один camera-only объект не получает выдуманную дальность.",
principalResult: `${metrics.routeDetectionCount.toLocaleString("ru-RU")} camera-track наблюдений получены на ${metrics.routeDetectionFrameCount.toLocaleString("ru-RU")} кадрах. LiDAR-фьюжн доступен на ${metrics.lidarFusedFrames.toLocaleString("ru-RU")} кадрах; принято ${metrics.rawAcceptedCuboids.toLocaleString("ru-RU")} исходных и ${metrics.temporalAcceptedCuboids.toLocaleString("ru-RU")} temporal-наблюдений 3D-кубоидов. ${metrics.duplicateSupportRejections.toLocaleString("ru-RU")} конфликтующих повторных назначений одной LiDAR-опоры заблокированы.`,
limitation: "Это полный recorded replay, а не живой транспорт и не независимая accuracy-разметка. EoMT остаётся multi-rate 2 Гц; следующий gate — тот же контур в тёплом worker runtime.",
}}
method={{
completeness: "complete",
executionClass: "deterministic",
pipelineId: "kb4-core3-yolox-eomt-lidar-e23-temporal/v1",
components: [
{ kind: "model", name: result.detector.architecture, version: result.detector.modelSha256, role: "camera semantic class and 2D track", identitySha256: result.detector.modelSha256 },
{ kind: "model", name: "EoMT semantic segmentation", version: `${metrics.semanticEffectiveFps.toFixed(2)} Hz multi-rate`, role: "semantic support for LiDAR association", identitySha256: null },
{ kind: "algorithm", name: "Calibrated LiDAR cuboid completion", version: result.sourceWorldStateResultId, role: "metric range and bounded amodal 3D cuboid", identitySha256: result.sourceWorldStateResultId.split("-").at(-1) ?? null },
{ kind: "algorithm", name: "E23 inline temporal state", version: "bounded hold and stitching", role: "preserve object state between qualified observations", identitySha256: null },
{ kind: "source", name: result.sourceL32ResultId, version: result.sourceSessionId, role: "exact camera frames and calibrated LiDAR overlay", identitySha256: result.sourceL32ResultId.split("-").at(-1) ?? null },
],
}}
/>}
evidence={<LaboratoryEvidence
eyebrow="RAVNOVES00 → CAMERA-FIRST ДОКАЗАТЕЛЬСТВО"
title="Объекты камеры, LiDAR-поддержка и метрическая дальность"
kind="diagnostic-model"
resizable
>
<L33CameraFirstDetectorVisual result={result} />
</LaboratoryEvidence>}
result={<LaboratoryResultSummary
title="Полный camera-first world state собран на записанном маршруте"
status="Replay gate принят · live gate следующий"
statusTone="warning"
metrics={[
{ label: "Полный маршрут", value: metrics.framesProcessed.toLocaleString("ru-RU"), hint: `${metrics.effectiveComposedFps.toFixed(2)} кадр/с effective` },
{ label: "LiDAR fusion", value: metrics.lidarFusedFrames.toLocaleString("ru-RU"), hint: `${percent(metrics.lidarFusedFrames / metrics.framesProcessed)} кадров` },
{ label: "3D cuboids", value: metrics.rawAcceptedCuboids.toLocaleString("ru-RU"), hint: "приняты до temporal hold" },
{ label: "Конфликты геометрии", value: metrics.duplicateSupportRejections.toLocaleString("ru-RU"), hint: "одна LiDAR-опора не размножает объекты" },
]}
conclusion={{
proved: "На всех 4 489 кадрах одной записи детектор, трекер, multi-rate сегментация, LiDAR-дальность, кубоид и temporal world state выполняются как один ограниченный контур с полным учётом кадров и принятым latency gate. Одна LiDAR-опора больше не может одновременно породить геометрию нескольких camera-track объектов.",
notProved: "Пока не доказаны live transport, длительная эксплуатационная устойчивость, precision/recall на независимой разметке и пригодность для navigation/safety. Визуальные пустые кадры остаются частью проверки, а не скрываются.",
decision: "Не создавать новый LAB. Эту L3.3 использовать как визуальный аудит полного replay; следующий инженерный шаг — подключить тот же состав в тёплый worker и измерить live cadence без изменения device acquisition sequence.",
}}
/>}
/>
);
}
@@ -0,0 +1,238 @@
import { useEffect, useRef, useState } from "react";
import type { L33VisualFrame } from "../../core/laboratory/l33CameraFirstDetectorReview";
function tokenColor(
host: HTMLElement,
token: string,
fallback: readonly [number, number, number],
alpha = 1,
): string {
const value = getComputedStyle(host).getPropertyValue(token).trim();
const channels = value.match(/[\d.]+/g)?.slice(0, 3).map(Number);
const [red, green, blue] = channels?.length === 3 ? channels : fallback;
return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
}
function labelForDetection(
label: string,
rangeM: number | null,
score: number,
): string {
const names: Readonly<Record<string, string>> = {
car: "Авто",
truck: "Грузовик",
bus: "Автобус",
person: "Человек",
bicycle: "Велосипед",
motorcycle: "Мотоцикл",
};
const qualifier = rangeM === null
? `${(score * 100).toFixed(0)}%`
: `${rangeM.toLocaleString("ru-RU", { maximumFractionDigits: 1 })} м`;
return `${names[label] ?? label} · ${qualifier}`;
}
type LabelRect = Readonly<{
x: number;
y: number;
width: number;
height: number;
}>;
function overlaps(left: LabelRect, right: LabelRect): boolean {
const margin = 3;
return !(
left.x + left.width + margin <= right.x
|| right.x + right.width + margin <= left.x
|| left.y + left.height + margin <= right.y
|| right.y + right.height + margin <= left.y
);
}
function placeLabel(
box: LabelRect,
labelWidth: number,
labelHeight: number,
imageBounds: LabelRect,
occupied: readonly LabelRect[],
): LabelRect | null {
const clampX = (value: number) => Math.min(
imageBounds.x + imageBounds.width - labelWidth,
Math.max(imageBounds.x, value),
);
const candidates: LabelRect[] = [];
const horizontal = [box.x, box.x + box.width - labelWidth];
for (let row = 0; row < 4; row += 1) {
const above = box.y - labelHeight - 3 - row * (labelHeight + 3);
const below = box.y + box.height + 3 + row * (labelHeight + 3);
for (const x of horizontal) {
candidates.push({ x: clampX(x), y: above, width: labelWidth, height: labelHeight });
candidates.push({ x: clampX(x), y: below, width: labelWidth, height: labelHeight });
}
}
candidates.push({
x: clampX(box.x + 3),
y: box.y + 3,
width: labelWidth,
height: labelHeight,
});
return candidates.find((candidate) => (
candidate.y >= imageBounds.y
&& candidate.y + candidate.height <= imageBounds.y + imageBounds.height
&& occupied.every((current) => !overlaps(candidate, current))
)) ?? null;
}
export function L33CameraFirstDetectorScene({
frame,
lidarVisible,
}: {
frame: L33VisualFrame;
lidarVisible: boolean;
}) {
const hostRef = useRef<HTMLDivElement | null>(null);
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const [image, setImage] = useState<HTMLImageElement | null>(null);
const [error, setError] = useState(false);
useEffect(() => {
const next = new Image();
next.decoding = "async";
next.onload = () => {
setError(false);
setImage(next);
};
next.onerror = () => {
setImage(null);
setError(true);
};
next.src = frame.cameraUrl;
return () => {
next.onload = null;
next.onerror = null;
};
}, [frame.cameraUrl]);
useEffect(() => {
const host = hostRef.current;
const canvas = canvasRef.current;
if (!host || !canvas || !image) return;
const context = canvas.getContext("2d");
if (!context) return;
const render = () => {
const width = Math.max(host.clientWidth, 1);
const height = Math.max(host.clientHeight, 1);
const ratio = Math.min(window.devicePixelRatio, 1.5);
canvas.width = Math.round(width * ratio);
canvas.height = Math.round(height * ratio);
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
context.setTransform(ratio, 0, 0, ratio, 0, 0);
context.fillStyle = tokenColor(host, "--nodedc-canvas-rgb", [5, 5, 6]);
context.fillRect(0, 0, width, height);
const scale = Math.min(width / frame.cameraWidth, height / frame.cameraHeight);
const drawWidth = frame.cameraWidth * scale;
const drawHeight = frame.cameraHeight * scale;
const offsetX = (width - drawWidth) / 2;
const offsetY = (height - drawHeight) / 2;
context.drawImage(image, offsetX, offsetY, drawWidth, drawHeight);
if (lidarVisible) {
for (let index = 0; index < frame.projectedPointsXyd.length; index += 3) {
const x = offsetX + frame.projectedPointsXyd[index] * scale;
const y = offsetY + frame.projectedPointsXyd[index + 1] * scale;
const depth = frame.projectedPointsXyd[index + 2];
const hue = Math.max(188, Math.min(226, 226 - depth * 1.1));
context.fillStyle = `hsla(${hue} 92% 68% / 0.56)`;
context.beginPath();
context.arc(x, y, Math.max(1.0, 1.45 * scale), 0, Math.PI * 2);
context.fill();
}
}
const rangeColor = tokenColor(host, "--nodedc-success-rgb", [143, 255, 93], 0.96);
const cameraColor = tokenColor(host, "--nodedc-text-primary-rgb", [247, 248, 244], 0.92);
const glassColor = tokenColor(host, "--nodedc-canvas-rgb", [5, 5, 6], 0.82);
const boxes = frame.detections.map((detection) => {
const [left, top, right, bottom] = detection.bboxXyxy;
const x = offsetX + left * scale;
const y = offsetY + top * scale;
const boxWidth = (right - left) * scale;
const boxHeight = (bottom - top) * scale;
const ranged = detection.rangeM !== null;
context.strokeStyle = ranged ? rangeColor : cameraColor;
context.lineWidth = Math.max(1.25, 1.8 * scale);
context.setLineDash(
detection.semanticProvenance === "bounded-track-interpolation"
|| detection.semanticProvenance === "e23-temporal-hold"
? [Math.max(4, 6 * scale), Math.max(3, 4 * scale)]
: [],
);
context.strokeRect(x, y, boxWidth, boxHeight);
return { detection, x, y, width: boxWidth, height: boxHeight, ranged };
});
const occupied: LabelRect[] = [];
const imageBounds = { x: offsetX, y: offsetY, width: drawWidth, height: drawHeight };
[...boxes]
.sort((left, right) => (
Number(right.ranged) - Number(left.ranged)
|| right.width * right.height - left.width * left.height
))
.forEach(({ detection, x, y, width: boxWidth, height: boxHeight, ranged }) => {
const label = labelForDetection(
detection.label,
detection.rangeM,
detection.score,
);
const fontSize = Math.max(9, 10 * scale);
const labelHeight = fontSize + 7;
context.font = `600 ${fontSize}px Inter, system-ui, sans-serif`;
const labelWidth = context.measureText(label).width + 12;
const placement = placeLabel(
{ x, y, width: boxWidth, height: boxHeight },
labelWidth,
labelHeight,
imageBounds,
occupied,
);
if (!placement) return;
occupied.push(placement);
context.setLineDash([]);
context.fillStyle = glassColor;
context.fillRect(
placement.x,
placement.y,
placement.width,
placement.height,
);
context.fillStyle = ranged ? rangeColor : cameraColor;
context.fillText(
label,
placement.x + 6,
placement.y + fontSize + 1,
);
});
context.setLineDash([]);
};
const observer = new ResizeObserver(render);
observer.observe(host);
render();
return () => observer.disconnect();
}, [frame, image, lidarVisible]);
return (
<div className="l32-camera-scene" ref={hostRef}>
<canvas
ref={canvasRef}
role="img"
aria-label={`RAVNOVES00 camera-first frame ${frame.frameId}${lidarVisible ? ", LiDAR on" : ", LiDAR off"}`}
/>
{error ? (
<div className="l3-visual-audit__state" role="status">
Точный кадр правой камеры недоступен.
</div>
) : null}
</div>
);
}
@@ -0,0 +1,169 @@
import { useEffect, useMemo, useState } from "react";
import { Icon, IconButton, Select } from "@nodedc/ui-react";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
import {
fetchL33CameraFirstDetectorReviewFrame,
type L33CameraFirstDetectorReviewResult,
type L33VisualFrame,
} from "../../core/laboratory/l33CameraFirstDetectorReview";
import { L3PointPillarsScene } from "./L3PointPillarsScene";
import { L33CameraFirstDetectorScene } from "./L33CameraFirstDetectorScene";
type ViewerMode = "cam" | "3d" | "bev";
type LidarMode = "on" | "off";
function frameLabel(frame: L33CameraFirstDetectorReviewResult["frames"][number]) {
return `${frame.sessionSeconds.toLocaleString("ru-RU", {
maximumFractionDigits: 1,
})} с · кадр ${frame.frameId} · объектов ${frame.detectionCount}`;
}
export function L33CameraFirstDetectorVisual({
result,
}: {
result: L33CameraFirstDetectorReviewResult;
}) {
const [selectedFrameId, setSelectedFrameId] = useState(result.frames[0]?.frameId ?? "");
const [frame, setFrame] = useState<L33VisualFrame | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [mode, setMode] = useState<ViewerMode>("cam");
const [lidarMode, setLidarMode] = useState<LidarMode>("on");
const [expanded, setExpanded] = useState(false);
useEffect(() => {
if (!selectedFrameId) return;
const controller = new AbortController();
setFrame(null);
setLoading(true);
setError(null);
void fetchL33CameraFirstDetectorReviewFrame(
result.resultId,
selectedFrameId,
{ signal: controller.signal },
).then((next) => {
if (!controller.signal.aborted) setFrame(next);
}).catch((caught: unknown) => {
if (!controller.signal.aborted) {
setError(caught instanceof Error ? caught.message : "Кадр L3.3 недоступен.");
}
}).finally(() => {
if (!controller.signal.aborted) setLoading(false);
});
return () => controller.abort();
}, [result.resultId, selectedFrameId]);
const selectedIndex = result.frames.findIndex(({ frameId }) => frameId === selectedFrameId);
const associationRays = useMemo(() => frame?.detections.map((detection) => ({
originXyzM: detection.cameraRayLidar.originXyzM,
directionXyz: detection.cameraRayLidar.directionXyz,
anchorXyzM: detection.geometryAnchorXyzM,
temporalHeld: detection.semanticProvenance === "e23-temporal-hold",
})) ?? [], [frame]);
const navigate = (offset: -1 | 1) => {
if (selectedIndex < 0) return;
const index = (selectedIndex + offset + result.frames.length) % result.frames.length;
setSelectedFrameId(result.frames[index].frameId);
};
const actions = (
<div className="l3-visual-audit__actions">
<div className="l3-visual-audit__pagination">
<IconButton label="Предыдущий camera-first кадр" onClick={() => navigate(-1)}>
<Icon name="chevron-left" size={16} />
</IconButton>
<IconButton label="Следующий camera-first кадр" onClick={() => navigate(1)}>
<Icon name="chevron-right" size={16} />
</IconButton>
</div>
<Select
label="Выбрать camera-first кадр RAVNOVES00"
value={selectedFrameId}
options={result.frames.map((item) => ({ value: item.frameId, label: frameLabel(item) }))}
variant="split"
menuWidth="anchor"
onChange={setSelectedFrameId}
/>
</div>
);
const overlay = frame ? (
<div className="l3-visual-audit__overlay">
<div>
<span>RAVNOVES00 · правая камера</span>
<strong>{frame.summary.sessionSeconds.toLocaleString("ru-RU", { maximumFractionDigits: 1 })} с · кадр {frame.frameId}</strong>
<small>{frame.summary.semanticProvenance === "e23-temporal-hold" ? "E23 удерживает состояние между наблюдениями" : "Текущий rectified camera track"}</small>
</div>
<div>
<span>YOLOX-S · камера владеет семантикой</span>
<strong>{frame.detections.length} объектов · {frame.summary.rangedDetectionCount} с LiDAR-дистанцией</strong>
<small>Порог detector score {(result.metrics.minimumDetectorScore * 100).toFixed(0)}%</small>
</div>
{mode === "cam" ? (
<div className="l3-visual-audit__legend">
<span data-tone="success">Зелёный контур · LiDAR подтвердил дальность</span>
<span data-tone="prediction">Белый контур · объект камеры без метрической дальности</span>
<span>Пунктир · E23 временно удерживает camera track</span>
</div>
) : (
<div className="l3-visual-audit__legend">
<span data-tone="success">Зелёный луч и точка · LiDAR-дальность; пунктир · E23 hold</span>
<span data-tone="prediction">Белый пунктирный луч · camera-only направление</span>
<span>Светлый куб · world-state объект; пунктир · E23 hold</span>
</div>
)}
</div>
) : undefined;
return (
<div className="l3-visual-audit">
<LaboratoryEvidenceViewer
label="Camera-first detector на RAVNOVES00"
mode={mode}
modes={[
{ value: "cam", label: "CAM" },
{ value: "3d", label: "3D" },
{ value: "bev", label: "BEV" },
]}
secondaryMode={mode === "cam" ? {
value: lidarMode,
modes: [
{ value: "on", label: "L on" },
{ value: "off", label: "L off" },
],
label: "LiDAR поверх камеры",
onChange: setLidarMode,
} : undefined}
expanded={expanded}
onModeChange={setMode}
onExpandedChange={setExpanded}
actions={actions}
overlay={overlay}
>
{loading ? (
<div className="l3-visual-audit__state" role="status">
<span className="busy-indicator" aria-hidden="true" />
<span>Открываем camera-first кадр RAVNOVES00</span>
</div>
) : error || !frame ? (
<div className="l3-visual-audit__state" role="status">
<Icon name="alert" size={18} />
<span>{error ?? "Кадр L3.3 недоступен."}</span>
</div>
) : mode === "cam" ? (
<L33CameraFirstDetectorScene frame={frame} lidarVisible={lidarMode === "on"} />
) : (
<L3PointPillarsScene frame={{
frameId: frame.frameId,
pointsXyzi: frame.pointsXyzi,
truthBoxes: [],
predictionBoxes: frame.predictionBoxes,
}}
mode={mode}
associationRays={associationRays}
fitToEvidence
/>
)}
</LaboratoryEvidenceViewer>
</div>
);
}
@@ -0,0 +1,126 @@
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import type {
L34AAssistedYoloxErrorAuditResult,
} from "../../core/laboratory/l34aAssistedYoloxErrorAudit";
import { formatNumber } from "../../presentation";
import { L34AAssistedYoloxErrorVisual } from "./L34AAssistedYoloxErrorVisual";
function digest(value: string): string | null {
const candidate = value.split("-").at(-1) ?? "";
return /^[a-f0-9]{64}$/.test(candidate) ? candidate : null;
}
function formatPercent(value: number): string {
return `${(value * 100).toLocaleString("ru-RU", { maximumFractionDigits: 1 })}%`;
}
export function L34AAssistedYoloxErrorResultView({
rigLabel,
result,
}: {
rigLabel: string;
result: L34AAssistedYoloxErrorAuditResult;
}) {
const metrics = result.metrics;
return (
<LaboratoryWorkTemplate
summary={(
<LaboratorySummary
title="LAB L3.4A · assisted YOLOX error audit"
description="Детерминированное сопоставление точного L3.4 candidate freeze с полностью проверенной candidate-seeded разметкой: инженерная диагностика ошибок, но не независимая truth."
status="Assisted diagnostic · не truth"
statusTone="warning"
facts={[
{ label: "Конфигурация", value: `${rigLabel} · RIGHT camera · recorded replay` },
{ label: "Сопоставление", value: "Greedy max-IoU · threshold 0.50" },
{ label: "Покрытие", value: `${formatNumber(metrics.frameCount, 0)} кадров · ${formatNumber(metrics.referenceCount, 0)} объектов` },
{ label: "Полномочия", value: "Диагностика · candidate не принят" },
]}
brief={{
question: "Какие конкретные FP, FN, дубли и ошибки класса делает замороженный YOLOX-S на 32 кадрах RAVNOVES00?",
approach: "Точные 268 L3.4 predictions пространственно сопоставлены при IoU ≥ 0.50 с 278 объектами сохранённой AI-assisted review. Каждый результат связан с исходным кадром, prediction box и reference box.",
principalResult: `${formatNumber(metrics.truePositive, 0)} совпадения, ${formatNumber(metrics.falsePositive, 0)} FP, ${formatNumber(metrics.falseNegative, 0)} FN, ${formatNumber(metrics.classMismatch, 0)} ошибок класса и ${formatNumber(metrics.duplicateFalsePositive, 0)} дублей; визуальные ошибки есть на ${formatNumber(metrics.errorCaseCount, 0)} из ${formatNumber(metrics.frameCount, 0)} кадров.`,
limitation: "Разметка создавалась поверх frozen candidate, поэтому alignment-метрики смещены в пользу модели и не являются blind accuracy, AP или основанием для acceptance. L3.5 остаётся закрыт до independent truth seal.",
}}
method={{
completeness: "complete",
executionClass: "deterministic",
pipelineId: result.pipelineId,
components: [
{
kind: "source",
name: "L3.4 RIGHT YOLOX freeze",
version: "268 immutable candidate boxes",
role: "prediction substrate",
identitySha256: null,
},
{
kind: "source",
name: result.assistedAnnotation.sessionId,
version: `revision ${result.assistedAnnotation.revision} · not truth`,
role: "candidate-seeded engineering reference",
identitySha256: result.assistedAnnotation.sessionSha256,
},
{
kind: "algorithm",
name: result.profileId,
version: "greedy max-IoU 0.50 · spatial match before class verdict",
role: "TP/FP/FN, duplicate and class-mismatch audit",
identitySha256: digest(result.resultId),
},
],
}}
/>
)}
evidence={(
<LaboratoryEvidence
eyebrow="ASSISTED ERROR AUDIT · SOURCE-BOUND VISUALS"
title="FP, FN, дубли и неверные классы на точных кадрах"
kind="diagnostic-model"
resizable
>
<L34AAssistedYoloxErrorVisual result={result} />
</LaboratoryEvidence>
)}
result={(
<LaboratoryResultSummary
title="Детектор требует раздельной коррекции post-processing, классов и данных"
status="Engineering diagnosis only"
statusTone="warning"
metrics={[
{
label: "Assisted precision@0.50",
value: formatPercent(metrics.precisionIou50),
hint: `${formatNumber(metrics.truePositive, 0)} TP · ${formatNumber(metrics.falsePositive, 0)} FP · не blind`,
},
{
label: "Assisted recall@0.50",
value: formatPercent(metrics.recallIou50),
hint: `${formatNumber(metrics.falseNegative, 0)} FN · не acceptance`,
},
{
label: "Классы",
value: formatNumber(metrics.classMismatch, 0),
hint: `${formatNumber(metrics.customReferenceCount, 0)} объектов с proposed ontology labels`,
},
{
label: "Дубли",
value: formatNumber(metrics.duplicateFalsePositive, 0),
hint: "отдельный post-processing сигнал",
},
]}
conclusion={{
proved: "На этих записанных кадрах воспроизводимо локализованы конкретные классы ошибок: дубли и лишние боксы, пропуски людей/статических препятствий и несовпадения stroller/scooter/laptop с текущей онтологией кандидата.",
notProved: "Не доказаны blind AP/recall, перенос на другой маршрут, работа LiDAR range, live/hardware качество, navigation или safety. Assisted alignment нельзя использовать как независимую truth.",
decision: "Использовать кейсы для отдельной коррекции NMS/post-processing, class mapping и подготовки detector-data. Не тюнить пороги по агрегату и не открывать L3.5 до двух независимых review и adjudication.",
}}
/>
)}
/>
);
}
@@ -0,0 +1,264 @@
import { useEffect, useRef, useState } from "react";
import type {
L34AAuditAnnotation,
L34AAuditCase,
L34AAuditPrediction,
} from "../../core/laboratory/l34aAssistedYoloxErrorAudit";
function tokenColor(
host: HTMLElement,
token: string,
fallback: readonly [number, number, number],
alpha = 1,
): string {
const value = getComputedStyle(host).getPropertyValue(token).trim();
const channels = value.match(/[\d.]+/g)?.slice(0, 3).map(Number);
const [red, green, blue] = channels?.length === 3 ? channels : fallback;
return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
}
const CATEGORY_NAMES: Readonly<Record<string, string>> = {
car: "Авто",
heavy_vehicle: "Тяжёлый транспорт",
person: "Человек",
bicycle: "Велосипед",
motorcycle: "Мотоцикл",
static_obstacle: "Препятствие",
animal: "Животное",
};
function categoryName(value: string): string {
if (value.startsWith("unmapped:")) return value.slice("unmapped:".length);
return CATEGORY_NAMES[value] ?? value;
}
function predictionLabel(
prediction: L34AAuditPrediction,
annotation: L34AAuditAnnotation | undefined,
): string | null {
const category = categoryName(prediction.category);
const score = `${(prediction.score * 100).toFixed(0)}%`;
if (prediction.verdict === "true_positive") return null;
if (prediction.verdict === "duplicate_false_positive") {
return `Дубль · ${category} · ${score}`;
}
if (prediction.verdict === "class_mismatch") {
return `Класс · ${category}${categoryName(annotation?.displayCategory ?? "?")}`;
}
return `FP · ${category} · ${score}`;
}
function annotationLabel(annotation: L34AAuditAnnotation): string | null {
if (annotation.verdict !== "false_negative") return null;
return `FN · ${categoryName(annotation.displayCategory)}`;
}
type LabelRect = Readonly<{
x: number;
y: number;
width: number;
height: number;
}>;
function overlaps(left: LabelRect, right: LabelRect): boolean {
const margin = 3;
return !(
left.x + left.width + margin <= right.x
|| right.x + right.width + margin <= left.x
|| left.y + left.height + margin <= right.y
|| right.y + right.height + margin <= left.y
);
}
function placeLabel(
box: LabelRect,
labelWidth: number,
labelHeight: number,
imageBounds: LabelRect,
occupied: readonly LabelRect[],
): LabelRect | null {
const x = Math.min(
imageBounds.x + imageBounds.width - labelWidth,
Math.max(imageBounds.x, box.x),
);
const candidates = [
box.y - labelHeight - 3,
box.y + box.height + 3,
box.y + 3,
].map((y) => ({ x, y, width: labelWidth, height: labelHeight }));
return candidates.find((candidate) => (
candidate.y >= imageBounds.y
&& candidate.y + candidate.height <= imageBounds.y + imageBounds.height
&& occupied.every((current) => !overlaps(candidate, current))
)) ?? null;
}
export function L34AAssistedYoloxErrorScene({
auditCase,
errorsVisible,
}: {
auditCase: L34AAuditCase;
errorsVisible: boolean;
}) {
const hostRef = useRef<HTMLDivElement | null>(null);
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const [image, setImage] = useState<HTMLImageElement | null>(null);
const [error, setError] = useState(false);
useEffect(() => {
const next = new Image();
next.decoding = "async";
next.onload = () => {
setError(false);
setImage(next);
};
next.onerror = () => {
setImage(null);
setError(true);
};
next.src = auditCase.cameraUrl;
return () => {
next.onload = null;
next.onerror = null;
};
}, [auditCase.cameraUrl]);
useEffect(() => {
const host = hostRef.current;
const canvas = canvasRef.current;
if (!host || !canvas || !image) return;
const context = canvas.getContext("2d");
if (!context) return;
const render = () => {
const width = Math.max(host.clientWidth, 1);
const height = Math.max(host.clientHeight, 1);
const ratio = Math.min(window.devicePixelRatio, 1.5);
canvas.width = Math.round(width * ratio);
canvas.height = Math.round(height * ratio);
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
context.setTransform(ratio, 0, 0, ratio, 0, 0);
context.fillStyle = tokenColor(host, "--nodedc-canvas-rgb", [5, 5, 6]);
context.fillRect(0, 0, width, height);
const scale = Math.min(
width / auditCase.cameraWidth,
height / auditCase.cameraHeight,
);
const drawWidth = auditCase.cameraWidth * scale;
const drawHeight = auditCase.cameraHeight * scale;
const offsetX = (width - drawWidth) / 2;
const offsetY = (height - drawHeight) / 2;
context.drawImage(image, offsetX, offsetY, drawWidth, drawHeight);
if (!errorsVisible) return;
const colors = {
tp: tokenColor(host, "--nodedc-success-rgb", [181, 255, 90], 0.66),
fp: tokenColor(host, "--nodedc-danger-rgb", [255, 116, 116], 0.98),
fn: tokenColor(host, "--nodedc-warning-rgb", [255, 209, 102], 0.98),
mismatch: tokenColor(host, "--nodedc-accent-rgb", [255, 47, 146], 0.98),
label: tokenColor(host, "--nodedc-canvas-rgb", [5, 5, 6], 0.86),
};
const imageBounds = {
x: offsetX,
y: offsetY,
width: drawWidth,
height: drawHeight,
};
const occupied: LabelRect[] = [];
const annotationById = new Map(
auditCase.annotations.map((annotation) => [annotation.objectId, annotation]),
);
const drawBox = (
rawBox: readonly [number, number, number, number],
color: string,
dashed: boolean,
): LabelRect => {
const [left, top, right, bottom] = rawBox;
const box = {
x: offsetX + left * scale,
y: offsetY + top * scale,
width: (right - left) * scale,
height: (bottom - top) * scale,
};
context.strokeStyle = color;
context.lineWidth = Math.max(1.35, 1.9 * scale);
context.setLineDash(dashed ? [6, 4] : []);
context.strokeRect(box.x, box.y, box.width, box.height);
context.setLineDash([]);
return box;
};
const drawLabel = (label: string | null, box: LabelRect, color: string) => {
if (!label) return;
const fontSize = Math.max(9, 10 * scale);
const labelHeight = fontSize + 7;
context.font = `600 ${fontSize}px Inter, system-ui, sans-serif`;
const labelWidth = context.measureText(label).width + 12;
const placement = placeLabel(
box,
labelWidth,
labelHeight,
imageBounds,
occupied,
);
if (!placement) return;
occupied.push(placement);
context.fillStyle = colors.label;
context.fillRect(
placement.x,
placement.y,
placement.width,
placement.height,
);
context.fillStyle = color;
context.fillText(label, placement.x + 6, placement.y + fontSize + 1);
};
auditCase.predictions.forEach((prediction) => {
const color = prediction.verdict === "true_positive"
? colors.tp
: prediction.verdict === "class_mismatch"
? colors.mismatch
: colors.fp;
const box = drawBox(prediction.boxXyxy, color, false);
drawLabel(
predictionLabel(
prediction,
prediction.matchedObjectId
? annotationById.get(prediction.matchedObjectId)
: undefined,
),
box,
color,
);
});
auditCase.annotations.forEach((annotation) => {
if (annotation.verdict === "true_positive") return;
const color = annotation.verdict === "class_mismatch"
? colors.mismatch
: colors.fn;
const box = drawBox(annotation.boxXyxy, color, true);
drawLabel(annotationLabel(annotation), box, color);
});
};
const observer = new ResizeObserver(render);
observer.observe(host);
render();
return () => observer.disconnect();
}, [auditCase, errorsVisible, image]);
return (
<div className="l32-camera-scene" ref={hostRef}>
<canvas
ref={canvasRef}
role="img"
aria-label={`L3.4A assisted error case frame ${auditCase.frameIndex}: ${auditCase.summary.truePositive} TP, ${auditCase.summary.falsePositive} FP, ${auditCase.summary.falseNegative} FN`}
/>
{error ? (
<div className="l3-visual-audit__state" role="status">
Точный кадр правой камеры L3.4A недоступен.
</div>
) : null}
</div>
);
}
@@ -0,0 +1,153 @@
import { useEffect, useMemo, useState } from "react";
import { Icon, IconButton, Select } from "@nodedc/ui-react";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
import {
fetchL34AAuditCase,
type L34AAssistedYoloxErrorAuditResult,
type L34AAuditCase,
type L34ACaseSummary,
} from "../../core/laboratory/l34aAssistedYoloxErrorAudit";
import { L34AAssistedYoloxErrorScene } from "./L34AAssistedYoloxErrorScene";
type ViewerMode = "errors" | "source";
function caseLabel(item: L34ACaseSummary): string {
const metrics = item.summary;
return `${item.truthIslandSequence}/32 · frame ${item.frameIndex} · ${item.groupId} · ${metrics.falsePositive} FP · ${metrics.falseNegative} FN · ${metrics.classMismatch} class`;
}
export function L34AAssistedYoloxErrorVisual({
result,
}: {
result: L34AAssistedYoloxErrorAuditResult;
}) {
const orderedCases = useMemo(() => {
const bySequence = new Map(
result.cases.map((item) => [item.truthIslandSequence, item]),
);
return result.caseOrder.map((sequence) => bySequence.get(sequence)).filter(
(item): item is L34ACaseSummary => item !== undefined,
);
}, [result.caseOrder, result.cases]);
const [selectedSequence, setSelectedSequence] = useState(
orderedCases[0]?.truthIslandSequence ?? 1,
);
const [auditCase, setAuditCase] = useState<L34AAuditCase | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [mode, setMode] = useState<ViewerMode>("errors");
const [expanded, setExpanded] = useState(false);
useEffect(() => {
const controller = new AbortController();
setAuditCase(null);
setLoading(true);
setError(null);
void fetchL34AAuditCase(result.resultId, selectedSequence, {
signal: controller.signal,
}).then((next) => {
if (!controller.signal.aborted) setAuditCase(next);
}).catch((caught: unknown) => {
if (!controller.signal.aborted) {
setError(
caught instanceof Error
? caught.message
: "Визуальный кейс L3.4A недоступен.",
);
}
}).finally(() => {
if (!controller.signal.aborted) setLoading(false);
});
return () => controller.abort();
}, [result.resultId, selectedSequence]);
const selectedIndex = orderedCases.findIndex(
({ truthIslandSequence }) => truthIslandSequence === selectedSequence,
);
const navigate = (offset: -1 | 1) => {
if (selectedIndex < 0 || !orderedCases.length) return;
const index = (selectedIndex + offset + orderedCases.length) % orderedCases.length;
setSelectedSequence(orderedCases[index].truthIslandSequence);
};
const actions = (
<div className="l3-visual-audit__actions">
<div className="l3-visual-audit__pagination">
<IconButton label="Предыдущий error-case L3.4A" onClick={() => navigate(-1)}>
<Icon name="chevron-left" size={16} />
</IconButton>
<IconButton label="Следующий error-case L3.4A" onClick={() => navigate(1)}>
<Icon name="chevron-right" size={16} />
</IconButton>
</div>
<Select
label="Выбрать error-case L3.4A"
value={String(selectedSequence)}
options={orderedCases.map((item) => ({
value: String(item.truthIslandSequence),
label: caseLabel(item),
}))}
variant="split"
menuWidth="anchor"
searchable
searchPlaceholder="Найти кадр, группу или ошибку"
onChange={(value) => setSelectedSequence(Number(value))}
/>
</div>
);
const overlay = auditCase ? (
<div className="l3-visual-audit__overlay">
<div>
<span>RAVNOVES00 · sensor.camera.right</span>
<strong>{auditCase.sessionSeconds.toLocaleString("ru-RU", { maximumFractionDigits: 1 })} с · frame {auditCase.frameIndex}</strong>
<small>Sequence {auditCase.truthIslandSequence}/32 · {auditCase.groupId}</small>
</div>
<div>
<span>Assisted IoU 0.50 diagnostic</span>
<strong>{auditCase.summary.truePositive} TP · {auditCase.summary.falsePositive} FP · {auditCase.summary.falseNegative} FN</strong>
<small>{auditCase.summary.classMismatch} class mismatch · {auditCase.summary.duplicateFalsePositive} duplicate</small>
</div>
<div className="l3-visual-audit__legend">
<span data-tone="tp">Зелёный · совпало</span>
<span data-tone="fp">Красный · FP/дубль</span>
<span data-tone="fn">Жёлтый пунктир · FN</span>
<span data-tone="prediction">Акцент · неверный класс</span>
</div>
</div>
) : undefined;
return (
<div className="l3-visual-audit">
<LaboratoryEvidenceViewer
label="L3.4A assisted YOLOX error audit"
mode={mode}
modes={[
{ value: "errors", label: "ERRORS" },
{ value: "source", label: "SOURCE" },
]}
expanded={expanded}
onModeChange={setMode}
onExpandedChange={setExpanded}
actions={actions}
overlay={overlay}
>
{loading ? (
<div className="l3-visual-audit__state" role="status">
<span className="busy-indicator" aria-hidden="true" />
<span>Открываем hash-bound кадр и error layers</span>
</div>
) : error || !auditCase ? (
<div className="l3-visual-audit__state" role="status">
<Icon name="alert" size={18} />
<span>{error ?? "Визуальный кейс L3.4A недоступен."}</span>
</div>
) : (
<L34AAssistedYoloxErrorScene
auditCase={auditCase}
errorsVisible={mode === "errors"}
/>
)}
</LaboratoryEvidenceViewer>
</div>
);
}
@@ -0,0 +1,66 @@
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import type { L34BResult } from "../../core/laboratory/l34bNestedBoxConsolidation";
import { formatNumber } from "../../presentation";
import { L34BNestedBoxConsolidationVisual } from "./L34BNestedBoxConsolidationVisual";
function formatPercent(value: number): string {
return `${(value * 100).toLocaleString("ru-RU", { maximumFractionDigits: 1 })}%`;
}
export function L34BNestedBoxConsolidationResultView({ rigLabel, result }: { rigLabel: string; result: L34BResult }) {
const { before, after } = result.metrics;
return (
<LaboratoryWorkTemplate
summary={<LaboratorySummary
title="LAB L3.4B · nested-box consolidation shadow"
description="Ограниченная post-normalization проверка: только почти полностью вложенные рамки одного класса объединяются; global IoU NMS не меняется."
status="Regression-free assisted shadow · не truth"
statusTone="success"
facts={[
{ label: "Конфигурация", value: `${rigLabel} · RIGHT camera · recorded replay` },
{ label: "Правило", value: "Same class · overlap-over-smaller ≥ 0.95 · union box" },
{ label: "Покрытие", value: `${formatNumber(after.frameCount, 0)} кадров · ${formatNumber(result.metrics.affectedCaseCount, 0)} изменён` },
{ label: "Полномочия", value: "Shadow post-processing · candidate не принят" },
]}
brief={{
question: "Можно ли убрать подтверждённые вложенные рамки, не потеряв корректные объекты?",
approach: "К точным 268 L3.4 predictions применено одно детерминированное правило: совпадающий нормализованный класс и покрытие меньшей рамки не менее 95%. Геометрия объединяется, score берётся максимальный; результат повторно сопоставляется с той же assisted review.",
principalResult: `${formatNumber(before.predictionCount, 0)}${formatNumber(after.predictionCount, 0)} рамок, ${formatNumber(before.falsePositive, 0)}${formatNumber(after.falsePositive, 0)} assisted FP; TP ${formatNumber(after.truePositive, 0)} и FN ${formatNumber(after.falseNegative, 0)} не изменились.`,
limitation: "Это source-scoped assisted shadow, а не blind accuracy. Четыре left/front seam split и semantic stroller collision этим правилом не исправляются.",
}}
method={{
completeness: "complete",
executionClass: "deterministic",
pipelineId: result.pipelineId,
components: [
{ kind: "source", name: "L3.4 immutable freeze", version: "268 normalized YOLOX boxes", role: "before substrate", identitySha256: null },
{ kind: "source", name: "L3.4A assisted audit", version: "32 reviewed frames · not truth", role: "diagnostic comparison", identitySha256: null },
{ kind: "algorithm", name: result.profile.profileId, version: "same class · overlap 0.95 · union", role: "nested-box consolidation shadow", identitySha256: result.resultId.split("-").at(-1) ?? null },
],
}}
/>}
evidence={<LaboratoryEvidence eyebrow="POST-PROCESSING SHADOW · BEFORE / AFTER / SOURCE" title="Одна вложенная пара объединена без assisted-регрессии" kind="diagnostic-model" resizable><L34BNestedBoxConsolidationVisual result={result} /></LaboratoryEvidence>}
result={<LaboratoryResultSummary
title="Узкое nested-box правило прошло; глобальный NMS — нет"
status="Bounded shadow accepted · not candidate acceptance"
statusTone="success"
metrics={[
{ label: "Assisted precision", value: `${formatPercent(before.precisionIou50)}${formatPercent(after.precisionIou50)}`, hint: "+0,3 п.п. · не blind" },
{ label: "Assisted recall", value: formatPercent(after.recallIou50), hint: "Без изменения" },
{ label: "FP", value: `${formatNumber(before.falsePositive, 0)}${formatNumber(after.falsePositive, 0)}`, hint: "Одна рамка удалена объединением" },
{ label: "Осталось seam-сигналов", value: formatNumber(result.decision.remainingL34aDuplicateSignals, 0), hint: "Нужен tile-aware stitching" },
]}
conclusion={{
proved: "На текущих 32 записанных кадрах правило вложенности ≥95% объединяет ровно одну пару одного класса и не ухудшает assisted TP, FN, recall или class mismatch. BEFORE/AFTER связан с исходным кадром.",
notProved: "Не доказаны blind AP/recall, перенос на другой маршрут, live/hardware, LiDAR range, navigation или safety. Четыре seam split не являются обычными IoU-дублями.",
decision: "Оставить nested-box consolidation как bounded shadow. Не снижать глобальный IoU NMS: тест 0.30 удалял корректные соседние машины. Следующая работа — сохранить rectification_tile в prediction contract и проверить temporal left/front seam stitching.",
}}
/>}
/>
);
}
@@ -0,0 +1,115 @@
import { useEffect, useRef, useState } from "react";
import type { L34BCase } from "../../core/laboratory/l34bNestedBoxConsolidation";
type SceneMode = "after" | "before" | "source";
function color(host: HTMLElement, token: string, fallback: readonly [number, number, number], alpha = 1): string {
const value = getComputedStyle(host).getPropertyValue(token).trim();
const channels = value.match(/[\d.]+/g)?.slice(0, 3).map(Number);
const [red, green, blue] = channels?.length === 3 ? channels : fallback;
return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
}
export function L34BNestedBoxConsolidationScene({
shadowCase,
mode,
}: {
shadowCase: L34BCase;
mode: SceneMode;
}) {
const hostRef = useRef<HTMLDivElement | null>(null);
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const [image, setImage] = useState<HTMLImageElement | null>(null);
const [failed, setFailed] = useState(false);
useEffect(() => {
const next = new Image();
next.decoding = "async";
next.onload = () => { setImage(next); setFailed(false); };
next.onerror = () => { setImage(null); setFailed(true); };
next.src = shadowCase.cameraUrl;
return () => { next.onload = null; next.onerror = null; };
}, [shadowCase.cameraUrl]);
useEffect(() => {
const host = hostRef.current;
const canvas = canvasRef.current;
if (!host || !canvas || !image) return;
const context = canvas.getContext("2d");
if (!context) return;
const render = () => {
const width = Math.max(host.clientWidth, 1);
const height = Math.max(host.clientHeight, 1);
const ratio = Math.min(window.devicePixelRatio, 1.5);
canvas.width = Math.round(width * ratio);
canvas.height = Math.round(height * ratio);
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
context.setTransform(ratio, 0, 0, ratio, 0, 0);
context.fillStyle = color(host, "--nodedc-canvas-rgb", [5, 5, 6]);
context.fillRect(0, 0, width, height);
const scale = Math.min(width / shadowCase.cameraWidth, height / shadowCase.cameraHeight);
const drawWidth = shadowCase.cameraWidth * scale;
const drawHeight = shadowCase.cameraHeight * scale;
const offsetX = (width - drawWidth) / 2;
const offsetY = (height - drawHeight) / 2;
context.drawImage(image, offsetX, offsetY, drawWidth, drawHeight);
if (mode === "source") return;
const sourceIndices = new Set(shadowCase.consolidations.flatMap((item) => item.sourcePredictionIndices));
const mergedIndices = new Set(shadowCase.consolidations.map((item) => item.outputPredictionIndex));
const predictions = mode === "before" ? shadowCase.beforePredictions : shadowCase.afterPredictions;
let highlightedLabelIndex = 0;
predictions.forEach((prediction) => {
const highlighted = mode === "before"
? sourceIndices.has(prediction.predictionIndex)
: mergedIndices.has(prediction.predictionIndex);
const [left, top, right, bottom] = prediction.boxXyxy;
const x = offsetX + left * scale;
const y = offsetY + top * scale;
const boxWidth = (right - left) * scale;
const boxHeight = (bottom - top) * scale;
const stroke = highlighted
? mode === "before"
? color(host, "--nodedc-danger-rgb", [255, 116, 116], 1)
: color(host, "--nodedc-success-rgb", [181, 255, 90], 1)
: color(host, "--nodedc-foreground-rgb", [240, 240, 240], 0.46);
context.strokeStyle = stroke;
context.lineWidth = highlighted ? Math.max(2, 2.7 * scale) : Math.max(1, 1.2 * scale);
context.setLineDash(mode === "before" && highlighted ? [7, 4] : []);
context.strokeRect(x, y, boxWidth, boxHeight);
context.setLineDash([]);
if (!highlighted) return;
const label = `${mode === "before" ? "BEFORE" : "AFTER"} · ${prediction.category} · ${(prediction.score * 100).toFixed(0)}%`;
const fontSize = Math.max(10, 11 * scale);
context.font = `600 ${fontSize}px Inter, system-ui, sans-serif`;
const labelWidth = context.measureText(label).width + 12;
const labelHeight = fontSize + 8;
const labelX = Math.min(
offsetX + drawWidth - labelWidth,
Math.max(offsetX, x),
);
const labelY = Math.max(
offsetY,
y - labelHeight - 3 - highlightedLabelIndex * (labelHeight + 3),
);
highlightedLabelIndex += 1;
context.fillStyle = color(host, "--nodedc-canvas-rgb", [5, 5, 6], 0.9);
context.fillRect(labelX, labelY, labelWidth, labelHeight);
context.fillStyle = stroke;
context.fillText(label, labelX + 6, labelY + fontSize + 1);
});
};
const observer = new ResizeObserver(render);
observer.observe(host);
render();
return () => observer.disconnect();
}, [image, mode, shadowCase]);
return (
<div className="l32-camera-scene" ref={hostRef}>
<canvas ref={canvasRef} role="img" aria-label={`L3.4B frame ${shadowCase.frameIndex}: ${mode} nested-box consolidation`} />
{failed ? <div className="l3-visual-audit__state" role="status">Точный кадр L3.4B недоступен.</div> : null}
</div>
);
}
@@ -0,0 +1,92 @@
import { useEffect, useMemo, useState } from "react";
import { Icon, IconButton, Select } from "@nodedc/ui-react";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
import {
fetchL34BCase,
type L34BCase,
type L34BCaseSummary,
type L34BResult,
} from "../../core/laboratory/l34bNestedBoxConsolidation";
import { L34BNestedBoxConsolidationScene } from "./L34BNestedBoxConsolidationScene";
type ViewerMode = "after" | "before" | "source";
function caseLabel(item: L34BCaseSummary): string {
return `${item.truthIslandSequence}/32 · frame ${item.frameIndex} · ${item.groupId} · ${item.consolidationCount ? "CONSOLIDATED" : "UNCHANGED"}`;
}
export function L34BNestedBoxConsolidationVisual({ result }: { result: L34BResult }) {
const orderedCases = useMemo(() => {
const bySequence = new Map(result.cases.map((item) => [item.truthIslandSequence, item]));
return result.caseOrder.map((sequence) => bySequence.get(sequence)).filter((item): item is L34BCaseSummary => item !== undefined);
}, [result.caseOrder, result.cases]);
const [selectedSequence, setSelectedSequence] = useState(orderedCases[0]?.truthIslandSequence ?? 1);
const [shadowCase, setShadowCase] = useState<L34BCase | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [mode, setMode] = useState<ViewerMode>("after");
const [expanded, setExpanded] = useState(false);
useEffect(() => {
const controller = new AbortController();
setLoading(true);
setError(null);
setShadowCase(null);
void fetchL34BCase(result.resultId, selectedSequence, { signal: controller.signal })
.then((next) => { if (!controller.signal.aborted) setShadowCase(next); })
.catch((caught: unknown) => { if (!controller.signal.aborted) setError(caught instanceof Error ? caught.message : "Визуальный кейс L3.4B недоступен."); })
.finally(() => { if (!controller.signal.aborted) setLoading(false); });
return () => controller.abort();
}, [result.resultId, selectedSequence]);
const selectedIndex = orderedCases.findIndex((item) => item.truthIslandSequence === selectedSequence);
const navigate = (offset: -1 | 1) => {
if (selectedIndex < 0 || !orderedCases.length) return;
setSelectedSequence(orderedCases[(selectedIndex + offset + orderedCases.length) % orderedCases.length].truthIslandSequence);
};
const actions = (
<div className="l3-visual-audit__actions">
<div className="l3-visual-audit__pagination">
<IconButton label="Предыдущий L3.4B case" onClick={() => navigate(-1)}><Icon name="chevron-left" size={16} /></IconButton>
<IconButton label="Следующий L3.4B case" onClick={() => navigate(1)}><Icon name="chevron-right" size={16} /></IconButton>
</div>
<Select
label="Выбрать L3.4B case"
value={String(selectedSequence)}
options={orderedCases.map((item) => ({ value: String(item.truthIslandSequence), label: caseLabel(item) }))}
variant="split"
menuWidth="anchor"
searchable
searchPlaceholder="Найти кадр или группу"
onChange={(value) => setSelectedSequence(Number(value))}
/>
</div>
);
const overlay = shadowCase ? (
<div className="l3-visual-audit__overlay">
<div><span>RAVNOVES00 · sensor.camera.right</span><strong>{shadowCase.sessionSeconds.toLocaleString("ru-RU", { maximumFractionDigits: 1 })} с · frame {shadowCase.frameIndex}</strong><small>Sequence {shadowCase.truthIslandSequence}/32 · {shadowCase.groupId}</small></div>
<div><span>Nested same-class overlap 95%</span><strong>{shadowCase.beforeSummary.predictionCount} {shadowCase.afterSummary.predictionCount} boxes</strong><small>{shadowCase.beforeSummary.falsePositive} {shadowCase.afterSummary.falsePositive} assisted FP · recall unchanged</small></div>
<div className="l3-visual-audit__legend"><span data-tone="fp">Красный пунктир · исходные вложенные рамки</span><span data-tone="tp">Зелёный · объединённая геометрия</span><span data-tone="prediction">SOURCE · чистый hash-bound кадр</span></div>
</div>
) : undefined;
return (
<div className="l3-visual-audit">
<LaboratoryEvidenceViewer
label="L3.4B nested-box consolidation before/after"
mode={mode}
modes={[{ value: "after", label: "AFTER" }, { value: "before", label: "BEFORE" }, { value: "source", label: "SOURCE" }]}
expanded={expanded}
onModeChange={setMode}
onExpandedChange={setExpanded}
actions={actions}
overlay={overlay}
>
{loading ? <div className="l3-visual-audit__state" role="status"><span className="busy-indicator" aria-hidden="true" /><span>Открываем BEFORE/AFTER evidence</span></div>
: error || !shadowCase ? <div className="l3-visual-audit__state" role="status"><Icon name="alert" size={18} /><span>{error ?? "Визуальный кейс L3.4B недоступен."}</span></div>
: <L34BNestedBoxConsolidationScene shadowCase={shadowCase} mode={mode} />}
</LaboratoryEvidenceViewer>
</div>
);
}
@@ -0,0 +1,79 @@
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import type { L34CResult } from "../../core/laboratory/l34cTileSeamStitch";
import { formatNumber } from "../../presentation";
import { L34CTileSeamStitchVisual } from "./L34CTileSeamStitchVisual";
function formatPercent(value: number): string {
return `${(value * 100).toLocaleString("ru-RU", { maximumFractionDigits: 1 })}%`;
}
export function L34CTileSeamStitchResultView({
rigLabel,
result,
}: {
rigLabel: string;
result: L34CResult;
}) {
const { before, after } = result.metrics;
return (
<LaboratoryWorkTemplate
summary={<LaboratorySummary
title="LAB L3.4C · temporal tile-seam stitch shadow"
description="Tile provenance восстановлен exact join с qualification; front/left split объединяется только при устойчивой геометрии минимум на трёх последовательных кадрах."
status="Regression-free assisted shadow · не truth"
statusTone="success"
facts={[
{ label: "Конфигурация", value: `${rigLabel} · RIGHT camera · recorded replay` },
{ label: "Provenance", value: "Exact L3.4 ↔ qualification · front / left tile" },
{ label: "Временной контракт", value: `${result.profile.minimumConsecutiveFrames} кадров · union IoU ≥${result.profile.temporalUnionIouThreshold.toLocaleString("ru-RU")}` },
{ label: "Полномочия", value: "Shadow post-processing · candidate не принят" },
]}
brief={{
question: "Можно ли убрать устойчивый разрыв одной машины между front и left rectification tiles, не склеивая соседние объекты?",
approach: "Каждая из 268 frozen YOLOX-рамок exact-join связана с raw label, class id, center и rectification tile из immutable qualification. Склейка допускается только для одного класса, front/left, крупной пересекающейся seam-геометрии и серии минимум из трёх последовательных кадров с union-IoU не ниже 0,80.",
principalResult: `${formatNumber(before.predictionCount, 0)}${formatNumber(after.predictionCount, 0)} рамок, ${formatNumber(before.falsePositive, 0)}${formatNumber(after.falsePositive, 0)} assisted FP и ${formatNumber(before.duplicateFalsePositive, 0)}${formatNumber(after.duplicateFalsePositive, 0)} duplicate-сигналов; TP и FN не изменились.`,
limitation: "Это source-scoped assisted shadow, а не blind accuracy. Подтверждён только один четырёхкадровый front/left run; одиночные случаи, front/right, другой маршрут и live/hardware не допущены.",
}}
method={{
completeness: "complete",
executionClass: "deterministic",
pipelineId: result.pipelineId,
components: [
{ kind: "source", name: "L3.4 immutable freeze", version: "268 normalized YOLOX boxes", role: "prediction substrate", identitySha256: null },
{ kind: "source", name: "rectified YOLOX qualification", version: "4489 frames", role: "exact tile provenance", identitySha256: null },
{ kind: "algorithm", name: result.profile.profileId, version: "front/left · temporal ≥3 · union", role: "fail-closed seam stitch shadow", identitySha256: result.resultId.split("-").at(-1) ?? null },
],
}}
/>}
evidence={<LaboratoryEvidence
eyebrow="TILE-AWARE SHADOW · BEFORE / AFTER / SOURCE"
title="Одна front/left seam-композиция подтверждена на четырёх кадрах"
kind="diagnostic-model"
resizable
>
<L34CTileSeamStitchVisual result={result} />
</LaboratoryEvidence>}
result={<LaboratoryResultSummary
title="Temporal front/left stitch прошёл assisted-проверку"
status="Bounded shadow accepted · not candidate acceptance"
statusTone="success"
metrics={[
{ label: "Assisted precision", value: `${formatPercent(before.precisionIou50)}${formatPercent(after.precisionIou50)}`, hint: "+1,3 п.п. · не blind" },
{ label: "Assisted recall", value: formatPercent(after.recallIou50), hint: "Без изменения" },
{ label: "Duplicate FP", value: `${formatNumber(before.duplicateFalsePositive, 0)}${formatNumber(after.duplicateFalsePositive, 0)}`, hint: "Четыре seam-рамки объединены" },
{ label: "Temporal admission", value: `${formatNumber(result.metrics.stitchCount, 0)} / ${formatNumber(result.metrics.staticCandidateCount, 0)}`, hint: "Один статический кандидат отклонён" },
]}
conclusion={{
proved: "На текущих 32 записанных RIGHT-кадрах exact tile provenance однозначно восстанавливается для каждой frozen prediction. Один front/left seam-run на кадрах 2620–2623 проходит временной контракт; четыре duplicate FP исчезают без ухудшения assisted TP, FN, recall или class mismatch.",
notProved: "Не доказаны blind AP/recall, перенос на front/right, другой маршрут, live/hardware, LiDAR range, navigation или safety. Assisted review не является независимой truth.",
decision: "Принять temporal seam stitch как отдельный bounded shadow. Глобальный NMS оставить неизменным. Следом композиционно проверить принятые L3.4B nested-box и L3.4C seam правила и только затем замораживать единый cumulative candidate.",
}}
/>}
/>
);
}
@@ -0,0 +1,152 @@
import { useEffect, useRef, useState } from "react";
import type { L34CCase } from "../../core/laboratory/l34cTileSeamStitch";
type SceneMode = "after" | "before" | "source";
function color(
host: HTMLElement,
token: string,
fallback: readonly [number, number, number],
alpha = 1,
): string {
const value = getComputedStyle(host).getPropertyValue(token).trim();
const channels = value.match(/[\d.]+/g)?.slice(0, 3).map(Number);
const [red, green, blue] = channels?.length === 3 ? channels : fallback;
return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
}
export function L34CTileSeamStitchScene({
shadowCase,
mode,
}: {
shadowCase: L34CCase;
mode: SceneMode;
}) {
const hostRef = useRef<HTMLDivElement | null>(null);
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const [image, setImage] = useState<HTMLImageElement | null>(null);
const [failed, setFailed] = useState(false);
useEffect(() => {
const next = new Image();
next.decoding = "async";
next.onload = () => { setImage(next); setFailed(false); };
next.onerror = () => { setImage(null); setFailed(true); };
next.src = shadowCase.cameraUrl;
return () => { next.onload = null; next.onerror = null; };
}, [shadowCase.cameraUrl]);
useEffect(() => {
const host = hostRef.current;
const canvas = canvasRef.current;
if (!host || !canvas || !image) return;
const context = canvas.getContext("2d");
if (!context) return;
const render = () => {
const width = Math.max(host.clientWidth, 1);
const height = Math.max(host.clientHeight, 1);
const ratio = Math.min(window.devicePixelRatio, 1.5);
canvas.width = Math.round(width * ratio);
canvas.height = Math.round(height * ratio);
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
context.setTransform(ratio, 0, 0, ratio, 0, 0);
context.fillStyle = color(host, "--nodedc-canvas-rgb", [5, 5, 6]);
context.fillRect(0, 0, width, height);
const scale = Math.min(
width / shadowCase.cameraWidth,
height / shadowCase.cameraHeight,
);
const drawWidth = shadowCase.cameraWidth * scale;
const drawHeight = shadowCase.cameraHeight * scale;
const offsetX = (width - drawWidth) / 2;
const offsetY = (height - drawHeight) / 2;
context.drawImage(image, offsetX, offsetY, drawWidth, drawHeight);
if (mode === "source") return;
const sourceIndices = new Set(
shadowCase.stitches.flatMap((item) => item.sourcePredictionIndices),
);
const predictions = mode === "before"
? shadowCase.beforePredictions
: shadowCase.afterPredictions;
const mergedIndices = new Set(
predictions
.filter((prediction) => (
"sourcePredictionIndices" in prediction
&& prediction.sourcePredictionIndices.length > 1
))
.map((prediction) => prediction.predictionIndex),
);
let highlightedLabelIndex = 0;
predictions.forEach((prediction) => {
const highlighted = mode === "before"
? sourceIndices.has(prediction.predictionIndex)
: mergedIndices.has(prediction.predictionIndex);
const [left, top, right, bottom] = prediction.boxXyxy;
const x = offsetX + left * scale;
const y = offsetY + top * scale;
const boxWidth = (right - left) * scale;
const boxHeight = (bottom - top) * scale;
const tile = "rectificationTile" in prediction
? prediction.rectificationTile
: prediction.sourceRectificationTiles.join("+");
const stroke = highlighted
? mode === "after"
? color(host, "--nodedc-success-rgb", [181, 255, 90])
: tile === "front"
? color(host, "--nodedc-accent-rgb", [232, 56, 126])
: color(host, "--nodedc-warning-rgb", [255, 197, 92])
: color(host, "--nodedc-foreground-rgb", [240, 240, 240], 0.35);
context.strokeStyle = stroke;
context.lineWidth = highlighted
? Math.max(2, 2.7 * scale)
: Math.max(1, 1.1 * scale);
context.setLineDash(mode === "before" && highlighted ? [7, 4] : []);
context.strokeRect(x, y, boxWidth, boxHeight);
context.setLineDash([]);
if (!highlighted) return;
const label = mode === "before"
? `BEFORE · ${tile} · ${prediction.category} · ${(prediction.score * 100).toFixed(0)}%`
: `AFTER · front+left union · ${prediction.category} · ${(prediction.score * 100).toFixed(0)}%`;
const fontSize = Math.max(10, 11 * scale);
context.font = `600 ${fontSize}px Inter, system-ui, sans-serif`;
const labelWidth = context.measureText(label).width + 12;
const labelHeight = fontSize + 8;
const labelX = Math.min(
offsetX + drawWidth - labelWidth,
Math.max(offsetX, x),
);
const labelY = Math.max(
offsetY,
y - labelHeight - 3 - highlightedLabelIndex * (labelHeight + 3),
);
highlightedLabelIndex += 1;
context.fillStyle = color(host, "--nodedc-canvas-rgb", [5, 5, 6], 0.9);
context.fillRect(labelX, labelY, labelWidth, labelHeight);
context.fillStyle = stroke;
context.fillText(label, labelX + 6, labelY + fontSize + 1);
});
};
const observer = new ResizeObserver(render);
observer.observe(host);
render();
return () => observer.disconnect();
}, [image, mode, shadowCase]);
return (
<div className="l32-camera-scene" ref={hostRef}>
<canvas
ref={canvasRef}
role="img"
aria-label={`L3.4C frame ${shadowCase.frameIndex}: ${mode} tile-seam stitch`}
/>
{failed ? (
<div className="l3-visual-audit__state" role="status">
Точный кадр L3.4C недоступен.
</div>
) : null}
</div>
);
}
@@ -0,0 +1,142 @@
import { useEffect, useMemo, useState } from "react";
import { Icon, IconButton, Select } from "@nodedc/ui-react";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
import {
fetchL34CCase,
type L34CCase,
type L34CCaseSummary,
type L34CResult,
} from "../../core/laboratory/l34cTileSeamStitch";
import { L34CTileSeamStitchScene } from "./L34CTileSeamStitchScene";
type ViewerMode = "after" | "before" | "source";
function caseLabel(item: L34CCaseSummary): string {
return `${item.truthIslandSequence}/32 · frame ${item.frameIndex} · ${item.groupId} · ${item.stitchCount ? "STITCHED" : "UNCHANGED"}`;
}
export function L34CTileSeamStitchVisual({ result }: { result: L34CResult }) {
const orderedCases = useMemo(() => {
const bySequence = new Map(result.cases.map((item) => [item.truthIslandSequence, item]));
return result.caseOrder
.map((sequence) => bySequence.get(sequence))
.filter((item): item is L34CCaseSummary => item !== undefined);
}, [result.caseOrder, result.cases]);
const [selectedSequence, setSelectedSequence] = useState(
orderedCases[0]?.truthIslandSequence ?? 1,
);
const [shadowCase, setShadowCase] = useState<L34CCase | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [mode, setMode] = useState<ViewerMode>("after");
const [expanded, setExpanded] = useState(false);
useEffect(() => {
const controller = new AbortController();
setLoading(true);
setError(null);
setShadowCase(null);
void fetchL34CCase(result.resultId, selectedSequence, {
signal: controller.signal,
}).then((next) => {
if (!controller.signal.aborted) setShadowCase(next);
}).catch((caught: unknown) => {
if (!controller.signal.aborted) {
setError(caught instanceof Error
? caught.message
: "Визуальный кейс L3.4C недоступен.");
}
}).finally(() => {
if (!controller.signal.aborted) setLoading(false);
});
return () => controller.abort();
}, [result.resultId, selectedSequence]);
const selectedIndex = orderedCases.findIndex(
(item) => item.truthIslandSequence === selectedSequence,
);
const navigate = (offset: -1 | 1) => {
if (selectedIndex < 0 || !orderedCases.length) return;
const next = (selectedIndex + offset + orderedCases.length) % orderedCases.length;
setSelectedSequence(orderedCases[next].truthIslandSequence);
};
const actions = (
<div className="l3-visual-audit__actions">
<div className="l3-visual-audit__pagination">
<IconButton label="Предыдущий L3.4C case" onClick={() => navigate(-1)}>
<Icon name="chevron-left" size={16} />
</IconButton>
<IconButton label="Следующий L3.4C case" onClick={() => navigate(1)}>
<Icon name="chevron-right" size={16} />
</IconButton>
</div>
<Select
label="Выбрать L3.4C case"
value={String(selectedSequence)}
options={orderedCases.map((item) => ({
value: String(item.truthIslandSequence),
label: caseLabel(item),
}))}
variant="split"
menuWidth="anchor"
searchable
searchPlaceholder="Найти кадр или группу"
onChange={(value) => setSelectedSequence(Number(value))}
/>
</div>
);
const stitch = shadowCase?.stitches[0];
const overlay = shadowCase ? (
<div className="l3-visual-audit__overlay">
<div>
<span>RAVNOVES00 · sensor.camera.right</span>
<strong>{shadowCase.sessionSeconds.toLocaleString("ru-RU", { maximumFractionDigits: 1 })} с · frame {shadowCase.frameIndex}</strong>
<small>Sequence {shadowCase.truthIslandSequence}/32 · {shadowCase.groupId}</small>
</div>
<div>
<span>Exact tile provenance · temporal seam contract</span>
<strong>{stitch ? `${stitch.sourceTiles.join(" + ")} → union` : "UNCHANGED"}</strong>
<small>{stitch ? `${stitch.temporalRunLength} consecutive frames · source IoU ${stitch.sourceIou.toLocaleString("ru-RU", { maximumFractionDigits: 2 })}` : "Не прошёл fail-closed admission"}</small>
</div>
<div className="l3-visual-audit__legend">
<span data-tone="fp">BEFORE · front и left source boxes</span>
<span data-tone="tp">AFTER · единая union geometry</span>
<span data-tone="prediction">SOURCE · чистый hash-bound кадр</span>
</div>
</div>
) : undefined;
return (
<div className="l3-visual-audit">
<LaboratoryEvidenceViewer
label="L3.4C temporal tile-seam stitch before/after"
mode={mode}
modes={[
{ value: "after", label: "AFTER" },
{ value: "before", label: "BEFORE" },
{ value: "source", label: "SOURCE" },
]}
expanded={expanded}
onModeChange={setMode}
onExpandedChange={setExpanded}
actions={actions}
overlay={overlay}
>
{loading ? (
<div className="l3-visual-audit__state" role="status">
<span className="busy-indicator" aria-hidden="true" />
<span>Открываем tile-aware BEFORE/AFTER evidence</span>
</div>
) : error || !shadowCase ? (
<div className="l3-visual-audit__state" role="status">
<Icon name="alert" size={18} />
<span>{error ?? "Визуальный кейс L3.4C недоступен."}</span>
</div>
) : (
<L34CTileSeamStitchScene shadowCase={shadowCase} mode={mode} />
)}
</LaboratoryEvidenceViewer>
</div>
);
}
@@ -0,0 +1,79 @@
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import type { L34DResult } from "../../core/laboratory/l34dCumulativePostprocessing";
import { formatNumber } from "../../presentation";
import { L34DCumulativePostprocessingVisual } from "./L34DCumulativePostprocessingVisual";
function formatPercent(value: number): string {
return `${(value * 100).toLocaleString("ru-RU", { maximumFractionDigits: 1 })}%`;
}
export function L34DCumulativePostprocessingResultView({
rigLabel,
result,
}: {
rigLabel: string;
result: L34DResult;
}) {
const { before, after } = result.metrics;
return (
<LaboratoryWorkTemplate
summary={<LaboratorySummary
title="LAB L3.4D · cumulative B+C candidate freeze"
description="Принятые nested-box и temporal seam операции применены одновременно к исходным L3.4 indices; любое пересечение политик отклоняет сборку."
status="Cumulative candidate frozen · не truth"
statusTone="success"
facts={[
{ label: "Конфигурация", value: `${rigLabel} · RIGHT camera · recorded replay` },
{ label: "Композиция", value: "L3.4B nested-box + L3.4C temporal seam" },
{ label: "Инвариант", value: "Original source indices · 0 policy conflicts" },
{ label: "Полномочия", value: "Candidate frozen · blind gate закрыт" },
]}
brief={{
question: "Можно ли объединить уже принятые L3.4B и L3.4C правила в один детерминированный candidate без зависимости от порядка и без assisted-регрессии?",
approach: "Операции B и C повторно связаны с исходными L3.4 prediction indices и проверены против immutable payload. Они применяются одновременно; пересечение source indices, drift рамок, score, класса или tile provenance приводит к отказу сборки.",
principalResult: `${formatNumber(before.predictionCount, 0)}${formatNumber(after.predictionCount, 0)} рамки и ${formatNumber(before.falsePositive, 0)}${formatNumber(after.falsePositive, 0)} assisted FP. Одна nested-box и четыре seam-операции дали точную сумму count-effects; TP, FN и recall не изменились.`,
limitation: "Candidate только заморожен, но не принят: сравнение использует ту же candidate-seeded assisted review. Independent prediction-hidden labels ещё не получены; live, hardware, left camera и другой маршрут не проверялись.",
}}
method={{
completeness: "complete",
executionClass: "deterministic",
pipelineId: result.pipelineId,
components: [
{ kind: "source", name: "L3.4B immutable shadow", version: "nested-box v1", role: "source-indexed consolidation operations", identitySha256: null },
{ kind: "source", name: "L3.4C immutable shadow", version: "temporal seam v1", role: "tile provenance and seam operations", identitySha256: null },
{ kind: "algorithm", name: result.profile.profileId, version: "fail-closed source-index composition", role: "freeze one cumulative post-processing candidate", identitySha256: result.resultId.split("-").at(-1) ?? null },
],
}}
/>}
evidence={<LaboratoryEvidence
eyebrow="CUMULATIVE CANDIDATE · BEFORE / AFTER / SOURCE"
title="Пять изменённых кадров: одна nested-box и четыре temporal seam операции"
kind="diagnostic-model"
resizable
>
<L34DCumulativePostprocessingVisual result={result} />
</LaboratoryEvidence>}
result={<LaboratoryResultSummary
title="B+C композиция заморожена без assisted-регрессии"
status="Frozen candidate · not independent acceptance"
statusTone="success"
metrics={[
{ label: "Assisted precision", value: `${formatPercent(before.precisionIou50)}${formatPercent(after.precisionIou50)}`, hint: "+1,7 п.п. · не blind" },
{ label: "Assisted recall", value: formatPercent(after.recallIou50), hint: "Без изменения" },
{ label: "False positive", value: `${formatNumber(before.falsePositive, 0)}${formatNumber(after.falsePositive, 0)}`, hint: "5 на 32 кадрах" },
{ label: "B / C / conflicts", value: `${result.metrics.nestedConsolidationCount} / ${result.metrics.temporalStitchCount} / ${result.metrics.operationConflictCount}`, hint: "Count-effects additive" },
]}
conclusion={{
proved: "На текущем immutable RIGHT-наборе L3.4B и L3.4C работают на непересекающихся исходных predictions. Совместная проекция воспроизводит одну nested-box и четыре temporal seam операции, убирает пять assisted FP и не меняет TP, FN, recall или class mismatch.",
notProved: "Не доказаны blind AP/recall, независимость assisted labels, перенос на другой маршрут или камеру, live/hardware, LiDAR range, navigation и safety. Замороженный candidate ещё не является принятым detector pipeline.",
decision: "Зафиксировать этот exact B+C состав как единственный cumulative candidate. Не менять правила, thresholds, global NMS или модель до одного prediction-hidden независимого label round и одноразовой оценки после раскрытия.",
}}
/>}
/>
);
}
@@ -0,0 +1,161 @@
import { useEffect, useRef, useState } from "react";
import type {
L34DCase,
L34DOperationType,
} from "../../core/laboratory/l34dCumulativePostprocessing";
type SceneMode = "after" | "before" | "source";
function color(
host: HTMLElement,
token: string,
fallback: readonly [number, number, number],
alpha = 1,
): string {
const value = getComputedStyle(host).getPropertyValue(token).trim();
const channels = value.match(/[\d.]+/g)?.slice(0, 3).map(Number);
const [red, green, blue] = channels?.length === 3 ? channels : fallback;
return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
}
function shortOperation(type: L34DOperationType | undefined): string {
return type === "nested-box-consolidation" ? "B nested union"
: type === "temporal-tile-seam-stitch" ? "C seam union"
: "unchanged";
}
export function L34DCumulativePostprocessingScene({
candidateCase,
mode,
}: {
candidateCase: L34DCase;
mode: SceneMode;
}) {
const hostRef = useRef<HTMLDivElement | null>(null);
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const [image, setImage] = useState<HTMLImageElement | null>(null);
const [failed, setFailed] = useState(false);
useEffect(() => {
const next = new Image();
next.decoding = "async";
next.onload = () => { setImage(next); setFailed(false); };
next.onerror = () => { setImage(null); setFailed(true); };
next.src = candidateCase.cameraUrl;
return () => { next.onload = null; next.onerror = null; };
}, [candidateCase.cameraUrl]);
useEffect(() => {
const host = hostRef.current;
const canvas = canvasRef.current;
if (!host || !canvas || !image) return;
const context = canvas.getContext("2d");
if (!context) return;
const render = () => {
const width = Math.max(host.clientWidth, 1);
const height = Math.max(host.clientHeight, 1);
const ratio = Math.min(window.devicePixelRatio, 1.5);
canvas.width = Math.round(width * ratio);
canvas.height = Math.round(height * ratio);
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
context.setTransform(ratio, 0, 0, ratio, 0, 0);
context.fillStyle = color(host, "--nodedc-canvas-rgb", [5, 5, 6]);
context.fillRect(0, 0, width, height);
const scale = Math.min(
width / candidateCase.cameraWidth,
height / candidateCase.cameraHeight,
);
const drawWidth = candidateCase.cameraWidth * scale;
const drawHeight = candidateCase.cameraHeight * scale;
const offsetX = (width - drawWidth) / 2;
const offsetY = (height - drawHeight) / 2;
context.drawImage(image, offsetX, offsetY, drawWidth, drawHeight);
if (mode === "source") return;
const operationBySourceIndex = new Map<number, L34DOperationType>();
candidateCase.operations.forEach((operation) => {
operation.sourcePredictionIndices.forEach((index) => {
operationBySourceIndex.set(index, operation.operationType);
});
});
const predictions = mode === "before"
? candidateCase.beforePredictions
: candidateCase.afterPredictions;
let highlightedLabelIndex = 0;
predictions.forEach((prediction) => {
const operation = mode === "before"
? operationBySourceIndex.get(prediction.predictionIndex)
: "operationTypes" in prediction
? prediction.operationTypes[0]
: undefined;
const highlighted = operation !== undefined;
const [left, top, right, bottom] = prediction.boxXyxy;
const x = offsetX + left * scale;
const y = offsetY + top * scale;
const boxWidth = (right - left) * scale;
const boxHeight = (bottom - top) * scale;
const tile = "rectificationTile" in prediction
? prediction.rectificationTile
: prediction.sourceRectificationTiles.join("+");
const stroke = highlighted
? mode === "after"
? color(host, "--nodedc-success-rgb", [181, 255, 90])
: operation === "nested-box-consolidation"
? color(host, "--nodedc-warning-rgb", [255, 197, 92])
: tile === "front"
? color(host, "--nodedc-accent-rgb", [232, 56, 126])
: color(host, "--nodedc-warning-rgb", [255, 197, 92])
: color(host, "--nodedc-foreground-rgb", [240, 240, 240], 0.35);
context.strokeStyle = stroke;
context.lineWidth = highlighted
? Math.max(2, 2.7 * scale)
: Math.max(1, 1.1 * scale);
context.setLineDash(mode === "before" && highlighted ? [7, 4] : []);
context.strokeRect(x, y, boxWidth, boxHeight);
context.setLineDash([]);
if (!highlighted) return;
const label = mode === "before"
? `BEFORE · ${operation === "nested-box-consolidation" ? "B nested" : tile} · ${prediction.category} · ${(prediction.score * 100).toFixed(0)}%`
: `AFTER · ${shortOperation(operation)} · ${prediction.category} · ${(prediction.score * 100).toFixed(0)}%`;
const fontSize = Math.max(10, 11 * scale);
context.font = `600 ${fontSize}px Inter, system-ui, sans-serif`;
const labelWidth = context.measureText(label).width + 12;
const labelHeight = fontSize + 8;
const labelX = Math.min(
offsetX + drawWidth - labelWidth,
Math.max(offsetX, x),
);
const labelY = Math.max(
offsetY,
y - labelHeight - 3 - highlightedLabelIndex * (labelHeight + 3),
);
highlightedLabelIndex += 1;
context.fillStyle = color(host, "--nodedc-canvas-rgb", [5, 5, 6], 0.9);
context.fillRect(labelX, labelY, labelWidth, labelHeight);
context.fillStyle = stroke;
context.fillText(label, labelX + 6, labelY + fontSize + 1);
});
};
const observer = new ResizeObserver(render);
observer.observe(host);
render();
return () => observer.disconnect();
}, [candidateCase, image, mode]);
return (
<div className="l32-camera-scene" ref={hostRef}>
<canvas
ref={canvasRef}
role="img"
aria-label={`L3.4D frame ${candidateCase.frameIndex}: ${mode} cumulative candidate`}
/>
{failed ? (
<div className="l3-visual-audit__state" role="status">
Точный кадр L3.4D недоступен.
</div>
) : null}
</div>
);
}
@@ -0,0 +1,173 @@
import { useEffect, useMemo, useState } from "react";
import { Icon, IconButton, Select } from "@nodedc/ui-react";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
import {
fetchL34DCase,
type L34DCase,
type L34DCaseSummary,
type L34DOperationType,
type L34DResult,
} from "../../core/laboratory/l34dCumulativePostprocessing";
import { L34DCumulativePostprocessingScene } from "./L34DCumulativePostprocessingScene";
type ViewerMode = "after" | "before" | "source";
function operationLabel(types: readonly L34DOperationType[]): string {
return types.includes("nested-box-consolidation")
? "B · NESTED"
: types.includes("temporal-tile-seam-stitch")
? "C · SEAM"
: "UNCHANGED";
}
function caseLabel(item: L34DCaseSummary): string {
return `${item.truthIslandSequence}/32 · frame ${item.frameIndex} · ${item.groupId} · ${operationLabel(item.operationTypes)}`;
}
export function L34DCumulativePostprocessingVisual({
result,
}: {
result: L34DResult;
}) {
const orderedCases = useMemo(() => {
const bySequence = new Map(
result.cases.map((item) => [item.truthIslandSequence, item]),
);
return result.caseOrder
.map((sequence) => bySequence.get(sequence))
.filter((item): item is L34DCaseSummary => item !== undefined);
}, [result.caseOrder, result.cases]);
const [selectedSequence, setSelectedSequence] = useState(
orderedCases[0]?.truthIslandSequence ?? 1,
);
const [candidateCase, setCandidateCase] = useState<L34DCase | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [mode, setMode] = useState<ViewerMode>("after");
const [expanded, setExpanded] = useState(false);
useEffect(() => {
const controller = new AbortController();
setLoading(true);
setError(null);
setCandidateCase(null);
void fetchL34DCase(result.resultId, selectedSequence, {
signal: controller.signal,
}).then((next) => {
if (!controller.signal.aborted) setCandidateCase(next);
}).catch((caught: unknown) => {
if (!controller.signal.aborted) {
setError(caught instanceof Error
? caught.message
: "Визуальный кейс L3.4D недоступен.");
}
}).finally(() => {
if (!controller.signal.aborted) setLoading(false);
});
return () => controller.abort();
}, [result.resultId, selectedSequence]);
const selectedIndex = orderedCases.findIndex(
(item) => item.truthIslandSequence === selectedSequence,
);
const navigate = (offset: -1 | 1) => {
if (selectedIndex < 0 || !orderedCases.length) return;
const next = (selectedIndex + offset + orderedCases.length)
% orderedCases.length;
setSelectedSequence(orderedCases[next].truthIslandSequence);
};
const actions = (
<div className="l3-visual-audit__actions">
<div className="l3-visual-audit__pagination">
<IconButton label="Предыдущий L3.4D case" onClick={() => navigate(-1)}>
<Icon name="chevron-left" size={16} />
</IconButton>
<IconButton label="Следующий L3.4D case" onClick={() => navigate(1)}>
<Icon name="chevron-right" size={16} />
</IconButton>
</div>
<Select
label="Выбрать L3.4D case"
value={String(selectedSequence)}
options={orderedCases.map((item) => ({
value: String(item.truthIslandSequence),
label: caseLabel(item),
}))}
variant="split"
menuWidth="anchor"
searchable
searchPlaceholder="Найти кадр или операцию"
onChange={(value) => setSelectedSequence(Number(value))}
/>
</div>
);
const operation = candidateCase?.operations[0];
const operationSummary = operation?.operationType
=== "nested-box-consolidation"
? "L3.4B nested-box → union"
: operation?.operationType === "temporal-tile-seam-stitch"
? `${operation.sourceTiles.join(" + ")} → union`
: "UNCHANGED";
const operationDetail = operation?.operationType
=== "nested-box-consolidation"
? `${operation.sourcePredictionIndices.length} nested source boxes · ${operation.category}`
: operation?.operationType === "temporal-tile-seam-stitch"
? `${operation.temporalRunLength} consecutive frames · ${operation.category}`
: "Ни одно принятое правило не изменяет этот кадр";
const overlay = candidateCase ? (
<div className="l3-visual-audit__overlay">
<div>
<span>RAVNOVES00 · sensor.camera.right</span>
<strong>{candidateCase.sessionSeconds.toLocaleString("ru-RU", { maximumFractionDigits: 1 })} с · frame {candidateCase.frameIndex}</strong>
<small>Sequence {candidateCase.truthIslandSequence}/32 · {candidateCase.groupId}</small>
</div>
<div>
<span>Frozen source-index cumulative candidate</span>
<strong>{operationSummary}</strong>
<small>{operationDetail}</small>
</div>
<div className="l3-visual-audit__legend">
<span data-tone="fp">BEFORE · исходные B/C boxes</span>
<span data-tone="tp">AFTER · frozen cumulative geometry</span>
<span data-tone="prediction">SOURCE · чистый hash-bound кадр</span>
</div>
</div>
) : undefined;
return (
<div className="l3-visual-audit">
<LaboratoryEvidenceViewer
label="L3.4D cumulative post-processing before/after"
mode={mode}
modes={[
{ value: "after", label: "AFTER" },
{ value: "before", label: "BEFORE" },
{ value: "source", label: "SOURCE" },
]}
expanded={expanded}
onModeChange={setMode}
onExpandedChange={setExpanded}
actions={actions}
overlay={overlay}
>
{loading ? (
<div className="l3-visual-audit__state" role="status">
<span className="busy-indicator" aria-hidden="true" />
<span>Открываем cumulative BEFORE/AFTER evidence</span>
</div>
) : error || !candidateCase ? (
<div className="l3-visual-audit__state" role="status">
<Icon name="alert" size={18} />
<span>{error ?? "Визуальный кейс L3.4D недоступен."}</span>
</div>
) : (
<L34DCumulativePostprocessingScene
candidateCase={candidateCase}
mode={mode}
/>
)}
</LaboratoryEvidenceViewer>
</div>
);
}
@@ -0,0 +1,80 @@
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import type { L34EResult } from "../../core/laboratory/l34eSelfReviewDiagnostic";
import { formatNumber } from "../../presentation";
import { L34ESelfReviewDiagnosticVisual } from "./L34ESelfReviewDiagnosticVisual";
function percent(value: number): string {
return `${(value * 100).toLocaleString("ru-RU", { maximumFractionDigits: 1 })}%`;
}
export function L34ESelfReviewDiagnosticResultView({
rigLabel,
result,
}: {
rigLabel: string;
result: L34EResult;
}) {
const strict = result.metrics.strictIou50;
const diagnostic = result.metrics.diagnosticAssociation;
return (
<LaboratoryWorkTemplate
summary={<LaboratorySummary
title="LAB L3.4E · self-review diagnostic disagreement audit"
description="Ручной prediction-hidden проход сопоставлен с exact L3.4D candidate. Строгий IoU50 сохранён как технический срез, но отделён от объектных совпадений и ошибок локализации."
status="Diagnostic complete · reference не metric-grade"
statusTone="warning"
facts={[
{ label: "Конфигурация", value: `${rigLabel} · RIGHT camera · recorded replay` },
{ label: "Разметка", value: "32/32 · 260 manual objects · 0 prelabels" },
{ label: "Сопоставление", value: "IoU50 strict → loose visual association" },
{ label: "Полномочия", value: "Not truth · retuning запрещён · blind gate закрыт" },
]}
brief={{
question: "Что именно расходится между замороженным L3.4D candidate и ручным self-review: существование объекта, класс или геометрия рамки?",
approach: "Сначала выполнен воспроизводимый greedy IoU50. Затем только для оставшихся объектов проведена диагностическая spatial-association по IoU ≥ 0,10 или overlap-over-smaller ≥ 0,30. Каждый конфликт опубликован поверх точного hash-bound source frame.",
principalResult: `${formatNumber(diagnostic.associatedPairCount, 0)} объектных пар сопоставлены: ${formatNumber(diagnostic.strictAlignment, 0)} строгих, ${formatNumber(diagnostic.localizationDisagreement, 0)} с разъехавшимися рамками и ${formatNumber(diagnostic.strictClassMismatch + diagnostic.classAndLocalizationDisagreement, 0)} с конфликтом класса. Без пары остались ${formatNumber(diagnostic.predictionOnly, 0)} candidate и ${formatNumber(diagnostic.referenceOnly, 0)} self-review объектов.`,
limitation: `Strict IoU50 F1 = ${percent(strict.f1Iou50)} не является качеством детектора: ручные рамки грубые, особенно на дальних машинах, а reviewer видел identity candidate. Нужны refinement/adjudication и независимый reviewer.`,
}}
method={{
completeness: "complete",
executionClass: "deterministic",
pipelineId: result.pipelineId,
components: [
{ kind: "source", name: "L3.4D immutable candidate", version: "cumulative B+C freeze", role: "candidate boxes and exact source binding", identitySha256: null },
{ kind: "source", name: "Manual self-review", version: "revision 3 · prediction-hidden", role: "coarse diagnostic reference · not truth", identitySha256: null },
{ kind: "algorithm", name: result.profile.profileId, version: "strict IoU50 + loose spatial association", role: "classify alignment, localization and unmatched objects", identitySha256: result.resultId.split("-").at(-1) ?? null },
],
}}
/>}
evidence={<LaboratoryEvidence
eyebrow="DIAGNOSTIC MISMATCH GALLERY · OVERLAY / CANDIDATE / SELF-REVIEW / SOURCE"
title="Все 32 кадра отсортированы по тяжести расхождения"
kind="diagnostic-model"
resizable
>
<L34ESelfReviewDiagnosticVisual result={result} />
</LaboratoryEvidence>}
result={<LaboratoryResultSummary
title="Self-review выявил расхождение, но не дал metric-grade benchmark"
status="Refine labels before detector decision"
statusTone="warning"
metrics={[
{ label: "Object associations", value: `${formatNumber(diagnostic.associatedPairCount, 0)} / ${formatNumber(diagnostic.referenceCount, 0)}`, hint: `${percent(diagnostic.referenceAssociationCoverage)} self-review coverage` },
{ label: "Localization", value: formatNumber(diagnostic.localizationDisagreement, 0), hint: "Object paired · geometry disagrees" },
{ label: "Unmatched P / R", value: `${formatNumber(diagnostic.predictionOnly, 0)} / ${formatNumber(diagnostic.referenceOnly, 0)}`, hint: "Requires visual adjudication" },
{ label: "Strict IoU50 F1", value: percent(strict.f1Iou50), hint: "Diagnostic only · not detector accuracy" },
]}
conclusion={{
proved: "Ручной проход завершён без prelabels, exact L3.4D source binding сохранён, и каждый конфликт можно визуально проверить. Основная масса strict FP/FN вызвана не только наличием объектов, но и сильным расхождением геометрии ручных и модельных рамок.",
notProved: "Не доказаны detector precision/recall/AP, регрессия или прогрессия L3.4D, пригодность ручных рамок как metric-grade truth и независимость review. Loose associations являются только маршрутизацией визуального аудита.",
decision: "Не перетюнивать модель, thresholds или post-processing по этим агрегатам. Сначала пройти mismatch gallery, уточнить спорные ручные рамки и классы, провести adjudication, затем отдать exact frozen candidate независимому reviewer и выполнить один acceptance calculation.",
}}
/>}
/>
);
}
@@ -0,0 +1,216 @@
import { useEffect, useRef, useState } from "react";
import type {
L34ECase,
L34EDiagnosticVerdict,
} from "../../core/laboratory/l34eSelfReviewDiagnostic";
export type L34ESceneMode = "overlay" | "candidate" | "review" | "source";
function color(
host: HTMLElement,
token: string,
fallback: readonly [number, number, number],
alpha = 1,
): string {
const value = getComputedStyle(host).getPropertyValue(token).trim();
const channels = value.match(/[\d.]+/g)?.slice(0, 3).map(Number);
const [red, green, blue] = channels?.length === 3 ? channels : fallback;
return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
}
function verdictColor(
host: HTMLElement,
verdict: L34EDiagnosticVerdict,
reference: boolean,
): string {
if (verdict === "strict_alignment") {
return color(host, "--nodedc-success-rgb", [181, 255, 90], 0.42);
}
if (verdict === "localization_disagreement") {
return color(host, "--nodedc-warning-rgb", [255, 197, 92], 0.95);
}
if (verdict === "strict_class_mismatch"
|| verdict === "class_and_localization_disagreement") {
return color(host, "--nodedc-accent-rgb", [232, 56, 126], 0.98);
}
return reference
? color(host, "--nodedc-accent-rgb", [232, 56, 126], 1)
: color(host, "--nodedc-danger-rgb", [255, 94, 94], 1);
}
function shortVerdict(verdict: L34EDiagnosticVerdict): string {
return verdict === "strict_alignment" ? "STRICT"
: verdict === "localization_disagreement" ? "LOC"
: verdict === "strict_class_mismatch" ? "CLASS"
: verdict === "class_and_localization_disagreement" ? "CLASS+LOC"
: verdict === "prediction_only" ? "P-ONLY"
: "R-ONLY";
}
export function L34ESelfReviewDiagnosticScene({
diagnosticCase,
mode,
}: {
diagnosticCase: L34ECase;
mode: L34ESceneMode;
}) {
const hostRef = useRef<HTMLDivElement | null>(null);
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const [image, setImage] = useState<HTMLImageElement | null>(null);
const [failed, setFailed] = useState(false);
useEffect(() => {
const next = new Image();
next.decoding = "async";
next.onload = () => { setImage(next); setFailed(false); };
next.onerror = () => { setImage(null); setFailed(true); };
next.src = diagnosticCase.cameraUrl;
return () => { next.onload = null; next.onerror = null; };
}, [diagnosticCase.cameraUrl]);
useEffect(() => {
const host = hostRef.current;
const canvas = canvasRef.current;
if (!host || !canvas || !image) return;
const context = canvas.getContext("2d");
if (!context) return;
const render = () => {
const width = Math.max(host.clientWidth, 1);
const height = Math.max(host.clientHeight, 1);
const pixelRatio = Math.min(window.devicePixelRatio, 1.5);
canvas.width = Math.round(width * pixelRatio);
canvas.height = Math.round(height * pixelRatio);
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
context.fillStyle = color(host, "--nodedc-canvas-rgb", [5, 5, 6]);
context.fillRect(0, 0, width, height);
const scale = Math.min(
width / diagnosticCase.cameraWidth,
height / diagnosticCase.cameraHeight,
);
const drawWidth = diagnosticCase.cameraWidth * scale;
const drawHeight = diagnosticCase.cameraHeight * scale;
const offsetX = (width - drawWidth) / 2;
const offsetY = (height - drawHeight) / 2;
context.drawImage(image, offsetX, offsetY, drawWidth, drawHeight);
if (mode === "source") return;
const labelRects: { x: number; y: number; width: number; height: number }[] = [];
const drawBox = ({
box,
category,
verdict,
reference,
}: {
box: readonly [number, number, number, number];
category: string;
verdict: L34EDiagnosticVerdict;
reference: boolean;
}) => {
const [left, top, right, bottom] = box;
const x = offsetX + left * scale;
const y = offsetY + top * scale;
const boxWidth = (right - left) * scale;
const boxHeight = (bottom - top) * scale;
const stroke = verdictColor(host, verdict, reference);
context.strokeStyle = stroke;
context.lineWidth = verdict === "strict_alignment"
? Math.max(1, 1.1 * scale)
: Math.max(2, 2.4 * scale);
context.setLineDash(reference ? [7, 4] : []);
context.strokeRect(x, y, boxWidth, boxHeight);
context.setLineDash([]);
if (verdict === "strict_alignment" || mode === "overlay") return;
const label = `${shortVerdict(verdict)} · ${category}`;
const fontSize = Math.max(10, 11 * scale);
context.font = `600 ${fontSize}px Inter, system-ui, sans-serif`;
const labelWidth = context.measureText(label).width + 12;
const labelHeight = fontSize + 8;
const labelX = Math.min(
offsetX + drawWidth - labelWidth,
Math.max(offsetX, x),
);
const candidateY = [
y - labelHeight - 2,
y + boxHeight + 2,
...Array.from({ length: 8 }, (_, index) => (
y - (index + 2) * (labelHeight + 2)
)),
...Array.from({ length: 8 }, (_, index) => (
y + boxHeight + (index + 2) * (labelHeight + 2)
)),
];
const fits = (candidate: number) => {
if (candidate < offsetY
|| candidate + labelHeight > offsetY + drawHeight) return false;
return labelRects.every((rect) => (
labelX + labelWidth <= rect.x
|| rect.x + rect.width <= labelX
|| candidate + labelHeight <= rect.y
|| rect.y + rect.height <= candidate
));
};
const labelY = candidateY.find(fits)
?? Math.min(offsetY + drawHeight - labelHeight, Math.max(offsetY, y));
labelRects.push({ x: labelX, y: labelY, width: labelWidth, height: labelHeight });
const boxAnchorX = x + boxWidth / 2;
const boxAnchorY = y;
const labelAnchorX = Math.min(
labelX + labelWidth,
Math.max(labelX, boxAnchorX),
);
const labelAnchorY = labelY > boxAnchorY
? labelY
: labelY + labelHeight;
context.strokeStyle = stroke;
context.lineWidth = 1;
context.beginPath();
context.moveTo(boxAnchorX, boxAnchorY);
context.lineTo(labelAnchorX, labelAnchorY);
context.stroke();
context.fillStyle = color(host, "--nodedc-canvas-rgb", [5, 5, 6], 0.88);
context.fillRect(labelX, labelY, labelWidth, labelHeight);
context.fillStyle = stroke;
context.fillText(label, labelX + 6, labelY + fontSize + 1);
};
if (mode === "overlay" || mode === "candidate") {
diagnosticCase.predictions.forEach((prediction) => drawBox({
box: prediction.boxXyxy,
category: prediction.category,
verdict: prediction.diagnosticVerdict,
reference: false,
}));
}
if (mode === "overlay" || mode === "review") {
diagnosticCase.references.forEach((reference) => drawBox({
box: reference.boxXyxy,
category: reference.displayCategory,
verdict: reference.diagnosticVerdict,
reference: true,
}));
}
};
const observer = new ResizeObserver(render);
observer.observe(host);
render();
return () => observer.disconnect();
}, [diagnosticCase, image, mode]);
return (
<div className="l32-camera-scene" ref={hostRef}>
<canvas
ref={canvasRef}
role="img"
aria-label={`L3.4E frame ${diagnosticCase.frameIndex}: ${mode} self-review diagnostic`}
/>
{failed ? (
<div className="l3-visual-audit__state" role="status">
Точный кадр L3.4E недоступен.
</div>
) : null}
</div>
);
}
@@ -0,0 +1,156 @@
import { useEffect, useMemo, useState } from "react";
import { Icon, IconButton, Select } from "@nodedc/ui-react";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
import {
fetchL34ECase,
type L34ECase,
type L34ECaseSummary,
type L34EResult,
} from "../../core/laboratory/l34eSelfReviewDiagnostic";
import {
L34ESelfReviewDiagnosticScene,
type L34ESceneMode,
} from "./L34ESelfReviewDiagnosticScene";
function caseLabel(item: L34ECaseSummary): string {
const summary = item.diagnosticSummary;
return `${item.truthIslandSequence}/32 · frame ${item.frameIndex} · LOC ${summary.localizationDisagreement} · P ${summary.predictionOnly} · R ${summary.referenceOnly}`;
}
export function L34ESelfReviewDiagnosticVisual({
result,
}: {
result: L34EResult;
}) {
const orderedCases = useMemo(() => {
const bySequence = new Map(
result.cases.map((item) => [item.truthIslandSequence, item]),
);
return result.caseOrder
.map((sequence) => bySequence.get(sequence))
.filter((item): item is L34ECaseSummary => item !== undefined);
}, [result.caseOrder, result.cases]);
const [selectedSequence, setSelectedSequence] = useState(
orderedCases[0]?.truthIslandSequence ?? 1,
);
const [diagnosticCase, setDiagnosticCase] = useState<L34ECase | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [mode, setMode] = useState<L34ESceneMode>("overlay");
const [expanded, setExpanded] = useState(false);
useEffect(() => {
const controller = new AbortController();
setLoading(true);
setError(null);
setDiagnosticCase(null);
void fetchL34ECase(result.resultId, selectedSequence, {
signal: controller.signal,
}).then((next) => {
if (!controller.signal.aborted) setDiagnosticCase(next);
}).catch((caught: unknown) => {
if (!controller.signal.aborted) {
setError(caught instanceof Error
? caught.message
: "Визуальный кейс L3.4E недоступен.");
}
}).finally(() => {
if (!controller.signal.aborted) setLoading(false);
});
return () => controller.abort();
}, [result.resultId, selectedSequence]);
const selectedIndex = orderedCases.findIndex(
(item) => item.truthIslandSequence === selectedSequence,
);
const navigate = (offset: -1 | 1) => {
if (selectedIndex < 0 || !orderedCases.length) return;
const next = (selectedIndex + offset + orderedCases.length)
% orderedCases.length;
setSelectedSequence(orderedCases[next].truthIslandSequence);
};
const actions = (
<div className="l3-visual-audit__actions">
<div className="l3-visual-audit__pagination">
<IconButton label="Предыдущий конфликт L3.4E" onClick={() => navigate(-1)}>
<Icon name="chevron-left" size={16} />
</IconButton>
<IconButton label="Следующий конфликт L3.4E" onClick={() => navigate(1)}>
<Icon name="chevron-right" size={16} />
</IconButton>
</div>
<Select
label="Выбрать конфликтный кадр L3.4E"
value={String(selectedSequence)}
options={orderedCases.map((item) => ({
value: String(item.truthIslandSequence),
label: caseLabel(item),
}))}
variant="split"
menuWidth="anchor"
searchable
searchPlaceholder="Найти кадр или тип расхождения"
onChange={(value) => setSelectedSequence(Number(value))}
/>
</div>
);
const summary = diagnosticCase?.diagnosticSummary;
const overlay = diagnosticCase && summary ? (
<div className="l3-visual-audit__overlay">
<div>
<span>RAVNOVES00 · sensor.camera.right</span>
<strong>{diagnosticCase.sessionSeconds.toLocaleString("ru-RU", { maximumFractionDigits: 1 })} с · frame {diagnosticCase.frameIndex}</strong>
<small>Sequence {diagnosticCase.truthIslandSequence}/32 · {diagnosticCase.groupId}</small>
</div>
<div>
<span>Diagnostic self-review · не truth</span>
<strong>{summary.associatedPairCount} пар · {summary.localizationDisagreement} LOC · {summary.strictClassMismatch + summary.classAndLocalizationDisagreement} CLASS</strong>
<small>{summary.predictionOnly} candidate-only · {summary.referenceOnly} review-only</small>
</div>
<div className="l3-visual-audit__legend">
<span data-tone="tp">STRICT · совпавшая геометрия</span>
<span data-tone="warning">LOC · объект совпал, рамка разъехалась</span>
<span data-tone="fp">P-ONLY · только candidate</span>
<span data-tone="fn">R-ONLY · только self-review</span>
</div>
</div>
) : undefined;
return (
<div className="l3-visual-audit">
<LaboratoryEvidenceViewer
label="L3.4E candidate and manual self-review disagreement gallery"
mode={mode}
modes={[
{ value: "overlay", label: "OVERLAY" },
{ value: "candidate", label: "CANDIDATE" },
{ value: "review", label: "SELF-REVIEW" },
{ value: "source", label: "SOURCE" },
]}
expanded={expanded}
onModeChange={setMode}
onExpandedChange={setExpanded}
actions={actions}
overlay={overlay}
>
{loading ? (
<div className="l3-visual-audit__state" role="status">
<span className="busy-indicator" aria-hidden="true" />
<span>Открываем hash-bound mismatch evidence</span>
</div>
) : error || !diagnosticCase ? (
<div className="l3-visual-audit__state" role="status">
<Icon name="alert" size={18} />
<span>{error ?? "Визуальный кейс L3.4E недоступен."}</span>
</div>
) : (
<L34ESelfReviewDiagnosticScene
diagnosticCase={diagnosticCase}
mode={mode}
/>
)}
</LaboratoryEvidenceViewer>
</div>
);
}
@@ -0,0 +1,112 @@
import { useEffect, useState } from "react";
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import { fetchL34ESelfReviewDiagnostic, type L34EResult } from "../../core/laboratory/l34eSelfReviewDiagnostic";
import type { L34FFrozenResult } from "../../core/laboratory/l34fAdjudication";
import { formatNumber } from "../../presentation";
import { L34ESelfReviewDiagnosticVisual } from "./L34ESelfReviewDiagnosticVisual";
function FrozenEvidence({ result }: { result: L34FFrozenResult }) {
const [diagnostic, setDiagnostic] = useState<L34EResult | null>(null);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const controller = new AbortController();
void fetchL34ESelfReviewDiagnostic({ signal: controller.signal })
.then((value) => {
if (controller.signal.aborted) return;
if (value.resultId !== result.diagnosticResultId) {
setError("Hash-bound diagnostic source L3.4E не совпал с L3.4F.");
return;
}
setDiagnostic(value);
})
.catch((caught: unknown) => {
if (!controller.signal.aborted) {
setError(caught instanceof Error ? caught.message : "Evidence L3.4F недоступен.");
}
});
return () => controller.abort();
}, [result.diagnosticResultId]);
if (error) return <div className="l3-visual-audit__state" role="status">{error}</div>;
if (!diagnostic) {
return <div className="l3-visual-audit__state" role="status"><span className="busy-indicator" aria-hidden="true" />Открываем frozen evidence L3.4F</div>;
}
return <L34ESelfReviewDiagnosticVisual result={diagnostic} />;
}
export function L34FAdjudicatedReferenceResultView({
rigLabel,
result,
}: {
rigLabel: string;
result: L34FFrozenResult;
}) {
const changedObjects = result.metrics.geometryChangedObjectCount
+ result.metrics.classChangedObjectCount
+ result.metrics.attributeChangedObjectCount
+ result.metrics.addedObjectCount
+ result.metrics.deletedObjectCount;
return (
<LaboratoryWorkTemplate
summary={<LaboratorySummary
title="LAB L3.4F · candidate-visible adjudicated engineering reference"
description="Все 32 конфликтных кадра L3.4E повторно просмотрены в полноэкранном редакторе: candidate оставался read-only, human-reference можно было двигать, ресайзить, удалять, добавлять и переклассифицировать."
status="Adjudication complete · engineering reference · not truth"
statusTone="warning"
facts={[
{ label: "Конфигурация", value: `${rigLabel} · RIGHT camera · recorded replay` },
{ label: "Проход", value: `${result.metrics.frameCount}/32 · ${result.metrics.adjudicatedReferenceCount} human objects` },
{ label: "Правки", value: `${result.metrics.changedFrameCount} кадров · ${changedObjects} операций` },
{ label: "Полномочия", value: "Candidate не принят · retuning запрещён · L3.5 закрыт" },
]}
brief={{
question: "Нужно ли исправлять человеческую self-review разметку после визуального разбора расхождений с candidate L3.4D?",
approach: "Каждый конфликт открыт на exact hash-bound source frame в четырёх слоях: OVERLAY, CANDIDATE, HUMAN и SOURCE. Candidate был видим, но неизменяем; сохранение и freeze разрешались только после отметки 32/32 кадров.",
principalResult: `${formatNumber(result.metrics.unchangedObjectCount, 0)} из ${formatNumber(result.metrics.sourceReferenceCount, 0)} исходных human-объектов оставлены без изменений. Добавлено ${formatNumber(result.metrics.addedObjectCount, 0)}, удалено ${formatNumber(result.metrics.deletedObjectCount, 0)}, геометрия изменена у ${formatNumber(result.metrics.geometryChangedObjectCount, 0)} объектов, класс — у ${formatNumber(result.metrics.classChangedObjectCount, 0)}.`,
limitation: "Reviewer видел candidate и ранее создавал исходную self-review разметку. Поэтому результат полезен как инженерная reference-разметка и визуальный протокол, но не является independent или metric-grade truth.",
}}
method={{
completeness: "complete",
executionClass: "hybrid",
pipelineId: "l34e-candidate-visible-adjudication/v1",
components: [
{ kind: "source", name: "L3.4E mismatch gallery", version: result.diagnosticResultId, role: "candidate + self-review + exact source", identitySha256: result.diagnosticResultId.split("-").at(-1) ?? null },
{ kind: "algorithm", name: "Fullscreen box adjudication", version: "revisioned 32/32 gate", role: "move, resize, add, delete, relabel, approve", identitySha256: null },
{ kind: "model", name: "Frozen L3.4D candidate", version: "scores hidden", role: "visible read-only comparison layer", identitySha256: null },
],
}}
/>}
evidence={<LaboratoryEvidence
eyebrow="FROZEN VISUAL EVIDENCE · OVERLAY / CANDIDATE / HUMAN / SOURCE"
title="32/32 кадров доступны для повторной визуальной проверки"
kind="diagnostic-model"
resizable
>
<FrozenEvidence result={result} />
</LaboratoryEvidence>}
result={<LaboratoryResultSummary
title="Разметка выдержала adjudication, но detector gate всё ещё закрыт"
status="Two independent blind reviews required before E48 / L3.5"
statusTone="warning"
metrics={[
{ label: "Reviewed", value: `${result.metrics.frameCount}/32`, hint: "Все конфликтные кадры" },
{ label: "Human objects", value: formatNumber(result.metrics.adjudicatedReferenceCount, 0), hint: `${formatNumber(result.metrics.unchangedObjectCount, 0)} unchanged` },
{ label: "Changed frames", value: formatNumber(result.metrics.changedFrameCount, 0), hint: `${formatNumber(changedObjects, 0)} edit operations` },
{ label: "Blind authority", value: "0", hint: "Not independent truth" },
]}
conclusion={{
proved: "Полный candidate-visible разбор завершён и заморожен как воспроизводимый immutable artifact. На просмотренных кадрах не нашлось честных оснований менять исходные human boxes только ради лучшего совпадения с моделью.",
notProved: "Не доказаны точность детектора, AP/precision/recall, прогрессия L3.4D, независимость разметки или пригодность результата для safety/navigation. Ноль правок не означает ноль ошибок модели.",
decision: "Не трогать модель по этому набору. Следующий шаг — два разных реальных reviewer получают те же 32 source frames без candidate identity, predictions и этой разметки. После двух freeze выполняется отдельная adjudication, затем E48 seal и только потом одноразовый L3.5 acceptance calculation.",
}}
/>}
/>
);
}
@@ -0,0 +1,122 @@
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import type {
L34RightYoloxTruthIslandResult,
} from "../../core/laboratory/l34RightYoloxTruthIsland";
import { formatNumber } from "../../presentation";
import { L34RightYoloxTruthIslandVisual } from "./L34RightYoloxTruthIslandVisual";
function digest(value: string): string | null {
const candidate = value.split("-").at(-1) ?? "";
return /^[a-f0-9]{64}$/.test(candidate) ? candidate : null;
}
export function L34RightYoloxTruthIslandResultView({
rigLabel,
result,
}: {
rigLabel: string;
result: L34RightYoloxTruthIslandResult;
}) {
const metrics = result.metrics;
return (
<LaboratoryWorkTemplate
summary={(
<LaboratorySummary
title="LAB L3.4 · RIGHT YOLOX truth-island freeze"
description="Первый честный benchmark-прогон текущего camera-first YOLOX pipeline: точные предсказания заморожены на 32 скрытых кадрах RAVNOVES00 до раскрытия независимой человеческой разметки."
status="Ожидает независимую truth"
statusTone="warning"
facts={[
{ label: "Конфигурация", value: `${rigLabel} · RIGHT camera · recorded replay` },
{ label: "Кандидат", value: `${result.candidate.architecture} · score ≥ ${result.candidate.minimumScore}` },
{ label: "Truth island", value: `${formatNumber(metrics.frameCount, 0)} кадров · ${formatNumber(metrics.temporalGroupCount, 0)} групп` },
{ label: "Состояние", value: "Predictions frozen · labels unread" },
]}
brief={{
question: "Как текущий YOLOX-S распознаёт task-relevant объекты на заранее выбранных скрытых кадрах RAVNOVES00?",
approach: "Использован только записанный sensor.camera.right. Из E46 взяты 32 model-independent кадра; из полного YOLOX replay зафиксированы все допущенные detection boxes при score ≥ 0.25. Human labels не читались и не использовались для настройки.",
principalResult: `Заморожено ${formatNumber(metrics.predictionCount, 0)} предсказаний на ${formatNumber(metrics.framesWithPredictions, 0)} из ${formatNumber(metrics.frameCount, 0)} кадров. Это полный candidate freeze, а не метрика качества.`,
limitation: "AP, recall, misses и false positives пока не существуют: их можно вычислить только после двух независимых blind-review и adjudication. Live, hardware, левая камера и второй маршрут исключены из этого прогона.",
}}
method={{
completeness: "complete",
executionClass: "ai-inference",
pipelineId: result.pipelineId,
components: [
{
kind: "source",
name: result.truthIsland.resultId,
version: "E46 blind truth island · labels unavailable",
role: "model-independent frame selection",
identitySha256: digest(result.truthIsland.resultId),
},
{
kind: "model",
name: result.candidate.architecture,
version: `COCO-80 · score ≥ ${result.candidate.minimumScore}`,
role: "right-camera task-relevant 2D detection candidate",
identitySha256: result.candidate.modelSha256,
},
{
kind: "algorithm",
name: result.profileId,
version: "candidate-freeze/v1",
role: "freeze predictions before independent truth reveal",
identitySha256: digest(result.resultId),
},
],
}}
/>
)}
evidence={(
<LaboratoryEvidence
eyebrow="FROZEN CANDIDATE · NO TRUTH REVEAL"
title="Правые кадры RAVNOVES00 и точные frozen YOLOX boxes"
kind="diagnostic-model"
resizable
>
<L34RightYoloxTruthIslandVisual result={result} />
</LaboratoryEvidence>
)}
result={(
<LaboratoryResultSummary
title="Кандидат зафиксирован; оценка качества ещё закрыта"
status="Human truth required"
statusTone="warning"
metrics={[
{
label: "Кадры",
value: formatNumber(metrics.frameCount, 0),
hint: `${formatNumber(metrics.temporalGroupCount, 0)} независимых temporal/anchor групп`,
},
{
label: "Предсказания",
value: formatNumber(metrics.predictionCount, 0),
hint: "content-addressed freeze",
},
{
label: "Truth read",
value: result.decision.truthLabelsRead ? "Да" : "Нет",
hint: "blindness contract preserved",
},
{
label: "Accuracy",
value: metrics.accuracyMetricsAvailable ? "Доступна" : "Недоступна",
hint: "не подменяется descriptive counts",
},
]}
conclusion={{
proved: "Текущий YOLOX-кандидат воспроизводимо привязан к одному записанному источнику, одной правой камере и заранее выбранным 32 кадрам; предсказания заморожены до раскрытия truth.",
notProved: "Не доказаны accuracy, recall, FP/FN, качество LiDAR range, перенос на другой маршрут, live transport, hardware runtime, navigation или safety.",
decision: "Не тюнить модель по этим кадрам. Следующий gate — две независимые слепые разметки E46, adjudication и вычисление AP/recall по этой точной prediction generation.",
}}
/>
)}
/>
);
}
@@ -0,0 +1,212 @@
import { useEffect, useRef, useState } from "react";
import type {
L34Prediction,
L34VisualFrame,
} from "../../core/laboratory/l34RightYoloxTruthIsland";
function tokenColor(
host: HTMLElement,
token: string,
fallback: readonly [number, number, number],
alpha = 1,
): string {
const value = getComputedStyle(host).getPropertyValue(token).trim();
const channels = value.match(/[\d.]+/g)?.slice(0, 3).map(Number);
const [red, green, blue] = channels?.length === 3 ? channels : fallback;
return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
}
function predictionLabel(prediction: L34Prediction): string {
const names: Readonly<Record<string, string>> = {
car: "Авто",
heavy_vehicle: "Тяжёлый транспорт",
person: "Человек",
bicycle: "Велосипед",
motorcycle: "Мотоцикл",
static_obstacle: "Препятствие",
animal: "Животное",
};
return `${names[prediction.label] ?? prediction.label} · ${(prediction.score * 100).toFixed(0)}%`;
}
type LabelRect = Readonly<{
x: number;
y: number;
width: number;
height: number;
}>;
function overlaps(left: LabelRect, right: LabelRect): boolean {
const margin = 3;
return !(
left.x + left.width + margin <= right.x
|| right.x + right.width + margin <= left.x
|| left.y + left.height + margin <= right.y
|| right.y + right.height + margin <= left.y
);
}
function placeLabel(
box: LabelRect,
labelWidth: number,
labelHeight: number,
imageBounds: LabelRect,
occupied: readonly LabelRect[],
): LabelRect | null {
const x = Math.min(
imageBounds.x + imageBounds.width - labelWidth,
Math.max(imageBounds.x, box.x),
);
const candidates = [
box.y - labelHeight - 3,
box.y + box.height + 3,
box.y + 3,
].map((y) => ({ x, y, width: labelWidth, height: labelHeight }));
return candidates.find((candidate) => (
candidate.y >= imageBounds.y
&& candidate.y + candidate.height <= imageBounds.y + imageBounds.height
&& occupied.every((current) => !overlaps(candidate, current))
)) ?? null;
}
export function L34RightYoloxTruthIslandScene({
frame,
predictionsVisible,
}: {
frame: L34VisualFrame;
predictionsVisible: boolean;
}) {
const hostRef = useRef<HTMLDivElement | null>(null);
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const [image, setImage] = useState<HTMLImageElement | null>(null);
const [error, setError] = useState(false);
useEffect(() => {
const next = new Image();
next.decoding = "async";
next.onload = () => {
setError(false);
setImage(next);
};
next.onerror = () => {
setImage(null);
setError(true);
};
next.src = frame.cameraUrl;
return () => {
next.onload = null;
next.onerror = null;
};
}, [frame.cameraUrl]);
useEffect(() => {
const host = hostRef.current;
const canvas = canvasRef.current;
if (!host || !canvas || !image) return;
const context = canvas.getContext("2d");
if (!context) return;
const render = () => {
const width = Math.max(host.clientWidth, 1);
const height = Math.max(host.clientHeight, 1);
const ratio = Math.min(window.devicePixelRatio, 1.5);
canvas.width = Math.round(width * ratio);
canvas.height = Math.round(height * ratio);
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
context.setTransform(ratio, 0, 0, ratio, 0, 0);
context.fillStyle = tokenColor(host, "--nodedc-canvas-rgb", [5, 5, 6]);
context.fillRect(0, 0, width, height);
const scale = Math.min(width / frame.cameraWidth, height / frame.cameraHeight);
const drawWidth = frame.cameraWidth * scale;
const drawHeight = frame.cameraHeight * scale;
const offsetX = (width - drawWidth) / 2;
const offsetY = (height - drawHeight) / 2;
context.drawImage(image, offsetX, offsetY, drawWidth, drawHeight);
if (!predictionsVisible) return;
const predictionColor = tokenColor(
host,
"--nodedc-accent-rgb",
[174, 255, 88],
0.96,
);
const labelSurface = tokenColor(
host,
"--nodedc-canvas-rgb",
[5, 5, 6],
0.84,
);
const boxes = frame.predictions.map((prediction) => {
const [left, top, right, bottom] = prediction.bboxXyxy;
const box = {
x: offsetX + left * scale,
y: offsetY + top * scale,
width: (right - left) * scale,
height: (bottom - top) * scale,
};
context.strokeStyle = predictionColor;
context.lineWidth = Math.max(1.25, 1.8 * scale);
context.strokeRect(box.x, box.y, box.width, box.height);
return { prediction, box };
});
const occupied: LabelRect[] = [];
const imageBounds = {
x: offsetX,
y: offsetY,
width: drawWidth,
height: drawHeight,
};
[...boxes]
.sort((left, right) => right.prediction.score - left.prediction.score)
.forEach(({ prediction, box }) => {
const label = predictionLabel(prediction);
const fontSize = Math.max(9, 10 * scale);
const labelHeight = fontSize + 7;
context.font = `600 ${fontSize}px Inter, system-ui, sans-serif`;
const labelWidth = context.measureText(label).width + 12;
const placement = placeLabel(
box,
labelWidth,
labelHeight,
imageBounds,
occupied,
);
if (!placement) return;
occupied.push(placement);
context.fillStyle = labelSurface;
context.fillRect(
placement.x,
placement.y,
placement.width,
placement.height,
);
context.fillStyle = predictionColor;
context.fillText(
label,
placement.x + 6,
placement.y + fontSize + 1,
);
});
};
const observer = new ResizeObserver(render);
observer.observe(host);
render();
return () => observer.disconnect();
}, [frame, image, predictionsVisible]);
return (
<div className="l32-camera-scene" ref={hostRef}>
<canvas
ref={canvasRef}
role="img"
aria-label={`L3.4 right-camera frame ${frame.frameIndex}, ${predictionsVisible ? `${frame.predictions.length} frozen predictions` : "immutable source only"}`}
/>
{error ? (
<div className="l3-visual-audit__state" role="status">
Точный кадр правой камеры L3.4 недоступен.
</div>
) : null}
</div>
);
}
@@ -0,0 +1,150 @@
import { useEffect, useState } from "react";
import { Icon, IconButton, Select } from "@nodedc/ui-react";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
import {
fetchL34RightYoloxTruthIslandFrame,
type L34FrameSummary,
type L34RightYoloxTruthIslandResult,
type L34VisualFrame,
} from "../../core/laboratory/l34RightYoloxTruthIsland";
import { L34RightYoloxTruthIslandScene } from "./L34RightYoloxTruthIslandScene";
type ViewerMode = "predictions" | "source";
function frameLabel(frame: L34FrameSummary): string {
const score = frame.maximumScore > 0
? ` · max ${(frame.maximumScore * 100).toFixed(0)}%`
: "";
return `${frame.truthIslandSequence}/32 · frame ${frame.frameIndex} · ${frame.groupId} · ${frame.predictionCount} boxes${score}`;
}
export function L34RightYoloxTruthIslandVisual({
result,
}: {
result: L34RightYoloxTruthIslandResult;
}) {
const initialSequence = result.frames.find(
({ groupId }) => groupId === "clip-stroller-person",
)?.truthIslandSequence ?? result.frames[0]?.truthIslandSequence ?? 1;
const [selectedSequence, setSelectedSequence] = useState(initialSequence);
const [frame, setFrame] = useState<L34VisualFrame | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [mode, setMode] = useState<ViewerMode>("predictions");
const [expanded, setExpanded] = useState(false);
useEffect(() => {
const controller = new AbortController();
setFrame(null);
setLoading(true);
setError(null);
void fetchL34RightYoloxTruthIslandFrame(
result.resultId,
selectedSequence,
{ signal: controller.signal },
).then((next) => {
if (!controller.signal.aborted) setFrame(next);
}).catch((caught: unknown) => {
if (!controller.signal.aborted) {
setError(
caught instanceof Error
? caught.message
: "Визуальный кадр L3.4 недоступен.",
);
}
}).finally(() => {
if (!controller.signal.aborted) setLoading(false);
});
return () => controller.abort();
}, [result.resultId, selectedSequence]);
const selectedIndex = result.frames.findIndex(
({ truthIslandSequence }) => truthIslandSequence === selectedSequence,
);
const navigate = (offset: -1 | 1) => {
if (selectedIndex < 0) return;
const index = (
selectedIndex + offset + result.frames.length
) % result.frames.length;
setSelectedSequence(result.frames[index].truthIslandSequence);
};
const actions = (
<div className="l3-visual-audit__actions">
<div className="l3-visual-audit__pagination">
<IconButton label="Предыдущий визуальный кадр L3.4" onClick={() => navigate(-1)}>
<Icon name="chevron-left" size={16} />
</IconButton>
<IconButton label="Следующий визуальный кадр L3.4" onClick={() => navigate(1)}>
<Icon name="chevron-right" size={16} />
</IconButton>
</div>
<Select
label="Выбрать визуальный кадр L3.4"
value={String(selectedSequence)}
options={result.frames.map((item) => ({
value: String(item.truthIslandSequence),
label: frameLabel(item),
}))}
variant="split"
menuWidth="anchor"
searchable
searchPlaceholder="Найти кадр или группу"
onChange={(value) => setSelectedSequence(Number(value))}
/>
</div>
);
const overlay = frame ? (
<div className="l3-visual-audit__overlay">
<div>
<span>RAVNOVES00 · sensor.camera.right</span>
<strong>{frame.sessionSeconds.toLocaleString("ru-RU", { maximumFractionDigits: 1 })} с · frame {frame.frameIndex}</strong>
<small>{frame.role === "temporal" ? "Temporal sequence" : "Independent anchor"} · image {frame.imageId}</small>
</div>
<div>
<span>Frozen YOLOX-S candidate</span>
<strong>{frame.predictions.length} boxes · labels скрыты</strong>
<small>image {frame.cameraSha256.slice(0, 12)} · predictions {frame.predictionRowsSha256.slice(0, 12)}</small>
</div>
<div className="l3-visual-audit__legend">
<span data-tone="prediction">Акцентный контур · frozen prediction</span>
<span>Источник переключается без нового backend-расчёта</span>
</div>
</div>
) : undefined;
return (
<div className="l3-visual-audit">
<LaboratoryEvidenceViewer
label="L3.4 visual candidate review"
mode={mode}
modes={[
{ value: "predictions", label: "BOXES" },
{ value: "source", label: "SOURCE" },
]}
expanded={expanded}
onModeChange={setMode}
onExpandedChange={setExpanded}
actions={actions}
overlay={overlay}
>
{loading ? (
<div className="l3-visual-audit__state" role="status">
<span className="busy-indicator" aria-hidden="true" />
<span>Открываем hash-bound кадр и frozen boxes</span>
</div>
) : error || !frame ? (
<div className="l3-visual-audit__state" role="status">
<Icon name="alert" size={18} />
<span>{error ?? "Визуальный кадр L3.4 недоступен."}</span>
</div>
) : (
<L34RightYoloxTruthIslandScene
frame={frame}
predictionsVisible={mode === "predictions"}
/>
)}
</LaboratoryEvidenceViewer>
</div>
);
}
@@ -9,6 +9,13 @@ import type {
export type L3VisualMode = "3d" | "bev";
export interface L3VisualAssociationRay {
originXyzM: readonly [number, number, number];
directionXyz: readonly [number, number, number];
anchorXyzM: readonly [number, number, number] | null;
temporalHeld?: boolean;
}
function tokenColor(
host: HTMLElement,
token: string,
@@ -66,6 +73,90 @@ function boxSegments(box: L3VisualBox): Float32Array {
return positions;
}
function scenePoint(value: readonly [number, number, number]): THREE.Vector3 {
return new THREE.Vector3(value[0], value[2], -value[1]);
}
function evidenceBounds(
boxes: readonly L3VisualBox[],
rays: readonly L3VisualAssociationRay[],
): THREE.Box3 | null {
const bounds = new THREE.Box3();
boxes.forEach((box) => {
const segments = boxSegments(box);
for (let index = 0; index < segments.length; index += 3) {
bounds.expandByPoint(new THREE.Vector3(
segments[index],
segments[index + 1],
segments[index + 2],
));
}
});
rays.forEach((ray) => {
bounds.expandByPoint(scenePoint(ray.originXyzM));
if (ray.anchorXyzM) bounds.expandByPoint(scenePoint(ray.anchorXyzM));
});
return bounds.isEmpty() ? null : bounds;
}
function addAssociationRays(
scene: THREE.Scene,
rays: readonly L3VisualAssociationRay[],
rangedColor: THREE.Color,
bearingColor: THREE.Color,
): { lines: THREE.Line[]; anchors: THREE.Mesh[] } {
const lines: THREE.Line[] = [];
const anchors: THREE.Mesh[] = [];
for (const ray of rays) {
const origin = scenePoint(ray.originXyzM);
const endpoint = ray.anchorXyzM
? scenePoint(ray.anchorXyzM)
: scenePoint([
ray.originXyzM[0] + ray.directionXyz[0] * 25,
ray.originXyzM[1] + ray.directionXyz[1] * 25,
ray.originXyzM[2] + ray.directionXyz[2] * 25,
]);
const geometry = new THREE.BufferGeometry().setFromPoints([origin, endpoint]);
const material = ray.anchorXyzM
? ray.temporalHeld
? new THREE.LineDashedMaterial({
color: rangedColor,
dashSize: 0.55,
gapSize: 0.34,
transparent: true,
opacity: 0.88,
depthWrite: false,
})
: new THREE.LineBasicMaterial({
color: rangedColor,
transparent: true,
opacity: 0.96,
depthWrite: false,
})
: new THREE.LineDashedMaterial({
color: bearingColor,
dashSize: 0.75,
gapSize: 0.5,
transparent: true,
opacity: 0.56,
depthWrite: false,
});
const line = new THREE.Line(geometry, material);
if (!ray.anchorXyzM || ray.temporalHeld) line.computeLineDistances();
scene.add(line);
lines.push(line);
if (ray.anchorXyzM) {
const anchorGeometry = new THREE.SphereGeometry(0.28, 14, 10);
const anchorMaterial = new THREE.MeshBasicMaterial({ color: rangedColor });
const anchor = new THREE.Mesh(anchorGeometry, anchorMaterial);
anchor.position.copy(endpoint);
scene.add(anchor);
anchors.push(anchor);
}
}
return { lines, anchors };
}
function addBoxes(
scene: THREE.Scene,
boxes: readonly L3VisualBox[],
@@ -78,14 +169,26 @@ function addBoxes(
"position",
new THREE.BufferAttribute(boxSegments(box), 3),
);
const material = new THREE.LineBasicMaterial({
color: colors[box.status],
transparent: true,
opacity,
depthTest: true,
depthWrite: false,
});
const held = box.temporalStatus?.startsWith("e23-held") ?? false;
const material = held
? new THREE.LineDashedMaterial({
color: colors[box.status],
dashSize: 0.48,
gapSize: 0.3,
transparent: true,
opacity: Math.min(opacity, 0.62),
depthTest: true,
depthWrite: false,
})
: new THREE.LineBasicMaterial({
color: colors[box.status],
transparent: true,
opacity,
depthTest: true,
depthWrite: false,
});
const lines = new THREE.LineSegments(geometry, material);
if (held) lines.computeLineDistances();
scene.add(lines);
return lines;
});
@@ -96,6 +199,8 @@ export function L3PointPillarsScene({
mode,
bevCenterX = 30,
bevHalfExtent = 42,
associationRays = [],
fitToEvidence = false,
}: {
frame: Pick<
L3VisualFrame,
@@ -104,6 +209,8 @@ export function L3PointPillarsScene({
mode: L3VisualMode;
bevCenterX?: number;
bevHalfExtent?: number;
associationRays?: readonly L3VisualAssociationRay[];
fitToEvidence?: boolean;
}) {
const hostRef = useRef<HTMLDivElement | null>(null);
const [renderError, setRenderError] = useState<string | null>(null);
@@ -183,6 +290,19 @@ export function L3PointPillarsScene({
colors,
0.72,
);
const associationGeometry = addAssociationRays(
scene,
associationRays,
colors["true-positive"],
colors.matched,
);
const fittedBounds = fitToEvidence
? evidenceBounds(frame.predictionBoxes, associationRays)
: null;
const fittedCenter = fittedBounds?.getCenter(new THREE.Vector3())
?? new THREE.Vector3(mode === "bev" ? bevCenterX : 30, 0, 0);
const fittedSize = fittedBounds?.getSize(new THREE.Vector3())
?? new THREE.Vector3(bevHalfExtent * 2, 8, bevHalfExtent * 2);
const grid = new THREE.GridHelper(
80,
40,
@@ -197,15 +317,32 @@ export function L3PointPillarsScene({
material.opacity = 0.22;
material.depthWrite = false;
});
if (fitToEvidence) {
grid.position.x = fittedCenter.x;
grid.position.z = fittedCenter.z;
}
scene.add(grid);
const perspective = new THREE.PerspectiveCamera(52, 1, 0.1, 500);
perspective.position.set(-12, 18, 36);
if (fitToEvidence) {
const span = Math.max(fittedSize.x, fittedSize.z, fittedSize.y * 2, 10);
perspective.position.copy(fittedCenter).add(new THREE.Vector3(
-span * 0.9,
span * 0.72,
span * 1.2,
));
} else {
perspective.position.set(-12, 18, 36);
}
const orthographic = new THREE.OrthographicCamera(-40, 40, 40, -40, 0.1, 500);
orthographic.position.set(bevCenterX + 5, 100, 0);
orthographic.position.set(
fitToEvidence ? fittedCenter.x : bevCenterX + 5,
100,
fitToEvidence ? fittedCenter.z : 0,
);
orthographic.up.set(1, 0, 0);
const camera = mode === "bev" ? orthographic : perspective;
camera.lookAt(mode === "bev" ? bevCenterX : 30, 0, 0);
camera.lookAt(fittedCenter);
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = false;
@@ -213,7 +350,7 @@ export function L3PointPillarsScene({
controls.enablePan = true;
controls.enableZoom = true;
controls.screenSpacePanning = true;
controls.target.set(mode === "bev" ? bevCenterX : 30, 0, 0);
controls.target.copy(fittedCenter);
controls.update();
const render = () => renderer.render(scene, camera);
@@ -226,11 +363,18 @@ export function L3PointPillarsScene({
camera.aspect = width / height;
camera.updateProjectionMatrix();
} else {
const horizontal = bevHalfExtent;
const aspect = width / height;
const horizontal = fitToEvidence
? Math.max(
8,
fittedSize.z * 0.64,
fittedSize.x * aspect * 0.64,
)
: bevHalfExtent;
camera.left = -horizontal;
camera.right = horizontal;
camera.top = horizontal / (width / height);
camera.bottom = -horizontal / (width / height);
camera.top = horizontal / aspect;
camera.bottom = -horizontal / aspect;
camera.updateProjectionMatrix();
}
render();
@@ -249,12 +393,20 @@ export function L3PointPillarsScene({
lines.geometry.dispose();
(lines.material as THREE.Material).dispose();
});
associationGeometry.lines.forEach((line) => {
line.geometry.dispose();
(line.material as THREE.Material).dispose();
});
associationGeometry.anchors.forEach((anchor) => {
anchor.geometry.dispose();
(anchor.material as THREE.Material).dispose();
});
grid.geometry.dispose();
gridMaterials.forEach((material) => material.dispose());
renderer.dispose();
renderer.domElement.remove();
};
}, [bevCenterX, bevHalfExtent, frame, mode]);
}, [associationRays, bevCenterX, bevHalfExtent, fitToEvidence, frame, mode]);
return (
<div className="l3-visual-audit__scene" ref={hostRef}>
@@ -6,7 +6,6 @@ import {
type ComponentType,
} from "react";
import { Icon, StatusBadge } from "@nodedc/ui-react";
import {
LaboratoryEvidence,
LaboratorySelector,
@@ -14,7 +13,6 @@ import {
LaboratoryWorkTemplate,
type LaboratoryMethod,
type LaboratoryMethodComponent,
type LaboratoryOption,
} from "../../components/laboratory/LaboratoryPresentation";
import type { ObservationSessionSummary } from "../../core/observation/sessionArchive";
import { useObservationSessions } from "../../core/observation/useObservationSessions";
@@ -42,7 +40,6 @@ import type { WorkspaceRendererProps } from "../contracts";
import {
AdvancedLaboratoryResult,
advancedLaboratorySourceSession,
advancedLaboratoryWorkOptions,
isAdvancedLaboratoryWorkId,
} from "./AdvancedLaboratoryResult";
import {
@@ -50,15 +47,22 @@ import {
e30LaboratoryBrief, PUBLISHED_LABORATORY_BRIEF,
} from "./laboratoryArchiveBriefs";
import { useAdvancedLaboratoryCatalog } from "./useAdvancedLaboratoryCatalog";
import { buildLaboratoryProfiles, workOptionsForProfile } from "./laboratoryArchiveProfiles";
import type { LaboratoryProfileId, LaboratoryWorkId } from "./laboratoryArchiveProfiles";
import { useL34AnnotationCapability } from "./annotation/useL34AnnotationCapability";
import {
buildLaboratoryCatalog,
buildLaboratoryProfiles,
experimentOptionsForProfile,
workOptionsForExperiment,
} from "./laboratoryArchiveProfiles";
import type {
LaboratoryCatalogSeed,
LaboratoryExperimentId,
LaboratoryProfileId,
LaboratoryWorkId,
} from "./laboratoryArchiveProfiles";
type LaboratoryWorkspaceProps = WorkspaceRendererProps & {
SpatialView: ComponentType<WorkspaceRendererProps>;
};
function laboratoryWorkOrdinal(value: string): number {
const match = value.match(/\bE(\d+)\b/i);
return match ? Number(match[1]) : -1;
}
function digestFromContentId(value: string | null | undefined): string | null {
const digest = value?.split("-").at(-1) ?? "";
@@ -546,8 +550,15 @@ function PublishedLaboratoryResult({
}
export function LaboratoryArchiveWorkspace(props: LaboratoryWorkspaceProps) {
const [profileId, setProfileId] = useState<LaboratoryProfileId>("sensor-fusion");
const [workId, setWorkId] = useState<LaboratoryWorkId>("e28-local-surface");
const [profileId, setProfileId] = useState<LaboratoryProfileId>(
"rig-right-yolox-lidar-range-v1",
);
const [experimentId, setExperimentId] = useState<LaboratoryExperimentId>(
"ravnoves00-perception-benchmark-v1",
);
const [workId, setWorkId] = useState<LaboratoryWorkId>(
"l34-right-yolox-truth-island-freeze",
);
const initialWorkSelectedRef = useRef(false);
const [e28Model, setE28Model] = useState<LidarLocalSurfaceModel | null>(null);
const [e29Result, setE29Result] = useState<E29EvidenceResult | null>(null);
@@ -569,12 +580,10 @@ export function LaboratoryArchiveWorkspace(props: LaboratoryWorkspaceProps) {
&& session.status === "ready"
&& session.replayable
&& session.modalities.includes("point-cloud")
)).sort((left, right) => {
const ordinalDelta = laboratoryWorkOrdinal(right.lab?.labId ?? "")
- laboratoryWorkOrdinal(left.lab?.labId ?? "");
if (ordinalDelta !== 0) return ordinalDelta;
return (right.startedAtUtc ?? "").localeCompare(left.startedAtUtc ?? "");
}),
)).sort((left, right) => (
Date.parse(right.lab?.runCreatedAtUtc ?? right.startedAtUtc)
- Date.parse(left.lab?.runCreatedAtUtc ?? left.startedAtUtc)
)),
[sessions.items],
);
const sourceSessions = useMemo(
@@ -593,7 +602,7 @@ export function LaboratoryArchiveWorkspace(props: LaboratoryWorkspaceProps) {
},
});
const advancedResults: AdvancedLaboratoryResults = advanced.results;
const annotationWorkspace = useL34AnnotationCapability({ selectedWorkId: workId, l34Result: advancedResults.l34, l34dResult: advancedResults.l34d, l34eResult: advancedResults.l34e, e46Result: advancedResults.e46, e46aResult: advancedResults.e46a, onActionChange: props.onLaboratoryAnnotationActionChange });
useEffect(() => {
const controller = new AbortController();
setEvidenceLoading(true);
@@ -636,12 +645,12 @@ export function LaboratoryArchiveWorkspace(props: LaboratoryWorkspaceProps) {
: "";
return sensorToken ? sensorToken.toLocaleUpperCase("ru-RU") : "Сенсорный риг";
}, [props.deviceLabel, publishedWorks]);
const sensorWorks = useMemo(() => {
const items: LaboratoryOption<LaboratoryWorkId>[] = [];
const knownWorks = useMemo(() => {
const items: LaboratoryCatalogSeed[] = [];
if (e28Model) {
items.push({
id: "e28-local-surface",
label: "LAB E28 · локальная поверхность L2.6",
createdAtUtc: e28Model.createdAtUtc ?? "",
});
}
if (
@@ -650,50 +659,42 @@ export function LaboratoryArchiveWorkspace(props: LaboratoryWorkspaceProps) {
) {
items.push({
id: "e29-camera-geometry",
label: "LAB E29 · camera-first + geometry",
createdAtUtc: e29Result.createdAtUtc ?? "",
});
}
if (e30Result && sourceSessions.has(e30Result.sourceSessionId)) {
items.push({
id: "e30-evidence-review",
label: "LAB E30 · evidence review A2",
createdAtUtc: e30Result.createdAtUtc ?? "",
});
}
items.push(
...advancedLaboratoryWorkOptions(advanced.index).filter(
({ id }) => id !== "l3-pointpillars-visual-audit",
),
);
return items.sort(
(left, right) => laboratoryWorkOrdinal(right.label) - laboratoryWorkOrdinal(left.label),
);
return items.filter(({ createdAtUtc }) => createdAtUtc.trim());
}, [
advanced.index,
e28Model,
e29Result,
e30Result,
sourceSessions,
]);
const publicBenchmarkWorks = useMemo(
() => advancedLaboratoryWorkOptions(advanced.index).filter(
({ id }) => id === "l3-pointpillars-visual-audit",
),
[advanced.index],
const catalog = useMemo(
() => buildLaboratoryCatalog({
rigLabel,
knownWorks,
advancedIndex: advanced.index,
publishedWorks,
}),
[advanced.index, knownWorks, publishedWorks, rigLabel],
);
const profiles = useMemo(
() => buildLaboratoryProfiles({
rigLabel,
sensorAvailable: sensorWorks.length > 0,
publicBenchmarkAvailable: publicBenchmarkWorks.length > 0,
publishedAvailable: publishedWorks.length > 0,
}),
[publicBenchmarkWorks.length, publishedWorks.length, rigLabel, sensorWorks.length],
() => buildLaboratoryProfiles(catalog),
[catalog],
);
const publishedWorkOptions = publishedWorks.map((session) => ({
id: `session:${session.id}` as const,
label: `${session.lab?.labId ?? "LAB"} · ${laboratorySessionTitle(session)}`,
}));
const workOptions = workOptionsForProfile(
profileId, sensorWorks, publicBenchmarkWorks, publishedWorkOptions,
const experimentOptions = useMemo(
() => experimentOptionsForProfile(profileId, catalog),
[catalog, profileId],
);
const workOptions = useMemo(
() => workOptionsForExperiment(profileId, experimentId, catalog),
[catalog, experimentId, profileId],
);
const selectedSessionId = workId.startsWith("session:")
? workId.slice("session:".length)
@@ -719,25 +720,11 @@ export function LaboratoryArchiveWorkspace(props: LaboratoryWorkspaceProps) {
const firstProfile = profiles[0];
if (!firstProfile) return;
setProfileId(firstProfile.id);
if (firstProfile.id === "sensor-fusion") {
const firstWork = sensorWorks[0];
if (firstWork) {
setWorkId(firstWork.id);
initialWorkSelectedRef.current = true;
}
} else if (firstProfile.id === "public-benchmarks") {
const firstWork = publicBenchmarkWorks[0];
if (firstWork) {
setWorkId(firstWork.id);
initialWorkSelectedRef.current = true;
}
} else {
const first = publishedWorks[0];
if (first) {
setWorkId(`session:${first.id}`);
initialWorkSelectedRef.current = true;
}
}
return;
}
if (!experimentOptions.some((experiment) => experiment.id === experimentId)) {
const firstExperiment = experimentOptions[0];
if (firstExperiment) setExperimentId(firstExperiment.id);
return;
}
if (!initialWorkSelectedRef.current) {
@@ -754,11 +741,10 @@ export function LaboratoryArchiveWorkspace(props: LaboratoryWorkspaceProps) {
}, [
evidenceLoading,
advanced.indexLoading,
experimentId,
experimentOptions,
profileId,
profiles,
publishedWorks,
publicBenchmarkWorks,
sensorWorks,
sessions.state,
workId,
workOptions,
@@ -767,21 +753,18 @@ export function LaboratoryArchiveWorkspace(props: LaboratoryWorkspaceProps) {
const selectProfile = (next: LaboratoryProfileId) => {
setProfileId(next);
initialWorkSelectedRef.current = true;
if (next === "sensor-fusion") {
const first = sensorWorks[0];
if (first) setWorkId(first.id);
return;
}
if (next === "public-benchmarks") {
const first = publicBenchmarkWorks[0];
if (first) setWorkId(first.id);
return;
}
const first = publishedWorks[0];
if (!first) return;
const nextWork = `session:${first.id}` as const;
setWorkId(nextWork);
void sessions.replay(first.id);
const firstExperiment = experimentOptionsForProfile(next, catalog)[0];
if (!firstExperiment) return;
setExperimentId(firstExperiment.id);
const firstWork = workOptionsForExperiment(next, firstExperiment.id, catalog)[0];
if (firstWork) selectWork(firstWork.id);
};
const selectExperiment = (next: LaboratoryExperimentId) => {
setExperimentId(next);
initialWorkSelectedRef.current = true;
const firstWork = workOptionsForExperiment(profileId, next, catalog)[0];
if (firstWork) selectWork(firstWork.id);
};
const selectWork = (next: LaboratoryWorkId) => {
@@ -851,23 +834,39 @@ export function LaboratoryArchiveWorkspace(props: LaboratoryWorkspaceProps) {
data-viewer-focused={viewerFocused ? "true" : undefined}
>
<LaboratorySelector
eyebrow="ПРОФИЛЬ ЛАБОРАТОРНОГО КОНТУРА"
eyebrow="PIPELINE-КОНТУР"
title={profiles.find((profile) => profile.id === profileId)?.label ?? rigLabel}
description="Профиль фиксирует объект исследования, сенсорные модули и вычислительный контур. Исходные данные остаются read-only; профиль объединяет серию сопоставимых лабораторных работ."
label="Профиль"
description="Датированный снимок конкретного perception pipeline. Контуры отсортированы по времени последнего зафиксированного результата; источник остаётся read-only."
label="Pipeline"
value={profileId}
options={profiles}
searchable
onChange={selectProfile}
/>
<LaboratorySelector
eyebrow="ЛАБОРАТОРНАЯ РАБОТА"
eyebrow="ЭКСПЕРИМЕНТ"
title={experimentOptions.find((experiment) => (
experiment.id === experimentId
))?.label ?? "Эксперимент не выбран"}
description="Эксперимент объединяет сопоставимые прогоны и модификации только внутри выбранного pipeline-контракта."
label="Эксперимент"
value={experimentId}
options={experimentOptions}
disabled={experimentOptions.length === 0}
searchable
onChange={selectExperiment}
/>
<LaboratorySelector
eyebrow="ПРОГОН / ВАРИАНТ"
title={workOptions.find((work) => work.id === workId)?.label ?? "Работа не выбрана"}
description="Выберите один зафиксированный эксперимент. Ниже откроются его задача и структурированный результат; viewer появляется только у опубликованного серверного доказательства."
label="Работа"
description="Строго хронологический список immutable-прогонов: дата запуска, LAB-id и конкретная модификация. Ниже открывается только опубликованное серверное доказательство."
label="Прогон"
value={workId}
options={workOptions}
disabled={workOptions.length === 0}
searchable
onChange={selectWork}
/>
@@ -994,6 +993,7 @@ export function LaboratoryArchiveWorkspace(props: LaboratoryWorkspaceProps) {
</div>
)}
</div>
{annotationWorkspace}
</div>
);
}
@@ -0,0 +1,488 @@
import {
useEffect,
useRef,
useState,
type PointerEvent as ReactPointerEvent,
} from "react";
import { Select } from "@nodedc/ui-react";
import {
L34_ANNOTATION_CLASS_OPTIONS,
L34_ANNOTATION_UNMAPPED_OPTION,
annotationOperationKey,
type L34AnnotationCategory,
type L34AnnotationObject,
type L34AnnotationSourceFrame,
} from "../../../core/laboratory/l34Annotation";
export interface L34AnnotationObjectDraft
extends Omit<L34AnnotationObject, "category" | "origin"> {
category: L34AnnotationCategory | null;
origin: string;
}
export interface L34AnnotationComparisonObject {
objectId: string;
category: string;
boxXyxy: readonly [number, number, number, number];
}
export type L34AnnotationLayerMode = "overlay" | "candidate" | "review" | "source";
type Point = Readonly<{ x: number; y: number }>;
type PlaneSize = Readonly<{ width: number; height: number }>;
type ResizeHandle = "nw" | "ne" | "sw" | "se";
type Interaction =
| Readonly<{
kind: "draw";
pointerId: number;
start: Point;
end: Point;
}>
| Readonly<{
kind: "move" | "resize";
pointerId: number;
objectId: string;
start: Point;
originalBox: readonly [number, number, number, number];
handle?: ResizeHandle;
}>;
function boundedPoint(
clientX: number,
clientY: number,
svg: SVGSVGElement,
frame: L34AnnotationSourceFrame,
): Point {
const bounds = svg.getBoundingClientRect();
const x = (clientX - bounds.left) * frame.cameraWidth / bounds.width;
const y = (clientY - bounds.top) * frame.cameraHeight / bounds.height;
return {
x: Math.max(0, Math.min(frame.cameraWidth, x)),
y: Math.max(0, Math.min(frame.cameraHeight, y)),
};
}
function movedBox(
original: readonly [number, number, number, number],
start: Point,
end: Point,
frame: L34AnnotationSourceFrame,
): readonly [number, number, number, number] {
const width = original[2] - original[0];
const height = original[3] - original[1];
const left = Math.max(
0,
Math.min(frame.cameraWidth - width, original[0] + end.x - start.x),
);
const top = Math.max(
0,
Math.min(frame.cameraHeight - height, original[1] + end.y - start.y),
);
return [left, top, left + width, top + height];
}
function resizedBox(
original: readonly [number, number, number, number],
handle: ResizeHandle,
point: Point,
): readonly [number, number, number, number] {
let [left, top, right, bottom] = original;
if (handle.includes("n")) top = Math.min(point.y, bottom - 4);
if (handle.includes("s")) bottom = Math.max(point.y, top + 4);
if (handle.includes("w")) left = Math.min(point.x, right - 4);
if (handle.includes("e")) right = Math.max(point.x, left + 4);
return [left, top, right, bottom];
}
function boxesNearlyEqual(
left: readonly [number, number, number, number],
right: readonly [number, number, number, number],
): boolean {
return left.every((value, index) => Math.abs(value - right[index]) < 0.5);
}
function customLabelValue(label: string): string {
return `custom:${encodeURIComponent(label)}`;
}
function objectLabel(object: L34AnnotationObjectDraft): string {
if (object.category === null) return "Выберите класс";
if (object.category === "unmapped") {
return object.proposedLabel ?? L34_ANNOTATION_UNMAPPED_OPTION.label;
}
return L34_ANNOTATION_CLASS_OPTIONS.find(
({ value }) => value === object.category,
)?.label ?? object.category;
}
function boxFromPoints(
start: Point,
end: Point,
): readonly [number, number, number, number] {
return [
Math.min(start.x, end.x),
Math.min(start.y, end.y),
Math.max(start.x, end.x),
Math.max(start.y, end.y),
];
}
export function L34AnnotationCanvas({
frame,
objects,
drawingEnabled,
selectedObjectId,
unmappedLabels,
allowUnmapped = true,
comparisonObjects = [],
layerMode = "review",
newObjectOrigin = "manual",
onObjectsChange,
onSelectedObjectIdChange,
onRequestUnmappedLabel,
}: {
frame: L34AnnotationSourceFrame;
objects: readonly L34AnnotationObjectDraft[];
drawingEnabled: boolean;
selectedObjectId: string | null;
unmappedLabels: readonly string[];
allowUnmapped?: boolean;
comparisonObjects?: readonly L34AnnotationComparisonObject[];
layerMode?: L34AnnotationLayerMode;
newObjectOrigin?: string;
onObjectsChange: (objects: readonly L34AnnotationObjectDraft[]) => void;
onSelectedObjectIdChange: (objectId: string | null) => void;
onRequestUnmappedLabel: (objectId: string) => void;
}) {
const stageRef = useRef<HTMLDivElement | null>(null);
const [planeSize, setPlaneSize] = useState<PlaneSize>({ width: 1, height: 1 });
const [interaction, setInteraction] = useState<Interaction | null>(null);
useEffect(() => {
const host = stageRef.current;
if (!host) return;
const measure = () => {
const width = Math.max(host.clientWidth, 1);
const height = Math.max(host.clientHeight, 1);
const scale = Math.min(
width / frame.cameraWidth,
height / frame.cameraHeight,
);
setPlaneSize({
width: Math.max(frame.cameraWidth * scale, 1),
height: Math.max(frame.cameraHeight * scale, 1),
});
};
const observer = new ResizeObserver(measure);
observer.observe(host);
measure();
return () => observer.disconnect();
}, [frame.cameraHeight, frame.cameraWidth]);
useEffect(() => setInteraction(null), [frame.truthIslandSequence]);
const updateObject = (
objectId: string,
patch: Partial<L34AnnotationObjectDraft>,
) => {
let changed = false;
const next = objects.map((object) => {
if (object.objectId !== objectId) return object;
const keys = Object.keys(patch) as Array<keyof L34AnnotationObjectDraft>;
const scalarChanged = keys.some((key) => (
key !== "boxXyxy" && object[key] !== patch[key]
));
const boxChanged = patch.boxXyxy !== undefined
&& !boxesNearlyEqual(object.boxXyxy, patch.boxXyxy);
if (!scalarChanged && !boxChanged) return object;
changed = true;
return { ...object, ...patch };
});
if (changed) onObjectsChange(next);
};
const finishInteraction = (event: ReactPointerEvent<SVGSVGElement>) => {
if (!interaction || interaction.pointerId !== event.pointerId) return;
if (event.currentTarget.hasPointerCapture(event.pointerId)) {
event.currentTarget.releasePointerCapture(event.pointerId);
}
const end = boundedPoint(
event.clientX,
event.clientY,
event.currentTarget,
frame,
);
if (interaction.kind !== "draw") {
const box = interaction.kind === "move"
? movedBox(interaction.originalBox, interaction.start, end, frame)
: resizedBox(
interaction.originalBox,
interaction.handle ?? "se",
end,
);
updateObject(interaction.objectId, { boxXyxy: box });
setInteraction(null);
return;
}
const box = boxFromPoints(interaction.start, end);
setInteraction(null);
if (box[2] - box[0] < 4 || box[3] - box[1] < 4) return;
const objectId = annotationOperationKey("box").replace(":", "-");
onObjectsChange([
...objects,
{
objectId,
category: null,
proposedLabel: null,
origin: newObjectOrigin,
boxXyxy: box,
occluded: false,
truncated: false,
},
]);
onSelectedObjectIdChange(objectId);
};
const humanVisible = layerMode === "overlay" || layerMode === "review";
const candidateVisible = layerMode === "overlay" || layerMode === "candidate";
const humanLabelsVisible = layerMode === "review";
const candidateLabelsVisible = layerMode === "candidate";
const editable = humanVisible;
const effectiveDrawingEnabled = drawingEnabled && editable;
const draftBox = interaction?.kind === "draw"
? boxFromPoints(interaction.start, interaction.end)
: null;
const selectedObject = objects.find(
({ objectId }) => objectId === selectedObjectId,
) ?? null;
return (
<div className="l34-annotation-canvas" ref={stageRef}>
<div
className="l34-annotation-canvas__plane"
style={{ width: planeSize.width, height: planeSize.height }}
>
<img
src={frame.cameraUrl}
alt={`Исходный кадр ${frame.frameIndex} правой камеры без model overlay`}
draggable={false}
/>
<svg
viewBox={`0 0 ${frame.cameraWidth} ${frame.cameraHeight}`}
role="img"
aria-label={`Разметка кадра ${frame.frameIndex}: ${objects.length} рамок`}
data-drawing={effectiveDrawingEnabled ? "true" : undefined}
onPointerDown={(event) => {
if (!effectiveDrawingEnabled || event.button !== 0) return;
const point = boundedPoint(
event.clientX,
event.clientY,
event.currentTarget,
frame,
);
event.currentTarget.setPointerCapture(event.pointerId);
setInteraction({
kind: "draw",
pointerId: event.pointerId,
start: point,
end: point,
});
onSelectedObjectIdChange(null);
}}
onPointerMove={(event) => {
if (!interaction || interaction.pointerId !== event.pointerId) return;
const point = boundedPoint(
event.clientX,
event.clientY,
event.currentTarget,
frame,
);
if (interaction.kind === "draw") {
setInteraction({ ...interaction, end: point });
return;
}
const box = interaction.kind === "move"
? movedBox(interaction.originalBox, interaction.start, point, frame)
: resizedBox(
interaction.originalBox,
interaction.handle ?? "se",
point,
);
updateObject(interaction.objectId, { boxXyxy: box });
}}
onPointerUp={finishInteraction}
onPointerCancel={() => setInteraction(null)}
>
{candidateVisible ? comparisonObjects.map((object) => {
const [left, top, right, bottom] = object.boxXyxy;
return (
<rect
className="l34-annotation-canvas__comparison"
key={object.objectId}
x={left}
y={top}
width={right - left}
height={bottom - top}
/>
);
}) : null}
{humanVisible ? objects.map((object) => {
const [left, top, right, bottom] = object.boxXyxy;
return (
<rect
key={object.objectId}
x={left}
y={top}
width={right - left}
height={bottom - top}
data-selected={object.objectId === selectedObjectId ? "true" : undefined}
onPointerDown={(event) => {
if (effectiveDrawingEnabled || event.button !== 0) return;
event.stopPropagation();
const svg = event.currentTarget.ownerSVGElement;
if (!svg) return;
const point = boundedPoint(
event.clientX,
event.clientY,
svg,
frame,
);
svg.setPointerCapture(event.pointerId);
onSelectedObjectIdChange(object.objectId);
setInteraction({
kind: "move",
pointerId: event.pointerId,
objectId: object.objectId,
start: point,
originalBox: object.boxXyxy,
});
}}
/>
);
}) : null}
{draftBox ? (
<rect
className="l34-annotation-canvas__draft"
x={draftBox[0]}
y={draftBox[1]}
width={draftBox[2] - draftBox[0]}
height={draftBox[3] - draftBox[1]}
/>
) : null}
{selectedObject && editable && !effectiveDrawingEnabled ? ([
["nw", selectedObject.boxXyxy[0], selectedObject.boxXyxy[1]],
["ne", selectedObject.boxXyxy[2], selectedObject.boxXyxy[1]],
["sw", selectedObject.boxXyxy[0], selectedObject.boxXyxy[3]],
["se", selectedObject.boxXyxy[2], selectedObject.boxXyxy[3]],
] as const).map(([handle, x, y]) => (
<circle
className="l34-annotation-canvas__resize-handle"
data-handle={handle}
key={`${selectedObject.objectId}-${handle}`}
cx={x}
cy={y}
r={5}
onPointerDown={(event) => {
if (event.button !== 0) return;
event.stopPropagation();
const svg = event.currentTarget.ownerSVGElement;
if (!svg) return;
svg.setPointerCapture(event.pointerId);
setInteraction({
kind: "resize",
pointerId: event.pointerId,
objectId: selectedObject.objectId,
start: boundedPoint(
event.clientX,
event.clientY,
svg,
frame,
),
originalBox: selectedObject.boxXyxy,
handle,
});
}}
/>
)) : null}
</svg>
{candidateLabelsVisible ? comparisonObjects.map((object) => {
const [left, top] = object.boxXyxy;
return (
<div
className="l34-annotation-canvas__comparison-label"
key={`${object.objectId}-comparison-label`}
style={{
left: `${left / frame.cameraWidth * 100}%`,
top: `${top / frame.cameraHeight * 100}%`,
transform: top < 42 ? "none" : "translateY(-100%)",
}}
>
Candidate · {object.category}
</div>
);
}) : null}
{humanLabelsVisible ? objects.map((object) => {
const [left, top] = object.boxXyxy;
const below = top < 42;
const selected = object.objectId === selectedObjectId;
return (
<div
className="l34-annotation-canvas__label"
data-unclassified={object.category === null ? "true" : undefined}
data-unmapped={object.category === "unmapped" ? "true" : undefined}
data-selected={selected ? "true" : undefined}
key={`${object.objectId}-label`}
style={{
left: `${left / frame.cameraWidth * 100}%`,
top: `${top / frame.cameraHeight * 100}%`,
transform: below ? "none" : "translateY(-100%)",
}}
onPointerDown={() => onSelectedObjectIdChange(object.objectId)}
>
{selected ? <Select
label={`Класс рамки ${object.objectId}`}
value={object.category === "unmapped" && object.proposedLabel
? customLabelValue(object.proposedLabel)
: object.category ?? ""}
options={[
{ value: "", label: "Выберите класс", disabled: true },
...L34_ANNOTATION_CLASS_OPTIONS,
...(allowUnmapped ? [
...Array.from(new Set([
...unmappedLabels,
...(object.proposedLabel ? [object.proposedLabel] : []),
])).map((label) => ({
value: customLabelValue(label),
label: `Другой · ${label}`,
})),
L34_ANNOTATION_UNMAPPED_OPTION,
] : []),
]}
menuWidth={210}
minMenuWidth={210}
placement={below ? "bottom-start" : "top-start"}
onChange={(value) => {
if (value.startsWith("custom:")) {
updateObject(object.objectId, {
category: "unmapped",
proposedLabel: decodeURIComponent(value.slice(7)),
});
return;
}
if (value === L34_ANNOTATION_UNMAPPED_OPTION.value) {
onRequestUnmappedLabel(object.objectId);
return;
}
updateObject(object.objectId, {
category: value as L34AnnotationCategory,
proposedLabel: null,
});
}}
/> : <span>{objectLabel(object)}</span>}
</div>
);
}) : null}
</div>
</div>
);
}
@@ -0,0 +1,635 @@
import {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { createPortal } from "react-dom";
import {
Button,
Checker,
Icon,
IconButton,
SegmentedControl,
Select,
StatusBadge,
TextField,
ToastStack,
Window,
WindowFooterActions,
type ToastItem,
} from "@nodedc/ui-react";
import {
type L34AnnotationSourceFrame,
} from "../../../core/laboratory/l34Annotation";
import type { L34EResult } from "../../../core/laboratory/l34eSelfReviewDiagnostic";
import {
createL34FSession,
fetchL34FCase,
fetchL34FSession,
fetchL34FSessions,
freezeL34FSession,
saveL34FSession,
type L34FCase,
type L34FFrame,
type L34FObject,
type L34FSession,
type L34FSessionSummary,
} from "../../../core/laboratory/l34fAdjudication";
import {
L34AnnotationCanvas,
type L34AnnotationLayerMode,
type L34AnnotationObjectDraft,
} from "./L34AnnotationCanvas";
interface FrameDraft extends Omit<L34FFrame, "objects"> {
objects: readonly L34AnnotationObjectDraft[];
}
const MODES: ReadonlyArray<{ value: L34AnnotationLayerMode; label: string }> = [
{ value: "overlay", label: "ВМЕСТЕ" },
{ value: "candidate", label: "CANDIDATE" },
{ value: "review", label: "HUMAN" },
{ value: "source", label: "SOURCE" },
];
function errorMessage(error: unknown): string {
return error instanceof Error && error.message.trim()
? error.message
: "Операция L3.4F не выполнена.";
}
function draftsFromSession(session: L34FSession): Map<number, FrameDraft> {
return new Map(session.frames.map((frame) => [
frame.truthIslandSequence,
{
...frame,
objects: frame.objects.map((object) => ({ ...object })),
},
]));
}
function sessionLabel(session: L34FSessionSummary): string {
return `${session.title} · ${session.progress.reviewedFrameCount}/32`;
}
export function L34FAdjudicationWorkspace({
result,
onClose,
}: {
result: L34EResult;
onClose: () => void;
}) {
const [sessions, setSessions] = useState<readonly L34FSessionSummary[]>([]);
const [session, setSession] = useState<L34FSession | null>(null);
const [drafts, setDrafts] = useState<ReadonlyMap<number, FrameDraft>>(new Map());
const [selectedSequence, setSelectedSequence] = useState(result.caseOrder[0] ?? 1);
const [diagnosticCase, setDiagnosticCase] = useState<L34FCase | null>(null);
const [selectedObjectId, setSelectedObjectId] = useState<string | null>(null);
const [mode, setMode] = useState<L34AnnotationLayerMode>("overlay");
const [drawingEnabled, setDrawingEnabled] = useState(false);
const [loading, setLoading] = useState(true);
const [frameLoading, setFrameLoading] = useState(true);
const [busy, setBusy] = useState(false);
const [dirty, setDirty] = useState(false);
const [error, setError] = useState<string | null>(null);
const [titleDraft, setTitleDraft] = useState("");
const [saveOpen, setSaveOpen] = useState(false);
const [discardOpen, setDiscardOpen] = useState(false);
const [customLabelOpen, setCustomLabelOpen] = useState(false);
const [customLabelObjectId, setCustomLabelObjectId] = useState<string | null>(null);
const [customLabelDraft, setCustomLabelDraft] = useState("");
const [toasts, setToasts] = useState<ToastItem[]>([]);
const previousFocusRef = useRef<HTMLElement | null>(null);
const notify = useCallback((item: Omit<ToastItem, "id">) => {
setToasts((current) => [
...current.filter(({ tone }) => tone !== "loading"),
{ ...item, id: `${Date.now()}-${Math.random()}` },
]);
}, []);
useEffect(() => {
previousFocusRef.current = document.activeElement instanceof HTMLElement
? document.activeElement
: null;
const previousOverflow = document.body.style.overflow;
document.body.style.overflow = "hidden";
return () => {
document.body.style.overflow = previousOverflow;
previousFocusRef.current?.focus();
};
}, []);
useEffect(() => {
const controller = new AbortController();
setLoading(true);
setError(null);
void fetchL34FSessions(result.resultId, { signal: controller.signal })
.then(async (items) => {
if (controller.signal.aborted) return;
setSessions(items);
const latest = items.at(-1);
if (!latest) return;
const loaded = await fetchL34FSession(result.resultId, latest.sessionId, {
signal: controller.signal,
});
if (controller.signal.aborted) return;
setSession(loaded);
setTitleDraft(loaded.title);
setDrafts(draftsFromSession(loaded));
})
.catch((caught: unknown) => {
if (!controller.signal.aborted) setError(errorMessage(caught));
})
.finally(() => {
if (!controller.signal.aborted) setLoading(false);
});
return () => controller.abort();
}, [result.resultId]);
useEffect(() => {
const controller = new AbortController();
setFrameLoading(true);
setDiagnosticCase(null);
void fetchL34FCase(result.resultId, selectedSequence, {
signal: controller.signal,
}).then((value) => {
if (!controller.signal.aborted) setDiagnosticCase(value);
}).catch((caught: unknown) => {
if (!controller.signal.aborted) setError(errorMessage(caught));
}).finally(() => {
if (!controller.signal.aborted) setFrameLoading(false);
});
return () => controller.abort();
}, [result.resultId, selectedSequence]);
useEffect(() => setSelectedObjectId(null), [selectedSequence]);
const currentDraft = drafts.get(selectedSequence) ?? null;
const selectedObject = currentDraft?.objects.find(
({ objectId }) => objectId === selectedObjectId,
) ?? null;
const reviewedCount = [...drafts.values()].filter(({ reviewed }) => reviewed).length;
const objectCount = [...drafts.values()].reduce(
(total, frame) => total + frame.objects.length,
0,
);
const unresolvedCount = [...drafts.values()].reduce(
(total, frame) => total + frame.objects.filter((object) => (
object.category === null
|| (object.category === "unmapped" && !object.proposedLabel?.trim())
)).length,
0,
);
const customLabels = useMemo(() => Array.from(new Set(
[...drafts.values()].flatMap(({ objects }) => objects
.filter((object) => object.category === "unmapped" && object.proposedLabel)
.map((object) => object.proposedLabel!)),
)).sort((left, right) => left.localeCompare(right, "ru-RU")), [drafts]);
const sourceFrame = useMemo<L34AnnotationSourceFrame | null>(() => (
diagnosticCase ? {
resultId: diagnosticCase.diagnosticResultId,
truthIslandSequence: diagnosticCase.truthIslandSequence,
imageId: diagnosticCase.imageId,
frameIndex: diagnosticCase.frameIndex,
groupId: diagnosticCase.groupId,
role: "temporal",
sessionSeconds: diagnosticCase.sessionSeconds,
cameraWidth: diagnosticCase.cameraWidth,
cameraHeight: diagnosticCase.cameraHeight,
cameraSha256: diagnosticCase.sourceImageSha256,
cameraUrl: diagnosticCase.cameraUrl,
modelMaterialIncluded: false,
} : null
), [diagnosticCase]);
const setCurrentObjects = (objects: readonly L34AnnotationObjectDraft[]) => {
if (!currentDraft) return;
setDrafts((current) => {
const next = new Map(current);
next.set(selectedSequence, { ...currentDraft, objects, reviewed: false });
return next;
});
setDirty(true);
};
const createSession = async () => {
if (dirty) return;
setBusy(true);
setError(null);
try {
const created = await createL34FSession(result.resultId);
setSession(created);
setSessions((current) => [
...current.filter(({ sessionId }) => sessionId !== created.sessionId),
created,
]);
setDrafts(draftsFromSession(created));
setTitleDraft(created.title);
setDirty(false);
notify({
tone: "success",
title: "Сессия L3.4F создана",
description: "Human-слой скопирован из self-review; candidate остаётся read-only.",
});
} catch (caught) {
setError(errorMessage(caught));
} finally {
setBusy(false);
}
};
const selectSession = async (sessionId: string) => {
if (dirty || sessionId === session?.sessionId) return;
setBusy(true);
try {
const loaded = await fetchL34FSession(result.resultId, sessionId);
setSession(loaded);
setDrafts(draftsFromSession(loaded));
setTitleDraft(loaded.title);
} catch (caught) {
setError(errorMessage(caught));
} finally {
setBusy(false);
}
};
const savableFrames = (): readonly L34FFrame[] | null => {
if (drafts.size !== 32) return null;
const frames: L34FFrame[] = [];
for (const frame of drafts.values()) {
if (frame.objects.some(({ category }) => category === null)) return null;
frames.push({
...frame,
objects: frame.objects as readonly L34FObject[],
});
}
return frames.sort((left, right) => left.truthIslandSequence - right.truthIslandSequence);
};
const save = async () => {
if (!session || !titleDraft.trim()) return;
const frames = savableFrames();
if (!frames) return;
setBusy(true);
setError(null);
try {
const saved = await saveL34FSession(session, titleDraft.trim(), frames);
setSession(saved);
setDrafts(draftsFromSession(saved));
setSessions((current) => [
...current.filter(({ sessionId }) => sessionId !== saved.sessionId),
saved,
]);
setDirty(false);
setSaveOpen(false);
notify({
tone: "success",
title: "Ревизия L3.4F сохранена",
description: `${saved.progress.reviewedFrameCount}/32 кадров · revision ${saved.revision}.`,
});
} catch (caught) {
setError(errorMessage(caught));
} finally {
setBusy(false);
}
};
const freeze = async () => {
if (!session || dirty || !session.progress.complete) return;
setBusy(true);
setError(null);
try {
const frozen = await freezeL34FSession(session);
notify({
tone: "success",
title: "L3.4F зафиксирован",
description: `${frozen.metrics.changedFrameCount} изменённых кадров · не independent truth.`,
});
} catch (caught) {
setError(errorMessage(caught));
} finally {
setBusy(false);
}
};
const updateSelectedObject = (patch: Partial<L34AnnotationObjectDraft>) => {
if (!currentDraft || !selectedObject) return;
setCurrentObjects(currentDraft.objects.map((object) => (
object.objectId === selectedObject.objectId ? { ...object, ...patch } : object
)));
};
const requestCustomLabel = (objectId: string) => {
const object = currentDraft?.objects.find((item) => item.objectId === objectId);
if (!object) return;
setSelectedObjectId(objectId);
setCustomLabelObjectId(objectId);
setCustomLabelDraft(object.proposedLabel ?? "");
setCustomLabelOpen(true);
};
const confirmCustomLabel = () => {
const label = customLabelDraft.trim();
if (!currentDraft || !customLabelObjectId || !label) return;
setCurrentObjects(currentDraft.objects.map((object) => (
object.objectId === customLabelObjectId
? { ...object, category: "unmapped", proposedLabel: label }
: object
)));
setCustomLabelOpen(false);
setCustomLabelObjectId(null);
};
const orderedCases = result.caseOrder.map((sequence) => result.cases.find(
(item) => item.truthIslandSequence === sequence,
)).filter((item): item is L34EResult["cases"][number] => Boolean(item));
const selectedIndex = result.caseOrder.indexOf(selectedSequence);
const navigate = (offset: -1 | 1) => {
const index = (selectedIndex + offset + result.caseOrder.length) % result.caseOrder.length;
setSelectedSequence(result.caseOrder[index]);
};
const requestClose = () => dirty ? setDiscardOpen(true) : onClose();
useEffect(() => {
const handler = (event: KeyboardEvent) => {
if (event.key !== "Escape" || saveOpen || customLabelOpen || discardOpen) return;
event.preventDefault();
requestClose();
};
window.addEventListener("keydown", handler);
return () => window.removeEventListener("keydown", handler);
});
const workspace = (
<section
className="l34-annotation-workspace"
role="dialog"
aria-modal="true"
aria-label="Разбор конфликтов LAB L3.4F"
>
<header className="l34-annotation-workspace__toolbar">
<div className="l34-annotation-workspace__session-tools">
<Select
label="Сессия разбора конфликтов"
value={session?.sessionId ?? ""}
options={sessions.length ? sessions.map((item) => ({
value: item.sessionId,
label: sessionLabel(item),
})) : [{ value: "", label: "Сессия не создана", disabled: true }]}
disabled={busy}
searchable
menuWidth={380}
minMenuWidth={300}
onChange={(value) => void selectSession(value)}
/>
<IconButton
label="Создать сессию L3.4F"
disabled={busy || dirty}
onClick={() => void createSession()}
>
<Icon name="plus" size={16} />
</IconButton>
<Button
size="compact"
variant={drawingEnabled ? "accent" : "secondary"}
icon={<Icon name="edit" size={16} />}
aria-pressed={drawingEnabled}
disabled={!session || busy || mode === "candidate" || mode === "source"}
onClick={() => setDrawingEnabled((value) => !value)}
>
Рамка
</Button>
<Button
size="compact"
variant="primary"
icon={<Icon name="save" size={16} />}
disabled={!session || busy}
onClick={() => setSaveOpen(true)}
>
Сохранить
</Button>
<Button
size="compact"
variant="secondary"
disabled={busy || dirty || !session?.progress.complete}
onClick={() => void freeze()}
>
Зафиксировать L3.4F
</Button>
</div>
<div className="l34-annotation-workspace__frame-tools">
<IconButton label="Предыдущий конфликт" onClick={() => navigate(-1)}>
<Icon name="chevron-left" size={16} />
</IconButton>
<IconButton label="Следующий конфликт" onClick={() => navigate(1)}>
<Icon name="chevron-right" size={16} />
</IconButton>
<Select
label="Конфликтный кадр"
value={String(selectedSequence)}
options={orderedCases.map((item) => ({
value: String(item.truthIslandSequence),
label: `${item.truthIslandSequence}/32 · frame ${item.frameIndex} · severity ${item.diagnosticSummary.severityScore}`,
}))}
searchable
menuWidth={380}
minMenuWidth={300}
onChange={(value) => setSelectedSequence(Number(value))}
/>
<StatusBadge tone={dirty ? "warning" : reviewedCount === 32 ? "success" : "neutral"}>
{dirty ? "Не сохранено" : `${reviewedCount}/32 · не truth`}
</StatusBadge>
<IconButton label="Закрыть L3.4F" onClick={requestClose}>
<Icon name="close" size={16} />
</IconButton>
</div>
</header>
<div className="l34-annotation-workspace__stage">
{loading || frameLoading ? (
<div className="l34-annotation-workspace__state" role="status">
<span className="busy-indicator" aria-hidden="true" />
<strong>Открываем candidate и human слоя без model scores</strong>
</div>
) : error || !sourceFrame || !diagnosticCase ? (
<div className="l34-annotation-workspace__state" role="alert">
<Icon name="alert" size={18} />
<strong>{error ?? "Кадр L3.4F недоступен."}</strong>
</div>
) : (
<L34AnnotationCanvas
frame={sourceFrame}
objects={currentDraft?.objects ?? []}
comparisonObjects={diagnosticCase.candidateObjects.map((object) => ({
objectId: object.candidateId,
category: object.category,
boxXyxy: object.boxXyxy,
}))}
layerMode={mode}
newObjectOrigin="adjudicated_manual"
drawingEnabled={drawingEnabled && Boolean(session)}
selectedObjectId={selectedObjectId}
unmappedLabels={customLabels}
onObjectsChange={setCurrentObjects}
onSelectedObjectIdChange={setSelectedObjectId}
onRequestUnmappedLabel={requestCustomLabel}
/>
)}
</div>
<footer className="l34-annotation-workspace__inspector">
<div className="l34-annotation-workspace__source-state">
<span>RAVNOVES00 · sensor.camera.right · L3.4F</span>
<strong>
{diagnosticCase
? `frame ${diagnosticCase.frameIndex} · severity ${diagnosticCase.severityScore}`
: "Источник проверяется"}
</strong>
<small>Candidate visible · scores скрыты · engineering reference · не independent truth</small>
</div>
<SegmentedControl
value={mode}
items={[...MODES]}
label="Слои L3.4F"
onChange={setMode}
/>
<div className="l34f-adjudication-workspace__legend">
<span>Candidate read-only</span>
<span>Human editable</span>
</div>
{session && currentDraft ? (
<Checker
checked={currentDraft.reviewed}
label={currentDraft.objects.length ? "Конфликт разобран" : "Кадр подтверждён пустым"}
onChange={(reviewed) => {
setDrafts((current) => {
const next = new Map(current);
next.set(selectedSequence, { ...currentDraft, reviewed });
return next;
});
setDirty(true);
}}
/>
) : null}
{selectedObject && (mode === "overlay" || mode === "review") ? (
<div className="l34-annotation-workspace__object-tools">
<Checker
checked={selectedObject.occluded}
label="Перекрыт"
onChange={(occluded) => updateSelectedObject({ occluded })}
/>
<Checker
checked={selectedObject.truncated}
label="Обрезан"
onChange={(truncated) => updateSelectedObject({ truncated })}
/>
<IconButton
label="Удалить human-рамку"
onClick={() => {
if (!currentDraft) return;
setCurrentObjects(currentDraft.objects.filter(
({ objectId }) => objectId !== selectedObject.objectId,
));
setSelectedObjectId(null);
}}
>
<Icon name="trash" size={16} />
</IconButton>
</div>
) : null}
</footer>
<Window
open={customLabelOpen}
title="Другой объект"
subtitle="Название останется предложением до отдельной нормализации taxonomy."
size="sm"
onClose={() => setCustomLabelOpen(false)}
footer={(
<WindowFooterActions>
<Button onClick={() => setCustomLabelOpen(false)}>Отмена</Button>
<Button variant="primary" disabled={!customLabelDraft.trim()} onClick={confirmCustomLabel}>
Добавить название
</Button>
</WindowFooterActions>
)}
>
<TextField
label="Название объекта"
value={customLabelDraft}
maxLength={80}
autoFocus
placeholder="Например, детская коляска"
onChange={(event) => setCustomLabelDraft(event.target.value)}
/>
</Window>
<Window
open={saveOpen}
title="Сохранить ревизию L3.4F"
subtitle="Сохранение не превращает candidate-visible adjudication в ground truth."
size="md"
closeOnBackdrop={!busy}
closeOnEscape={!busy}
onClose={() => !busy && setSaveOpen(false)}
footer={(
<WindowFooterActions>
<Button disabled={busy} onClick={() => setSaveOpen(false)}>Отмена</Button>
<Button
variant="primary"
disabled={busy || !titleDraft.trim() || unresolvedCount > 0}
onClick={() => void save()}
>
{busy ? "Сохраняем" : "Сохранить ревизию"}
</Button>
</WindowFooterActions>
)}
>
<div className="l34-annotation-save-form">
<TextField
label="Название сессии"
value={titleDraft}
maxLength={160}
autoFocus
onChange={(event) => setTitleDraft(event.target.value)}
/>
<div className="l34-annotation-save-form__summary">
<span>Разобрано кадров</span><strong>{reviewedCount} / 32</strong>
<span>Human-объектов</span><strong>{objectCount}</strong>
<span>Без класса</span><strong>{unresolvedCount}</strong>
</div>
<p>
После 32/32 сохраните ревизию и нажмите «Зафиксировать L3.4F». Следующий независимый gate reviewer-B без candidate overlay.
</p>
</div>
</Window>
<Window
open={discardOpen}
title="Закрыть без сохранения?"
subtitle="Несохранённые изменения human-слоя будут потеряны."
size="sm"
onClose={() => setDiscardOpen(false)}
footer={(
<WindowFooterActions>
<Button onClick={() => setDiscardOpen(false)}>Остаться</Button>
<Button variant="danger" onClick={onClose}>Закрыть без сохранения</Button>
</WindowFooterActions>
)}
>
Сохранённая серверная ревизия останется неизменной.
</Window>
<ToastStack
items={toasts}
onDismiss={(id) => setToasts((current) => current.filter((item) => item.id !== id))}
/>
</section>
);
return typeof document === "undefined" ? null : createPortal(workspace, document.body);
}
@@ -0,0 +1,82 @@
import { useCallback, useEffect, useState, type ReactNode } from "react";
import type { L34RightYoloxTruthIslandResult } from "../../../core/laboratory/l34RightYoloxTruthIsland";
import type { L34DResult } from "../../../core/laboratory/l34dCumulativePostprocessing";
import type { L34EResult } from "../../../core/laboratory/l34eSelfReviewDiagnostic";
import type { E46BlindReviewResult } from "../../../core/laboratory/e46BlindReview";
import type { E46AAiEngineeringPreannotationResult } from "../../../core/laboratory/e46aAiEngineeringPreannotation";
import type { LaboratoryAnnotationAction } from "../../contracts";
import { L34AnnotationWorkspace } from "./L34AnnotationWorkspace";
import { L34FAdjudicationWorkspace } from "./L34FAdjudicationWorkspace";
export function useL34AnnotationCapability({
selectedWorkId,
l34Result,
l34dResult,
l34eResult,
e46Result,
e46aResult,
onActionChange,
}: {
selectedWorkId: string;
l34Result: L34RightYoloxTruthIslandResult | null;
l34dResult: L34DResult | null;
l34eResult: L34EResult | null;
e46Result: E46BlindReviewResult | null;
e46aResult: E46AAiEngineeringPreannotationResult | null;
onActionChange: (action: LaboratoryAnnotationAction | null) => void;
}): ReactNode {
const [open, setOpen] = useState(false);
const openWorkspace = useCallback(() => setOpen(true), []);
const available = selectedWorkId === "l34-right-yolox-truth-island-freeze"
&& l34Result
? { resultId: l34Result.resultId, workflow: "assisted-candidate" as const }
: selectedWorkId === "e46-detector-truth-island" && e46Result
? { resultId: e46Result.resultId, workflow: "independent-blind" as const }
: selectedWorkId === "e46a-ai-engineering-preannotation" && e46aResult
? { resultId: e46aResult.resultId, workflow: "engineering-preannotation" as const }
: selectedWorkId === "l34d-cumulative-postprocessing-candidate"
&& l34dResult
? { resultId: l34dResult.resultId, workflow: "prediction-hidden" as const }
: selectedWorkId === "l34e-self-review-diagnostic" && l34eResult
? { resultId: l34eResult.resultId, workflow: "adjudication" as const }
: null;
useEffect(() => {
if (!available) {
onActionChange(null);
setOpen(false);
return;
}
onActionChange({
label: open
? "Рабочая область открыта"
: available.workflow === "adjudication"
? "Разобрать конфликты"
: available.workflow === "independent-blind"
? "Независимая разметка"
: available.workflow === "engineering-preannotation"
? "Проверить и исправить"
: "Разметить данные",
disabled: open,
onClick: openWorkspace,
});
return () => onActionChange(null);
}, [available, onActionChange, open, openWorkspace]);
if (open && available?.workflow === "adjudication" && l34eResult) {
return (
<L34FAdjudicationWorkspace
result={l34eResult}
onClose={() => setOpen(false)}
/>
);
}
return open && available && available.workflow !== "adjudication" ? (
<L34AnnotationWorkspace
resultId={available.resultId}
workflow={available.workflow}
onClose={() => setOpen(false)}
/>
) : null;
}
@@ -1,10 +1,25 @@
import type { LaboratoryOption } from "../../components/laboratory/LaboratoryPresentation";
import type { AdvancedLaboratoryWorkId } from "../../core/laboratory/advancedIndex";
import type {
AdvancedLaboratoryIndexItem,
AdvancedLaboratoryWorkId,
} from "../../core/laboratory/advancedIndex";
import type { ObservationSessionSummary } from "../../core/observation/sessionArchive";
export type LaboratoryProfileId =
| "sensor-fusion"
| "public-benchmarks"
| "published-perception";
| "rig-camera-local-surface-v1"
| "rig-track-geometry-temporal-v1"
| "rig-ravnoves-perception-gate-v1"
| "rig-pointpillars-transfer-v1"
| "rig-right-yolox-lidar-range-v1"
| "rig-nvidia-ready-stack-v1"
| "rig-nvidia-dashcam-ready-stack-v1"
| "rig-nvidia-rectified-ready-stack-v1"
| "rig-nvidia-grounding-dino-front-v1"
| "rig-right-raw-fisheye-yolox-realtime-v1"
| "kitti-pointpillars-benchmark-v1"
| `published:${string}`;
export type LaboratoryExperimentId = string;
export type LaboratoryWorkId =
| "e28-local-surface"
@@ -13,48 +28,430 @@ export type LaboratoryWorkId =
| AdvancedLaboratoryWorkId
| `session:${string}`;
export function buildLaboratoryProfiles({
rigLabel,
sensorAvailable,
publicBenchmarkAvailable,
publishedAvailable,
}: {
rigLabel: string;
sensorAvailable: boolean;
publicBenchmarkAvailable: boolean;
publishedAvailable: boolean;
}): readonly LaboratoryOption<LaboratoryProfileId>[] {
const profiles: LaboratoryOption<LaboratoryProfileId>[] = [];
if (sensorAvailable) {
profiles.push({
id: "sensor-fusion",
label: `${rigLabel} · камера + LiDAR · control plane`,
});
}
if (publicBenchmarkAvailable) {
profiles.push({
id: "public-benchmarks",
label: "Публичные датасеты · внешний benchmark-контур",
});
}
if (publishedAvailable) {
profiles.push({
id: "published-perception",
label: `${rigLabel} · опубликованный perception pipeline`,
});
}
return profiles;
export interface LaboratoryCatalogSeed {
id: LaboratoryWorkId;
createdAtUtc: string;
}
export function workOptionsForProfile(
export interface LaboratoryCatalogEntry {
id: LaboratoryWorkId;
createdAtUtc: string;
profileId: LaboratoryProfileId;
profileName: string;
experimentId: LaboratoryExperimentId;
experimentName: string;
variantName: string;
}
interface KnownWorkDefinition {
profileId: Exclude<LaboratoryProfileId, `published:${string}`>;
profileName: (rigLabel: string) => string;
experimentId: LaboratoryExperimentId;
experimentName: string;
variantName: string;
}
const rig = (rigLabel: string): string => rigLabel.trim() || "Сенсорный риг";
const KNOWN_WORKS: Readonly<Record<Exclude<LaboratoryWorkId, `session:${string}`>, KnownWorkDefinition>> = {
"e28-local-surface": {
profileId: "rig-camera-local-surface-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · Camera-first + local-surface LiDAR`,
experimentId: "local-surface-semantic-geometry",
experimentName: "Local surface + semantic geometry",
variantName: "E28 · Local surface L2.6",
},
"e29-camera-geometry": {
profileId: "rig-camera-local-surface-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · Camera-first + local-surface LiDAR`,
experimentId: "local-surface-semantic-geometry",
experimentName: "Local surface + semantic geometry",
variantName: "E29 · Camera-first + geometry",
},
"e30-evidence-review": {
profileId: "rig-camera-local-surface-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · Camera-first + local-surface LiDAR`,
experimentId: "local-surface-semantic-geometry",
experimentName: "Local surface + semantic geometry",
variantName: "E30 · Evidence review A2",
},
"e31-source-binding": {
profileId: "rig-track-geometry-temporal-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · TrackGeometry + temporal occupied/unknown`,
experimentId: "track-geometry-source-contract",
experimentName: "Source contract + TrackGeometry",
variantName: "E31 · Source binding",
},
"e32-track-geometry": {
profileId: "rig-track-geometry-temporal-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · TrackGeometry + temporal occupied/unknown`,
experimentId: "track-geometry-source-contract",
experimentName: "Source contract + TrackGeometry",
variantName: "E32 · TrackGeometry v1",
},
"e33-worker-shadow": {
profileId: "rig-track-geometry-temporal-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · TrackGeometry + temporal occupied/unknown`,
experimentId: "temporal-shadow-envelope",
experimentName: "Temporal shadow envelope",
variantName: "E33 · Worker shadow 1×",
},
"e34-temporal-layer": {
profileId: "rig-track-geometry-temporal-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · TrackGeometry + temporal occupied/unknown`,
experimentId: "temporal-shadow-envelope",
experimentName: "Temporal shadow envelope",
variantName: "E34 · Occupied/unknown layer",
},
"e35-degradation-recovery": {
profileId: "rig-track-geometry-temporal-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · TrackGeometry + temporal occupied/unknown`,
experimentId: "temporal-shadow-envelope",
experimentName: "Temporal shadow envelope",
variantName: "E35 · Degradation recovery",
},
"e37-ravnoves-acceptance": {
profileId: "rig-ravnoves-perception-gate-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · RAVNOVES00 perception gate`,
experimentId: "ravnoves00-quality-gate",
experimentName: "RAVNOVES00 quality gate",
variantName: "E37 · Acceptance R0",
},
"e38-perception-baseline": {
profileId: "rig-ravnoves-perception-gate-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · RAVNOVES00 perception gate`,
experimentId: "ravnoves00-quality-gate",
experimentName: "RAVNOVES00 quality gate",
variantName: "E38 · Perception quality R1",
},
"e39-perception-refinement": {
profileId: "rig-ravnoves-perception-gate-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · RAVNOVES00 perception gate`,
experimentId: "ravnoves00-quality-gate",
experimentName: "RAVNOVES00 quality gate",
variantName: "E39 · Perception refinement R1",
},
"e40-perception-product-gate": {
profileId: "rig-ravnoves-perception-gate-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · RAVNOVES00 perception gate`,
experimentId: "ravnoves00-quality-gate",
experimentName: "RAVNOVES00 quality gate",
variantName: "E40 · Historical visible evaluation",
},
"e46-detector-truth-island": {
profileId: "rig-right-yolox-lidar-range-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · YOLOX camera-first + LiDAR range`,
experimentId: "ravnoves00-perception-benchmark-v1",
experimentName: "RAVNOVES00_RIGHT_YOLOX_TRUTH_ISLAND_V1",
variantName: "E46 · Independent source-only reviewer collection · not truth",
},
"e46a-ai-engineering-preannotation": {
profileId: "rig-right-yolox-lidar-range-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · YOLOX camera-first + LiDAR range`,
experimentId: "ravnoves00-perception-benchmark-v1",
experimentName: "RAVNOVES00_RIGHT_YOLOX_TRUTH_ISLAND_V1",
variantName: "E46A · AI engineering preannotation · 32/32 · not truth",
},
"e46b-temporal-motion": {
profileId: "rig-right-yolox-lidar-range-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · YOLOX camera-first + LiDAR range`,
experimentId: "ravnoves00-perception-benchmark-v1",
experimentName: "RAVNOVES00_RIGHT_YOLOX_TRUTH_ISLAND_V1",
variantName: "E46B · RIGHT temporal tracks + motion-state · 16/16",
},
"e46c-full-replay-world-tracks": {
profileId: "rig-right-yolox-lidar-range-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · YOLOX camera-first + LiDAR range`,
experimentId: "ravnoves00-perception-benchmark-v1",
experimentName: "RAVNOVES00_RIGHT_YOLOX_TRUTH_ISLAND_V1",
variantName: "E46C · full recorded RIGHT route/world tracks · 4489/4489",
},
"e46d-temporal-failure-audit": {
profileId: "rig-right-yolox-lidar-range-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · YOLOX camera-first + LiDAR range`,
experimentId: "ravnoves00-perception-benchmark-v1",
experimentName: "RAVNOVES00_RIGHT_YOLOX_TRUTH_ISLAND_V1",
variantName: "E46D · full replay temporal failure audit · automatic clips",
},
"e46e-ready-stack": {
profileId: "rig-nvidia-ready-stack-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · NVIDIA RT-DETR + NvDCF ready stack`,
experimentId: "nvidia-ready-stack-bakeoff-r1",
experimentName: "NVIDIA ready-stack bake-off R1",
variantName: "E46E · TrafficCamNet RT-DETR + NvDCF · full replay",
},
"e46f-dashcam-bakeoff": {
profileId: "rig-nvidia-dashcam-ready-stack-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · DashCamNet DetectNet_v2 + NvDCF`,
experimentId: "nvidia-mobile-detector-bakeoff-r2",
experimentName: "NVIDIA mobile-detector bake-off R2",
variantName: "E46F · DashCamNet DetectNet_v2 + NvDCF · rejected on raw fisheye",
},
"e46g-rectified-detector-bakeoff": {
profileId: "rig-nvidia-rectified-ready-stack-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · NVIDIA KB4 FRONT + TrafficCamNet`,
experimentId: "nvidia-calibrated-detector-bakeoff-r3",
experimentName: "NVIDIA calibrated ready-detector bake-off R3",
variantName: "E46G · KB4 nvdewarper · TrafficCamNet FRONT selected",
},
"e46h-full-rectified-front-replay": {
profileId: "rig-nvidia-rectified-ready-stack-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · NVIDIA KB4 FRONT + TrafficCamNet`,
experimentId: "nvidia-front-full-route-qualification-r1",
experimentName: "NVIDIA FRONT full-route qualification R1",
variantName: "E46H · full FRONT replay · large semantic false tracks",
},
"e46i-grounding-dino-full-replay": {
profileId: "rig-nvidia-grounding-dino-front-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · NVIDIA KB4 FRONT + Grounding DINO`,
experimentId: "nvidia-grounding-dino-full-route-shadow-r1",
experimentName: "NVIDIA Grounding DINO full-route shadow R1",
variantName: "E46I · semantic regression suppressed · awaiting temporal layer",
},
"e46j-raw-fisheye-realtime": {
profileId: "rig-right-raw-fisheye-yolox-realtime-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · raw KB4 fisheye + YOLOX-S`,
experimentId: "raw-fisheye-one-pass-realtime-r1",
experimentName: "Raw fisheye one-pass realtime qualification R1",
variantName: "E46J · full raw fisheye · realtime capacity passed",
},
"l34-right-yolox-truth-island-freeze": {
profileId: "rig-right-yolox-lidar-range-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · YOLOX camera-first + LiDAR range`,
experimentId: "ravnoves00-perception-benchmark-v1",
experimentName: "RAVNOVES00_RIGHT_YOLOX_TRUTH_ISLAND_V1",
variantName: "L3.4 · RIGHT YOLOX predictions frozen · awaiting truth",
},
"l34a-assisted-yolox-error-audit": {
profileId: "rig-right-yolox-lidar-range-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · YOLOX camera-first + LiDAR range`,
experimentId: "ravnoves00-perception-benchmark-v1",
experimentName: "RAVNOVES00_RIGHT_YOLOX_TRUTH_ISLAND_V1",
variantName: "L3.4A · Assisted YOLOX error audit · not truth",
},
"l34b-nested-box-consolidation-shadow": {
profileId: "rig-right-yolox-lidar-range-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · YOLOX camera-first + LiDAR range`,
experimentId: "ravnoves00-perception-benchmark-v1",
experimentName: "RAVNOVES00_RIGHT_YOLOX_TRUTH_ISLAND_V1",
variantName: "L3.4B · Nested-box consolidation shadow · not truth",
},
"l34c-tile-seam-stitch-shadow": {
profileId: "rig-right-yolox-lidar-range-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · YOLOX camera-first + LiDAR range`,
experimentId: "ravnoves00-perception-benchmark-v1",
experimentName: "RAVNOVES00_RIGHT_YOLOX_TRUTH_ISLAND_V1",
variantName: "L3.4C · Temporal front/left seam stitch · not truth",
},
"l34d-cumulative-postprocessing-candidate": {
profileId: "rig-right-yolox-lidar-range-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · YOLOX camera-first + LiDAR range`,
experimentId: "ravnoves00-perception-benchmark-v1",
experimentName: "RAVNOVES00_RIGHT_YOLOX_TRUTH_ISLAND_V1",
variantName: "L3.4D · Cumulative B+C candidate freeze · not truth",
},
"l34e-self-review-diagnostic": {
profileId: "rig-right-yolox-lidar-range-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · YOLOX camera-first + LiDAR range`,
experimentId: "ravnoves00-perception-benchmark-v1",
experimentName: "RAVNOVES00_RIGHT_YOLOX_TRUTH_ISLAND_V1",
variantName: "L3.4E · Self-review diagnostic disagreement audit · not truth",
},
"l34f-adjudicated-reference": {
profileId: "rig-right-yolox-lidar-range-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · YOLOX camera-first + LiDAR range`,
experimentId: "ravnoves00-perception-benchmark-v1",
experimentName: "RAVNOVES00_RIGHT_YOLOX_TRUTH_ISLAND_V1",
variantName: "L3.4F · Candidate-visible adjudicated engineering reference · not truth",
},
"l3-pointpillars-visual-audit": {
profileId: "kitti-pointpillars-benchmark-v1",
profileName: () => "KITTI · PointPillars external benchmark",
experimentId: "kitti-pointpillars-visual-audit",
experimentName: "PointPillars external validation",
variantName: "L3 · KITTI visual audit",
},
"l31-pointpillars-ravnoves": {
profileId: "rig-pointpillars-transfer-v1",
profileName: (rigLabel) => `${rig(rigLabel)} · PointPillars LiDAR transfer`,
experimentId: "pointpillars-ravnoves00-transfer",
experimentName: "RAVNOVES00 transfer",
variantName: "L3.1 · PointPillars hypotheses",
},
"l32-pointpillars-camera-review": {
profileId: "rig-pointpillars-transfer-v1",
profileName: (rigLabel) => `${rig(rigLabel)} · PointPillars LiDAR transfer`,
experimentId: "pointpillars-ravnoves00-transfer",
experimentName: "RAVNOVES00 transfer",
variantName: "L3.2 · RIGHT camera review",
},
"l33-camera-first-detector-review": {
profileId: "rig-right-yolox-lidar-range-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · YOLOX camera-first + LiDAR range`,
experimentId: "right-camera-detector-admission",
experimentName: "Detector admission + metric range",
variantName: "L3.3 · Detector review + LiDAR range",
},
};
const PUBLISHED_PIPELINE_NAMES: Readonly<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 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": "Camera + ego-motion fusion",
};
function timestamp(value: string): number {
const parsed = Date.parse(value);
return Number.isFinite(parsed) ? parsed : 0;
}
export function laboratoryDate(value: string): string {
const parsed = new Date(value);
if (!Number.isFinite(parsed.getTime())) return "0000-00-00";
return new Intl.DateTimeFormat("sv-SE", {
timeZone: "Europe/Moscow",
year: "numeric",
month: "2-digit",
day: "2-digit",
}).format(parsed);
}
export function laboratoryTimestamp(value: string): string {
const parsed = new Date(value);
if (!Number.isFinite(parsed.getTime())) return "0000-00-00 00:00:00 MSK";
const parts = Object.fromEntries(new Intl.DateTimeFormat("en-CA", {
timeZone: "Europe/Moscow",
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hourCycle: "h23",
}).formatToParts(parsed).map(({ type, value: part }) => [type, part]));
return `${parts.year}-${parts.month}-${parts.day} ${parts.hour}:${parts.minute}:${parts.second} MSK`;
}
function newestFirst(
left: Pick<LaboratoryCatalogEntry, "id" | "createdAtUtc">,
right: Pick<LaboratoryCatalogEntry, "id" | "createdAtUtc">,
): number {
return timestamp(right.createdAtUtc) - timestamp(left.createdAtUtc)
|| right.id.localeCompare(left.id);
}
function latestTimestamp(entries: readonly LaboratoryCatalogEntry[]): string {
const latest = [...entries].sort(newestFirst)[0];
return laboratoryTimestamp(latest?.createdAtUtc ?? "");
}
function pipelineIdForSession(session: ObservationSessionSummary): string {
const method = session.lab?.provenance.method;
if (method && typeof method === "object" && !Array.isArray(method)) {
const pipelineId = (method as Record<string, unknown>).pipeline_id;
if (typeof pipelineId === "string" && pipelineId.trim()) return pipelineId.trim();
}
return session.lab?.resultKind ?? "legacy-perception";
}
export function buildLaboratoryCatalog({
rigLabel,
knownWorks,
advancedIndex,
publishedWorks,
}: {
rigLabel: string;
knownWorks: readonly LaboratoryCatalogSeed[];
advancedIndex: readonly AdvancedLaboratoryIndexItem[];
publishedWorks: readonly ObservationSessionSummary[];
}): readonly LaboratoryCatalogEntry[] {
const seeded = new Map<LaboratoryWorkId, string>();
for (const work of knownWorks) seeded.set(work.id, work.createdAtUtc);
for (const work of advancedIndex) seeded.set(work.workId, work.createdAtUtc);
const entries: LaboratoryCatalogEntry[] = [];
for (const [id, createdAtUtc] of seeded) {
if (id.startsWith("session:")) continue;
const definition = KNOWN_WORKS[id as Exclude<LaboratoryWorkId, `session:${string}`>];
if (!definition) continue;
entries.push({
id,
createdAtUtc,
profileId: definition.profileId,
profileName: definition.profileName(rigLabel),
experimentId: definition.experimentId,
experimentName: definition.experimentName,
variantName: definition.variantName,
});
}
for (const session of publishedWorks) {
const pipelineId = pipelineIdForSession(session);
const profileId = `published:${pipelineId}` as const;
const pipelineName = PUBLISHED_PIPELINE_NAMES[pipelineId] ?? pipelineId;
entries.push({
id: `session:${session.id}`,
createdAtUtc: session.lab?.runCreatedAtUtc ?? session.startedAtUtc,
profileId,
profileName: `${rig(rigLabel)} RIGHT · ${pipelineName}`,
experimentId: `${profileId}:ravnoves00`,
experimentName: `RAVNOVES00 · ${pipelineName}`,
variantName: session.label,
});
}
return entries.sort(newestFirst);
}
export function buildLaboratoryProfiles(
catalog: readonly LaboratoryCatalogEntry[],
): readonly LaboratoryOption<LaboratoryProfileId>[] {
const groups = new Map<LaboratoryProfileId, LaboratoryCatalogEntry[]>();
for (const entry of catalog) {
const group = groups.get(entry.profileId) ?? [];
group.push(entry);
groups.set(entry.profileId, group);
}
return [...groups.entries()].map(([id, entries]) => ({
id,
label: `${latestTimestamp(entries)} · ${entries[0]?.profileName ?? id}`,
latest: Math.max(...entries.map(({ createdAtUtc }) => timestamp(createdAtUtc))),
})).sort((left, right) => right.latest - left.latest || left.label.localeCompare(right.label))
.map(({ id, label }) => ({ id, label }));
}
export function experimentOptionsForProfile(
profileId: LaboratoryProfileId,
sensorWorks: readonly LaboratoryOption<LaboratoryWorkId>[],
publicBenchmarkWorks: readonly LaboratoryOption<LaboratoryWorkId>[],
publishedWorks: readonly LaboratoryOption<LaboratoryWorkId>[],
catalog: readonly LaboratoryCatalogEntry[],
): readonly LaboratoryOption<LaboratoryExperimentId>[] {
const groups = new Map<LaboratoryExperimentId, LaboratoryCatalogEntry[]>();
for (const entry of catalog) {
if (entry.profileId !== profileId) continue;
const group = groups.get(entry.experimentId) ?? [];
group.push(entry);
groups.set(entry.experimentId, group);
}
return [...groups.entries()].map(([id, entries]) => ({
id,
label: `${latestTimestamp(entries)} · ${entries[0]?.experimentName ?? id}`,
latest: Math.max(...entries.map(({ createdAtUtc }) => timestamp(createdAtUtc))),
})).sort((left, right) => right.latest - left.latest || left.label.localeCompare(right.label))
.map(({ id, label }) => ({ id, label }));
}
export function workOptionsForExperiment(
profileId: LaboratoryProfileId,
experimentId: LaboratoryExperimentId,
catalog: readonly LaboratoryCatalogEntry[],
): readonly LaboratoryOption<LaboratoryWorkId>[] {
return profileId === "sensor-fusion"
? sensorWorks
: profileId === "public-benchmarks"
? publicBenchmarkWorks
: publishedWorks;
return catalog.filter((entry) => (
entry.profileId === profileId && entry.experimentId === experimentId
)).sort(newestFirst).map((entry) => ({
id: entry.id,
label: `${laboratoryTimestamp(entry.createdAtUtc)} · ${entry.variantName}`,
}));
}
@@ -20,6 +20,8 @@ function mergeResults(
return {
l3: next.l3 ?? current.l3,
l31: next.l31 ?? current.l31,
l32: next.l32 ?? current.l32,
l33: next.l33 ?? current.l33,
e31: next.e31 ?? current.e31,
e32: next.e32 ?? current.e32,
e33: next.e33 ?? current.e33,
@@ -29,6 +31,24 @@ function mergeResults(
e38: next.e38 ?? current.e38,
e39: next.e39 ?? current.e39,
e40: next.e40 ?? current.e40,
e46: next.e46 ?? current.e46,
e46a: next.e46a ?? current.e46a,
e46b: next.e46b ?? current.e46b,
e46c: next.e46c ?? current.e46c,
e46d: next.e46d ?? current.e46d,
e46e: next.e46e ?? current.e46e,
e46f: next.e46f ?? current.e46f,
e46g: next.e46g ?? current.e46g,
e46h: next.e46h ?? current.e46h,
e46i: next.e46i ?? current.e46i,
e46j: next.e46j ?? current.e46j,
l34: next.l34 ?? current.l34,
l34a: next.l34a ?? current.l34a,
l34b: next.l34b ?? current.l34b,
l34c: next.l34c ?? current.l34c,
l34d: next.l34d ?? current.l34d,
l34e: next.l34e ?? current.l34e,
l34f: next.l34f ?? current.l34f,
};
}
@@ -8,6 +8,20 @@ let fetchAdvancedLaboratoryResults;
let fetchAdvancedLaboratoryIndex;
let fetchAdvancedLaboratoryResult;
let AdvancedLaboratoryContractError;
let buildLaboratoryCatalog;
let buildLaboratoryProfiles;
let experimentOptionsForProfile;
let workOptionsForExperiment;
let fetchL34RightYoloxTruthIsland;
let fetchL34RightYoloxTruthIslandFrame;
let fetchL34AAssistedYoloxErrorAudit;
let fetchL34AAuditCase;
let fetchL34AnnotationSourceCatalog;
let fetchL34AnnotationSourceFrame;
let fetchL34AnnotationSeedFrame;
let fetchL34AnnotationLabels;
let createL34AnnotationSession;
let saveL34AnnotationSession;
const authority = {
commands_enabled: false,
@@ -25,6 +39,198 @@ function catalog(item) {
};
}
function l34() {
return {
result_id: `l34-right-yolox-truth-island-freeze-${"5".repeat(64)}`,
created_at_utc: "2026-08-01T00:30:00Z",
status: "predictions-frozen-awaiting-independent-truth",
profile_id: "RAVNOVES00_RIGHT_YOLOX_TRUTH_ISLAND_V1",
pipeline_id: "kb4-core3-yolox-eomt-k1-lidar-e23-temporal/v1",
source_session_id: "20260720T065719Z_viewer_live",
camera_source_id: "sensor.camera.right",
candidate: {
architecture: "YOLOX-S",
model_sha256: "c".repeat(64),
minimum_score: 0.25,
l33_result_id: `l33-camera-first-detector-review-${"3".repeat(64)}`,
},
truth_island: {
result_id: `e46-detector-truth-island-${"4".repeat(64)}`,
truth_state: "labels-unavailable",
},
metrics: {
frame_count: 32,
temporal_group_count: 20,
prediction_count: 268,
frames_with_predictions: 32,
class_counts: { car: 234, heavy_vehicle: 22, person: 7 },
accuracy_metrics_available: false,
},
frames: Array.from({ length: 32 }, (_, index) => ({
truth_island_sequence: index + 1,
image_id: index + 1,
frame_index: 1248 + index,
group_id: index === 0 ? "clip-stroller-person" : `anchor-${index + 1}`,
prediction_count: index < 12 ? 9 : 8,
maximum_score: 0.91,
})),
decision: {
candidate_predictions_frozen: true,
truth_labels_read: false,
candidate_accepted: false,
model_retraining_authorized: false,
next_gate: "two-independent-reviewer-truth-release",
},
limitations: ["right-camera recorded replay only"],
access: "read-only",
};
}
function l34a() {
const caseSummary = (index) => ({
truth_island_sequence: index + 1,
image_id: index + 2,
frame_index: 70 + index,
group_id: index === 4 ? "clip-stroller-person" : `anchor-${index + 1}`,
session_seconds: 10 + index,
source_image_sha256: "a".repeat(64),
summary: {
prediction_count: 8,
reference_count: 9,
true_positive: 7,
false_positive: 1,
false_negative: 2,
class_mismatch: index === 4 ? 1 : 0,
duplicate_false_positive: index === 14 ? 1 : 0,
unmatched_false_positive: index === 14 ? 0 : 1,
unmatched_false_negative: 1,
severity_score: index === 4 ? 5 : 3,
},
});
return {
result_id: `l34a-assisted-yolox-error-audit-${"6".repeat(64)}`,
created_at_utc: "2026-08-02T12:45:00Z",
status: "completed-assisted-candidate-error-audit-not-truth",
profile_id: "l34a-assisted-yolox-error-audit/v1",
pipeline_id: "ravnoves00-right-yolox-assisted-error-audit/v1",
source_session_id: "RAVNOVES00",
camera_source_id: "sensor.camera.right",
assisted_annotation: {
session_id: `l34-annotation-session-${"7".repeat(64)}`,
session_sha256: "8".repeat(64),
revision: 1,
updated_at_utc: "2026-08-02T12:38:19Z",
independent_truth_eligible: false,
},
metrics: {
frame_count: 32,
prediction_count: 268,
reference_count: 278,
true_positive: 234,
false_positive: 34,
false_negative: 44,
class_mismatch: 7,
duplicate_false_positive: 5,
unmatched_false_positive: 22,
unmatched_false_negative: 37,
precision_iou50: 234 / 268,
recall_iou50: 234 / 278,
f1_iou50: 6 / 7,
error_case_count: 26,
custom_reference_count: 9,
per_class: {
car: {
reference_count: 219,
true_positive: 210,
false_positive: 20,
false_negative: 9,
precision_iou50: 210 / 230,
recall_iou50: 210 / 219,
},
},
},
cases: Array.from({ length: 32 }, (_, index) => caseSummary(index)),
case_order: Array.from({ length: 32 }, (_, index) => index + 1),
decision: {
assisted_alignment_available: true,
blind_accuracy_available: false,
postprocessing_issue_confirmed: true,
ontology_gap_confirmed: true,
candidate_accepted: false,
model_retraining_authorized: false,
l35_blind_gate_open: false,
next_action: "visual engineering audit only",
},
limitations: ["candidate-seeded review is not truth"],
authority: {
ground_truth: false,
candidate_accepted: false,
commands_enabled: false,
navigation_or_safety_accepted: false,
},
ground_truth: false,
access: "read-only",
};
}
function annotationSession({ revision = 0, state = "draft", frames = [] } = {}) {
return {
schema_version: "missioncore.l34-annotation-session/v3",
session_id: `l34-annotation-session-${"7".repeat(64)}`,
result_id: l34().result_id,
truth_island_id: `e46-detector-truth-island-${"4".repeat(64)}`,
contract_id: "l34-assisted-candidate-review/v1",
title: "Разметка · LAB L3.4 · Рецензент 1",
reviewer_slot: 1,
revision,
state,
created_at_utc: "2026-08-01T08:00:00.000Z",
updated_at_utc: "2026-08-01T08:00:00.000Z",
blindness: {
candidate_identity_seen: true,
model_prelabels_seen: true,
model_predictions_seen: true,
model_scores_seen: false,
},
assistance: {
mode: "frozen-candidate-seeded",
independent_truth_eligible: false,
},
authority: {
ground_truth: false,
candidate_accepted: false,
commands_enabled: false,
navigation_or_safety_accepted: false,
},
progress: {
reviewed_frame_count: frames.length,
frame_count: 32,
object_count: frames.reduce((total, frame) => total + frame.objects.length, 0),
complete: frames.length === 32,
},
frames,
};
}
function l34dBlindAnnotationSession({ revision = 0, state = "draft", frames = [] } = {}) {
return {
...annotationSession({ revision, state, frames }),
result_id: `l34d-cumulative-postprocessing-candidate-${"d".repeat(64)}`,
contract_id: "l34d-prediction-hidden-review/v1",
title: "Разметка · LAB L3.4D · Рецензент 1",
blindness: {
candidate_identity_seen: true,
model_prelabels_seen: false,
model_predictions_seen: false,
model_scores_seen: false,
},
assistance: {
mode: "prediction-hidden-manual",
independent_truth_eligible: false,
},
};
}
function e31() {
return {
result_id: `e31-source-qualification-${"1".repeat(64)}`,
@@ -643,12 +849,573 @@ before(async () => {
fetchAdvancedLaboratoryIndex,
fetchAdvancedLaboratoryResult,
} = await server.ssrLoadModule("/src/core/laboratory/advancedIndex.ts"));
({
buildLaboratoryCatalog,
buildLaboratoryProfiles,
experimentOptionsForProfile,
workOptionsForExperiment,
} = await server.ssrLoadModule(
"/src/workspaces/laboratory/laboratoryArchiveProfiles.ts",
));
({
fetchL34RightYoloxTruthIsland,
fetchL34RightYoloxTruthIslandFrame,
} = await server.ssrLoadModule(
"/src/core/laboratory/l34RightYoloxTruthIsland.ts",
));
({
fetchL34AAssistedYoloxErrorAudit,
fetchL34AAuditCase,
} = await server.ssrLoadModule(
"/src/core/laboratory/l34aAssistedYoloxErrorAudit.ts",
));
({
fetchL34AnnotationSourceCatalog,
fetchL34AnnotationSourceFrame,
fetchL34AnnotationSeedFrame,
fetchL34AnnotationLabels,
createL34AnnotationSession,
saveL34AnnotationSession,
} = await server.ssrLoadModule(
"/src/core/laboratory/l34Annotation.ts",
));
});
after(async () => {
await server?.close();
});
test("LAB catalog is pipeline-scoped and ordered by real run time", () => {
const catalog = buildLaboratoryCatalog({
rigLabel: "K1",
knownWorks: [],
advancedIndex: [
{
workId: "e40-perception-product-gate",
resultId: `e40-perception-product-gate-${"4".repeat(64)}`,
createdAtUtc: "2026-07-28T11:07:52.003Z",
},
{
workId: "l32-pointpillars-camera-review",
resultId: `l32-pointpillars-camera-review-${"2".repeat(64)}`,
createdAtUtc: "2026-07-31T13:11:00.469Z",
},
{
workId: "l33-camera-first-detector-review",
resultId: `l33-camera-first-detector-review-${"3".repeat(64)}`,
createdAtUtc: "2026-07-31T21:08:08.728Z",
},
],
publishedWorks: [],
});
const profiles = buildLaboratoryProfiles(catalog);
assert.deepEqual(profiles.map(({ id }) => id), [
"rig-right-yolox-lidar-range-v1",
"rig-pointpillars-transfer-v1",
"rig-ravnoves-perception-gate-v1",
]);
assert.equal(
profiles[0].label,
"2026-08-01 00:08:08 MSK · K1 RIGHT · YOLOX camera-first + LiDAR range",
);
const experiments = experimentOptionsForProfile(profiles[0].id, catalog);
assert.equal(
experiments[0].label,
"2026-08-01 00:08:08 MSK · Detector admission + metric range",
);
assert.equal(
workOptionsForExperiment(profiles[0].id, experiments[0].id, catalog)[0].label,
"2026-08-01 00:08:08 MSK · L3.3 · Detector review + LiDAR range",
);
});
test("E46E is exposed as the newest independent NVIDIA pipeline", () => {
const catalog = buildLaboratoryCatalog({
rigLabel: "K1",
knownWorks: [],
advancedIndex: [
{
workId: "e46d-temporal-failure-audit",
resultId: `e46d-temporal-failure-audit-${"d".repeat(64)}`,
createdAtUtc: "2026-08-04T07:49:43.801363Z",
},
{
workId: "e46e-ready-stack",
resultId: `e46e-ready-stack-${"e".repeat(64)}`,
createdAtUtc: "2026-08-04T11:12:18.354Z",
},
],
publishedWorks: [],
});
const profiles = buildLaboratoryProfiles(catalog);
assert.equal(profiles[0].id, "rig-nvidia-ready-stack-v1");
assert.equal(
profiles[0].label,
"2026-08-04 14:12:18 MSK · K1 RIGHT · NVIDIA RT-DETR + NvDCF ready stack",
);
const experiments = experimentOptionsForProfile(profiles[0].id, catalog);
assert.deepEqual(experiments, [{
id: "nvidia-ready-stack-bakeoff-r1",
label: "2026-08-04 14:12:18 MSK · NVIDIA ready-stack bake-off R1",
}]);
assert.deepEqual(
workOptionsForExperiment(profiles[0].id, experiments[0].id, catalog),
[{
id: "e46e-ready-stack",
label: "2026-08-04 14:12:18 MSK · E46E · TrafficCamNet RT-DETR + NvDCF · full replay",
}],
);
});
test("E46J is the newest pipeline and exposes its exact Moscow timestamp", () => {
const catalog = buildLaboratoryCatalog({
rigLabel: "K1",
knownWorks: [],
advancedIndex: [
{
workId: "e46i-grounding-dino-full-replay",
resultId: `e46i-grounding-dino-full-replay-${"i".repeat(64)}`,
createdAtUtc: "2026-08-04T16:17:39.318Z",
},
{
workId: "e46j-raw-fisheye-realtime",
resultId: `e46j-raw-fisheye-realtime-${"a".repeat(64)}`,
createdAtUtc: "2026-08-04T19:37:51.841Z",
},
],
publishedWorks: [],
});
const profiles = buildLaboratoryProfiles(catalog);
assert.equal(profiles[0].id, "rig-right-raw-fisheye-yolox-realtime-v1");
assert.equal(
profiles[0].label,
"2026-08-04 22:37:51 MSK · K1 RIGHT · raw KB4 fisheye + YOLOX-S",
);
const experiments = experimentOptionsForProfile(profiles[0].id, catalog);
assert.equal(
experiments[0].label,
"2026-08-04 22:37:51 MSK · Raw fisheye one-pass realtime qualification R1",
);
assert.equal(
workOptionsForExperiment(profiles[0].id, experiments[0].id, catalog)[0].label,
"2026-08-04 22:37:51 MSK · E46J · full raw fisheye · realtime capacity passed",
);
});
test("decodes L3.4 only as a right-camera replay candidate awaiting truth", async () => {
const decoded = await fetchL34RightYoloxTruthIsland({
fetcher: async () => new Response(JSON.stringify({
schema_version: "missioncore.l34-right-yolox-truth-island-catalog/v1",
configured: true,
items: [l34()],
candidate_total: 1,
invalid_total: 0,
access: "read-only",
}), { status: 200 }),
});
assert.equal(decoded.cameraSourceId, "sensor.camera.right");
assert.equal(decoded.metrics.predictionCount, 268);
assert.equal(decoded.metrics.accuracyMetricsAvailable, false);
assert.equal(decoded.decision.candidateAccepted, false);
assert.equal(decoded.frames[0].groupId, "clip-stroller-person");
await assert.rejects(
() => fetchL34RightYoloxTruthIsland({
fetcher: async () => new Response(JSON.stringify({
schema_version: "missioncore.l34-right-yolox-truth-island-catalog/v1",
items: [{ ...l34(), camera_source_id: "sensor.camera.left" }],
access: "read-only",
}), { status: 200 }),
}),
/camera_source_id/,
);
});
test("binds an L3.4 visual frame to exact camera bytes and frozen boxes", async () => {
const resultId = l34().result_id;
const frame = await fetchL34RightYoloxTruthIslandFrame(resultId, 5, {
fetcher: async () => new Response(JSON.stringify({
schema_version: "missioncore.l34-right-yolox-truth-island-frame/v1",
result_id: resultId,
truth_island_sequence: 5,
image_id: 15,
frame_index: 1248,
group_id: "clip-stroller-person",
role: "temporal",
session_seconds: 160.142857292,
camera: {
width: 800,
height: 600,
byte_length: 479279,
sha256: "a".repeat(64),
},
prediction_rows_sha256: "b".repeat(64),
predictions: [{
label: "person",
score: 0.731547654,
bbox_xyxy: [230.005722, 311.645813, 317.241974, 421.966339],
}],
truth_labels_read: false,
access: "read-only",
}), { status: 200 }),
});
assert.equal(frame.cameraUrl, `/api/v1/laboratory/l34/results/${resultId}/frames/5/camera`);
assert.equal(frame.predictions[0].label, "person");
assert.deepEqual(frame.predictions[0].bboxXyxy, [
230.005722, 311.645813, 317.241974, 421.966339,
]);
assert.equal(frame.truthLabelsRead, false);
});
test("decodes L3.4A only as assisted diagnostic alignment", async () => {
const payload = l34a();
const decoded = await fetchL34AAssistedYoloxErrorAudit({
fetcher: async () => new Response(JSON.stringify({
schema_version: "missioncore.l34a-assisted-yolox-error-catalog/v1",
configured: true,
items: [payload],
candidate_total: 1,
invalid_total: 0,
access: "read-only",
}), { status: 200 }),
});
assert.equal(decoded.metrics.truePositive, 234);
assert.equal(decoded.metrics.duplicateFalsePositive, 5);
assert.equal(decoded.metrics.customReferenceCount, 9);
assert.equal(decoded.groundTruth, false);
assert.equal(decoded.decision.blindAccuracyAvailable, false);
assert.equal(decoded.decision.l35BlindGateOpen, false);
});
test("binds each L3.4A visual case to prediction and assisted-reference layers", async () => {
const resultId = l34a().result_id;
const decoded = await fetchL34AAuditCase(resultId, 5, {
fetcher: async () => new Response(JSON.stringify({
schema_version: "missioncore.l34a-assisted-yolox-error-case/v1",
result_id: resultId,
truth_island_sequence: 5,
image_id: 15,
frame_index: 1248,
group_id: "clip-stroller-person",
session_seconds: 160.1,
source_image_sha256: "a".repeat(64),
camera: { width: 800, height: 600 },
predictions: [{
prediction_index: 1,
category: "motorcycle",
score: 0.66,
box_xyxy: [190, 302, 335, 467],
verdict: "class_mismatch",
matched_object_id: "stroller-1",
match_iou: 0.78,
}],
annotations: [{
object_id: "stroller-1",
category: "unmapped",
proposed_label: "Детская коляска",
display_category: "unmapped:Детская коляска",
origin: "manual",
box_xyxy: [190, 302, 335, 467],
occluded: false,
truncated: false,
verdict: "class_mismatch",
matched_prediction_index: 1,
match_iou: 0.78,
}],
matches: [],
summary: {
prediction_count: 1,
reference_count: 1,
true_positive: 0,
false_positive: 1,
false_negative: 1,
class_mismatch: 1,
duplicate_false_positive: 0,
unmatched_false_positive: 0,
unmatched_false_negative: 0,
severity_score: 3,
},
ground_truth: false,
access: "read-only",
}), { status: 200 }),
});
assert.equal(decoded.predictions[0].verdict, "class_mismatch");
assert.equal(decoded.annotations[0].displayCategory, "unmapped:Детская коляска");
assert.equal(decoded.cameraUrl, `/api/v1/laboratory/l34a/results/${resultId}/cases/5/camera`);
assert.equal(decoded.groundTruth, false);
});
test("opens L3.4 annotation from a prediction-free source contract", async () => {
const resultId = l34().result_id;
const catalogPayload = {
schema_version: "missioncore.l34-annotation-source-catalog/v2",
result_id: resultId,
truth_island_id: `e46-detector-truth-island-${"4".repeat(64)}`,
contract: {
contract_id: "e46-detector-blind-review/v1",
classes: [
"person",
"bicycle",
"motorcycle",
"car",
"heavy_vehicle",
"static_obstacle",
"animal",
],
unmapped_class: {
value: "unmapped",
proposed_label_required: true,
normalization_state: "pending-adjudication",
},
},
frames: [{
truth_island_sequence: 5,
image_id: 15,
frame_index: 1248,
group_id: "clip-stroller-person",
role: "temporal",
session_seconds: 160.142857292,
camera: { width: 800, height: 600, sha256: "a".repeat(64) },
}],
frame_count: 1,
model_material_included: false,
access: "annotation-source-read-only",
};
const decoded = await fetchL34AnnotationSourceCatalog(resultId, {
fetcher: async () => new Response(JSON.stringify(catalogPayload), { status: 200 }),
});
assert.equal(decoded.frames[0].frameIndex, 1248);
assert.equal(decoded.modelMaterialIncluded, false);
assert.equal(JSON.stringify(decoded).includes("prediction"), false);
const frame = await fetchL34AnnotationSourceFrame(resultId, 5, {
fetcher: async () => new Response(JSON.stringify({
schema_version: "missioncore.l34-annotation-source/v1",
result_id: resultId,
...catalogPayload.frames[0],
camera_url: `/api/v1/laboratory/l34/results/${resultId}/annotation-source/frames/5/camera`,
model_material_included: false,
access: "annotation-source-read-only",
}), { status: 200 }),
});
assert.match(frame.cameraUrl, /annotation-source\/frames\/5\/camera$/);
const seed = await fetchL34AnnotationSeedFrame(resultId, 5, {
fetcher: async () => new Response(JSON.stringify({
schema_version: "missioncore.l34-annotation-seed/v1",
result_id: resultId,
truth_island_sequence: 5,
source_sha256: "a".repeat(64),
objects: [{
object_id: "seed-5-1",
category: "person",
proposed_label: null,
origin: "frozen_candidate_seed",
box_xyxy: [10, 20, 30, 40],
occluded: false,
truncated: false,
}],
assistance: {
mode: "frozen-candidate-seeded",
independent_truth_eligible: false,
},
model_material_included: true,
access: "assisted-annotation-seed-read-only",
}), { status: 200 }),
});
assert.equal(seed.objects[0].origin, "frozen_candidate_seed");
assert.equal(JSON.stringify(seed).includes("score"), false);
const labels = await fetchL34AnnotationLabels(resultId, {
fetcher: async () => new Response(JSON.stringify({
schema_version: "missioncore.l34-annotation-label-catalog/v1",
result_id: resultId,
items: [{
value: "Детская коляска",
normalization_state: "pending-adjudication",
}],
total: 1,
access: "assisted-annotation-taxonomy",
}), { status: 200 }),
});
assert.equal(labels[0].value, "Детская коляска");
await assert.rejects(
() => fetchL34AnnotationSourceCatalog(resultId, {
fetcher: async () => new Response(JSON.stringify({
...catalogPayload,
model_material_included: true,
}), { status: 200 }),
}),
/model material/,
);
});
test("opens L3.4D as prediction-hidden manual labeling without a seed endpoint", async () => {
const resultId = `l34d-cumulative-postprocessing-candidate-${"d".repeat(64)}`;
let requestedUrl = "";
const decoded = await fetchL34AnnotationSourceCatalog(resultId, {
fetcher: async (input) => {
requestedUrl = String(input);
return new Response(JSON.stringify({
schema_version: "missioncore.l34-annotation-source-catalog/v2",
result_id: resultId,
truth_island_id: `e46-detector-truth-island-${"4".repeat(64)}`,
contract: {
contract_id: "l34d-prediction-hidden-review/v1",
classes: [
"person",
"bicycle",
"motorcycle",
"car",
"heavy_vehicle",
"static_obstacle",
"animal",
],
unmapped_class: {
value: "unmapped",
proposed_label_required: true,
normalization_state: "pending-adjudication",
},
},
candidate_binding: {
result_id: resultId,
source_l34_result_id: `l34-right-yolox-truth-island-freeze-${"b".repeat(64)}`,
},
frames: [{
truth_island_sequence: 1,
image_id: 15,
frame_index: 1248,
group_id: "clip-stroller-person",
role: "temporal",
session_seconds: 160.142857292,
camera: { width: 800, height: 600, sha256: "a".repeat(64) },
}],
frame_count: 1,
model_material_included: false,
candidate_predictions_included: false,
prelabels_included: false,
access: "prediction-hidden-annotation-source-read-only",
}), { status: 200 });
},
});
assert.match(requestedUrl, /\/laboratory\/l34d\/results\//);
assert.equal(decoded.workflow, "prediction-hidden");
assert.equal(decoded.contractId, "l34d-prediction-hidden-review/v1");
await assert.rejects(
() => fetchL34AnnotationSeedFrame(resultId, 1, {
fetcher: async () => {
throw new Error("blind workflow must not request a seed");
},
}),
/не предоставляет предразметку/,
);
const created = await createL34AnnotationSession(resultId, {
fetcher: async (input) => {
assert.match(String(input), /\/laboratory\/l34d\/results\//);
return new Response(JSON.stringify(l34dBlindAnnotationSession()), { status: 200 });
},
});
assert.equal(created.assistance.mode, "prediction-hidden-manual");
assert.equal(created.contractId, "l34d-prediction-hidden-review/v1");
});
test("creates and saves a revisioned annotation draft without truth authority", async () => {
let requestBody;
const created = await createL34AnnotationSession(l34().result_id, {
fetcher: async (_input, init) => {
requestBody = JSON.parse(init.body);
return new Response(JSON.stringify(annotationSession()), { status: 200 });
},
});
assert.match(requestBody.idempotency_key, /^create:/);
assert.equal(created.progress.complete, false);
const frames = [{
truthIslandSequence: 5,
reviewed: true,
hardNegative: false,
objects: [{
objectId: "box-1",
category: "person",
proposedLabel: null,
origin: "frozen_candidate_seed",
boxXyxy: [10, 20, 30, 40],
occluded: true,
truncated: false,
}, {
objectId: "box-stroller",
category: "unmapped",
proposedLabel: "Детская коляска",
origin: "manual",
boxXyxy: [40, 50, 80, 90],
occluded: false,
truncated: false,
}],
}];
let saveBody;
const saved = await saveL34AnnotationSession(
created,
created.title,
frames,
"frozen-candidate-seeded",
{
fetcher: async (_input, init) => {
saveBody = JSON.parse(init.body);
return new Response(JSON.stringify(annotationSession({
revision: 1,
state: "saved",
frames: [{
truth_island_sequence: 5,
image_id: 15,
frame_index: 1248,
source_sha256: "a".repeat(64),
reviewed: true,
hard_negative: false,
objects: [{
object_id: "box-1",
category: "person",
proposed_label: null,
origin: "frozen_candidate_seed",
box_xyxy: [10, 20, 30, 40],
occluded: true,
truncated: false,
notes: null,
}, {
object_id: "box-stroller",
category: "unmapped",
proposed_label: "Детская коляска",
origin: "manual",
box_xyxy: [40, 50, 80, 90],
occluded: false,
truncated: false,
notes: null,
}],
}],
})), { status: 200 });
},
},
);
assert.equal(saveBody.expected_revision, 0);
assert.equal(saveBody.frames[0].objects[0].category, "person");
assert.equal(saveBody.frames[0].objects[1].category, "unmapped");
assert.equal(saveBody.frames[0].objects[1].proposed_label, "Детская коляска");
assert.equal(saveBody.assistance_mode, "frozen-candidate-seeded");
assert.equal(JSON.stringify(saveBody).includes("prediction"), false);
assert.equal(saved.revision, 1);
assert.equal(saved.frames[0].objects[0].occluded, true);
assert.equal(saved.frames[0].objects[1].proposedLabel, "Детская коляска");
});
test("decodes E31E40 from separate read-only catalogs", async () => {
const requests = [];
const items = [
@@ -0,0 +1,120 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import { after, before, test } from "node:test";
import { createServer } from "vite";
let server;
let fetchE46CVideoOverlay;
let selectE46CVideoFrame;
before(async () => {
server = await createServer({
appType: "custom",
logLevel: "silent",
server: { middlewareMode: true },
});
({ fetchE46CVideoOverlay, selectE46CVideoFrame } = await server.ssrLoadModule(
"/src/core/laboratory/e46cFullReplayWorldTracks.ts",
));
});
after(async () => {
await server?.close();
});
test("E46C decodes the complete path-free temporal video overlay", async () => {
const resultId = `e46c-full-replay-world-tracks-${"a".repeat(64)}`;
const start = 35.421857292;
const end = 484.044857292;
const step = (end - start) / 4488;
const frames = Array.from({ length: 4489 }, (_, frameIndex) => ({
frame_index: frameIndex,
session_seconds: frameIndex === 4488 ? end : start + frameIndex * step,
fusion_state: "fused",
objects: frameIndex === 20
? [{
bbox_xyxy: [100, 120, 240, 360],
category: "person",
score: 0.91,
route_track_id: 83,
world_track_id: 240001,
motion_state: "dynamic",
motion_confidence: 0.88,
track_hits: 12,
track_age_seconds: 1.2,
camera_evidence_current: true,
world_evidence_current: false,
}]
: [],
}));
const fetcher = async (input) => {
assert.equal(
String(input),
`/api/v1/laboratory/e46c/results/${resultId}/video-overlay`,
);
return new Response(JSON.stringify({
schema_version: "missioncore.e46c-recorded-video-overlay/v1",
result_id: resultId,
recorded_source: {
session_id: "20260720T065719Z_viewer_live",
source_id: "sensor.camera.right",
input_sha256: "4".repeat(64),
synchronization: "host-arrival-best-effort",
},
image_width: 800,
image_height: 600,
timeline_start_seconds: start,
timeline_end_seconds: end,
frame_count: 4489,
frames,
review_windows: [{
id: "dynamic-person-window",
kind: "target-motion",
class_group: "person",
start_seconds: 54,
end_seconds: 62,
target_source_track_ids: [83],
}],
ground_truth: false,
access: "read-only-full-route-diagnostic-video",
}), { status: 200, headers: { "Content-Type": "application/json" } });
};
const overlay = await fetchE46CVideoOverlay(resultId, { fetcher });
assert.equal(overlay.frameCount, 4489);
assert.equal(overlay.recordedSourceSessionId, "20260720T065719Z_viewer_live");
assert.equal(overlay.frames[20].objects[0].routeTrackId, 83);
assert.equal(overlay.frames[20].objects[0].displayCategory, "Человек");
assert.equal(overlay.reviewWindows[0].targetSourceTrackIds[0], 83);
assert.equal(JSON.stringify(overlay).includes("/Users/"), false);
assert.equal(selectE46CVideoFrame(overlay.frames, overlay.frames[20].sessionSeconds).frameIndex, 20);
});
test("E46C viewer opens with full VIDEO and reuses the admitted recorded player", async () => {
const [visual, videoScene, player] = await Promise.all([
readFile(
new URL(
"../src/workspaces/laboratory/E46CFullReplayWorldTracksVisual.tsx",
import.meta.url,
),
"utf8",
),
readFile(
new URL(
"../src/workspaces/laboratory/E46CRecordedVideoScene.tsx",
import.meta.url,
),
"utf8",
),
readFile(new URL("../src/components/RecordedFmp4Player.tsx", import.meta.url), "utf8"),
]);
assert.match(visual, /useState<E46CViewMode>\("video"\)/);
assert.match(visual, /\{ value: "video", label: "VIDEO" \}/);
assert.match(visual, /replayObservationSession\(overlay\.recordedSourceSessionId/);
assert.match(videoScene, /<RecordedFmp4Player/);
assert.match(videoScene, /selectE46CVideoFrame/);
assert.match(player, /requestVideoFrameCallback/);
assert.match(player, /controls=\{interactive\}/);
});
@@ -0,0 +1,122 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test, { after, before } from "node:test";
import { createServer } from "vite";
let server;
let fetchE46DTemporalFailureAudit;
before(async () => {
server = await createServer({
server: { middlewareMode: true },
appType: "custom",
logLevel: "silent",
});
({ fetchE46DTemporalFailureAudit } = await server.ssrLoadModule(
"/src/core/laboratory/e46dTemporalFailureAudit.ts",
));
});
after(async () => {
await server?.close();
});
test("E46D decodes ranked temporal clips without filesystem paths", async () => {
const e46dId = `e46d-temporal-failure-audit-${"a".repeat(64)}`;
const e46cId = `e46c-full-replay-world-tracks-${"b".repeat(64)}`;
const e26Id = `e10-integrated-perception-${"c".repeat(64)}`;
const clip = {
schema_version: "missioncore.e46d-temporal-review-clip/v1",
clip_id: `e46d-clip-01-${"d".repeat(20)}`,
rank: 1,
priority: "critical",
kind: "layer-blackout",
signal_id: `e46d-signal-${"d".repeat(20)}`,
start_seconds: 472.7,
event_start_seconds: 474.7,
event_end_seconds: 475.6,
end_seconds: 477.6,
start_frame: 4396,
end_frame: 4405,
route_track_ids: [1584, 1615],
world_track_ids: [],
evidence: {
before_object_count: 10,
minimum_object_count: 0,
after_object_count: 9,
duration_seconds: 1,
zero_frame_count: 10,
},
};
const metrics = {
route_frame_count: 4489,
route_span_seconds: 448.623,
object_observation_count: 20513,
route_track_count: 1461,
zero_object_frame_count: 600,
zero_object_frame_fraction: 0.133660058,
camera_held_observation_count: 5371,
camera_held_observation_fraction: 0.261833959,
detector_hold_episode_count: 785,
layer_blackout_episode_count: 71,
route_layer_gap_episode_count: 964,
route_id_rebirth_candidate_count: 11,
bbox_jump_episode_count: 3,
motion_state_flap_episode_count: 88,
world_binding_flap_episode_count: 238,
short_route_track_count: 572,
short_route_track_fraction: 0.391512663,
short_track_burst_episode_count: 48,
failure_signal_count: 2208,
review_clip_count: 1,
temporal_continuity_passed: false,
};
const fetcher = async () => new Response(JSON.stringify({
schema_version: "missioncore.e46d-temporal-failure-audit-catalog/v1",
items: [{
schema_version: "missioncore.e46d-temporal-failure-audit-view/v1",
result_id: e46dId,
created_at_utc: "2026-08-04T08:00:00Z",
source_e46c_result_id: e46cId,
source_e26_result_id: e26Id,
metrics,
acceptance: {
full_route_accounted: true,
temporal_continuity_passed: false,
independent_truth_available: false,
navigation_or_safety_accepted: false,
},
decision: {
temporal_regression_confirmed: true,
detector_gap_visible: true,
route_fragmentation_visible: true,
world_binding_instability_visible: true,
next_action: "rerun A/B",
},
limitations: ["diagnostic, not truth"],
review_clips: [clip],
ground_truth: false,
}],
}), { status: 200, headers: { "Content-Type": "application/json" } });
const result = await fetchE46DTemporalFailureAudit({ fetcher });
assert.equal(result.resultId, e46dId);
assert.equal(result.metrics.layerBlackoutEpisodeCount, 71);
assert.equal(result.reviewClips[0].kind, "layer-blackout");
assert.equal(result.reviewClips[0].evidence.before_object_count, 10);
assert.doesNotMatch(JSON.stringify(result), /Users|runtime|\\/);
});
test("E46D reuses the admitted E46C video viewer and canonical LAB template", async () => {
const [resultView, videoView] = await Promise.all([
readFile(new URL("../src/workspaces/laboratory/E46DTemporalFailureAuditResult.tsx", import.meta.url), "utf8"),
readFile(new URL("../src/workspaces/laboratory/E46CFullReplayWorldTracksVisual.tsx", import.meta.url), "utf8"),
]);
assert.match(resultView, /LaboratorySummary/);
assert.match(resultView, /LaboratoryEvidence/);
assert.match(resultView, /LaboratoryResultSummary/);
assert.match(resultView, /E46CFullReplayWorldTracksVisual/);
assert.match(resultView, /auditWindows=/);
assert.match(videoView, /автоматически найденному эпизоду E46D/);
assert.match(videoView, /RecordedVideoScene/);
});
@@ -0,0 +1,113 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test, { after, before } from "node:test";
import { createServer } from "vite";
let server;
let fetchE46EReadyStack;
before(async () => {
server = await createServer({
server: { middlewareMode: true },
appType: "custom",
logLevel: "silent",
});
({ fetchE46EReadyStack } = await server.ssrLoadModule(
"/src/core/laboratory/e46eReadyStack.ts",
));
});
after(async () => {
await server?.close();
});
test("E46E admits a stock full-replay result and its immutable video", async () => {
const resultId = `e46e-ready-stack-${"a".repeat(64)}`;
const identity = "b".repeat(64);
const fetcher = async () => new Response(JSON.stringify({
schema_version: "missioncore.e46e-ready-stack-catalog/v1",
items: [{
schema_version: "missioncore.e46e-ready-stack-view/v1",
result_id: resultId,
created_at_utc: "2026-08-04T09:00:00Z",
source_session_id: "20260720T065719Z_viewer_live",
camera_source_id: "sensor.camera.right",
metrics: {
frame_count: 4489,
route_duration_seconds: 448.623,
detection_observation_count: 12000,
track_observation_count: 12500,
detection_box_clipped_count: 0,
track_box_clipped_count: 488,
unique_track_count: 700,
mean_tracked_objects_per_frame: 2.784,
zero_detection_frame_count: 100,
zero_track_frame_count: 40,
tracker_recovered_frame_count: 80,
full_layer_blackout_event_count: 3,
route_id_gap_event_count: 20,
short_track_count: 100,
short_track_fraction: 0.142857,
track_class_switch_count: 2,
},
acceptance: {
full_route_accounted: true,
stock_detector_tracker_executed: true,
visual_overlay_available: true,
independent_truth_available: false,
navigation_or_safety_accepted: false,
},
decision: {
ready_stack_baseline_available: true,
custom_temporal_logic_used: false,
next_action: "compare A/B",
},
method: {
schema_version: "missioncore.laboratory-method/v1",
completeness: "complete",
execution_class: "hybrid",
pipeline_id: "e46e-ready-stack/v1",
components: [{
kind: "model",
name: "TrafficCamNet Transformer Lite",
version: "deployable_resnet50_v2.0",
role: "traffic detector",
identity_sha256: identity,
}],
},
limitations: ["not independent truth"],
video: {
url: `${resultId}/overlay.mp4`,
media_type: "video/mp4",
byte_length: 123456,
sha256: identity,
width: 800,
height: 600,
},
ground_truth: false,
}],
}), { status: 200, headers: { "Content-Type": "application/json" } });
const result = await fetchE46EReadyStack({ fetcher });
assert.equal(result.resultId, resultId);
assert.equal(result.metrics.frameCount, 4489);
assert.equal(result.metrics.trackerRecoveredFrameCount, 80);
assert.equal(result.metrics.trackBoxClippedCount, 488);
assert.equal(result.decision.customTemporalLogicUsed, false);
assert.equal(result.method.components[0].identitySha256, identity);
assert.equal(result.video.sha256, identity);
assert.doesNotMatch(JSON.stringify(result), /Users|D:\\|runtime\/experiments/);
});
test("E46E uses the fixed LAB anatomy and reusable evidence viewer", async () => {
const [resultView, visual] = await Promise.all([
readFile(new URL("../src/workspaces/laboratory/E46EReadyStackResult.tsx", import.meta.url), "utf8"),
readFile(new URL("../src/workspaces/laboratory/E46EReadyStackVisual.tsx", import.meta.url), "utf8"),
]);
assert.match(resultView, /LaboratorySummary/);
assert.match(resultView, /LaboratoryEvidence/);
assert.match(resultView, /LaboratoryResultSummary/);
assert.match(visual, /LaboratoryEvidenceViewer/);
assert.match(visual, /<video/);
assert.match(visual, /controls/);
});
@@ -0,0 +1,155 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test, { after, before } from "node:test";
import { createServer } from "vite";
let server;
let fetchE46FDashCamBakeoff;
before(async () => {
server = await createServer({
server: { middlewareMode: true },
appType: "custom",
logLevel: "silent",
});
({ fetchE46FDashCamBakeoff } = await server.ssrLoadModule(
"/src/core/laboratory/e46fDashCamBakeoff.ts",
));
});
after(async () => {
await server?.close();
});
function metrics(overrides = {}) {
return {
frame_count: 4489,
route_duration_seconds: 458.713353,
detection_observation_count: 23478,
track_observation_count: 24937,
detection_box_clipped_count: 0,
track_box_clipped_count: 1630,
unique_track_count: 545,
mean_tracked_objects_per_frame: 5.555135,
zero_detection_frame_count: 39,
zero_track_frame_count: 25,
tracker_recovered_frame_count: 29,
full_layer_blackout_event_count: 1,
route_id_gap_event_count: 0,
short_track_count: 37,
short_track_fraction: 0.06789,
track_class_switch_count: 0,
...overrides,
};
}
test("E46F preserves temporal gains but admits the semantic rejection", async () => {
const resultId = `e46f-dashcam-bakeoff-${"a".repeat(64)}`;
const baselineId = `e46e-ready-stack-${"b".repeat(64)}`;
const identity = "c".repeat(64);
const fetcher = async () => new Response(JSON.stringify({
schema_version: "missioncore.e46f-dashcam-bakeoff-catalog/v1",
items: [{
schema_version: "missioncore.e46f-dashcam-bakeoff-view/v1",
result_id: resultId,
created_at_utc: "2026-08-04T09:00:00Z",
source_session_id: "20260720T065719Z_viewer_live",
camera_source_id: "sensor.camera.right",
metrics: metrics(),
acceptance: {
full_route_accounted: true,
stock_detector_tracker_executed: true,
controlled_detector_only_change: true,
visual_overlay_available: true,
independent_truth_available: false,
navigation_or_safety_accepted: false,
},
method: {
schema_version: "missioncore.laboratory-method/v1",
completeness: "complete",
execution_class: "hybrid",
pipeline_id: "e46f-deepstream-dashcamnet-detectnet-v2-nvdcf/v1",
components: [{
kind: "model",
name: "NVIDIA DashCamNet",
version: "pruned_onnx_v1.0.4",
role: "moving-camera traffic-object detection",
identity_sha256: identity,
}],
},
limitations: ["not independent truth"],
comparison: {
controlled_change: "detector-only",
baseline_result_id: baselineId,
baseline_metrics: metrics({
unique_track_count: 909,
zero_track_frame_count: 28,
full_layer_blackout_event_count: 2,
short_track_fraction: 0.093509,
}),
delta: {
zero_track_frame_count: -3,
full_layer_blackout_event_count: -1,
unique_track_count: -364,
short_track_fraction: -0.025619,
},
large_box_visual_triage: {
area_ratio_threshold: 0.2,
candidate: {
observation_count: 2912,
frame_count: 2173,
track_id_count: 23,
class_observations: { person: 2912 },
},
baseline: {
observation_count: 191,
frame_count: 191,
track_id_count: 4,
class_observations: { car: 191 },
},
interpretation: "diagnostic visual triage; not precision/recall",
},
visual_review: {
status: "rejected-semantic-regression",
sample_video_seconds: [4.2, 9, 22, 44, 264, 418],
finding: "fisheye rim becomes huge person tracks",
next_action: "rectify valid FOV and repeat A/B",
},
verdict: "reject-dashcamnet-on-unrectified-fisheye",
},
video: {
url: `/api/v1/laboratory/e46f/results/${resultId}/overlay.mp4`,
media_type: "video/mp4",
byte_length: 150513080,
sha256: identity,
width: 800,
height: 600,
},
ground_truth: false,
}],
}), { status: 200, headers: { "Content-Type": "application/json" } });
const result = await fetchE46FDashCamBakeoff({ fetcher });
assert.equal(result.resultId, resultId);
assert.equal(result.comparison.verdict, "reject-dashcamnet-on-unrectified-fisheye");
assert.equal(result.comparison.delta.zeroTrackFrameCount, -3);
assert.equal(result.comparison.largeBoxVisualTriage.candidate.observationCount, 2912);
assert.equal(result.comparison.visualReview.status, "rejected-semantic-regression");
assert.equal(result.acceptance.navigationOrSafetyAccepted, false);
assert.equal(result.video.sha256, identity);
assert.doesNotMatch(JSON.stringify(result), /Users|D:\\|runtime\/experiments/);
});
test("E46F uses the fixed LAB anatomy and exposes the frozen full video", async () => {
const [resultView, visual] = await Promise.all([
readFile(new URL("../src/workspaces/laboratory/E46FDashCamBakeoffResult.tsx", import.meta.url), "utf8"),
readFile(new URL("../src/workspaces/laboratory/E46EReadyStackVisual.tsx", import.meta.url), "utf8"),
]);
assert.match(resultView, /LaboratorySummary/);
assert.match(resultView, /LaboratoryEvidence/);
assert.match(resultView, /LaboratoryResultSummary/);
assert.match(resultView, /semantic regression/);
assert.match(visual, /LaboratoryEvidenceViewer/);
assert.match(visual, /<video/);
assert.match(visual, /controls/);
});
@@ -0,0 +1,182 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test, { after, before } from "node:test";
import { createServer } from "vite";
let server;
let fetchE46GRectifiedDetectorBakeoff;
before(async () => {
server = await createServer({
server: { middlewareMode: true },
appType: "custom",
logLevel: "silent",
});
({ fetchE46GRectifiedDetectorBakeoff } = await server.ssrLoadModule(
"/src/core/laboratory/e46gRectifiedDetectorBakeoff.ts",
));
});
after(async () => {
await server?.close();
});
function viewMetrics(overrides = {}) {
return {
frame_count: 600,
detection_observation_count: 1200,
track_observation_count: 1300,
unique_track_count: 40,
mean_tracked_objects_per_frame: 2.166667,
zero_detection_frame_count: 4,
zero_track_frame_count: 2,
full_layer_blackout_event_count: 1,
short_track_fraction: 0.1,
large_track_observation_count: 3,
large_track_fraction: 0.002308,
...overrides,
};
}
function candidateMetrics(front) {
return {
source_frame_count: 600,
view_frame_count: 1800,
detection_observation_count: 3000,
track_observation_count: 3500,
unique_track_count: 80,
large_track_observation_count: 400,
large_track_fraction: 0.114286,
views: {
left: viewMetrics({ large_track_observation_count: 200 }),
front,
right: viewMetrics({ large_track_observation_count: 197 }),
},
};
}
test("E46G selects TrafficCamNet FRONT without granting perception authority", async () => {
const resultId = `e46g-rectified-detector-bakeoff-${"a".repeat(64)}`;
const identity = "b".repeat(64);
const video = (candidate) => ({
url: `/api/v1/laboratory/e46g/results/${resultId}/${candidate}.mp4`,
media_type: "video/mp4",
byte_length: 42_000_000,
sha256: identity,
width: 2880,
height: 544,
duration_seconds: 60,
view_order: ["left", "front", "right"],
});
const fetcher = async () => new Response(JSON.stringify({
schema_version: "missioncore.e46g-rectified-detector-bakeoff-catalog/v1",
items: [{
schema_version: "missioncore.e46g-rectified-detector-bakeoff-view/v1",
result_id: resultId,
created_at_utc: "2026-08-04T13:28:23.707Z",
source_session_id: "20260720T065719Z_viewer_live",
camera_source_id: "sensor.camera.right",
status: "selected-for-next-diagnostic-full-route",
selection: {
first_source_frame_index: 1000,
last_source_frame_index: 1599,
frame_count: 600,
},
rectification: {
provider: "NVIDIA Gst-nvdewarper",
provider_version: "DeepStream 9.1",
output_resolution: [960, 544],
horizontal_fov_degrees: 100,
retained_source_frame_index_range: [0, 4487],
excluded_source_tail_frame_count: 1,
view_order: ["left", "front", "right"],
},
metrics: {
trafficcamnet: candidateMetrics(viewMetrics({
track_observation_count: 4034,
zero_track_frame_count: 0,
})),
dashcamnet: candidateMetrics(viewMetrics({
track_observation_count: 1436,
zero_track_frame_count: 34,
})),
},
acceptance: {
exact_recorded_right_source_bound: true,
factory_calibration_bound: true,
official_nvidia_dewarper_executed: true,
stock_detector_tracker_executed: true,
same_views_and_frames_for_both_candidates: true,
visual_comparison_videos_available: true,
independent_truth_available: false,
candidate_accepted: false,
navigation_or_safety_accepted: false,
},
method: {
schema_version: "missioncore.laboratory-method/v1",
completeness: "complete",
execution_class: "hybrid",
pipeline_id: "e46g-k1-right-kb4-nvdewarper-ready-detector-bakeoff/v1",
components: [{
kind: "tool",
name: "XGRIDS K1 factory camera_1 KB4",
version: "KB4",
role: "fisheye source geometry",
identity_sha256: identity,
}],
},
limitations: ["not independent truth"],
comparison: {
visual_review: {
status: "selected-for-next-diagnostic",
reviewed_video_seconds: [0, 10, 20, 30, 40, 50],
selected_candidate: "trafficcamnet",
selected_view: "front",
excluded_views: ["left", "right"],
finding: "TrafficCamNet keeps more visible vehicles and people.",
risk: "Duplicate boxes remain and side views contain the camera mount.",
next_action: "Run complete FRONT replay.",
},
verdict: "select-trafficcamnet-front-only-for-e46h",
},
videos: {
trafficcamnet: video("trafficcamnet"),
dashcamnet: video("dashcamnet"),
},
ground_truth: false,
authority: {
ground_truth: false,
independent_truth: false,
metric_grade_reference: false,
candidate_accepted: false,
free_space_authority: false,
commands_enabled: false,
navigation_or_safety_accepted: false,
},
}],
}), { status: 200, headers: { "Content-Type": "application/json" } });
const result = await fetchE46GRectifiedDetectorBakeoff({ fetcher });
assert.equal(result.comparison.visualReview.selectedCandidate, "trafficcamnet");
assert.equal(result.comparison.visualReview.selectedView, "front");
assert.deepEqual(result.comparison.visualReview.excludedViews, ["left", "right"]);
assert.equal(result.metrics.trafficcamnet.views.front.zeroTrackFrameCount, 0);
assert.equal(result.metrics.dashcamnet.views.front.zeroTrackFrameCount, 34);
assert.equal(result.acceptance.candidateAccepted, false);
assert.doesNotMatch(JSON.stringify(result), /Users|D:\\|runtime\/experiments/);
});
test("E46G uses the fixed LAB anatomy and switches immutable comparison videos", async () => {
const [resultView, visual] = await Promise.all([
readFile(new URL("../src/workspaces/laboratory/E46GRectifiedDetectorBakeoffResult.tsx", import.meta.url), "utf8"),
readFile(new URL("../src/workspaces/laboratory/E46GRectifiedDetectorBakeoffVisual.tsx", import.meta.url), "utf8"),
]);
assert.match(resultView, /LaboratorySummary/);
assert.match(resultView, /LaboratoryEvidence/);
assert.match(resultView, /LaboratoryResultSummary/);
assert.match(resultView, /TrafficCamNet FRONT only/);
assert.match(visual, /LaboratoryEvidenceViewer/);
assert.match(visual, /TRAFFICCAMNET/);
assert.match(visual, /DASHCAMNET/);
assert.match(visual, /<video/);
});
@@ -0,0 +1,171 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test, { after, before } from "node:test";
import { createServer } from "vite";
let server;
let fetchE46HFullRectifiedFrontReplay;
before(async () => {
server = await createServer({
server: { middlewareMode: true },
appType: "custom",
logLevel: "silent",
});
({ fetchE46HFullRectifiedFrontReplay } = await server.ssrLoadModule(
"/src/core/laboratory/e46hFullRectifiedFrontReplay.ts",
));
});
after(async () => {
await server?.close();
});
test("E46H binds the full FRONT video to five semantic failures without promotion", async () => {
const resultId = `e46h-full-rectified-front-replay-${"a".repeat(64)}`;
const identity = "b".repeat(64);
const reviewWindows = [
["wall", 6, 10.9, 6, "semantic-false-positive"],
["shrub", 178.6, 180.3, 371, "semantic-false-positive"],
["ground", 250.7, 265.6, 481, "semantic-false-positive"],
["road", 392.2, 400.6, 822, "semantic-false-positive"],
["empty", 419.4, 426.9, null, "empty-scene-expected"],
["terrace", 440.8, 448.4, 927, "semantic-false-positive"],
].map(([id, start, end, track, verdict]) => ({
id,
label: `${start}${end}`,
start_seconds: start,
end_seconds: end,
source_track_id: track,
verdict,
}));
const payload = {
schema_version: "missioncore.e46h-full-rectified-front-replay-catalog/v1",
items: [{
schema_version: "missioncore.e46h-full-rectified-front-replay-view/v1",
result_id: resultId,
created_at_utc: "2026-08-04T14:17:42.899Z",
source_session_id: "20260720T065719Z_viewer_live",
camera_source_id: "sensor.camera.right",
status: "diagnostic-regression-large-semantic-false-tracks",
baseline_result_id: `e46g-rectified-detector-bakeoff-${"c".repeat(64)}`,
selection: {
first_source_frame_index: 0,
last_source_frame_index: 4487,
frame_count: 4488,
excluded_source_tail_frame_count: 1,
},
rectification: {
provider: "NVIDIA Gst-nvdewarper",
provider_version: "DeepStream 9.1",
projection: "fisheye-to-perspective",
view: "front",
output_resolution: [960, 544],
horizontal_fov_degrees: 100,
},
metrics: {
frame_count: 4488,
route_duration_seconds: 453.566029,
detection_observation_count: 26782,
track_observation_count: 30634,
unique_track_count: 942,
mean_tracked_objects_per_frame: 6.825758,
zero_detection_frame_count: 46,
zero_track_frame_count: 70,
tracker_recovered_frame_count: 2,
full_layer_blackout_event_count: 2,
route_id_gap_event_count: 0,
short_track_count: 91,
short_track_fraction: 0.096603,
track_class_switch_count: 0,
large_track_observation_count: 278,
large_track_fraction: 0.009075,
},
acceptance: {
exact_recorded_right_source_bound: true,
factory_calibration_bound: true,
official_nvidia_dewarper_executed: true,
selected_stock_detector_tracker_executed: true,
retained_route_accounted: true,
terminal_source_frame_excluded: true,
full_visual_review_completed: false,
independent_truth_available: false,
candidate_accepted: false,
navigation_or_safety_accepted: false,
},
decision: {
selected_provider: "front-trafficcamnet-stock-nvdcf",
custom_detector_or_tracker_logic_used: false,
provider_promoted: false,
next_action: "compare another ready provider",
},
method: {
schema_version: "missioncore.laboratory-method/v1",
completeness: "complete",
execution_class: "hybrid",
pipeline_id: "e46h-right-kb4-front-trafficcamnet-full-replay/v1",
components: [{
kind: "model",
name: "NVIDIA TrafficCamNet",
version: "2.0",
role: "ready detector",
identity_sha256: identity,
}],
},
limitations: ["not truth"],
visual_review: {
status: "full-continuous-and-targeted-review-completed",
complete_video_reviewed: true,
reviewed_video_range_seconds: [0, 448.8],
verdict: "useful-front-continuity-but-semantic-regression-blocks-promotion",
review_windows: reviewWindows,
finding: "five large false semantic tracks",
blackout_interpretation: "empty scene",
next_action: "compare another ready provider",
},
video: {
url: `/api/v1/laboratory/e46h/results/${resultId}/overlay.mp4`,
media_type: "video/mp4",
byte_length: 336027470,
sha256: identity,
width: 960,
height: 544,
duration_seconds: 448.8,
},
ground_truth: false,
authority: {
ground_truth: false,
independent_truth: false,
candidate_accepted: false,
commands_enabled: false,
navigation_or_safety_accepted: false,
},
}],
};
const result = await fetchE46HFullRectifiedFrontReplay({
fetcher: async () => new Response(JSON.stringify(payload), {
status: 200,
headers: { "Content-Type": "application/json" },
}),
});
assert.equal(result.metrics.frameCount, 4488);
assert.equal(result.visualReview.reviewWindows.length, 6);
assert.equal(result.visualReview.reviewWindows.filter(({ verdict }) => verdict === "semantic-false-positive").length, 5);
assert.equal(result.acceptance.candidateAccepted, false);
assert.equal(result.decision.providerPromoted, false);
assert.doesNotMatch(JSON.stringify(result), /Users|D:\\|runtime\/experiments/);
});
test("E46H uses the fixed LAB anatomy and a seekable full video navigator", async () => {
const [resultView, visual] = await Promise.all([
readFile(new URL("../src/workspaces/laboratory/E46HFullRectifiedFrontReplayResult.tsx", import.meta.url), "utf8"),
readFile(new URL("../src/workspaces/laboratory/E46HFullRectifiedFrontReplayVisual.tsx", import.meta.url), "utf8"),
]);
assert.match(resultView, /LaboratorySummary/);
assert.match(resultView, /LaboratoryEvidence/);
assert.match(resultView, /LaboratoryResultSummary/);
assert.match(resultView, /448,8/);
assert.match(visual, /LaboratoryEvidenceViewer/);
assert.match(visual, /reviewWindows/);
assert.match(visual, /<video/);
});
@@ -0,0 +1,169 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test, { after, before } from "node:test";
import { createServer } from "vite";
let server;
let fetchE46JRawFisheyeRealtime;
before(async () => {
server = await createServer({
server: { middlewareMode: true },
appType: "custom",
logLevel: "silent",
});
({ fetchE46JRawFisheyeRealtime } = await server.ssrLoadModule(
"/src/core/laboratory/e46jRawFisheyeRealtime.ts",
));
});
after(async () => {
await server?.close();
});
test("E46J binds full raw fisheye capacity to an honest visual exception", async () => {
const identity = "b".repeat(64);
const resultId = `e46j-raw-fisheye-realtime-${"a".repeat(64)}`;
const payload = {
schema_version: "missioncore.e46j-raw-fisheye-realtime-catalog/v1",
items: [{
schema_version: "missioncore.e46j-raw-fisheye-realtime-view/v1",
result_id: resultId,
created_at_utc: "2026-08-04T18:20:00.000Z",
status: "realtime-capacity-passed-awaiting-temporal-layer",
source: {
camera_source_id: "sensor.camera.right",
session_id: "20260720T065719Z_viewer_live",
resolution: [800, 600],
frame_count: 4489,
frame_rate: 10.003944527024467,
calibration_model: "KB4",
},
detector: {
architecture: "YOLOX-S",
source: "Megvii-BaseDetection/YOLOX release 0.1.1rc0",
license: "Apache-2.0",
runtime: "NVIDIA Triton 2.70.0 ONNX Runtime GPU backend",
},
detection: { minimum_score: 0.5, nms_iou_threshold: 0.45 },
metrics: {
frame_count: 4489,
failed_frame_count: 0,
detection_observation_count: 15499,
class_observation_counts: { car: 14229, person: 625, truck: 608 },
mean_detections_per_frame: 3.452662,
max_detections_per_frame: 9,
zero_detection_frame_count: 181,
longest_zero_detection_run_frames: 24,
core_capacity_fps: 47.84049,
core_path_mean_ms: 20.902796,
core_path_p95_ms: 25.355265,
inference_request_mean_ms: 12.437504,
inference_request_p95_ms: 16.414979,
gpu_utilization_mean_percent: 26.396947,
operator_shadow_window_frame_count: 75,
operator_shadow_person_frame_count: 35,
},
visual_review: {
reviewed_video_range_seconds: [0, 448.723],
verdict: "realtime-detector-progress-with-known-shadow-exception",
review_windows: [
["wall", 6, 10.9, "legacy-background-false-positive-suppressed"],
["shrub", 178.6, 180.3, "legacy-background-false-positive-suppressed"],
["ground", 250.7, 265.6, "legacy-background-false-positive-suppressed"],
["road", 392.2, 400.6, "legacy-background-false-positive-suppressed"],
["shadow", 419.4, 426.9, "operator-shadow-person-false-positive-observed"],
["terrace", 440.8, 448.4, "legacy-background-false-positive-suppressed"],
].map(([id, start, end, verdict]) => ({
id,
label: `${start}${end}`,
start_seconds: start,
end_seconds: end,
verdict,
})),
finding: "full raw fisheye retained",
known_error: "operator shadow becomes person",
},
acceptance: {
ten_hz_capacity_gate_passed: true,
latency_gate_passed: true,
full_raw_fisheye_retained: true,
},
decision: {
selected_provider: "megvii-yolox-s-0.1.1rc0",
realtime_capacity_passed: true,
ready_for_temporal_bakeoff: true,
provider_promoted: false,
next_action: "attach ready temporal tracker",
},
method: {
schema_version: "missioncore.laboratory-method/v1",
completeness: "complete",
execution_class: "hybrid",
pipeline_id: "e46j-k1-right-raw-kb4-yolox-s-one-pass/v1",
components: [{
kind: "model",
name: "YOLOX-S",
version: "0.1.1rc0",
role: "ready detector",
identity_sha256: identity,
}],
},
limitations: ["not truth", "no temporal identity"],
video: {
url: `/api/v1/laboratory/e46j/results/${resultId}/overlay.mp4`,
media_type: "video/mp4",
byte_length: 150563706,
sha256: identity,
width: 800,
height: 600,
frame_rate: 10.003944527024467,
frame_count: 4489,
duration_seconds: 448.723,
},
visuals: Object.fromEntries(
["full_route", "targeted_windows", "operator_shadow"].map((key) => [key, {
url: `/visual/${key}.png`,
media_type: "image/png",
byte_length: 1000,
sha256: identity,
}]),
),
ground_truth: false,
authority: {
ground_truth: false,
provider_promoted: false,
commands_enabled: false,
navigation_or_safety_accepted: false,
},
}],
};
const result = await fetchE46JRawFisheyeRealtime({
fetcher: async () => new Response(JSON.stringify(payload), {
status: 200,
headers: { "Content-Type": "application/json" },
}),
});
assert.equal(result.source.frameCount, 4489);
assert.equal(result.source.resolution.join("x"), "800x600");
assert.equal(result.metrics.coreCapacityFps, 47.84049);
assert.equal(result.metrics.operatorShadowPersonFrameCount, 35);
assert.equal(result.visualReview.reviewWindows.length, 6);
assert.equal(result.decision.providerPromoted, false);
assert.doesNotMatch(JSON.stringify(result), /Users|D:\\|runtime\/experiments/);
});
test("E46J uses the fixed LAB anatomy and seekable full video", async () => {
const [resultView, visual] = await Promise.all([
readFile(new URL("../src/workspaces/laboratory/E46JRawFisheyeRealtimeResult.tsx", import.meta.url), "utf8"),
readFile(new URL("../src/workspaces/laboratory/E46JRawFisheyeRealtimeVisual.tsx", import.meta.url), "utf8"),
]);
assert.match(resultView, /LaboratorySummary/);
assert.match(resultView, /LaboratoryEvidence/);
assert.match(resultView, /LaboratoryResultSummary/);
assert.match(resultView, /full raw fisheye realtime gate/);
assert.match(visual, /LaboratoryEvidenceViewer/);
assert.match(visual, /operator-shadow/);
assert.match(visual, /<video/);
assert.match(visual, /\[mode, selectedWindow\]/);
});

Some files were not shown because too many files have changed in this diff Show More