diff --git a/apps/control-station/src/core/laboratory/m48sFixedClassDetector.ts b/apps/control-station/src/core/laboratory/m48sFixedClassDetector.ts index 258eef7..ad4f6bf 100644 --- a/apps/control-station/src/core/laboratory/m48sFixedClassDetector.ts +++ b/apps/control-station/src/core/laboratory/m48sFixedClassDetector.ts @@ -118,8 +118,12 @@ export interface M48SLoadEnvelopeScenario { worldStateCompletionAgeP95Ms: number; worldStateCompletionAgeP99Ms: number; worldStateCompletionAgeMaximumMs: number; - decodeP95Ms: number; - decodeMaximumMs: number; + hotLoopDecodeP95Ms: number; + hotLoopDecodeMaximumMs: number; + preadmissionDecodeMaximumMs: number; + sourcePrefetchPreparationMaximumMs: number; + sourcePacingLatenessP95Ms: number; + sourcePacingLatenessMaximumMs: number; detectorP95Ms: number; detectorMaximumMs: number; gpuUtilizationMeanPercent: number; @@ -129,6 +133,8 @@ export interface M48SLoadEnvelopeScenario { queueHighWatermarks: Readonly>; queueCapacity: number; additionalInferencePasses: number; + repeatCount: number; + allRepetitionsPassed: boolean; integrityGatePassed: boolean; operatingTargetGatePassed: boolean; thresholds: { @@ -136,15 +142,36 @@ export interface M48SLoadEnvelopeScenario { minimumEffectiveWorldStateFps: number; maximumWorldStateCompletionP95Ms: number; }; + repetitions: readonly M48SLoadEnvelopeRepetition[]; +} + +export interface M48SLoadEnvelopeRepetition { + runId: string; + repetition: 1 | 2 | 3; + deliveredWorldStates: number; + supersededFrames: number; + deliveryRatio: number; + effectiveWorldStateFps: number; + worldStateCompletionAgeP95Ms: number; + hotLoopDecodeP95Ms: number; + hotLoopDecodeMaximumMs: number; + preadmissionDecodeMaximumMs: number; + sourcePacingLatenessMaximumMs: number; + operatingTargetGatePassed: boolean; + resultSha256: string; + frameEvidenceSha256: string; } export interface M48SLoadEnvelopeComparison { - productionRateRepeatabilityPassed: false; - reserve12FpsPassed: true; - limit15FpsPassed: true; - loadEnvelopeAccepted: false; + repeatCountPerRate: number; + productionRateRepeatabilityPassed: boolean; + reserve12FpsPassed: boolean; + limit15FpsPassed: boolean; + loadEnvelopeAccepted: boolean; computeCapacityAtLeastFps: 15; - bottleneckInterpretation: "rare-source-decode-or-scheduling-tail-not-steady-gpu-saturation"; + bottleneckInterpretation: + | "rare-source-decode-or-scheduling-tail-not-steady-gpu-saturation" + | "cold-video-decode-isolated-before-admission-no-steady-gpu-saturation"; scenarios: readonly M48SLoadEnvelopeScenario[]; } @@ -508,24 +535,113 @@ function runtimeHardeningComparisonValue(value: unknown): M48SRuntimeHardeningCo function loadEnvelopeComparisonValue(value: unknown): M48SLoadEnvelopeComparison { const comparison = objectValue(value, "M4.8S.metrics.load_envelope"); + if (comparison.schema_version === "missioncore.m48s-load-envelope-comparison/v1") { + exact(comparison.production_rate_repeatability_passed, false, "M4.8S.load_envelope.production_rate_repeatability_passed"); + exact(comparison.reserve_12_fps_passed, true, "M4.8S.load_envelope.reserve_12_fps_passed"); + exact(comparison.limit_15_fps_passed, true, "M4.8S.load_envelope.limit_15_fps_passed"); + exact(comparison.load_envelope_accepted, false, "M4.8S.load_envelope.load_envelope_accepted"); + exact(comparison.compute_capacity_at_least_fps, 15, "M4.8S.load_envelope.compute_capacity_at_least_fps"); + exact( + comparison.bottleneck_interpretation, + "rare-source-decode-or-scheduling-tail-not-steady-gpu-saturation", + "M4.8S.load_envelope.bottleneck_interpretation", + ); + const ids = ["production-10fps", "reserve-12fps", "limit-15fps"] as const; + const purposes = ["production-rate", "reserve-gate", "limit-discovery"] as const; + const targetGates = [false, true, true] as const; + const scenarios = arrayValue(comparison.scenarios, "M4.8S.load_envelope.scenarios").map((value, index) => { + const label = `M4.8S.load_envelope.scenarios[${index}]`; + const scenario = objectValue(value, label); + const id = ids[index]; + const purpose = purposes[index]; + const targetGate = targetGates[index]; + if (id === undefined || purpose === undefined || targetGate === undefined) { + throw new M48SFixedClassDetectorContractError("M4.8S.load_envelope.scenarios: лишний legacy-сценарий."); + } + exact(scenario.id, id, `${label}.id`); + exact(scenario.load_purpose, purpose, `${label}.load_purpose`); + exact(scenario.integrity_gate_passed, true, `${label}.integrity_gate_passed`); + exact(scenario.operating_target_gate_passed, targetGate, `${label}.operating_target_gate_passed`); + exact(scenario.additional_inference_passes, 0, `${label}.additional_inference_passes`); + const queues = integerRecordValue(scenario.queue_high_watermarks, `${label}.queue_high_watermarks`); + const thresholds = objectValue(scenario.thresholds, `${label}.thresholds`); + const frameEvidenceSha256 = textValue(scenario.frame_evidence_sha256, `${label}.frame_evidence_sha256`); + if (!SHA256.test(frameEvidenceSha256)) { + throw new M48SFixedClassDetectorContractError(`${label}.frame_evidence_sha256: нарушен SHA-256.`); + } + const legacyDecodeP95 = numberValue(scenario.decode_p95_ms, `${label}.decode_p95_ms`); + const legacyDecodeMaximum = numberValue(scenario.decode_maximum_ms, `${label}.decode_maximum_ms`); + return { + id, + loadPurpose: purpose, + requestedSourceRateHz: numberValue(scenario.requested_source_rate_hz, `${label}.requested_source_rate_hz`), + sourceFramesAdmitted: integerValue(scenario.source_frames_admitted, `${label}.source_frames_admitted`), + deliveredWorldStates: integerValue(scenario.delivered_world_states, `${label}.delivered_world_states`), + supersededFrames: integerValue(scenario.superseded_frames, `${label}.superseded_frames`), + deliveryRatio: numberValue(scenario.delivery_ratio, `${label}.delivery_ratio`), + effectiveWorldStateFps: numberValue(scenario.effective_world_state_fps, `${label}.effective_world_state_fps`), + worldStateCompletionAgeP95Ms: numberValue(scenario.world_state_completion_age_p95_ms, `${label}.world_state_completion_age_p95_ms`), + worldStateCompletionAgeP99Ms: numberValue(scenario.world_state_completion_age_p99_ms, `${label}.world_state_completion_age_p99_ms`), + worldStateCompletionAgeMaximumMs: numberValue(scenario.world_state_completion_age_maximum_ms, `${label}.world_state_completion_age_maximum_ms`), + hotLoopDecodeP95Ms: legacyDecodeP95, + hotLoopDecodeMaximumMs: legacyDecodeMaximum, + preadmissionDecodeMaximumMs: legacyDecodeMaximum, + sourcePrefetchPreparationMaximumMs: 0, + sourcePacingLatenessP95Ms: 0, + sourcePacingLatenessMaximumMs: 0, + detectorP95Ms: numberValue(scenario.detector_p95_ms, `${label}.detector_p95_ms`), + detectorMaximumMs: numberValue(scenario.detector_maximum_ms, `${label}.detector_maximum_ms`), + gpuUtilizationMeanPercent: numberValue(scenario.gpu_utilization_mean_percent, `${label}.gpu_utilization_mean_percent`), + gpuUtilizationMaximumPercent: numberValue(scenario.gpu_utilization_maximum_percent, `${label}.gpu_utilization_maximum_percent`), + gpuMemoryMaximumMib: numberValue(scenario.gpu_memory_maximum_mib, `${label}.gpu_memory_maximum_mib`), + processPeakRssMib: numberValue(scenario.process_peak_rss_mib, `${label}.process_peak_rss_mib`), + queueHighWatermarks: queues as M48SLoadEnvelopeScenario["queueHighWatermarks"], + queueCapacity: integerValue(scenario.queue_capacity, `${label}.queue_capacity`), + additionalInferencePasses: 0, + repeatCount: 1, + allRepetitionsPassed: targetGate, + integrityGatePassed: true, + operatingTargetGatePassed: targetGate, + thresholds: { + minimumDeliveryRatio: numberValue(thresholds.minimum_delivery_ratio, `${label}.thresholds.minimum_delivery_ratio`), + minimumEffectiveWorldStateFps: numberValue(thresholds.minimum_effective_world_state_fps, `${label}.thresholds.minimum_effective_world_state_fps`), + maximumWorldStateCompletionP95Ms: numberValue(thresholds.maximum_world_state_completion_p95_ms, `${label}.thresholds.maximum_world_state_completion_p95_ms`), + }, + repetitions: [], + }; + }); + if (scenarios.length !== 3) { + throw new M48SFixedClassDetectorContractError("M4.8S.load_envelope.scenarios: неполный legacy-набор."); + } + return { + repeatCountPerRate: 1, + productionRateRepeatabilityPassed: false, + reserve12FpsPassed: true, + limit15FpsPassed: true, + loadEnvelopeAccepted: false, + computeCapacityAtLeastFps: 15, + bottleneckInterpretation: "rare-source-decode-or-scheduling-tail-not-steady-gpu-saturation", + scenarios, + }; + } exact( comparison.schema_version, - "missioncore.m48s-load-envelope-comparison/v1", + "missioncore.m48s-load-envelope-comparison/v2", "M4.8S.metrics.load_envelope.schema_version", ); - exact(comparison.production_rate_repeatability_passed, false, "M4.8S.load_envelope.production_rate_repeatability_passed"); + exact(comparison.repeat_count_per_rate, 3, "M4.8S.load_envelope.repeat_count_per_rate"); + exact(comparison.production_rate_repeatability_passed, true, "M4.8S.load_envelope.production_rate_repeatability_passed"); exact(comparison.reserve_12_fps_passed, true, "M4.8S.load_envelope.reserve_12_fps_passed"); exact(comparison.limit_15_fps_passed, true, "M4.8S.load_envelope.limit_15_fps_passed"); - exact(comparison.load_envelope_accepted, false, "M4.8S.load_envelope.load_envelope_accepted"); + exact(comparison.load_envelope_accepted, true, "M4.8S.load_envelope.load_envelope_accepted"); exact(comparison.compute_capacity_at_least_fps, 15, "M4.8S.load_envelope.compute_capacity_at_least_fps"); exact( comparison.bottleneck_interpretation, - "rare-source-decode-or-scheduling-tail-not-steady-gpu-saturation", + "cold-video-decode-isolated-before-admission-no-steady-gpu-saturation", "M4.8S.load_envelope.bottleneck_interpretation", ); const ids = ["production-10fps", "reserve-12fps", "limit-15fps"] as const; const purposes = ["production-rate", "reserve-gate", "limit-discovery"] as const; - const targetGates = [false, true, true] as const; const scenarios = arrayValue(comparison.scenarios, "M4.8S.load_envelope.scenarios").map((value, index) => { const label = `M4.8S.load_envelope.scenarios[${index}]`; const scenario = objectValue(value, label); @@ -536,8 +652,10 @@ function loadEnvelopeComparisonValue(value: unknown): M48SLoadEnvelopeComparison } exact(scenario.id, id, `${label}.id`); exact(scenario.load_purpose, purpose, `${label}.load_purpose`); + exact(scenario.repeat_count, 3, `${label}.repeat_count`); + exact(scenario.all_repetitions_passed, true, `${label}.all_repetitions_passed`); exact(scenario.integrity_gate_passed, true, `${label}.integrity_gate_passed`); - exact(scenario.operating_target_gate_passed, targetGates[index], `${label}.operating_target_gate_passed`); + exact(scenario.operating_target_gate_passed, true, `${label}.operating_target_gate_passed`); exact(scenario.additional_inference_passes, 0, `${label}.additional_inference_passes`); const queues = integerRecordValue(scenario.queue_high_watermarks, `${label}.queue_high_watermarks`); for (const key of ["detector", "geometry", "temporal", "rolling", "threat"] as const) { @@ -546,9 +664,37 @@ function loadEnvelopeComparisonValue(value: unknown): M48SLoadEnvelopeComparison } } const thresholds = objectValue(scenario.thresholds, `${label}.thresholds`); - const frameEvidenceSha256 = textValue(scenario.frame_evidence_sha256, `${label}.frame_evidence_sha256`); - if (!SHA256.test(frameEvidenceSha256)) { - throw new M48SFixedClassDetectorContractError(`${label}.frame_evidence_sha256: нарушен SHA-256.`); + const repetitions = arrayValue(scenario.repetitions, `${label}.repetitions`).map((value, repetitionIndex) => { + const repetitionLabel = `${label}.repetitions[${repetitionIndex}]`; + const repetition = objectValue(value, repetitionLabel); + const repetitionNumber = repetitionIndex + 1; + exact(repetition.repetition, repetitionNumber, `${repetitionLabel}.repetition`); + exact(repetition.operating_target_gate_passed, true, `${repetitionLabel}.operating_target_gate_passed`); + const runId = textValue(repetition.run_id, `${repetitionLabel}.run_id`); + const resultSha256 = textValue(repetition.result_sha256, `${repetitionLabel}.result_sha256`); + const frameEvidenceSha256 = textValue(repetition.frame_evidence_sha256, `${repetitionLabel}.frame_evidence_sha256`); + if (!SHA256.test(resultSha256) || !SHA256.test(frameEvidenceSha256)) { + throw new M48SFixedClassDetectorContractError(`${repetitionLabel}: нарушен SHA-256.`); + } + return { + runId, + repetition: repetitionNumber as 1 | 2 | 3, + deliveredWorldStates: integerValue(repetition.delivered_world_states, `${repetitionLabel}.delivered_world_states`), + supersededFrames: integerValue(repetition.superseded_frames, `${repetitionLabel}.superseded_frames`), + deliveryRatio: numberValue(repetition.delivery_ratio, `${repetitionLabel}.delivery_ratio`), + effectiveWorldStateFps: numberValue(repetition.effective_world_state_fps, `${repetitionLabel}.effective_world_state_fps`), + worldStateCompletionAgeP95Ms: numberValue(repetition.world_state_completion_age_p95_ms, `${repetitionLabel}.world_state_completion_age_p95_ms`), + hotLoopDecodeP95Ms: numberValue(repetition.hot_loop_decode_p95_ms, `${repetitionLabel}.hot_loop_decode_p95_ms`), + hotLoopDecodeMaximumMs: numberValue(repetition.hot_loop_decode_maximum_ms, `${repetitionLabel}.hot_loop_decode_maximum_ms`), + preadmissionDecodeMaximumMs: numberValue(repetition.preadmission_decode_maximum_ms, `${repetitionLabel}.preadmission_decode_maximum_ms`), + sourcePacingLatenessMaximumMs: numberValue(repetition.source_pacing_lateness_maximum_ms, `${repetitionLabel}.source_pacing_lateness_maximum_ms`), + operatingTargetGatePassed: true as const, + resultSha256, + frameEvidenceSha256, + }; + }); + if (repetitions.length !== 3) { + throw new M48SFixedClassDetectorContractError(`${label}.repetitions: требуется три повтора.`); } return { id, @@ -562,8 +708,12 @@ function loadEnvelopeComparisonValue(value: unknown): M48SLoadEnvelopeComparison worldStateCompletionAgeP95Ms: numberValue(scenario.world_state_completion_age_p95_ms, `${label}.world_state_completion_age_p95_ms`), worldStateCompletionAgeP99Ms: numberValue(scenario.world_state_completion_age_p99_ms, `${label}.world_state_completion_age_p99_ms`), worldStateCompletionAgeMaximumMs: numberValue(scenario.world_state_completion_age_maximum_ms, `${label}.world_state_completion_age_maximum_ms`), - decodeP95Ms: numberValue(scenario.decode_p95_ms, `${label}.decode_p95_ms`), - decodeMaximumMs: numberValue(scenario.decode_maximum_ms, `${label}.decode_maximum_ms`), + hotLoopDecodeP95Ms: numberValue(scenario.hot_loop_decode_p95_ms, `${label}.hot_loop_decode_p95_ms`), + hotLoopDecodeMaximumMs: numberValue(scenario.hot_loop_decode_maximum_ms, `${label}.hot_loop_decode_maximum_ms`), + preadmissionDecodeMaximumMs: numberValue(scenario.preadmission_decode_maximum_ms, `${label}.preadmission_decode_maximum_ms`), + sourcePrefetchPreparationMaximumMs: numberValue(scenario.source_prefetch_preparation_maximum_ms, `${label}.source_prefetch_preparation_maximum_ms`), + sourcePacingLatenessP95Ms: numberValue(scenario.source_pacing_lateness_p95_ms, `${label}.source_pacing_lateness_p95_ms`), + sourcePacingLatenessMaximumMs: numberValue(scenario.source_pacing_lateness_maximum_ms, `${label}.source_pacing_lateness_maximum_ms`), detectorP95Ms: numberValue(scenario.detector_p95_ms, `${label}.detector_p95_ms`), detectorMaximumMs: numberValue(scenario.detector_maximum_ms, `${label}.detector_maximum_ms`), gpuUtilizationMeanPercent: numberValue(scenario.gpu_utilization_mean_percent, `${label}.gpu_utilization_mean_percent`), @@ -573,25 +723,29 @@ function loadEnvelopeComparisonValue(value: unknown): M48SLoadEnvelopeComparison queueHighWatermarks: queues as M48SLoadEnvelopeScenario["queueHighWatermarks"], queueCapacity: integerValue(scenario.queue_capacity, `${label}.queue_capacity`), additionalInferencePasses: 0, - integrityGatePassed: true, - operatingTargetGatePassed: targetGates[index], + repeatCount: 3 as const, + allRepetitionsPassed: true as const, + integrityGatePassed: true as const, + operatingTargetGatePassed: true as const, thresholds: { minimumDeliveryRatio: numberValue(thresholds.minimum_delivery_ratio, `${label}.thresholds.minimum_delivery_ratio`), minimumEffectiveWorldStateFps: numberValue(thresholds.minimum_effective_world_state_fps, `${label}.thresholds.minimum_effective_world_state_fps`), maximumWorldStateCompletionP95Ms: numberValue(thresholds.maximum_world_state_completion_p95_ms, `${label}.thresholds.maximum_world_state_completion_p95_ms`), }, + repetitions, }; }); if (scenarios.length !== ids.length) { throw new M48SFixedClassDetectorContractError("M4.8S.load_envelope.scenarios: неполный набор."); } return { - productionRateRepeatabilityPassed: false, + repeatCountPerRate: 3, + productionRateRepeatabilityPassed: true, reserve12FpsPassed: true, limit15FpsPassed: true, - loadEnvelopeAccepted: false, + loadEnvelopeAccepted: true, computeCapacityAtLeastFps: 15, - bottleneckInterpretation: "rare-source-decode-or-scheduling-tail-not-steady-gpu-saturation", + bottleneckInterpretation: "cold-video-decode-isolated-before-admission-no-steady-gpu-saturation", scenarios, }; } @@ -647,10 +801,10 @@ function parseResult(value: unknown, resultId: string): M48SFixedClassDetectorRe exact(decision.production_accepted, false, "M4.8S.decision.production_accepted"); if (loadEnvelope !== null) { exact(decision.load_envelope_evaluated, true, "M4.8S.decision.load_envelope_evaluated"); - exact(decision.production_rate_repeatability_passed, false, "M4.8S.decision.production_rate_repeatability_passed"); - exact(decision.reserve_12_fps_passed, true, "M4.8S.decision.reserve_12_fps_passed"); - exact(decision.limit_15_fps_passed, true, "M4.8S.decision.limit_15_fps_passed"); - exact(decision.load_envelope_accepted, false, "M4.8S.decision.load_envelope_accepted"); + exact(decision.production_rate_repeatability_passed, loadEnvelope.productionRateRepeatabilityPassed, "M4.8S.decision.production_rate_repeatability_passed"); + exact(decision.reserve_12_fps_passed, loadEnvelope.reserve12FpsPassed, "M4.8S.decision.reserve_12_fps_passed"); + exact(decision.limit_15_fps_passed, loadEnvelope.limit15FpsPassed, "M4.8S.decision.limit_15_fps_passed"); + exact(decision.load_envelope_accepted, loadEnvelope.loadEnvelopeAccepted, "M4.8S.decision.load_envelope_accepted"); } const frames = arrayValue(payload.frames, "M4.8S.frames").map(descriptorValue); const evidenceFrameCount = integerValue(source.evidence_frame_count, "M4.8S.source.evidence_frame_count"); @@ -709,10 +863,10 @@ function parseResult(value: unknown, resultId: string): M48SFixedClassDetectorRe integratedWorldStateGatePassed: integratedGate, detectorReplacementAuthorized: false, loadEnvelopeEvaluated: loadEnvelope !== null, - productionRateRepeatabilityPassed: false, + productionRateRepeatabilityPassed: loadEnvelope?.productionRateRepeatabilityPassed ?? false, reserve12FpsPassed: loadEnvelope?.reserve12FpsPassed ?? false, limit15FpsPassed: loadEnvelope?.limit15FpsPassed ?? false, - loadEnvelopeAccepted: false, + loadEnvelopeAccepted: loadEnvelope?.loadEnvelopeAccepted ?? false, productionAccepted: false, }, limitations: arrayValue(payload.limitations, "M4.8S.limitations").map((item, index) => textValue(item, `M4.8S.limitations[${index}]`)), diff --git a/apps/control-station/src/workspaces/laboratory/M48SFixedClassDetectorResult.tsx b/apps/control-station/src/workspaces/laboratory/M48SFixedClassDetectorResult.tsx index 7f29694..9e4c20a 100644 --- a/apps/control-station/src/workspaces/laboratory/M48SFixedClassDetectorResult.tsx +++ b/apps/control-station/src/workspaces/laboratory/M48SFixedClassDetectorResult.tsx @@ -29,8 +29,11 @@ export function M48SFixedClassDetectorResultView({ const load = loadEnvelope && production && reserve && limit ? { comparison: loadEnvelope, production, reserve, limit } : null; + const loadAccepted = load?.comparison.loadEnvelopeAccepted === true; const status = load - ? "Capacity ≥ 15 FPS подтверждён; repeatability 10 FPS требует закрытия" + ? loadAccepted + ? "3 × 10 / 12 / 15 FPS прошли; realtime envelope ≥ 15 FPS принят" + : "Capacity ≥ 15 FPS подтверждён; repeatability 10 FPS требует закрытия" : hardening ? `После hardening: ${hardening.hardened.deliveredWorldStates.toLocaleString("ru-RU")} из ${hardening.hardened.sourceFramesAdmitted.toLocaleString("ru-RU")} world states доставлены` : integrated @@ -41,9 +44,11 @@ export function M48SFixedClassDetectorResultView({ summary={( scenario.sourcePrefetchPreparationMaximumMs)) / 1000, 2)} s / ${decimal(Math.max(...load.comparison.scenarios.map((scenario) => scenario.sourcePacingLatenessMaximumMs)), 3)} ms`, hint: "prefetch до admission / wall-clock pacing в hot loop" } + : { label: "Decode maximum", value: `${decimal(load.production.hotLoopDecodeMaximumMs, 0)} / ${decimal(load.reserve.hotLoopDecodeMaximumMs, 0)} / ${decimal(load.limit.hotLoopDecodeMaximumMs, 0)} ms`, hint: "редкий cold source/decode tail" }, { label: "Detector p95", value: `${decimal(load.production.detectorP95Ms, 3)} / ${decimal(load.reserve.detectorP95Ms, 3)} / ${decimal(load.limit.detectorP95Ms, 3)} ms`, hint: "тот же RF-DETR · один inference" }, { label: "GPU mean", value: `${decimal(load.production.gpuUtilizationMeanPercent)}% / ${decimal(load.reserve.gpuUtilizationMeanPercent)}% / ${decimal(load.limit.gpuUtilizationMeanPercent)}%`, hint: "10 / 12 / 15 FPS · насыщения нет" }, { label: "VRAM / process RAM peak", value: `${decimal(Math.max(...load.comparison.scenarios.map((scenario) => scenario.gpuMemoryMaximumMib)) / 1024, 2)} / ${decimal(Math.max(...load.comparison.scenarios.map((scenario) => scenario.processPeakRssMib)) / 1024, 2)} GiB`, hint: "общий GPU counter / graph process RSS" }, @@ -135,17 +149,21 @@ export function M48SFixedClassDetectorResultView({ ]} conclusion={{ proved: load - ? `Полный single-pass graph сохранил bounded queues 2/2, нулевые failed/stale/rejected/unavailable и нулевые дополнительные inference-проходы во всех трёх режимах. Reserve 12 FPS прошёл с delivery ${(load.reserve.deliveryRatio * 100).toLocaleString("ru-RU", { maximumFractionDigits: 3 })}% и p95 ${decimal(load.reserve.worldStateCompletionAgeP95Ms, 3)} ms; 15 FPS также прошли с delivery ${(load.limit.deliveryRatio * 100).toLocaleString("ru-RU", { maximumFractionDigits: 3 })}%. Вычислительная capacity доказана как минимум до 15 FPS без более мощного Worker.` + ? `Все ${load.comparison.repeatCountPerRate * load.comparison.scenarios.length} полных single-pass прогона сохранили bounded queues 2/2, нулевые failed/stale/rejected/unavailable и нулевые дополнительные inference-проходы. Худший 10 FPS delivery — ${(load.production.deliveryRatio * 100).toLocaleString("ru-RU", { maximumFractionDigits: 3 })}%, 12 FPS — ${(load.reserve.deliveryRatio * 100).toLocaleString("ru-RU", { maximumFractionDigits: 3 })}%, 15 FPS — ${(load.limit.deliveryRatio * 100).toLocaleString("ru-RU", { maximumFractionDigits: 3 })}%. Realtime capacity доказана как минимум до 15 FPS без более мощного Worker.` : hardening ? `На полном сравнительном прогоне с ${hardening.hardened.sourceFramesAdmitted.toLocaleString("ru-RU")} одинаковыми входными кадрами hardening сохранил realtime FPS, доставил на ${(hardening.hardened.deliveredWorldStates - hardening.baseline.deliveredWorldStates).toLocaleString("ru-RU")} world states больше и сократил вытеснения ${hardening.baseline.supersededFrames} → ${hardening.hardened.supersededFrames}. Максимальный стоп rolling stage уменьшился ${decimal(hardening.baseline.rollingMaximumMs, 3)} → ${decimal(hardening.hardened.rollingMaximumMs, 3)} ms. Отдельный prewarm-прогон снизил первый detector frame ${decimal(hardening.startup.baseline.detectorMs, 3)} → ${decimal(hardening.startup.prewarmed.detectorMs, 3)} ms и первый world state ${decimal(hardening.startup.baseline.worldStateMs, 3)} → ${decimal(hardening.startup.prewarmed.worldStateMs, 3)} ms без дополнительного inference на кадрах.` : integrated ? `На Worker 006 полный граф обработал ${integrated.sourceFramesAdmitted.toLocaleString("ru-RU")} входных кадров, доставил ${integrated.deliveredWorldStates.toLocaleString("ru-RU")} состояний без ошибок, удержал все очереди в пределах ${integrated.queueCapacity} и p95 ${decimal(integrated.worldStateCompletionAgeP95Ms, 3)} ms. Advisory сформировал публикации по семействам: geometry-only (${integrated.advisoryFamilyCounts["generic-obstacle"].toLocaleString("ru-RU")}), люди (${integrated.advisoryFamilyCounts.person.toLocaleString("ru-RU")}), животные (${integrated.advisoryFamilyCounts.animal.toLocaleString("ru-RU")}) и транспорт (${integrated.advisoryFamilyCounts.vehicle.toLocaleString("ru-RU")}); это не количество уникальных физических объектов и не потребовало второго inference.` : `RF-DETR-L ${decimal(detectorLoad.durationSeconds / 60, 0)} минут устойчиво потреблял source-paced поток около 10 FPS: ${detectorLoad.sourceFramesConsumed.toLocaleString("ru-RU")} кадров, 0 замен, 0 ошибок, completion-age p95 ${decimal(detectorLoad.completionAgeP95Ms, 3)} ms.`, - notProved: load - ? `Не закрыта repeatability delivery на production-rate: 10 FPS дали ${(load.production.deliveryRatio * 100).toLocaleString("ru-RU", { maximumFractionDigits: 3 })}% при gate ${(load.production.thresholds.minimumDeliveryRatio * 100).toLocaleString("ru-RU", { maximumFractionDigits: 1 })}%. Немонотонность 10 → 12 → 15 и decode maxima 3,2–3,6 s не позволяют объявить production envelope принятым.` + notProved: loadAccepted + ? "Не доказаны unbiased precision/recall классов, независимая истинность track identity и risk policy, работа planner, collision safety или physical-live режим. Принятый runtime envelope не включает production/navigation authority." + : load + ? `Не закрыта repeatability delivery на production-rate: 10 FPS дали ${(load.production.deliveryRatio * 100).toLocaleString("ru-RU", { maximumFractionDigits: 3 })}% при gate ${(load.production.thresholds.minimumDeliveryRatio * 100).toLocaleString("ru-RU", { maximumFractionDigits: 1 })}%. Исторический v1 результат сохранён; он не переинтерпретируется после появления v2.` : "Не доказаны unbiased precision/recall классов, независимое качество track identity и risk policy, поведение planner или collision safety. Кадровые рамки не заменяют геометрическую occupancy-карту.", - decision: load - ? "Не усиливать GPU и не добавлять второй детектор. Сначала изолировать/предзагружать video decode и измерить scheduler tail, затем повторить тот же sealed 10/12/15 FPS набор серией прогонов. До закрытия repeatability production switch не разрешён." + decision: loadAccepted + ? "Сохранить bounded 64-frame prefetch до source admission как runtime baseline. Не усиливать GPU и не добавлять второй детектор: Worker 006 уже держит как минимум 15 FPS. Следующий этап — semantic/track/risk truth и physical-live shadow; production switch не разрешён." + : load + ? "Историческое решение v1: не усиливать GPU и не добавлять второй детектор; изолировать cold video decode и повторить sealed 10/12/15 FPS серией прогонов. Результат v2 закрывает этот следующий этап отдельно, не переписывая v1." : hardening ? "Сохранить разгрузку cyclic GC и обязательный detector prewarm как текущий runtime baseline. Следующий этап — измерять запас realtime при росте сцены и числа семантических объектов, не добавляя второй детектор и не расширяя inference-нагрузку. Production switch не разрешён." : "Сохранить RF-DETR-L как risk-semantic shadow provider полного reference graph. Не классифицировать миллионы статических форм: неизвестное неподвижное препятствие остаётся geometry-owned и объезжается; классы сохраняются для людей, животных и транспорта. Production switch не разрешён.", diff --git a/apps/control-station/test/m48sFixedClassDetector.test.mjs b/apps/control-station/test/m48sFixedClassDetector.test.mjs index d110034..9983ec1 100644 --- a/apps/control-station/test/m48sFixedClassDetector.test.mjs +++ b/apps/control-station/test/m48sFixedClassDetector.test.mjs @@ -45,7 +45,7 @@ function resultPayload() { selected: false, ...overrides, }); - const loadScenario = (id, loadPurpose, rate, delivered, superseded, targetPassed) => ({ + const loadScenario = (id, loadPurpose, rate, delivered, superseded) => ({ id, load_purpose: loadPurpose, requested_source_rate_hz: rate, @@ -57,8 +57,12 @@ function resultPayload() { world_state_completion_age_p95_ms: 75, world_state_completion_age_p99_ms: 100, world_state_completion_age_maximum_ms: 400, - decode_p95_ms: 12, - decode_maximum_ms: 3200, + hot_loop_decode_p95_ms: 14, + hot_loop_decode_maximum_ms: 42, + preadmission_decode_maximum_ms: 3633, + source_prefetch_preparation_maximum_ms: 4353, + source_pacing_lateness_p95_ms: 1.7, + source_pacing_lateness_maximum_ms: 11, detector_p95_ms: 40, detector_maximum_ms: 370, gpu_utilization_mean_percent: 35, @@ -68,14 +72,31 @@ function resultPayload() { queue_high_watermarks: { detector: 2, geometry: 2, temporal: 2, rolling: 2, threat: 2 }, queue_capacity: 2, additional_inference_passes: 0, + repeat_count: 3, + all_repetitions_passed: true, integrity_gate_passed: true, - operating_target_gate_passed: targetPassed, + operating_target_gate_passed: true, thresholds: { minimum_delivery_ratio: rate === 10 ? 0.999 : rate === 12 ? 0.995 : 0.95, minimum_effective_world_state_fps: rate * 0.95, maximum_world_state_completion_p95_ms: rate === 10 ? 125 : rate === 12 ? 150 : 175, }, - frame_evidence_sha256: "8".repeat(64), + repetitions: [1, 2, 3].map((repetition) => ({ + run_id: `m48s-load-prefetch-v1-${id}-a${repetition}`, + repetition, + delivered_world_states: delivered, + superseded_frames: superseded, + delivery_ratio: delivered / 4489, + effective_world_state_fps: rate * 0.975, + world_state_completion_age_p95_ms: 75, + hot_loop_decode_p95_ms: 14, + hot_loop_decode_maximum_ms: 42, + preadmission_decode_maximum_ms: 3633, + source_pacing_lateness_maximum_ms: 11, + operating_target_gate_passed: true, + result_sha256: "7".repeat(64), + frame_evidence_sha256: "8".repeat(64), + })), }); return { schema_version: "missioncore.m48s-fixed-class-detector-result-view/v1", @@ -205,17 +226,18 @@ function resultPayload() { }, }, load_envelope: { - schema_version: "missioncore.m48s-load-envelope-comparison/v1", - production_rate_repeatability_passed: false, + schema_version: "missioncore.m48s-load-envelope-comparison/v2", + repeat_count_per_rate: 3, + production_rate_repeatability_passed: true, reserve_12_fps_passed: true, limit_15_fps_passed: true, - load_envelope_accepted: false, + load_envelope_accepted: true, compute_capacity_at_least_fps: 15, - bottleneck_interpretation: "rare-source-decode-or-scheduling-tail-not-steady-gpu-saturation", + bottleneck_interpretation: "cold-video-decode-isolated-before-admission-no-steady-gpu-saturation", scenarios: [ - loadScenario("production-10fps", "production-rate", 10, 4483, 6, false), - loadScenario("reserve-12fps", "reserve-gate", 12, 4477, 12, true), - loadScenario("limit-15fps", "limit-discovery", 15, 4488, 1, true), + loadScenario("production-10fps", "production-rate", 10, 4489, 0), + loadScenario("reserve-12fps", "reserve-gate", 12, 4488, 1), + loadScenario("limit-15fps", "limit-discovery", 15, 4488, 1), ], }, }, @@ -226,10 +248,10 @@ function resultPayload() { integrated_world_state_gate_passed: true, detector_replacement_authorized: false, load_envelope_evaluated: true, - production_rate_repeatability_passed: false, + production_rate_repeatability_passed: true, reserve_12_fps_passed: true, limit_15_fps_passed: true, - load_envelope_accepted: false, + load_envelope_accepted: true, production_accepted: false, }, limitations: ["No independent semantic ground truth."], @@ -263,15 +285,68 @@ test("M4.8S result exposes complete graph load without production authority", as assert.equal(result.metrics.runtimeHardening.baseline.supersededFrames, 9); assert.equal(result.metrics.runtimeHardening.hardened.supersededFrames, 1); assert.equal(result.metrics.runtimeHardening.startup.prewarmed.worldStateMs, 60.516957); - assert.equal(result.metrics.loadEnvelope.scenarios[0].operatingTargetGatePassed, false); - assert.equal(result.metrics.loadEnvelope.scenarios[1].deliveredWorldStates, 4477); + assert.equal(result.metrics.loadEnvelope.scenarios[0].operatingTargetGatePassed, true); + assert.equal(result.metrics.loadEnvelope.scenarios[1].deliveredWorldStates, 4488); + assert.equal(result.metrics.loadEnvelope.scenarios[2].repetitions.length, 3); + assert.equal(result.metrics.loadEnvelope.scenarios[0].hotLoopDecodeP95Ms, 14); assert.equal(result.metrics.loadEnvelope.computeCapacityAtLeastFps, 15); - assert.equal(result.decision.loadEnvelopeAccepted, false); + assert.equal(result.decision.loadEnvelopeAccepted, true); assert.equal(result.decision.integratedWorldStateGatePassed, true); assert.equal(result.decision.productionAccepted, false); assert.equal(result.authority.navigationOrSafetyAccepted, false); }); +test("M4.8S keeps the immutable v1 load-envelope result readable", async () => { + const payload = resultPayload(); + payload.metrics.load_envelope = { + schema_version: "missioncore.m48s-load-envelope-comparison/v1", + production_rate_repeatability_passed: false, + reserve_12_fps_passed: true, + limit_15_fps_passed: true, + load_envelope_accepted: false, + compute_capacity_at_least_fps: 15, + bottleneck_interpretation: "rare-source-decode-or-scheduling-tail-not-steady-gpu-saturation", + scenarios: payload.metrics.load_envelope.scenarios.map((scenario, index) => ({ + id: scenario.id, + load_purpose: scenario.load_purpose, + requested_source_rate_hz: scenario.requested_source_rate_hz, + source_frames_admitted: scenario.source_frames_admitted, + delivered_world_states: index === 0 ? 4483 : scenario.delivered_world_states, + superseded_frames: index === 0 ? 6 : scenario.superseded_frames, + delivery_ratio: index === 0 ? 4483 / 4489 : scenario.delivery_ratio, + effective_world_state_fps: scenario.effective_world_state_fps, + world_state_completion_age_p95_ms: scenario.world_state_completion_age_p95_ms, + world_state_completion_age_p99_ms: scenario.world_state_completion_age_p99_ms, + world_state_completion_age_maximum_ms: scenario.world_state_completion_age_maximum_ms, + decode_p95_ms: scenario.hot_loop_decode_p95_ms, + decode_maximum_ms: 3200, + detector_p95_ms: scenario.detector_p95_ms, + detector_maximum_ms: scenario.detector_maximum_ms, + gpu_utilization_mean_percent: scenario.gpu_utilization_mean_percent, + gpu_utilization_maximum_percent: scenario.gpu_utilization_maximum_percent, + gpu_memory_maximum_mib: scenario.gpu_memory_maximum_mib, + process_peak_rss_mib: scenario.process_peak_rss_mib, + queue_high_watermarks: scenario.queue_high_watermarks, + queue_capacity: 2, + additional_inference_passes: 0, + integrity_gate_passed: true, + operating_target_gate_passed: index !== 0, + thresholds: scenario.thresholds, + frame_evidence_sha256: "8".repeat(64), + })), + }; + payload.decision.production_rate_repeatability_passed = false; + payload.decision.load_envelope_accepted = false; + + const result = await fetchM48SFixedClassDetectorResult(resultId, { + fetcher: async () => response(payload), + }); + + assert.equal(result.metrics.loadEnvelope.repeatCountPerRate, 1); + assert.equal(result.metrics.loadEnvelope.loadEnvelopeAccepted, false); + assert.equal(result.metrics.loadEnvelope.scenarios[0].operatingTargetGatePassed, false); +}); + test("M4.8S frame binds exact camera endpoint and risk-only boxes", async () => { const frame = await fetchM48SFixedClassDetectorFrame(resultId, "000253", { fetcher: async () => response({ diff --git a/config/perception/m48s-load-envelope-prefetch-v1.json b/config/perception/m48s-load-envelope-prefetch-v1.json new file mode 100644 index 0000000..3638b26 --- /dev/null +++ b/config/perception/m48s-load-envelope-prefetch-v1.json @@ -0,0 +1,113 @@ +{ + "schema_version": "missioncore.m48s-load-envelope-profile/v2", + "profile_id": "m48s-rf-detr-reference-graph-load-envelope-prefetch/v1", + "decision_question": "Does bounded recorded-image prefetch remove decode-startup interference and make the 10, 12 and 15 FPS complete-graph results repeatable on Worker 006?", + "hypothesis": "A 64-frame bounded decoder prefetch completed before source admission isolates cold video open and initial decode from the realtime hot loop. Three complete repetitions at each rate will retain exact terminal accounting, satisfy the predeclared targets and attribute any residual tail to hot-loop decode, source pacing or graph completion.", + "source": { + "source_id": "RAVNOVES00", + "source_profile_id": "m4-ravnoves00-recorded-realtime/v1", + "frame_count": 4489, + "graph_id": "reference-perception-graph/v2", + "detector_provider_id": "triton-rf-detr-large-coco-risk-fp16-shadow/v0", + "sensor_timestamps_preserved": true + }, + "controlled_change_from_v1": { + "change": "bounded 64-frame recorded-image decode prefetch before source admission plus source pacing attribution", + "unchanged": [ + "source frames and source timestamps", + "RF-DETR TensorRT engine and threshold", + "geometry, temporal, motion, rolling-map and threat providers", + "latest-wins queue capacities", + "single inference pass per admitted detector frame", + "detector prewarm and cyclic-GC hot-loop policy" + ] + }, + "series_control": { + "repeat_count_per_rate": 3, + "execution_order": [ + "production-10fps-a1", + "reserve-12fps-a1", + "limit-15fps-a1", + "production-10fps-a2", + "reserve-12fps-a2", + "limit-15fps-a2", + "production-10fps-a3", + "reserve-12fps-a3", + "limit-15fps-a3" + ], + "worker": "DESKTOP-OPJ8J04 / Worker 006", + "execution": "strictly sequential; one graph container and one candidate Triton container per run" + }, + "scenarios": [ + { + "id": "production-10fps", + "run_ids": [ + "m48s-load-prefetch-v1-production-10fps-a1", + "m48s-load-prefetch-v1-production-10fps-a2", + "m48s-load-prefetch-v1-production-10fps-a3" + ], + "load_purpose": "production-rate", + "source_rate_hz": 10.0, + "thresholds": { + "minimum_delivery_ratio": 0.999, + "minimum_effective_world_state_fps": 9.5, + "maximum_world_state_completion_p95_ms": 125.0 + }, + "required_for_reserve_decision": true + }, + { + "id": "reserve-12fps", + "run_ids": [ + "m48s-load-prefetch-v1-reserve-12fps-a1", + "m48s-load-prefetch-v1-reserve-12fps-a2", + "m48s-load-prefetch-v1-reserve-12fps-a3" + ], + "load_purpose": "reserve-gate", + "source_rate_hz": 12.0, + "thresholds": { + "minimum_delivery_ratio": 0.995, + "minimum_effective_world_state_fps": 11.4, + "maximum_world_state_completion_p95_ms": 150.0 + }, + "required_for_reserve_decision": true + }, + { + "id": "limit-15fps", + "run_ids": [ + "m48s-load-prefetch-v1-limit-15fps-a1", + "m48s-load-prefetch-v1-limit-15fps-a2", + "m48s-load-prefetch-v1-limit-15fps-a3" + ], + "load_purpose": "limit-discovery", + "source_rate_hz": 15.0, + "thresholds": { + "minimum_delivery_ratio": 0.95, + "minimum_effective_world_state_fps": 14.25, + "maximum_world_state_completion_p95_ms": 175.0 + }, + "required_for_reserve_decision": false + } + ], + "common_integrity_gates": [ + "closed terminal accounting", + "zero failed, stale, rejected or unavailable frames", + "all queue high-watermarks at or below capacity two", + "complete single-pass pipeline timing", + "detector prewarm before source admission", + "source prefetch ready before source admission", + "source pacing attribution for every admitted frame", + "cyclic GC disabled only during the hot loop and restored after", + "all command, actuation, navigation and safety authority remains false" + ], + "exit_decision": { + "repeatability_accepted_when": "All three repetitions at 10 and 12 FPS pass every integrity and operating-target gate; 15 FPS remains limit evidence and must pass integrity.", + "optimization_scope_when_rejected": "Optimize only the phase identified by preadmission/hot-loop decode and source-pacing attribution; do not add another detector or inference pass." + }, + "authority": { + "candidate_accepted": false, + "commands_enabled": false, + "actuation_allowed": false, + "navigation_or_safety_accepted": false, + "production_accepted": false + } +} diff --git a/src/k1link/laboratory/m48s_fixed_class_detector_lab.py b/src/k1link/laboratory/m48s_fixed_class_detector_lab.py index 0f98abb..c520964 100644 --- a/src/k1link/laboratory/m48s_fixed_class_detector_lab.py +++ b/src/k1link/laboratory/m48s_fixed_class_detector_lab.py @@ -22,7 +22,7 @@ CATALOG_SCHEMA: Final = "missioncore.m48s-fixed-class-detector-frame-catalog/v1" FRAME_SCHEMA: Final = "missioncore.m48s-fixed-class-detector-frame/v1" REPORT_SCHEMA: Final = "missioncore.m48s-fixed-class-detector-report/v1" RUNTIME_HARDENING_SCHEMA: Final = "missioncore.m48s-runtime-hardening-comparison/v1" -LOAD_ENVELOPE_SCHEMA: Final = "missioncore.m48s-load-envelope-comparison/v1" +LOAD_ENVELOPE_SCHEMA: Final = "missioncore.m48s-load-envelope-comparison/v2" METHOD_SCHEMA: Final = "missioncore.laboratory-method/v1" RESULT_PREFIX: Final = "m48s-fixed-class-detector-lab-" TOURNAMENT_ID: Final = ( @@ -67,45 +67,123 @@ RUNTIME_HARDENING_RUNS: Final = { }, } LOAD_ENVELOPE_RUNS: Final = { - "production-10fps": { - "run_id": "m48s-load-envelope-v1-production-10fps-a1", + "production-10fps-a1": { + "scenario_id": "production-10fps", + "repetition": 1, + "run_id": "m48s-load-prefetch-v1-production-10fps-a1", "load_purpose": "production-rate", "source_rate_hz": 10.0, - "result_sha256": "60d4d82a8d47e5087019640353c9f8f7ddae518b52801dfd8c64423f5aba9fe2", - "frames_sha256": "9e836f13a49390be498ce2b133fe2c20538af43adf667b0e7b89dc6349379ab0", - "delivered": 4483, - "superseded": 6, - "operating_target_gate_passed": False, - }, - "reserve-12fps": { - "run_id": "m48s-load-envelope-v1-reserve-12fps-a1", - "load_purpose": "reserve-gate", - "source_rate_hz": 12.0, - "result_sha256": "c8cb917f6f3a8cb6ffcc5146211066dbaed4aa2d8671e55820c266c49d5d5321", - "frames_sha256": "3c1e635f6fbea643024bbd1711fb6c1ed5380430972cbb434ec76f4488d1c8b7", - "delivered": 4477, - "superseded": 12, + "result_sha256": "0cf8b682b3972d434e9e10a6eb68a5abc2c1005d731cd61374de951f13d45282", + "frames_sha256": "40d02cb59cb3531b764eface2e5f8b8dbf1d1e4ac83801746a97306ac8363910", + "delivered": 4489, + "superseded": 0, "operating_target_gate_passed": True, }, - "limit-15fps": { - "run_id": "m48s-load-envelope-v1-limit-15fps-a1", - "load_purpose": "limit-discovery", - "source_rate_hz": 15.0, - "result_sha256": "a78767ee2a7d8f3f7f7625c408e9bc8fb6c60e8ba6f77ab54fddbca018540713", - "frames_sha256": "8be16ed155111ffa7093bbf46ceda4bc679593bfaf7a00c38c5d50c2385138d0", + "reserve-12fps-a1": { + "scenario_id": "reserve-12fps", + "repetition": 1, + "run_id": "m48s-load-prefetch-v1-reserve-12fps-a1", + "load_purpose": "reserve-gate", + "source_rate_hz": 12.0, + "result_sha256": "70e0a623624ed162387960867d3615139e452ff30a6ba8daebe6965f39d76f58", + "frames_sha256": "d54187c1b56e43f06498d2deac26374120a36cfd113c3b8f3e4dc170280353aa", "delivered": 4488, "superseded": 1, "operating_target_gate_passed": True, }, + "limit-15fps-a1": { + "scenario_id": "limit-15fps", + "repetition": 1, + "run_id": "m48s-load-prefetch-v1-limit-15fps-a1", + "load_purpose": "limit-discovery", + "source_rate_hz": 15.0, + "result_sha256": "d97ebdcc3bd70dc50284c6482fef614b7ec76180749c32b45c31d5e7e6667133", + "frames_sha256": "b1b79205d266585b0d545bb5a02e87c957759774aeacab2f2d5290cb5d735bf7", + "delivered": 4488, + "superseded": 1, + "operating_target_gate_passed": True, + }, + "production-10fps-a2": { + "scenario_id": "production-10fps", + "repetition": 2, + "run_id": "m48s-load-prefetch-v1-production-10fps-a2", + "load_purpose": "production-rate", + "source_rate_hz": 10.0, + "result_sha256": "c98c052b759cecd390e6e79d9eac223e45c96e532bd974d9961d5904945fbfe8", + "frames_sha256": "8574b98aee54ea25e31fc80cbcb7720683bdb36116dc3f2606aed4b0316b8cfa", + "delivered": 4489, + "superseded": 0, + "operating_target_gate_passed": True, + }, + "reserve-12fps-a2": { + "scenario_id": "reserve-12fps", + "repetition": 2, + "run_id": "m48s-load-prefetch-v1-reserve-12fps-a2", + "load_purpose": "reserve-gate", + "source_rate_hz": 12.0, + "result_sha256": "16cc2cb0fd9a56707910dfb397021613eb57b1a1d6259b52d14ec38a1e348eb6", + "frames_sha256": "cabd11a2a363b702e2a345198de12408079532ebaa8a095bcfa458acf763325f", + "delivered": 4489, + "superseded": 0, + "operating_target_gate_passed": True, + }, + "limit-15fps-a2": { + "scenario_id": "limit-15fps", + "repetition": 2, + "run_id": "m48s-load-prefetch-v1-limit-15fps-a2", + "load_purpose": "limit-discovery", + "source_rate_hz": 15.0, + "result_sha256": "bf23b09b6874f506170448484d1ca2886d8abeb951f9536e810ed24991a0efa6", + "frames_sha256": "fe3b37bde43fe92d92e5da4004509ef87aa85b25700cf8d9d5c84817e13b2422", + "delivered": 4488, + "superseded": 1, + "operating_target_gate_passed": True, + }, + "production-10fps-a3": { + "scenario_id": "production-10fps", + "repetition": 3, + "run_id": "m48s-load-prefetch-v1-production-10fps-a3", + "load_purpose": "production-rate", + "source_rate_hz": 10.0, + "result_sha256": "ebb81d69c16b32aa6f2237a3a5e183def58dcb422b1b82fa2621d340cc1b6fcc", + "frames_sha256": "2668b936d274a273dbe440dade55716f686af507aaa4f1a02803396accfe240d", + "delivered": 4489, + "superseded": 0, + "operating_target_gate_passed": True, + }, + "reserve-12fps-a3": { + "scenario_id": "reserve-12fps", + "repetition": 3, + "run_id": "m48s-load-prefetch-v1-reserve-12fps-a3", + "load_purpose": "reserve-gate", + "source_rate_hz": 12.0, + "result_sha256": "655d3c0b6c7210f12cfb3a9edf84da5851459afa7303c9732b879e454ac05dc2", + "frames_sha256": "419e8862f0da0e54a3b82f581230ab72874bf52841f62a4e49e1905ed5a77520", + "delivered": 4485, + "superseded": 4, + "operating_target_gate_passed": True, + }, + "limit-15fps-a3": { + "scenario_id": "limit-15fps", + "repetition": 3, + "run_id": "m48s-load-prefetch-v1-limit-15fps-a3", + "load_purpose": "limit-discovery", + "source_rate_hz": 15.0, + "result_sha256": "e844183dcee5982f389c9aa847cd98cec78fb81e0b3920e606063740eb35f421", + "frames_sha256": "477cffcecbaf607c8c68979b5425f84963d07f1be76917d2a21925f290bb2736", + "delivered": 4489, + "superseded": 0, + "operating_target_gate_passed": True, + }, } LOAD_ENVELOPE_PROFILE_SHA256: Final = ( - "d63c711534d84ee526d056885cfa0b5221df61d8394b244188b735bb34d20869" + "f0c9467d71622085b31f650d89b70ad5107fa49842f1bc828ccc2c83f85628a3" ) LOAD_RUNTIME_ARTIFACT_SHA256: Final = ( - "c2344c91ce48111abb364cad38bd73fca9e5b30c8d001c113718dc1b30a8d859" + "9929b735fd139369812f2d5c67149f7737cbf3d3f34076b590e9e9672666bf03" ) LOAD_RUNNER_SHA256: Final = ( - "0ff2ed9bdf5d5982cce08c4930853f65aa7be2c5986afacfe6a7cf925a2815e9" + "d3ca51b8500b681307d3a9974f57ae72cdbf04267b5bba26e7ffb091ddd9df31" ) YOLOX_ID: Final = ( "m48s-yolox-all-coco-shadow-7dbe6043b3fc12c7ddb162f609f883d86b34a4f2dd3785a632795f257e192d06" @@ -191,9 +269,14 @@ def build_m48s_fixed_class_detector_lab( } for name, definition in RUNTIME_HARDENING_RUNS.items() } - load_envelope_profile_path = repository / "config/perception/m48s-load-envelope-v1.json" + load_envelope_profile_path = ( + repository / "config/perception/m48s-load-envelope-prefetch-v1.json" + ) load_envelope_paths = { - name: runtime / "load-envelope-worker" / str(definition["run_id"]) / "result.json" + name: runtime + / "load-envelope-prefetch-worker" + / str(definition["run_id"]) + / "result.json" for name, definition in LOAD_ENVELOPE_RUNS.items() } yolox_root = runtime / "yolox-all-coco-results" / YOLOX_ID @@ -352,7 +435,7 @@ def build_m48s_fixed_class_detector_lab( } identity_sha256 = hashlib.sha256(canonical_json(identity)).hexdigest() result_id = RESULT_PREFIX + identity_sha256 - completed_utc_ns = load_envelope_runs["limit-15fps"].get("completed_utc_ns") + completed_utc_ns = load_envelope_runs["limit-15fps-a3"].get("completed_utc_ns") if not isinstance(completed_utc_ns, int) or isinstance(completed_utc_ns, bool): raise M48SFixedClassDetectorLabError( "complete reference-graph completion time is unavailable" @@ -378,6 +461,7 @@ def build_m48s_fixed_class_detector_lab( hardening_first_frames=hardening_first_frames, load_envelope_runs=load_envelope_runs, ) + envelope = cast(dict[str, Any], metrics["load_envelope"]) decision = { "bounded_question_accepted": True, "selected_candidate": "rf-detr", @@ -386,10 +470,12 @@ def build_m48s_fixed_class_detector_lab( "integrated_world_state_gate_passed": True, "full_replay_visual_published": True, "load_envelope_evaluated": True, - "production_rate_repeatability_passed": False, - "reserve_12_fps_passed": True, - "limit_15_fps_passed": True, - "load_envelope_accepted": False, + "production_rate_repeatability_passed": envelope[ + "production_rate_repeatability_passed" + ], + "reserve_12_fps_passed": envelope["reserve_12_fps_passed"], + "limit_15_fps_passed": envelope["limit_15_fps_passed"], + "load_envelope_accepted": envelope["load_envelope_accepted"], "detector_replacement_authorized": False, "production_accepted": False, } @@ -409,9 +495,9 @@ def build_m48s_fixed_class_detector_lab( "metrics come from separately sealed, input-identical replay runs." ), ( - "The 12 and 15 FPS operating gates passed, but the separately predeclared 10 FPS " - "delivery-ratio gate missed by two frames; capacity is demonstrated while delivery " - "repeatability remains open." + "Three complete repetitions at each of 10, 12 and 15 FPS passed their predeclared " + "integrity and operating-target gates after bounded decode prefetch was completed " + "before source admission." ), ] @@ -763,23 +849,38 @@ def _validate_load_envelope( ) -> None: scenarios = profile.get("scenarios") if ( - profile.get("schema_version") != "missioncore.m48s-load-envelope-profile/v1" - or profile.get("profile_id") != "m48s-rf-detr-reference-graph-load-envelope/v1" + profile.get("schema_version") != "missioncore.m48s-load-envelope-profile/v2" + or profile.get("profile_id") + != "m48s-rf-detr-reference-graph-load-envelope-prefetch/v1" or sha256_path(profile_path) != LOAD_ENVELOPE_PROFILE_SHA256 or not isinstance(scenarios, list) - or len(scenarios) != len(LOAD_ENVELOPE_RUNS) + or len(scenarios) != 3 + or profile.get("series_control", {}).get("repeat_count_per_rate") != 3 ): raise M48SFixedClassDetectorLabError("load-envelope profile identity changed") scenarios_by_id = { scenario.get("id"): scenario for scenario in scenarios if isinstance(scenario, dict) } - if set(scenarios_by_id) != set(LOAD_ENVELOPE_RUNS): + expected_scenario_ids = { + str(definition["scenario_id"]) for definition in LOAD_ENVELOPE_RUNS.values() + } + if set(scenarios_by_id) != expected_scenario_ids: raise M48SFixedClassDetectorLabError("load-envelope scenarios changed") + declared_run_ids = { + run_id + for scenario in scenarios_by_id.values() + for run_id in cast(list[object], scenario.get("run_ids", [])) + } + expected_run_ids = { + str(definition["run_id"]) for definition in LOAD_ENVELOPE_RUNS.values() + } + if declared_run_ids != expected_run_ids: + raise M48SFixedClassDetectorLabError("load-envelope run series changed") shared_inputs: dict[str, object] | None = None for name, definition in LOAD_ENVELOPE_RUNS.items(): run = runs.get(name) path = paths.get(name) - scenario = scenarios_by_id[name] + scenario = scenarios_by_id[str(definition["scenario_id"])] if not isinstance(run, dict) or not isinstance(path, Path): raise M48SFixedClassDetectorLabError("load-envelope evidence is incomplete") source = run.get("source") @@ -790,8 +891,22 @@ def _validate_load_envelope( frame_evidence = execution.get("frame_evidence") if isinstance(execution, dict) else None terminal = execution.get("terminal_outcomes") if isinstance(execution, dict) else None pipeline = run.get("metrics", {}).get("pipeline_timing") + decode_by_phase = ( + pipeline.get("decode_duration_by_phase_ms") if isinstance(pipeline, dict) else None + ) + pacing = ( + pipeline.get("delivered_source_pacing_lateness_ms") + if isinstance(pipeline, dict) + else None + ) + loops = execution.get("loops") if isinstance(execution, dict) else None + prefetch = ( + loops[0].get("source_prefetch") + if isinstance(loops, list) and len(loops) == 1 and isinstance(loops[0], dict) + else None + ) if ( - run.get("schema_version") != "missioncore.m48s-reference-graph-shadow-load/v4" + run.get("schema_version") != "missioncore.m48s-reference-graph-shadow-load/v5" or run.get("completed") is not True or run.get("production_accepted") is not False or run.get("authority") != false_authority() @@ -821,14 +936,27 @@ def _validate_load_envelope( or execution.get("delivered_world_states") != definition["delivered"] or not isinstance(terminal, dict) or terminal.get("delivered") != definition["delivered"] - or terminal.get("superseded") != definition["superseded"] + or terminal.get("superseded", 0) != definition["superseded"] or sum(terminal.values()) != 4489 or not isinstance(frame_evidence, dict) or frame_evidence.get("row_count") != definition["delivered"] or frame_evidence.get("sha256") != definition["frames_sha256"] or not isinstance(pipeline, dict) or pipeline.get("additional_inference_passes") != 0 + or not isinstance(decode_by_phase, dict) + or set(decode_by_phase) != {"preadmission", "hot-loop"} + or any( + not isinstance(decode_by_phase.get(phase), dict) + for phase in ("preadmission", "hot-loop") + ) + or not isinstance(pacing, dict) + or not isinstance(prefetch, dict) + or prefetch.get("capacity_frames") != 64 + or prefetch.get("ready_frames") != 64 + or prefetch.get("buffered_frames") != 64 + or not isinstance(prefetch.get("preparation_duration_ns"), int) or thresholds != scenario.get("thresholds") + or definition["run_id"] not in scenario.get("run_ids", []) or sha256_path(path) != definition["result_sha256"] ): raise M48SFixedClassDetectorLabError( @@ -1054,54 +1182,164 @@ def _metrics( def _load_envelope_metrics(runs: dict[str, dict[str, Any]]) -> dict[str, object]: scenarios: list[dict[str, object]] = [] - for name in LOAD_ENVELOPE_RUNS: - run = runs[name] - execution = run["execution"] - metrics = run["metrics"] - completion = metrics["world_state_completion_age_ms"] - gpu = metrics["gpu"] - pipeline = metrics["pipeline_timing"] - terminal = execution["terminal_outcomes"] - thresholds = run["predeclared_thresholds"] + scenario_order = ("production-10fps", "reserve-12fps", "limit-15fps") + for scenario_id in scenario_order: + definitions = sorted( + ( + (name, definition) + for name, definition in LOAD_ENVELOPE_RUNS.items() + if definition["scenario_id"] == scenario_id + ), + key=lambda item: int(item[1]["repetition"]), + ) + repetitions: list[dict[str, object]] = [] + queue_high_watermarks: dict[str, int] = {} + for name, definition in definitions: + run = runs[name] + execution = run["execution"] + metrics = run["metrics"] + completion = metrics["world_state_completion_age_ms"] + pipeline = metrics["pipeline_timing"] + decode = pipeline["decode_duration_by_phase_ms"] + pacing = pipeline["delivered_source_pacing_lateness_ms"] + terminal = execution["terminal_outcomes"] + prefetch = execution["loops"][0]["source_prefetch"] + for queue, depth in execution["queue_high_watermarks"].items(): + queue_high_watermarks[queue] = max( + queue_high_watermarks.get(queue, 0), int(depth) + ) + repetitions.append( + { + "run_id": definition["run_id"], + "repetition": definition["repetition"], + "delivered_world_states": execution["delivered_world_states"], + "superseded_frames": terminal.get("superseded", 0), + "delivery_ratio": execution["delivery_ratio"], + "effective_world_state_fps": execution["effective_world_state_fps"], + "world_state_completion_age_p95_ms": completion["p95"], + "hot_loop_decode_p95_ms": decode["hot-loop"]["p95"], + "hot_loop_decode_maximum_ms": decode["hot-loop"]["maximum"], + "preadmission_decode_maximum_ms": decode["preadmission"]["maximum"], + "source_pacing_lateness_maximum_ms": pacing["maximum"], + "source_prefetch_preparation_ms": ( + prefetch["preparation_duration_ns"] / 1_000_000 + ), + "operating_target_gate_passed": run["operating_target_gate_passed"], + "result_sha256": definition["result_sha256"], + "frame_evidence_sha256": execution["frame_evidence"]["sha256"], + } + ) + first_name = definitions[0][0] + first_run = runs[first_name] + run_values = [runs[name] for name, _definition in definitions] + executions = [run["execution"] for run in run_values] + run_metrics = [run["metrics"] for run in run_values] + pipelines = [metrics["pipeline_timing"] for metrics in run_metrics] + completions = [metrics["world_state_completion_age_ms"] for metrics in run_metrics] + gpus = [metrics["gpu"] for metrics in run_metrics] + all_integrity = all(run["evidence_integrity_gate_passed"] for run in run_values) + all_targets = all(run["operating_target_gate_passed"] for run in run_values) scenarios.append( { - "id": name, - "load_purpose": execution["load_purpose"], - "requested_source_rate_hz": execution["requested_source_rate_hz"], - "source_frames_admitted": execution["admitted_frames"], - "delivered_world_states": execution["delivered_world_states"], - "superseded_frames": terminal.get("superseded", 0), - "delivery_ratio": execution["delivery_ratio"], - "effective_world_state_fps": execution["effective_world_state_fps"], - "world_state_completion_age_p95_ms": completion["p95"], - "world_state_completion_age_p99_ms": completion["p99"], - "world_state_completion_age_maximum_ms": completion["maximum"], - "decode_p95_ms": pipeline["decode_duration_ms"]["p95"], - "decode_maximum_ms": pipeline["decode_duration_ms"]["maximum"], - "detector_p95_ms": pipeline["detector_ms"]["total"]["p95"], - "detector_maximum_ms": pipeline["detector_ms"]["total"]["maximum"], - "gpu_utilization_mean_percent": gpu["gpu_utilization_percent"]["mean"], - "gpu_utilization_maximum_percent": gpu["gpu_utilization_percent"]["maximum"], - "gpu_memory_maximum_mib": gpu["gpu_memory_used_mib"]["maximum"], - "process_peak_rss_mib": metrics["process_peak_rss_after_mib"], - "queue_high_watermarks": execution["queue_high_watermarks"], + "id": scenario_id, + "load_purpose": first_run["execution"]["load_purpose"], + "requested_source_rate_hz": first_run["execution"][ + "requested_source_rate_hz" + ], + "repeat_count": len(repetitions), + "all_repetitions_passed": all_integrity and all_targets, + "source_frames_admitted": min( + execution["admitted_frames"] for execution in executions + ), + "delivered_world_states": min( + execution["delivered_world_states"] for execution in executions + ), + "superseded_frames": max( + execution["terminal_outcomes"].get("superseded", 0) + for execution in executions + ), + "delivery_ratio": min(execution["delivery_ratio"] for execution in executions), + "effective_world_state_fps": min( + execution["effective_world_state_fps"] for execution in executions + ), + "world_state_completion_age_p95_ms": max( + completion["p95"] for completion in completions + ), + "world_state_completion_age_p99_ms": max( + completion["p99"] for completion in completions + ), + "world_state_completion_age_maximum_ms": max( + completion["maximum"] for completion in completions + ), + "hot_loop_decode_p95_ms": max( + pipeline["decode_duration_by_phase_ms"]["hot-loop"]["p95"] + for pipeline in pipelines + ), + "hot_loop_decode_maximum_ms": max( + pipeline["decode_duration_by_phase_ms"]["hot-loop"]["maximum"] + for pipeline in pipelines + ), + "preadmission_decode_maximum_ms": max( + pipeline["decode_duration_by_phase_ms"]["preadmission"]["maximum"] + for pipeline in pipelines + ), + "source_prefetch_preparation_maximum_ms": max( + repetition["source_prefetch_preparation_ms"] + for repetition in repetitions + ), + "source_pacing_lateness_p95_ms": max( + pipeline["delivered_source_pacing_lateness_ms"]["p95"] + for pipeline in pipelines + ), + "source_pacing_lateness_maximum_ms": max( + pipeline["delivered_source_pacing_lateness_ms"]["maximum"] + for pipeline in pipelines + ), + "detector_p95_ms": max( + pipeline["detector_ms"]["total"]["p95"] for pipeline in pipelines + ), + "detector_maximum_ms": max( + pipeline["detector_ms"]["total"]["maximum"] + for pipeline in pipelines + ), + "gpu_utilization_mean_percent": max( + gpu["gpu_utilization_percent"]["mean"] for gpu in gpus + ), + "gpu_utilization_maximum_percent": max( + gpu["gpu_utilization_percent"]["maximum"] for gpu in gpus + ), + "gpu_memory_maximum_mib": max( + gpu["gpu_memory_used_mib"]["maximum"] for gpu in gpus + ), + "process_peak_rss_mib": max( + metrics["process_peak_rss_after_mib"] for metrics in run_metrics + ), + "queue_high_watermarks": queue_high_watermarks, "queue_capacity": 2, - "additional_inference_passes": pipeline["additional_inference_passes"], - "integrity_gate_passed": run["evidence_integrity_gate_passed"], - "operating_target_gate_passed": run["operating_target_gate_passed"], - "thresholds": thresholds, - "frame_evidence_sha256": execution["frame_evidence"]["sha256"], + "additional_inference_passes": max( + pipeline["additional_inference_passes"] for pipeline in pipelines + ), + "integrity_gate_passed": all_integrity, + "operating_target_gate_passed": all_targets, + "thresholds": first_run["predeclared_thresholds"], + "repetitions": repetitions, } ) + production_passed = cast(bool, scenarios[0]["all_repetitions_passed"]) + reserve_passed = cast(bool, scenarios[1]["all_repetitions_passed"]) + limit_passed = cast(bool, scenarios[2]["all_repetitions_passed"]) return { "schema_version": LOAD_ENVELOPE_SCHEMA, - "production_rate_repeatability_passed": False, - "reserve_12_fps_passed": True, - "limit_15_fps_passed": True, - "load_envelope_accepted": False, - "compute_capacity_at_least_fps": 15.0, + "repeat_count_per_rate": 3, + "production_rate_repeatability_passed": production_passed, + "reserve_12_fps_passed": reserve_passed, + "limit_15_fps_passed": limit_passed, + "load_envelope_accepted": production_passed and reserve_passed, + "compute_capacity_at_least_fps": ( + 15.0 if limit_passed else 12.0 if reserve_passed else 10.0 + ), "bottleneck_interpretation": ( - "rare-source-decode-or-scheduling-tail-not-steady-gpu-saturation" + "cold-video-decode-isolated-before-admission-no-steady-gpu-saturation" ), "scenarios": scenarios, } @@ -1261,10 +1499,10 @@ def _artifact_manifest(root: Path) -> list[dict[str, object]]: ) role = "upstream-runtime-hardening-evidence" elif relative == "runtime-load-envelope-profile.json": - schema_version = "missioncore.m48s-load-envelope-profile/v1" + schema_version = "missioncore.m48s-load-envelope-profile/v2" role = "predeclared-load-envelope-profile" elif relative.startswith("runtime-load-"): - schema_version = "missioncore.m48s-reference-graph-shadow-load/v4" + schema_version = "missioncore.m48s-reference-graph-shadow-load/v5" role = "upstream-load-envelope-evidence" artifacts.append( { diff --git a/src/k1link/web/m48s_fixed_class_detector_lab_api.py b/src/k1link/web/m48s_fixed_class_detector_lab_api.py index dfb2302..6d8ca8b 100644 --- a/src/k1link/web/m48s_fixed_class_detector_lab_api.py +++ b/src/k1link/web/m48s_fixed_class_detector_lab_api.py @@ -305,6 +305,7 @@ def _load_result_uncached(candidate: Path) -> dict[str, Any]: decision = manifest.get("decision") method = manifest.get("method") metrics = manifest.get("metrics") + load_envelope = metrics.get("load_envelope") if isinstance(metrics, dict) else None status = manifest.get("status") integrated = status == INTEGRATED_STATUS if ( @@ -332,13 +333,18 @@ def _load_result_uncached(candidate: Path) -> dict[str, Any]: or (integrated and decision.get("integrated_world_state_gate_passed") is not True) or (integrated and decision.get("detector_replacement_authorized") is not False) or ( - metrics.get("load_envelope") is not None + load_envelope is not None and ( decision.get("load_envelope_evaluated") is not True - or decision.get("production_rate_repeatability_passed") is not False - or decision.get("reserve_12_fps_passed") is not True - or decision.get("limit_15_fps_passed") is not True - or decision.get("load_envelope_accepted") is not False + or not isinstance(load_envelope, dict) + or decision.get("production_rate_repeatability_passed") + is not load_envelope.get("production_rate_repeatability_passed") + or decision.get("reserve_12_fps_passed") + is not load_envelope.get("reserve_12_fps_passed") + or decision.get("limit_15_fps_passed") + is not load_envelope.get("limit_15_fps_passed") + or decision.get("load_envelope_accepted") + is not load_envelope.get("load_envelope_accepted") ) ) or decision.get("production_accepted") is not False @@ -435,10 +441,104 @@ def _valid_runtime_hardening(value: object) -> bool: def _valid_load_envelope(value: object) -> bool: - if not isinstance(value, dict) or value.get("schema_version") != ( - "missioncore.m48s-load-envelope-comparison/v1" + if not isinstance(value, dict): + return False + if value.get("schema_version") == "missioncore.m48s-load-envelope-comparison/v1": + return _valid_legacy_load_envelope(value) + if value.get("schema_version") != "missioncore.m48s-load-envelope-comparison/v2": + return False + if ( + value.get("repeat_count_per_rate") != 3 + or value.get("production_rate_repeatability_passed") is not True + or value.get("reserve_12_fps_passed") is not True + or value.get("limit_15_fps_passed") is not True + or value.get("load_envelope_accepted") is not True + or value.get("compute_capacity_at_least_fps") != 15.0 + or value.get("bottleneck_interpretation") + != "cold-video-decode-isolated-before-admission-no-steady-gpu-saturation" ): return False + scenarios = value.get("scenarios") + scenario_ids = [item.get("id") for item in scenarios if isinstance(item, dict)] if ( + isinstance(scenarios, list) + ) else [] + if scenario_ids != ["production-10fps", "reserve-12fps", "limit-15fps"]: + return False + required_numeric = { + "requested_source_rate_hz", + "source_frames_admitted", + "delivered_world_states", + "superseded_frames", + "delivery_ratio", + "effective_world_state_fps", + "world_state_completion_age_p95_ms", + "world_state_completion_age_p99_ms", + "world_state_completion_age_maximum_ms", + "hot_loop_decode_p95_ms", + "hot_loop_decode_maximum_ms", + "preadmission_decode_maximum_ms", + "source_prefetch_preparation_maximum_ms", + "source_pacing_lateness_p95_ms", + "source_pacing_lateness_maximum_ms", + "detector_p95_ms", + "detector_maximum_ms", + "gpu_utilization_mean_percent", + "gpu_utilization_maximum_percent", + "gpu_memory_maximum_mib", + "process_peak_rss_mib", + "queue_capacity", + "additional_inference_passes", + } + for scenario in scenarios: + repetitions = scenario.get("repetitions") if isinstance(scenario, dict) else None + if ( + not isinstance(scenario, dict) + or scenario.get("repeat_count") != 3 + or scenario.get("all_repetitions_passed") is not True + or scenario.get("integrity_gate_passed") is not True + or scenario.get("operating_target_gate_passed") is not True + or scenario.get("additional_inference_passes") != 0 + or scenario.get("queue_capacity") != 2 + or not isinstance(scenario.get("queue_high_watermarks"), dict) + or any(value != 2 for value in scenario["queue_high_watermarks"].values()) + or not isinstance(scenario.get("thresholds"), dict) + or any(not _nonnegative_number(scenario.get(key)) for key in required_numeric) + or not isinstance(repetitions, list) + or len(repetitions) != 3 + ): + return False + for expected_repetition, repetition in enumerate(repetitions, start=1): + if ( + not isinstance(repetition, dict) + or repetition.get("repetition") != expected_repetition + or repetition.get("operating_target_gate_passed") is not True + or not isinstance(repetition.get("run_id"), str) + or not repetition["run_id"] + or any( + not _nonnegative_number(repetition.get(key)) + for key in { + "delivered_world_states", + "superseded_frames", + "delivery_ratio", + "effective_world_state_fps", + "world_state_completion_age_p95_ms", + "hot_loop_decode_p95_ms", + "hot_loop_decode_maximum_ms", + "preadmission_decode_maximum_ms", + "source_pacing_lateness_maximum_ms", + } + ) + or any( + not isinstance(repetition.get(key), str) + or SHA256.fullmatch(repetition[key]) is None + for key in ("result_sha256", "frame_evidence_sha256") + ) + ): + return False + return True + + +def _valid_legacy_load_envelope(value: dict[str, object]) -> bool: if ( value.get("production_rate_repeatability_passed") is not False or value.get("reserve_12_fps_passed") is not True @@ -450,10 +550,9 @@ def _valid_load_envelope(value: object) -> bool: ): return False scenarios = value.get("scenarios") - scenario_ids = [item.get("id") for item in scenarios if isinstance(item, dict)] if ( - isinstance(scenarios, list) - ) else [] - if scenario_ids != ["production-10fps", "reserve-12fps", "limit-15fps"]: + if not isinstance(scenarios, list) or [ + item.get("id") for item in scenarios if isinstance(item, dict) + ] != ["production-10fps", "reserve-12fps", "limit-15fps"]: return False expected_targets = (False, True, True) required_numeric = { @@ -485,7 +584,7 @@ def _valid_load_envelope(value: object) -> bool: or scenario.get("additional_inference_passes") != 0 or scenario.get("queue_capacity") != 2 or not isinstance(scenario.get("queue_high_watermarks"), dict) - or any(value != 2 for value in scenario["queue_high_watermarks"].values()) + or any(depth != 2 for depth in scenario["queue_high_watermarks"].values()) or not isinstance(scenario.get("thresholds"), dict) or any(not _nonnegative_number(scenario.get(key)) for key in required_numeric) or not isinstance(scenario.get("frame_evidence_sha256"), str) diff --git a/tests/test_m48s_fixed_class_detector_lab.py b/tests/test_m48s_fixed_class_detector_lab.py index 7907e86..31f132d 100644 --- a/tests/test_m48s_fixed_class_detector_lab.py +++ b/tests/test_m48s_fixed_class_detector_lab.py @@ -38,7 +38,7 @@ def test_m48s_lab_seals_visual_comparison_and_load_evidence(tmp_path: Path) -> N assert manifest["result_id"] == result.result_id assert result.result_id.endswith(identity_digest) assert manifest["identity_sha256"] == identity_digest - assert len(manifest["artifacts"]) == 39 + assert len(manifest["artifacts"]) == 45 assert manifest["method"]["completeness"] == "complete" assert manifest["bounded_question_accepted"] is True assert manifest["ground_truth"] is False @@ -50,10 +50,10 @@ def test_m48s_lab_seals_visual_comparison_and_load_evidence(tmp_path: Path) -> N "integrated_world_state_gate_passed": True, "full_replay_visual_published": True, "load_envelope_evaluated": True, - "production_rate_repeatability_passed": False, + "production_rate_repeatability_passed": True, "reserve_12_fps_passed": True, "limit_15_fps_passed": True, - "load_envelope_accepted": False, + "load_envelope_accepted": True, "detector_replacement_authorized": False, "production_accepted": False, } @@ -86,22 +86,31 @@ def test_m48s_lab_seals_visual_comparison_and_load_evidence(tmp_path: Path) -> N assert hardening["startup"]["prewarm_duration_ms"] == 420.721918 assert hardening["startup"]["validation_frames"] == 1_000 envelope = manifest["metrics"]["load_envelope"] - assert envelope["schema_version"] == "missioncore.m48s-load-envelope-comparison/v1" - assert envelope["production_rate_repeatability_passed"] is False + assert envelope["schema_version"] == "missioncore.m48s-load-envelope-comparison/v2" + assert envelope["repeat_count_per_rate"] == 3 + assert envelope["production_rate_repeatability_passed"] is True assert envelope["reserve_12_fps_passed"] is True assert envelope["limit_15_fps_passed"] is True - assert envelope["load_envelope_accepted"] is False + assert envelope["load_envelope_accepted"] is True assert envelope["compute_capacity_at_least_fps"] == 15.0 production, reserve, limit = envelope["scenarios"] - assert production["delivered_world_states"] == 4_483 - assert production["superseded_frames"] == 6 - assert production["operating_target_gate_passed"] is False - assert reserve["delivered_world_states"] == 4_477 - assert reserve["superseded_frames"] == 12 + assert production["delivered_world_states"] >= 4_485 + assert production["superseded_frames"] <= 5 + assert production["operating_target_gate_passed"] is True + assert reserve["delivered_world_states"] >= 4_467 + assert reserve["superseded_frames"] <= 22 assert reserve["operating_target_gate_passed"] is True assert limit["delivered_world_states"] == 4_488 assert limit["superseded_frames"] == 1 assert limit["operating_target_gate_passed"] is True + assert all(item["repeat_count"] == 3 for item in envelope["scenarios"]) + assert all(len(item["repetitions"]) == 3 for item in envelope["scenarios"]) + assert all(item["all_repetitions_passed"] is True for item in envelope["scenarios"]) + assert all(item["hot_loop_decode_p95_ms"] < 30 for item in envelope["scenarios"]) + assert all( + item["source_prefetch_preparation_maximum_ms"] > 3_000 + for item in envelope["scenarios"] + ) assert all(item["additional_inference_passes"] == 0 for item in envelope["scenarios"]) catalog = json.loads((result.result_root / "catalog.json").read_text("utf-8")) @@ -122,7 +131,7 @@ def test_m48s_lab_seals_visual_comparison_and_load_evidence(tmp_path: Path) -> N ) proof = verify_laboratory_evidence_result(definition, result.result_root) assert proof["result_id"] == result.result_id - assert proof["artifact_count"] == 39 + assert proof["artifact_count"] == 45 with pytest.raises(M48SFixedClassDetectorLabError, match="already exists"): build_m48s_fixed_class_detector_lab( diff --git a/tests/test_m48s_fixed_class_detector_lab_api.py b/tests/test_m48s_fixed_class_detector_lab_api.py index dca0e95..b838b89 100644 --- a/tests/test_m48s_fixed_class_detector_lab_api.py +++ b/tests/test_m48s_fixed_class_detector_lab_api.py @@ -52,9 +52,11 @@ def test_m48s_lab_api_projects_verified_result_frame_and_camera(tmp_path: Path) assert hardening["hardened"]["delivered_world_states"] == 4_488 assert hardening["startup"]["prewarmed"]["world_state_ms"] == 60.516957 envelope = result.json()["metrics"]["load_envelope"] - assert envelope["production_rate_repeatability_passed"] is False + assert envelope["production_rate_repeatability_passed"] is True assert envelope["reserve_12_fps_passed"] is True assert envelope["limit_15_fps_passed"] is True + assert envelope["load_envelope_accepted"] is True + assert envelope["repeat_count_per_rate"] == 3 assert [item["requested_source_rate_hz"] for item in envelope["scenarios"]] == [ 10.0, 12.0, diff --git a/tests/test_m48s_reference_graph_timing.py b/tests/test_m48s_reference_graph_timing.py index d19e806..64dea9a 100644 --- a/tests/test_m48s_reference_graph_timing.py +++ b/tests/test_m48s_reference_graph_timing.py @@ -6,7 +6,7 @@ from pathlib import Path from unittest.mock import patch from k1link.perception.detector import DetectorFrameTiming -from k1link.perception.recorded_source import DecodedFrameTiming +from k1link.perception.recorded_source import DecodedFrameTiming, SourcePacingTiming REPOSITORY_ROOT = Path(__file__).resolve().parents[1] RUNNER_PATH = REPOSITORY_ROOT / "experiments/perception/run_m48s_reference_graph_shadow_worker.py" @@ -19,6 +19,14 @@ SPEC.loader.exec_module(RUNNER) def test_frame_timing_store_closes_provider_and_unattributed_time() -> None: store = RUNNER.FrameTimingStore() store.observe_decode(DecodedFrameTiming(sequence=7, duration_ns=5)) + store.observe_pacing( + SourcePacingTiming( + sequence=7, + scheduled_monotonic_ns=80, + emitted_monotonic_ns=90, + lateness_ns=10, + ) + ) store.observe_detector( DetectorFrameTiming( sequence=7, @@ -50,6 +58,14 @@ def test_frame_timing_store_closes_provider_and_unattributed_time() -> None: def test_pipeline_timing_metrics_preserve_single_pass_stage_breakdown() -> None: store = RUNNER.FrameTimingStore() store.observe_decode(DecodedFrameTiming(sequence=3, duration_ns=1_000_000)) + store.observe_pacing( + SourcePacingTiming( + sequence=3, + scheduled_monotonic_ns=0, + emitted_monotonic_ns=1_000_000, + lateness_ns=1_000_000, + ) + ) store.observe_detector( DetectorFrameTiming( sequence=3, @@ -79,6 +95,7 @@ def test_pipeline_timing_metrics_preserve_single_pass_stage_breakdown() -> None: assert metrics["detector_ms"]["inference_transport"]["maximum"] == 3.0 assert metrics["provider_ms"]["threat"]["maximum"] == 5.0 assert metrics["graph_unattributed_ms"]["maximum"] == 6.0 + assert metrics["delivered_source_pacing_lateness_ms"]["maximum"] == 1.0 assert metrics["maximum_graph_sequence"] == 3 assert metrics["additional_inference_passes"] == 0