feat(lab): complete E30 evidence review gate

This commit is contained in:
DCCONSTRUCTIONS
2026-07-27 11:00:32 +03:00
parent a44d7627fd
commit 001d597a89
55 changed files with 15897 additions and 1548 deletions
@@ -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/);