feat(lab): seal native risk review evidence

This commit is contained in:
DCCONSTRUCTIONS
2026-08-26 10:15:49 +03:00
parent c05db1fbe0
commit f9a76fed0e
7 changed files with 975 additions and 76 deletions
+12 -2
View File
@@ -145,11 +145,21 @@ def test_product_registry_declares_every_advanced_evidence_source() -> None:
"m48t-risk-quality-temporal",
}
m48 = next(
item for item in registry.definitions
if item.work_id == "m48-object-centric-quality"
item for item in registry.definitions if item.work_id == "m48-object-centric-quality"
)
assert [variant.phase for variant in m48.evidence_variants] == ["review", "result"]
assert [variant.result_id_prefix for variant in m48.evidence_variants] == [
"m48-object-quality-pack",
"m48-object-quality-result",
]
m48t = next(
item for item in registry.definitions if item.work_id == "m48t-risk-quality-temporal"
)
assert [variant.phase for variant in m48t.evidence_variants] == [
"legacy-quality",
"result",
]
assert [variant.result_id_prefix for variant in m48t.evidence_variants] == [
"m48t-risk-quality-temporal-lab",
"m48q-native-risk-quality-lab",
]
+138
View File
@@ -6,6 +6,18 @@ from pathlib import Path
from fastapi import FastAPI
from fastapi.testclient import TestClient
from k1link.laboratory.m48q_native_risk_quality_lab import (
CATALOG_SCHEMA as NATIVE_CATALOG_SCHEMA,
)
from k1link.laboratory.m48q_native_risk_quality_lab import (
LAB_SCHEMA as NATIVE_LAB_SCHEMA,
)
from k1link.laboratory.m48q_native_risk_quality_lab import (
REPORT_SCHEMA as NATIVE_REPORT_SCHEMA,
)
from k1link.laboratory.m48q_native_risk_quality_lab import (
RESULT_PREFIX as NATIVE_RESULT_PREFIX,
)
from k1link.laboratory.m48t_risk_quality_lab import (
CATALOG_SCHEMA,
LAB_SCHEMA,
@@ -150,3 +162,129 @@ def test_m48t_lab_api_fails_closed_after_visual_tamper(tmp_path: Path) -> None:
catalog = client.get("/api/v1/laboratory/m48t/risk-quality/results")
assert catalog.json()["items"] == []
assert catalog.json()["invalid_total"] == 1
def _native_fixture(tmp_path: Path) -> tuple[TestClient, Path, str]:
root = tmp_path / "native-results"
root.mkdir()
identity = {"schema_version": NATIVE_LAB_SCHEMA, "authority": false_authority()}
identity_sha256 = hashlib.sha256(canonical_json(identity)).hexdigest()
result_id = NATIVE_RESULT_PREFIX + identity_sha256
result_root = root / result_id
cases_root = result_root / "cases"
cases_root.mkdir(parents=True)
cases = []
image_paths = []
for index in range(24):
case_id = f"{index * 20 + 12:06d}"
image_path = cases_root / f"frame-{case_id}.jpg"
image_path.write_bytes(b"native-jpeg" + bytes([index]))
image_paths.append(image_path)
cases.append(
{
"case_id": case_id,
"sequence": int(case_id),
"frame_id": f"frame-{case_id}",
"evidence_time_ns": 35_000_000_000 + index * 1_000_000,
"path": f"cases/{image_path.name}",
"media_type": "image/jpeg",
"width": 800,
"height": 600,
"byte_length": image_path.stat().st_size,
"sha256": hashlib.sha256(image_path.read_bytes()).hexdigest(),
"geometric_resampling": False,
"selection_buckets": ["person"],
"comparison": {
"native_detection_count": 1,
"legacy_704_detection_count": 1,
"matched_detection_count_iou_at_least_0_5": 1,
},
"proposals": [
{
"proposal_id": f"proposal-{index}-0",
"class_name": "person",
"risk_family": "person",
"score": 0.75,
"box_xyxy": [10.0, 20.0, 100.0, 200.0],
}
],
}
)
catalog = {
"schema_version": NATIVE_CATALOG_SCHEMA,
"result_id": result_id,
"case_count": 24,
"source_raster": {"width": 800, "height": 600},
"overlay": {"client_rendered": True, "toggleable": True},
"ground_truth": False,
"cases": cases,
}
report = {
"schema_version": NATIVE_REPORT_SCHEMA,
"result_id": result_id,
"source": {},
"configuration": {},
"method": {},
"execution": {},
"metrics": {},
"acceptance": {},
"decision": {
"review_ready": True,
"quality_evaluated": False,
"candidate_accepted": False,
},
"limitations": [],
"authority": false_authority(),
}
catalog_path = result_root / "catalog.json"
report_path = result_root / "report.json"
_write_json(catalog_path, catalog)
_write_json(report_path, report)
artifacts = [
_descriptor(catalog_path, result_root, "visual-evidence-catalog", "application/json"),
_descriptor(report_path, result_root, "laboratory-report", "application/json"),
*[
_descriptor(path, result_root, "visual-evidence-native-raw-fisheye-frame", "image/jpeg")
for path in image_paths
],
]
manifest = {
"schema_version": NATIVE_LAB_SCHEMA,
"result_id": result_id,
"identity_sha256": identity_sha256,
"identity": identity,
"created_at_utc": "2026-08-26T09:45:15.574709Z",
"status": "complete-review-ready-quality-not-adjudicated",
"completed": True,
"bounded_question_accepted": True,
"ground_truth": False,
"authority": false_authority(),
"artifacts": artifacts,
}
_write_json(result_root / "manifest.json", manifest)
app = FastAPI()
app.include_router(build_m48t_risk_quality_lab_router(native_root_provider=lambda: root))
return TestClient(app), result_root, result_id
def test_m48q_lab_api_projects_native_raw_cases_without_quality_promotion(
tmp_path: Path,
) -> None:
client, result_root, result_id = _native_fixture(tmp_path)
catalog = client.get("/api/v1/laboratory/m48t/risk-quality/results")
assert catalog.status_code == 200
assert catalog.json()["items"][0]["variant"] == "native-risk-review"
result = client.get(f"/api/v1/laboratory/m48t/risk-quality/results/{result_id}")
assert result.status_code == 200
assert result.json()["ground_truth"] is False
assert result.json()["decision"]["quality_evaluated"] is False
assert len(result.json()["review"]["cases"]) == 24
assert result.json()["review"]["cases"][0]["geometric_resampling"] is False
case_id = "000012"
image = client.get(
f"/api/v1/laboratory/m48t/risk-quality/results/{result_id}/review/{case_id}.jpg"
)
assert image.status_code == 200
assert image.content == (result_root / f"cases/frame-{case_id}.jpg").read_bytes()