fix(lab): restore canonical RAV004 replay

This commit is contained in:
DCCONSTRUCTIONS
2026-08-29 21:08:49 +03:00
parent c30d77572e
commit 525ab74168
8 changed files with 594 additions and 14 deletions
@@ -81,7 +81,9 @@ export function LaboratoryRecordedClipPlayer({
sourceCount, sourceCount,
cameraRef, cameraRef,
cameraOverlay, cameraOverlay,
cameraControls,
alternativeScene, alternativeScene,
spatialControls,
onSequenceChange, onSequenceChange,
onPlayingChange, onPlayingChange,
onPlaybackRateChange, onPlaybackRateChange,
@@ -97,7 +99,9 @@ export function LaboratoryRecordedClipPlayer({
sourceCount: number; sourceCount: number;
cameraRef?: RefObject<HTMLDivElement | null>; cameraRef?: RefObject<HTMLDivElement | null>;
cameraOverlay?: ReactNode; cameraOverlay?: ReactNode;
cameraControls?: ReactNode;
alternativeScene?: ReactNode; alternativeScene?: ReactNode;
spatialControls?: ReactNode;
onSequenceChange: (sequence: number) => void; onSequenceChange: (sequence: number) => void;
onPlayingChange: (playing: boolean) => void; onPlayingChange: (playing: boolean) => void;
onPlaybackRateChange: (rate: number) => void; onPlaybackRateChange: (rate: number) => void;
@@ -173,6 +177,14 @@ export function LaboratoryRecordedClipPlayer({
aria-hidden={cameraPresentation === "primary"} aria-hidden={cameraPresentation === "primary"}
> >
{alternativeScene} {alternativeScene}
{spatialControls ? (
<div
className="laboratory-recorded-clip-player__pane-controls"
data-pane="spatial"
>
{spatialControls}
</div>
) : null}
</div> </div>
); );
const cameraPane = ( const cameraPane = (
@@ -195,6 +207,14 @@ export function LaboratoryRecordedClipPlayer({
/> />
) : null} ) : null}
{cameraPresentation !== "hidden" ? cameraOverlay : null} {cameraPresentation !== "hidden" ? cameraOverlay : null}
{cameraPresentation !== "hidden" && cameraControls ? (
<div
className="laboratory-recorded-clip-player__pane-controls"
data-pane="camera"
>
{cameraControls}
</div>
) : null}
</div> </div>
); );
return ( return (
@@ -119,6 +119,7 @@ export interface VegetationFullRouteLayer {
export interface VegetationFullRouteReview { export interface VegetationFullRouteReview {
sourceId: "RAVNOVES004TREE"; sourceId: "RAVNOVES004TREE";
sessionId: "20260828T130511Z_viewer_live"; sessionId: "20260828T130511Z_viewer_live";
linkedRouteReviewResultId: string;
sourceJobId: "recorded-camera-eb2783c5480d56bda07c8af0"; sourceJobId: "recorded-camera-eb2783c5480d56bda07c8af0";
sourceJobInputSha256: string; sourceJobInputSha256: string;
sourceStreamSha256: string; sourceStreamSha256: string;
@@ -692,6 +693,15 @@ function fullRouteReviewValue(value: unknown): VegetationFullRouteReview | null
"recorded-camera-eb2783c5480d56bda07c8af0", "recorded-camera-eb2783c5480d56bda07c8af0",
"vegetation.route_full_review.source_job_id", "vegetation.route_full_review.source_job_id",
); );
const linkedRouteReviewResultId = textValue(
row.linked_route_review_result_id,
"vegetation.route_full_review.linked_route_review_result_id",
);
if (!RESULT_ID.test(linkedRouteReviewResultId)) {
throw new VegetationShadowContractError(
"vegetation.route_full_review: linked route review identity invalid.",
);
}
exact(row.frame_count, 6830, "vegetation.route_full_review.frame_count"); exact(row.frame_count, 6830, "vegetation.route_full_review.frame_count");
exact(row.width, 800, "vegetation.route_full_review.width"); exact(row.width, 800, "vegetation.route_full_review.width");
exact(row.height, 600, "vegetation.route_full_review.height"); exact(row.height, 600, "vegetation.route_full_review.height");
@@ -810,6 +820,7 @@ function fullRouteReviewValue(value: unknown): VegetationFullRouteReview | null
return { return {
sourceId: "RAVNOVES004TREE", sourceId: "RAVNOVES004TREE",
sessionId: "20260828T130511Z_viewer_live", sessionId: "20260828T130511Z_viewer_live",
linkedRouteReviewResultId,
sourceJobId: "recorded-camera-eb2783c5480d56bda07c8af0", sourceJobId: "recorded-camera-eb2783c5480d56bda07c8af0",
sourceJobInputSha256, sourceJobInputSha256,
sourceStreamSha256, sourceStreamSha256,
@@ -33,6 +33,29 @@
pointer-events: auto; pointer-events: auto;
} }
.laboratory-recorded-clip-player__pane-controls {
position: absolute;
z-index: 6;
top: 0.6rem;
display: flex;
max-width: calc(100% - 1.2rem);
flex-wrap: wrap;
align-items: center;
gap: 0.38rem;
border-radius: var(--nodedc-radius-control-pill);
background: var(--nodedc-floating-surface);
padding: 0.28rem;
backdrop-filter: blur(var(--nodedc-blur-control));
}
.laboratory-recorded-clip-player__pane-controls[data-pane="spatial"] {
right: 0.6rem;
}
.laboratory-recorded-clip-player__pane-controls[data-pane="camera"] {
left: 0.6rem;
}
.laboratory-recorded-clip-player__split .laboratory-recorded-clip-player__split
> .nodedc-split-pane__separator::before { > .nodedc-split-pane__separator::before {
background: transparent; background: transparent;
@@ -1,5 +1,9 @@
import { useEffect, useState } from "react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Button, SegmentedControl } from "@nodedc/ui-react";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
import { LaboratoryRecordedClipPlayer } from "../../components/laboratory/LaboratoryRecordedClipPlayer";
import { RerunViewport } from "../../components/RerunViewport";
import { import {
LaboratoryEvidence, LaboratoryEvidence,
LaboratoryResultSummary, LaboratoryResultSummary,
@@ -7,7 +11,18 @@ import {
LaboratoryWorkTemplate, LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation"; } from "../../components/laboratory/LaboratoryPresentation";
import { import {
RecordedEvidenceSemanticMaskOverlay,
type RecordedEvidenceSemanticClass,
type RecordedEvidenceSemanticPaletteEntry,
} from "../../components/laboratory/RecordedEvidenceSemanticMaskOverlay";
import {
fetchVegetationShadowResult,
vegetationFullRouteMaskUrl,
vegetationVideoMaskUrl, vegetationVideoMaskUrl,
type VegetationFullRouteLayer,
type VegetationFullRouteReview,
type VegetationMixedRouteCase,
type VegetationMixedRouteReview,
type VegetationShadowResult, type VegetationShadowResult,
} from "../../core/laboratory/vegetationShadow"; } from "../../core/laboratory/vegetationShadow";
import { import {
@@ -15,11 +30,480 @@ import {
type M49TgsFullShadowResult, type M49TgsFullShadowResult,
} from "../../core/laboratory/m49TgsFullShadow"; } from "../../core/laboratory/m49TgsFullShadow";
import { M49TgsFullShadowEvidence } from "./M49TgsFullShadowEvidence"; import { M49TgsFullShadowEvidence } from "./M49TgsFullShadowEvidence";
import { recordedObservationSources } from "../../core/observation/recordedObservationSources";
import type { ObservationSessionReplayLaunch } from "../../core/observation/sessionArchive";
import { resolveObservationSessionReplay } from "../../core/observation/useObservationSessions";
import {
recordedSessionRerunProfile,
type RerunPlaybackController,
} from "../../core/observation/viewerProfile";
import type { ObservationSourceDescriptor } from "../../core/runtime/contracts";
import { defaultSceneSettings, type SceneSettings } from "../../sceneSettings";
import {
M48EvidenceModeRail,
type M48BlindEvidenceMode,
} from "./annotation/M48EvidenceModeControls";
function decimal(value: number, digits = 1): string { function decimal(value: number, digits = 1): string {
return value.toLocaleString("ru-RU", { maximumFractionDigits: digits }); return value.toLocaleString("ru-RU", { maximumFractionDigits: digits });
} }
const FULL_ROUTE_SEMANTIC_MODES = [
{ value: "city", label: "ГОРОД · EoMT" },
{ value: "vegetation", label: "ПРИРОДА · DDRNet" },
] as const;
function semanticPresentation(layer: VegetationFullRouteLayer): {
classes: readonly RecordedEvidenceSemanticClass[];
palette: readonly RecordedEvidenceSemanticPaletteEntry[];
} {
return {
classes: layer.taxonomy.map((item) => ({ id: item.classId, label: item.label })),
palette: layer.taxonomy.map((item) => ({
classId: item.classId,
color: item.classId === 0
? { kind: "transparent" as const }
: { kind: "diagnostic" as const, rgb: item.colorRgb },
})),
};
}
function nearestTgsCase(
cases: readonly VegetationMixedRouteCase[],
sequence: number,
): VegetationMixedRouteCase | null {
return cases.reduce<VegetationMixedRouteCase | null>((nearest, candidate) => (
!nearest
|| Math.abs(candidate.sourceSequence - sequence)
< Math.abs(nearest.sourceSequence - sequence)
? candidate
: nearest
), null);
}
function FullRouteReviewEvidence({
resultId,
review,
}: {
resultId: string;
review: VegetationFullRouteReview;
}) {
const [sequence, setSequence] = useState(1);
const [playing, setPlaying] = useState(false);
const [playbackRate, setPlaybackRate] = useState(1);
const [semanticLayer, setSemanticLayer] = useState<"city" | "vegetation">("vegetation");
const [showCameraSemantic, setShowCameraSemantic] = useState(true);
const [expanded, setExpanded] = useState(false);
const [evidenceMode, setEvidenceMode] = useState<M48BlindEvidenceMode>("3d");
const [cameraVisible, setCameraVisible] = useState(true);
const [sceneSettings, setSceneSettings] = useState<SceneSettings>(() => ({
...defaultSceneSettings,
accumulationSeconds: 12,
showPoints: true,
showTrajectory: true,
}));
const [videoSource, setVideoSource] = useState<ObservationSourceDescriptor | null>(null);
const [replayLaunch, setReplayLaunch] = useState<ObservationSessionReplayLaunch | null>(null);
const [videoError, setVideoError] = useState<string | null>(null);
const [linkedReview, setLinkedReview] = useState<VegetationMixedRouteReview | null>(null);
const [linkedReviewError, setLinkedReviewError] = useState<string | null>(null);
const spatialControllerRef = useRef<RerunPlaybackController | null>(null);
const frames = useMemo(
() => review.frameSourceTimesNs.map((sourceTimeNs, index) => ({
sequence: index + 1,
sourceTimeNs,
})),
[review.frameSourceTimesNs],
);
const layer = review[semanticLayer];
const semantic = useMemo(() => semanticPresentation(layer), [layer]);
const maskSequence = sequence - 1;
const prefetchSrcs = useMemo(() => showCameraSemantic
? Array.from({ length: 8 }, (_, offset) => maskSequence + offset + 1)
.filter((candidate) => candidate < review.frameCount)
.map((candidate) => vegetationFullRouteMaskUrl(resultId, semanticLayer, candidate))
: [], [maskSequence, resultId, review.frameCount, semanticLayer, showCameraSemantic]);
useEffect(() => {
const controller = new AbortController();
setVideoSource(null);
setReplayLaunch(null);
setVideoError(null);
void resolveObservationSessionReplay(review.sessionId, { signal: controller.signal })
.then((launch) => {
const source = recordedObservationSources(launch).find((candidate) => (
candidate.id === review.recordedMediaSourceId
&& candidate.modality === "video"
&& candidate.semanticChannelId === "camera.video.recorded"
&& candidate.delivery?.kind === "recorded-fmp4-manifest"
&& candidate.delivery.manifestGenerationSha256 === review.recordedMediaGenerationSha256
&& candidate.delivery.timelineStartSeconds === review.timelineStartSeconds
&& candidate.delivery.timelineEndSeconds >= review.timelineEndSeconds
));
if (!source) {
throw new Error("RIGHT-видео не совпало с sealed RAVNOVES004TREE timeline.");
}
if (!controller.signal.aborted) {
setVideoSource(source);
setReplayLaunch(launch);
}
})
.catch((caught: unknown) => {
if (!controller.signal.aborted) {
setVideoError(caught instanceof Error ? caught.message : "Записанное видео недоступно.");
}
});
return () => controller.abort();
}, [
review.recordedMediaGenerationSha256,
review.recordedMediaSourceId,
review.sessionId,
review.timelineEndSeconds,
review.timelineStartSeconds,
]);
useEffect(() => {
const controller = new AbortController();
setLinkedReview(null);
setLinkedReviewError(null);
void fetchVegetationShadowResult(review.linkedRouteReviewResultId, {
signal: controller.signal,
}).then((result) => {
if (
!result.routeReview
|| result.routeReview.sourceId !== review.sourceId
|| result.routeReview.sessionId !== review.sessionId
) {
throw new Error("TGS anchors имеют другую source identity.");
}
if (!controller.signal.aborted) setLinkedReview(result.routeReview);
}).catch((caught: unknown) => {
if (!controller.signal.aborted) {
setLinkedReviewError(caught instanceof Error ? caught.message : "TGS anchors недоступны.");
}
});
return () => controller.abort();
}, [review.linkedRouteReviewResultId, review.sessionId, review.sourceId]);
const spatialProfile = useMemo(() => replayLaunch ? recordedSessionRerunProfile({
sourceUrl: replayLaunch.viewerSourceUrl,
artifact: {
sourceUrl: replayLaunch.sourceUrl,
viewerSourceUrl: replayLaunch.viewerSourceUrl,
byteLength: replayLaunch.byteLength,
sha256: replayLaunch.sha256,
},
autoplayWhenReady: false,
presentationGate: "ready",
expectedTimelineStartSeconds: replayLaunch.timelineStartSeconds,
expectedTimelineEndSeconds: replayLaunch.timelineEndSeconds,
initialPlaybackStartSeconds: review.timelineStartSeconds,
view: "spatial",
viewResetGeneration: 0,
followTrajectory: false,
perceptionLayers: {
enabled: false,
detections2d: false,
segmentation: false,
cuboids3d: false,
},
perceptionRetryGeneration: 0,
lockPerceptionCameraInteraction: false,
}) : null, [replayLaunch, review.timelineStartSeconds]);
const activeFrame = frames.find((candidate) => candidate.sequence === sequence)
?? frames[0]
?? null;
const activeSourceTimeNsRef = useRef(activeFrame?.sourceTimeNs ?? null);
activeSourceTimeNsRef.current = activeFrame?.sourceTimeNs ?? null;
const handleSpatialControllerChange = useCallback((controller: RerunPlaybackController | null) => {
spatialControllerRef.current = controller;
const sourceTimeNs = activeSourceTimeNsRef.current;
if (!controller || sourceTimeNs === null) return;
controller.setPlaying(false);
controller.seek(sourceTimeNs);
}, []);
useEffect(() => {
const controller = spatialControllerRef.current;
if (!controller || !activeFrame) return;
controller.setPlaying(false);
controller.seek(activeFrame.sourceTimeNs);
}, [activeFrame]);
const selectedTgsCase = linkedReview
? nearestTgsCase(linkedReview.cases, sequence)
: null;
const selectTgsPlan = useCallback(() => {
if (!linkedReview) return;
const item = nearestTgsCase(linkedReview.cases, sequence);
if (!item) return;
setPlaying(false);
setSequence(item.sourceSequence);
setEvidenceMode("plan");
}, [linkedReview, sequence]);
const handleEvidenceModeChange = useCallback((nextMode: M48BlindEvidenceMode) => {
if (nextMode === "plan") {
selectTgsPlan();
return;
}
setEvidenceMode(nextMode);
}, [selectTgsPlan]);
const cameraPresentation = evidenceMode === "camera"
? "primary"
: cameraVisible ? "companion" : "hidden";
const spatialScene = evidenceMode === "plan" ? (
selectedTgsCase ? (
<div className="recorded-evidence-image-scene">
<img
src={selectedTgsCase.assets.tgs}
alt={`TGS costmap · sequence ${selectedTgsCase.sourceSequence}`}
draggable={false}
/>
<div className="m48-clip-player__pane-label" data-pane="spatial">
TGS COSTMAP · ЯКОРЬ {selectedTgsCase.sourceSequence} · {selectedTgsCase.tgs.occupiedCells} OCCUPIED
</div>
</div>
) : (
<div className="m4-replay-threat-visual__pane-status" role="status">
{linkedReviewError ?? "Открываем sealed TGS anchors…"}
</div>
)
) : spatialProfile ? (
<RerunViewport
profile={spatialProfile}
sceneSettings={sceneSettings}
onPlaybackControllerChange={handleSpatialControllerChange}
/>
) : (
<div className="m4-replay-threat-visual__pane-status" role="status">
Открываем sealed RRD, source points и SLAM trajectory
</div>
);
return (
<LaboratoryEvidenceViewer
label="RAVNOVES004TREE full recorded review"
className="m48-atlas-visual"
mode={semanticLayer}
modes={FULL_ROUTE_SEMANTIC_MODES}
expanded={expanded}
onModeChange={setSemanticLayer}
onExpandedChange={setExpanded}
modeControlsVisible={false}
chromeLayout="stacked"
>
<div className="m48-evidence-stage">
{videoSource ? (
<LaboratoryRecordedClipPlayer
source={videoSource}
segmentCount={review.frameCount}
frames={frames}
sequence={sequence}
playing={playing}
playbackRate={playbackRate}
cameraPresentation={cameraPresentation}
continuousPlayback
sourceCount={4}
onSequenceChange={setSequence}
onPlayingChange={setPlaying}
onPlaybackRateChange={setPlaybackRate}
alternativeScene={spatialScene}
cameraControls={(
<>
<Button
size="compact"
shape="pill"
variant={showCameraSemantic ? "primary" : "secondary"}
aria-pressed={showCameraSemantic}
onClick={() => setShowCameraSemantic((visible) => !visible)}
>
SEMANTICS
</Button>
<SegmentedControl
value={semanticLayer}
items={[...FULL_ROUTE_SEMANTIC_MODES]}
label="Источник семантики"
onChange={(value) => {
setSemanticLayer(value);
setShowCameraSemantic(true);
}}
/>
</>
)}
spatialControls={(
<>
<Button
size="compact"
shape="pill"
variant={sceneSettings.showPoints ? "primary" : "secondary"}
aria-pressed={sceneSettings.showPoints}
onClick={() => {
setEvidenceMode("3d");
setSceneSettings((settings) => ({
...settings,
showPoints: !settings.showPoints,
}));
}}
>
SOURCE POINTS
</Button>
<Button
size="compact"
shape="pill"
variant={sceneSettings.showTrajectory ? "primary" : "secondary"}
aria-pressed={sceneSettings.showTrajectory}
onClick={() => {
setEvidenceMode("3d");
setSceneSettings((settings) => ({
...settings,
showTrajectory: !settings.showTrajectory,
}));
}}
>
LOCAL SLAM
</Button>
<Button
size="compact"
shape="pill"
variant={evidenceMode === "plan" ? "primary" : "secondary"}
aria-pressed={evidenceMode === "plan"}
disabled={!linkedReview}
title={linkedReviewError ?? "10 sealed causal TGS anchors; continuous TGS отсутствует"}
onClick={selectTgsPlan}
>
TGS COSTMAP
</Button>
<Button
size="compact"
shape="pill"
variant="secondary"
disabled
title="Для RAV004 не опубликован point-aligned 3D semantic archive"
>
SEMANTICS
</Button>
</>
)}
cameraOverlay={(
<>
<div className="m48-clip-player__pane-label" data-pane="camera">
{showCameraSemantic
? `${semanticLayer === "city" ? "EoMT CITY" : "DDRNet NATURE"} · КАДР ${sequence}/${review.frameCount}`
: `SOURCE · КАДР ${sequence}/${review.frameCount}`}
</div>
{showCameraSemantic ? (
<div className="m48-clip-player__overlay">
<RecordedEvidenceSemanticMaskOverlay
src={vegetationFullRouteMaskUrl(resultId, semanticLayer, maskSequence)}
prefetchSrcs={prefetchSrcs}
imageWidth={review.width}
imageHeight={review.height}
classes={semantic.classes}
palette={semantic.palette}
opacity={0.76}
ariaLabel={`${layer.name} semantic prediction`}
/>
</div>
) : null}
</>
)}
/>
) : (
<div className="m4-replay-threat-visual__pane-status" role={videoError ? "alert" : "status"}>
{videoError ?? "Открываем автономный RAVNOVES004TREE source…"}
</div>
)}
{videoSource ? (
<M48EvidenceModeRail
mode={evidenceMode}
cameraVisible={cameraVisible}
spatialAvailable={Boolean(spatialProfile)}
planAvailable={Boolean(linkedReview)}
onModeChange={handleEvidenceModeChange}
onCameraVisibleChange={setCameraVisible}
/>
) : null}
</div>
</LaboratoryEvidenceViewer>
);
}
function FullRouteReviewResult({
rigLabel,
resultId,
review,
}: {
rigLabel: string;
resultId: string;
review: VegetationFullRouteReview;
}) {
return (
<LaboratoryWorkTemplate
summary={(
<LaboratorySummary
title="LAB V1 · RAVNOVES004TREE · полный маршрут"
description="Общий recorded-LAB шаблон воспроизводит запись с травой и оврагами: RIGHT camera, исходное облако, SLAM trajectory, два независимых semantic-слоя и десять реально просчитанных TGS-якорей."
status="FULL RECORDED REVIEW · truth отсутствует · commands OFF"
statusTone="warning"
facts={[
{ label: "Источник", value: `${review.sourceId} · ${review.frameCount}/${review.frameCount} frames` },
{ label: "3D", value: "1437 source point chunks · 2825 SLAM poses · recorded RRD" },
{ label: "Город", value: `${review.city.name} · ${decimal(review.city.inferenceFps, 2)} fps` },
{ label: "Природа", value: `${review.vegetation.name} · ${decimal(review.vegetation.inferenceFps, 2)} fps` },
{ label: "TGS", value: "10 sealed causal anchors · continuous costmap отсутствует" },
{ label: "Authority", value: `${rigLabel} · VISUAL REVIEW ONLY · commands OFF` },
]}
brief={{
question: "Что реально видно на полном RAV004-прогоне с высокой травой, оврагами и переходом к городу?",
approach: "Одна recorded timeline открывается общим LAB viewer. Camera и RRD синхронизированы; EoMT/DDRNet переключаются на камере, source points и SLAM trajectory — в 3D, TGS — только на десяти запечатанных якорях.",
principalResult: "RAV004 больше не подменяется RAV00: доступна полная исходная запись и её реальные пространственные слои.",
limitation: "Ручной truth, continuous TGS и point-aligned 3D semantics отсутствуют. Один повреждённый H.264-пакет на позиции 6092 заменён предыдущим декодированным кадром и отражён в proof.",
}}
method={{
completeness: "complete",
executionClass: "ai-inference",
pipelineId: "ravnoves004tree-full-eomt-ddrnet-recorded-review/v1",
components: [
{ kind: "algorithm", name: "Recorded source points + SLAM trajectory", version: "sealed RRD", role: "spatial source evidence", identitySha256: null },
{ kind: "model", name: review.city.name, version: "sealed Worker 006 run", role: "urban semantic review", identitySha256: null },
{ kind: "model", name: review.vegetation.name, version: "GOOSE DDRNet-39", role: "vegetation semantic review", identitySha256: null },
{ kind: "algorithm", name: "Causal TGS", version: "10 linked route anchors", role: "bounded geometric evidence", identitySha256: null },
],
}}
/>
)}
evidence={(
<LaboratoryEvidence
eyebrow="CANONICAL RECORDED LAB · RAVNOVES004TREE"
title="CAMERA + SOURCE POINTS + LOCAL SLAM + TGS COSTMAP + SEMANTICS · 6830/6830"
kind="recorded-replay"
resizable
>
<FullRouteReviewEvidence resultId={resultId} review={review} />
</LaboratoryEvidence>
)}
result={(
<LaboratoryResultSummary
title="Полный RAV004 visual review восстановлен; управление не авторизовано"
status="Recorded evidence ready · navigation/actuation OFF"
statusTone="warning"
metrics={[
{ label: "Route masks", value: "6830/6830 × 2", hint: "sealed local archives · Worker не требуется" },
{ label: "EoMT throughput", value: `${decimal(review.city.inferenceFps, 2)} fps`, hint: "изолированный полный прогон" },
{ label: "DDRNet throughput", value: `${decimal(review.vegetation.inferenceFps, 2)} fps`, hint: "изолированный полный прогон" },
{ label: "Spatial evidence", value: "RRD + 10 TGS anchors", hint: "continuous TGS и 3D semantics отсутствуют" },
]}
conclusion={{
proved: "Полный RAV004 открывается в каноническом recorded viewer с camera, source points, SLAM trajectory, EoMT, DDRNet и связанными TGS-якорями.",
notProved: "Не доказаны truth accuracy, временная стабильность DDRNet, continuous negative-obstacle detection и безопасное управление ровером.",
decision: "Использовать как visual audit. Следующий gate — truth-набор овраг/трава/дерево/яма и motion-aware temporal evaluation при целевых ≥10 FPS; navigation/actuation оставить OFF.",
}}
/>
)}
/>
);
}
function VegetationRouteEvidence({ result }: { result: VegetationShadowResult }) { function VegetationRouteEvidence({ result }: { result: VegetationShadowResult }) {
const route = result.routeVideo!; const route = result.routeVideo!;
const linkedTgsResultId = route.linkedTgsResultId; const linkedTgsResultId = route.linkedTgsResultId;
@@ -92,6 +576,15 @@ export function VegetationShadowResultView({
rigLabel: string; rigLabel: string;
result: VegetationShadowResult; result: VegetationShadowResult;
}) { }) {
if (result.routeFullReview) {
return (
<FullRouteReviewResult
rigLabel={rigLabel}
resultId={result.resultId}
review={result.routeFullReview}
/>
);
}
if (!result.routeVideo?.linkedTgsResultId) { if (!result.routeVideo?.linkedTgsResultId) {
throw new Error( throw new Error(
"Vegetation LAB result has no canonical M4 source timeline and linked M4.9 TGS evidence.", "Vegetation LAB result has no canonical M4 source timeline and linked M4.9 TGS evidence.",
@@ -185,6 +185,7 @@ function fullRouteReview() {
return { return {
source_id: "RAVNOVES004TREE", source_id: "RAVNOVES004TREE",
session_id: "20260828T130511Z_viewer_live", session_id: "20260828T130511Z_viewer_live",
linked_route_review_result_id: `lab-v1-vegetation-shadow-${"9".repeat(64)}`,
source_job_id: "recorded-camera-eb2783c5480d56bda07c8af0", source_job_id: "recorded-camera-eb2783c5480d56bda07c8af0",
source_job_input_sha256: "eb2783c5480d56bda07c8af008dff5344d19dc550ef70fe2075d6f098f7cc715", source_job_input_sha256: "eb2783c5480d56bda07c8af008dff5344d19dc550ef70fe2075d6f098f7cc715",
source_stream_sha256: "e5eb017e2cc0f546736eda5235ca157b501913093cb64af5e548e335417e1bac", source_stream_sha256: "e5eb017e2cc0f546736eda5235ca157b501913093cb64af5e548e335417e1bac",
@@ -385,12 +386,15 @@ test("vegetation realtime LAB and archival benchmark use separate admitted instr
assert.match(resultSource, /EoMT CITY \/ DDRNet VEGETATION/); assert.match(resultSource, /EoMT CITY \/ DDRNet VEGETATION/);
assert.match(m49Source, /spatialSemantic=\{spatialSemantic\}/); assert.match(m49Source, /spatialSemantic=\{spatialSemantic\}/);
assert.match(m49Source, /controlLabel: "SEMANTICS"/); assert.match(m49Source, /controlLabel: "SEMANTICS"/);
assert.equal(resultSource.match(/<LaboratoryEvidence\b/g)?.length, 1); assert.equal(resultSource.match(/<LaboratoryEvidence\b/g)?.length, 2);
assert.doesNotMatch(resultSource, /RAVNOVES004TREE mixed route review/); assert.doesNotMatch(resultSource, /RAVNOVES004TREE mixed route review/);
assert.doesNotMatch(resultSource, /RAVNOVES004TREE full recorded review/); assert.match(resultSource, /RAVNOVES004TREE full recorded review/);
assert.doesNotMatch(resultSource, /LaboratoryRecordedClipPlayer/); assert.match(resultSource, /LaboratoryRecordedClipPlayer/);
assert.doesNotMatch(resultSource, /RerunViewport/); assert.match(resultSource, /RerunViewport/);
assert.doesNotMatch(resultSource, /M48EvidenceModeRail/); assert.match(resultSource, /M48EvidenceModeRail/);
assert.match(resultSource, /SOURCE POINTS/);
assert.match(resultSource, /LOCAL SLAM/);
assert.match(resultSource, /TGS COSTMAP/);
assert.match(resultSource, /linked canonical M4\.9 TGS evidence/); assert.match(resultSource, /linked canonical M4\.9 TGS evidence/);
assert.match(resultSource, /linkedTgsResultId/); assert.match(resultSource, /linkedTgsResultId/);
assert.match(benchmarkSource, /M48MaskComparisonVisual/); assert.match(benchmarkSource, /M48MaskComparisonVisual/);
@@ -742,6 +742,7 @@ def seal_mixed_route_full_video_review(
full_route = { full_route = {
"source_id": FULL_ROUTE_SOURCE_ID, "source_id": FULL_ROUTE_SOURCE_ID,
"session_id": job.session_id, "session_id": job.session_id,
"linked_route_review_result_id": base["result_id"],
"source_job_id": job.job_id, "source_job_id": job.job_id,
"source_job_input_sha256": job.input_sha256, "source_job_input_sha256": job.input_sha256,
"source_stream_sha256": FULL_ROUTE_STREAM_SHA256, "source_stream_sha256": FULL_ROUTE_STREAM_SHA256,
+16
View File
@@ -77,6 +77,7 @@ _E39_RESULT_ID = re.compile(r"^e39-perception-refinement-[a-f0-9]{64}$")
_E40_RESULT_ID = re.compile(r"^e40-perception-product-gate-[a-f0-9]{64}$") _E40_RESULT_ID = re.compile(r"^e40-perception-product-gate-[a-f0-9]{64}$")
_M4_RESULT_ID = re.compile(r"^m4-threat-replay-[a-f0-9]{64}$") _M4_RESULT_ID = re.compile(r"^m4-threat-replay-[a-f0-9]{64}$")
_M49_TGS_RESULT_ID = re.compile(r"^m49-tgs-full-shadow-[a-f0-9]{64}$") _M49_TGS_RESULT_ID = re.compile(r"^m49-tgs-full-shadow-[a-f0-9]{64}$")
_VEGETATION_RESULT_ID = re.compile(r"^lab-v1-vegetation-shadow-[a-f0-9]{64}$")
RootProvider = Callable[[], Path | None] RootProvider = Callable[[], Path | None]
@@ -266,6 +267,21 @@ def _validate_product_publication_shape(
) -> None: ) -> None:
if work_id != "lab-v1-vegetation-shadow": if work_id != "lab-v1-vegetation-shadow":
return return
full_route = document.get("route_full_review")
if isinstance(full_route, dict):
if (
full_route.get("source_id") != "RAVNOVES004TREE"
or full_route.get("session_id") != "20260828T130511Z_viewer_live"
or full_route.get("frame_count") != 6830
or _VEGETATION_RESULT_ID.fullmatch(
str(full_route.get("linked_route_review_result_id", ""))
)
is None
or document.get("route_video") is not None
or document.get("route_review") is not None
):
raise ValueError("vegetation LAB has no canonical RAVNOVES004TREE publication shape")
return
route = document.get("route_video") route = document.get("route_video")
fusion = route.get("fusion") if isinstance(route, dict) else None fusion = route.get("fusion") if isinstance(route, dict) else None
if ( if (
+20 -8
View File
@@ -152,10 +152,10 @@ def test_advanced_index_projects_one_most_mature_lifecycle_phase(tmp_path: Path)
] ]
def test_advanced_index_skips_noncanonical_vegetation_viewers(tmp_path: Path) -> None: def test_advanced_index_prefers_canonical_rav004_full_review(tmp_path: Path) -> None:
root = tmp_path / "vegetation" root = tmp_path / "vegetation"
def publish(digest: str, *, canonical: bool) -> Path: def publish(digest: str, *, publication: str) -> Path:
result_id = f"lab-v1-vegetation-shadow-{digest}" result_id = f"lab-v1-vegetation-shadow-{digest}"
candidate = root / result_id candidate = root / result_id
candidate.mkdir(parents=True) candidate.mkdir(parents=True)
@@ -167,7 +167,17 @@ def test_advanced_index_skips_noncanonical_vegetation_viewers(tmp_path: Path) ->
"mode": "synchronised-multilayer-review", "mode": "synchronised-multilayer-review",
"pixel_raster_fusion": False, "pixel_raster_fusion": False,
}, },
} if canonical else None } if publication == "rav00" else None
route_full_review = {
"source_id": "RAVNOVES004TREE",
"session_id": "20260828T130511Z_viewer_live",
"frame_count": 6830,
"linked_route_review_result_id": (
f"lab-v1-vegetation-shadow-{'4' * 64}"
if publication == "rav004"
else None
),
} if publication != "rav00" else None
(candidate / "manifest.json").write_text( (candidate / "manifest.json").write_text(
json.dumps( json.dumps(
{ {
@@ -184,17 +194,19 @@ def test_advanced_index_skips_noncanonical_vegetation_viewers(tmp_path: Path) ->
"ground_truth": False, "ground_truth": False,
"route_video": route_video, "route_video": route_video,
"route_review": None, "route_review": None,
"route_full_review": None if canonical else {"frame_count": 6830}, "route_full_review": route_full_review,
} }
), ),
encoding="utf-8", encoding="utf-8",
) )
return candidate return candidate
canonical = publish("a" * 64, canonical=True) rav00 = publish("a" * 64, publication="rav00")
incomplete = publish("b" * 64, canonical=False) incomplete = publish("b" * 64, publication="incomplete")
os.utime(canonical, ns=(10_000_000_000, 10_000_000_000)) rav004 = publish("c" * 64, publication="rav004")
os.utime(rav00, ns=(10_000_000_000, 10_000_000_000))
os.utime(incomplete, ns=(20_000_000_000, 20_000_000_000)) os.utime(incomplete, ns=(20_000_000_000, 20_000_000_000))
os.utime(rav004, ns=(30_000_000_000, 30_000_000_000))
registry = _evidence_registry( registry = _evidence_registry(
root, root,
work_id="lab-v1-vegetation-shadow", work_id="lab-v1-vegetation-shadow",
@@ -211,7 +223,7 @@ def test_advanced_index_skips_noncanonical_vegetation_viewers(tmp_path: Path) ->
assert index["items"] == [ # type: ignore[index] assert index["items"] == [ # type: ignore[index]
{ {
"work_id": "lab-v1-vegetation-shadow", "work_id": "lab-v1-vegetation-shadow",
"result_id": canonical.name, "result_id": rav004.name,
"created_at_utc": "2026-08-29T10:00:00Z", "created_at_utc": "2026-08-29T10:00:00Z",
"access": "read-only", "access": "read-only",
} }