From 6db5ac05efdfe638f77eace5a8fc81cdc8876c22 Mon Sep 17 00:00:00 2001 From: DCCONSTRUCTIONS Date: Tue, 28 Jul 2026 02:37:38 +0300 Subject: [PATCH] feat(lab): publish E37 R0 acceptance result --- .../src/core/laboratory/advancedResults.ts | 195 +++++++++++++++++- .../laboratory/AdvancedLaboratoryResult.tsx | 14 +- .../src/workspaces/laboratory/E37Result.tsx | 171 +++++++++++++++ .../laboratory/LaboratoryArchiveWorkspace.tsx | 1 + .../test/advancedLaboratoryResults.test.mjs | 67 +++++- .../perception/LAB_E37_REPORT_2026-07-28.md | 134 ++++++++++++ src/k1link/web/advanced_laboratory_api.py | 105 ++++++++++ src/k1link/web/app.py | 7 + tests/test_advanced_laboratory_api.py | 116 ++++++++++- 9 files changed, 801 insertions(+), 9 deletions(-) create mode 100644 apps/control-station/src/workspaces/laboratory/E37Result.tsx create mode 100644 experiments/perception/LAB_E37_REPORT_2026-07-28.md diff --git a/apps/control-station/src/core/laboratory/advancedResults.ts b/apps/control-station/src/core/laboratory/advancedResults.ts index 8d5243a..cc73fa0 100644 --- a/apps/control-station/src/core/laboratory/advancedResults.ts +++ b/apps/control-station/src/core/laboratory/advancedResults.ts @@ -89,12 +89,56 @@ export interface E33LaboratoryResult { access: "read-only"; } +export interface E37AcceptanceContractResult { + resultId: string; + createdAtUtc: string | null; + sourceSessionId: string; + sourceDisplayName: string; + status: "accepted-r0-source-scoped-contract"; + profileId: string; + workerNode: string; + metrics: { + reviewedItems: number; + developmentItems: number; + validationItems: number; + engineeringItems: number; + humanExceptionItems: number; + terminalOutcomes: number; + accountingFraction: number; + falseFreeClaims: number; + }; + dimensionDistributions: { + presence: Readonly>; + geometryAssociation: Readonly>; + freshness: Readonly>; + }; + severityDistribution: Readonly>; + labelProvenance: { + engineeringItems: number; + humanExceptionItems: number; + independentGroundTruth: false; + }; + split: { + strategy: string; + validationFraction: number; + }; + targets: { + presenceTarget: number; + geometryAssociationTarget: number; + freshnessTarget: number; + }; + qualityTargetEvaluated: false; + limitations: readonly string[]; + access: "read-only"; +} + export interface AdvancedLaboratoryResults { e31: E31LaboratoryResult | null; e32: E32LaboratoryResult | null; e33: E33LaboratoryResult | null; e34: E34TemporalLayerResult | null; e35: E35DegradationRecoveryResult | null; + e37: E37AcceptanceContractResult | null; } export class AdvancedLaboratoryContractError extends Error { @@ -181,6 +225,19 @@ function strings(value: unknown, label: string): readonly string[] { return value.map((item, index) => stringValue(item, `${label}[${index}]`)); } +function numberRecord( + value: unknown, + label: string, +): Readonly> { + const source = record(value, label); + return Object.fromEntries( + Object.entries(source).map(([key, item]) => [ + key, + integerValue(item, `${label}.${key}`), + ]), + ); +} + function contentId(value: unknown, prefix: string, label: string): string { const parsed = stringValue(value, label); if (!new RegExp(`^${prefix}-[a-f0-9]{64}$`).test(parsed)) { @@ -349,6 +406,139 @@ function parseE33(value: unknown): E33LaboratoryResult { }; } +function parseE37(value: unknown): E37AcceptanceContractResult { + const item = record(value, "E37"); + const metrics = record(item.metrics, "E37.metrics"); + const distributions = record( + item.dimension_distributions, + "E37.dimension_distributions", + ); + const provenance = record(item.label_provenance, "E37.label_provenance"); + const split = record(item.split, "E37.split"); + const targets = record(item.targets, "E37.targets"); + diagnosticAuthority(item.authority, "E37.authority"); + if (item.quality_target_evaluated !== false) { + throw new AdvancedLaboratoryContractError( + "E37.quality_target_evaluated: R0 не должен объявлять метрику проверенной.", + ); + } + if (provenance.independent_ground_truth !== false) { + throw new AdvancedLaboratoryContractError( + "E37.label_provenance: R0 не является независимой разметкой.", + ); + } + return { + resultId: contentId( + item.result_id, + "e37-ravnoves-acceptance", + "E37.result_id", + ), + createdAtUtc: optionalString(item.created_at_utc, "E37.created_at_utc"), + sourceSessionId: stringValue( + item.source_session_id, + "E37.source_session_id", + ), + sourceDisplayName: stringValue( + item.source_display_name, + "E37.source_display_name", + ), + status: exactString( + item.status, + "accepted-r0-source-scoped-contract", + "E37.status", + ), + profileId: stringValue(item.profile_id, "E37.profile_id"), + workerNode: stringValue(item.worker_node, "E37.worker_node"), + metrics: { + reviewedItems: integerValue( + metrics.reviewed_items, + "E37.metrics.reviewed_items", + ), + developmentItems: integerValue( + metrics.development_items, + "E37.metrics.development_items", + ), + validationItems: integerValue( + metrics.validation_items, + "E37.metrics.validation_items", + ), + engineeringItems: integerValue( + metrics.engineering_items, + "E37.metrics.engineering_items", + ), + humanExceptionItems: integerValue( + metrics.human_exception_items, + "E37.metrics.human_exception_items", + ), + terminalOutcomes: integerValue( + metrics.terminal_outcomes, + "E37.metrics.terminal_outcomes", + ), + accountingFraction: numberValue( + metrics.accounting_fraction, + "E37.metrics.accounting_fraction", + ), + falseFreeClaims: integerValue( + metrics.false_free_claims, + "E37.metrics.false_free_claims", + ), + }, + dimensionDistributions: { + presence: numberRecord( + distributions.presence, + "E37.dimension_distributions.presence", + ), + geometryAssociation: numberRecord( + distributions.geometry_association, + "E37.dimension_distributions.geometry_association", + ), + freshness: numberRecord( + distributions.freshness, + "E37.dimension_distributions.freshness", + ), + }, + severityDistribution: numberRecord( + item.severity_distribution, + "E37.severity_distribution", + ), + labelProvenance: { + engineeringItems: integerValue( + provenance.engineering_items, + "E37.label_provenance.engineering_items", + ), + humanExceptionItems: integerValue( + provenance.human_exception_items, + "E37.label_provenance.human_exception_items", + ), + independentGroundTruth: false, + }, + split: { + strategy: stringValue(split.strategy, "E37.split.strategy"), + validationFraction: numberValue( + split.validation_fraction, + "E37.split.validation_fraction", + ), + }, + targets: { + presenceTarget: numberValue( + targets.presence_target, + "E37.targets.presence_target", + ), + geometryAssociationTarget: numberValue( + targets.geometry_association_target, + "E37.targets.geometry_association_target", + ), + freshnessTarget: numberValue( + targets.freshness_target, + "E37.targets.freshness_target", + ), + }, + qualityTargetEvaluated: false, + limitations: strings(item.limitations, "E37.limitations"), + access: exactString(item.access, "read-only", "E37.access"), + }; +} + async function fetchOne( path: string, parser: (value: unknown) => T, @@ -373,14 +563,15 @@ export async function fetchAdvancedLaboratoryResults({ fetcher?: LaboratoryFetch; signal?: AbortSignal; } = {}): Promise { - const [e31, e32, e33, e34, e35] = await Promise.all([ + const [e31, e32, e33, e34, e35, e37] = await Promise.all([ fetchOne("/api/v1/laboratory/e31/results?limit=1", parseE31, fetcher, signal), fetchOne("/api/v1/laboratory/e32/results?limit=1", parseE32, fetcher, signal), fetchOne("/api/v1/laboratory/e33/results?limit=1", parseE33, fetcher, signal), fetchE34TemporalLayerResult({ fetcher, signal }), fetchE35DegradationRecoveryResult({ fetcher, signal }), + fetchOne("/api/v1/laboratory/e37/results?limit=1", parseE37, fetcher, signal), ]); - return { e31, e32, e33, e34, e35 }; + return { e31, e32, e33, e34, e35, e37 }; } import { fetchE34TemporalLayerResult, diff --git a/apps/control-station/src/workspaces/laboratory/AdvancedLaboratoryResult.tsx b/apps/control-station/src/workspaces/laboratory/AdvancedLaboratoryResult.tsx index 73b0f44..9630e4e 100644 --- a/apps/control-station/src/workspaces/laboratory/AdvancedLaboratoryResult.tsx +++ b/apps/control-station/src/workspaces/laboratory/AdvancedLaboratoryResult.tsx @@ -9,6 +9,7 @@ import { E32Result } from "./E32Result"; import { E33Result } from "./E33Result"; import { E34Result } from "./E34Result"; import { E35Result } from "./E35Result"; +import { E37Result } from "./E37Result"; import { RecordedReplayEvidence } from "./RecordedReplayEvidence"; export type AdvancedLaboratoryWorkId = @@ -16,7 +17,8 @@ export type AdvancedLaboratoryWorkId = | "e32-track-geometry" | "e33-worker-shadow" | "e34-temporal-layer" - | "e35-degradation-recovery"; + | "e35-degradation-recovery" + | "e37-ravnoves-acceptance"; type LaboratoryWorkspaceProps = WorkspaceRendererProps & { SpatialView: ComponentType; @@ -31,6 +33,7 @@ export function isAdvancedLaboratoryWorkId( || value === "e33-worker-shadow" || value === "e34-temporal-layer" || value === "e35-degradation-recovery" + || value === "e37-ravnoves-acceptance" ); } @@ -69,6 +72,12 @@ export function advancedLaboratoryWorkOptions( label: "LAB E35 · degradation recovery", }); } + if (results.e37) { + options.push({ + id: "e37-ravnoves-acceptance", + label: "LAB E37 · RAVNOVES00 acceptance R0", + }); + } return options; } @@ -106,6 +115,9 @@ export function AdvancedLaboratoryResult({ failedSessionId: string | null; replayError: string | null; }) { + if (workId === "e37-ravnoves-acceptance" && results.e37) { + return ; + } if (workId === "e35-degradation-recovery" && results.e35) { return ; } diff --git a/apps/control-station/src/workspaces/laboratory/E37Result.tsx b/apps/control-station/src/workspaces/laboratory/E37Result.tsx new file mode 100644 index 0000000..4c84814 --- /dev/null +++ b/apps/control-station/src/workspaces/laboratory/E37Result.tsx @@ -0,0 +1,171 @@ +import { + LaboratoryEvidence, + LaboratoryResultSummary, + LaboratorySummary, + LaboratoryWorkTemplate, +} from "../../components/laboratory/LaboratoryPresentation"; +import type { E37AcceptanceContractResult } from "../../core/laboratory/advancedResults"; +import { formatNumber } from "../../presentation"; + +function distribution( + values: Readonly>, +): string { + return Object.entries(values) + .sort((left, right) => right[1] - left[1]) + .map(([label, count]) => `${label}: ${formatNumber(count, 0)}`) + .join(" · "); +} + +function ContractEvidence({ + result, +}: { + result: E37AcceptanceContractResult; +}) { + return ( +
+
+ Presence + {Object.keys(result.dimensionDistributions.presence).length} исхода + {distribution(result.dimensionDistributions.presence)} +
+
+ Geometry association + + {Object.keys(result.dimensionDistributions.geometryAssociation).length} + {" исходов"} + + {distribution(result.dimensionDistributions.geometryAssociation)} +
+
+ Freshness + {Object.keys(result.dimensionDistributions.freshness).length} исхода + {distribution(result.dimensionDistributions.freshness)} +
+
+ Критичность + {Object.keys(result.severityDistribution).length} уровня + {distribution(result.severityDistribution)} +
+
+ ); +} + +export function E37Result({ + rigLabel, + result, +}: { + rigLabel: string; + result: E37AcceptanceContractResult; +}) { + const metrics = result.metrics; + const percent = (value: number) => ( + `${(value * 100).toLocaleString("ru-RU", { maximumFractionDigits: 1 })}%` + ); + return ( + + )} + evidence={( + + + + )} + result={( + + )} + /> + ); +} diff --git a/apps/control-station/src/workspaces/laboratory/LaboratoryArchiveWorkspace.tsx b/apps/control-station/src/workspaces/laboratory/LaboratoryArchiveWorkspace.tsx index 40d9c60..1a5b7d9 100644 --- a/apps/control-station/src/workspaces/laboratory/LaboratoryArchiveWorkspace.tsx +++ b/apps/control-station/src/workspaces/laboratory/LaboratoryArchiveWorkspace.tsx @@ -68,6 +68,7 @@ const EMPTY_ADVANCED_RESULTS: AdvancedLaboratoryResults = { e33: null, e34: null, e35: null, + e37: null, }; function digestFromContentId(value: string | null | undefined): string | null { diff --git a/apps/control-station/test/advancedLaboratoryResults.test.mjs b/apps/control-station/test/advancedLaboratoryResults.test.mjs index 6df6c85..48018ca 100644 --- a/apps/control-station/test/advancedLaboratoryResults.test.mjs +++ b/apps/control-station/test/advancedLaboratoryResults.test.mjs @@ -299,6 +299,62 @@ function e35() { }; } +function e37() { + return { + result_id: `e37-ravnoves-acceptance-${"7".repeat(64)}`, + created_at_utc: "2026-07-27T23:27:02Z", + source_session_id: "20260720T065719Z_viewer_live", + source_display_name: "RAVNOVES00", + status: "accepted-r0-source-scoped-contract", + profile_id: "e37-ravnoves00-r0-acceptance/v1", + worker_node: "DESKTOP-OPJ8J04", + metrics: { + reviewed_items: 486, + development_items: 340, + validation_items: 146, + engineering_items: 484, + human_exception_items: 2, + terminal_outcomes: 486, + accounting_fraction: 1, + false_free_claims: 0, + }, + dimension_distributions: { + presence: { + "object-present": 300, + "occupied-environment": 104, + "background-or-noise": 82, + }, + geometry_association: { + "object-associated": 108, + "independent-occupied": 104, + "insufficient-support": 100, + unknown: 92, + "rejected-nonobject": 82, + }, + freshness: { current: 292, unavailable: 102, stale: 92 }, + }, + severity_distribution: { standard: 290, medium: 71, high: 125 }, + label_provenance: { + engineering_items: 484, + human_exception_items: 2, + independent_ground_truth: false, + }, + split: { + strategy: "deterministic-source-stratum-range-holdout", + validation_fraction: 0.3, + }, + targets: { + presence_target: 0.9, + geometry_association_target: 0.9, + freshness_target: 0.9, + }, + quality_target_evaluated: false, + limitations: ["source-scoped"], + authority, + access: "read-only", + }; +} + before(async () => { server = await createServer({ appType: "custom", @@ -315,9 +371,9 @@ after(async () => { await server?.close(); }); -test("decodes E31–E35 from separate read-only catalogs", async () => { +test("decodes E31–E37 from separate read-only catalogs", async () => { const requests = []; - const items = [e31(), e32(), e33(), e34(), e35()]; + const items = [e31(), e32(), e33(), e34(), e35(), e37()]; const decoded = await fetchAdvancedLaboratoryResults({ fetcher: async (input, init) => { requests.push({ input: String(input), method: init?.method }); @@ -337,12 +393,16 @@ test("decodes E31–E35 from separate read-only catalogs", async () => { 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.equal(decoded.e37.metrics.reviewedItems, 486); + assert.equal(decoded.e37.metrics.validationItems, 146); + assert.equal(decoded.e37.qualityTargetEvaluated, false); 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" }, + { input: "/api/v1/laboratory/e37/results?limit=1", method: "GET" }, ]); }); @@ -357,7 +417,8 @@ test("rejects authority escalation in an accepted-looking result", async () => { String(input).includes("/e31/") ? forged : String(input).includes("/e32/") ? e32() : String(input).includes("/e33/") ? e33() - : String(input).includes("/e34/") ? e34() : e35(), + : String(input).includes("/e34/") ? e34() + : String(input).includes("/e35/") ? e35() : e37(), )), { status: 200 }), }), AdvancedLaboratoryContractError, diff --git a/experiments/perception/LAB_E37_REPORT_2026-07-28.md b/experiments/perception/LAB_E37_REPORT_2026-07-28.md new file mode 100644 index 0000000..6e6053e --- /dev/null +++ b/experiments/perception/LAB_E37_REPORT_2026-07-28.md @@ -0,0 +1,134 @@ +# LAB E37 — RAVNOVES00 acceptance contract R0 + +## Result identity + +Status: `accepted-r0-source-scoped-contract`. + +Result: +`e37-ravnoves-acceptance-01b1efd586f747341c712d82f0907b39436a6f91ae92b1dfae987eca05fd8344`. + +Worker package: +`e37-worker-package-960b804df639dd2937895377aa67d5dca4b325f293ab48868d16049da2ff28bc`. + +Physical execution node: `DESKTOP-OPJ8J04` / Worker 006. + +## Question + +E37 does not ask whether K1 has already reached 90% quality. It asks which +immutable RAVNOVES00 cases, task ontology, split and metrics will be used to +make that claim honestly in the next gate. + +The prior E30 evidence review supplied a useful engineering-reviewed +substrate, but it was not yet an acceptance contract. Without freezing the +denominator and validation holdout, later tuning could silently move the +evaluation set or collapse presence, point ownership and freshness into one +ambiguous score. + +## Frozen source and provenance + +Source recording: + +- display name: `RAVNOVES00`; +- session: `20260720T065719Z_viewer_live`; +- classification: immutable private physical recording. + +Inputs: + +- E30 materialization: + `e30-materialization-841af926d8d28ab93538c46d8f31278a2234c4d1c12c7dc4dc296b249d59735a`; +- E30 engineering generation: + `e30-engineering-generation-62a4fea10dea9b77f69ceac1af5bf0e4928d9c7716083c22258a03670fe5bd4f`; +- E30 human exception generation: + `e30-review-generation-7982a882558d0be690b4c7092e328c080bfcbf52478a220452be7e887a588250`. + +The result binds the exact manifest and decision-file SHA-256 values for all +three inputs. Of 486 terminal labels, 484 come from the accepted engineering +generation and two from the saved user exception decisions. These labels are +not described as independent ground truth. + +## Method + +Profile: `e37-ravnoves00-r0-acceptance/v1`. + +The deterministic builder: + +1. verifies the exact E30 identities and artifact digests; +2. requires one engineering decision for every materialized review item; +3. requires human decisions to match the declared exception set exactly; +4. projects each reviewed item into three independent dimensions: + presence, geometry association and freshness; +5. assigns severity without changing the reviewed outcome; +6. creates a deterministic 30% validation holdout inside each source-stratum + and range bucket using seed `ravnoves00-r0-validation-v1`; +7. closes terminal accounting and rejects any free-space claim; +8. publishes four immutable artifacts atomically. + +The worker package contains only the runtime projection, profile and the six +required input manifest/data files. Execution used the pinned Triton image +with no network, a read-only container filesystem, dropped capabilities, +`no-new-privileges`, a bounded PID limit and no GPU allocation. + +## Contract + +| Measure | Result | +| --- | ---: | +| reviewed items | 486 | +| development items | 340 | +| validation items | 146 | +| terminal outcomes | 486 | +| accounting | 100% | +| engineering-reviewed labels | 484 | +| human exception labels | 2 | +| false-free claims | 0 | + +Frozen distributions: + +- presence: 300 `object-present`, 104 `occupied-environment`, + 82 `background-or-noise`; +- geometry association: 108 `object-associated`, + 104 `independent-occupied`, 100 `insufficient-support`, + 92 `unknown`, 82 `rejected-nonobject`; +- freshness: 292 `current`, 102 `unavailable`, 92 `stale`; +- severity: 290 `standard`, 71 `medium`, 125 `high`. + +The following targets are declared separately for R1 and later gates: + +- presence quality: at least 90%; +- geometry-association quality: at least 90%; +- freshness quality: at least 90%; +- accounting: 100%; +- false-free claims: zero. + +## Acceptance + +All seven predeclared R0 checks pass: + +- source identity is frozen; +- reviewed denominator is complete; +- development and validation split is complete; +- every item has a terminal label in all three dimensions; +- human exception accounting is complete; +- false-free claims are zero; +- authority remains diagnostic. + +## What this proves + +RAVNOVES00 now has one reproducible source-scoped evaluation contract. Future +algorithm changes can use the 340-item development set without changing the +146-item validation holdout, label ontology or denominator. Presence, +geometry association and freshness can no longer be reported as one blended +success score. + +## What this does not prove + +E37 does not evaluate or pass any 90% quality target. It does not provide +independent ground truth, prove another route, camera, rig or mount, validate +free space, or grant navigation, command or safety authority. + +## Decision + +Accept R0 as the only RAVNOVES00 measurement basis. The next planned K1 +laboratory gate is R1: compute the first source-scoped perception-quality +baseline on the frozen validation holdout, diagnose the separate deficits and +tune only against the development partition. + diff --git a/src/k1link/web/advanced_laboratory_api.py b/src/k1link/web/advanced_laboratory_api.py index cd2dacd..881f453 100644 --- a/src/k1link/web/advanced_laboratory_api.py +++ b/src/k1link/web/advanced_laboratory_api.py @@ -34,6 +34,11 @@ from k1link.compute.e35_degradation_replay import ( E35DegradationReplayError, read_e35_degradation_replay, ) +from k1link.compute.e37_acceptance_contract import ( + E37AcceptanceContract, + E37AcceptanceContractError, + read_e37_acceptance_contract, +) LABORATORY_ADVANCED_CATALOG_SCHEMA: Final = ( "missioncore.laboratory-advanced-catalog/v1" @@ -44,6 +49,7 @@ _E32_RESULT_ID = re.compile(r"^e32-track-geometry-[a-f0-9]{64}$") _E33_RESULT_ID = re.compile(r"^e33-worker-shadow-[a-f0-9]{64}$") _E34_RESULT_ID = re.compile(r"^e34-temporal-occupied-[a-f0-9]{64}$") _E35_RESULT_ID = re.compile(r"^e35-degradation-recovery-[a-f0-9]{64}$") +_E37_RESULT_ID = re.compile(r"^e37-ravnoves-acceptance-[a-f0-9]{64}$") RootProvider = Callable[[], Path | None] @@ -103,6 +109,15 @@ def _read_e35_cached( return read_e35_degradation_replay(Path(root_text)) +@lru_cache(maxsize=16) +def _read_e37_cached( + root_text: str, + signature: tuple[int, ...], +) -> E37AcceptanceContract: + del signature + return read_e37_acceptance_contract(Path(root_text)) + + def _configured_root(provider: RootProvider) -> Path | None: value = provider() if value is None: @@ -486,6 +501,60 @@ def _project_e35(result: E35DegradationReplay) -> dict[str, object]: } +def _project_e37(result: E37AcceptanceContract) -> dict[str, object]: + identity = _object(result.manifest.get("identity"), "E37 identity") + source = _object(identity.get("source"), "E37 source") + execution = _object(identity.get("execution"), "E37 execution") + profile = _object(identity.get("profile"), "E37 profile") + provenance = _object( + result.contract.get("label_provenance"), + "E37 label provenance", + ) + metrics = _object(result.report.get("metrics"), "E37 metrics") + decision = _object(result.report.get("decision"), "E37 decision") + acceptance = _object(result.report.get("acceptance"), "E37 acceptance") + if acceptance.get("accepted") is not True: + raise ValueError("E37 result is not accepted") + return { + "result_id": result.result_id, + "created_at_utc": result.manifest.get("created_at_utc"), + "source_session_id": source.get("session_id"), + "source_display_name": source.get("display_name"), + "status": result.manifest.get("acceptance_state"), + "profile_id": profile.get("profile_id"), + "worker_node": execution.get("worker_node"), + "metrics": { + "reviewed_items": metrics.get("reviewed_items"), + "development_items": metrics.get("development_items"), + "validation_items": metrics.get("validation_items"), + "engineering_items": metrics.get("engineering_items"), + "human_exception_items": metrics.get("human_exception_items"), + "terminal_outcomes": metrics.get("terminal_outcomes"), + "accounting_fraction": metrics.get("accounting_fraction"), + "false_free_claims": metrics.get("false_free_claims"), + }, + "dimension_distributions": copy.deepcopy( + result.contract.get("dimension_distributions") + ), + "severity_distribution": copy.deepcopy( + result.contract.get("severity_distribution") + ), + "label_provenance": { + "engineering_items": provenance.get("engineering_items"), + "human_exception_items": provenance.get("human_exception_items"), + "independent_ground_truth": provenance.get( + "independent_ground_truth" + ), + }, + "split": copy.deepcopy(result.contract.get("split")), + "targets": copy.deepcopy(result.contract.get("targets")), + "quality_target_evaluated": decision.get("quality_target_evaluated"), + "limitations": copy.deepcopy(result.report.get("limitations")), + "authority": copy.deepcopy(result.report.get("authority")), + "access": "read-only", + } + + def _empty_catalog(configured: bool) -> dict[str, object]: return { "schema_version": LABORATORY_ADVANCED_CATALOG_SCHEMA, @@ -504,6 +573,7 @@ def build_advanced_laboratory_router( e33_root_provider: RootProvider = lambda: None, e34_root_provider: RootProvider = lambda: None, e35_root_provider: RootProvider = lambda: None, + e37_root_provider: RootProvider = lambda: None, ) -> APIRouter: router = APIRouter(prefix="/api/v1/laboratory", tags=["laboratory"]) @@ -698,4 +768,39 @@ def build_advanced_laboratory_router( "invalid_total": invalid_total, } + @router.get("/e37/results") + def list_e37_results( + limit: int = Query(default=1, ge=1, le=10), + ) -> dict[str, object]: + root = _configured_root(e37_root_provider) + if root is None: + return _empty_catalog(False) + candidates = _candidates(root, _E37_RESULT_ID) + items: list[dict[str, object]] = [] + invalid_total = 0 + for candidate in candidates: + try: + result = _read_e37_cached( + str(candidate.resolve()), + _result_signature(candidate), + ) + if not result.accepted: + raise ValueError("E37 result is not accepted") + if len(items) < limit: + items.append(_project_e37(result)) + except ( + E37AcceptanceContractError, + KeyError, + OSError, + TypeError, + ValueError, + ): + invalid_total += 1 + return { + **_empty_catalog(True), + "items": items, + "candidate_total": len(candidates), + "invalid_total": invalid_total, + } + return router diff --git a/src/k1link/web/app.py b/src/k1link/web/app.py index 2d865ca..3ff6bc2 100644 --- a/src/k1link/web/app.py +++ b/src/k1link/web/app.py @@ -536,6 +536,13 @@ app.include_router( / "e35" / "results" ), + e37_root_provider=lambda: ( + REPOSITORY_ROOT + / ".runtime" + / "compute-experiments" + / "e37" + / "results" + ), ) ) app.include_router( diff --git a/tests/test_advanced_laboratory_api.py b/tests/test_advanced_laboratory_api.py index 1778722..9d7964e 100644 --- a/tests/test_advanced_laboratory_api.py +++ b/tests/test_advanced_laboratory_api.py @@ -26,7 +26,7 @@ def _endpoint(router: APIRouter, path: str) -> object: def test_advanced_catalogs_are_empty_when_not_configured() -> None: router = build_advanced_laboratory_router() - for name in ("e31", "e32", "e33", "e34", "e35"): + for name in ("e31", "e32", "e33", "e34", "e35", "e37"): route = _endpoint(router, f"/api/v1/laboratory/{name}/results") catalog = route(limit=1) # type: ignore[operator] assert catalog == { @@ -47,22 +47,25 @@ def test_advanced_catalogs_fail_closed_on_incomplete_results( e33 = tmp_path / "e33" e34 = tmp_path / "e34" e35 = tmp_path / "e35" - for root in (e31, e32, e33, e34, e35): + e37 = tmp_path / "e37" + for root in (e31, e32, e33, e34, e35, e37): root.mkdir() (e31 / f"e31-source-qualification-{'1' * 64}").mkdir() (e32 / f"e32-track-geometry-{'2' * 64}").mkdir() (e33 / f"e33-worker-shadow-{'3' * 64}").mkdir() (e34 / f"e34-temporal-occupied-{'4' * 64}").mkdir() (e35 / f"e35-degradation-recovery-{'5' * 64}").mkdir() + (e37 / f"e37-ravnoves-acceptance-{'7' * 64}").mkdir() router = build_advanced_laboratory_router( e31_root_provider=lambda: e31, e32_root_provider=lambda: e32, e33_root_provider=lambda: e33, e34_root_provider=lambda: e34, e35_root_provider=lambda: e35, + e37_root_provider=lambda: e37, ) - for name in ("e31", "e32", "e33", "e34", "e35"): + for name in ("e31", "e32", "e33", "e34", "e35", "e37"): route = _endpoint(router, f"/api/v1/laboratory/{name}/results") catalog = route(limit=1) # type: ignore[operator] assert catalog["configured"] is True @@ -312,3 +315,110 @@ def test_e35_catalog_projects_recovery_and_review( assert item["review"]["scenarios"] == [] assert item["authority"] == authority assert item["access"] == "read-only" + + +def test_e37_catalog_projects_the_frozen_r0_contract( + tmp_path: Path, + monkeypatch: MonkeyPatch, +) -> None: + result_id = f"e37-ravnoves-acceptance-{'7' * 64}" + root = tmp_path / "e37" + candidate = root / result_id + candidate.mkdir(parents=True) + (candidate / "manifest.json").write_text("{}", encoding="utf-8") + authority = { + "commands_enabled": False, + "navigation_or_safety_accepted": False, + } + result = SimpleNamespace( + result_id=result_id, + accepted=True, + manifest={ + "created_at_utc": "2026-07-28T10:00:00Z", + "acceptance_state": "accepted-r0-source-scoped-contract", + "identity": { + "source": { + "session_id": "20260720T065719Z_viewer_live", + "display_name": "RAVNOVES00", + }, + "profile": { + "profile_id": "e37-ravnoves00-r0-acceptance/v1", + }, + "execution": { + "class": "deterministic-offline-contract-build", + "worker_node": "DESKTOP-OPJ8J04", + }, + }, + }, + contract={ + "dimension_distributions": { + "presence": {"object-present": 200, "unknown": 2}, + "geometry_association": { + "object-associated": 180, + "unknown": 2, + }, + "freshness": {"current": 300, "unavailable": 186}, + }, + "severity_distribution": { + "standard": 401, + "medium": 81, + "high": 4, + }, + "label_provenance": { + "engineering_items": 484, + "human_exception_items": 2, + "independent_ground_truth": False, + }, + "split": { + "strategy": "deterministic-source-stratum-range-holdout", + "validation_fraction": 0.3, + }, + "targets": { + "presence_target": 0.9, + "geometry_association_target": 0.9, + "freshness_target": 0.9, + }, + }, + report={ + "metrics": { + "reviewed_items": 486, + "development_items": 340, + "validation_items": 146, + "engineering_items": 484, + "human_exception_items": 2, + "terminal_outcomes": 486, + "accounting_fraction": 1.0, + "false_free_claims": 0, + }, + "acceptance": {"accepted": True, "checks": {}}, + "decision": {"quality_target_evaluated": False}, + "limitations": ["source-scoped"], + "authority": authority, + }, + ) + + def fake_read( + root_text: str, + signature: tuple[int, ...], + ) -> SimpleNamespace: + assert root_text == str(candidate.resolve()) + assert signature + return result + + monkeypatch.setattr(advanced_api, "_read_e37_cached", fake_read) + router = build_advanced_laboratory_router( + e37_root_provider=lambda: root, + ) + route = _endpoint(router, "/api/v1/laboratory/e37/results") + catalog = route(limit=1) # type: ignore[operator] + + assert catalog["candidate_total"] == 1 + assert catalog["invalid_total"] == 0 + item = catalog["items"][0] + assert item["status"] == "accepted-r0-source-scoped-contract" + assert item["worker_node"] == "DESKTOP-OPJ8J04" + assert item["metrics"]["reviewed_items"] == 486 + assert item["metrics"]["validation_items"] == 146 + assert item["quality_target_evaluated"] is False + assert item["authority"] == authority + assert item["access"] == "read-only"