feat(lab): complete E30 evidence review gate
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readdir, readFile } from "node:fs/promises";
|
||||
import { extname, join } from "node:path";
|
||||
import { test } from "node:test";
|
||||
|
||||
const sourceRoot = new URL("../src/", import.meta.url);
|
||||
|
||||
async function sourceFiles(relativeDirectory) {
|
||||
const directory = new URL(`${relativeDirectory}/`, sourceRoot);
|
||||
const entries = await readdir(directory, {
|
||||
recursive: true,
|
||||
withFileTypes: true,
|
||||
});
|
||||
return entries
|
||||
.filter((entry) => entry.isFile() && [".ts", ".tsx"].includes(extname(entry.name)))
|
||||
.map((entry) => join(entry.parentPath, entry.name));
|
||||
}
|
||||
|
||||
async function read(relativePath) {
|
||||
return readFile(new URL(relativePath, sourceRoot), "utf8");
|
||||
}
|
||||
|
||||
test("dependency direction keeps core and reusable components below workspaces", async () => {
|
||||
const coreFiles = await sourceFiles("core");
|
||||
const componentFiles = await sourceFiles("components");
|
||||
|
||||
for (const file of coreFiles) {
|
||||
const source = await readFile(file, "utf8");
|
||||
assert.doesNotMatch(
|
||||
source,
|
||||
/from\s+["'][^"']*(?:workspaces|\/App)["']/,
|
||||
`${file} imports an application composition layer`,
|
||||
);
|
||||
assert.doesNotMatch(
|
||||
source,
|
||||
/@nodedc\/ui-(?:react|dom)/,
|
||||
`${file} imports a visual adapter from the domain layer`,
|
||||
);
|
||||
}
|
||||
|
||||
for (const file of componentFiles) {
|
||||
const source = await readFile(file, "utf8");
|
||||
assert.doesNotMatch(
|
||||
source,
|
||||
/from\s+["'][^"']*(?:workspaces|\/App)["']/,
|
||||
`${file} imports a workspace composition layer`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test("shared visual primitives come only from the Design Guideline packages", async () => {
|
||||
const packageJson = JSON.parse(
|
||||
await readFile(new URL("../package.json", import.meta.url), "utf8"),
|
||||
);
|
||||
for (const packageName of [
|
||||
"@nodedc/tokens",
|
||||
"@nodedc/ui-core",
|
||||
"@nodedc/ui-react",
|
||||
]) {
|
||||
assert.equal(typeof packageJson.dependencies[packageName], "string");
|
||||
}
|
||||
|
||||
const files = await sourceFiles(".");
|
||||
for (const file of files) {
|
||||
const source = await readFile(file, "utf8");
|
||||
assert.doesNotMatch(source, /lucide-react/);
|
||||
assert.doesNotMatch(source, /NODEDC_DESIGN_GUIDELINE\/(?:apps|src)\//);
|
||||
}
|
||||
});
|
||||
|
||||
test("laboratory UI is a bounded feature slice, not a central workspace branch", async () => {
|
||||
const workspaceHub = await read("workspaces/Workspaces.tsx");
|
||||
const laboratory = await read(
|
||||
"workspaces/laboratory/LaboratoryArchiveWorkspace.tsx",
|
||||
);
|
||||
const workspaceCss = await read("styles/workspaces.css");
|
||||
const laboratoryCss = await read("styles/laboratory.css");
|
||||
const e30Review = await read("workspaces/E30ReviewWorkspace.tsx");
|
||||
const e30HumanReview = await read(
|
||||
"workspaces/E30HumanReviewPanel.tsx",
|
||||
);
|
||||
const e30HumanReviewCss = await read("styles/e30-human-review.css");
|
||||
|
||||
assert.doesNotMatch(
|
||||
workspaceHub,
|
||||
/fetchE(?:29|30)|LaboratoryWorkTemplate|function\s+E(?:29|30)LaboratoryResult/,
|
||||
);
|
||||
assert.match(
|
||||
workspaceHub,
|
||||
/<LaboratoryArchiveWorkspace[\s\S]*SpatialView=\{SpatialWorkspace\}/,
|
||||
);
|
||||
assert.match(laboratory, /export function LaboratoryArchiveWorkspace/);
|
||||
assert.match(laboratory, /type LaboratoryWorkspaceProps = WorkspaceRendererProps/);
|
||||
assert.doesNotMatch(workspaceCss, /\.(?:lab-|laboratory-|e30-)/);
|
||||
assert.match(laboratoryCss, /\.laboratory-work-template/);
|
||||
assert.match(laboratoryCss, /\.e30-review-workspace/);
|
||||
assert.doesNotMatch(laboratory, /E30HumanReviewPanel/);
|
||||
assert.match(e30Review, /<E30HumanReviewPanel/);
|
||||
assert.doesNotMatch(e30Review, /E30EngineeringAuditPanel/);
|
||||
assert.match(e30HumanReview, /from "@nodedc\/ui-react"/);
|
||||
assert.doesNotMatch(laboratoryCss, /\.e30-human-review/);
|
||||
assert.match(e30HumanReviewCss, /\.e30-human-review/);
|
||||
});
|
||||
|
||||
test("central composition files cannot silently become monoliths again", async () => {
|
||||
const ratchets = [
|
||||
["App.tsx", 1_250],
|
||||
["workspaces/Workspaces.tsx", 1_200],
|
||||
["workspaces/laboratory/LaboratoryArchiveWorkspace.tsx", 1_000],
|
||||
["styles/workspaces.css", 4_350],
|
||||
["styles/laboratory.css", 900],
|
||||
];
|
||||
|
||||
for (const [relativePath, maximumLines] of ratchets) {
|
||||
const lineCount = (await read(relativePath)).split("\n").length;
|
||||
assert.ok(
|
||||
lineCount <= maximumLines,
|
||||
`${relativePath} has ${lineCount} lines; split the feature instead of raising ${maximumLines}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,194 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { after, before, test } from "node:test";
|
||||
|
||||
import { createServer } from "vite";
|
||||
|
||||
let server;
|
||||
let parseE30EngineeringCatalog;
|
||||
let parseE30EngineeringDecision;
|
||||
let fetchE30EngineeringDecision;
|
||||
let E30EngineeringContractError;
|
||||
|
||||
const resultId = `e30-materialization-${"a".repeat(64)}`;
|
||||
const generationId = `e30-engineering-generation-${"b".repeat(64)}`;
|
||||
const itemId = `e30-review-item-${"c".repeat(64)}`;
|
||||
|
||||
function generation(overrides = {}) {
|
||||
return {
|
||||
generation_id: generationId,
|
||||
created_at_utc: "2026-07-26T22:01:18.464Z",
|
||||
materialization_id: resultId,
|
||||
producer: {
|
||||
kind: "ai-assisted-engineering-review",
|
||||
producer_id: "codex:a3-engineering-review",
|
||||
method_id: "camera-lidar-42-sheet-audit/v1",
|
||||
review_sheet_identity_sha256: "d".repeat(64),
|
||||
claims_human_ground_truth: false,
|
||||
},
|
||||
summary: {
|
||||
item_count: 486,
|
||||
reviewed_item_count: 486,
|
||||
verdict_distribution: {
|
||||
confirmed: 401,
|
||||
corrected: 80,
|
||||
"insufficient-evidence": 5,
|
||||
},
|
||||
detector_distribution: { valid: 259, "false-positive": 78 },
|
||||
projection_distribution: { aligned: 371, "not-assessable": 115 },
|
||||
point_ownership_distribution: { object: 108 },
|
||||
human_exception_count: 5,
|
||||
mean_confidence: 0.8705,
|
||||
},
|
||||
cause_distribution: {
|
||||
schema_version: "missioncore.e30-engineering-causes/v1",
|
||||
item_count_with_cause: 312,
|
||||
reasons: [
|
||||
{ reason_code: "detector_error", count: 109 },
|
||||
{ reason_code: "time_mismatch", count: 91 },
|
||||
],
|
||||
},
|
||||
human_exceptions: [{
|
||||
item_id: itemId,
|
||||
review_key: "semantic:162:7",
|
||||
source_stratum: "camera-only",
|
||||
confidence: 0.48,
|
||||
review_prompt: {
|
||||
question: "Белый кластер — самостоятельное препятствие?",
|
||||
focus: "Проверьте выбранные белые точки.",
|
||||
effects: {
|
||||
"object-present": "Сохранить занятую геометрию.",
|
||||
"background-or-noise": "Исключить кластер.",
|
||||
"insufficient-evidence": "Оставить неизвестным.",
|
||||
},
|
||||
},
|
||||
}],
|
||||
ai_review_complete: true,
|
||||
human_exception_complete: false,
|
||||
human_review_complete: false,
|
||||
lab_published: false,
|
||||
access: "read-only",
|
||||
authority: {
|
||||
commands_enabled: false,
|
||||
navigation_or_safety_accepted: false,
|
||||
},
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function catalog(overrides = {}) {
|
||||
return {
|
||||
schema_version: "missioncore.laboratory-e30-engineering-generations/v1",
|
||||
configured: true,
|
||||
items: [generation()],
|
||||
candidate_total: 1,
|
||||
invalid_total: 0,
|
||||
access: "read-only",
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function decision(overrides = {}) {
|
||||
return {
|
||||
schema_version: "missioncore.laboratory-e30-engineering-decision/v1",
|
||||
generation_id: generationId,
|
||||
decision: {
|
||||
sequence: 362,
|
||||
item_id: itemId,
|
||||
review_key: "semantic:343:2",
|
||||
source_stratum: "conflict",
|
||||
verdict: "corrected",
|
||||
effective_stratum: null,
|
||||
detector_assessment: "false-positive",
|
||||
projection_assessment: "aligned",
|
||||
point_ownership: "surface-or-background",
|
||||
cause_code: "detector_error",
|
||||
confidence: 0.95,
|
||||
human_exception_required: false,
|
||||
exception_reason: null,
|
||||
evidence_note: "Кадр 343. Рамка находится на ограждении.",
|
||||
review_sheet: {
|
||||
path: "conflict-01.jpg",
|
||||
sha256: "e".repeat(64),
|
||||
ordinal: 1,
|
||||
},
|
||||
...overrides,
|
||||
},
|
||||
access: "read-only",
|
||||
};
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
server = await createServer({
|
||||
appType: "custom",
|
||||
logLevel: "silent",
|
||||
server: { middlewareMode: true },
|
||||
});
|
||||
({
|
||||
parseE30EngineeringCatalog,
|
||||
parseE30EngineeringDecision,
|
||||
fetchE30EngineeringDecision,
|
||||
E30EngineeringContractError,
|
||||
} = await server.ssrLoadModule(
|
||||
"/src/core/laboratory/e30Engineering.ts",
|
||||
));
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await server?.close();
|
||||
});
|
||||
|
||||
test("decodes immutable A3 summary and per-item engineering decision", () => {
|
||||
const decodedCatalog = parseE30EngineeringCatalog(catalog());
|
||||
const decodedDecision = parseE30EngineeringDecision(decision());
|
||||
|
||||
assert.equal(decodedCatalog.items[0].summary.reviewedItemCount, 486);
|
||||
assert.equal(decodedCatalog.items[0].summary.humanExceptionCount, 5);
|
||||
assert.equal(decodedCatalog.items[0].humanExceptions[0].reviewKey, "semantic:162:7");
|
||||
assert.equal(
|
||||
decodedCatalog.items[0].humanExceptions[0].reviewPrompt.effects["object-present"],
|
||||
"Сохранить занятую геометрию.",
|
||||
);
|
||||
assert.equal(decodedDecision.verdict, "corrected");
|
||||
assert.equal(decodedDecision.effectiveStratum, null);
|
||||
assert.equal(decodedDecision.detectorAssessment, "false-positive");
|
||||
});
|
||||
|
||||
test("rejects a generation that claims human ground truth", () => {
|
||||
assert.throws(
|
||||
() => parseE30EngineeringCatalog(catalog({
|
||||
items: [generation({
|
||||
producer: {
|
||||
...generation().producer,
|
||||
claims_human_ground_truth: true,
|
||||
},
|
||||
})],
|
||||
})),
|
||||
E30EngineeringContractError,
|
||||
);
|
||||
});
|
||||
|
||||
test("fetches one content-addressed item decision", async () => {
|
||||
const requests = [];
|
||||
const decoded = await fetchE30EngineeringDecision(
|
||||
resultId,
|
||||
generationId,
|
||||
itemId,
|
||||
{
|
||||
fetcher: async (input, init) => {
|
||||
requests.push({ input: String(input), method: init?.method });
|
||||
return new Response(JSON.stringify(decision()), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
assert.equal(decoded.pointOwnership, "surface-or-background");
|
||||
assert.deepEqual(requests, [{
|
||||
input:
|
||||
`/api/v1/laboratory/e30/reviews/${resultId}/engineering-generations/`
|
||||
+ `${generationId}/items/${itemId}`,
|
||||
method: "GET",
|
||||
}]);
|
||||
});
|
||||
@@ -0,0 +1,207 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { after, before, test } from "node:test";
|
||||
|
||||
import { createServer } from "vite";
|
||||
|
||||
let server;
|
||||
let parseE30HumanReviewDraft;
|
||||
let parseE30HumanReviewFinalized;
|
||||
let createOrResumeE30HumanReview;
|
||||
let saveE30HumanReviewDecision;
|
||||
let finalizeE30HumanReview;
|
||||
let E30ReviewContractError;
|
||||
|
||||
const resultId = `e30-materialization-${"a".repeat(64)}`;
|
||||
const engineeringGenerationId =
|
||||
`e30-engineering-generation-${"b".repeat(64)}`;
|
||||
const draftId = `e30-human-draft-${"c".repeat(64)}`;
|
||||
const reviewGenerationId = `e30-review-generation-${"d".repeat(64)}`;
|
||||
const firstItemId = `e30-review-item-${"e".repeat(64)}`;
|
||||
const secondItemId = `e30-review-item-${"f".repeat(64)}`;
|
||||
const firstEventId = `e30-review-event-${"1".repeat(64)}`;
|
||||
const secondEventId = `e30-review-event-${"2".repeat(64)}`;
|
||||
|
||||
function decision(overrides = {}) {
|
||||
return {
|
||||
item_id: firstItemId,
|
||||
source_stratum: "conflict",
|
||||
disposition: "object-present",
|
||||
notes: null,
|
||||
event_id: firstEventId,
|
||||
decided_at_utc: "2026-07-27T06:00:00Z",
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function draft(overrides = {}) {
|
||||
return {
|
||||
schema_version: "missioncore.e30-human-review-draft/v2",
|
||||
draft_id: draftId,
|
||||
materialization_id: resultId,
|
||||
engineering_generation_id: engineeringGenerationId,
|
||||
reviewer_id: "DC",
|
||||
created_at_utc: "2026-07-27T06:00:00Z",
|
||||
state: "active",
|
||||
revision: 1,
|
||||
item_count: 2,
|
||||
reviewed_item_count: 1,
|
||||
remaining_item_count: 1,
|
||||
disposition_distribution: { "object-present": 1 },
|
||||
generation_id: null,
|
||||
decisions: [decision()],
|
||||
lab_published: false,
|
||||
access: "review-write",
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function finalized() {
|
||||
return {
|
||||
schema_version: "missioncore.laboratory-e30-human-review-finalized/v2",
|
||||
draft: draft({
|
||||
state: "finalized",
|
||||
revision: 2,
|
||||
reviewed_item_count: 2,
|
||||
remaining_item_count: 0,
|
||||
disposition_distribution: {
|
||||
"object-present": 1,
|
||||
"background-or-noise": 1,
|
||||
},
|
||||
generation_id: reviewGenerationId,
|
||||
decisions: [
|
||||
decision(),
|
||||
decision({
|
||||
item_id: secondItemId,
|
||||
source_stratum: "unknown",
|
||||
disposition: "background-or-noise",
|
||||
event_id: secondEventId,
|
||||
}),
|
||||
],
|
||||
access: "read-only",
|
||||
}),
|
||||
generation: {
|
||||
schema_version: "missioncore.e30-human-review-generation/v2",
|
||||
generation_id: reviewGenerationId,
|
||||
materialization_id: resultId,
|
||||
engineering_generation_id: engineeringGenerationId,
|
||||
reviewer_id: "DC",
|
||||
created_at_utc: "2026-07-27T06:05:00Z",
|
||||
item_count: 2,
|
||||
disposition_distribution: {
|
||||
"object-present": 1,
|
||||
"background-or-noise": 1,
|
||||
},
|
||||
coverage: {
|
||||
expected_item_count: 2,
|
||||
reviewed_item_count: 2,
|
||||
complete: true,
|
||||
},
|
||||
human_review_complete: true,
|
||||
lab_published: false,
|
||||
access: "read-only",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
server = await createServer({
|
||||
appType: "custom",
|
||||
logLevel: "silent",
|
||||
server: { middlewareMode: true },
|
||||
});
|
||||
({
|
||||
parseE30HumanReviewDraft,
|
||||
parseE30HumanReviewFinalized,
|
||||
createOrResumeE30HumanReview,
|
||||
saveE30HumanReviewDecision,
|
||||
finalizeE30HumanReview,
|
||||
} = await server.ssrLoadModule(
|
||||
"/src/core/laboratory/e30HumanReview.ts",
|
||||
));
|
||||
({ E30ReviewContractError } = await server.ssrLoadModule(
|
||||
"/src/core/laboratory/e30Review.ts",
|
||||
));
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await server?.close();
|
||||
});
|
||||
|
||||
test("decodes an exception-only draft and immutable final generation", () => {
|
||||
const active = parseE30HumanReviewDraft(draft());
|
||||
const complete = parseE30HumanReviewFinalized(finalized());
|
||||
|
||||
assert.equal(active.engineeringGenerationId, engineeringGenerationId);
|
||||
assert.equal(active.reviewedItemCount, 1);
|
||||
assert.equal(active.decisions[0].disposition, "object-present");
|
||||
assert.equal(complete.draft.state, "finalized");
|
||||
assert.equal(complete.generation.humanReviewComplete, true);
|
||||
assert.equal(complete.generation.labPublished, false);
|
||||
});
|
||||
|
||||
test("rejects unknown dispositions and finalized writable access", () => {
|
||||
assert.throws(
|
||||
() => parseE30HumanReviewDraft(draft({
|
||||
decisions: [decision({ disposition: "invented" })],
|
||||
})),
|
||||
E30ReviewContractError,
|
||||
);
|
||||
assert.throws(
|
||||
() => parseE30HumanReviewDraft(draft({
|
||||
state: "finalized",
|
||||
access: "review-write",
|
||||
})),
|
||||
E30ReviewContractError,
|
||||
);
|
||||
});
|
||||
|
||||
test("uses generation-bound write endpoints and explicit finalization", async () => {
|
||||
const requests = [];
|
||||
const fetcher = async (input, init) => {
|
||||
const body = init?.body ? JSON.parse(String(init.body)) : null;
|
||||
requests.push({ input: String(input), method: init?.method, body });
|
||||
const payload = String(input).includes("/finalize?")
|
||||
? finalized()
|
||||
: draft();
|
||||
return new Response(JSON.stringify(payload), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
};
|
||||
|
||||
await createOrResumeE30HumanReview(
|
||||
resultId,
|
||||
engineeringGenerationId,
|
||||
"DC",
|
||||
{ fetcher },
|
||||
);
|
||||
await saveE30HumanReviewDecision(
|
||||
resultId,
|
||||
engineeringGenerationId,
|
||||
draftId,
|
||||
firstItemId,
|
||||
{
|
||||
expectedRevision: 1,
|
||||
idempotencyKey: "ui-decision-001",
|
||||
disposition: "insufficient-evidence",
|
||||
notes: null,
|
||||
},
|
||||
{ fetcher },
|
||||
);
|
||||
await finalizeE30HumanReview(
|
||||
resultId,
|
||||
engineeringGenerationId,
|
||||
draftId,
|
||||
2,
|
||||
{ fetcher },
|
||||
);
|
||||
|
||||
assert.equal(requests[0].method, "POST");
|
||||
assert.deepEqual(requests[0].body, {
|
||||
reviewer_id: "DC",
|
||||
engineering_generation_id: engineeringGenerationId,
|
||||
});
|
||||
assert.match(requests[1].input, /engineering_generation_id=/);
|
||||
assert.equal(requests[1].body.disposition, "insufficient-evidence");
|
||||
assert.equal(requests[2].body.confirm_generation, true);
|
||||
});
|
||||
@@ -0,0 +1,244 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { after, before, test } from "node:test";
|
||||
|
||||
import { createServer } from "vite";
|
||||
|
||||
let server;
|
||||
let parseE30ReviewCatalog;
|
||||
let parseE30ReviewItems;
|
||||
let parseE30ReviewItemDetail;
|
||||
let fetchE30ReviewItems;
|
||||
let E30ReviewContractError;
|
||||
|
||||
const resultId = `e30-materialization-${"a".repeat(64)}`;
|
||||
const reviewPackId = `e30-review-pack-${"b".repeat(64)}`;
|
||||
const e29ResultId = `e29-camera-geometry-${"c".repeat(64)}`;
|
||||
const itemId = `e30-review-item-${"d".repeat(64)}`;
|
||||
|
||||
function result(overrides = {}) {
|
||||
return {
|
||||
result_id: resultId,
|
||||
created_at_utc: "2026-07-26T00:00:00Z",
|
||||
review_pack_id: reviewPackId,
|
||||
e29_result_id: e29ResultId,
|
||||
source_session_id: "20260720T065719Z_viewer_live",
|
||||
item_count: 486,
|
||||
stratum_counts: {
|
||||
conflict: 38,
|
||||
agree: 96,
|
||||
"camera-only": 128,
|
||||
unknown: 96,
|
||||
"geometry-only": 128,
|
||||
},
|
||||
reason_taxonomy: ["time_mismatch", "geometry_mismatch", "other"],
|
||||
projection: {
|
||||
width: 800,
|
||||
height: 600,
|
||||
source_id: "sensor.camera.right",
|
||||
calibration_slot: "camera_1",
|
||||
},
|
||||
camera_evidence_available: true,
|
||||
human_review_complete: false,
|
||||
lab_published: false,
|
||||
access: "read-only",
|
||||
authority: {
|
||||
commands_enabled: false,
|
||||
navigation_or_safety_accepted: false,
|
||||
},
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function item(overrides = {}) {
|
||||
return {
|
||||
item_id: itemId,
|
||||
sequence: 212,
|
||||
review_key: "semantic:1800:1",
|
||||
stratum: "conflict",
|
||||
range_bucket: "unavailable",
|
||||
frame_index: 1800,
|
||||
source_frame_index: 1800,
|
||||
session_seconds: 215.323857292,
|
||||
locator: {
|
||||
kind: "semantic-observation",
|
||||
observation_index: 1,
|
||||
},
|
||||
snapshot: {
|
||||
label: "car",
|
||||
geometry_status: "conflict",
|
||||
geometry_reason: "camera-object-region-observed-as-local-surface",
|
||||
range_m: null,
|
||||
bbox_xyxy: [223.6, 270.2, 282.4, 305.2],
|
||||
},
|
||||
materialization: {
|
||||
frame_point_count: 1813,
|
||||
projected_point_count: 3,
|
||||
candidate_point_count: 2,
|
||||
selected_point_count: 1,
|
||||
rejected_candidate_point_count: 1,
|
||||
selected_and_candidate_lossless: true,
|
||||
free_space_valid: false,
|
||||
human_review_complete: false,
|
||||
detector_score: 0.83,
|
||||
},
|
||||
camera_frame_available: true,
|
||||
engineering_triage: {
|
||||
provenance: "deterministic-evidence-readiness/v1",
|
||||
state: "ready-for-ai-review",
|
||||
attention: "standard",
|
||||
signals: [],
|
||||
semantic_verdict: null,
|
||||
human_exception_required: null,
|
||||
},
|
||||
review: {
|
||||
state: "unreviewed",
|
||||
reason_code: null,
|
||||
notes: null,
|
||||
},
|
||||
access: "read-only",
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function catalog(overrides = {}) {
|
||||
return {
|
||||
schema_version: "missioncore.laboratory-e30-catalog/v1",
|
||||
configured: true,
|
||||
items: [result()],
|
||||
candidate_total: 1,
|
||||
invalid_total: 0,
|
||||
access: "read-only",
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function items(overrides = {}) {
|
||||
return {
|
||||
schema_version: "missioncore.laboratory-e30-items/v1",
|
||||
result_id: resultId,
|
||||
stratum: "conflict",
|
||||
items: [item()],
|
||||
total: 38,
|
||||
next_cursor: null,
|
||||
reason_taxonomy: ["time_mismatch", "geometry_mismatch", "other"],
|
||||
access: "read-only",
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function detail(overrides = {}) {
|
||||
return {
|
||||
schema_version: "missioncore.laboratory-e30-item-detail/v1",
|
||||
result_id: resultId,
|
||||
item: {
|
||||
...item(),
|
||||
camera_frame: {
|
||||
available: true,
|
||||
url: `/api/v1/laboratory/e30/reviews/${resultId}/items/${itemId}/camera-frame?generation=${"e".repeat(64)}`,
|
||||
sha256: "e".repeat(64),
|
||||
width: 800,
|
||||
height: 600,
|
||||
source_frame_index: 1800,
|
||||
exact_source_frame: true,
|
||||
},
|
||||
selected: {
|
||||
source_indices: [7],
|
||||
points_map_xyz_m: [[1, 2, 3]],
|
||||
},
|
||||
candidate: {
|
||||
source_indices: [7, 8],
|
||||
points_map_xyz_m: [[1, 2, 3], [1.1, 2.1, 3.1]],
|
||||
},
|
||||
projection: {
|
||||
source_indices: [7, 8, 9],
|
||||
points_map_xyz_m: [[1, 2, 3], [1.1, 2.1, 3.1], [2, 3, 4]],
|
||||
pixels_xy: [[220, 270], [230, 275], [500, 400]],
|
||||
depth_m: [3, 3.1, 4],
|
||||
point_class: [2, 1, 0],
|
||||
point_height_m: [0.8, 0.1, 0],
|
||||
candidate_mask: [1, 1, 0],
|
||||
selected_mask: [1, 0, 0],
|
||||
},
|
||||
pose: {
|
||||
position_map_xyz_m: [10, 11, 0],
|
||||
orientation_map_from_lidar_xyzw: [0, 0, 0, 1],
|
||||
},
|
||||
...overrides,
|
||||
},
|
||||
access: "read-only",
|
||||
};
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
server = await createServer({
|
||||
appType: "custom",
|
||||
logLevel: "silent",
|
||||
server: { middlewareMode: true },
|
||||
});
|
||||
({
|
||||
parseE30ReviewCatalog,
|
||||
parseE30ReviewItems,
|
||||
parseE30ReviewItemDetail,
|
||||
fetchE30ReviewItems,
|
||||
E30ReviewContractError,
|
||||
} = await server.ssrLoadModule("/src/core/laboratory/e30Review.ts"));
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await server?.close();
|
||||
});
|
||||
|
||||
test("decodes immutable E30 catalog, strata and exact point evidence", () => {
|
||||
const decodedCatalog = parseE30ReviewCatalog(catalog());
|
||||
const decodedItems = parseE30ReviewItems(items());
|
||||
const decodedDetail = parseE30ReviewItemDetail(detail());
|
||||
|
||||
assert.equal(decodedCatalog.items[0].stratumCounts.conflict, 38);
|
||||
assert.equal(decodedCatalog.items[0].humanReviewComplete, false);
|
||||
assert.equal(decodedItems.items[0].sessionSeconds, 215.323857292);
|
||||
assert.deepEqual(decodedDetail.selected.sourceIndices, [7]);
|
||||
assert.deepEqual(decodedDetail.projection.selectedMask, [1, 0, 0]);
|
||||
assert.equal(decodedDetail.cameraFrame.exactSourceFrame, true);
|
||||
});
|
||||
|
||||
test("rejects authority escalation and inconsistent materialized arrays", () => {
|
||||
assert.throws(
|
||||
() => parseE30ReviewCatalog(catalog({
|
||||
items: [result({
|
||||
authority: {
|
||||
commands_enabled: true,
|
||||
navigation_or_safety_accepted: false,
|
||||
},
|
||||
})],
|
||||
})),
|
||||
E30ReviewContractError,
|
||||
);
|
||||
assert.throws(
|
||||
() => parseE30ReviewItemDetail(detail({
|
||||
selected: {
|
||||
source_indices: [7, 8],
|
||||
points_map_xyz_m: [[1, 2, 3]],
|
||||
},
|
||||
})),
|
||||
E30ReviewContractError,
|
||||
);
|
||||
});
|
||||
|
||||
test("fetches the canonical read-only stratum endpoint", async () => {
|
||||
const requests = [];
|
||||
const decoded = await fetchE30ReviewItems(resultId, "conflict", {
|
||||
fetcher: async (input, init) => {
|
||||
requests.push({ input: String(input), method: init?.method });
|
||||
return new Response(JSON.stringify(items()), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(decoded.total, 38);
|
||||
assert.deepEqual(requests, [{
|
||||
input: `/api/v1/laboratory/e30/reviews/${resultId}/items?stratum=conflict&limit=128&cursor=0`,
|
||||
method: "GET",
|
||||
}]);
|
||||
});
|
||||
@@ -0,0 +1,80 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { test } from "node:test";
|
||||
|
||||
const workspaceSourceUrl = new URL(
|
||||
"../src/workspaces/E30ReviewWorkspace.tsx",
|
||||
import.meta.url,
|
||||
);
|
||||
const presentationSourceUrl = new URL(
|
||||
"../src/components/laboratory/LaboratoryPresentation.tsx",
|
||||
import.meta.url,
|
||||
);
|
||||
const viewerSourceUrl = new URL(
|
||||
"../src/components/laboratory/LaboratoryEvidenceViewer.tsx",
|
||||
import.meta.url,
|
||||
);
|
||||
const laboratoryStylesUrl = new URL(
|
||||
"../src/styles/laboratory.css",
|
||||
import.meta.url,
|
||||
);
|
||||
|
||||
test("E30 uses one reusable evidence viewer with camera, 3D and expand controls", async () => {
|
||||
const [workspaceSource, viewerSource, laboratoryStyles] = await Promise.all([
|
||||
readFile(workspaceSourceUrl, "utf8"),
|
||||
readFile(viewerSourceUrl, "utf8"),
|
||||
readFile(laboratoryStylesUrl, "utf8"),
|
||||
]);
|
||||
|
||||
assert.match(workspaceSource, /<LaboratoryEvidenceViewer/);
|
||||
assert.match(workspaceSource, /\{ value: "3d", label: "3D" \}/);
|
||||
assert.match(workspaceSource, /\{ value: "camera", label: "Камера" \}/);
|
||||
assert.match(workspaceSource, /<E30EvidenceProjection/);
|
||||
assert.match(workspaceSource, /<E30EvidenceTelemetry/);
|
||||
assert.match(workspaceSource, /aria-pressed=\{pointLayerVisible\}/);
|
||||
assert.match(workspaceSource, />\s*LiDAR\s*<\/Button>/);
|
||||
assert.match(workspaceSource, /review: "Проверка"/);
|
||||
assert.doesNotMatch(workspaceSource, /e30-review-evidence__facts/);
|
||||
assert.doesNotMatch(workspaceSource, /E30EngineeringAuditPanel/);
|
||||
assert.match(viewerSource, /createPortal/);
|
||||
assert.match(viewerSource, /name=\{expanded \? "minimize" : "expand"\}/);
|
||||
assert.match(
|
||||
laboratoryStyles,
|
||||
/\.e30-evidence-telemetry dl\s*\{[^}]*grid-template-columns:\s*minmax\(0,\s*1fr\)/s,
|
||||
);
|
||||
assert.match(
|
||||
laboratoryStyles,
|
||||
/\.e30-projection-scene canvas\s*\{[^}]*object-fit:\s*cover/s,
|
||||
);
|
||||
});
|
||||
|
||||
test("E30 exception review is a separate block after the fixed evidence workspace", async () => {
|
||||
const workspaceSource = await readFile(workspaceSourceUrl, "utf8");
|
||||
const bodyStart = workspaceSource.indexOf(
|
||||
'<div className="e30-review-workspace__body">',
|
||||
);
|
||||
const detailClose = workspaceSource.indexOf(
|
||||
"</div>\n\n {!detailLoading",
|
||||
bodyStart,
|
||||
);
|
||||
const reviewPanel = workspaceSource.indexOf(
|
||||
"<E30HumanReviewPanel",
|
||||
bodyStart,
|
||||
);
|
||||
|
||||
assert.ok(bodyStart >= 0);
|
||||
assert.ok(detailClose > bodyStart);
|
||||
assert.ok(reviewPanel > detailClose);
|
||||
});
|
||||
|
||||
test("LAB product surface has a compact canonical summary and no roadmap footer", async () => {
|
||||
const [workspaceSource, presentationSource] = await Promise.all([
|
||||
readFile(workspaceSourceUrl, "utf8"),
|
||||
readFile(presentationSourceUrl, "utf8"),
|
||||
]);
|
||||
|
||||
assert.match(presentationSource, /export function LaboratorySummary/);
|
||||
assert.match(presentationSource, /export function LaboratoryWorkTemplate/);
|
||||
assert.doesNotMatch(workspaceSource, /СЛЕДУЮЩИЙ GATE/);
|
||||
assert.doesNotMatch(workspaceSource, /e30-review-workspace__taxonomy/);
|
||||
});
|
||||
@@ -178,23 +178,30 @@ test("data recordings keep the compact session dropdown and laboratory results s
|
||||
new URL("../src/workspaces/Workspaces.tsx", import.meta.url),
|
||||
"utf8",
|
||||
);
|
||||
const laboratorySource = await readFile(
|
||||
new URL(
|
||||
"../src/workspaces/laboratory/LaboratoryArchiveWorkspace.tsx",
|
||||
import.meta.url,
|
||||
),
|
||||
"utf8",
|
||||
);
|
||||
const productSource = await readFile(
|
||||
new URL("../src/productModel.ts", import.meta.url),
|
||||
"utf8",
|
||||
);
|
||||
const recordingsSource = workspaceSource.slice(
|
||||
workspaceSource.indexOf("function RecordingsWorkspace"),
|
||||
workspaceSource.indexOf("type LaboratoryProfileId"),
|
||||
workspaceSource.indexOf("export function WorkspaceRenderer"),
|
||||
);
|
||||
|
||||
assert.match(appSource, /activeDefinition\.kind === "recordings"[\s\S]*<ObservationSessionSelect/);
|
||||
assert.match(recordingsSource, /<SpatialWorkspace \{\.\.\.props\} \/>/);
|
||||
assert.doesNotMatch(recordingsSource, /ObservationSessionArchive/);
|
||||
assert.match(productSource, /label: "Лабораторные контуры"/);
|
||||
assert.match(workspaceSource, /ПРОФИЛЬ ЛАБОРАТОРНОГО КОНТУРА/);
|
||||
assert.match(workspaceSource, /ЛАБОРАТОРНАЯ РАБОТА/);
|
||||
assert.match(workspaceSource, /e28-local-surface/);
|
||||
assert.match(workspaceSource, /e29-camera-geometry/);
|
||||
assert.match(laboratorySource, /ПРОФИЛЬ ЛАБОРАТОРНОГО КОНТУРА/);
|
||||
assert.match(laboratorySource, /ЛАБОРАТОРНАЯ РАБОТА/);
|
||||
assert.match(laboratorySource, /e28-local-surface/);
|
||||
assert.match(laboratorySource, /e29-camera-geometry/);
|
||||
});
|
||||
|
||||
test("source and laboratory catalogs are requested as disjoint backend projections", async () => {
|
||||
|
||||
@@ -31,11 +31,21 @@ test("top navigation has no Center and Park owns contour health first", () => {
|
||||
|
||||
test("every laboratory result uses the shared evidence template", async () => {
|
||||
const source = await readFile(
|
||||
new URL("../src/workspaces/Workspaces.tsx", import.meta.url),
|
||||
new URL(
|
||||
"../src/workspaces/laboratory/LaboratoryArchiveWorkspace.tsx",
|
||||
import.meta.url,
|
||||
),
|
||||
"utf8",
|
||||
);
|
||||
const presentation = await readFile(
|
||||
new URL(
|
||||
"../src/components/laboratory/LaboratoryPresentation.tsx",
|
||||
import.meta.url,
|
||||
),
|
||||
"utf8",
|
||||
);
|
||||
const css = await readFile(
|
||||
new URL("../src/styles/workspaces.css", import.meta.url),
|
||||
new URL("../src/styles/laboratory.css", import.meta.url),
|
||||
"utf8",
|
||||
);
|
||||
const app = await readFile(
|
||||
@@ -43,11 +53,14 @@ test("every laboratory result uses the shared evidence template", async () => {
|
||||
"utf8",
|
||||
);
|
||||
|
||||
assert.match(source, /function LaboratoryWorkTemplate\(/);
|
||||
assert.match(source, /function LaboratoryEvidence\(/);
|
||||
assert.match(source, /function LaboratoryMethodCard\(/);
|
||||
assert.match(source, /method:\s*ReactNode/);
|
||||
assert.match(source, /data-evidence-kind=\{kind\}/);
|
||||
assert.match(source, /LaboratoryWorkTemplate,/);
|
||||
assert.match(source, /LaboratorySummary,/);
|
||||
assert.match(presentation, /export function LaboratoryWorkTemplate\(/);
|
||||
assert.match(presentation, /export function LaboratoryEvidence\(/);
|
||||
assert.match(presentation, /export function LaboratorySummary\(/);
|
||||
assert.match(presentation, /summary:\s*ReactNode/);
|
||||
assert.match(presentation, /data-evidence-kind=\{kind\}/);
|
||||
assert.doesNotMatch(source, /function LaboratoryWorkTemplate\(/);
|
||||
assert.match(source, /data-viewer-focused=/);
|
||||
assert.match(css, /height:\s*clamp\(42rem,\s*68vh,\s*58rem\)/);
|
||||
assert.match(css, /resize:\s*vertical/);
|
||||
|
||||
Reference in New Issue
Block a user