feat(lab): seal full RAVNOVES004TREE semantic review

This commit is contained in:
DCCONSTRUCTIONS
2026-08-29 13:37:57 +03:00
parent 24b65926fe
commit 9dec37a8a9
12 changed files with 1494 additions and 30 deletions
@@ -106,6 +106,39 @@ export interface VegetationMixedRouteReview {
cases: readonly VegetationMixedRouteCase[]; cases: readonly VegetationMixedRouteCase[];
} }
export interface VegetationFullRouteLayer {
name: string;
resultId: string;
frameCount: 6830;
taxonomy: readonly VegetationVideoSemanticClass[];
inferenceFps: number;
latencyP95Ms: number;
peakReservedVramBytes: number;
}
export interface VegetationFullRouteReview {
sourceId: "RAVNOVES004TREE";
sessionId: "20260828T130511Z_viewer_live";
sourceJobId: "recorded-camera-eb2783c5480d56bda07c8af0";
sourceJobInputSha256: string;
sourceStreamSha256: string;
recordedMediaSourceId: "recorded.camera.6a3945242828a038";
recordedMediaGenerationSha256: string;
frameCount: 6830;
width: 800;
height: 600;
timelineStartSeconds: number;
timelineEndSeconds: number;
frameSourceTimesNs: readonly number[];
decodeRepair: {
repairedFrameCount: 1;
sequence: 6092;
method: "duplicate-previous-decoded-frame";
};
city: VegetationFullRouteLayer;
vegetation: VegetationFullRouteLayer;
}
export interface VegetationShadowResult { export interface VegetationShadowResult {
resultId: string; resultId: string;
createdAtUtc: string; createdAtUtc: string;
@@ -116,6 +149,7 @@ export interface VegetationShadowResult {
validationCases: readonly VegetationVisualCase[]; validationCases: readonly VegetationVisualCase[];
routeVideo: VegetationRouteVideo | null; routeVideo: VegetationRouteVideo | null;
routeReview: VegetationMixedRouteReview | null; routeReview: VegetationMixedRouteReview | null;
routeFullReview: VegetationFullRouteReview | null;
limitations: readonly string[]; limitations: readonly string[];
visualShadowReady: true; visualShadowReady: true;
missionPolicyReadyForConfiguration: true; missionPolicyReadyForConfiguration: true;
@@ -545,6 +579,246 @@ function mixedRouteReviewValue(
}; };
} }
function fullRouteTaxonomyValue(
value: unknown,
label: string,
schema: string,
classCount: number,
): readonly VegetationVideoSemanticClass[] {
const taxonomy = objectValue(value, `${label}.taxonomy`);
exact(taxonomy.schema_version, schema, `${label}.taxonomy.schema`);
const classes = arrayValue(taxonomy.classes, `${label}.taxonomy.classes`).map(
(raw, expectedId): VegetationVideoSemanticClass => {
const item = objectValue(raw, `${label}.taxonomy[${expectedId}]`);
const classId = integerValue(item.class_id, `${label}.class_id[${expectedId}]`);
if (classId !== expectedId) {
throw new VegetationShadowContractError(`${label}: taxonomy order changed.`);
}
const color = arrayValue(item.color_rgb, `${label}.color[${expectedId}]`)
.map((channel, index) => integerValue(channel, `${label}.color[${expectedId}][${index}]`));
if (color.length !== 3 || color.some((channel) => channel > 255)) {
throw new VegetationShadowContractError(`${label}: taxonomy color invalid.`);
}
const disposition = item.disposition;
if (
disposition !== "labeled"
&& disposition !== "ambiguous"
&& disposition !== "prediction"
&& disposition !== "undefined"
) {
throw new VegetationShadowContractError(`${label}: taxonomy disposition changed.`);
}
return {
classId,
label: textValue(item.label, `${label}.label[${expectedId}]`),
colorRgb: color as unknown as readonly [number, number, number],
disposition,
materialClass: item.material_class === null || item.material_class === undefined
? null
: textValue(item.material_class, `${label}.material[${expectedId}]`),
evidenceState: item.evidence_state === null || item.evidence_state === undefined
? null
: textValue(item.evidence_state, `${label}.evidence[${expectedId}]`),
};
},
);
if (classes.length !== classCount) {
throw new VegetationShadowContractError(`${label}: taxonomy size changed.`);
}
return classes;
}
function fullRouteLayerValue(
value: unknown,
layer: "city" | "vegetation",
): VegetationFullRouteLayer {
const label = `vegetation.route_full_review.layers.${layer}`;
const row = objectValue(value, label);
const resultId = textValue(row.result_id, `${label}.result_id`);
const identity = layer === "city"
? /^result-[a-f0-9]{64}$/
: /^lab-v1-ravnoves-video-ddrnet-[a-f0-9]{64}$/;
if (!identity.test(resultId)) {
throw new VegetationShadowContractError(`${label}: identity invalid.`);
}
exact(row.frame_count, 6830, `${label}.frame_count`);
const archive = objectValue(row.mask_archive, `${label}.mask_archive`);
exact(
archive.path,
layer === "city" ? "video/eomt-semantic-masks.zip" : "video/ddrnet-semantic-masks.zip",
`${label}.mask_archive.path`,
);
const digest = textValue(archive.sha256, `${label}.mask_archive.sha256`);
if (!SHA256.test(digest)) {
throw new VegetationShadowContractError(`${label}: archive digest invalid.`);
}
integerValue(archive.byte_length, `${label}.mask_archive.byte_length`);
return {
name: textValue(row.name, `${label}.name`),
resultId,
frameCount: 6830,
taxonomy: fullRouteTaxonomyValue(
row.taxonomy,
label,
layer === "city"
? "missioncore.recorded-eomt-taxonomy/v1"
: "missioncore.lab-v1-vegetation-taxonomy/v1",
layer === "city" ? 16 : 64,
),
inferenceFps: numberValue(row.inference_fps, `${label}.inference_fps`),
latencyP95Ms: numberValue(row.latency_p95_ms, `${label}.latency_p95_ms`),
peakReservedVramBytes: integerValue(
row.peak_reserved_vram_bytes,
`${label}.peak_reserved_vram_bytes`,
),
};
}
function fullRouteReviewValue(value: unknown): VegetationFullRouteReview | null {
if (value === null || value === undefined) return null;
const row = objectValue(value, "vegetation.route_full_review");
exact(row.source_id, "RAVNOVES004TREE", "vegetation.route_full_review.source_id");
exact(
row.session_id,
"20260828T130511Z_viewer_live",
"vegetation.route_full_review.session_id",
);
exact(
row.source_job_id,
"recorded-camera-eb2783c5480d56bda07c8af0",
"vegetation.route_full_review.source_job_id",
);
exact(row.frame_count, 6830, "vegetation.route_full_review.frame_count");
exact(row.width, 800, "vegetation.route_full_review.width");
exact(row.height, 600, "vegetation.route_full_review.height");
exact(row.ground_truth, false, "vegetation.route_full_review.ground_truth");
const sourceJobInputSha256 = textValue(
row.source_job_input_sha256,
"vegetation.route_full_review.source_job_input_sha256",
);
const sourceStreamSha256 = textValue(
row.source_stream_sha256,
"vegetation.route_full_review.source_stream_sha256",
);
exact(
sourceJobInputSha256,
"eb2783c5480d56bda07c8af008dff5344d19dc550ef70fe2075d6f098f7cc715",
"vegetation.route_full_review.source_job_input_sha256",
);
exact(
sourceStreamSha256,
"e5eb017e2cc0f546736eda5235ca157b501913093cb64af5e548e335417e1bac",
"vegetation.route_full_review.source_stream_sha256",
);
exact(
row.recorded_media_source_id,
"recorded.camera.6a3945242828a038",
"vegetation.route_full_review.recorded_media_source_id",
);
const recordedMediaGenerationSha256 = textValue(
row.recorded_media_generation_sha256,
"vegetation.route_full_review.recorded_media_generation_sha256",
);
exact(
recordedMediaGenerationSha256,
"b073ea1e7babf1c77a664e1a5b95e3702d0e05b0e34c1e85a7c67a6f8b392ded",
"vegetation.route_full_review.recorded_media_generation_sha256",
);
if (
!SHA256.test(sourceJobInputSha256)
|| !SHA256.test(sourceStreamSha256)
|| !SHA256.test(recordedMediaGenerationSha256)
) {
throw new VegetationShadowContractError("vegetation.route_full_review: source digest invalid.");
}
const timelineStartSeconds = numberValue(
row.timeline_start_seconds,
"vegetation.route_full_review.timeline_start_seconds",
);
const timelineEndSeconds = numberValue(
row.timeline_end_seconds,
"vegetation.route_full_review.timeline_end_seconds",
);
if (timelineEndSeconds <= timelineStartSeconds) {
throw new VegetationShadowContractError("vegetation.route_full_review: timeline invalid.");
}
const frameSourceTimesNs = arrayValue(
row.frame_source_times_ns,
"vegetation.route_full_review.frame_source_times_ns",
).map((raw, index) => {
const time = integerValue(raw, `vegetation.route_full_review.frame_source_times_ns[${index}]`);
if (!Number.isSafeInteger(time)) {
throw new VegetationShadowContractError("vegetation.route_full_review: unsafe frame time.");
}
return time;
});
if (
frameSourceTimesNs.length !== 6830
|| frameSourceTimesNs.some((time, index) => index > 0 && time <= frameSourceTimesNs[index - 1]!)
) {
throw new VegetationShadowContractError("vegetation.route_full_review: frame timeline changed.");
}
const decodeRepair = objectValue(
row.decode_repair,
"vegetation.route_full_review.decode_repair",
);
exact(decodeRepair.repaired_frame_count, 1, "vegetation.route_full_review.decode_repair.count");
exact(decodeRepair.sequence, 6092, "vegetation.route_full_review.decode_repair.sequence");
exact(
decodeRepair.method,
"duplicate-previous-decoded-frame",
"vegetation.route_full_review.decode_repair.method",
);
const repairProofs = objectValue(
decodeRepair.proofs,
"vegetation.route_full_review.decode_repair.proofs",
);
for (const [key, expectedPath] of Object.entries({
eomt: "proofs/decode_repair.json",
ddrnet: "proofs/ddrnet_decode_repair.json",
})) {
const proof = objectValue(
repairProofs[key],
`vegetation.route_full_review.decode_repair.proofs.${key}`,
);
exact(
proof.path,
expectedPath,
`vegetation.route_full_review.decode_repair.proofs.${key}.path`,
);
const digest = textValue(
proof.sha256,
`vegetation.route_full_review.decode_repair.proofs.${key}.sha256`,
);
if (!SHA256.test(digest)) {
throw new VegetationShadowContractError("vegetation.route_full_review: repair proof invalid.");
}
}
const layers = objectValue(row.layers, "vegetation.route_full_review.layers");
return {
sourceId: "RAVNOVES004TREE",
sessionId: "20260828T130511Z_viewer_live",
sourceJobId: "recorded-camera-eb2783c5480d56bda07c8af0",
sourceJobInputSha256,
sourceStreamSha256,
recordedMediaSourceId: "recorded.camera.6a3945242828a038",
recordedMediaGenerationSha256,
frameCount: 6830,
width: 800,
height: 600,
timelineStartSeconds,
timelineEndSeconds,
frameSourceTimesNs,
decodeRepair: {
repairedFrameCount: 1,
sequence: 6092,
method: "duplicate-previous-decoded-frame",
},
city: fullRouteLayerValue(layers.city, "city"),
vegetation: fullRouteLayerValue(layers.vegetation, "vegetation"),
};
}
function parseResult( function parseResult(
value: unknown, value: unknown,
resultId: string, resultId: string,
@@ -589,9 +863,10 @@ function parseResult(
const validationCases = arrayValue(catalogs.goose, "vegetation.catalogs.goose") const validationCases = arrayValue(catalogs.goose, "vegetation.catalogs.goose")
.map((item) => visualCaseValue(item, resultId, "goose", endpointRoot)); .map((item) => visualCaseValue(item, resultId, "goose", endpointRoot));
const routeReview = mixedRouteReviewValue(payload.route_review, resultId, endpointRoot); const routeReview = mixedRouteReviewValue(payload.route_review, resultId, endpointRoot);
const routeFullReview = fullRouteReviewValue(payload.route_full_review);
if ( if (
routeCases.length !== 0 routeCases.length !== 0
|| (routeReview ? validationCases.length !== 0 : validationCases.length !== 12) || (routeReview || routeFullReview ? validationCases.length !== 0 : validationCases.length !== 12)
) { ) {
throw new VegetationShadowContractError("vegetation.catalogs: ожидалось 12 truth-backed GOOSE случаев без route viewer."); throw new VegetationShadowContractError("vegetation.catalogs: ожидалось 12 truth-backed GOOSE случаев без route viewer.");
} }
@@ -605,6 +880,7 @@ function parseResult(
validationCases, validationCases,
routeVideo: routeVideoValue(payload.route_video), routeVideo: routeVideoValue(payload.route_video),
routeReview, routeReview,
routeFullReview,
limitations: arrayValue(payload.limitations, "vegetation.limitations") limitations: arrayValue(payload.limitations, "vegetation.limitations")
.map((item, index) => textValue(item, `vegetation.limitations[${index}]`)), .map((item, index) => textValue(item, `vegetation.limitations[${index}]`)),
visualShadowReady: true, visualShadowReady: true,
@@ -625,6 +901,23 @@ export function vegetationVideoMaskUrl(resultId: string, sequence: number): stri
return `/api/v1/laboratory/vegetation-shadow/${encodeURIComponent(resultId)}/masks/${sequence}`; return `/api/v1/laboratory/vegetation-shadow/${encodeURIComponent(resultId)}/masks/${sequence}`;
} }
export function vegetationFullRouteMaskUrl(
resultId: string,
layer: "city" | "vegetation",
sequence: number,
): string {
if (
!RESULT_ID.test(resultId)
|| (layer !== "city" && layer !== "vegetation")
|| !Number.isInteger(sequence)
|| sequence < 0
|| sequence >= 6830
) {
throw new VegetationShadowContractError("Vegetation full-route mask identity недопустима.");
}
return `/api/v1/laboratory/vegetation-shadow/${encodeURIComponent(resultId)}/route-masks/${layer}/${sequence}`;
}
export async function fetchVegetationShadowResult( export async function fetchVegetationShadowResult(
resultId: string, resultId: string,
{ {
@@ -1,7 +1,8 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { Icon, IconButton, StatusBadge } from "@nodedc/ui-react"; import { Icon, IconButton, StatusBadge } from "@nodedc/ui-react";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer"; import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
import { LaboratoryRecordedClipPlayer } from "../../components/laboratory/LaboratoryRecordedClipPlayer";
import { import {
LaboratoryEvidence, LaboratoryEvidence,
LaboratoryResultSummary, LaboratoryResultSummary,
@@ -9,10 +10,21 @@ import {
LaboratoryWorkTemplate, LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation"; } from "../../components/laboratory/LaboratoryPresentation";
import { import {
RecordedEvidenceSemanticMaskOverlay,
type RecordedEvidenceSemanticClass,
type RecordedEvidenceSemanticPaletteEntry,
} from "../../components/laboratory/RecordedEvidenceSemanticMaskOverlay";
import {
vegetationFullRouteMaskUrl,
vegetationVideoMaskUrl, vegetationVideoMaskUrl,
type VegetationFullRouteLayer,
type VegetationFullRouteReview,
type VegetationMixedRouteReview, type VegetationMixedRouteReview,
type VegetationShadowResult, type VegetationShadowResult,
} from "../../core/laboratory/vegetationShadow"; } from "../../core/laboratory/vegetationShadow";
import { recordedObservationSources } from "../../core/observation/recordedObservationSources";
import { resolveObservationSessionReplay } from "../../core/observation/useObservationSessions";
import type { ObservationSourceDescriptor } from "../../core/runtime/contracts";
import { import {
fetchM49TgsFullShadowResult, fetchM49TgsFullShadowResult,
type M49TgsFullShadowResult, type M49TgsFullShadowResult,
@@ -31,6 +43,217 @@ const MIXED_ROUTE_MODES = [
{ value: "tgs", label: "TGS" }, { value: "tgs", label: "TGS" },
] as const; ] as const;
const FULL_ROUTE_MODES = [
{ value: "source", label: "SOURCE" },
{ 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 FullRouteReviewEvidence({
resultId,
review,
}: {
resultId: string;
review: VegetationFullRouteReview;
}) {
const [sequence, setSequence] = useState(1);
const [playing, setPlaying] = useState(false);
const [playbackRate, setPlaybackRate] = useState(1);
const [mode, setMode] = useState<typeof FULL_ROUTE_MODES[number]["value"]>("vegetation");
const [expanded, setExpanded] = useState(false);
const [videoSource, setVideoSource] = useState<ObservationSourceDescriptor | null>(null);
const [videoError, setVideoError] = useState<string | null>(null);
const frames = useMemo(
() => review.frameSourceTimesNs.map((sourceTimeNs, index) => ({
sequence: index + 1,
sourceTimeNs,
})),
[review.frameSourceTimesNs],
);
const layer = mode === "source" ? null : review[mode];
const semantic = useMemo(() => layer ? semanticPresentation(layer) : null, [layer]);
const maskSequence = sequence - 1;
const prefetchSrcs = useMemo(() => layer
? Array.from({ length: 8 }, (_, offset) => maskSequence + offset + 1)
.filter((candidate) => candidate < review.frameCount)
.map((candidate) => vegetationFullRouteMaskUrl(resultId, mode as "city" | "vegetation", candidate))
: [], [layer, maskSequence, mode, resultId, review.frameCount]);
useEffect(() => {
const controller = new AbortController();
setVideoSource(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);
})
.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,
]);
return (
<LaboratoryEvidenceViewer
label="RAVNOVES004TREE full recorded review"
className="m48-atlas-visual"
mode={mode}
modes={FULL_ROUTE_MODES}
expanded={expanded}
onModeChange={setMode}
onExpandedChange={setExpanded}
chromeLayout="stacked"
>
{videoSource ? (
<LaboratoryRecordedClipPlayer
source={videoSource}
segmentCount={review.frameCount}
frames={frames}
sequence={sequence}
playing={playing}
playbackRate={playbackRate}
cameraPresentation="primary"
continuousPlayback
sourceCount={1}
onSequenceChange={setSequence}
onPlayingChange={setPlaying}
onPlaybackRateChange={setPlaybackRate}
cameraOverlay={(
<>
<div className="m48-clip-player__pane-label" data-pane="camera">
{mode === "source" ? "SOURCE" : `${mode === "city" ? "EoMT CITY" : "DDRNet NATURE"} · КАДР ${sequence}/${review.frameCount}`}
</div>
{layer && semantic ? (
<RecordedEvidenceSemanticMaskOverlay
src={vegetationFullRouteMaskUrl(resultId, mode as "city" | "vegetation", maskSequence)}
prefetchSrcs={prefetchSrcs}
imageWidth={review.width}
imageHeight={review.height}
classes={semantic.classes}
palette={semantic.palette}
opacity={0.76}
ariaLabel={`${layer.name} semantic prediction`}
/>
) : null}
</>
)}
/>
) : (
<div className="m4-replay-threat-visual__pane-status" role={videoError ? "alert" : "status"}>
{videoError ?? "Открываем автономный recorded source…"}
</div>
)}
</LaboratoryEvidenceViewer>
);
}
function FullRouteReviewResult({
rigLabel,
resultId,
review,
}: {
rigLabel: string;
resultId: string;
review: VegetationFullRouteReview;
}) {
return (
<LaboratoryWorkTemplate
summary={(
<LaboratorySummary
title="LAB V1 · RAVNOVES004TREE · полный маршрут"
description="Существующий M4.7-шаблон воспроизводит всю запись и переключает два независимых sealed semantic-слоя: городской EoMT и природный DDRNet. Worker для открытия результата не нужен."
status="FULL RECORDED REVIEW · truth отсутствует · commands OFF"
statusTone="warning"
facts={[
{ label: "Источник", value: `${review.sourceId} · ${review.frameCount}/${review.frameCount} frames` },
{ label: "Город", value: `${review.city.name} · ${decimal(review.city.inferenceFps, 2)} fps` },
{ label: "Природа", value: `${review.vegetation.name} · ${decimal(review.vegetation.inferenceFps, 2)} fps` },
{ label: "Authority", value: `${rigLabel} · VISUAL REVIEW ONLY · commands OFF` },
]}
brief={{
question: "Как оба semantic-кандидата ведут себя на полном переходе от сельской среды к городской?",
approach: "Все 6830 позиции одной recorded timeline последовательно прогнаны на Worker 006 и сохранены двумя независимыми архивами масок. В M4.7 переключается только видимый слой.",
principalResult: "Полная временная шкала доступна локально в SOURCE / EoMT CITY / DDRNet NATURE без обращения к Worker.",
limitation: "Ручной truth отсутствует. Один повреждённый H.264-пакет на позиции 6092 представлен предыдущим декодированным кадром и явно зафиксирован в proof. Полный TGS и кюветы этим прогоном не проверялись.",
}}
method={{
completeness: "complete",
executionClass: "ai-inference",
pipelineId: "ravnoves004tree-full-eomt-ddrnet-recorded-review/v1",
components: [
{ 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 },
],
}}
/>
)}
evidence={(
<LaboratoryEvidence
eyebrow="M4.7 TEMPLATE · RAVNOVES004TREE FULL VIDEO"
title="SOURCE / EoMT CITY / DDRNet NATURE · 6830/6830 · TRUTH отсутствует"
kind="diagnostic-model"
resizable
>
<FullRouteReviewEvidence resultId={resultId} review={review} />
</LaboratoryEvidence>
)}
result={(
<LaboratoryResultSummary
title="Полный двухслойный 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 p95", value: `${decimal(review.city.latencyP95Ms, 2)} ms`, hint: "последовательный изолированный прогон" },
{ label: "DDRNet p95", value: `${decimal(review.vegetation.latencyP95Ms, 2)} ms`, hint: "последовательный изолированный прогон" },
{ label: "Decode repair", value: "1/6830", hint: "sequence 6092 · previous frame · sealed proof" },
]}
conclusion={{
proved: "Городской EoMT и природный DDRNet воспроизводимо обработали полную запись и доступны в одном существующем M4.7 viewer.",
notProved: "Не доказаны truth accuracy, одновременный realtime-load, полный TGS, отрицательные препятствия и безопасное управление ровером.",
decision: "Использовать результат только как визуальную диагностику. Navigation/actuation оставить OFF; следующий gate — оценка временной стабильности и независимый person/vehicle STOP.",
}}
/>
)}
/>
);
}
function MixedRouteReviewEvidence({ review }: { review: VegetationMixedRouteReview }) { function MixedRouteReviewEvidence({ review }: { review: VegetationMixedRouteReview }) {
const [index, setIndex] = useState(0); const [index, setIndex] = useState(0);
const [mode, setMode] = useState<typeof MIXED_ROUTE_MODES[number]["value"]>("vegetation"); const [mode, setMode] = useState<typeof MIXED_ROUTE_MODES[number]["value"]>("vegetation");
@@ -224,6 +447,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.routeReview) { if (result.routeReview) {
return <MixedRouteReviewResult rigLabel={rigLabel} review={result.routeReview} />; return <MixedRouteReviewResult rigLabel={rigLabel} review={result.routeReview} />;
} }
@@ -7,6 +7,7 @@ import { createServer } from "vite";
let server; let server;
let fetchVegetationBenchmarkResult; let fetchVegetationBenchmarkResult;
let fetchVegetationShadowResult; let fetchVegetationShadowResult;
let vegetationFullRouteMaskUrl;
before(async () => { before(async () => {
server = await createServer({ server = await createServer({
@@ -14,7 +15,11 @@ before(async () => {
logLevel: "silent", logLevel: "silent",
server: { middlewareMode: true }, server: { middlewareMode: true },
}); });
({ fetchVegetationBenchmarkResult, fetchVegetationShadowResult } = await server.ssrLoadModule( ({
fetchVegetationBenchmarkResult,
fetchVegetationShadowResult,
vegetationFullRouteMaskUrl,
} = await server.ssrLoadModule(
"/src/core/laboratory/vegetationShadow.ts", "/src/core/laboratory/vegetationShadow.ts",
)); ));
}); });
@@ -148,6 +153,63 @@ function coarseRouteVideo() {
}; };
} }
function fullRouteReview() {
const layer = (kind) => ({
name: kind === "city" ? "EoMT Cityscapes" : "ddrnet_39",
result_id: kind === "city"
? `result-${"2".repeat(64)}`
: `lab-v1-ravnoves-video-ddrnet-${"3".repeat(64)}`,
frame_count: 6830,
taxonomy: {
schema_version: kind === "city"
? "missioncore.recorded-eomt-taxonomy/v1"
: "missioncore.lab-v1-vegetation-taxonomy/v1",
classes: Array.from({ length: kind === "city" ? 16 : 64 }, (_, classId) => ({
class_id: classId,
label: classId === 0 ? "undefined" : `${kind}-${classId}`,
color_rgb: [classId, classId, classId],
disposition: classId === 0 ? "undefined" : "prediction",
})),
},
mask_archive: {
path: kind === "city"
? "video/eomt-semantic-masks.zip"
: "video/ddrnet-semantic-masks.zip",
sha256: "4".repeat(64),
byte_length: 4096,
},
inference_fps: 9.5,
latency_p95_ms: 101.2,
peak_reserved_vram_bytes: 3_000_000_000,
});
return {
source_id: "RAVNOVES004TREE",
session_id: "20260828T130511Z_viewer_live",
source_job_id: "recorded-camera-eb2783c5480d56bda07c8af0",
source_job_input_sha256: "eb2783c5480d56bda07c8af008dff5344d19dc550ef70fe2075d6f098f7cc715",
source_stream_sha256: "e5eb017e2cc0f546736eda5235ca157b501913093cb64af5e548e335417e1bac",
recorded_media_source_id: "recorded.camera.6a3945242828a038",
recorded_media_generation_sha256: "b073ea1e7babf1c77a664e1a5b95e3702d0e05b0e34c1e85a7c67a6f8b392ded",
frame_count: 6830,
width: 800,
height: 600,
timeline_start_seconds: 39.215263458,
timeline_end_seconds: 757.260263458,
frame_source_times_ns: Array.from({ length: 6830 }, (_, index) => 39_215_263_458 + index * 100_000_000),
ground_truth: false,
decode_repair: {
repaired_frame_count: 1,
sequence: 6092,
method: "duplicate-previous-decoded-frame",
proofs: {
eomt: { path: "proofs/decode_repair.json", sha256: "7".repeat(64) },
ddrnet: { path: "proofs/ddrnet_decode_repair.json", sha256: "8".repeat(64) },
},
},
layers: { city: layer("city"), vegetation: layer("vegetation") },
};
}
function labPayload(route = routeVideo()) { function labPayload(route = routeVideo()) {
return { return {
schema_version: "missioncore.lab-v1-vegetation-shadow/v1", schema_version: "missioncore.lab-v1-vegetation-shadow/v1",
@@ -233,6 +295,29 @@ test("vegetation LAB parses coarse material policy and sealed TGS binding", asyn
assert.equal(result.routeVideo.fusionMode, "synchronised-multilayer-review"); assert.equal(result.routeVideo.fusionMode, "synchronised-multilayer-review");
}); });
test("vegetation LAB parses the full 004 pass inside the existing result contract", async () => {
const payload = {
...labPayload(null),
catalogs: { goose: [], ravnoves: [] },
route_full_review: fullRouteReview(),
};
const result = await fetchVegetationShadowResult(resultId, {
fetcher: async () => new Response(JSON.stringify(payload), {
status: 200,
headers: { "Content-Type": "application/json" },
}),
});
assert.equal(result.routeVideo, null);
assert.equal(result.routeFullReview.frameCount, 6830);
assert.equal(result.routeFullReview.city.taxonomy.length, 16);
assert.equal(result.routeFullReview.vegetation.taxonomy.length, 64);
assert.equal(result.routeFullReview.decodeRepair.sequence, 6092);
assert.equal(
vegetationFullRouteMaskUrl(resultId, "vegetation", 6829),
`/api/v1/laboratory/vegetation-shadow/${resultId}/route-masks/vegetation/6829`,
);
});
test("vegetation GOOSE benchmark opens through its separate archival endpoint", async () => { test("vegetation GOOSE benchmark opens through its separate archival endpoint", async () => {
let requestedUrl = ""; let requestedUrl = "";
const result = await fetchVegetationBenchmarkResult(benchmarkResultId, { const result = await fetchVegetationBenchmarkResult(benchmarkResultId, {
@@ -271,8 +356,10 @@ test("vegetation realtime LAB and archival benchmark use separate admitted instr
assert.match(resultSource, /M49TgsFullShadowEvidence/); assert.match(resultSource, /M49TgsFullShadowEvidence/);
assert.match(resultSource, /semanticOverride/); assert.match(resultSource, /semanticOverride/);
assert.match(resultSource, /EoMT CITY \/ DDRNet VEGETATION/); assert.match(resultSource, /EoMT CITY \/ DDRNet VEGETATION/);
assert.equal(resultSource.match(/<LaboratoryEvidence\b/g)?.length, 3); assert.equal(resultSource.match(/<LaboratoryEvidence\b/g)?.length, 4);
assert.match(resultSource, /RAVNOVES004TREE mixed route review/); assert.match(resultSource, /RAVNOVES004TREE mixed route review/);
assert.match(resultSource, /RAVNOVES004TREE full recorded review/);
assert.match(resultSource, /LaboratoryRecordedClipPlayer/);
assert.match(resultSource, /linkedTgsResultId/); assert.match(resultSource, /linkedTgsResultId/);
assert.match(benchmarkSource, /M48MaskComparisonVisual/); assert.match(benchmarkSource, /M48MaskComparisonVisual/);
assert.doesNotMatch(benchmarkSource, /M49TgsFullShadowEvidence/); assert.doesNotMatch(benchmarkSource, /M49TgsFullShadowEvidence/);
@@ -0,0 +1,19 @@
{
"schema_version": "missioncore.lab-v1-ravnoves-source/v1",
"profile_id": "ravnoves004tree-full-video-source/v1",
"source": {
"source_id": "RAVNOVES004TREE/right-e5eb017e2cc0f546736eda5235ca157b501913093cb64af5e548e335417e1bac",
"source_sha256": "e5eb017e2cc0f546736eda5235ca157b501913093cb64af5e548e335417e1bac",
"source_job_id": "recorded-camera-eb2783c5480d56bda07c8af0",
"source_job_input_sha256": "eb2783c5480d56bda07c8af008dff5344d19dc550ef70fe2075d6f098f7cc715",
"session_id": "20260828T130511Z_viewer_live",
"base_m4_result_id": null,
"expected_width": 800,
"expected_height": 600,
"expected_frame_count": 6830,
"timeline_start_seconds": 39.215263458,
"timeline_end_seconds": 757.260263458,
"frame_indices": [],
"crop_contract": "center-600-square-to-512; outside-crop-is-undefined"
}
}
@@ -187,12 +187,15 @@ Write-Output "PHASE=e4-preflight-complete"
$runToken = [Guid]::NewGuid().ToString("N") $runToken = [Guid]::NewGuid().ToString("N")
$workRoot = Join-Path $tmpRoot ("{0}-e4-{1}" -f $job.job_id, $runToken) $workRoot = Join-Path $tmpRoot ("{0}-e4-{1}" -f $job.job_id, $runToken)
$framesRoot = Join-Path $workRoot "frames" $framesRoot = Join-Path $workRoot "frames"
$decodedFramesRoot = Join-Path $workRoot "decoded-by-pts"
$streamPath = Join-Path $workRoot "camera.mp4" $streamPath = Join-Path $workRoot "camera.mp4"
$ptsPath = Join-Path $workRoot "pts.json" $packetsPath = Join-Path $workRoot "packets.csv"
$decodeRepairPath = Join-Path $workRoot "decode-repair.json"
$timelinePath = Join-Path $workRoot "timeline.jsonl" $timelinePath = Join-Path $workRoot "timeline.jsonl"
$publishRoot = Join-Path $derivedRoot (".{0}-e4-{1}.publish" -f $job.job_id, $runToken) $publishRoot = Join-Path $derivedRoot (".{0}-e4-{1}.publish" -f $job.job_id, $runToken)
$stagingRoot = Join-Path $publishRoot "output" $stagingRoot = Join-Path $publishRoot "output"
$null = New-Item -ItemType Directory -Path $framesRoot $null = New-Item -ItemType Directory -Path $framesRoot
$null = New-Item -ItemType Directory -Path $decodedFramesRoot
$null = New-Item -ItemType Directory -Path $publishRoot $null = New-Item -ItemType Directory -Path $publishRoot
$totalWatch = [Diagnostics.Stopwatch]::StartNew() $totalWatch = [Diagnostics.Stopwatch]::StartNew()
$completed = $false $completed = $false
@@ -230,29 +233,69 @@ try {
$extractWatch = [Diagnostics.Stopwatch]::StartNew() $extractWatch = [Diagnostics.Stopwatch]::StartNew()
Write-Output "PHASE=e4-frame-extraction-start" Write-Output "PHASE=e4-frame-extraction-start"
& ffmpeg -hide_banner -loglevel fatal -i $streamPath -map 0:v:0 -fps_mode passthrough -frames:v $activeFrameCount (Join-Path $framesRoot "frame-%06d.png") & ffprobe -v error -select_streams v:0 -show_packets -show_entries packet=pts,flags -of csv=p=0 -o $packetsPath $streamPath
Assert-LastExitCode "LAB E4 packet timestamp probe"
$packetRows = @(Get-Content -LiteralPath $packetsPath | Select-Object -First $activeFrameCount)
if ($packetRows.Count -ne $activeFrameCount) {
throw "LAB E4 packet count differs from the requested camera epoch"
}
& ffmpeg -hide_banner -loglevel error `
-hwaccel cuda -hwaccel_output_format cuda -c:v h264_cuvid `
-err_detect ignore_err -flags +output_corrupt -copyts `
-i $streamPath -map 0:v:0 -vf "hwdownload,format=nv12" `
-fps_mode passthrough -enc_time_base demux -frames:v $activeFrameCount `
-frame_pts 1 (Join-Path $decodedFramesRoot "frame-%d.png")
Assert-LastExitCode "LAB E4 camera extraction" Assert-LastExitCode "LAB E4 camera extraction"
& ffprobe -v error -select_streams v:0 -show_entries frame=best_effort_timestamp_time -of json $streamPath | Set-Content -LiteralPath $ptsPath -Encoding utf8
Assert-LastExitCode "LAB E4 camera timestamp probe" $decodedCount = @(Get-ChildItem -LiteralPath $decodedFramesRoot -File -Filter "frame-*.png").Count
$repairs = @()
$packetPts = @()
for ($index = 0; $index -lt $activeFrameCount; $index++) {
$columns = ([string]$packetRows[$index]).Split(",")
if ($columns.Count -lt 2) {
throw "LAB E4 packet timestamp row is malformed"
}
$pts = [int64]::Parse($columns[0].Trim(), [Globalization.CultureInfo]::InvariantCulture)
$packetPts += $pts
$decodedPath = Join-Path $decodedFramesRoot ("frame-{0}.png" -f $pts)
$canonicalPath = Join-Path $framesRoot ("frame-{0:D6}.png" -f ($index + 1))
if (Test-Path -LiteralPath $decodedPath -PathType Leaf) {
Move-Item -LiteralPath $decodedPath -Destination $canonicalPath
continue
}
if ($index -eq 0 -or $repairs.Count -ge 1) {
throw "LAB E4 source contains more than one recoverable decoder gap"
}
$previousPath = Join-Path $framesRoot ("frame-{0:D6}.png" -f $index)
Copy-Item -LiteralPath $previousPath -Destination $canonicalPath
$repairs += [ordered]@{
sequence = $index + 1
packet_pts = $pts
method = "duplicate-previous-decoded-frame"
}
}
$decodedFrames = @(Get-ChildItem -LiteralPath $framesRoot -File -Filter "frame-*.png") $decodedFrames = @(Get-ChildItem -LiteralPath $framesRoot -File -Filter "frame-*.png")
$ptsDocument = Get-Content -LiteralPath $ptsPath -Raw | ConvertFrom-Json if ($decodedFrames.Count -ne $activeFrameCount) {
$pts = @($ptsDocument.frames)
if ($decodedFrames.Count -ne $activeFrameCount -or $pts.Count -lt $activeFrameCount) {
throw "Decoded LAB E4 frame count differs from the requested camera epoch" throw "Decoded LAB E4 frame count differs from the requested camera epoch"
} }
$firstEpochSeconds = [double]::Parse( $decodeRepair = [ordered]@{
([string]$pts[0].best_effort_timestamp_time).Trim(), schema_version = "missioncore.recorded-video-decode-repair/v1"
[Globalization.CultureInfo]::InvariantCulture decoder = "ffmpeg-h264_cuvid-output-corrupt"
) packets_requested = $activeFrameCount
frames_decoded = $decodedCount
repaired_frame_count = $repairs.Count
repairs = $repairs
}
$decodeRepair | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $decodeRepairPath -Encoding utf8
$firstPacketPts = [int64]$packetPts[0]
$previousEpochSeconds = -1.0 $previousEpochSeconds = -1.0
$timelineWriter = [IO.StreamWriter]::new($timelinePath, $false, [Text.UTF8Encoding]::new($false)) $timelineWriter = [IO.StreamWriter]::new($timelinePath, $false, [Text.UTF8Encoding]::new($false))
try { try {
for ($index = 0; $index -lt $activeFrameCount; $index++) { for ($index = 0; $index -lt $activeFrameCount; $index++) {
$epochSeconds = [double]::Parse( $epochSeconds = ([int64]$packetPts[$index] - $firstPacketPts) / 90000.0
([string]$pts[$index].best_effort_timestamp_time).Trim(),
[Globalization.CultureInfo]::InvariantCulture
) - $firstEpochSeconds
if ($epochSeconds -le $previousEpochSeconds -or $epochSeconds -gt ($timelineDuration + 0.001)) { if ($epochSeconds -le $previousEpochSeconds -or $epochSeconds -gt ($timelineDuration + 0.001)) {
throw "Decoded LAB E4 timestamps are not strictly monotonic inside the camera timeline" throw "Decoded LAB E4 timestamps are not strictly monotonic inside the camera timeline"
} }
@@ -313,6 +356,7 @@ try {
Write-Output ("PHASE=e4-inference-start FRAMES={0}" -f $activeFrameCount) Write-Output ("PHASE=e4-inference-start FRAMES={0}" -f $activeFrameCount)
& docker @runArgs & docker @runArgs
Assert-LastExitCode "LAB E4 semantic inference" Assert-LastExitCode "LAB E4 semantic inference"
Copy-Item -LiteralPath $decodeRepairPath -Destination (Join-Path $stagingRoot "decode-repair.json")
$freeBytesPostInference = Assert-FreeSpace "post-inference" $freeBytesPostInference = Assert-FreeSpace "post-inference"
Write-Output "PHASE=e4-inference-complete" Write-Output "PHASE=e4-inference-complete"
@@ -12,7 +12,18 @@ param(
[string]$OutputRoot = "D:\NDC_MISSIONCORE\runtime\experiments\lab-v1-vegetation", [string]$OutputRoot = "D:\NDC_MISSIONCORE\runtime\experiments\lab-v1-vegetation",
[string]$RavnovesVideo = "D:\NDC_MISSIONCORE\runtime\experiments\e46e\inputs\right-cadd1696ff000904eb78633a0a8418104b8024f178b91f3421789021ccb160e8.mp4" [string]$RavnovesVideo = "D:\NDC_MISSIONCORE\runtime\experiments\e46e\inputs\right-cadd1696ff000904eb78633a0a8418104b8024f178b91f3421789021ccb160e8.mp4",
[string]$RavnovesSourceId = "RAVNOVES00/right-cadd1696ff000904eb78633a0a8418104b8024f178b91f3421789021ccb160e8",
[string]$RavnovesSha256 = "cadd1696ff000904eb78633a0a8418104b8024f178b91f3421789021ccb160e8",
[ValidateRange(1, 1000000)]
[int]$RavnovesExpectedFrameCount = 4489,
[string]$RavnovesBaseM4ResultId = "m4-threat-replay-2a953c5f27f2a5b1dddc5c658c1de2c323d7796084a099c024987a1da03aa324",
[string]$RavnovesSourceProfile = ""
) )
Set-StrictMode -Version Latest Set-StrictMode -Version Latest
@@ -38,6 +49,31 @@ $configRoot = Join-Path $ToolRoot "config"
$benchmarkConfig = Join-Path $configRoot "lab-v1-goose-vegetation-benchmark-v1.json" $benchmarkConfig = Join-Path $configRoot "lab-v1-goose-vegetation-benchmark-v1.json"
$policyConfig = Join-Path $configRoot "lab-v1-vegetation-mission-policy-v1.json" $policyConfig = Join-Path $configRoot "lab-v1-vegetation-mission-policy-v1.json"
$providerMapConfig = Join-Path $configRoot "lab-v1-vegetation-provider-label-map-v1.json" $providerMapConfig = Join-Path $configRoot "lab-v1-vegetation-provider-label-map-v1.json"
$ravnovesProfileDocument = $null
if (-not [string]::IsNullOrWhiteSpace($RavnovesSourceProfile)) {
$resolvedProfile = (Resolve-Path -LiteralPath $RavnovesSourceProfile).Path
if (-not $resolvedProfile.StartsWith($ToolRoot, [StringComparison]::OrdinalIgnoreCase)) {
throw "RAVNOVES source profile must stay under ToolRoot"
}
$ravnovesProfileDocument = Get-Content -LiteralPath $resolvedProfile -Raw | ConvertFrom-Json
$source = $ravnovesProfileDocument.source
if (
$ravnovesProfileDocument.schema_version -ne "missioncore.lab-v1-ravnoves-source/v1" -or
$null -eq $source -or
[string]::IsNullOrWhiteSpace([string]$source.source_id) -or
[string]$source.source_sha256 -notmatch "^[a-f0-9]{64}$" -or
[int]$source.expected_width -ne 800 -or
[int]$source.expected_height -ne 600 -or
[int]$source.expected_frame_count -lt 1 -or
[string]$source.crop_contract -ne "center-600-square-to-512; outside-crop-is-undefined"
) {
throw "RAVNOVES source profile is incompatible"
}
$RavnovesSourceId = [string]$source.source_id
$RavnovesSha256 = [string]$source.source_sha256
$RavnovesExpectedFrameCount = [int]$source.expected_frame_count
$RavnovesBaseM4ResultId = [string]$source.base_m4_result_id
}
$datasetRoot = Join-Path $AssetRoot "goose-2d\validation" $datasetRoot = Join-Path $AssetRoot "goose-2d\validation"
$checkpointRelative = if ($candidateKey -eq "ddrnet") { $checkpointRelative = if ($candidateKey -eq "ddrnet") {
"models\goose\ddrnet_class_512.pth" "models\goose\ddrnet_class_512.pth"
@@ -51,7 +87,6 @@ $expectedCheckpointSha256 = if ($candidateKey -eq "ddrnet") {
"6dd412c0c99115e359896c4cab43a8e6bce9e09b843e7fa885fe597b0a6121cd" "6dd412c0c99115e359896c4cab43a8e6bce9e09b843e7fa885fe597b0a6121cd"
} }
$expectedCheckpointBytes = if ($candidateKey -eq "ddrnet") { 259419077 } else { 98208249 } $expectedCheckpointBytes = if ($candidateKey -eq "ddrnet") { 259419077 } else { 98208249 }
$ravnovesSha256 = "cadd1696ff000904eb78633a0a8418104b8024f178b91f3421789021ccb160e8"
$frameIndices = @(0, 253, 512, 768, 1024, 1536, 2048, 2560, 3072, 3584, 4096, 4488) $frameIndices = @(0, 253, 512, 768, 1024, 1536, 2048, 2560, 3072, 3584, 4096, 4488)
$dockerConfig = "D:\NDC_MISSIONCORE\datasets\state\lab-v1-vegetation\docker-config" $dockerConfig = "D:\NDC_MISSIONCORE\datasets\state\lab-v1-vegetation\docker-config"
@@ -110,6 +145,18 @@ function Invoke-IsolatedRun {
[string]$FramesRoot = "" [string]$FramesRoot = ""
) )
$containerName = "ndc-lab-v1-goose-$candidateKey-$([Guid]::NewGuid().ToString('N').Substring(0, 10))" $containerName = "ndc-lab-v1-goose-$candidateKey-$([Guid]::NewGuid().ToString('N').Substring(0, 10))"
$activeConfigRoot = $configRoot
if ($RunMode -eq "ravnoves-video" -and $null -ne $ravnovesProfileDocument) {
$activeConfigRoot = Join-Path $RunRoot "effective-config"
New-Item -ItemType Directory -Path $activeConfigRoot | Out-Null
Copy-Item -LiteralPath $policyConfig -Destination $activeConfigRoot
Copy-Item -LiteralPath $providerMapConfig -Destination $activeConfigRoot
$benchmark = Get-Content -LiteralPath $benchmarkConfig -Raw | ConvertFrom-Json
$benchmark.ravnoves = $ravnovesProfileDocument.source
$benchmark | ConvertTo-Json -Depth 32 | Set-Content -LiteralPath (
Join-Path $activeConfigRoot "lab-v1-goose-vegetation-benchmark-v1.json"
) -Encoding utf8
}
$visualCount = if ($RunMode -eq "ravnoves-video") { 0 } else { 12 } $visualCount = if ($RunMode -eq "ravnoves-video") { 0 } else { 12 }
$arguments = @( $arguments = @(
"run", "--rm", "--name", $containerName, "run", "--rm", "--name", $containerName,
@@ -125,7 +172,7 @@ function Invoke-IsolatedRun {
"--env", "HOME=/tmp", "--env", "HOME=/tmp",
"--mount", "type=bind,src=$datasetRoot,dst=/data/goose,readonly", "--mount", "type=bind,src=$datasetRoot,dst=/data/goose,readonly",
"--mount", "type=bind,src=$checkpoint,dst=/models/candidate.pth,readonly", "--mount", "type=bind,src=$checkpoint,dst=/models/candidate.pth,readonly",
"--mount", "type=bind,src=$configRoot,dst=/config,readonly", "--mount", "type=bind,src=$activeConfigRoot,dst=/config,readonly",
"--mount", "type=bind,src=$RunRoot,dst=/output", "--mount", "type=bind,src=$RunRoot,dst=/output",
$image, $image,
"--mode", $RunMode, "--mode", $RunMode,
@@ -147,15 +194,27 @@ function Invoke-IsolatedRun {
$tail = @($arguments[$mountIndex..($arguments.Count - 1)]) $tail = @($arguments[$mountIndex..($arguments.Count - 1)])
$arguments = $head + @("--mount", "type=bind,src=$FramesRoot,dst=/input,readonly") + $tail $arguments = $head + @("--mount", "type=bind,src=$FramesRoot,dst=/input,readonly") + $tail
} }
& docker @arguments $dockerExitCode = -1
if ($LASTEXITCODE -ne 0) { $previousErrorActionPreference = $ErrorActionPreference
throw "LAB V1 container failed with exit code $LASTEXITCODE" try {
# Windows PowerShell exposes native stderr as ErrorRecord objects. Model
# libraries legitimately emit warnings there, so merge the stream and
# fail only on the native process exit code.
$ErrorActionPreference = "Continue"
& docker @arguments 2>&1 | ForEach-Object { Write-Output $_ }
$dockerExitCode = $LASTEXITCODE
}
finally {
$ErrorActionPreference = $previousErrorActionPreference
}
if ($dockerExitCode -ne 0) {
throw "LAB V1 container failed with exit code $dockerExitCode"
} }
} }
function Export-RavnovesFrames { function Export-RavnovesFrames {
param([string]$Destination) param([string]$Destination)
Assert-FileIdentity -Path $RavnovesVideo -ExpectedBytes (Get-Item -LiteralPath $RavnovesVideo).Length -ExpectedSha256 $ravnovesSha256 Assert-FileIdentity -Path $RavnovesVideo -ExpectedBytes (Get-Item -LiteralPath $RavnovesVideo).Length -ExpectedSha256 $RavnovesSha256
New-Item -ItemType Directory -Path $Destination | Out-Null New-Item -ItemType Directory -Path $Destination | Out-Null
$expression = ($frameIndices | ForEach-Object { "eq(n\,$_ )" }) -join "+" $expression = ($frameIndices | ForEach-Object { "eq(n\,$_ )" }) -join "+"
$temporaryPattern = Join-Path $Destination "selected-%03d.png" $temporaryPattern = Join-Path $Destination "selected-%03d.png"
@@ -175,14 +234,68 @@ function Export-RavnovesFrames {
function Export-RavnovesVideoFrames { function Export-RavnovesVideoFrames {
param([string]$Destination) param([string]$Destination)
Assert-FileIdentity -Path $RavnovesVideo -ExpectedBytes (Get-Item -LiteralPath $RavnovesVideo).Length -ExpectedSha256 $ravnovesSha256 Assert-FileIdentity -Path $RavnovesVideo -ExpectedBytes (Get-Item -LiteralPath $RavnovesVideo).Length -ExpectedSha256 $RavnovesSha256
New-Item -ItemType Directory -Path $Destination | Out-Null New-Item -ItemType Directory -Path $Destination | Out-Null
& ffmpeg -hide_banner -loglevel error -i $RavnovesVideo -map 0:v:0 -fps_mode passthrough (Join-Path $Destination "frame-%06d.png") $decodedRoot = "{0}-decoded-by-pts" -f $Destination
$packetsPath = "{0}-packets.csv" -f $Destination
New-Item -ItemType Directory -Path $decodedRoot | Out-Null
& ffprobe -v error -select_streams v:0 -show_packets -show_entries packet=pts,flags -of csv=p=0 -o $packetsPath $RavnovesVideo
if ($LASTEXITCODE -ne 0) {
throw "RAVNOVES full-video packet probe failed"
}
$packetRows = @(Get-Content -LiteralPath $packetsPath | Select-Object -First $RavnovesExpectedFrameCount)
if ($packetRows.Count -ne $RavnovesExpectedFrameCount) {
throw "RAVNOVES full-video packet sequence changed"
}
& ffmpeg -hide_banner -loglevel error `
-hwaccel cuda -hwaccel_output_format cuda -c:v h264_cuvid `
-err_detect ignore_err -flags +output_corrupt -copyts `
-i $RavnovesVideo -map 0:v:0 -vf "hwdownload,format=nv12" `
-fps_mode passthrough -enc_time_base demux -frames:v $RavnovesExpectedFrameCount `
-frame_pts 1 (Join-Path $decodedRoot "frame-%d.png")
if ($LASTEXITCODE -ne 0) { if ($LASTEXITCODE -ne 0) {
throw "RAVNOVES full-video frame extraction failed" throw "RAVNOVES full-video frame extraction failed"
} }
$decodedCount = @(Get-ChildItem -LiteralPath $decodedRoot -File -Filter "frame-*.png").Count
$repairs = @()
for ($index = 0; $index -lt $RavnovesExpectedFrameCount; $index++) {
$columns = ([string]$packetRows[$index]).Split(",")
if ($columns.Count -lt 2) {
throw "RAVNOVES full-video packet row is malformed"
}
$pts = [int64]::Parse($columns[0].Trim(), [Globalization.CultureInfo]::InvariantCulture)
$decodedPath = Join-Path $decodedRoot ("frame-{0}.png" -f $pts)
$canonicalPath = Join-Path $Destination ("frame-{0:D6}.png" -f ($index + 1))
if (Test-Path -LiteralPath $decodedPath -PathType Leaf) {
Move-Item -LiteralPath $decodedPath -Destination $canonicalPath
continue
}
if ($index -eq 0 -or $repairs.Count -ge 1) {
throw "RAVNOVES source contains more than one recoverable decoder gap"
}
$previousPath = Join-Path $Destination ("frame-{0:D6}.png" -f $index)
Copy-Item -LiteralPath $previousPath -Destination $canonicalPath
$repairs += [ordered]@{
sequence = $index + 1
packet_pts = $pts
method = "duplicate-previous-decoded-frame"
}
}
Remove-Item -LiteralPath $decodedRoot -Recurse -Force
Remove-Item -LiteralPath $packetsPath -Force
[ordered]@{
schema_version = "missioncore.recorded-video-decode-repair/v1"
decoder = "ffmpeg-h264_cuvid-output-corrupt"
packets_requested = $RavnovesExpectedFrameCount
frames_decoded = $decodedCount
repaired_frame_count = $repairs.Count
repairs = $repairs
} | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath (
Join-Path (Split-Path $Destination -Parent) "decode-repair.json"
) -Encoding utf8
$frames = @(Get-ChildItem -LiteralPath $Destination -File -Filter "frame-*.png" | Sort-Object Name) $frames = @(Get-ChildItem -LiteralPath $Destination -File -Filter "frame-*.png" | Sort-Object Name)
if ($frames.Count -ne 4489 -or $frames[0].Name -ne "frame-000001.png" -or $frames[-1].Name -ne "frame-004489.png") { $lastFrameName = "frame-{0:D6}.png" -f $RavnovesExpectedFrameCount
if ($frames.Count -ne $RavnovesExpectedFrameCount -or $frames[0].Name -ne "frame-000001.png" -or $frames[-1].Name -ne $lastFrameName) {
throw "RAVNOVES full-video frame sequence changed" throw "RAVNOVES full-video frame sequence changed"
} }
} }
@@ -244,6 +357,9 @@ try {
$framesRoot = Join-Path $runRoot "input-frames" $framesRoot = Join-Path $runRoot "input-frames"
Export-RavnovesVideoFrames -Destination $framesRoot Export-RavnovesVideoFrames -Destination $framesRoot
Invoke-IsolatedRun -RunMode "ravnoves-video" -RunRoot $runRoot -Limit 0 -FramesRoot $framesRoot Invoke-IsolatedRun -RunMode "ravnoves-video" -RunRoot $runRoot -Limit 0 -FramesRoot $framesRoot
Copy-Item -LiteralPath (Join-Path $runRoot "decode-repair.json") -Destination (
Join-Path $runRoot "result\decode-repair.json"
)
Remove-Item -LiteralPath $framesRoot -Recurse -Force Remove-Item -LiteralPath $framesRoot -Recurse -Force
} }
} }
@@ -0,0 +1,38 @@
#!/usr/bin/env python3
"""Seal the complete RAVNOVES004TREE semantic pass into existing LAB V1."""
from __future__ import annotations
import argparse
from pathlib import Path
from k1link.laboratory.mixed_route_vegetation_review import (
seal_mixed_route_full_video_review,
)
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--base-lab-root", type=Path, required=True)
parser.add_argument("--job-root", type=Path, required=True)
parser.add_argument("--recorded-media-preparation", type=Path, required=True)
parser.add_argument("--eomt-root", type=Path, required=True)
parser.add_argument("--eomt-profile", type=Path, required=True)
parser.add_argument("--ddrnet-root", type=Path, required=True)
parser.add_argument("--output-root", type=Path, required=True)
args = parser.parse_args()
print(
seal_mixed_route_full_video_review(
base_lab_root=args.base_lab_root,
job_root=args.job_root,
recorded_media_preparation_path=args.recorded_media_preparation,
eomt_root=args.eomt_root,
eomt_profile_path=args.eomt_profile,
ddrnet_root=args.ddrnet_root,
output_root=args.output_root,
)
)
if __name__ == "__main__":
main()
@@ -7,7 +7,9 @@ import hashlib
import json import json
import os import os
import shutil import shutil
import tarfile
import tempfile import tempfile
import zipfile
from datetime import UTC, datetime from datetime import UTC, datetime
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
@@ -15,6 +17,8 @@ from typing import Any
import numpy as np import numpy as np
from PIL import Image, ImageDraw from PIL import Image, ImageDraw
from k1link.compute.jobs import validate_camera_compute_job
from k1link.laboratory.vegetation_shadow_lab import ( from k1link.laboratory.vegetation_shadow_lab import (
LAB_SCHEMA, LAB_SCHEMA,
RESULT_PREFIX, RESULT_PREFIX,
@@ -45,11 +49,20 @@ TGS_COLORS = {
2: (235, 112, 122), 2: (235, 112, 122),
3: (150, 154, 163), 3: (150, 154, 163),
} }
FULL_ROUTE_SOURCE_ID = "RAVNOVES004TREE"
FULL_ROUTE_FRAME_COUNT = 6830
FULL_ROUTE_JOB_ID = "recorded-camera-eb2783c5480d56bda07c8af0"
FULL_ROUTE_INPUT_SHA256 = (
"eb2783c5480d56bda07c8af008dff5344d19dc550ef70fe2075d6f098f7cc715"
)
FULL_ROUTE_STREAM_SHA256 = (
"e5eb017e2cc0f546736eda5235ca157b501913093cb64af5e548e335417e1bac"
)
def _read_json(path: Path, label: str) -> dict[str, Any]: def _read_json(path: Path, label: str) -> dict[str, Any]:
try: try:
value = json.loads(path.read_text(encoding="utf-8")) value = json.loads(path.read_text(encoding="utf-8-sig"))
except (OSError, json.JSONDecodeError) as exc: except (OSError, json.JSONDecodeError) as exc:
raise VegetationShadowLabError(f"{label} is invalid") from exc raise VegetationShadowLabError(f"{label} is invalid") from exc
if not isinstance(value, dict): if not isinstance(value, dict):
@@ -86,6 +99,126 @@ def _image_proof(descriptor: dict[str, object]) -> dict[str, object]:
return {"path": descriptor["path"], "sha256": descriptor["sha256"]} return {"path": descriptor["path"], "sha256": descriptor["sha256"]}
def _mask_archive_descriptor(
path: Path,
relative: str,
artifacts: list[dict[str, object]],
*,
role: str,
) -> dict[str, object]:
descriptor = {
"role": role,
"path": relative,
"byte_length": path.stat().st_size,
"sha256": sha256_path(path),
"media_type": "application/zip",
}
artifacts.append(descriptor)
return descriptor
def _repack_eomt_masks(source: Path, destination: Path, frame_count: int) -> None:
destination.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
expected = [f"semantic-masks/frame-{sequence + 1:06d}.png" for sequence in range(frame_count)]
try:
with (
tarfile.open(source, mode="r:gz") as archive,
zipfile.ZipFile(
destination,
mode="x",
compression=zipfile.ZIP_STORED,
allowZip64=True,
) as output,
):
members = [member for member in archive.getmembers() if member.isfile()]
if [member.name.removeprefix("./") for member in members] != expected:
raise VegetationShadowLabError("full-route EoMT mask sequence changed")
for member, expected_name in zip(members, expected, strict=True):
if member.size < 8 or member.size > 1024 * 1024:
raise VegetationShadowLabError("full-route EoMT mask size changed")
stream = archive.extractfile(member)
if stream is None:
raise VegetationShadowLabError("full-route EoMT mask is unavailable")
output.writestr(
f"masks/{Path(expected_name).name}",
stream.read(),
)
except (OSError, tarfile.TarError, zipfile.BadZipFile) as exc:
destination.unlink(missing_ok=True)
raise VegetationShadowLabError("full-route EoMT archive is invalid") from exc
def _validate_zip_masks(path: Path, frame_count: int) -> None:
expected = [f"masks/frame-{sequence + 1:06d}.png" for sequence in range(frame_count)]
try:
with zipfile.ZipFile(path) as archive:
members = archive.infolist()
if (
[member.filename for member in members] != expected
or any(
member.is_dir() or member.file_size < 8 or member.file_size > 1024 * 1024
for member in members
)
):
raise VegetationShadowLabError("full-route semantic mask sequence changed")
except (OSError, zipfile.BadZipFile) as exc:
raise VegetationShadowLabError("full-route semantic archive is invalid") from exc
def _full_route_frame_times(media: dict[str, Any], frame_count: int) -> list[int]:
epochs = media.get("epochs")
start = media.get("timeline_start_seconds")
end = media.get("timeline_end_seconds")
if (
not isinstance(epochs, list)
or len(epochs) != 1
or not isinstance(start, (int, float))
or not isinstance(end, (int, float))
):
raise VegetationShadowLabError("recorded media timeline changed")
epoch = epochs[0]
segments = epoch.get("segments") if isinstance(epoch, dict) else None
if not isinstance(segments, list) or len(segments) != frame_count:
raise VegetationShadowLabError("recorded media segment count changed")
starts = [float(start)]
previous_end = 0.0
for sequence, raw in enumerate(segments, start=1):
if (
not isinstance(raw, dict)
or raw.get("sequence") != sequence
or not isinstance(raw.get("end_time_seconds"), (int, float))
or float(raw["end_time_seconds"]) <= previous_end
):
raise VegetationShadowLabError("recorded media segment timeline changed")
if sequence < frame_count:
starts.append(float(start) + float(raw["end_time_seconds"]))
previous_end = float(raw["end_time_seconds"])
if abs((float(start) + previous_end) - float(end)) > 0.001:
raise VegetationShadowLabError("recorded media duration changed")
return [round(value * 1_000_000_000) for value in starts]
def _eomt_taxonomy(profile: dict[str, Any]) -> dict[str, object]:
taxonomy = profile.get("target_taxonomy")
if not isinstance(taxonomy, dict) or set(taxonomy) != {str(index) for index in range(16)}:
raise VegetationShadowLabError("EoMT target taxonomy changed")
classes = []
for class_id in range(16):
digest = hashlib.sha256(f"mission-core-segment-{class_id}".encode()).digest()
classes.append(
{
"class_id": class_id,
"label": taxonomy[str(class_id)],
"color_rgb": [64 + digest[index] % 176 for index in range(3)],
"disposition": "undefined" if class_id == 0 else "prediction",
}
)
return {
"schema_version": "missioncore.recorded-eomt-taxonomy/v1",
"classes": classes,
}
def _render_tgs_costmaps(tgs_root: Path, destination: Path) -> list[Path]: def _render_tgs_costmaps(tgs_root: Path, destination: Path) -> list[Path]:
result = _read_json(tgs_root / "result.json", "mixed-route TGS result") result = _read_json(tgs_root / "result.json", "mixed-route TGS result")
evidence = result.get("evidence") evidence = result.get("evidence")
@@ -392,6 +525,338 @@ def seal_mixed_route_vegetation_review(
raise raise
def seal_mixed_route_full_video_review(
*,
base_lab_root: Path,
job_root: Path,
recorded_media_preparation_path: Path,
eomt_root: Path,
eomt_profile_path: Path,
ddrnet_root: Path,
output_root: Path,
) -> Path:
"""Publish the complete 004 city/nature pass in the existing M4.7 LAB."""
base_root = base_lab_root.resolve(strict=True)
base = _read_json(base_root / "result.json", "base vegetation LAB")
base_identity = base.get("identity")
if (
base.get("schema_version") != LAB_SCHEMA
or not isinstance(base_identity, dict)
or hashlib.sha256(canonical_json(base_identity)).hexdigest()
!= base.get("identity_sha256")
or base.get("result_id") != base_root.name
or not base_root.name.startswith(RESULT_PREFIX)
or base.get("authority", {}).get("commands_enabled") is not False
):
raise VegetationShadowLabError("base vegetation LAB proof changed")
job = validate_camera_compute_job(job_root)
if (
job.job_id != FULL_ROUTE_JOB_ID
or job.input_sha256 != FULL_ROUTE_INPUT_SHA256
or job.session_id != "20260828T130511Z_viewer_live"
or job.source_id != "sensor.camera.right"
or job.segment_count != FULL_ROUTE_FRAME_COUNT
):
raise VegetationShadowLabError("full-route camera job changed")
eomt = _read_json(eomt_root / "result.json", "full-route EoMT result")
eomt_report = _read_json(eomt_root / "run-report.json", "full-route EoMT report")
decode_repair = _read_json(
eomt_root / "decode-repair.json",
"full-route video decode repair",
)
eomt_input = eomt_report.get("input")
eomt_metrics = eomt_report.get("metrics")
if (
eomt.get("schema_version") != "missioncore.recorded-perception-result/v2"
or eomt.get("ground_truth") is not False
or eomt.get("frames_processed") != FULL_ROUTE_FRAME_COUNT
or not isinstance(eomt_input, dict)
or eomt_input.get("job_id") != job.job_id
or eomt_input.get("input_sha256") != job.input_sha256
or eomt_input.get("frames_admitted") != FULL_ROUTE_FRAME_COUNT
or not isinstance(eomt_metrics, dict)
or eomt_metrics.get("frames_processed") != FULL_ROUTE_FRAME_COUNT
):
raise VegetationShadowLabError("full-route EoMT contract changed")
if (
decode_repair.get("schema_version")
!= "missioncore.recorded-video-decode-repair/v1"
or decode_repair.get("decoder") != "ffmpeg-h264_cuvid-output-corrupt"
or decode_repair.get("packets_requested") != FULL_ROUTE_FRAME_COUNT
or decode_repair.get("frames_decoded") != FULL_ROUTE_FRAME_COUNT - 1
or decode_repair.get("repaired_frame_count") != 1
or decode_repair.get("repairs")
!= [
{
"sequence": 6092,
"packet_pts": 55656450,
"method": "duplicate-previous-decoded-frame",
}
]
):
raise VegetationShadowLabError("full-route video decode repair changed")
eomt_artifacts = {
item.get("kind"): item
for item in eomt.get("artifacts", [])
if isinstance(item, dict)
}
eomt_archive_proof = eomt_artifacts.get("panoptic-mask-archive")
if not isinstance(eomt_archive_proof, dict):
raise VegetationShadowLabError("full-route EoMT mask proof is missing")
eomt_archive = eomt_root / str(eomt_archive_proof.get("path"))
if (
not eomt_archive.is_file()
or eomt_archive.stat().st_size != eomt_archive_proof.get("byte_length")
or sha256_path(eomt_archive) != eomt_archive_proof.get("sha256")
):
raise VegetationShadowLabError("full-route EoMT mask proof changed")
ddrnet = _read_json(ddrnet_root / "result.json", "full-route DDRNet result")
ddrnet_decode_repair = _read_json(
ddrnet_root / "decode-repair.json",
"full-route DDRNet video decode repair",
)
ddrnet_source = ddrnet.get("source")
ddrnet_video = ddrnet.get("video_semantics")
if (
ddrnet.get("schema_version") != "missioncore.lab-v1-goose-vegetation-run/v1"
or ddrnet.get("mode") != "ravnoves-video"
or ddrnet.get("candidate", {}).get("candidate_key") != "ddrnet"
or not isinstance(ddrnet_source, dict)
or ddrnet_source.get("source_id")
!= f"{FULL_ROUTE_SOURCE_ID}/right-{FULL_ROUTE_STREAM_SHA256}"
or ddrnet_source.get("input_count") != FULL_ROUTE_FRAME_COUNT
or ddrnet_source.get("ground_truth_available") is not False
or not isinstance(ddrnet_video, dict)
or ddrnet_video.get("base_m4_result_id") is not None
or ddrnet.get("authority", {}).get("navigation_accepted") is not False
or ddrnet.get("authority", {}).get("actuation_accepted") is not False
):
raise VegetationShadowLabError("full-route DDRNet contract changed")
if ddrnet_decode_repair != decode_repair:
raise VegetationShadowLabError("full-route model decoders disagree")
ddrnet_archive_proof = ddrnet_video.get("mask_archive")
ddrnet_taxonomy = ddrnet_video.get("taxonomy")
if (
not isinstance(ddrnet_archive_proof, dict)
or ddrnet_archive_proof.get("frame_count") != FULL_ROUTE_FRAME_COUNT
or not isinstance(ddrnet_taxonomy, dict)
):
raise VegetationShadowLabError("full-route DDRNet mask proof changed")
ddrnet_archive = ddrnet_root / str(ddrnet_archive_proof.get("path"))
if (
not ddrnet_archive.is_file()
or ddrnet_archive.stat().st_size != ddrnet_archive_proof.get("byte_length")
or sha256_path(ddrnet_archive) != ddrnet_archive_proof.get("sha256")
):
raise VegetationShadowLabError("full-route DDRNet archive changed")
_validate_zip_masks(ddrnet_archive, FULL_ROUTE_FRAME_COUNT)
media_document = _read_json(
recorded_media_preparation_path.resolve(strict=True),
"recorded media preparation",
)
media = media_document.get("manifest")
if (
media_document.get("schema_version") != "missioncore.recorded-media-preparation/v3"
or media_document.get("session_id") != job.session_id
or media_document.get("artifact_id") != "recorded-video-6a3945242828a038"
or media_document.get("checksum_sha256")
!= "557e61f2839140dc9f97b5aea855c576b0616573080dff5d2852ab1df0558665"
or not isinstance(media, dict)
or media.get("source_id") != "recorded.camera.6a3945242828a038"
or media.get("generation_sha256")
!= "b073ea1e7babf1c77a664e1a5b95e3702d0e05b0e34c1e85a7c67a6f8b392ded"
or media.get("byte_length") != 551674491
or media.get("timeline_start_seconds") != job.timeline_start_seconds
or media.get("timeline_end_seconds") != job.timeline_end_seconds
or media.get("synchronization") != "host-arrival-best-effort"
):
raise VegetationShadowLabError("recorded media preparation changed")
frame_times_ns = _full_route_frame_times(media, FULL_ROUTE_FRAME_COUNT)
eomt_profile = _read_json(eomt_profile_path.resolve(strict=True), "EoMT profile")
eomt_taxonomy = _eomt_taxonomy(eomt_profile)
output_root.mkdir(mode=0o700, parents=True, exist_ok=True)
temporary = Path(tempfile.mkdtemp(prefix=".mixed-route-full-video-", dir=output_root))
artifacts: list[dict[str, object]] = []
try:
eomt_destination = temporary / "video" / "eomt-semantic-masks.zip"
_repack_eomt_masks(eomt_archive, eomt_destination, FULL_ROUTE_FRAME_COUNT)
_validate_zip_masks(eomt_destination, FULL_ROUTE_FRAME_COUNT)
eomt_descriptor = _mask_archive_descriptor(
eomt_destination,
"video/eomt-semantic-masks.zip",
artifacts,
role="full-route-eomt-semantic-mask-archive",
)
ddrnet_descriptor = _artifact(
ddrnet_archive,
temporary,
"video/ddrnet-semantic-masks.zip",
artifacts,
role="full-route-ddrnet-semantic-mask-archive",
media_type="application/zip",
)
_validate_zip_masks(
temporary / "video" / "ddrnet-semantic-masks.zip",
FULL_ROUTE_FRAME_COUNT,
)
proof_descriptors: dict[str, dict[str, object]] = {}
for key, path in (
("base", base_root / "result.json"),
("job", job.manifest_path),
("media", recorded_media_preparation_path.resolve(strict=True)),
("eomt", eomt_root / "result.json"),
("eomt_report", eomt_root / "run-report.json"),
("decode_repair", eomt_root / "decode-repair.json"),
("ddrnet", ddrnet_root / "result.json"),
("ddrnet_decode_repair", ddrnet_root / "decode-repair.json"),
):
descriptor = _artifact(
path,
temporary,
f"proofs/{key}.json",
artifacts,
role="full-route-proof",
media_type="application/json",
)
proof_descriptors[key] = _image_proof(descriptor)
full_route = {
"source_id": FULL_ROUTE_SOURCE_ID,
"session_id": job.session_id,
"source_job_id": job.job_id,
"source_job_input_sha256": job.input_sha256,
"source_stream_sha256": FULL_ROUTE_STREAM_SHA256,
"recorded_media_source_id": media["source_id"],
"recorded_media_generation_sha256": media["generation_sha256"],
"frame_count": FULL_ROUTE_FRAME_COUNT,
"width": 800,
"height": 600,
"timeline_start_seconds": job.timeline_start_seconds,
"timeline_end_seconds": job.timeline_end_seconds,
"frame_source_times_ns": frame_times_ns,
"ground_truth": False,
"decode_repair": {
"repaired_frame_count": 1,
"sequence": 6092,
"method": "duplicate-previous-decoded-frame",
"proofs": {
"eomt": proof_descriptors["decode_repair"],
"ddrnet": proof_descriptors["ddrnet_decode_repair"],
},
},
"layers": {
"city": {
"name": "EoMT Cityscapes",
"result_id": eomt["result_id"],
"frame_count": FULL_ROUTE_FRAME_COUNT,
"taxonomy": eomt_taxonomy,
"mask_archive": {
"path": eomt_descriptor["path"],
"sha256": eomt_descriptor["sha256"],
"byte_length": eomt_descriptor["byte_length"],
},
"inference_fps": eomt_metrics["inference_frames_per_second"],
"latency_p95_ms": eomt_metrics["latency_ms"]["end_to_end_ms"]["p95"],
"peak_reserved_vram_bytes": int(
float(eomt_metrics["cuda_peak_memory_reserved_mib"]) * 1024 * 1024
),
},
"vegetation": {
"name": ddrnet["candidate"]["loaded_model_name"],
"result_id": ddrnet["result_id"],
"frame_count": FULL_ROUTE_FRAME_COUNT,
"taxonomy": ddrnet_taxonomy,
"mask_archive": {
"path": ddrnet_descriptor["path"],
"sha256": ddrnet_descriptor["sha256"],
"byte_length": ddrnet_descriptor["byte_length"],
},
"inference_fps": ddrnet["timing"]["throughput_fps_from_mean_inference"],
"latency_p95_ms": ddrnet["timing"]["latency_ms_p95"],
"peak_reserved_vram_bytes": ddrnet["resource"]["peak_reserved_vram_bytes"],
},
},
"proofs": proof_descriptors,
"limitations": [
"RAVNOVES004TREE has no manual route truth.",
"One corrupt H.264 packet at sequence 6092 was represented by the previous decoded frame; the repair is sealed as evidence.",
"EoMT and DDRNet were executed sequentially, not as a concurrent realtime stack.",
"DDRNet vegetation subtypes remain prediction-only and are not planner authority.",
"This full-video pass does not add full-route TGS, ditch or negative-obstacle proof.",
"People and vehicles still require an independent fail-safe detector and STOP path.",
],
}
authority = {
"commands_enabled": False,
"navigation_or_safety_accepted": False,
"actuation_accepted": False,
"camera_semantics_can_clear_rigid_geometry": False,
}
identity = {
"lab_id": "lab-v1-vegetation-mission-policy",
"base_result_id": base["result_id"],
"selected_candidate": base_identity["selected_candidate"],
"candidate_metrics": base_identity["candidate_metrics"],
"source": {
"shadow_session": FULL_ROUTE_SOURCE_ID,
"shadow_camera": job.source_id,
"shadow_frame_count": FULL_ROUTE_FRAME_COUNT,
"video_shadow_frame_count": FULL_ROUTE_FRAME_COUNT,
},
"route_full_review": full_route,
"authority": authority,
}
identity_sha256 = hashlib.sha256(canonical_json(identity)).hexdigest()
result_id = f"{RESULT_PREFIX}{identity_sha256}"
manifest = {
"schema_version": LAB_SCHEMA,
"result_id": result_id,
"identity_sha256": identity_sha256,
"created_at_utc": datetime.now(UTC).isoformat(),
"ground_truth": False,
"status": "visual-shadow-ready-policy-not-authorized",
"identity": identity,
"source": identity["source"],
"route_video": None,
"route_review": None,
"route_full_review": full_route,
"method": {
"completeness": "complete",
"execution_class": "ai-inference",
"pipeline_id": "ravnoves004tree-full-eomt-ddrnet-recorded-review/v1",
},
"metrics": {"candidates": base["metrics"]["candidates"]},
"decision": {
"selected_candidate": base_identity["selected_candidate"],
"visual_shadow_ready": True,
"full_video_shadow_ready": True,
"mission_policy_ready_for_configuration": True,
"multilayer_policy_review_ready": True,
"navigation_accepted": False,
"production_accepted": False,
},
"limitations": full_route["limitations"],
"authority": authority,
"catalogs": {"goose": [], "ravnoves": []},
"artifacts": artifacts,
}
(temporary / "result.json").write_bytes(canonical_json(manifest) + b"\n")
destination = output_root / result_id
if destination.exists():
raise VegetationShadowLabError("immutable full-route LAB result already exists")
os.replace(temporary, destination)
return destination
except Exception:
shutil.rmtree(temporary, ignore_errors=True)
raise
def main() -> None: def main() -> None:
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--base-lab-root", type=Path, required=True) parser.add_argument("--base-lab-root", type=Path, required=True)
@@ -179,9 +179,77 @@ def _build_vegetation_lab_router(
}, },
) )
@router.get("/{result_id}/route-masks/{layer}/{sequence}")
def get_full_route_mask(result_id: str, layer: str, sequence: int) -> Response:
candidate = _resolve_candidate(root_provider, definition, result_id)
manifest = _read_verified(candidate, definition)
route = manifest.get("route_full_review")
layers = route.get("layers") if isinstance(route, dict) else None
frame_count = route.get("frame_count") if isinstance(route, dict) else None
selected = layers.get(layer) if isinstance(layers, dict) else None
archive = selected.get("mask_archive") if isinstance(selected, dict) else None
archive_relative = archive.get("path") if isinstance(archive, dict) else None
if (
layer not in {"city", "vegetation"}
or not isinstance(frame_count, int)
or not 0 <= sequence < frame_count
or not isinstance(archive_relative, str)
):
raise HTTPException(status_code=404, detail="Full-route semantic mask not found")
relative = PurePosixPath(archive_relative)
artifacts = manifest.get("artifacts")
if (
relative.is_absolute()
or str(relative) != archive_relative
or any(part in {"", ".", ".."} for part in relative.parts)
or relative.suffix != ".zip"
or not isinstance(artifacts, list)
or not any(
isinstance(item, dict)
and item.get("path") == archive_relative
and item.get("media_type") == "application/zip"
for item in artifacts
)
):
raise HTTPException(status_code=404, detail="Full-route semantic mask not found")
return _zip_mask_response(candidate.joinpath(*relative.parts), sequence)
return router return router
def _zip_mask_response(archive_path: Path, sequence: int) -> Response:
member = f"masks/frame-{sequence + 1:06d}.png"
try:
before = archive_path.stat()
with zipfile.ZipFile(archive_path) as frozen:
info = frozen.getinfo(member)
if info.is_dir() or info.file_size < 8 or info.file_size > 1024 * 1024:
raise ValueError("Semantic mask member is invalid")
payload = frozen.read(info)
after = archive_path.stat()
if (
before.st_size != after.st_size
or before.st_mtime_ns != after.st_mtime_ns
or len(payload) != info.file_size
):
raise ValueError("Semantic mask archive changed during read")
except (KeyError, OSError, ValueError, zipfile.BadZipFile):
raise HTTPException(
status_code=503,
detail="Semantic mask failed verification",
) from None
digest = hashlib.sha256(payload).hexdigest()
return Response(
content=payload,
media_type="image/png",
headers={
"Cache-Control": "private, max-age=31536000, immutable",
"ETag": f'"{digest}"',
"X-Content-Type-Options": "nosniff",
},
)
def _configured_root(provider: RootProvider) -> Path | None: def _configured_root(provider: RootProvider) -> Path | None:
candidate = provider() candidate = provider()
if candidate is None: if candidate is None:
@@ -104,3 +104,20 @@ def test_e4_class_fractions_use_only_valid_fov_pixels() -> None:
assert {item["id"]: item["pixels"] for item in classes} == {1: 1, 4: 2, 7: 1} assert {item["id"]: item["pixels"] for item in classes} == {1: 1, 4: 2, 7: 1}
assert sum(float(item["fraction_of_valid_fov"]) for item in classes) == 1.0 assert sum(float(item["fraction_of_valid_fov"]) for item in classes) == 1.0
def test_e4_orchestrator_seals_a_single_decoder_gap_without_frame_shift() -> None:
path = (
Path(__file__).parents[1]
/ "experiments"
/ "perception"
/ "worker"
/ "Invoke-E4FullSessionSegmentation.ps1"
)
source = path.read_text(encoding="utf-8")
assert "-c:v h264_cuvid" in source
assert "-frame_pts 1" in source
assert '$decodedPath = Join-Path $decodedFramesRoot ("frame-{0}.png" -f $pts)' in source
assert "$repairs.Count -ge 1" in source
assert 'method = "duplicate-previous-decoded-frame"' in source
assert 'schema_version = "missioncore.recorded-video-decode-repair/v1"' in source
@@ -25,6 +25,12 @@ POWERSHELL_PATH = (
/ "worker" / "worker"
/ "Invoke-LabV1VegetationGooseBenchmark.ps1" / "Invoke-LabV1VegetationGooseBenchmark.ps1"
) )
RAV004_SOURCE_PATH = (
REPOSITORY_ROOT
/ "config"
/ "perception"
/ "lab-v1-ravnoves004tree-full-video-source-v1.json"
)
def test_benchmark_contract_is_bounded_and_fail_closed() -> None: def test_benchmark_contract_is_bounded_and_fail_closed() -> None:
@@ -93,3 +99,21 @@ def test_worker_wrapper_is_isolated_from_canonical_triton() -> None:
assert '"--cap-drop", "ALL"' in source assert '"--cap-drop", "ALL"' in source
assert '"--security-opt", "no-new-privileges"' in source assert '"--security-opt", "no-new-privileges"' in source
assert "if ($canonicalAfter -ne $canonicalBefore)" in source assert "if ($canonicalAfter -ne $canonicalBefore)" in source
def test_rav004_full_video_profile_and_decoder_gap_are_explicit() -> None:
profile = json.loads(RAV004_SOURCE_PATH.read_text(encoding="utf-8"))
source_profile = profile["source"]
assert profile["schema_version"] == "missioncore.lab-v1-ravnoves-source/v1"
assert source_profile["source_job_id"] == (
"recorded-camera-eb2783c5480d56bda07c8af0"
)
assert source_profile["expected_frame_count"] == 6830
assert source_profile["base_m4_result_id"] is None
source = POWERSHELL_PATH.read_text(encoding="utf-8")
assert "-c:v h264_cuvid" in source
assert 'schema_version = "missioncore.recorded-video-decode-repair/v1"' in source
assert 'method = "duplicate-previous-decoded-frame"' in source
assert "$repairs.Count -ge 1" in source
assert '& docker @arguments 2>&1 | ForEach-Object { Write-Output $_ }' in source
assert 'if ($dockerExitCode -ne 0)' in source
+61
View File
@@ -270,6 +270,67 @@ def test_vegetation_shadow_lab_seals_autonomous_visual_evidence(
assert mask.content == b"\x89PNG\r\n\x1a\n" assert mask.content == b"\x89PNG\r\n\x1a\n"
assert mask.headers["cache-control"].endswith("immutable") assert mask.headers["cache-control"].endswith("immutable")
full_archive_payloads = (b"\x89PNG\r\n\x1a\ncity", b"\x89PNG\r\n\x1a\nvegetation")
full_identity = dict(manifest["identity"])
full_route = {
"frame_count": 2,
"layers": {
layer: {"mask_archive": {"path": "video/full-route-masks.zip"}}
for layer in ("city", "vegetation")
},
}
full_identity["route_full_review"] = full_route
full_identity_sha = hashlib.sha256(
json.dumps(
full_identity,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
).encode("utf-8")
).hexdigest()
full_result_id = f"lab-v1-vegetation-shadow-{full_identity_sha}"
full_root = result_root.parent / full_result_id
shutil.copytree(result_root, full_root)
full_archive = full_root / "video" / "full-route-masks.zip"
full_archive.parent.mkdir(exist_ok=True)
with zipfile.ZipFile(full_archive, "x", compression=zipfile.ZIP_STORED) as frozen:
for sequence, payload in enumerate(full_archive_payloads, start=1):
frozen.writestr(f"masks/frame-{sequence:06d}.png", payload)
full_manifest = dict(manifest)
full_manifest["result_id"] = full_result_id
full_manifest["identity"] = full_identity
full_manifest["identity_sha256"] = full_identity_sha
full_manifest["route_full_review"] = full_route
full_manifest["artifacts"] = [
*manifest["artifacts"],
{
"role": "full-route-mask-fixture",
"path": "video/full-route-masks.zip",
"byte_length": full_archive.stat().st_size,
"sha256": _sha256(full_archive),
"media_type": "application/zip",
},
]
(full_root / "result.json").write_text(
json.dumps(full_manifest, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
+ "\n",
encoding="utf-8",
)
for layer, sequence, expected in (
("city", 0, full_archive_payloads[0]),
("vegetation", 1, full_archive_payloads[1]),
):
response = client.get(
f"/api/v1/laboratory/vegetation-shadow/{full_result_id}"
f"/route-masks/{layer}/{sequence}"
)
assert response.status_code == 200
assert response.content == expected
assert response.headers["cache-control"].endswith("immutable")
assert client.get(
f"/api/v1/laboratory/vegetation-shadow/{full_result_id}/route-masks/city/2"
).status_code == 404
(result_root / asset_path).write_bytes(b"tampered") (result_root / asset_path).write_bytes(b"tampered")
assert ( assert (
client.get(f"/api/v1/laboratory/vegetation-shadow/{result_root.name}").status_code client.get(f"/api/v1/laboratory/vegetation-shadow/{result_root.name}").status_code