feat(lab): publish full TGS shadow evidence

This commit is contained in:
DCCONSTRUCTIONS
2026-08-26 23:07:06 +03:00
parent 40c850b167
commit d722b0f82b
22 changed files with 1508 additions and 28 deletions
@@ -47,6 +47,7 @@ import { fetchM48R3StaticOccupancyShadow } from "./m48r3StaticOccupancyShadow";
import { fetchM48SFixedClassDetectorResult } from "./m48sFixedClassDetector";
import { fetchM48TRiskQualityResult } from "./m48tRiskQuality";
import { fetchM49TgsFailClosedResult } from "./m49TgsFailClosed";
import { fetchM49TgsFullShadowResult } from "./m49TgsFullShadow";
export type AdvancedLaboratoryWorkId =
| "m48-object-centric-quality"
@@ -56,6 +57,7 @@ export type AdvancedLaboratoryWorkId =
| "m48s-fixed-class-detector"
| "m48t-risk-quality-temporal"
| "m49-tgs-fail-closed-evidence"
| "m49-tgs-full-shadow"
| "m47-reference-graph-shadow"
| "m4-replay-threat"
| "l3-pointpillars-visual-audit"
@@ -105,6 +107,7 @@ const WORK_IDS: readonly AdvancedLaboratoryWorkId[] = [
"m48s-fixed-class-detector",
"m48t-risk-quality-temporal",
"m49-tgs-fail-closed-evidence",
"m49-tgs-full-shadow",
"m47-reference-graph-shadow",
"m4-replay-threat",
"l3-pointpillars-visual-audit",
@@ -149,6 +152,7 @@ const RESULT_PREFIX: Readonly<Record<AdvancedLaboratoryWorkId, string>> = {
"m48s-fixed-class-detector": "m48s-fixed-class-detector-lab",
"m48t-risk-quality-temporal": "(?:m48t-risk-quality-temporal-lab|m48q-native-risk-quality-lab)",
"m49-tgs-fail-closed-evidence": "m49-tgs-fail-closed",
"m49-tgs-full-shadow": "m49-tgs-full-shadow",
"m47-reference-graph-shadow": "m47-reference-graph-lab",
"m4-replay-threat": "m4-threat-replay",
"l3-pointpillars-visual-audit": "l3-pointpillars-visual-audit",
@@ -201,6 +205,7 @@ export function emptyAdvancedLaboratoryResults(): AdvancedLaboratoryResults {
m48s: null,
m48t: null,
m49Tgs: null,
m49TgsFull: null,
m4Threat: null,
l3: null,
l31: null,
@@ -332,6 +337,7 @@ export function advancedLaboratoryResultAvailable(
: workId === "m48s-fixed-class-detector" ? results.m48s !== null
: workId === "m48t-risk-quality-temporal" ? results.m48t !== null
: workId === "m49-tgs-fail-closed-evidence" ? results.m49Tgs !== null
: workId === "m49-tgs-full-shadow" ? results.m49TgsFull !== null
: workId === "m47-reference-graph-shadow" ? results.m47Graph !== null
: workId === "m4-replay-threat" ? results.m4Threat !== null
: workId === "l3-pointpillars-visual-audit" ? results.l3 !== null
@@ -402,6 +408,9 @@ export async function fetchAdvancedLaboratoryResult(
} else if (workId === "m49-tgs-fail-closed-evidence") {
if (!resultId) throw new AdvancedLaboratoryContractError("M4.9 TGS LAB identity не выбрана.");
results.m49Tgs = await fetchM49TgsFailClosedResult(resultId, { fetcher, signal });
} else if (workId === "m49-tgs-full-shadow") {
if (!resultId) throw new AdvancedLaboratoryContractError("M4.9 full TGS shadow identity не выбрана.");
results.m49TgsFull = await fetchM49TgsFullShadowResult(resultId, { fetcher, signal });
} else if (workId === "m47-reference-graph-shadow") {
if (!resultId) {
throw new AdvancedLaboratoryContractError("M4.7 LAB identity не выбрана.");
@@ -41,6 +41,7 @@ import type { M48R3StaticOccupancyShadowResult } from "./m48r3StaticOccupancySha
import type { M48SFixedClassDetectorResult } from "./m48sFixedClassDetector";
import type { M48TRiskQualityResult } from "./m48tRiskQuality";
import type { M49TgsFailClosedResult } from "./m49TgsFailClosed";
import type { M49TgsFullShadowResult } from "./m49TgsFullShadow";
export interface AdvancedLaboratoryResults {
m47Graph: M47ReferenceGraphLabResult | null;
@@ -51,6 +52,7 @@ export interface AdvancedLaboratoryResults {
m48s: M48SFixedClassDetectorResult | null;
m48t: M48TRiskQualityResult | null;
m49Tgs: M49TgsFailClosedResult | null;
m49TgsFull: M49TgsFullShadowResult | null;
m4Threat: M4ThreatReplayResult | null;
l3: L3PointPillarsVisualAuditResult | null;
l31: L31PointPillarsRavnovesResult | null;
@@ -969,7 +969,7 @@ export async function fetchAdvancedLaboratoryResults({
return {
m47Graph: null, m48: null, m48SmallStatic: null, m48StaticOccupancy: null,
m48r3StaticOccupancy: null,
m48s: null, m48t: null, m49Tgs: null, m4Threat: null,
m48s: null, m48t: null, m49Tgs: null, m49TgsFull: null, m4Threat: null,
l3: null, l31: null, l32: null, l33: null,
e31,
e32,
@@ -0,0 +1,327 @@
import type { LaboratoryFetch } from "./advancedResults";
const RESULT_ID = /^m49-tgs-full-shadow-[a-f0-9]{64}$/;
export type M49TgsFullShadowStateCode = 0 | 1 | 2 | 3;
export interface M49TgsFullShadowResult {
resultId: string;
createdAtUtc: string;
source: {
sourcePackSha256: string;
linkedVisualResultId: string;
};
configuration: {
configSha256: string;
cellSizeM: number;
radiusM: number;
historySeconds: number;
};
execution: {
wrapperElapsedSeconds: number;
};
timeline: {
frameCount: 4489;
availableLidarFrameCount: 3928;
missingLidarFrameCount: 561;
durationSeconds: number;
effectiveFps: number;
};
pointAccounting: {
eligible: number;
ground: number;
nonground: number;
rejected: number;
unaccounted: 0;
};
performance: {
candidateTgsMs: { p50: number; p95: number; p99: number; max: number };
completionAgeMs: { p50: number; p95: number; p99: number; max: number };
capacityDropCount: number;
};
acceptance: {
representationComplete: true;
visualQualityAccepted: false;
integratedGraphPerformanceAccepted: false;
allFramesAccounted: boolean;
allEligiblePointsAccounted: boolean;
};
decision: {
state: string;
candidateRetained: boolean;
nextAction: string;
};
}
export interface M49TgsFullShadowSpatial {
resultId: string;
sourceSequence: number;
sourceFrameIndex: number;
sessionSeconds: number;
sampleAvailable: boolean;
costmap: {
cellSizeM: number;
radiusM: number;
centersXyM: readonly (readonly [number, number])[];
states: readonly M49TgsFullShadowStateCode[];
zBoundsM: readonly (readonly [number | null, number | null])[];
};
metrics: {
eligiblePointCount: number;
groundPointCount: number;
nongroundPointCount: number;
rejectedPointCount: number;
occupiedCellCount: number;
};
}
export interface M49TgsFullShadowSpatialChunk {
resultId: string;
start: number;
count: number;
frames: readonly M49TgsFullShadowSpatial[];
}
export class M49TgsFullShadowContractError extends Error {}
function objectValue(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new M49TgsFullShadowContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function text(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new M49TgsFullShadowContractError(`${label}: ожидалась строка.`);
}
return value;
}
function numberValue(value: unknown, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new M49TgsFullShadowContractError(`${label}: ожидалось число.`);
}
return value;
}
function integer(value: unknown, label: string): number {
const parsed = numberValue(value, label);
if (!Number.isSafeInteger(parsed) || parsed < 0) {
throw new M49TgsFullShadowContractError(`${label}: ожидалось целое число.`);
}
return parsed;
}
function booleanValue(value: unknown, label: string): boolean {
if (typeof value !== "boolean") {
throw new M49TgsFullShadowContractError(`${label}: ожидался boolean.`);
}
return value;
}
function exact<T extends string | boolean | number>(value: unknown, expected: T, label: string): T {
if (value !== expected) throw new M49TgsFullShadowContractError(`${label}: нарушен контракт.`);
return expected;
}
function timing(value: unknown, label: string) {
const row = objectValue(value, label);
return {
p50: numberValue(row.p50, `${label}.p50`),
p95: numberValue(row.p95, `${label}.p95`),
p99: numberValue(row.p99, `${label}.p99`),
max: numberValue(row.max, `${label}.max`),
};
}
export async function fetchM49TgsFullShadowResult(
id: string,
{ fetcher = fetch, signal }: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
): Promise<M49TgsFullShadowResult> {
if (!RESULT_ID.test(id)) throw new M49TgsFullShadowContractError("M49 full-shadow identity недопустима.");
const response = await fetcher(
`/api/v1/laboratory/m49/tgs-full-shadow/${encodeURIComponent(id)}`,
{ method: "GET", headers: { Accept: "application/json" }, signal },
);
if (!response.ok) throw new M49TgsFullShadowContractError(`M49 full shadow недоступен: HTTP ${response.status}.`);
const payload = objectValue(await response.json(), "M49 full shadow");
exact(payload.schema_version, "missioncore.m49-tgs-full-shadow-view/v1", "M49 full schema");
exact(payload.result_id, id, "M49 full result");
exact(payload.ground_truth, false, "M49 full ground truth");
exact(payload.access, "read-only", "M49 full access");
const source = objectValue(payload.source, "M49 full source");
const configuration = objectValue(payload.configuration, "M49 full configuration");
const execution = objectValue(payload.execution, "M49 full execution");
const timeline = objectValue(payload.timeline, "M49 full timeline");
const accounting = objectValue(payload.point_accounting, "M49 full accounting");
const performance = objectValue(payload.performance, "M49 full performance");
const acceptance = objectValue(payload.acceptance, "M49 full acceptance");
const decision = objectValue(payload.decision, "M49 full decision");
return {
resultId: text(payload.result_id, "M49 full result"),
createdAtUtc: text(payload.created_at_utc, "M49 full created"),
source: {
sourcePackSha256: text(source.source_pack_sha256, "M49 full source pack"),
linkedVisualResultId: text(source.linked_visual_result_id, "M49 full visual result"),
},
configuration: {
configSha256: text(configuration.config_sha256, "M49 full config"),
cellSizeM: numberValue(configuration.cell_size_m, "M49 full cell size"),
radiusM: numberValue(configuration.radius_m, "M49 full radius"),
historySeconds: numberValue(configuration.history_seconds, "M49 full history"),
},
execution: { wrapperElapsedSeconds: numberValue(execution.wrapper_elapsed_seconds, "M49 full wall") },
timeline: {
frameCount: exact(integer(timeline.frame_count, "M49 full frames"), 4489, "M49 full frames"),
availableLidarFrameCount: exact(integer(timeline.available_lidar_frame_count, "M49 full available"), 3928, "M49 full available"),
missingLidarFrameCount: exact(integer(timeline.missing_lidar_frame_count, "M49 full missing"), 561, "M49 full missing"),
durationSeconds: numberValue(timeline.duration_seconds, "M49 full duration"),
effectiveFps: numberValue(timeline.effective_fps, "M49 full fps"),
},
pointAccounting: {
eligible: integer(accounting.eligible, "M49 full eligible"),
ground: integer(accounting.ground, "M49 full ground"),
nonground: integer(accounting.nonground, "M49 full nonground"),
rejected: integer(accounting.rejected, "M49 full rejected"),
unaccounted: exact(integer(accounting.unaccounted, "M49 full unaccounted"), 0, "M49 full unaccounted"),
},
performance: {
candidateTgsMs: timing(performance.candidate_tgs_ms, "M49 full TGS"),
completionAgeMs: timing(performance.completion_age_ms, "M49 full completion"),
capacityDropCount: integer(performance.capacity_drop_count, "M49 full drops"),
},
acceptance: {
representationComplete: exact(acceptance.representation_complete, true, "M49 full representation"),
visualQualityAccepted: exact(acceptance.visual_quality_accepted, false, "M49 full visual"),
integratedGraphPerformanceAccepted: exact(acceptance.integrated_graph_performance_accepted, false, "M49 full integrated"),
allFramesAccounted: booleanValue(acceptance.all_frames_accounted, "M49 full frame accounting"),
allEligiblePointsAccounted: booleanValue(acceptance.all_eligible_points_accounted, "M49 full point accounting"),
},
decision: {
state: text(decision.state, "M49 full decision"),
candidateRetained: booleanValue(decision.candidate_retained, "M49 full retained"),
nextAction: text(decision.next_action, "M49 full next action"),
},
};
}
function pair(value: unknown, label: string): readonly [number, number] {
if (!Array.isArray(value) || value.length !== 2) {
throw new M49TgsFullShadowContractError(`${label}: неверная размерность.`);
}
return [numberValue(value[0], label), numberValue(value[1], label)];
}
function parseSpatial(
id: string,
sourceSequence: number,
payload: Record<string, unknown>,
sharedCostmap?: Record<string, unknown>,
): M49TgsFullShadowSpatial {
exact(payload.source_sequence, sourceSequence, "M49 full spatial sequence");
const costmap = sharedCostmap ?? objectValue(payload.costmap, "M49 full costmap");
const metrics = objectValue(payload.metrics, "M49 full frame metrics");
const centers = costmap.centers_xy_m;
const statesRaw = sharedCostmap ? payload.states : costmap.states;
const zBoundsRaw = sharedCostmap ? payload.z_bounds_m : costmap.z_bounds_m;
if (!Array.isArray(centers) || !Array.isArray(statesRaw) || !Array.isArray(zBoundsRaw)
|| centers.length !== 2244 || statesRaw.length !== 2244 || zBoundsRaw.length !== 2244) {
throw new M49TgsFullShadowContractError("M49 full costmap: неверная размерность.");
}
const states = statesRaw.map((value, index) => {
const parsed = integer(value, `M49 full state ${index}`);
if (parsed > 3) throw new M49TgsFullShadowContractError("M49 full state недопустим.");
return parsed as M49TgsFullShadowStateCode;
});
const zBounds = zBoundsRaw.map((value, index) => {
if (!Array.isArray(value) || value.length !== 2) {
throw new M49TgsFullShadowContractError(`M49 full z ${index}: неверная размерность.`);
}
return value.map((item) => item === null
? null
: numberValue(item, `M49 full z ${index}`)) as [number | null, number | null];
});
const sampleAvailable = booleanValue(payload.sample_available, "M49 full sample available");
if (!sampleAvailable && states.some((state) => state !== 0)) {
throw new M49TgsFullShadowContractError("M49 missing LiDAR frame не остался UNOBSERVED.");
}
return {
resultId: id,
sourceSequence,
sourceFrameIndex: integer(payload.source_frame_index, "M49 full source frame"),
sessionSeconds: numberValue(payload.session_seconds, "M49 full session seconds"),
sampleAvailable,
costmap: {
cellSizeM: numberValue(costmap.cell_size_m, "M49 full cell size"),
radiusM: numberValue(costmap.radius_m, "M49 full radius"),
centersXyM: centers.map((value, index) => pair(value, `M49 full center ${index}`)),
states,
zBoundsM: zBounds,
},
metrics: {
eligiblePointCount: integer(metrics.eligible_point_count, "M49 full eligible"),
groundPointCount: integer(metrics.ground_point_count, "M49 full ground"),
nongroundPointCount: integer(metrics.nonground_point_count, "M49 full nonground"),
rejectedPointCount: integer(metrics.rejected_point_count, "M49 full rejected"),
occupiedCellCount: integer(metrics.occupied_cell_count, "M49 full occupied cells"),
},
};
}
export async function fetchM49TgsFullShadowSpatial(
id: string,
sourceSequence: number,
{ fetcher = fetch, signal }: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
): Promise<M49TgsFullShadowSpatial> {
if (!RESULT_ID.test(id) || !Number.isSafeInteger(sourceSequence) || sourceSequence < 0 || sourceSequence >= 4489) {
throw new M49TgsFullShadowContractError("M49 full-shadow frame identity недопустима.");
}
const response = await fetcher(
`/api/v1/laboratory/m49/tgs-full-shadow/${encodeURIComponent(id)}/frames/${sourceSequence}/spatial`,
{ method: "GET", headers: { Accept: "application/json" }, signal },
);
if (!response.ok) throw new M49TgsFullShadowContractError(`M49 full-shadow frame недоступен: HTTP ${response.status}.`);
const payload = objectValue(await response.json(), "M49 full spatial");
exact(payload.schema_version, "missioncore.m49-tgs-full-shadow-spatial/v1", "M49 full spatial schema");
exact(payload.result_id, id, "M49 full spatial result");
return parseSpatial(id, sourceSequence, payload);
}
export async function fetchM49TgsFullShadowSpatialChunk(
id: string,
start: number,
count = 24,
{ fetcher = fetch, signal }: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
): Promise<M49TgsFullShadowSpatialChunk> {
if (!RESULT_ID.test(id) || !Number.isSafeInteger(start) || start < 0 || start >= 4489
|| !Number.isSafeInteger(count) || count < 1 || count > 24) {
throw new M49TgsFullShadowContractError("M49 full-shadow chunk identity недопустима.");
}
const response = await fetcher(
`/api/v1/laboratory/m49/tgs-full-shadow/${encodeURIComponent(id)}/spatial/chunk?start=${start}&count=${count}`,
{ method: "GET", headers: { Accept: "application/json" }, signal },
);
if (!response.ok) throw new M49TgsFullShadowContractError(`M49 full-shadow chunk недоступен: HTTP ${response.status}.`);
const payload = objectValue(await response.json(), "M49 full spatial chunk");
exact(payload.schema_version, "missioncore.m49-tgs-full-shadow-spatial-chunk/v1", "M49 full chunk schema");
exact(payload.result_id, id, "M49 full chunk result");
exact(payload.start, start, "M49 full chunk start");
const returnedCount = integer(payload.count, "M49 full chunk count");
if (!Array.isArray(payload.frames) || payload.frames.length !== returnedCount
|| returnedCount !== Math.min(count, 4489 - start)) {
throw new M49TgsFullShadowContractError("M49 full chunk: неверная размерность.");
}
const costmap = objectValue(payload.costmap, "M49 full chunk costmap");
return {
resultId: id,
start,
count: returnedCount,
frames: payload.frames.map((value, index) => parseSpatial(
id,
start + index,
objectValue(value, `M49 full chunk frame ${index}`),
costmap,
)),
};
}