Compare commits
6
Commits
d50d3bb3d3
...
34ed8a0f5f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34ed8a0f5f | ||
|
|
64860be4ae | ||
|
|
4ac07f7671 | ||
|
|
bcda5d001a | ||
|
|
cbbaea9148 | ||
|
|
3d3c0eced2 |
@@ -37,8 +37,10 @@ import { fetchE46IGroundingDinoFullReplay } from "./e46iGroundingDinoFullReplay"
|
||||
import { fetchE46JRawFisheyeRealtime } from "./e46jRawFisheyeRealtime";
|
||||
import { fetchE47SemanticSlamResult } from "./e47SemanticSlam";
|
||||
import { fetchM4ThreatReplayResult } from "./m4ReplayThreat";
|
||||
import { fetchM47ReferenceGraphLab } from "./m47ReferenceGraph";
|
||||
|
||||
export type AdvancedLaboratoryWorkId =
|
||||
| "m47-reference-graph-shadow"
|
||||
| "m4-replay-threat"
|
||||
| "l3-pointpillars-visual-audit"
|
||||
| "l31-pointpillars-ravnoves"
|
||||
@@ -80,6 +82,7 @@ export interface AdvancedLaboratoryIndexItem {
|
||||
}
|
||||
|
||||
const WORK_IDS: readonly AdvancedLaboratoryWorkId[] = [
|
||||
"m47-reference-graph-shadow",
|
||||
"m4-replay-threat",
|
||||
"l3-pointpillars-visual-audit",
|
||||
"l31-pointpillars-ravnoves",
|
||||
@@ -116,6 +119,7 @@ const WORK_IDS: readonly AdvancedLaboratoryWorkId[] = [
|
||||
];
|
||||
|
||||
const RESULT_PREFIX: Readonly<Record<AdvancedLaboratoryWorkId, string>> = {
|
||||
"m47-reference-graph-shadow": "m47-reference-graph-lab",
|
||||
"m4-replay-threat": "m4-threat-replay",
|
||||
"l3-pointpillars-visual-audit": "l3-pointpillars-visual-audit",
|
||||
"l31-pointpillars-ravnoves": "l31-pointpillars-ravnoves",
|
||||
@@ -159,6 +163,7 @@ export function isAdvancedLaboratoryWorkId(
|
||||
|
||||
export function emptyAdvancedLaboratoryResults(): AdvancedLaboratoryResults {
|
||||
return {
|
||||
m47Graph: null,
|
||||
m4Threat: null,
|
||||
l3: null,
|
||||
l31: null,
|
||||
@@ -283,7 +288,8 @@ export function advancedLaboratoryResultAvailable(
|
||||
workId: AdvancedLaboratoryWorkId,
|
||||
results: AdvancedLaboratoryResults,
|
||||
): boolean {
|
||||
return workId === "m4-replay-threat" ? results.m4Threat !== null
|
||||
return workId === "m47-reference-graph-shadow" ? results.m47Graph !== null
|
||||
: workId === "m4-replay-threat" ? results.m4Threat !== null
|
||||
: workId === "l3-pointpillars-visual-audit" ? results.l3 !== null
|
||||
: workId === "l31-pointpillars-ravnoves" ? results.l31 !== null
|
||||
: workId === "l32-pointpillars-camera-review" ? results.l32 !== null
|
||||
@@ -323,13 +329,24 @@ export async function fetchAdvancedLaboratoryResult(
|
||||
{
|
||||
fetcher = fetch,
|
||||
signal,
|
||||
resultId,
|
||||
}: {
|
||||
fetcher?: LaboratoryFetch;
|
||||
signal?: AbortSignal;
|
||||
resultId?: string;
|
||||
} = {},
|
||||
): Promise<AdvancedLaboratoryResults> {
|
||||
const results = emptyAdvancedLaboratoryResults();
|
||||
if (workId === "m4-replay-threat") {
|
||||
if (workId === "m47-reference-graph-shadow") {
|
||||
if (!resultId) {
|
||||
throw new AdvancedLaboratoryContractError("M4.7 LAB identity не выбрана.");
|
||||
}
|
||||
results.m47Graph = await fetchM47ReferenceGraphLab({
|
||||
resultId,
|
||||
fetcher,
|
||||
signal,
|
||||
});
|
||||
} else if (workId === "m4-replay-threat") {
|
||||
results.m4Threat = await fetchM4ThreatReplayResult({ fetcher, signal });
|
||||
} else if (workId === "l3-pointpillars-visual-audit") {
|
||||
results.l3 = await fetchL3PointPillarsVisualAudit({ fetcher, signal });
|
||||
|
||||
@@ -33,8 +33,10 @@ import type { E46IGroundingDinoFullReplayResult } from "./e46iGroundingDinoFullR
|
||||
import type { E46JRawFisheyeRealtimeResult } from "./e46jRawFisheyeRealtime";
|
||||
import type { E47SemanticSlamResult } from "./e47SemanticSlam";
|
||||
import type { M4ThreatReplayResult } from "./m4ReplayThreat";
|
||||
import type { M47ReferenceGraphLabResult } from "./m47ReferenceGraph";
|
||||
|
||||
export interface AdvancedLaboratoryResults {
|
||||
m47Graph: M47ReferenceGraphLabResult | null;
|
||||
m4Threat: M4ThreatReplayResult | null;
|
||||
l3: L3PointPillarsVisualAuditResult | null;
|
||||
l31: L31PointPillarsRavnovesResult | null;
|
||||
|
||||
@@ -967,7 +967,7 @@ export async function fetchAdvancedLaboratoryResults({
|
||||
const e39 = settledCatalogValue(settled[7]);
|
||||
const e40 = settledCatalogValue(settled[8]);
|
||||
return {
|
||||
m4Threat: null,
|
||||
m47Graph: null, m4Threat: null,
|
||||
l3: null, l31: null,
|
||||
l32: null,
|
||||
l33: null,
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
import {
|
||||
fetchLaboratoryEvidenceReport,
|
||||
type JsonValue,
|
||||
} from "./evidenceReport";
|
||||
import {
|
||||
fetchM4ThreatReplayResult,
|
||||
type M4ThreatReplayResult,
|
||||
} from "./m4ReplayThreat";
|
||||
|
||||
export interface M47ReferenceGraphLabResult {
|
||||
resultId: string;
|
||||
createdAtUtc: string;
|
||||
graphResultId: string;
|
||||
linkedVisualResultId: string;
|
||||
graphId: "reference-perception-graph/v2";
|
||||
worker: {
|
||||
id: "worker-006";
|
||||
node: string;
|
||||
artifactSha256: string;
|
||||
codeRevision: string;
|
||||
elapsedSeconds: number;
|
||||
};
|
||||
frames: {
|
||||
expected: 4489;
|
||||
admitted: 4489;
|
||||
delivered: 4489;
|
||||
};
|
||||
parityMismatchCounts: Readonly<Record<
|
||||
| "source_binding"
|
||||
| "current"
|
||||
| "rolling_retained"
|
||||
| "held"
|
||||
| "expired"
|
||||
| "camera_uncertainty"
|
||||
| "threat_assessments",
|
||||
0
|
||||
>>;
|
||||
queueHighWatermarks: Readonly<Record<
|
||||
"detector" | "geometry" | "temporal" | "rolling" | "threat",
|
||||
number
|
||||
>>;
|
||||
limitations: readonly string[];
|
||||
visual: M4ThreatReplayResult;
|
||||
}
|
||||
|
||||
type LaboratoryFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
||||
|
||||
class M47ReferenceGraphContractError extends Error {}
|
||||
|
||||
function object(value: unknown, label: string): Record<string, unknown> {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
throw new M47ReferenceGraphContractError(`${label}: ожидался объект.`);
|
||||
}
|
||||
return value as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function text(value: unknown, label: string): string {
|
||||
if (typeof value !== "string" || !value.trim()) {
|
||||
throw new M47ReferenceGraphContractError(`${label}: ожидалась строка.`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function exact<T extends string | number | boolean>(
|
||||
value: unknown,
|
||||
expected: T,
|
||||
label: string,
|
||||
): T {
|
||||
if (value !== expected) {
|
||||
throw new M47ReferenceGraphContractError(`${label}: нарушен контракт.`);
|
||||
}
|
||||
return expected;
|
||||
}
|
||||
|
||||
function finite(value: unknown, label: string): number {
|
||||
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
||||
throw new M47ReferenceGraphContractError(`${label}: ожидалось неотрицательное число.`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function digest(value: unknown, label: string, length: 40 | 64): string {
|
||||
const parsed = text(value, label);
|
||||
if (!new RegExp(`^[a-f0-9]{${length}}$`).test(parsed)) {
|
||||
throw new M47ReferenceGraphContractError(`${label}: нарушена digest-идентичность.`);
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function zeroParity(value: unknown): M47ReferenceGraphLabResult["parityMismatchCounts"] {
|
||||
const document = object(value, "M4.7 parity");
|
||||
const keys = [
|
||||
"source_binding",
|
||||
"current",
|
||||
"rolling_retained",
|
||||
"held",
|
||||
"expired",
|
||||
"camera_uncertainty",
|
||||
"threat_assessments",
|
||||
] as const;
|
||||
if (
|
||||
Object.keys(document).length !== keys.length
|
||||
|| keys.some((key) => document[key] !== 0)
|
||||
) {
|
||||
throw new M47ReferenceGraphContractError("M4.7 parity: обнаружено расхождение.");
|
||||
}
|
||||
return Object.fromEntries(keys.map((key) => [key, 0])) as unknown as (
|
||||
M47ReferenceGraphLabResult["parityMismatchCounts"]
|
||||
);
|
||||
}
|
||||
|
||||
function queueWatermarks(
|
||||
value: unknown,
|
||||
): M47ReferenceGraphLabResult["queueHighWatermarks"] {
|
||||
const document = object(value, "M4.7 queue watermarks");
|
||||
const keys = ["detector", "geometry", "temporal", "rolling", "threat"] as const;
|
||||
if (Object.keys(document).length !== keys.length) {
|
||||
throw new M47ReferenceGraphContractError("M4.7 queues: состав стадий изменился.");
|
||||
}
|
||||
return Object.fromEntries(keys.map((key) => {
|
||||
const watermark = finite(document[key], `M4.7 queue ${key}`);
|
||||
if (!Number.isInteger(watermark) || watermark > 2) {
|
||||
throw new M47ReferenceGraphContractError(`M4.7 queue ${key}: нарушена граница.`);
|
||||
}
|
||||
return [key, watermark];
|
||||
})) as unknown as M47ReferenceGraphLabResult["queueHighWatermarks"];
|
||||
}
|
||||
|
||||
function stringArray(value: JsonValue | undefined, label: string): readonly string[] {
|
||||
if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || !item.trim())) {
|
||||
throw new M47ReferenceGraphContractError(`${label}: ожидался список строк.`);
|
||||
}
|
||||
return value as string[];
|
||||
}
|
||||
|
||||
export async function fetchM47ReferenceGraphLab({
|
||||
resultId,
|
||||
fetcher = fetch,
|
||||
signal,
|
||||
}: {
|
||||
resultId: string;
|
||||
fetcher?: LaboratoryFetch;
|
||||
signal?: AbortSignal;
|
||||
}): Promise<M47ReferenceGraphLabResult> {
|
||||
if (!/^m47-reference-graph-lab-[a-f0-9]{64}$/.test(resultId)) {
|
||||
throw new M47ReferenceGraphContractError("M4.7 result id: нарушена идентичность.");
|
||||
}
|
||||
const evidence = await fetchLaboratoryEvidenceReport({
|
||||
workId: "m47-reference-graph-shadow",
|
||||
resultId,
|
||||
fetcher,
|
||||
signal,
|
||||
});
|
||||
const report = evidence.rawReport;
|
||||
exact(
|
||||
report.schema_version,
|
||||
"missioncore.reference-perception-graph-lab-report/v1",
|
||||
"M4.7 report schema",
|
||||
);
|
||||
exact(report.result_id, resultId, "M4.7 report result");
|
||||
const source = object(report.source, "M4.7 source");
|
||||
const method = object(report.method, "M4.7 method");
|
||||
const execution = object(report.execution, "M4.7 execution");
|
||||
const metrics = object(report.metrics, "M4.7 metrics");
|
||||
const frames = object(metrics.frames, "M4.7 frames");
|
||||
const acceptance = object(report.acceptance, "M4.7 acceptance");
|
||||
const authority = object(report.authority, "M4.7 authority");
|
||||
const visualEvidence = object(report.visual_evidence, "M4.7 visual evidence");
|
||||
exact(acceptance.accepted, true, "M4.7 acceptance");
|
||||
exact(authority.commands_enabled, false, "M4.7 commands");
|
||||
exact(authority.actuation_allowed, false, "M4.7 actuation");
|
||||
exact(authority.navigation_or_safety_accepted, false, "M4.7 safety authority");
|
||||
exact(visualEvidence.shared_recorded_clock, true, "M4.7 recorded clock");
|
||||
exact(
|
||||
visualEvidence.video_camera_3d_plan_available,
|
||||
true,
|
||||
"M4.7 visual evidence",
|
||||
);
|
||||
const linkedVisualResultId = text(
|
||||
visualEvidence.linked_result_id,
|
||||
"M4.7 linked visual result",
|
||||
);
|
||||
if (!/^m4-threat-replay-[a-f0-9]{64}$/.test(linkedVisualResultId)) {
|
||||
throw new M47ReferenceGraphContractError("M4.7 visual binding: нарушена идентичность.");
|
||||
}
|
||||
const visual = await fetchM4ThreatReplayResult({
|
||||
resultId: linkedVisualResultId,
|
||||
fetcher,
|
||||
signal,
|
||||
});
|
||||
if (!visual || visual.resultId !== linkedVisualResultId) {
|
||||
throw new M47ReferenceGraphContractError(
|
||||
"M4.7 visual binding: сервер вернул другой replay result.",
|
||||
);
|
||||
}
|
||||
const graphResultId = text(source.graph_result_id, "M4.7 graph result");
|
||||
if (!/^m47-reference-graph-[a-f0-9]{64}$/.test(graphResultId)) {
|
||||
throw new M47ReferenceGraphContractError("M4.7 graph result: нарушена идентичность.");
|
||||
}
|
||||
return {
|
||||
resultId,
|
||||
createdAtUtc: text(evidence.createdAtUtc, "M4.7 created at"),
|
||||
graphResultId,
|
||||
linkedVisualResultId,
|
||||
graphId: exact(method.graph_id, "reference-perception-graph/v2", "M4.7 graph id"),
|
||||
worker: {
|
||||
id: exact(execution.worker_id, "worker-006", "M4.7 worker"),
|
||||
node: text(execution.worker_node, "M4.7 worker node"),
|
||||
artifactSha256: digest(execution.artifact_sha256, "M4.7 artifact", 64),
|
||||
codeRevision: digest(execution.code_revision, "M4.7 revision", 40),
|
||||
elapsedSeconds: finite(execution.elapsed_seconds, "M4.7 elapsed"),
|
||||
},
|
||||
frames: {
|
||||
expected: exact(frames.expected, 4489, "M4.7 expected frames"),
|
||||
admitted: exact(frames.admitted, 4489, "M4.7 admitted frames"),
|
||||
delivered: exact(frames.delivered, 4489, "M4.7 delivered frames"),
|
||||
},
|
||||
parityMismatchCounts: zeroParity(metrics.parity_mismatch_counts),
|
||||
queueHighWatermarks: queueWatermarks(metrics.queue_high_watermarks),
|
||||
limitations: stringArray(report.limitations, "M4.7 limitations"),
|
||||
visual,
|
||||
};
|
||||
}
|
||||
@@ -315,22 +315,59 @@ function parseMetricVisual(value: unknown): M4ThreatMetricVisual {
|
||||
}
|
||||
|
||||
export async function fetchM4ThreatReplayResult({
|
||||
resultId: requestedResultId,
|
||||
fetcher = fetch,
|
||||
signal,
|
||||
}: {
|
||||
resultId?: string;
|
||||
fetcher?: LaboratoryFetch;
|
||||
signal?: AbortSignal;
|
||||
} = {}): Promise<M4ThreatReplayResult | null> {
|
||||
const response = await fetcher("/api/v1/laboratory/m4-threat/results?limit=1", {
|
||||
headers: { Accept: "application/json" },
|
||||
signal,
|
||||
});
|
||||
if (
|
||||
requestedResultId !== undefined
|
||||
&& !/^m4-threat-replay-[a-f0-9]{64}$/.test(requestedResultId)
|
||||
) {
|
||||
throw new M4ThreatContractError("M4.6 requested result id: нарушена идентичность.");
|
||||
}
|
||||
let response = await fetcher(
|
||||
requestedResultId === undefined
|
||||
? "/api/v1/laboratory/m4-threat/results?limit=1"
|
||||
: `/api/v1/laboratory/m4-threat/results/${requestedResultId}`,
|
||||
{ headers: { Accept: "application/json" }, signal },
|
||||
);
|
||||
// Rolling deployments may briefly pair the new UI with the previous read-only
|
||||
// API. Preserve exact-result binding by selecting the requested result from
|
||||
// the catalog instead of silently falling back to whatever happens to be latest.
|
||||
let catalogFallback = false;
|
||||
if (requestedResultId !== undefined && response.status === 404) {
|
||||
response = await fetcher("/api/v1/laboratory/m4-threat/results?limit=10", {
|
||||
headers: { Accept: "application/json" },
|
||||
signal,
|
||||
});
|
||||
catalogFallback = true;
|
||||
}
|
||||
if (!response.ok) throw new M4ThreatContractError(`M4.6 LAB недоступен: HTTP ${response.status}.`);
|
||||
const catalog = object(await response.json(), "M4.6 catalog");
|
||||
exact(catalog.schema_version, "missioncore.m4-threat-replay-catalog/v1", "M4.6 catalog schema");
|
||||
const items = array(catalog.items, "M4.6 results");
|
||||
if (!items.length) return null;
|
||||
const item = object(items[0], "M4.6 result");
|
||||
const payload = await response.json();
|
||||
let item: Record<string, unknown>;
|
||||
if (requestedResultId === undefined || catalogFallback) {
|
||||
const catalog = object(payload, "M4.6 catalog");
|
||||
exact(catalog.schema_version, "missioncore.m4-threat-replay-catalog/v1", "M4.6 catalog schema");
|
||||
const items = array(catalog.items, "M4.6 results");
|
||||
if (!items.length) return null;
|
||||
const selected = requestedResultId === undefined
|
||||
? items[0]
|
||||
: items.find((candidate) => (
|
||||
typeof candidate === "object"
|
||||
&& candidate !== null
|
||||
&& (candidate as Record<string, unknown>).result_id === requestedResultId
|
||||
));
|
||||
if (selected === undefined) {
|
||||
throw new M4ThreatContractError("M4.6 requested result: точный результат отсутствует в каталоге.");
|
||||
}
|
||||
item = object(selected, "M4.6 result");
|
||||
} else {
|
||||
item = object(payload, "M4.6 result");
|
||||
}
|
||||
exact(item.schema_version, "missioncore.m4-threat-replay-view/v1", "M4.6 view schema");
|
||||
exact(item.accepted, true, "M4.6 acceptance");
|
||||
exact(item.authority, "replay-simulated", "M4.6 authority");
|
||||
@@ -349,8 +386,12 @@ export async function fetchM4ThreatReplayResult({
|
||||
const configuration = object(item.configuration, "M4.6 configuration");
|
||||
const configuredBodyFrame = object(configuration.body_frame, "M4.6 configured body frame");
|
||||
const sourceResultIds = object(item.source_result_ids, "M4.6 sources");
|
||||
const parsedResultId = resultId(item.result_id);
|
||||
if (requestedResultId !== undefined) {
|
||||
exact(parsedResultId, requestedResultId, "M4.6 requested result");
|
||||
}
|
||||
return {
|
||||
resultId: resultId(item.result_id),
|
||||
resultId: parsedResultId,
|
||||
createdAtUtc: text(item.created_at_utc, "M4.6 created"),
|
||||
profileId: text(item.profile_id, "M4.6 profile"),
|
||||
rigProfileId: text(item.rig_profile_id, "M4.6 rig"),
|
||||
|
||||
@@ -41,6 +41,7 @@ import { E46IGroundingDinoFullReplayResultView } from "./E46IGroundingDinoFullRe
|
||||
import { E46JRawFisheyeRealtimeResultView } from "./E46JRawFisheyeRealtimeResult";
|
||||
import { E47SemanticSlamResultView } from "./E47SemanticSlamResult";
|
||||
import { M4ReplayThreatResultView } from "./M4ReplayThreatResult";
|
||||
import { M47ReferenceGraphResultView } from "./M47ReferenceGraphResult";
|
||||
|
||||
export { isAdvancedLaboratoryWorkId };
|
||||
export type { AdvancedLaboratoryWorkId };
|
||||
@@ -83,6 +84,9 @@ export function AdvancedLaboratoryResult({
|
||||
failedSessionId: string | null;
|
||||
replayError: string | null;
|
||||
}) {
|
||||
if (workId === "m47-reference-graph-shadow" && results.m47Graph) {
|
||||
return <M47ReferenceGraphResultView rigLabel={rigLabel} result={results.m47Graph} />;
|
||||
}
|
||||
if (workId === "m4-replay-threat" && results.m4Threat) {
|
||||
return <M4ReplayThreatResultView rigLabel={rigLabel} result={results.m4Threat} />;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
import {
|
||||
LaboratoryEvidence,
|
||||
LaboratoryResultSummary,
|
||||
LaboratorySummary,
|
||||
LaboratoryWorkTemplate,
|
||||
} from "../../components/laboratory/LaboratoryPresentation";
|
||||
import type { M47ReferenceGraphLabResult } from "../../core/laboratory/m47ReferenceGraph";
|
||||
import { formatNumber } from "../../presentation";
|
||||
import { M4ReplayThreatVisual } from "./M4ReplayThreatVisual";
|
||||
|
||||
export function M47ReferenceGraphResultView({
|
||||
rigLabel,
|
||||
result,
|
||||
}: {
|
||||
rigLabel: string;
|
||||
result: M47ReferenceGraphLabResult;
|
||||
}) {
|
||||
const parityDimensions = Object.keys(result.parityMismatchCounts).length;
|
||||
const maximumQueue = Math.max(...Object.values(result.queueHighWatermarks));
|
||||
return (
|
||||
<LaboratoryWorkTemplate
|
||||
summary={(
|
||||
<LaboratorySummary
|
||||
title="M4.7 · canonical reference graph shadow"
|
||||
description="Полный source→detector→geometry→temporal/motion→rolling→threat граф выполнен на Worker 006 и побайтно сопоставлен с принятыми M4.5R/M4.6 ledgers. Ни один live-контур и ни одна команда не участвовали."
|
||||
status="4489/4489 · lossless graph parity passed"
|
||||
statusTone="success"
|
||||
facts={[
|
||||
{
|
||||
label: "Конфигурация",
|
||||
value: `${rigLabel} · RIGHT camera + LiDAR · recorded replay`,
|
||||
},
|
||||
{
|
||||
label: "Исполнение",
|
||||
value: `${result.worker.id} / ${result.worker.node} · ${formatNumber(result.worker.elapsedSeconds, 1)} с`,
|
||||
},
|
||||
{
|
||||
label: "Artifact",
|
||||
value: `${result.worker.artifactSha256.slice(0, 12)}… · commit ${result.worker.codeRevision.slice(0, 8)}`,
|
||||
},
|
||||
{
|
||||
label: "Graph accounting",
|
||||
value: `${result.frames.admitted}/${result.frames.expected} admitted · ${result.frames.delivered} delivered`,
|
||||
},
|
||||
{
|
||||
label: "Визуал",
|
||||
value: "VIDEO/CAMERA/3D/PLAN · единый recorded clock · exact M4.6 ledger binding",
|
||||
},
|
||||
]}
|
||||
brief={{
|
||||
question: "Сохраняет ли собранный канонический perception graph уже принятые результаты каждого кадра без потерь, перестановочных артефактов и скрытого backpressure?",
|
||||
approach: "Worker 006 выполнил все 4489 кадров в lossless replay через изолированный Triton и временный graph container. Результат связан с точным artifact SHA, commit, container identity и двумя принятыми parity-ledgers. Визуальный слой использует тот же threat/temporal payload, поэтому его можно проверять на общем таймлайне.",
|
||||
principalResult: `${parityDimensions}/${parityDimensions} parity dimensions имеют ноль расхождений; максимальная заполненность каждой bounded queue — ${maximumQueue}. Все ${result.frames.delivered} кадров завершились delivered.`,
|
||||
limitation: "Это принятие сборки графа на recorded replay. Оно не доказывает независимую object-level точность, физический live, навигационную безопасность или готовность выдавать команды моторам.",
|
||||
}}
|
||||
method={{
|
||||
completeness: "complete",
|
||||
executionClass: "hybrid",
|
||||
pipelineId: result.graphId,
|
||||
components: [
|
||||
{
|
||||
kind: "source",
|
||||
name: result.graphResultId,
|
||||
version: "lossless-replay",
|
||||
role: "immutable 4489-frame canonical graph output",
|
||||
identitySha256: result.graphResultId.split("-").at(-1) ?? null,
|
||||
},
|
||||
{
|
||||
kind: "source",
|
||||
name: result.linkedVisualResultId,
|
||||
version: "exact ledger parity",
|
||||
role: "synchronized VIDEO/CAMERA/3D/PLAN evidence",
|
||||
identitySha256: result.linkedVisualResultId.split("-").at(-1) ?? null,
|
||||
},
|
||||
{
|
||||
kind: "algorithm",
|
||||
name: "canonical reference perception graph",
|
||||
version: result.graphId,
|
||||
role: "source-neutral perception assembly without command authority",
|
||||
identitySha256: result.worker.artifactSha256,
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
evidence={(
|
||||
<LaboratoryEvidence
|
||||
eyebrow="M4.7 VISUAL EVIDENCE · VIDEO / CAMERA / 3D / PLAN"
|
||||
title="Синхронная проверка accepted graph payload на общем recorded timeline"
|
||||
kind="diagnostic-model"
|
||||
resizable
|
||||
>
|
||||
<M4ReplayThreatVisual resultId={result.visual.resultId} />
|
||||
</LaboratoryEvidence>
|
||||
)}
|
||||
result={(
|
||||
<LaboratoryResultSummary
|
||||
title="Каноническая сборка графа принята; открыт object-centric gate"
|
||||
status="Зелёный: graph assembly parity · жёлтый: independent object truth ещё отсутствует"
|
||||
statusTone="warning"
|
||||
metrics={[
|
||||
{
|
||||
label: "Replay frames",
|
||||
value: `${result.frames.delivered}/${result.frames.expected}`,
|
||||
hint: `${formatNumber(result.worker.elapsedSeconds, 1)} с на Worker 006`,
|
||||
},
|
||||
{
|
||||
label: "Parity",
|
||||
value: `${parityDimensions}/${parityDimensions} · 0 mismatch`,
|
||||
hint: "source/current/rolling/held/expired/camera/threat",
|
||||
},
|
||||
{
|
||||
label: "Queue high-water",
|
||||
value: String(maximumQueue),
|
||||
hint: "bounded on every graph stage",
|
||||
},
|
||||
{
|
||||
label: "Authority",
|
||||
value: "OFF",
|
||||
hint: "commands=false · actuation=false",
|
||||
},
|
||||
]}
|
||||
conclusion={{
|
||||
proved: "Канонический граф полностью обработал immutable RAVNOVES00 и сохранил уже принятые temporal/threat решения для каждого кадра. Runtime provenance от Worker 006 до LAB результата замкнут хэшами.",
|
||||
notProved: "Не доказаны независимая полнота/точность детекции объектов, live realtime на машине, измеренная геометрия корпуса, collision safety, планирование движения и управление моторами.",
|
||||
decision: "Считать M4.7 graph assembly завершённым и переходить к следующей лаборатории: независимому object-centric detection gate с визуальным разбором miss/duplicate/geometry ошибок.",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -63,6 +63,13 @@ interface KnownWorkDefinition {
|
||||
const rig = (rigLabel: string): string => rigLabel.trim() || "Сенсорный риг";
|
||||
|
||||
const KNOWN_WORKS: Readonly<Record<Exclude<LaboratoryWorkId, `session:${string}`>, KnownWorkDefinition>> = {
|
||||
"m47-reference-graph-shadow": {
|
||||
profileId: "rig-dual-evidence-virtual-corridor-v1",
|
||||
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · Camera + LiDAR dual evidence`,
|
||||
experimentId: "m47-reference-perception-graph-shadow",
|
||||
experimentName: "RAVNOVES00 canonical reference graph",
|
||||
variantName: "M4.7 · Worker 006 · lossless graph parity",
|
||||
},
|
||||
"m4-replay-threat": {
|
||||
profileId: "rig-dual-evidence-virtual-corridor-v1",
|
||||
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · Camera + LiDAR dual evidence`,
|
||||
|
||||
@@ -18,6 +18,7 @@ function mergeResults(
|
||||
next: AdvancedLaboratoryResults,
|
||||
): AdvancedLaboratoryResults {
|
||||
return {
|
||||
m47Graph: next.m47Graph ?? current.m47Graph,
|
||||
m4Threat: next.m4Threat ?? current.m4Threat,
|
||||
l3: next.l3 ?? current.l3,
|
||||
l31: next.l31 ?? current.l31,
|
||||
@@ -109,12 +110,15 @@ export function useAdvancedLaboratoryCatalog({
|
||||
!isAdvancedLaboratoryWorkId(selectedWorkId)
|
||||
|| advancedLaboratoryResultAvailable(selectedWorkId, results)
|
||||
) return;
|
||||
const indexedResultId = index.find((item) => item.workId === selectedWorkId)?.resultId;
|
||||
if (selectedWorkId === "m47-reference-graph-shadow" && !indexedResultId) return;
|
||||
const controller = new AbortController();
|
||||
setLoadingWorkId(selectedWorkId);
|
||||
setFailedWorkId(null);
|
||||
setResultError(null);
|
||||
void fetchAdvancedLaboratoryResult(selectedWorkId, {
|
||||
signal: controller.signal,
|
||||
resultId: indexedResultId,
|
||||
}).then(async (next) => {
|
||||
if (controller.signal.aborted) return;
|
||||
setResults((current) => mergeResults(current, next));
|
||||
@@ -128,7 +132,7 @@ export function useAdvancedLaboratoryCatalog({
|
||||
if (!controller.signal.aborted) setLoadingWorkId(null);
|
||||
});
|
||||
return () => controller.abort();
|
||||
}, [results, selectedWorkId]);
|
||||
}, [index, results, selectedWorkId]);
|
||||
|
||||
return {
|
||||
index,
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { after, before, test } from "node:test";
|
||||
|
||||
import { createServer } from "vite";
|
||||
|
||||
let server;
|
||||
let fetchM47ReferenceGraphLab;
|
||||
|
||||
const resultId = `m47-reference-graph-lab-${"a".repeat(64)}`;
|
||||
const graphResultId = `m47-reference-graph-${"b".repeat(64)}`;
|
||||
const visualResultId = `m4-threat-replay-${"c".repeat(64)}`;
|
||||
|
||||
before(async () => {
|
||||
server = await createServer({
|
||||
appType: "custom",
|
||||
logLevel: "silent",
|
||||
server: { middlewareMode: true },
|
||||
});
|
||||
({ fetchM47ReferenceGraphLab } = await server.ssrLoadModule(
|
||||
"/src/core/laboratory/m47ReferenceGraph.ts",
|
||||
));
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await server?.close();
|
||||
});
|
||||
|
||||
function m4Result() {
|
||||
return {
|
||||
schema_version: "missioncore.m4-threat-replay-view/v1",
|
||||
result_id: visualResultId,
|
||||
created_at_utc: "2026-08-05T15:36:01.553Z",
|
||||
status: "accepted",
|
||||
profile_id: "m4-ravnoves00-virtual-corridor/v3",
|
||||
rig_profile_id: "virtual-base-footprint-1000x600/v3",
|
||||
corridor_profile_id: "ravnoves00-forward-corridor-8m/v3",
|
||||
source_result_ids: {
|
||||
detector: `m4-detector-replay-${"d".repeat(64)}`,
|
||||
geometry: `m4-geometry-replay-${"e".repeat(64)}`,
|
||||
temporal: `m4-temporal-replay-${"f".repeat(64)}`,
|
||||
},
|
||||
metrics: {
|
||||
decisions: { threat: 7606, "not-threat": 10769, unknown: 128878 },
|
||||
evidence: {
|
||||
"camera-only": 10158,
|
||||
"current-metric": 28081,
|
||||
"rolling-map-retained": 70989,
|
||||
"stale-or-held": 38025,
|
||||
},
|
||||
fixtures: { critical: 5, critical_false_not_threat: 0, passed: 10, total: 10 },
|
||||
runtime: {
|
||||
frames_per_second: 175.0,
|
||||
provider_latency_p50_ms: 2.5,
|
||||
provider_latency_p95_ms: 7.2,
|
||||
provider_latency_max_ms: 26.6,
|
||||
},
|
||||
body_frame: {
|
||||
available: 3928,
|
||||
qualified: 3861,
|
||||
rejected: 67,
|
||||
camera_forward_alignment_deg: { p95: 8.439, maximum: 24.252 },
|
||||
},
|
||||
reason_counts: { "geometry-only-evidence": 87550 },
|
||||
},
|
||||
configuration: {
|
||||
virtual_body_m: [1, 0.6],
|
||||
nominal_sensor_height_m: 1.25,
|
||||
forward_corridor_m: 8,
|
||||
prediction_horizon_seconds: 5,
|
||||
body_frame: {
|
||||
origin: "local-surface-vertical-projection",
|
||||
up: "vendor-slam-map-gravity-axis",
|
||||
forward: "smoothed-slam-trajectory-validated-by-camera-axis",
|
||||
},
|
||||
},
|
||||
limitations: ["replay only"],
|
||||
accepted: true,
|
||||
authority: "replay-simulated",
|
||||
physical_collision_accepted: false,
|
||||
actuation_allowed: false,
|
||||
};
|
||||
}
|
||||
|
||||
function evidenceReport() {
|
||||
const mismatch = {
|
||||
source_binding: 0,
|
||||
current: 0,
|
||||
rolling_retained: 0,
|
||||
held: 0,
|
||||
expired: 0,
|
||||
camera_uncertainty: 0,
|
||||
threat_assessments: 0,
|
||||
};
|
||||
const raw = {
|
||||
schema_version: "missioncore.reference-perception-graph-lab-report/v1",
|
||||
result_id: resultId,
|
||||
source: { graph_result_id: graphResultId },
|
||||
method: { graph_id: "reference-perception-graph/v2" },
|
||||
execution: {
|
||||
worker_id: "worker-006",
|
||||
worker_node: "DESKTOP-OPJ8J04",
|
||||
artifact_sha256: "1".repeat(64),
|
||||
code_revision: "2".repeat(40),
|
||||
elapsed_seconds: 198.126,
|
||||
},
|
||||
metrics: {
|
||||
frames: { expected: 4489, admitted: 4489, delivered: 4489 },
|
||||
parity_mismatch_counts: mismatch,
|
||||
queue_high_watermarks: {
|
||||
detector: 2,
|
||||
geometry: 2,
|
||||
temporal: 2,
|
||||
rolling: 2,
|
||||
threat: 2,
|
||||
},
|
||||
},
|
||||
acceptance: { accepted: true },
|
||||
authority: {
|
||||
commands_enabled: false,
|
||||
actuation_allowed: false,
|
||||
navigation_or_safety_accepted: false,
|
||||
},
|
||||
visual_evidence: {
|
||||
linked_result_id: visualResultId,
|
||||
shared_recorded_clock: true,
|
||||
video_camera_3d_plan_available: true,
|
||||
},
|
||||
limitations: ["recorded replay only"],
|
||||
};
|
||||
return {
|
||||
schema_version: "missioncore.laboratory-evidence-report/v1",
|
||||
work_id: "m47-reference-graph-shadow",
|
||||
result_id: resultId,
|
||||
created_at_utc: "2026-08-23T18:00:16.061Z",
|
||||
access: "read-only",
|
||||
proof: {
|
||||
document_schema_version: "missioncore.reference-perception-graph-lab/v1",
|
||||
document_sha256: "3".repeat(64),
|
||||
identity_sha256: "a".repeat(64),
|
||||
report_schema_version: raw.schema_version,
|
||||
report_sha256: "4".repeat(64),
|
||||
artifact_count: 6,
|
||||
verified_artifact_count: 6,
|
||||
},
|
||||
completeness: { identity: "recorded" },
|
||||
identity: { authority: raw.authority },
|
||||
source: raw.source,
|
||||
configuration: null,
|
||||
method: raw.method,
|
||||
execution: raw.execution,
|
||||
resources: null,
|
||||
metrics: raw.metrics,
|
||||
gates: raw.acceptance,
|
||||
decision: null,
|
||||
limitations: raw.limitations,
|
||||
authority: raw.authority,
|
||||
artifacts: [],
|
||||
visual_evidence: {},
|
||||
raw_report: raw,
|
||||
};
|
||||
}
|
||||
|
||||
test("M4.7 binds exact Worker graph proof to the synchronized M4.6 visual", async () => {
|
||||
const requests = [];
|
||||
const result = await fetchM47ReferenceGraphLab({
|
||||
resultId,
|
||||
fetcher: async (input) => {
|
||||
const path = String(input);
|
||||
requests.push(path);
|
||||
return new Response(JSON.stringify(
|
||||
path.includes("/evidence-reports/") ? evidenceReport() : m4Result(),
|
||||
), { status: 200 });
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(result.graphResultId, graphResultId);
|
||||
assert.equal(result.visual.resultId, visualResultId);
|
||||
assert.equal(result.worker.id, "worker-006");
|
||||
assert.equal(result.frames.delivered, 4489);
|
||||
assert.deepEqual(Object.values(result.parityMismatchCounts), Array(7).fill(0));
|
||||
assert.equal(Math.max(...Object.values(result.queueHighWatermarks)), 2);
|
||||
assert.deepEqual(requests, [
|
||||
`/api/v1/laboratory/evidence-reports/m47-reference-graph-shadow/${resultId}`,
|
||||
`/api/v1/laboratory/m4-threat/results/${visualResultId}`,
|
||||
]);
|
||||
});
|
||||
|
||||
test("M4.7 keeps exact visual binding during a rolling API deployment", async () => {
|
||||
const requests = [];
|
||||
const result = await fetchM47ReferenceGraphLab({
|
||||
resultId,
|
||||
fetcher: async (input) => {
|
||||
const path = String(input);
|
||||
requests.push(path);
|
||||
if (path.includes("/evidence-reports/")) {
|
||||
return new Response(JSON.stringify(evidenceReport()), { status: 200 });
|
||||
}
|
||||
if (path.endsWith(`/${visualResultId}`)) {
|
||||
return new Response(null, { status: 404 });
|
||||
}
|
||||
return new Response(JSON.stringify({
|
||||
schema_version: "missioncore.m4-threat-replay-catalog/v1",
|
||||
items: [m4Result()],
|
||||
}), { status: 200 });
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(result.visual.resultId, visualResultId);
|
||||
assert.deepEqual(requests, [
|
||||
`/api/v1/laboratory/evidence-reports/m47-reference-graph-shadow/${resultId}`,
|
||||
`/api/v1/laboratory/m4-threat/results/${visualResultId}`,
|
||||
"/api/v1/laboratory/m4-threat/results?limit=10",
|
||||
]);
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"schema_version": "missioncore.laboratory-evidence-definition/v1",
|
||||
"work_id": "m47-reference-graph-shadow",
|
||||
"evidence": {
|
||||
"runtime_relative_root": "m47/reference-graph-labs",
|
||||
"result_id_prefix": "m47-reference-graph-lab",
|
||||
"document_name": "manifest.json",
|
||||
"schema_version": "missioncore.reference-perception-graph-lab/v1"
|
||||
}
|
||||
}
|
||||
@@ -89,6 +89,7 @@
|
||||
}
|
||||
],
|
||||
"legacy_work_ids": [
|
||||
"m47-reference-graph-shadow",
|
||||
"e31-source-binding",
|
||||
"e32-track-geometry",
|
||||
"e34-temporal-layer",
|
||||
|
||||
@@ -1172,7 +1172,7 @@ terminal, report and manifest ledgers; acceptance requires exactly `4,489`
|
||||
delivered frames with zero failed, stale, superseded, rejected or unavailable
|
||||
terminal outcomes.
|
||||
|
||||
The new Worker package uses transition `m47-canonical-graph-shadow-v1`. It is a
|
||||
The first Worker package used transition `m47-canonical-graph-shadow-v1`. It is a
|
||||
separate deterministic artifact and does not relabel the accepted historical
|
||||
`m4-detector-shadow-v1` wheel. Its PowerShell runner verifies release files,
|
||||
inputs, dependency trees, disk reserve, pinned Worker/Triton predecessor and
|
||||
@@ -1182,6 +1182,17 @@ the run and re-verifies the predecessor. Provider readiness and graph readiness
|
||||
are emitted separately. The production builder refuses a dirty worktree so an
|
||||
artifact cannot claim a Git revision which does not contain its wheel.
|
||||
|
||||
The live audit on 2026-08-23 found both pinned historical Mission Core
|
||||
containers present with their exact identities but stopped. M4.7 therefore
|
||||
uses the additive transition `m47-canonical-graph-isolated-shadow-v1`: it does
|
||||
not start, stop or replace either historical container. The runner creates a
|
||||
private, no-public-port Triton candidate from the same digest and a separate
|
||||
one-shot graph candidate, binds the accepted model/evidence read-only, and
|
||||
removes both candidates on every exit path. It records and re-verifies the
|
||||
identity and running state of the historical E15 worker and Triton before
|
||||
accepting the run. This makes Worker 006 usable without touching the stabilized
|
||||
K1/Zarya connection path.
|
||||
|
||||
Local contract, graph, result-sealing, artifact and historical-rollback tests
|
||||
pass. This is implementation evidence only. It does not claim that Worker 006
|
||||
has the pinned local-surface input, that preflight has passed, that the 4,489
|
||||
|
||||
@@ -136,6 +136,10 @@ function Write-Utf8NoBom([string]$Path, [string]$Value) {
|
||||
[IO.File]::WriteAllText($Path, $Value, $encoding)
|
||||
}
|
||||
|
||||
function Test-NoPublishedPorts([object]$Container) {
|
||||
return @($Container.HostConfig.PortBindings.PSObject.Properties).Count -eq 0
|
||||
}
|
||||
|
||||
if ($env:COMPUTERNAME -cne "DESKTOP-OPJ8J04") {
|
||||
throw "M4.7 shadow release is pinned to DESKTOP-OPJ8J04"
|
||||
}
|
||||
@@ -143,11 +147,11 @@ if ($env:COMPUTERNAME -cne "DESKTOP-OPJ8J04") {
|
||||
$release = Resolve-DDirectory $ReleaseRoot "M4.7 release root" $false
|
||||
$payload = Resolve-DDirectory (Join-Path $release "payload") "M4.7 payload" $false
|
||||
$artifact = Assert-FileSha256 $ArtifactPath $ExpectedArtifactSha256 "M4.7 release artifact"
|
||||
$descriptorPath = Join-Path $payload "mission-core-worker-m47-graph-shadow-v2.json"
|
||||
$descriptorPath = Join-Path $payload "mission-core-worker-m47-graph-shadow-v3.json"
|
||||
$descriptor = Get-Content -LiteralPath $descriptorPath -Raw | ConvertFrom-Json
|
||||
if (
|
||||
$descriptor.schema_version -cne "nodedc.mission-core-worker.shadow-release/v2" -or
|
||||
$descriptor.transition -cne "m47-canonical-graph-shadow-v1" -or
|
||||
$descriptor.schema_version -cne "nodedc.mission-core-worker.shadow-release/v3" -or
|
||||
$descriptor.transition -cne "m47-canonical-graph-isolated-shadow-v1" -or
|
||||
$descriptor.component -cne "mission-core-worker" -or
|
||||
$descriptor.host.node -cne $env:COMPUTERNAME -or
|
||||
$descriptor.host.worker_id -cne "worker-006" -or
|
||||
@@ -215,31 +219,52 @@ foreach ($dependency in $descriptor.dependencies) {
|
||||
$imageRef = [string]$descriptor.container.image_ref
|
||||
& docker image inspect $imageRef *> $null
|
||||
Assert-LastExitCode "Pinned M4.7 image inspection"
|
||||
$predecessor = $descriptor.predecessor.durable_worker
|
||||
$tritonExpected = $descriptor.predecessor.triton
|
||||
$durable = Assert-ContainerIdentity (
|
||||
$predecessor.name
|
||||
) $predecessor.container_id $predecessor.image_id $false
|
||||
$triton = Assert-ContainerIdentity (
|
||||
$tritonExpected.name
|
||||
) $tritonExpected.container_id $tritonExpected.image_id $true
|
||||
|
||||
function Get-PreservedContainerSnapshot([object]$Expected, [string]$Label) {
|
||||
$container = Get-ContainerIdentity $Expected.name
|
||||
if (
|
||||
$container.Id -cne $Expected.container_id -or
|
||||
$container.Image -cne $Expected.image_id
|
||||
) {
|
||||
throw "$Label identity changed"
|
||||
}
|
||||
return [pscustomobject]@{
|
||||
Id = [string]$container.Id
|
||||
Image = [string]$container.Image
|
||||
Running = [bool]$container.State.Running
|
||||
}
|
||||
}
|
||||
|
||||
function Assert-PreservedContainerSnapshot(
|
||||
[object]$Expected,
|
||||
[object]$Before,
|
||||
[string]$Label
|
||||
) {
|
||||
$after = Get-PreservedContainerSnapshot $Expected $Label
|
||||
if ($after.Running -ne $Before.Running) {
|
||||
throw "$Label running state changed during isolated shadow"
|
||||
}
|
||||
return $after
|
||||
}
|
||||
|
||||
$durableExpected = $descriptor.predecessor.durable_worker
|
||||
$historicalTritonExpected = $descriptor.predecessor.triton
|
||||
$durableBefore = Get-PreservedContainerSnapshot $durableExpected "Historical durable worker"
|
||||
$historicalTritonBefore = Get-PreservedContainerSnapshot (
|
||||
$historicalTritonExpected
|
||||
) "Historical Triton"
|
||||
$modelRepository = Resolve-DDirectory (
|
||||
[string]$descriptor.container.model_repository_host_path
|
||||
) "M4.7 model repository" $false
|
||||
$output = Resolve-DDirectory $OutputRoot "M4.7 output root" $true
|
||||
$freeBefore = Assert-FreeSpace "preflight"
|
||||
|
||||
if ($PreflightOnly) {
|
||||
Write-Output ("PATCH_ID={0}" -f $descriptor.patch_id)
|
||||
Write-Output ("ARTIFACT_SHA256={0}" -f $ExpectedArtifactSha256)
|
||||
Write-Output ("DURABLE_WORKER_ID={0}" -f $durable.Id)
|
||||
Write-Output ("TRITON_CONTAINER_ID={0}" -f $triton.Id)
|
||||
Write-Output "PROVIDER_READINESS=accepted"
|
||||
Write-Output "GRAPH_READINESS=not-run"
|
||||
Write-Output "PREFLIGHT=accepted"
|
||||
return
|
||||
}
|
||||
|
||||
$candidateName = "ndc-mission-core-m47-graph-shadow"
|
||||
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$candidateName$") {
|
||||
throw "M4.7 candidate container already exists"
|
||||
$candidateName = [string]$descriptor.container.name
|
||||
$tritonCandidateName = [string]$descriptor.container.triton_name
|
||||
foreach ($name in @($candidateName, $tritonCandidateName)) {
|
||||
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$name$") {
|
||||
throw "M4.7 isolated candidate $name already exists"
|
||||
}
|
||||
}
|
||||
$scratch = Join-Path $output (".runtime-{0}" -f $descriptor.patch_id)
|
||||
if (Test-Path -LiteralPath $scratch) {
|
||||
@@ -251,10 +276,40 @@ $runtimeIdentityPath = Join-Path $scratch "runtime-identity.json"
|
||||
$dockerPayload = Convert-ToDockerPath $payload
|
||||
$dockerOutput = Convert-ToDockerPath $output
|
||||
$dockerScratch = Convert-ToDockerPath $scratch
|
||||
$dockerArguments = @(
|
||||
$dockerModelRepository = Convert-ToDockerPath $modelRepository
|
||||
$tritonArguments = @(
|
||||
"create",
|
||||
"--name", $tritonCandidateName,
|
||||
"--read-only",
|
||||
"--security-opt", "no-new-privileges:true",
|
||||
"--cap-drop", "ALL",
|
||||
"--pids-limit", "512",
|
||||
"--shm-size", "1g",
|
||||
"--gpus", "all",
|
||||
"--tmpfs", "/tmp:rw,noexec,nosuid,size=2g",
|
||||
"--health-cmd", "curl --fail --silent http://127.0.0.1:8000/v2/health/ready",
|
||||
"--health-interval", "5s",
|
||||
"--health-timeout", "3s",
|
||||
"--health-start-period", "20s",
|
||||
"--health-retries", "24",
|
||||
"-v", ("{0}:/models:ro" -f $dockerModelRepository),
|
||||
$imageRef,
|
||||
"tritonserver",
|
||||
"--model-repository=/models",
|
||||
"--model-control-mode=explicit",
|
||||
"--load-model=yolox_s",
|
||||
"--disable-auto-complete-config",
|
||||
"--strict-readiness=true",
|
||||
"--exit-on-error=true",
|
||||
"--allow-http=true",
|
||||
"--allow-grpc=false",
|
||||
"--allow-metrics=false"
|
||||
)
|
||||
|
||||
$graphArguments = @(
|
||||
"create",
|
||||
"--name", $candidateName,
|
||||
"--network", ("container:{0}" -f $tritonExpected.name),
|
||||
"--network", ("container:{0}" -f $tritonCandidateName),
|
||||
"--read-only",
|
||||
"--security-opt", "no-new-privileges:true",
|
||||
"--cap-drop", "ALL",
|
||||
@@ -268,7 +323,7 @@ $dockerArguments = @(
|
||||
)
|
||||
foreach ($entry in $descriptor.inputs.PSObject.Properties) {
|
||||
if ($null -ne $entry.Value.container_path) {
|
||||
$dockerArguments += @(
|
||||
$graphArguments += @(
|
||||
"-v", ("{0}:{1}:ro" -f (
|
||||
Convert-ToDockerPath $entry.Value.host_path
|
||||
), $entry.Value.container_path)
|
||||
@@ -276,13 +331,13 @@ foreach ($entry in $descriptor.inputs.PSObject.Properties) {
|
||||
}
|
||||
}
|
||||
foreach ($dependency in $descriptor.dependencies) {
|
||||
$dockerArguments += @(
|
||||
$graphArguments += @(
|
||||
"-v", ("{0}:{1}:ro" -f (
|
||||
Convert-ToDockerPath $dependency.host_path
|
||||
), $dependency.container_path)
|
||||
)
|
||||
}
|
||||
$dockerArguments += @(
|
||||
$graphArguments += @(
|
||||
"--entrypoint", "python3",
|
||||
$imageRef,
|
||||
"-m", "k1link.perception.reference_graph_cli",
|
||||
@@ -302,75 +357,137 @@ $dockerArguments += @(
|
||||
"--triton-origin", $descriptor.container.triton_origin,
|
||||
"--mode", $descriptor.acceptance.run_mode,
|
||||
"--expected-frames", ([string]$descriptor.acceptance.expected_frames),
|
||||
"--runtime-identity", "/run/mission-core/runtime-identity.json",
|
||||
"--output-root", "/output"
|
||||
)
|
||||
|
||||
$candidateCreated = $false
|
||||
$tritonCreated = $false
|
||||
$graphCreated = $false
|
||||
$providerAccepted = $false
|
||||
$graphAccepted = $false
|
||||
$runFailure = $null
|
||||
try {
|
||||
$candidateId = (& docker @dockerArguments).Trim()
|
||||
Assert-LastExitCode "M4.7 candidate creation"
|
||||
if ($candidateId -notmatch "^[a-f0-9]{64}$") {
|
||||
throw "M4.7 candidate id is invalid"
|
||||
$tritonCandidateId = (& docker @tritonArguments).Trim()
|
||||
Assert-LastExitCode "M4.7 isolated Triton creation"
|
||||
if ($tritonCandidateId -notmatch "^[a-f0-9]{64}$") {
|
||||
throw "M4.7 isolated Triton id is invalid"
|
||||
}
|
||||
$tritonCreated = $true
|
||||
& docker start $tritonCandidateName *> $null
|
||||
Assert-LastExitCode "M4.7 isolated Triton start"
|
||||
$tritonCandidate = $null
|
||||
foreach ($attempt in 1..120) {
|
||||
$tritonCandidate = Get-ContainerIdentity $tritonCandidateName
|
||||
if (-not $tritonCandidate.State.Running) {
|
||||
throw "M4.7 isolated Triton stopped before readiness"
|
||||
}
|
||||
if ($tritonCandidate.State.Health.Status -ceq "healthy") {
|
||||
break
|
||||
}
|
||||
if ($attempt -eq 120) {
|
||||
throw "M4.7 isolated Triton readiness timed out"
|
||||
}
|
||||
Start-Sleep -Seconds 2
|
||||
}
|
||||
$candidateCreated = $true
|
||||
$candidate = Get-ContainerIdentity $candidateName
|
||||
if (
|
||||
$candidate.Id -cne $candidateId -or
|
||||
$candidate.Image -cne $descriptor.container.image_id -or
|
||||
$candidate.HostConfig.NetworkMode -cne ("container:{0}" -f $triton.Id) -or
|
||||
-not $candidate.HostConfig.ReadonlyRootfs
|
||||
$tritonCandidate.Id -cne $tritonCandidateId -or
|
||||
$tritonCandidate.Image -cne $descriptor.container.image_id -or
|
||||
-not $tritonCandidate.HostConfig.ReadonlyRootfs -or
|
||||
-not (Test-NoPublishedPorts $tritonCandidate)
|
||||
) {
|
||||
throw "M4.7 candidate isolation contract changed"
|
||||
throw "M4.7 isolated Triton contract changed"
|
||||
}
|
||||
$runtimeIdentity = [ordered]@{
|
||||
schema_version = "missioncore.reference-graph-runtime-identity/v1"
|
||||
worker_id = "worker-006"
|
||||
worker_node = $env:COMPUTERNAME
|
||||
worker_container_id = $candidate.Id
|
||||
worker_image_id = $candidate.Image
|
||||
triton_container_id = $triton.Id
|
||||
triton_image_id = $triton.Image
|
||||
artifact_sha256 = $ExpectedArtifactSha256
|
||||
code_revision = $descriptor.code_revision
|
||||
graph_id = $descriptor.readiness.graph.graph_id
|
||||
source_mount_read_only = $true
|
||||
model_service_reused = $true
|
||||
public_worker_port_added = $false
|
||||
commands_enabled = $false
|
||||
actuation_allowed = $false
|
||||
$providerAccepted = $true
|
||||
|
||||
if (-not $PreflightOnly) {
|
||||
$candidateId = (& docker @graphArguments).Trim()
|
||||
Assert-LastExitCode "M4.7 graph candidate creation"
|
||||
if ($candidateId -notmatch "^[a-f0-9]{64}$") {
|
||||
throw "M4.7 graph candidate id is invalid"
|
||||
}
|
||||
$graphCreated = $true
|
||||
$candidate = Get-ContainerIdentity $candidateName
|
||||
if (
|
||||
$candidate.Id -cne $candidateId -or
|
||||
$candidate.Image -cne $descriptor.container.image_id -or
|
||||
$candidate.HostConfig.NetworkMode -cne ("container:{0}" -f $tritonCandidate.Id) -or
|
||||
-not $candidate.HostConfig.ReadonlyRootfs -or
|
||||
-not (Test-NoPublishedPorts $candidate)
|
||||
) {
|
||||
throw "M4.7 graph candidate isolation contract changed"
|
||||
}
|
||||
$runtimeIdentity = [ordered]@{
|
||||
schema_version = "missioncore.reference-graph-runtime-identity/v3"
|
||||
worker_id = "worker-006"
|
||||
worker_node = $env:COMPUTERNAME
|
||||
worker_container_id = $candidate.Id
|
||||
worker_image_id = $candidate.Image
|
||||
isolated_triton_container_id = $tritonCandidate.Id
|
||||
isolated_triton_image_id = $tritonCandidate.Image
|
||||
historical_worker_container_id = $durableBefore.Id
|
||||
historical_worker_running = $durableBefore.Running
|
||||
historical_triton_container_id = $historicalTritonBefore.Id
|
||||
historical_triton_running = $historicalTritonBefore.Running
|
||||
artifact_sha256 = $ExpectedArtifactSha256
|
||||
patch_id = $descriptor.patch_id
|
||||
code_revision = $descriptor.code_revision
|
||||
graph_id = $descriptor.readiness.graph.graph_id
|
||||
started_at_utc = [DateTime]::UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
|
||||
source_mount_read_only = $true
|
||||
isolated_model_service = $true
|
||||
public_worker_port_added = $false
|
||||
commands_enabled = $false
|
||||
actuation_allowed = $false
|
||||
}
|
||||
Write-Utf8NoBom $runtimeIdentityPath ($runtimeIdentity | ConvertTo-Json -Depth 4)
|
||||
& docker start --attach $candidateName
|
||||
Assert-LastExitCode "M4.7 canonical graph isolated shadow"
|
||||
$graphAccepted = $true
|
||||
}
|
||||
Write-Utf8NoBom $runtimeIdentityPath ($runtimeIdentity | ConvertTo-Json -Depth 4)
|
||||
Write-Output ("PATCH_ID={0}" -f $descriptor.patch_id)
|
||||
Write-Output ("ARTIFACT_SHA256={0}" -f $ExpectedArtifactSha256)
|
||||
Write-Output ("CANDIDATE_CONTAINER_ID={0}" -f $candidate.Id)
|
||||
Write-Output "PROVIDER_READINESS=accepted"
|
||||
& docker start --attach $candidateName
|
||||
Assert-LastExitCode "M4.7 canonical graph shadow"
|
||||
Write-Output "GRAPH_READINESS=accepted"
|
||||
} catch {
|
||||
$runFailure = $_
|
||||
} finally {
|
||||
if ($candidateCreated) {
|
||||
if ($graphCreated) {
|
||||
& docker rm --force $candidateName *> $null
|
||||
if ($LASTEXITCODE -ne 0 -and $null -eq $runFailure) {
|
||||
$runFailure = "M4.7 candidate cleanup failed"
|
||||
$runFailure = "M4.7 graph candidate cleanup failed"
|
||||
}
|
||||
}
|
||||
if ($tritonCreated) {
|
||||
& docker rm --force $tritonCandidateName *> $null
|
||||
if ($LASTEXITCODE -ne 0 -and $null -eq $runFailure) {
|
||||
$runFailure = "M4.7 isolated Triton cleanup failed"
|
||||
}
|
||||
}
|
||||
Remove-Item -LiteralPath $scratch -Force -Recurse -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
$null = Assert-ContainerIdentity $predecessor.name $predecessor.container_id (
|
||||
$predecessor.image_id
|
||||
) $false
|
||||
$null = Assert-ContainerIdentity $tritonExpected.name $tritonExpected.container_id (
|
||||
$tritonExpected.image_id
|
||||
) $true
|
||||
$durableAfter = Assert-PreservedContainerSnapshot (
|
||||
$durableExpected
|
||||
) $durableBefore "Historical durable worker"
|
||||
$historicalTritonAfter = Assert-PreservedContainerSnapshot (
|
||||
$historicalTritonExpected
|
||||
) $historicalTritonBefore "Historical Triton"
|
||||
$freeAfter = Assert-FreeSpace "completed"
|
||||
Write-Output ("PATCH_ID={0}" -f $descriptor.patch_id)
|
||||
Write-Output ("ARTIFACT_SHA256={0}" -f $ExpectedArtifactSha256)
|
||||
Write-Output ("DISK_FREE_BYTES_BEFORE={0}" -f $freeBefore)
|
||||
Write-Output ("DISK_FREE_BYTES_AFTER={0}" -f $freeAfter)
|
||||
Write-Output ("HISTORICAL_DURABLE_WORKER_RUNNING={0}" -f $durableAfter.Running)
|
||||
Write-Output ("HISTORICAL_TRITON_RUNNING={0}" -f $historicalTritonAfter.Running)
|
||||
Write-Output "DURABLE_WORKER_ACTION=none"
|
||||
Write-Output "TRITON_ACTION=none"
|
||||
Write-Output "HISTORICAL_TRITON_ACTION=none"
|
||||
Write-Output "ISOLATED_TRITON_ACTION=removed"
|
||||
if ($null -ne $runFailure) {
|
||||
throw $runFailure
|
||||
}
|
||||
if (-not $providerAccepted) {
|
||||
throw "M4.7 provider readiness was not accepted"
|
||||
}
|
||||
Write-Output "PROVIDER_READINESS=accepted"
|
||||
if ($PreflightOnly) {
|
||||
Write-Output "GRAPH_READINESS=not-run"
|
||||
Write-Output "PREFLIGHT=accepted"
|
||||
} elseif ($graphAccepted) {
|
||||
Write-Output "GRAPH_READINESS=accepted"
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
|
||||
BASE_TEMPLATE = REPOSITORY_ROOT / "config/deployment/mission-core-worker-shadow-v1.template.json"
|
||||
RUNNER = REPOSITORY_ROOT / "scripts/Invoke-M47CanonicalGraphShadow.ps1"
|
||||
WHEEL_NAME = "nodedc_mission_core-0.1.0-py3-none-any.whl"
|
||||
DESCRIPTOR_NAME = "mission-core-worker-m47-graph-shadow-v2.json"
|
||||
DESCRIPTOR_NAME = "mission-core-worker-m47-graph-shadow-v3.json"
|
||||
PATCH_ID = re.compile(r"^[A-Za-z0-9._-]{1,96}$")
|
||||
EXPECTED_BASE_TEMPLATE_SHA256 = (
|
||||
"319e7ac7f14e5911ad44234c9ec918c73e11a3406724d5cee3e2ef64bb036e0c"
|
||||
@@ -101,16 +101,18 @@ def render_descriptor(
|
||||
descriptor = json.loads(BASE_TEMPLATE.read_text("utf-8"))
|
||||
descriptor.update(
|
||||
{
|
||||
"schema_version": "nodedc.mission-core-worker.shadow-release/v2",
|
||||
"schema_version": "nodedc.mission-core-worker.shadow-release/v3",
|
||||
"patch_id": patch_id,
|
||||
"code_revision": revision,
|
||||
"transition": "m47-canonical-graph-shadow-v1",
|
||||
"transition": "m47-canonical-graph-isolated-shadow-v1",
|
||||
"artifact_type": "shadow-release",
|
||||
}
|
||||
)
|
||||
descriptor["container"].update(
|
||||
{
|
||||
"name": "ndc-mission-core-m47-graph-shadow",
|
||||
"triton_name": "ndc-mission-core-m47-triton-shadow",
|
||||
"model_repository_host_path": "D:\\NDC_MISSIONCORE\\runtime\\models",
|
||||
"python_path": (
|
||||
f"/release/{WHEEL_NAME}:/opt/media:/opt/opencv:/opt/pillow"
|
||||
),
|
||||
@@ -179,10 +181,19 @@ def render_descriptor(
|
||||
"actuation_allowed": False,
|
||||
},
|
||||
}
|
||||
descriptor["rollback"] = {
|
||||
"durable_worker_action": "none",
|
||||
"historical_triton_action": "none",
|
||||
"preserve_failed_evidence": True,
|
||||
"remove_candidate_graph_container": True,
|
||||
"remove_candidate_triton_container": True,
|
||||
"remove_unaccepted_release": True,
|
||||
}
|
||||
if (
|
||||
descriptor["predecessor"]["durable_worker"]["name"]
|
||||
!= "ndc-mission-core-perception-worker"
|
||||
or descriptor["rollback"]["durable_worker_action"] != "none"
|
||||
or descriptor["predecessor"]["triton"]["name"]
|
||||
!= "ndc-mission-core-triton"
|
||||
or descriptor["boundary"]["external_deploy_registry"] is not False
|
||||
):
|
||||
raise ArtifactBuildError("M4.7 predecessor or deployment boundary changed")
|
||||
@@ -278,7 +289,7 @@ def build_artifact(
|
||||
"code_revision": selected_revision,
|
||||
"wheel_sha256": wheel_sha256,
|
||||
"payload_files": payload_files,
|
||||
"transition": "m47-canonical-graph-shadow-v1",
|
||||
"transition": "m47-canonical-graph-isolated-shadow-v1",
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Publish the accepted Worker 006 M4.7 graph as a visual LAB result."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from k1link.laboratory.m47_reference_graph import publish_m47_reference_graph_lab
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--graph-result-root", type=Path, required=True)
|
||||
parser.add_argument("--visual-result-root", type=Path, required=True)
|
||||
parser.add_argument("--output-root", type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
result = publish_m47_reference_graph_lab(
|
||||
graph_result_root=args.graph_result_root,
|
||||
visual_result_root=args.visual_result_root,
|
||||
output_root=args.output_root,
|
||||
)
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"accepted": True,
|
||||
"result_id": result.result_id,
|
||||
"result_root": str(result.result_root),
|
||||
},
|
||||
sort_keys=True,
|
||||
separators=(",", ":"),
|
||||
)
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,355 @@
|
||||
"""Publish an immutable visual LAB binding for the accepted M4.7 graph shadow."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import uuid
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Final
|
||||
|
||||
from k1link.perception.reference_graph_result import read_reference_graph_result
|
||||
from k1link.perception.threat_replay import read_threat_replay_result
|
||||
|
||||
M47_REFERENCE_GRAPH_LAB_SCHEMA: Final = "missioncore.reference-perception-graph-lab/v1"
|
||||
M47_REFERENCE_GRAPH_LAB_REPORT_SCHEMA: Final = (
|
||||
"missioncore.reference-perception-graph-lab-report/v1"
|
||||
)
|
||||
M47_REFERENCE_GRAPH_LAB_PREFIX: Final = "m47-reference-graph-lab-"
|
||||
|
||||
|
||||
class M47ReferenceGraphLabError(RuntimeError):
|
||||
"""The M4.7 graph and visual replay cannot be bound without exact proof."""
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class M47ReferenceGraphLab:
|
||||
result_id: str
|
||||
result_root: Path
|
||||
manifest: dict[str, object]
|
||||
report: dict[str, object]
|
||||
|
||||
|
||||
def publish_m47_reference_graph_lab(
|
||||
*,
|
||||
graph_result_root: Path,
|
||||
visual_result_root: Path,
|
||||
output_root: Path,
|
||||
) -> M47ReferenceGraphLab:
|
||||
graph = read_reference_graph_result(graph_result_root)
|
||||
visual = read_threat_replay_result(visual_result_root)
|
||||
if not graph.accepted or not visual.accepted:
|
||||
raise M47ReferenceGraphLabError("both graph and visual replay must be accepted")
|
||||
graph_report = graph.report
|
||||
graph_manifest = graph.manifest
|
||||
graph_runtime = _object(graph_manifest.get("runtime"), "graph runtime")
|
||||
parity = _object(graph_report.get("accepted_parity"), "graph parity")
|
||||
mismatches = _object(parity.get("mismatch_counts"), "graph parity mismatches")
|
||||
visual_identity = _object(visual.manifest.get("identity"), "visual identity")
|
||||
if (
|
||||
parity.get("accepted") is not True
|
||||
or parity.get("expected_frames") != 4489
|
||||
or parity.get("compared_frames") != 4489
|
||||
or set(mismatches)
|
||||
!= {
|
||||
"source_binding",
|
||||
"current",
|
||||
"rolling_retained",
|
||||
"held",
|
||||
"expired",
|
||||
"camera_uncertainty",
|
||||
"threat_assessments",
|
||||
}
|
||||
or any(value != 0 for value in mismatches.values())
|
||||
or parity.get("threat_frames_sha256") != visual_identity.get("frames_sha256")
|
||||
or parity.get("temporal_frames_sha256") != visual_identity.get("temporal_frames_sha256")
|
||||
):
|
||||
raise M47ReferenceGraphLabError("graph-to-visual parity proof changed")
|
||||
terminal = _object(graph_report.get("terminal_outcomes"), "terminal outcomes")
|
||||
queues = _object(graph_report.get("queue_high_watermarks"), "queue high watermarks")
|
||||
execution = _object(graph_report.get("execution"), "graph execution")
|
||||
authority = {
|
||||
"physical_live": False,
|
||||
"commands_enabled": False,
|
||||
"actuation_allowed": False,
|
||||
"navigation_or_safety_accepted": False,
|
||||
"ground_truth": False,
|
||||
"mode": "replay-simulated",
|
||||
}
|
||||
visual_evidence = {
|
||||
"linked_result_id": visual.result_id,
|
||||
"binding": "exact-threat-and-temporal-ledger-parity",
|
||||
"timeline_frames": 4489,
|
||||
"shared_recorded_clock": True,
|
||||
"video_camera_3d_plan_available": True,
|
||||
"regression_sequences": [138, 274, 1880, 2584],
|
||||
"independent_ground_truth": False,
|
||||
}
|
||||
identity: dict[str, object] = {
|
||||
"schema_version": M47_REFERENCE_GRAPH_LAB_SCHEMA,
|
||||
"binding_id": "m47-reference-graph-visual-binding/v1",
|
||||
"source": {
|
||||
"source_id": "RAVNOVES00",
|
||||
"source_session_id": visual_identity.get("source_session_id"),
|
||||
"graph_result_id": graph.result_id,
|
||||
"visual_result_id": visual.result_id,
|
||||
"temporal_frames_sha256": parity.get("temporal_frames_sha256"),
|
||||
"threat_frames_sha256": parity.get("threat_frames_sha256"),
|
||||
},
|
||||
"method": {
|
||||
"graph_id": graph_manifest.get("graph_id"),
|
||||
"run_mode": graph_manifest.get("run_mode"),
|
||||
"source_profile_id": graph_manifest.get("source_profile_id"),
|
||||
"canonical_payload_sha256": graph_manifest.get("canonical_payload_sha256"),
|
||||
"parity_schema_version": parity.get("schema_version"),
|
||||
},
|
||||
"execution": graph_runtime,
|
||||
"acceptance": {
|
||||
"accepted": True,
|
||||
"expected_frames": 4489,
|
||||
"admitted_frames": graph_report.get("admitted_frames"),
|
||||
"delivered_frames": terminal.get("delivered"),
|
||||
"parity_mismatch_counts": mismatches,
|
||||
},
|
||||
"visual_evidence": visual_evidence,
|
||||
"authority": authority,
|
||||
}
|
||||
identity_sha256 = _canonical_sha256(identity)
|
||||
result_id = f"{M47_REFERENCE_GRAPH_LAB_PREFIX}{identity_sha256}"
|
||||
elapsed_seconds = execution.get("elapsed_seconds")
|
||||
report: dict[str, object] = {
|
||||
"schema_version": M47_REFERENCE_GRAPH_LAB_REPORT_SCHEMA,
|
||||
"result_id": result_id,
|
||||
"source": identity["source"],
|
||||
"method": identity["method"],
|
||||
"execution": {
|
||||
**graph_runtime,
|
||||
"elapsed_seconds": elapsed_seconds,
|
||||
},
|
||||
"metrics": {
|
||||
"frames": {
|
||||
"expected": 4489,
|
||||
"admitted": graph_report.get("admitted_frames"),
|
||||
"delivered": terminal.get("delivered"),
|
||||
"failed": terminal.get("failed", 0),
|
||||
"stale": terminal.get("stale", 0),
|
||||
"superseded": terminal.get("superseded", 0),
|
||||
"rejected": terminal.get("rejected", 0),
|
||||
"unavailable": terminal.get("unavailable", 0),
|
||||
},
|
||||
"queue_high_watermarks": queues,
|
||||
"parity_mismatch_counts": mismatches,
|
||||
"canonical_payload_sha256": graph_manifest.get("canonical_payload_sha256"),
|
||||
},
|
||||
"acceptance": {
|
||||
"accepted": True,
|
||||
"gates": graph_report.get("gates"),
|
||||
"parity": parity,
|
||||
},
|
||||
"decision": {
|
||||
"state": "accepted-reference-graph-replay",
|
||||
"next_gate": "independent-object-centric-detection-quality",
|
||||
"summary": (
|
||||
"Canonical source→detector→geometry→temporal/motion→rolling→threat "
|
||||
"graph preserves the accepted M4.5R/M4.6 payload for every frame."
|
||||
),
|
||||
},
|
||||
"limitations": [
|
||||
"This result proves recorded lossless replay parity, not physical live operation.",
|
||||
"The linked M4.6 visual replay is engineering evidence, not independent object truth.",
|
||||
"No navigation, safety, command or actuation authority is granted.",
|
||||
"Independent object-centric detection quality remains the next acceptance gate.",
|
||||
],
|
||||
"authority": authority,
|
||||
"visual_evidence": visual_evidence,
|
||||
}
|
||||
output = output_root.expanduser().resolve()
|
||||
output.mkdir(mode=0o700, parents=True, exist_ok=True)
|
||||
if output.is_symlink() or not output.is_dir():
|
||||
raise M47ReferenceGraphLabError("LAB output root must be a real directory")
|
||||
staging = output / f".m47-reference-graph-lab.{uuid.uuid4().hex}.tmp"
|
||||
staging.mkdir(mode=0o700, exist_ok=False)
|
||||
try:
|
||||
sources = {
|
||||
"graph-manifest.json": graph.result_root / "manifest.json",
|
||||
"graph-report.json": graph.result_root / "report.json",
|
||||
"graph-runtime.json": graph.result_root / "runtime.json",
|
||||
"visual-manifest.json": visual.result_root / "manifest.json",
|
||||
"visual-report.json": visual.result_root / "report.json",
|
||||
}
|
||||
for name, source in sources.items():
|
||||
shutil.copyfile(source, staging / name)
|
||||
_write_json(staging / "report.json", report)
|
||||
roles = {
|
||||
"report.json": "m47-lab-report",
|
||||
"graph-manifest.json": "m47-graph-manifest",
|
||||
"graph-report.json": "m47-graph-report",
|
||||
"graph-runtime.json": "m47-runtime",
|
||||
"visual-manifest.json": "linked-visual-manifest",
|
||||
"visual-report.json": "linked-visual-report",
|
||||
}
|
||||
schemas = {
|
||||
"graph-manifest.json": graph_manifest.get("schema_version"),
|
||||
"graph-report.json": graph_report.get("schema_version"),
|
||||
"graph-runtime.json": graph_runtime.get("schema_version"),
|
||||
"visual-manifest.json": visual.manifest.get("schema_version"),
|
||||
"visual-report.json": visual.report.get("schema_version"),
|
||||
"report.json": M47_REFERENCE_GRAPH_LAB_REPORT_SCHEMA,
|
||||
}
|
||||
artifacts = [_artifact(staging / name, role, schemas[name]) for name, role in roles.items()]
|
||||
manifest: dict[str, object] = {
|
||||
"schema_version": M47_REFERENCE_GRAPH_LAB_SCHEMA,
|
||||
"result_id": result_id,
|
||||
"identity_sha256": identity_sha256,
|
||||
"identity": identity,
|
||||
"created_at_utc": graph_runtime.get("started_at_utc"),
|
||||
"accepted": True,
|
||||
"ground_truth": False,
|
||||
"authority": authority,
|
||||
"artifacts": artifacts,
|
||||
}
|
||||
_write_json(staging / "manifest.json", manifest)
|
||||
target = output / result_id
|
||||
_publish(staging, target)
|
||||
except Exception:
|
||||
shutil.rmtree(staging, ignore_errors=True)
|
||||
raise
|
||||
return read_m47_reference_graph_lab(target)
|
||||
|
||||
|
||||
def read_m47_reference_graph_lab(result_root: Path) -> M47ReferenceGraphLab:
|
||||
candidate = result_root.expanduser().absolute()
|
||||
if candidate.is_symlink():
|
||||
raise M47ReferenceGraphLabError("M4.7 LAB result root is invalid")
|
||||
resolved = candidate.resolve(strict=True)
|
||||
if (
|
||||
resolved.is_symlink()
|
||||
or not resolved.is_dir()
|
||||
or not resolved.name.startswith(M47_REFERENCE_GRAPH_LAB_PREFIX)
|
||||
):
|
||||
raise M47ReferenceGraphLabError("M4.7 LAB result root is invalid")
|
||||
manifest = _read_json(resolved / "manifest.json")
|
||||
if (
|
||||
manifest.get("schema_version") != M47_REFERENCE_GRAPH_LAB_SCHEMA
|
||||
or manifest.get("result_id") != resolved.name
|
||||
or manifest.get("accepted") is not True
|
||||
or manifest.get("ground_truth") is not False
|
||||
):
|
||||
raise M47ReferenceGraphLabError("M4.7 LAB manifest changed")
|
||||
identity = _object(manifest.get("identity"), "LAB identity")
|
||||
identity_sha256 = _canonical_sha256(identity)
|
||||
if (
|
||||
manifest.get("identity_sha256") != identity_sha256
|
||||
or resolved.name != f"{M47_REFERENCE_GRAPH_LAB_PREFIX}{identity_sha256}"
|
||||
):
|
||||
raise M47ReferenceGraphLabError("M4.7 LAB identity changed")
|
||||
artifacts = manifest.get("artifacts")
|
||||
if not isinstance(artifacts, list) or len(artifacts) != 6:
|
||||
raise M47ReferenceGraphLabError("M4.7 LAB artifact inventory changed")
|
||||
for value in artifacts:
|
||||
descriptor = _object(value, "LAB artifact")
|
||||
path_value = descriptor.get("path")
|
||||
if not isinstance(path_value, str) or "/" in path_value or "\\" in path_value:
|
||||
raise M47ReferenceGraphLabError("M4.7 LAB artifact path changed")
|
||||
path = resolved / path_value
|
||||
if (
|
||||
path.is_symlink()
|
||||
or not path.is_file()
|
||||
or path.stat().st_size != descriptor.get("byte_length")
|
||||
or _file_sha256(path) != descriptor.get("sha256")
|
||||
):
|
||||
raise M47ReferenceGraphLabError("M4.7 LAB artifact proof changed")
|
||||
report = _read_json(resolved / "report.json")
|
||||
if (
|
||||
report.get("schema_version") != M47_REFERENCE_GRAPH_LAB_REPORT_SCHEMA
|
||||
or report.get("result_id") != resolved.name
|
||||
or _object(report.get("acceptance"), "LAB acceptance").get("accepted") is not True
|
||||
or report.get("authority") != identity.get("authority")
|
||||
):
|
||||
raise M47ReferenceGraphLabError("M4.7 LAB report changed")
|
||||
return M47ReferenceGraphLab(
|
||||
result_id=resolved.name,
|
||||
result_root=resolved,
|
||||
manifest=manifest,
|
||||
report=report,
|
||||
)
|
||||
|
||||
|
||||
def _artifact(path: Path, role: str, schema: object) -> dict[str, object]:
|
||||
return {
|
||||
"role": role,
|
||||
"path": path.name,
|
||||
"byte_length": path.stat().st_size,
|
||||
"sha256": _file_sha256(path),
|
||||
"schema_version": schema,
|
||||
"media_type": "application/json",
|
||||
}
|
||||
|
||||
|
||||
def _publish(staging: Path, target: Path) -> None:
|
||||
if target.exists():
|
||||
existing = {path.name: _file_sha256(path) for path in target.iterdir() if path.is_file()}
|
||||
proposed = {path.name: _file_sha256(path) for path in staging.iterdir() if path.is_file()}
|
||||
if existing != proposed:
|
||||
raise M47ReferenceGraphLabError("immutable M4.7 LAB identity collision")
|
||||
shutil.rmtree(staging)
|
||||
return
|
||||
os.replace(staging, target)
|
||||
|
||||
|
||||
def _object(value: object, label: str) -> dict[str, object]:
|
||||
if not isinstance(value, dict) or any(not isinstance(key, str) for key in value):
|
||||
raise M47ReferenceGraphLabError(f"{label} must be an object")
|
||||
return value
|
||||
|
||||
|
||||
def _write_json(path: Path, document: dict[str, object]) -> None:
|
||||
with path.open("wb") as stream:
|
||||
stream.write(_canonical_json(document) + b"\n")
|
||||
stream.flush()
|
||||
os.fsync(stream.fileno())
|
||||
|
||||
|
||||
def _read_json(path: Path) -> dict[str, object]:
|
||||
if path.is_symlink() or not path.is_file() or path.stat().st_size > 1024 * 1024:
|
||||
raise M47ReferenceGraphLabError("M4.7 LAB JSON artifact is invalid")
|
||||
try:
|
||||
return _object(json.loads(path.read_text("utf-8")), "M4.7 LAB JSON artifact")
|
||||
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as error:
|
||||
raise M47ReferenceGraphLabError("M4.7 LAB JSON artifact is unreadable") from error
|
||||
|
||||
|
||||
def _canonical_json(value: object) -> bytes:
|
||||
return json.dumps(
|
||||
value,
|
||||
ensure_ascii=False,
|
||||
sort_keys=True,
|
||||
separators=(",", ":"),
|
||||
allow_nan=False,
|
||||
).encode("utf-8")
|
||||
|
||||
|
||||
def _canonical_sha256(value: object) -> str:
|
||||
return hashlib.sha256(_canonical_json(value)).hexdigest()
|
||||
|
||||
|
||||
def _file_sha256(path: Path) -> str:
|
||||
digest = hashlib.sha256()
|
||||
with path.open("rb") as stream:
|
||||
for chunk in iter(lambda: stream.read(1024 * 1024), b""):
|
||||
digest.update(chunk)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
__all__ = [
|
||||
"M47_REFERENCE_GRAPH_LAB_PREFIX",
|
||||
"M47_REFERENCE_GRAPH_LAB_REPORT_SCHEMA",
|
||||
"M47_REFERENCE_GRAPH_LAB_SCHEMA",
|
||||
"M47ReferenceGraphLab",
|
||||
"M47ReferenceGraphLabError",
|
||||
"publish_m47_reference_graph_lab",
|
||||
"read_m47_reference_graph_lab",
|
||||
]
|
||||
@@ -8,14 +8,15 @@ from contextlib import AbstractContextManager
|
||||
from dataclasses import dataclass, replace
|
||||
from queue import Empty, Full, Queue
|
||||
from threading import Event, Lock, Thread
|
||||
from typing import TypeVar, cast
|
||||
from typing import TYPE_CHECKING, TypeVar, cast
|
||||
|
||||
from k1link.compute.pipeline_telemetry import (
|
||||
PipelineStageOutcome,
|
||||
PipelineTelemetryEmitter,
|
||||
PipelineTelemetryIdentity,
|
||||
PipelineTelemetrySink,
|
||||
)
|
||||
if TYPE_CHECKING:
|
||||
from k1link.compute.pipeline_telemetry import (
|
||||
PipelineStageOutcome,
|
||||
PipelineTelemetryEmitter,
|
||||
PipelineTelemetryIdentity,
|
||||
PipelineTelemetrySink,
|
||||
)
|
||||
|
||||
from .baseline import BASELINE_PROFILE_ID, BASELINE_SOURCE_ID
|
||||
from .contracts import (
|
||||
@@ -763,6 +764,8 @@ class ReferencePerceptionGraphV1:
|
||||
def _run_emitter(self) -> PipelineTelemetryEmitter | None:
|
||||
if self.telemetry_identity is None or self.telemetry_sink is None:
|
||||
return None
|
||||
from k1link.compute.pipeline_telemetry import PipelineTelemetryEmitter
|
||||
|
||||
return PipelineTelemetryEmitter(
|
||||
identity=self.telemetry_identity,
|
||||
sink=self.telemetry_sink,
|
||||
@@ -772,6 +775,8 @@ class ReferencePerceptionGraphV1:
|
||||
def _frame_emitter(self, packet: SourcePacket) -> PipelineTelemetryEmitter | None:
|
||||
if self.telemetry_identity is None or self.telemetry_sink is None:
|
||||
return None
|
||||
from k1link.compute.pipeline_telemetry import PipelineTelemetryEmitter
|
||||
|
||||
identity = replace(
|
||||
self.telemetry_identity,
|
||||
request_id=packet.envelope.frame_id,
|
||||
|
||||
@@ -8,6 +8,7 @@ import time
|
||||
from pathlib import Path
|
||||
|
||||
from .graph_contracts import GraphRunMode, GraphRunResultV2
|
||||
from .reference_graph_identity import ReferenceGraphRuntimeIdentity
|
||||
from .reference_graph_parity import compare_reference_graph_to_accepted_ledgers
|
||||
from .reference_graph_result import seal_reference_graph_result
|
||||
from .reference_graph_runtime import ReferenceGraphRuntimePaths, build_reference_graph_runtime
|
||||
@@ -35,8 +36,12 @@ def main(argv: list[str] | None = None) -> int:
|
||||
default=GraphRunMode.LOSSLESS_REPLAY.value,
|
||||
)
|
||||
parser.add_argument("--expected-frames", type=int, default=4489)
|
||||
parser.add_argument("--runtime-identity", type=Path, required=True)
|
||||
parser.add_argument("--output-root", type=Path, required=True)
|
||||
args = parser.parse_args(argv)
|
||||
runtime_identity = ReferenceGraphRuntimeIdentity.from_dict(
|
||||
json.loads(args.runtime_identity.resolve(strict=True).read_text("utf-8"))
|
||||
)
|
||||
paths = ReferenceGraphRuntimePaths(
|
||||
graph_config=args.graph_config,
|
||||
baseline_profile=args.baseline_profile,
|
||||
@@ -65,17 +70,20 @@ def main(argv: list[str] | None = None) -> int:
|
||||
threat_frames_path=args.threat_parity_frames,
|
||||
expected_frames=args.expected_frames,
|
||||
)
|
||||
execution_elapsed_seconds = (time.perf_counter_ns() - started_ns) / 1_000_000_000
|
||||
sealed = seal_reference_graph_result(
|
||||
graph_result,
|
||||
output_root=args.output_root,
|
||||
expected_frames=args.expected_frames,
|
||||
parity=parity,
|
||||
runtime_identity=runtime_identity,
|
||||
execution_elapsed_seconds=execution_elapsed_seconds,
|
||||
)
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"accepted": sealed.accepted,
|
||||
"elapsed_seconds": (time.perf_counter_ns() - started_ns) / 1_000_000_000,
|
||||
"elapsed_seconds": execution_elapsed_seconds,
|
||||
"result_id": sealed.result_id,
|
||||
"result_root": str(sealed.result_root),
|
||||
},
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
"""Strict execution identity for the isolated M4.7 reference graph shadow."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime as dt
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from typing import Final
|
||||
|
||||
from .graph_contracts import REFERENCE_GRAPH_ID_V2
|
||||
|
||||
REFERENCE_GRAPH_RUNTIME_IDENTITY_SCHEMA: Final = "missioncore.reference-graph-runtime-identity/v3"
|
||||
|
||||
_SHA256 = re.compile(r"^[a-f0-9]{64}$")
|
||||
_GIT_REVISION = re.compile(r"^[a-f0-9]{40}$")
|
||||
_DOCKER_IMAGE_ID = re.compile(r"^sha256:[a-f0-9]{64}$")
|
||||
_IDENTIFIER = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.:/-]{0,255}$")
|
||||
|
||||
|
||||
class ReferenceGraphRuntimeIdentityError(ValueError):
|
||||
"""The Worker execution identity is incomplete or exceeds shadow authority."""
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class ReferenceGraphRuntimeIdentity:
|
||||
worker_id: str
|
||||
worker_node: str
|
||||
worker_container_id: str
|
||||
worker_image_id: str
|
||||
isolated_triton_container_id: str
|
||||
isolated_triton_image_id: str
|
||||
historical_worker_container_id: str
|
||||
historical_worker_running: bool
|
||||
historical_triton_container_id: str
|
||||
historical_triton_running: bool
|
||||
artifact_sha256: str
|
||||
patch_id: str
|
||||
code_revision: str
|
||||
graph_id: str
|
||||
started_at_utc: str
|
||||
source_mount_read_only: bool
|
||||
isolated_model_service: bool
|
||||
public_worker_port_added: bool
|
||||
commands_enabled: bool
|
||||
actuation_allowed: bool
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.worker_id != "worker-006":
|
||||
raise ReferenceGraphRuntimeIdentityError("M4.7 shadow must run on Worker 006")
|
||||
_identifier(self.worker_node, "worker node")
|
||||
_identifier(self.patch_id, "patch id")
|
||||
for value, label in (
|
||||
(self.worker_container_id, "worker container id"),
|
||||
(self.isolated_triton_container_id, "isolated Triton container id"),
|
||||
(self.historical_worker_container_id, "historical worker container id"),
|
||||
(self.historical_triton_container_id, "historical Triton container id"),
|
||||
):
|
||||
if _SHA256.fullmatch(value) is None:
|
||||
raise ReferenceGraphRuntimeIdentityError(f"{label} must be a full digest")
|
||||
for value, label in (
|
||||
(self.worker_image_id, "worker image id"),
|
||||
(self.isolated_triton_image_id, "isolated Triton image id"),
|
||||
):
|
||||
if _DOCKER_IMAGE_ID.fullmatch(value) is None:
|
||||
raise ReferenceGraphRuntimeIdentityError(f"{label} must be a full image digest")
|
||||
if _SHA256.fullmatch(self.artifact_sha256) is None:
|
||||
raise ReferenceGraphRuntimeIdentityError("artifact must be digest-bound")
|
||||
if _GIT_REVISION.fullmatch(self.code_revision) is None:
|
||||
raise ReferenceGraphRuntimeIdentityError("code revision must be a full Git SHA")
|
||||
if self.graph_id != REFERENCE_GRAPH_ID_V2:
|
||||
raise ReferenceGraphRuntimeIdentityError("runtime graph id changed")
|
||||
try:
|
||||
started = dt.datetime.fromisoformat(self.started_at_utc.replace("Z", "+00:00"))
|
||||
except ValueError as error:
|
||||
raise ReferenceGraphRuntimeIdentityError(
|
||||
"runtime start must be an ISO-8601 UTC timestamp"
|
||||
) from error
|
||||
if started.tzinfo != dt.UTC:
|
||||
raise ReferenceGraphRuntimeIdentityError("runtime start must use UTC")
|
||||
if not self.source_mount_read_only:
|
||||
raise ReferenceGraphRuntimeIdentityError("source mounts must be read-only")
|
||||
if not self.isolated_model_service:
|
||||
raise ReferenceGraphRuntimeIdentityError("model service must be isolated")
|
||||
if self.public_worker_port_added:
|
||||
raise ReferenceGraphRuntimeIdentityError("shadow must not add a public port")
|
||||
if self.commands_enabled or self.actuation_allowed:
|
||||
raise ReferenceGraphRuntimeIdentityError("shadow authority must remain disabled")
|
||||
|
||||
def to_dict(self) -> dict[str, object]:
|
||||
return {
|
||||
"schema_version": REFERENCE_GRAPH_RUNTIME_IDENTITY_SCHEMA,
|
||||
"worker_id": self.worker_id,
|
||||
"worker_node": self.worker_node,
|
||||
"worker_container_id": self.worker_container_id,
|
||||
"worker_image_id": self.worker_image_id,
|
||||
"isolated_triton_container_id": self.isolated_triton_container_id,
|
||||
"isolated_triton_image_id": self.isolated_triton_image_id,
|
||||
"historical_worker_container_id": self.historical_worker_container_id,
|
||||
"historical_worker_running": self.historical_worker_running,
|
||||
"historical_triton_container_id": self.historical_triton_container_id,
|
||||
"historical_triton_running": self.historical_triton_running,
|
||||
"artifact_sha256": self.artifact_sha256,
|
||||
"patch_id": self.patch_id,
|
||||
"code_revision": self.code_revision,
|
||||
"graph_id": self.graph_id,
|
||||
"started_at_utc": self.started_at_utc,
|
||||
"source_mount_read_only": self.source_mount_read_only,
|
||||
"isolated_model_service": self.isolated_model_service,
|
||||
"public_worker_port_added": self.public_worker_port_added,
|
||||
"commands_enabled": self.commands_enabled,
|
||||
"actuation_allowed": self.actuation_allowed,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, value: object) -> ReferenceGraphRuntimeIdentity:
|
||||
if not isinstance(value, dict) or any(not isinstance(key, str) for key in value):
|
||||
raise ReferenceGraphRuntimeIdentityError("runtime identity must be an object")
|
||||
fields = {
|
||||
"worker_id",
|
||||
"worker_node",
|
||||
"worker_container_id",
|
||||
"worker_image_id",
|
||||
"isolated_triton_container_id",
|
||||
"isolated_triton_image_id",
|
||||
"historical_worker_container_id",
|
||||
"historical_worker_running",
|
||||
"historical_triton_container_id",
|
||||
"historical_triton_running",
|
||||
"artifact_sha256",
|
||||
"patch_id",
|
||||
"code_revision",
|
||||
"graph_id",
|
||||
"started_at_utc",
|
||||
"source_mount_read_only",
|
||||
"isolated_model_service",
|
||||
"public_worker_port_added",
|
||||
"commands_enabled",
|
||||
"actuation_allowed",
|
||||
}
|
||||
if set(value) != fields | {"schema_version"}:
|
||||
raise ReferenceGraphRuntimeIdentityError("runtime identity fields changed")
|
||||
if value.get("schema_version") != REFERENCE_GRAPH_RUNTIME_IDENTITY_SCHEMA:
|
||||
raise ReferenceGraphRuntimeIdentityError("runtime identity schema changed")
|
||||
strings = {
|
||||
field: _string(value.get(field), field)
|
||||
for field in fields
|
||||
if field
|
||||
not in {
|
||||
"historical_worker_running",
|
||||
"historical_triton_running",
|
||||
"source_mount_read_only",
|
||||
"isolated_model_service",
|
||||
"public_worker_port_added",
|
||||
"commands_enabled",
|
||||
"actuation_allowed",
|
||||
}
|
||||
}
|
||||
booleans = {
|
||||
field: _boolean(value.get(field), field) for field in fields if field not in strings
|
||||
}
|
||||
return cls(**strings, **booleans)
|
||||
|
||||
|
||||
def _string(value: object, label: str) -> str:
|
||||
if not isinstance(value, str) or not value:
|
||||
raise ReferenceGraphRuntimeIdentityError(f"{label} must be a non-empty string")
|
||||
return value
|
||||
|
||||
|
||||
def _boolean(value: object, label: str) -> bool:
|
||||
if not isinstance(value, bool):
|
||||
raise ReferenceGraphRuntimeIdentityError(f"{label} must be boolean")
|
||||
return value
|
||||
|
||||
|
||||
def _identifier(value: str, label: str) -> None:
|
||||
if _IDENTIFIER.fullmatch(value) is None:
|
||||
raise ReferenceGraphRuntimeIdentityError(f"{label} is invalid")
|
||||
|
||||
|
||||
__all__ = [
|
||||
"REFERENCE_GRAPH_RUNTIME_IDENTITY_SCHEMA",
|
||||
"ReferenceGraphRuntimeIdentity",
|
||||
"ReferenceGraphRuntimeIdentityError",
|
||||
]
|
||||
@@ -134,10 +134,15 @@ def compare_reference_graph_to_accepted_ledgers(
|
||||
]
|
||||
if observed_camera_uncertainty != expected_camera_uncertainty:
|
||||
mismatch["camera_uncertainty"] += 1
|
||||
if [item.to_dict() for item in delivery.threats] != _array(
|
||||
threat,
|
||||
"assessments",
|
||||
):
|
||||
observed_assessments = _assessment_index(
|
||||
[item.to_dict() for item in delivery.threats],
|
||||
"graph threat assessments",
|
||||
)
|
||||
expected_assessments = _assessment_index(
|
||||
_array(threat, "assessments"),
|
||||
"accepted threat assessments",
|
||||
)
|
||||
if observed_assessments != expected_assessments:
|
||||
mismatch["threat_assessments"] += 1
|
||||
compared += 1
|
||||
if temporal_stream.readline() or threat_stream.readline():
|
||||
@@ -204,6 +209,19 @@ def _boolean(document: dict[str, object], key: str) -> bool:
|
||||
return value
|
||||
|
||||
|
||||
def _assessment_index(
|
||||
rows: list[dict[str, object]],
|
||||
label: str,
|
||||
) -> dict[str, dict[str, object]]:
|
||||
indexed: dict[str, dict[str, object]] = {}
|
||||
for row in rows:
|
||||
component_id = _string(row, "component_id")
|
||||
if component_id in indexed:
|
||||
raise ReferenceGraphParityError(f"{label} contains a duplicate component")
|
||||
indexed[component_id] = row
|
||||
return indexed
|
||||
|
||||
|
||||
__all__ = [
|
||||
"REFERENCE_GRAPH_PARITY_SCHEMA",
|
||||
"ReferenceGraphParityError",
|
||||
|
||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
import shutil
|
||||
import uuid
|
||||
@@ -13,6 +14,7 @@ from pathlib import Path
|
||||
from typing import Final
|
||||
|
||||
from .graph_contracts import GraphRunMode, GraphRunResultV2, GraphState, TerminalOutcomeType
|
||||
from .reference_graph_identity import ReferenceGraphRuntimeIdentity
|
||||
from .reference_graph_parity import ReferenceGraphParityReport
|
||||
|
||||
REFERENCE_GRAPH_RESULT_PREFIX: Final = "m47-reference-graph-"
|
||||
@@ -39,9 +41,15 @@ def seal_reference_graph_result(
|
||||
output_root: Path,
|
||||
expected_frames: int,
|
||||
parity: ReferenceGraphParityReport,
|
||||
runtime_identity: ReferenceGraphRuntimeIdentity,
|
||||
execution_elapsed_seconds: float,
|
||||
) -> SealedReferenceGraphResult:
|
||||
if expected_frames < 1:
|
||||
raise ReferenceGraphResultError("expected frame count must be positive")
|
||||
if not math.isfinite(execution_elapsed_seconds) or execution_elapsed_seconds < 0.0:
|
||||
raise ReferenceGraphResultError("execution elapsed seconds must be finite and non-negative")
|
||||
if runtime_identity.graph_id != result.graph_id:
|
||||
raise ReferenceGraphResultError("runtime and graph result identities differ")
|
||||
outcomes = Counter(item.outcome.value for item in result.terminal_outcomes)
|
||||
gates = {
|
||||
"lossless_replay_mode": result.run_mode is GraphRunMode.LOSSLESS_REPLAY,
|
||||
@@ -58,6 +66,7 @@ def seal_reference_graph_result(
|
||||
"accepted_m45r_m46_parity": parity.accepted,
|
||||
}
|
||||
accepted = all(gates.values())
|
||||
runtime = runtime_identity.to_dict()
|
||||
report: dict[str, object] = {
|
||||
"schema_version": REFERENCE_GRAPH_REPORT_SCHEMA,
|
||||
"graph_id": result.graph_id,
|
||||
@@ -73,6 +82,10 @@ def seal_reference_graph_result(
|
||||
"accepted_parity": parity.to_dict(),
|
||||
"gates": gates,
|
||||
"accepted": accepted,
|
||||
"execution": {
|
||||
"elapsed_seconds": execution_elapsed_seconds,
|
||||
"runtime_identity": runtime,
|
||||
},
|
||||
"authority": {
|
||||
"physical_live": False,
|
||||
"commands_enabled": False,
|
||||
@@ -90,6 +103,7 @@ def seal_reference_graph_result(
|
||||
frames_path = staging / "frames.jsonl"
|
||||
outcomes_path = staging / "outcomes.jsonl"
|
||||
report_path = staging / "report.json"
|
||||
runtime_path = staging / "runtime.json"
|
||||
_write_json_lines(
|
||||
frames_path,
|
||||
tuple(delivery.canonical_dict() for delivery in result.deliveries),
|
||||
@@ -99,18 +113,20 @@ def seal_reference_graph_result(
|
||||
tuple(outcome.to_dict() for outcome in result.terminal_outcomes),
|
||||
)
|
||||
_write_json(report_path, report)
|
||||
_write_json(runtime_path, runtime)
|
||||
file_rows = {
|
||||
name: {
|
||||
"sha256": _sha256_file(staging / name),
|
||||
"bytes": (staging / name).stat().st_size,
|
||||
}
|
||||
for name in ("frames.jsonl", "outcomes.jsonl", "report.json")
|
||||
for name in ("frames.jsonl", "outcomes.jsonl", "report.json", "runtime.json")
|
||||
}
|
||||
identity: dict[str, object] = {
|
||||
"graph_id": result.graph_id,
|
||||
"source_profile_id": result.source_profile_id,
|
||||
"run_mode": result.run_mode.value,
|
||||
"canonical_payload_sha256": result.canonical_payload_sha256,
|
||||
"runtime": runtime,
|
||||
"files": file_rows,
|
||||
}
|
||||
identity_sha256 = hashlib.sha256(_canonical_json(identity)).hexdigest()
|
||||
@@ -137,6 +153,103 @@ def seal_reference_graph_result(
|
||||
)
|
||||
|
||||
|
||||
def read_reference_graph_result(result_root: Path) -> SealedReferenceGraphResult:
|
||||
candidate = result_root.expanduser().absolute()
|
||||
if candidate.is_symlink():
|
||||
raise ReferenceGraphResultError("reference graph result root is invalid")
|
||||
resolved = candidate.resolve(strict=True)
|
||||
if (
|
||||
resolved.is_symlink()
|
||||
or not resolved.is_dir()
|
||||
or not resolved.name.startswith(REFERENCE_GRAPH_RESULT_PREFIX)
|
||||
):
|
||||
raise ReferenceGraphResultError("reference graph result root is invalid")
|
||||
manifest = _read_json(resolved / "manifest.json")
|
||||
expected_manifest_keys = {
|
||||
"schema_version",
|
||||
"result_id",
|
||||
"identity_sha256",
|
||||
"graph_id",
|
||||
"source_profile_id",
|
||||
"run_mode",
|
||||
"canonical_payload_sha256",
|
||||
"runtime",
|
||||
"files",
|
||||
"accepted",
|
||||
}
|
||||
if set(manifest) != expected_manifest_keys:
|
||||
raise ReferenceGraphResultError("reference graph manifest fields changed")
|
||||
if manifest.get("schema_version") != REFERENCE_GRAPH_MANIFEST_SCHEMA:
|
||||
raise ReferenceGraphResultError("reference graph manifest schema changed")
|
||||
if not isinstance(manifest.get("accepted"), bool):
|
||||
raise ReferenceGraphResultError("reference graph acceptance type changed")
|
||||
identity = {
|
||||
key: manifest[key]
|
||||
for key in (
|
||||
"graph_id",
|
||||
"source_profile_id",
|
||||
"run_mode",
|
||||
"canonical_payload_sha256",
|
||||
"runtime",
|
||||
"files",
|
||||
)
|
||||
}
|
||||
identity_sha256 = hashlib.sha256(_canonical_json(identity)).hexdigest()
|
||||
if (
|
||||
manifest.get("result_id") != resolved.name
|
||||
or manifest.get("identity_sha256") != identity_sha256
|
||||
or resolved.name != f"{REFERENCE_GRAPH_RESULT_PREFIX}{identity_sha256}"
|
||||
):
|
||||
raise ReferenceGraphResultError("reference graph identity changed")
|
||||
runtime = ReferenceGraphRuntimeIdentity.from_dict(manifest.get("runtime"))
|
||||
files = manifest.get("files")
|
||||
if not isinstance(files, dict) or set(files) != {
|
||||
"frames.jsonl",
|
||||
"outcomes.jsonl",
|
||||
"report.json",
|
||||
"runtime.json",
|
||||
}:
|
||||
raise ReferenceGraphResultError("reference graph artifact inventory changed")
|
||||
for name, descriptor_value in files.items():
|
||||
if not isinstance(descriptor_value, dict) or set(descriptor_value) != {
|
||||
"sha256",
|
||||
"bytes",
|
||||
}:
|
||||
raise ReferenceGraphResultError("reference graph artifact proof changed")
|
||||
path = _safe_result_file(resolved, name)
|
||||
if descriptor_value.get("bytes") != path.stat().st_size or descriptor_value.get(
|
||||
"sha256"
|
||||
) != _sha256_file(path):
|
||||
raise ReferenceGraphResultError("reference graph artifact proof does not match")
|
||||
runtime_document = _read_json(resolved / "runtime.json")
|
||||
if runtime_document != runtime.to_dict():
|
||||
raise ReferenceGraphResultError("reference graph runtime artifact changed")
|
||||
report = _read_json(resolved / "report.json")
|
||||
if (
|
||||
report.get("schema_version") != REFERENCE_GRAPH_REPORT_SCHEMA
|
||||
or report.get("graph_id") != manifest.get("graph_id")
|
||||
or report.get("canonical_payload_sha256") != manifest.get("canonical_payload_sha256")
|
||||
or not isinstance(report.get("execution"), dict)
|
||||
or report["execution"].get("runtime_identity") != runtime_document
|
||||
or report.get("accepted") is not manifest.get("accepted")
|
||||
):
|
||||
raise ReferenceGraphResultError("reference graph report changed")
|
||||
gates = report.get("gates")
|
||||
if (
|
||||
not isinstance(gates, dict)
|
||||
or not gates
|
||||
or manifest.get("accepted") is not all(value is True for value in gates.values())
|
||||
):
|
||||
raise ReferenceGraphResultError("reference graph acceptance proof changed")
|
||||
return SealedReferenceGraphResult(
|
||||
result_id=resolved.name,
|
||||
result_root=resolved,
|
||||
accepted=bool(manifest["accepted"]),
|
||||
report=report,
|
||||
manifest=manifest,
|
||||
)
|
||||
|
||||
|
||||
def _write_json_lines(path: Path, rows: tuple[dict[str, object], ...]) -> None:
|
||||
with path.open("wb") as handle:
|
||||
for row in rows:
|
||||
@@ -152,20 +265,34 @@ def _write_json(path: Path, document: dict[str, object]) -> None:
|
||||
os.fsync(handle.fileno())
|
||||
|
||||
|
||||
def _read_json(path: Path) -> dict[str, object]:
|
||||
if path.is_symlink() or not path.is_file() or path.stat().st_size > 1024 * 1024:
|
||||
raise ReferenceGraphResultError("reference graph JSON artifact is invalid")
|
||||
try:
|
||||
document = json.loads(path.read_text("utf-8"))
|
||||
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as error:
|
||||
raise ReferenceGraphResultError("reference graph JSON artifact is unreadable") from error
|
||||
if not isinstance(document, dict) or any(not isinstance(key, str) for key in document):
|
||||
raise ReferenceGraphResultError("reference graph JSON artifact must be an object")
|
||||
return document
|
||||
|
||||
|
||||
def _safe_result_file(root: Path, name: str) -> Path:
|
||||
path = root / name
|
||||
if path.is_symlink():
|
||||
raise ReferenceGraphResultError("reference graph artifact must not be a symlink")
|
||||
resolved = path.resolve(strict=True)
|
||||
if resolved.parent != root or not resolved.is_file():
|
||||
raise ReferenceGraphResultError("reference graph artifact escaped its result")
|
||||
return resolved
|
||||
|
||||
|
||||
def _publish_immutable(staging: Path, target: Path) -> None:
|
||||
if target.exists():
|
||||
if target.is_symlink() or not target.is_dir():
|
||||
raise ReferenceGraphResultError("immutable result target is not a real directory")
|
||||
expected = {
|
||||
path.name: _sha256_file(path)
|
||||
for path in staging.iterdir()
|
||||
if path.is_file()
|
||||
}
|
||||
observed = {
|
||||
path.name: _sha256_file(path)
|
||||
for path in target.iterdir()
|
||||
if path.is_file()
|
||||
}
|
||||
expected = {path.name: _sha256_file(path) for path in staging.iterdir() if path.is_file()}
|
||||
observed = {path.name: _sha256_file(path) for path in target.iterdir() if path.is_file()}
|
||||
if expected != observed:
|
||||
raise ReferenceGraphResultError("immutable result identity collision")
|
||||
shutil.rmtree(staging)
|
||||
@@ -191,5 +318,6 @@ __all__ = [
|
||||
"REFERENCE_GRAPH_RESULT_PREFIX",
|
||||
"ReferenceGraphResultError",
|
||||
"SealedReferenceGraphResult",
|
||||
"read_reference_graph_result",
|
||||
"seal_reference_graph_result",
|
||||
]
|
||||
|
||||
@@ -98,6 +98,10 @@ def build_m4_threat_replay_router(
|
||||
"access": "read-only-replay-simulated",
|
||||
}
|
||||
|
||||
@router.get("/results/{result_id}")
|
||||
def get_result(result_id: str) -> dict[str, object]:
|
||||
return _project_result(result(result_id))
|
||||
|
||||
@router.get("/results/{result_id}/visuals")
|
||||
def list_visuals(result_id: str) -> dict[str, object]:
|
||||
frozen = result(result_id)
|
||||
|
||||
@@ -127,7 +127,7 @@ def test_product_registry_declares_every_advanced_evidence_source() -> None:
|
||||
repository_root / "config" / "laboratories"
|
||||
)
|
||||
|
||||
assert len(registry.definitions) == 33
|
||||
assert len(registry.definitions) == 34
|
||||
assert {item.work_id for item in registry.definitions} >= {
|
||||
"e31-source-binding",
|
||||
"e46j-raw-fisheye-realtime",
|
||||
@@ -138,4 +138,5 @@ def test_product_registry_declares_every_advanced_evidence_source() -> None:
|
||||
"l33-camera-first-detector-review",
|
||||
"l34f-adjudicated-reference",
|
||||
"m4-replay-threat",
|
||||
"m47-reference-graph-shadow",
|
||||
}
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
|
||||
from k1link.laboratory.evidence_registry import LaboratoryEvidenceRegistry
|
||||
from k1link.laboratory.evidence_report import (
|
||||
LaboratoryEvidenceReportService,
|
||||
verify_laboratory_evidence_result,
|
||||
)
|
||||
from k1link.laboratory.m47_reference_graph import (
|
||||
publish_m47_reference_graph_lab,
|
||||
read_m47_reference_graph_lab,
|
||||
)
|
||||
|
||||
|
||||
def _write_json(path: Path, value: object) -> None:
|
||||
path.write_text(json.dumps(value, sort_keys=True, separators=(",", ":")) + "\n")
|
||||
|
||||
|
||||
def test_m47_lab_binds_worker_graph_to_exact_visual_replay(
|
||||
tmp_path: Path,
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
graph_root = tmp_path / "graph"
|
||||
visual_root = tmp_path / "visual"
|
||||
graph_root.mkdir()
|
||||
visual_root.mkdir()
|
||||
runtime = {
|
||||
"schema_version": "missioncore.reference-graph-runtime-identity/v3",
|
||||
"worker_id": "worker-006",
|
||||
"worker_node": "DESKTOP-OPJ8J04",
|
||||
"worker_container_id": "a" * 64,
|
||||
"worker_image_id": "sha256:" + "b" * 64,
|
||||
"isolated_triton_container_id": "c" * 64,
|
||||
"isolated_triton_image_id": "sha256:" + "b" * 64,
|
||||
"historical_worker_container_id": "d" * 64,
|
||||
"historical_worker_running": False,
|
||||
"historical_triton_container_id": "e" * 64,
|
||||
"historical_triton_running": False,
|
||||
"artifact_sha256": "f" * 64,
|
||||
"patch_id": "mission-core-m47-test",
|
||||
"code_revision": "1" * 40,
|
||||
"graph_id": "reference-perception-graph/v2",
|
||||
"started_at_utc": "2026-08-23T18:00:16.061Z",
|
||||
"source_mount_read_only": True,
|
||||
"isolated_model_service": True,
|
||||
"public_worker_port_added": False,
|
||||
"commands_enabled": False,
|
||||
"actuation_allowed": False,
|
||||
}
|
||||
mismatch_counts = {
|
||||
"source_binding": 0,
|
||||
"current": 0,
|
||||
"rolling_retained": 0,
|
||||
"held": 0,
|
||||
"expired": 0,
|
||||
"camera_uncertainty": 0,
|
||||
"threat_assessments": 0,
|
||||
}
|
||||
graph_manifest = {
|
||||
"schema_version": "missioncore.reference-perception-graph-manifest/v1",
|
||||
"graph_id": "reference-perception-graph/v2",
|
||||
"source_profile_id": "m4-ravnoves00-recorded-realtime/v1",
|
||||
"run_mode": "lossless-replay",
|
||||
"canonical_payload_sha256": "2" * 64,
|
||||
"runtime": runtime,
|
||||
}
|
||||
graph_report = {
|
||||
"schema_version": "missioncore.reference-perception-graph-report/v1",
|
||||
"admitted_frames": 4489,
|
||||
"terminal_outcomes": {"delivered": 4489},
|
||||
"queue_high_watermarks": {
|
||||
"detector": 2,
|
||||
"geometry": 2,
|
||||
"temporal": 2,
|
||||
"rolling": 2,
|
||||
"threat": 2,
|
||||
},
|
||||
"accepted_parity": {
|
||||
"schema_version": "missioncore.reference-perception-graph-parity/v1",
|
||||
"accepted": True,
|
||||
"expected_frames": 4489,
|
||||
"compared_frames": 4489,
|
||||
"temporal_frames_sha256": "3" * 64,
|
||||
"threat_frames_sha256": "4" * 64,
|
||||
"mismatch_counts": mismatch_counts,
|
||||
},
|
||||
"execution": {"elapsed_seconds": 198.0, "runtime_identity": runtime},
|
||||
"gates": {"accepted": True},
|
||||
}
|
||||
visual_manifest = {
|
||||
"schema_version": "missioncore.perception-threat-replay-result/v2",
|
||||
"identity": {
|
||||
"source_session_id": "20260720T065719Z_viewer_live",
|
||||
"frames_sha256": "4" * 64,
|
||||
"temporal_frames_sha256": "3" * 64,
|
||||
},
|
||||
}
|
||||
visual_report = {"schema_version": "missioncore.perception-threat-replay-report/v2"}
|
||||
for root, documents in (
|
||||
(
|
||||
graph_root,
|
||||
{
|
||||
"manifest.json": graph_manifest,
|
||||
"report.json": graph_report,
|
||||
"runtime.json": runtime,
|
||||
},
|
||||
),
|
||||
(
|
||||
visual_root,
|
||||
{"manifest.json": visual_manifest, "report.json": visual_report},
|
||||
),
|
||||
):
|
||||
for name, document in documents.items():
|
||||
_write_json(root / name, document)
|
||||
graph = SimpleNamespace(
|
||||
accepted=True,
|
||||
result_id="m47-reference-graph-" + "5" * 64,
|
||||
result_root=graph_root,
|
||||
manifest=graph_manifest,
|
||||
report=graph_report,
|
||||
)
|
||||
visual = SimpleNamespace(
|
||||
accepted=True,
|
||||
result_id="m4-threat-replay-" + "6" * 64,
|
||||
result_root=visual_root,
|
||||
manifest=visual_manifest,
|
||||
report=visual_report,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"k1link.laboratory.m47_reference_graph.read_reference_graph_result",
|
||||
lambda _: graph,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"k1link.laboratory.m47_reference_graph.read_threat_replay_result",
|
||||
lambda _: visual,
|
||||
)
|
||||
|
||||
runtime_root = tmp_path / "runtime"
|
||||
result = publish_m47_reference_graph_lab(
|
||||
graph_result_root=graph_root,
|
||||
visual_result_root=visual_root,
|
||||
output_root=runtime_root / "m47" / "reference-graph-labs",
|
||||
)
|
||||
|
||||
assert read_m47_reference_graph_lab(result.result_root) == result
|
||||
assert result.report["visual_evidence"]["linked_result_id"] == visual.result_id
|
||||
assert result.report["execution"]["worker_id"] == "worker-006"
|
||||
assert result.report["metrics"]["parity_mismatch_counts"] == mismatch_counts
|
||||
repository_root = Path(__file__).resolve().parents[1]
|
||||
registry = LaboratoryEvidenceRegistry.from_directory(
|
||||
repository_root / "config" / "laboratories"
|
||||
)
|
||||
definition = next(
|
||||
item for item in registry.definitions if item.work_id == "m47-reference-graph-shadow"
|
||||
)
|
||||
proof = verify_laboratory_evidence_result(definition, result.result_root)
|
||||
assert proof["artifact_count"] == 6
|
||||
projected = LaboratoryEvidenceReportService(registry, lambda: runtime_root).read(
|
||||
definition.work_id,
|
||||
result.result_id,
|
||||
)
|
||||
assert projected["raw_report"]["schema_version"] == (
|
||||
"missioncore.reference-perception-graph-lab-report/v1"
|
||||
)
|
||||
@@ -39,25 +39,23 @@ def test_m47_worker_artifact_is_deterministic_and_self_contained(tmp_path: Path)
|
||||
|
||||
assert first_bytes == second_bytes
|
||||
assert first["sha256"] == _sha256(first_bytes)
|
||||
assert first["transition"] == "m47-canonical-graph-shadow-v1"
|
||||
assert first["transition"] == "m47-canonical-graph-isolated-shadow-v1"
|
||||
with tarfile.open(first["artifact"], "r:gz") as archive:
|
||||
regular = _regular_files(archive)
|
||||
payload_names = sorted(
|
||||
name.removeprefix("payload/")
|
||||
for name in regular
|
||||
if name.startswith("payload/")
|
||||
name.removeprefix("payload/") for name in regular if name.startswith("payload/")
|
||||
)
|
||||
assert payload_names == first["payload_files"]
|
||||
assert regular["files.txt"].decode().splitlines() == first["payload_files"]
|
||||
assert _sha256(regular[f"payload/{BUILDER.WHEEL_NAME}"]) == first["wheel_sha256"]
|
||||
assert regular[f"payload/{BUILDER.RUNNER.name}"] == BUILDER.RUNNER.read_bytes()
|
||||
for relative in BUILDER.CONFIG_PATHS:
|
||||
assert regular[f"payload/{relative.name}"] == (
|
||||
BUILDER.REPOSITORY_ROOT / relative
|
||||
).read_bytes()
|
||||
assert (
|
||||
regular[f"payload/{relative.name}"] == (BUILDER.REPOSITORY_ROOT / relative).read_bytes()
|
||||
)
|
||||
|
||||
|
||||
def test_m47_descriptor_preserves_predecessor_and_separates_readiness() -> None:
|
||||
def test_m47_descriptor_preserves_nonparticipants_and_separates_readiness() -> None:
|
||||
descriptor = json.loads(
|
||||
BUILDER.render_descriptor(
|
||||
"mission-core-m47-graph-shadow-unit-002",
|
||||
@@ -66,10 +64,14 @@ def test_m47_descriptor_preserves_predecessor_and_separates_readiness() -> None:
|
||||
)
|
||||
)
|
||||
|
||||
assert descriptor["schema_version"] == "nodedc.mission-core-worker.shadow-release/v2"
|
||||
assert descriptor["transition"] == "m47-canonical-graph-shadow-v1"
|
||||
assert descriptor["schema_version"] == "nodedc.mission-core-worker.shadow-release/v3"
|
||||
assert descriptor["transition"] == "m47-canonical-graph-isolated-shadow-v1"
|
||||
assert descriptor["boundary"]["external_deploy_registry"] is False
|
||||
assert descriptor["container"]["public_ports"] is False
|
||||
assert descriptor["container"]["triton_name"] == ("ndc-mission-core-m47-triton-shadow")
|
||||
assert descriptor["container"]["model_repository_host_path"] == (
|
||||
"D:\\NDC_MISSIONCORE\\runtime\\models"
|
||||
)
|
||||
assert descriptor["inputs"]["local_surface"]["sha256"] == (
|
||||
"f57eb2485b6cef47f2a97a2d9ff1aa9fd9265fe1eb69cd5852d12f39e13b8bc6"
|
||||
)
|
||||
@@ -84,10 +86,11 @@ def test_m47_descriptor_preserves_predecessor_and_separates_readiness() -> None:
|
||||
)
|
||||
assert descriptor["rollback"] == {
|
||||
"durable_worker_action": "none",
|
||||
"historical_triton_action": "none",
|
||||
"preserve_failed_evidence": True,
|
||||
"remove_candidate_container": True,
|
||||
"remove_candidate_graph_container": True,
|
||||
"remove_candidate_triton_container": True,
|
||||
"remove_unaccepted_release": True,
|
||||
"triton_action": "none",
|
||||
}
|
||||
assert descriptor["acceptance"] == {
|
||||
"run_mode": "lossless-replay",
|
||||
@@ -104,9 +107,7 @@ def test_m47_descriptor_preserves_predecessor_and_separates_readiness() -> None:
|
||||
"terminal_accounting_required": True,
|
||||
"actuation_allowed": False,
|
||||
}
|
||||
assert set(descriptor["release"]["configs"]) == {
|
||||
path.name for path in BUILDER.CONFIG_PATHS
|
||||
}
|
||||
assert set(descriptor["release"]["configs"]) == {path.name for path in BUILDER.CONFIG_PATHS}
|
||||
serialized = json.dumps(descriptor).encode()
|
||||
assert b"PRIVATE KEY" not in serialized
|
||||
assert b"password=" not in serialized.lower()
|
||||
@@ -120,7 +121,13 @@ def test_m47_runner_calls_only_the_canonical_graph_entrypoint() -> None:
|
||||
assert '"--mode", $descriptor.acceptance.run_mode' in runner
|
||||
assert '"--temporal-parity-frames"' in runner
|
||||
assert '"--threat-parity-frames"' in runner
|
||||
assert '"--runtime-identity", "/run/mission-core/runtime-identity.json"' in runner
|
||||
assert 'schema_version = "missioncore.reference-graph-runtime-identity/v3"' in runner
|
||||
assert "patch_id = $descriptor.patch_id" in runner
|
||||
assert 'Write-Output "PROVIDER_READINESS=accepted"' in runner
|
||||
assert 'Write-Output "GRAPH_READINESS=accepted"' in runner
|
||||
assert 'Write-Output "DURABLE_WORKER_ACTION=none"' in runner
|
||||
assert 'Write-Output "TRITON_ACTION=none"' in runner
|
||||
assert 'Write-Output "HISTORICAL_TRITON_ACTION=none"' in runner
|
||||
assert 'Write-Output "ISOLATED_TRITON_ACTION=removed"' in runner
|
||||
assert "function Test-NoPublishedPorts" in runner
|
||||
assert "PortBindings.PSObject.Properties" in runner
|
||||
|
||||
@@ -100,11 +100,13 @@ def test_threat_result_is_content_bound_and_visual_evidence_is_complete() -> Non
|
||||
|
||||
def test_m4_6_lab_api_projects_report_and_exact_visual_frame() -> None:
|
||||
list_results = _endpoint("/api/v1/laboratory/m4-threat/results")
|
||||
get_result = _endpoint("/api/v1/laboratory/m4-threat/results/{result_id}")
|
||||
list_visuals = _endpoint("/api/v1/laboratory/m4-threat/results/{result_id}/visuals")
|
||||
get_visual = _endpoint("/api/v1/laboratory/m4-threat/results/{result_id}/visuals/{ordinal}")
|
||||
|
||||
catalog = list_results(limit=1)
|
||||
assert catalog["items"][0]["result_id"] == RESULT_ID
|
||||
assert get_result(RESULT_ID) == catalog["items"][0]
|
||||
assert catalog["items"][0]["authority"] == "replay-simulated"
|
||||
visuals = list_visuals(RESULT_ID)
|
||||
assert len(visuals["items"]) == 32
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
from collections.abc import Iterator
|
||||
from dataclasses import replace
|
||||
@@ -62,6 +64,23 @@ from k1link.perception.recorded_source import (
|
||||
)
|
||||
|
||||
|
||||
def test_graph_import_does_not_initialize_legacy_compute_dependencies() -> None:
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
"-c",
|
||||
(
|
||||
"import sys; import k1link.perception.graph; "
|
||||
"assert 'k1link.compute' not in sys.modules"
|
||||
),
|
||||
],
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
assert result.returncode == 0, result.stderr
|
||||
|
||||
|
||||
def _status(available: bool = True) -> ModalityStatus:
|
||||
return ModalityStatus(
|
||||
available=available,
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from k1link.perception.contracts import LocalObstacleMap, SourceAccounting
|
||||
from k1link.perception.contracts import LocalObstacleMap, SourceAccounting, ThreatAssessment
|
||||
from k1link.perception.graph_contracts import (
|
||||
REFERENCE_GRAPH_ID_V2,
|
||||
DeliveredFrame,
|
||||
@@ -18,7 +18,7 @@ from k1link.perception.reference_graph_parity import (
|
||||
)
|
||||
|
||||
|
||||
def _result():
|
||||
def _result(threats: tuple[ThreatAssessment, ...] = ()):
|
||||
obstacle_map = LocalObstacleMap(
|
||||
source_id="RAVNOVES00",
|
||||
session_id="20260720T065719Z_viewer_live",
|
||||
@@ -48,7 +48,7 @@ def _result():
|
||||
reason="object-payload-delivered",
|
||||
),
|
||||
),
|
||||
deliveries=(DeliveredFrame(0, obstacle_map, ()),),
|
||||
deliveries=(DeliveredFrame(0, obstacle_map, threats),),
|
||||
queue_high_watermarks=(("detector", 1),),
|
||||
)
|
||||
|
||||
@@ -119,3 +119,43 @@ def test_reference_graph_parity_reports_threat_drift(tmp_path: Path) -> None:
|
||||
|
||||
assert report.accepted is False
|
||||
assert dict(report.mismatch_counts)["threat_assessments"] == 1
|
||||
|
||||
|
||||
def test_reference_graph_parity_treats_assessment_order_as_nonsemantic(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
first = {
|
||||
"schema_version": "missioncore.threat-assessment/v1",
|
||||
"assessment_id": "threat-first",
|
||||
"component_id": "component-first",
|
||||
"rig_profile_id": "rig-test",
|
||||
"corridor_profile_id": "corridor-test",
|
||||
"qualification": "unqualified",
|
||||
"relative_speed_mps": None,
|
||||
"closest_approach_m": None,
|
||||
"ttc_seconds": None,
|
||||
"corridor_intersection": "unknown",
|
||||
"decision": "unknown",
|
||||
"reason_codes": ["test-unavailable"],
|
||||
"authority": "replay-simulated",
|
||||
"physical_collision_accepted": False,
|
||||
"actuation_allowed": False,
|
||||
}
|
||||
second = {
|
||||
**first,
|
||||
"assessment_id": "threat-second",
|
||||
"component_id": "component-second",
|
||||
}
|
||||
temporal, threat = _write_ledgers(
|
||||
tmp_path,
|
||||
threat_assessments=[second, first],
|
||||
)
|
||||
|
||||
report = compare_reference_graph_to_accepted_ledgers(
|
||||
_result((ThreatAssessment.from_dict(first), ThreatAssessment.from_dict(second))),
|
||||
temporal_frames_path=temporal,
|
||||
threat_frames_path=threat,
|
||||
expected_frames=1,
|
||||
)
|
||||
|
||||
assert report.accepted is True
|
||||
|
||||
@@ -14,12 +14,44 @@ from k1link.perception.graph_contracts import (
|
||||
build_graph_run_result_v2,
|
||||
)
|
||||
from k1link.perception.providers import ReferencePerceptionGraphConfigV2
|
||||
from k1link.perception.reference_graph_identity import ReferenceGraphRuntimeIdentity
|
||||
from k1link.perception.reference_graph_parity import ReferenceGraphParityReport
|
||||
from k1link.perception.reference_graph_result import seal_reference_graph_result
|
||||
from k1link.perception.reference_graph_result import (
|
||||
read_reference_graph_result,
|
||||
seal_reference_graph_result,
|
||||
)
|
||||
|
||||
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def _runtime() -> ReferenceGraphRuntimeIdentity:
|
||||
return ReferenceGraphRuntimeIdentity.from_dict(
|
||||
{
|
||||
"schema_version": "missioncore.reference-graph-runtime-identity/v3",
|
||||
"worker_id": "worker-006",
|
||||
"worker_node": "DESKTOP-OPJ8J04",
|
||||
"worker_container_id": "c" * 64,
|
||||
"worker_image_id": "sha256:" + "d" * 64,
|
||||
"isolated_triton_container_id": "e" * 64,
|
||||
"isolated_triton_image_id": "sha256:" + "d" * 64,
|
||||
"historical_worker_container_id": "f" * 64,
|
||||
"historical_worker_running": False,
|
||||
"historical_triton_container_id": "a" * 64,
|
||||
"historical_triton_running": False,
|
||||
"artifact_sha256": "b" * 64,
|
||||
"patch_id": "mission-core-m47-test",
|
||||
"code_revision": "1" * 40,
|
||||
"graph_id": "reference-perception-graph/v2",
|
||||
"started_at_utc": "2026-08-23T12:00:00.000Z",
|
||||
"source_mount_read_only": True,
|
||||
"isolated_model_service": True,
|
||||
"public_worker_port_added": False,
|
||||
"commands_enabled": False,
|
||||
"actuation_allowed": False,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _parity(accepted: bool = True) -> ReferenceGraphParityReport:
|
||||
return ReferenceGraphParityReport(
|
||||
expected_frames=1,
|
||||
@@ -110,12 +142,16 @@ def test_reference_graph_result_is_content_addressed_and_reproducible(tmp_path:
|
||||
output_root=tmp_path / "one",
|
||||
expected_frames=1,
|
||||
parity=_parity(),
|
||||
runtime_identity=_runtime(),
|
||||
execution_elapsed_seconds=1.25,
|
||||
)
|
||||
second = seal_reference_graph_result(
|
||||
_result(),
|
||||
output_root=tmp_path / "two",
|
||||
expected_frames=1,
|
||||
parity=_parity(),
|
||||
runtime_identity=_runtime(),
|
||||
execution_elapsed_seconds=1.25,
|
||||
)
|
||||
|
||||
assert first.accepted is True
|
||||
@@ -134,9 +170,14 @@ def test_reference_graph_result_is_content_addressed_and_reproducible(tmp_path:
|
||||
"no_unavailable_frames": True,
|
||||
"accepted_m45r_m46_parity": True,
|
||||
}
|
||||
assert {
|
||||
path.name: path.read_bytes() for path in first.result_root.iterdir()
|
||||
} == {path.name: path.read_bytes() for path in second.result_root.iterdir()}
|
||||
assert {path.name: path.read_bytes() for path in first.result_root.iterdir()} == {
|
||||
path.name: path.read_bytes() for path in second.result_root.iterdir()
|
||||
}
|
||||
runtime = json.loads((first.result_root / "runtime.json").read_text("utf-8"))
|
||||
assert runtime == _runtime().to_dict()
|
||||
assert first.manifest["runtime"] == runtime
|
||||
assert first.manifest["files"]["runtime.json"]["sha256"]
|
||||
assert read_reference_graph_result(first.result_root) == first
|
||||
|
||||
|
||||
def test_reference_graph_result_fails_closed_on_supersession(tmp_path: Path) -> None:
|
||||
@@ -145,6 +186,8 @@ def test_reference_graph_result_fails_closed_on_supersession(tmp_path: Path) ->
|
||||
output_root=tmp_path,
|
||||
expected_frames=1,
|
||||
parity=_parity(),
|
||||
runtime_identity=_runtime(),
|
||||
execution_elapsed_seconds=1.25,
|
||||
)
|
||||
|
||||
assert sealed.accepted is False
|
||||
|
||||
Reference in New Issue
Block a user