feat(perception): qualify E35 degradation recovery
This commit is contained in:
@@ -206,6 +206,99 @@ function e34() {
|
||||
};
|
||||
}
|
||||
|
||||
function e35() {
|
||||
return {
|
||||
result_id: `e35-degradation-recovery-${"5".repeat(64)}`,
|
||||
created_at_utc: "2026-07-27T15:00:00Z",
|
||||
source_session_id: "20260720T065719Z_viewer_live",
|
||||
status: "accepted-deterministic-degradation-recovery",
|
||||
e32_result_id: e32().result_id,
|
||||
e33_result_id: e33().result_id,
|
||||
e34_result_id: e34().result_id,
|
||||
profile_id: "e35-six-channel-degradation-recovery/v1",
|
||||
pipeline_id: "track-geometry/temporal-layer/degradation-recovery/v1",
|
||||
coordinate_frame: "map",
|
||||
configuration: {
|
||||
maximum_recovery_seconds: 0.25,
|
||||
scenario_count: 6,
|
||||
},
|
||||
metrics: {
|
||||
source_frames: 4489,
|
||||
variant_count: 6,
|
||||
variant_frame_outcomes: 26934,
|
||||
injection_records: 360,
|
||||
variant_frame_processing_p95_ms: 1.08,
|
||||
build_elapsed_ms: 18618,
|
||||
},
|
||||
scenarios: [{
|
||||
scenario_id: "camera-loss",
|
||||
kind: "camera-loss",
|
||||
frame_start: 600,
|
||||
frame_end: 659,
|
||||
frames_processed: 4489,
|
||||
injected_frames: 60,
|
||||
dropped_frames: 0,
|
||||
hidden_success_frames: 0,
|
||||
false_free_rows: 0,
|
||||
semantic_claims_during_camera_loss: 0,
|
||||
metric_rows_during_lidar_or_pose_loss: 0,
|
||||
agree_claims_during_timing_offset: 0,
|
||||
late_results_reintroduced: 0,
|
||||
maximum_current_components_during_fault: 13,
|
||||
maximum_held_components_during_fault: 24,
|
||||
expired_components_during_fault: 60,
|
||||
recovery_frame_index: 660,
|
||||
recovery_seconds: 0.086,
|
||||
}],
|
||||
review: {
|
||||
schema_version: "missioncore.e35-recovery-review/v1",
|
||||
result_id: `e35-degradation-recovery-${"5".repeat(64)}`,
|
||||
scenarios: [{
|
||||
scenario: {
|
||||
schema_version: "missioncore.e35-degradation-scenario/v1",
|
||||
scenario_id: "camera-loss",
|
||||
kind: "camera-loss",
|
||||
frame_start: 600,
|
||||
frame_end: 659,
|
||||
parameters: { drop_camera_observations: true },
|
||||
},
|
||||
frames: [{
|
||||
scenario_id: "camera-loss",
|
||||
kind: "camera-loss",
|
||||
frame_index: 600,
|
||||
source_frame_index: 600,
|
||||
session_seconds: 95.4,
|
||||
fault_phase: "during",
|
||||
action: "camera-observations-removed",
|
||||
channels: {
|
||||
camera: "unavailable",
|
||||
lidar: "available",
|
||||
pose: "available",
|
||||
delivery: "on-time",
|
||||
},
|
||||
input_point_rows: 300,
|
||||
transformed_point_rows: 300,
|
||||
layer_state: "current",
|
||||
counts: { current: 5, held: 2, expired: 0 },
|
||||
components: [{
|
||||
temporal_id: 10,
|
||||
state: "current",
|
||||
occupancy_state: "occupied",
|
||||
owner_kind: "geometry-cluster",
|
||||
semantic_labels: [],
|
||||
last_observed_age_seconds: 0,
|
||||
centroid_map_xyz_m: [1, 2, 0.5],
|
||||
}],
|
||||
cell_centers_map_xyz_m: [[1, 2, 0.5]],
|
||||
}],
|
||||
}],
|
||||
},
|
||||
acceptance: { accepted: true },
|
||||
authority,
|
||||
access: "read-only",
|
||||
};
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
server = await createServer({
|
||||
appType: "custom",
|
||||
@@ -222,9 +315,9 @@ after(async () => {
|
||||
await server?.close();
|
||||
});
|
||||
|
||||
test("decodes E31–E34 from separate read-only catalogs", async () => {
|
||||
test("decodes E31–E35 from separate read-only catalogs", async () => {
|
||||
const requests = [];
|
||||
const items = [e31(), e32(), e33(), e34()];
|
||||
const items = [e31(), e32(), e33(), e34(), e35()];
|
||||
const decoded = await fetchAdvancedLaboratoryResults({
|
||||
fetcher: async (input, init) => {
|
||||
requests.push({ input: String(input), method: init?.method });
|
||||
@@ -241,11 +334,15 @@ test("decodes E31–E34 from separate read-only catalogs", async () => {
|
||||
assert.equal(decoded.e33.metrics.resultAgeMaxMs, 13.638);
|
||||
assert.equal(decoded.e34.metrics.processedFrames, 4489);
|
||||
assert.equal(decoded.e34.reviewFrames[0].components[0].state, "expired");
|
||||
assert.equal(decoded.e35.metrics.variantFrameOutcomes, 26934);
|
||||
assert.equal(decoded.e35.scenarios[0].recoverySeconds, 0.086);
|
||||
assert.equal(decoded.e35.reviewScenarios[0].frames[0].faultPhase, "during");
|
||||
assert.deepEqual(requests, [
|
||||
{ input: "/api/v1/laboratory/e31/results?limit=1", method: "GET" },
|
||||
{ input: "/api/v1/laboratory/e32/results?limit=1", method: "GET" },
|
||||
{ input: "/api/v1/laboratory/e33/results?limit=1", method: "GET" },
|
||||
{ input: "/api/v1/laboratory/e34/results?limit=1", method: "GET" },
|
||||
{ input: "/api/v1/laboratory/e35/results?limit=1", method: "GET" },
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -259,7 +356,8 @@ test("rejects authority escalation in an accepted-looking result", async () => {
|
||||
fetcher: async (input) => new Response(JSON.stringify(catalog(
|
||||
String(input).includes("/e31/") ? forged
|
||||
: String(input).includes("/e32/") ? e32()
|
||||
: String(input).includes("/e33/") ? e33() : e34(),
|
||||
: String(input).includes("/e33/") ? e33()
|
||||
: String(input).includes("/e34/") ? e34() : e35(),
|
||||
)), { status: 200 }),
|
||||
}),
|
||||
AdvancedLaboratoryContractError,
|
||||
|
||||
@@ -26,6 +26,10 @@ const e34ResultUrl = new URL(
|
||||
"../src/workspaces/laboratory/E34Result.tsx",
|
||||
import.meta.url,
|
||||
);
|
||||
const e35ResultUrl = new URL(
|
||||
"../src/workspaces/laboratory/E35Result.tsx",
|
||||
import.meta.url,
|
||||
);
|
||||
const advancedLaboratoryResultUrl = new URL(
|
||||
"../src/workspaces/laboratory/AdvancedLaboratoryResult.tsx",
|
||||
import.meta.url,
|
||||
@@ -161,6 +165,25 @@ test("E34 keeps temporal evidence inside the canonical LAB and viewer contracts"
|
||||
assert.match(advancedSource, /<E34Result/);
|
||||
});
|
||||
|
||||
test("E35 extends the canonical LAB with fault and recovery evidence", async () => {
|
||||
const [e35Source, advancedSource] = await Promise.all([
|
||||
readFile(e35ResultUrl, "utf8"),
|
||||
readFile(advancedLaboratoryResultUrl, "utf8"),
|
||||
]);
|
||||
|
||||
assert.match(e35Source, /<LaboratoryWorkTemplate/);
|
||||
assert.match(e35Source, /<LaboratoryEvidenceViewer/);
|
||||
assert.match(e35Source, /\{ value: "3d", label: "3D" \}/);
|
||||
assert.match(e35Source, /\{ value: "plan", label: "План" \}/);
|
||||
assert.match(e35Source, /Шесть неизменяемых полных replay-вариантов/);
|
||||
assert.match(e35Source, /небезопасных утверждений —/);
|
||||
assert.match(e35Source, /Следующий критический gate — A9\/E36/);
|
||||
assert.match(e35Source, /SCENARIO_LABELS/);
|
||||
assert.match(e35Source, /PHASE_LABELS/);
|
||||
assert.match(advancedSource, /id: "e35-degradation-recovery"/);
|
||||
assert.match(advancedSource, /<E35Result/);
|
||||
});
|
||||
|
||||
test("the primary point-cloud viewer restores from fullscreen on Escape", async () => {
|
||||
const workspacesSource = await readFile(workspacesUrl, "utf8");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user