feat(perception): prepare blind detector review handoff

This commit is contained in:
DCCONSTRUCTIONS
2026-07-30 00:03:41 +03:00
parent cc6838ed24
commit 091ab2671e
7 changed files with 1259 additions and 1 deletions
+839
View File
@@ -0,0 +1,839 @@
"""Operational blind-review handoff from E46 into human CVAT review."""
from __future__ import annotations
import hashlib
import json
import math
import os
import re
import shutil
import uuid
import zipfile
from dataclasses import dataclass
from datetime import UTC, datetime
from pathlib import Path, PurePosixPath
from typing import Any, Final
from PIL import Image
from .e46_detector_truth_island import (
E46_CONTRACT_NAME,
E46_MANIFEST_NAME,
E46_REFERENCES_NAME,
E46_REVIEW_NAME,
E46DetectorTruthIslandError,
read_e46_detector_truth_island,
)
from .e48_detector_truth_seal import (
E48_REVIEW_SCHEMA,
E48DetectorTruthSealError,
validate_e48_detector_review_submission,
)
E46_REVIEW_HANDOFF_SCHEMA: Final = "missioncore.e46-review-handoff/v1"
E46_REVIEW_HANDOFF_PROFILE: Final = "cvat-coco-empty-blind-two-reviewer/v1"
E46_REVIEW_SOURCE_SCHEMA: Final = "missioncore.e46-review-source/v1"
E46_REVIEW_HANDOFF_MANIFEST: Final = "manifest.json"
E46_REVIEW_HANDOFF_SLOTS: Final = ("reviewer-a", "reviewer-b")
_AUTHORITY: Final = {
"commands_enabled": False,
"navigation_or_safety_accepted": False,
}
_BLINDNESS: Final = {
"candidate_identity_seen": False,
"model_prelabels_seen": False,
"model_predictions_seen": False,
"model_scores_seen": False,
}
_SAFE_HANDOFF = re.compile(r"^e46-review-handoff-[a-f0-9]{64}$")
_SAFE_REVIEWER = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{1,63}$")
_ZIP_TIMESTAMP: Final = (1980, 1, 1, 0, 0, 0)
class E46ReviewHandoffError(RuntimeError):
"""The E46 reviewer handoff or completed CVAT export is invalid."""
@dataclass(frozen=True, slots=True)
class E46ReviewHandoff:
result_id: str
result_root: Path
manifest: dict[str, Any]
def build_e46_review_handoff(
*,
truth_island_root: Path,
evaluation_pack_root: Path,
output_root: Path,
) -> E46ReviewHandoff:
"""Create two self-contained, prediction-free reviewer packages."""
try:
truth = read_e46_detector_truth_island(truth_island_root)
except E46DetectorTruthIslandError as reason:
raise E46ReviewHandoffError("E46 truth island is invalid") from reason
evaluation_root = evaluation_pack_root.resolve(strict=True)
evaluation_manifest = _read_json(evaluation_root / "manifest.json")
truth_source = _object(
_object(truth.manifest.get("identity"), "E46 identity").get("source"),
"E46 source",
)
if (
evaluation_manifest.get("generation_id") != evaluation_root.name
or truth_source.get("evaluation_pack_id") != evaluation_root.name
):
raise E46ReviewHandoffError("E2 evaluation pack identity changed")
references = tuple(_read_jsonl(truth.result_root / E46_REFERENCES_NAME))
contract = _read_json(truth.result_root / E46_CONTRACT_NAME)
review_template = _read_json(truth.result_root / E46_REVIEW_NAME)
annotation = _object(contract.get("annotation"), "E46 annotation contract")
raw_classes = annotation.get("classes")
if (
contract.get("reviewer_package", {}).get("model_predictions_included")
is not False
or contract.get("reviewer_package", {}).get("model_prelabels_included")
is not False
or not isinstance(raw_classes, list)
or not raw_classes
or not all(isinstance(value, str) and value for value in raw_classes)
):
raise E46ReviewHandoffError("E46 blind annotation contract is invalid")
classes = tuple(raw_classes)
if (
review_template.get("truth_island_id") != truth.result_id
or len(_list(review_template.get("images"), "E46 review images"))
!= len(references)
):
raise E46ReviewHandoffError("E46 review template is invalid")
sources = tuple(
_verified_source(
evaluation_root=evaluation_root,
reference=reference,
)
for reference in references
)
identity = {
"schema_version": E46_REVIEW_HANDOFF_SCHEMA,
"profile": E46_REVIEW_HANDOFF_PROFILE,
"truth_island": {
"result_id": truth.result_id,
"manifest_sha256": _sha256(truth.result_root / E46_MANIFEST_NAME),
"references_sha256": _sha256(
truth.result_root / E46_REFERENCES_NAME
),
},
"evaluation_pack": {
"generation_id": evaluation_root.name,
"manifest_sha256": _sha256(evaluation_root / "manifest.json"),
},
"reviewer_slots": list(E46_REVIEW_HANDOFF_SLOTS),
"frame_count": len(sources),
"classes": list(classes),
"blindness": _BLINDNESS,
"authority": _AUTHORITY,
}
identity_sha256 = hashlib.sha256(_canonical_json(identity)).hexdigest()
result_id = f"e46-review-handoff-{identity_sha256}"
parent = output_root.expanduser().absolute()
destination = parent / result_id
if destination.exists():
return validate_e46_review_handoff(destination)
parent.mkdir(mode=0o700, parents=True, exist_ok=True)
staging = parent / f".{result_id}.{uuid.uuid4().hex}.tmp"
staging.mkdir(mode=0o700)
try:
for slot in E46_REVIEW_HANDOFF_SLOTS:
_write_reviewer_slot(
root=staging / slot,
slot=slot,
truth_island_id=truth.result_id,
sources=sources,
classes=classes,
review_template=review_template,
)
_write_text(staging / "README.md", _handoff_readme(truth.result_id))
artifacts = [
_artifact(path, staging)
for path in sorted(staging.rglob("*"))
if path.is_file()
]
manifest = {
"schema_version": E46_REVIEW_HANDOFF_SCHEMA,
"result_id": result_id,
"identity_sha256": identity_sha256,
"identity": identity,
"created_at_utc": _utc_now(),
"state": "prepared-awaiting-two-independent-human-reviews",
"ground_truth": False,
"artifacts": artifacts,
"authority": _AUTHORITY,
}
_write_json(staging / E46_REVIEW_HANDOFF_MANIFEST, manifest)
_fsync_tree(staging)
os.replace(staging, destination)
_fsync_directory(parent)
except BaseException:
shutil.rmtree(staging, ignore_errors=True)
raise
return validate_e46_review_handoff(destination)
def validate_e46_review_handoff(root: Path) -> E46ReviewHandoff:
"""Revalidate a complete E46 blind-review handoff."""
resolved = root.resolve(strict=True)
manifest = _read_json(resolved / E46_REVIEW_HANDOFF_MANIFEST)
identity = _object(manifest.get("identity"), "handoff identity")
identity_sha256 = manifest.get("identity_sha256")
if (
not resolved.is_dir()
or _SAFE_HANDOFF.fullmatch(resolved.name) is None
or manifest.get("schema_version") != E46_REVIEW_HANDOFF_SCHEMA
or manifest.get("result_id") != resolved.name
or manifest.get("state")
!= "prepared-awaiting-two-independent-human-reviews"
or manifest.get("ground_truth") is not False
or manifest.get("authority") != _AUTHORITY
or not isinstance(identity_sha256, str)
or hashlib.sha256(_canonical_json(identity)).hexdigest()
!= identity_sha256
or resolved.name != f"e46-review-handoff-{identity_sha256}"
or identity.get("profile") != E46_REVIEW_HANDOFF_PROFILE
or identity.get("blindness") != _BLINDNESS
or identity.get("reviewer_slots") != list(E46_REVIEW_HANDOFF_SLOTS)
):
raise E46ReviewHandoffError("E46 review handoff identity is invalid")
artifacts = _list(manifest.get("artifacts"), "handoff artifacts")
artifact_paths: set[str] = set()
for raw in artifacts:
artifact = _object(raw, "handoff artifact")
relative = artifact.get("path")
if not isinstance(relative, str):
raise E46ReviewHandoffError("handoff artifact path is invalid")
path = _safe_relative(resolved, relative)
if (
not path.is_file()
or path.is_symlink()
or path.stat().st_size != artifact.get("byte_length")
or _sha256(path) != artifact.get("sha256")
):
raise E46ReviewHandoffError("handoff artifact changed")
artifact_paths.add(relative)
required = {"README.md"}
for slot in E46_REVIEW_HANDOFF_SLOTS:
required.update(
{
f"{slot}/README.md",
f"{slot}/images.zip",
f"{slot}/cvat-empty-coco.zip",
f"{slot}/labels.json",
f"{slot}/source-map.jsonl",
f"{slot}/submission-template.json",
}
)
if not required.issubset(artifact_paths):
raise E46ReviewHandoffError("reviewer handoff artifacts are incomplete")
for slot in E46_REVIEW_HANDOFF_SLOTS:
_validate_slot(
resolved / slot,
expected_frame_count=_positive_int(
identity.get("frame_count"),
"handoff frame count",
),
expected_classes=tuple(
_string(value, "handoff class")
for value in _list(identity.get("classes"), "handoff classes")
),
)
return E46ReviewHandoff(
result_id=resolved.name,
result_root=resolved,
manifest=manifest,
)
def convert_cvat_coco_to_e48_review(
*,
handoff_root: Path,
truth_island_root: Path,
reviewer_slot: str,
reviewer_id: str,
cvat_export_path: Path,
submitted_at_utc: str,
output_path: Path,
) -> dict[str, Any]:
"""Convert one completed CVAT COCO export into a strict E48 review."""
handoff = validate_e46_review_handoff(handoff_root)
if reviewer_slot not in E46_REVIEW_HANDOFF_SLOTS:
raise E46ReviewHandoffError("reviewer slot is invalid")
if _SAFE_REVIEWER.fullmatch(reviewer_id) is None:
raise E46ReviewHandoffError("reviewer identity is invalid")
submitted = _utc_timestamp(submitted_at_utc, "review submitted_at_utc")
slot_root = handoff.result_root / reviewer_slot
source_rows = tuple(_read_jsonl(slot_root / "source-map.jsonl"))
template = _read_json(slot_root / "submission-template.json")
coco = _read_cvat_coco(cvat_export_path)
categories = _coco_categories(
coco.get("categories"),
allowed=frozenset(handoff.manifest["identity"]["classes"]),
)
image_names = _coco_images(
coco.get("images"),
expected={str(row["file_name"]) for row in source_rows},
)
objects_by_name: dict[str, list[dict[str, Any]]] = {
str(row["file_name"]): [] for row in source_rows
}
raw_annotations = _list(coco.get("annotations"), "COCO annotations")
for raw_annotation in raw_annotations:
annotation = _object(raw_annotation, "COCO annotation")
image_id = annotation.get("image_id")
category_id = annotation.get("category_id")
if image_id not in image_names or category_id not in categories:
raise E46ReviewHandoffError("COCO annotation identity is invalid")
attributes = _annotation_attributes(annotation)
objects_by_name[image_names[image_id]].append(
{
"category": categories[category_id],
"box_xyxy": _coco_box(annotation.get("bbox")),
"occluded": _required_bool(attributes, "occluded"),
"truncated": _required_bool(attributes, "truncated"),
"notes": _optional_text(attributes.get("notes")),
}
)
images: list[dict[str, Any]] = []
template_images = _list(template.get("images"), "submission images")
if len(template_images) != len(source_rows):
raise E46ReviewHandoffError("submission template coverage changed")
for raw_template, source in zip(template_images, source_rows, strict=True):
image = dict(_object(raw_template, "submission image"))
file_name = str(source["file_name"])
raw_objects = sorted(
objects_by_name[file_name],
key=lambda item: (
str(item["category"]),
tuple(float(value) for value in item["box_xyxy"]),
),
)
image["review_state"] = "reviewed"
image["objects"] = [
{"object_id": f"object-{index:03d}", **item}
for index, item in enumerate(raw_objects, start=1)
]
image["hard_negative"] = not raw_objects
image["notes"] = None
images.append(image)
document = {
"schema_version": E48_REVIEW_SCHEMA,
"truth_island_id": template["truth_island_id"],
"state": "completed-independent-no-model-assistance",
"reviewer_id": reviewer_id,
"review_round": 1,
"blindness": _BLINDNESS,
"images": images,
"acceptance": {
"all_images_reviewed": True,
"independent": True,
"submitted_at_utc": submitted,
},
}
_write_json_atomic(output_path, document)
try:
validate_e48_detector_review_submission(
truth_island_root=truth_island_root,
review_path=output_path,
)
except E48DetectorTruthSealError as reason:
raise E46ReviewHandoffError(
"converted E48 review is invalid"
) from reason
return document
def _write_reviewer_slot(
*,
root: Path,
slot: str,
truth_island_id: str,
sources: tuple[dict[str, Any], ...],
classes: tuple[str, ...],
review_template: dict[str, Any],
) -> None:
root.mkdir(mode=0o700, parents=True)
file_payloads = tuple(
(str(source["file_name"]), Path(source["path"]).read_bytes())
for source in sources
)
_write_zip(root / "images.zip", file_payloads)
coco = {
"info": {
"description": "Mission Core E46 blind detector review",
"version": E46_REVIEW_HANDOFF_PROFILE,
"truth_island_id": truth_island_id,
"reviewer_slot": slot,
"model_material_included": False,
},
"licenses": [],
"images": [
{
"id": int(source["truth_island_sequence"]),
"width": 800,
"height": 600,
"file_name": source["file_name"],
}
for source in sources
],
"annotations": [],
"categories": [
{"id": index, "name": name, "supercategory": "missioncore"}
for index, name in enumerate(classes, start=1)
],
}
_write_zip(
root / "cvat-empty-coco.zip",
(("annotations/instances_default.json", _canonical_json(coco) + b"\n"),),
)
labels = {
"schema_version": "missioncore.e46-cvat-labels/v1",
"labels": [
{
"name": name,
"attributes": [
{
"name": "occluded",
"input_type": "checkbox",
"default_value": "false",
},
{
"name": "truncated",
"input_type": "checkbox",
"default_value": "false",
},
],
}
for name in classes
],
}
_write_json(root / "labels.json", labels)
source_rows = [
{
"schema_version": E46_REVIEW_SOURCE_SCHEMA,
"truth_island_sequence": source["truth_island_sequence"],
"file_name": source["file_name"],
"source_path": source["source_path"],
"source_sha256": source["source_sha256"],
"byte_length": source["byte_length"],
}
for source in sources
]
_write_jsonl(root / "source-map.jsonl", source_rows)
submission = json.loads(json.dumps(review_template))
submission["schema_version"] = E48_REVIEW_SCHEMA
submission["state"] = "prepared-awaiting-independent-review"
submission["reviewer_id"] = None
submission["review_round"] = 1
submission["blindness"] = _BLINDNESS
submission["acceptance"] = None
_write_json(root / "submission-template.json", submission)
_write_text(root / "README.md", _slot_readme(slot))
def _verified_source(
*,
evaluation_root: Path,
reference: dict[str, Any],
) -> dict[str, Any]:
relative = _string(reference.get("source_path"), "E46 source path")
path = _safe_relative(evaluation_root, relative)
if path.is_symlink() or not path.is_file():
raise E46ReviewHandoffError("E46 source image is unavailable")
byte_length = _positive_int(reference.get("byte_length"), "source bytes")
source_sha256 = _string(reference.get("sha256"), "source SHA-256")
if path.stat().st_size != byte_length or _sha256(path) != source_sha256:
raise E46ReviewHandoffError("E46 source image identity changed")
with Image.open(path) as image:
if image.format != "PNG" or image.size != (800, 600):
raise E46ReviewHandoffError("E46 source image geometry changed")
image.verify()
sequence = _positive_int(
reference.get("truth_island_sequence"),
"truth island sequence",
)
return {
"truth_island_sequence": sequence,
"file_name": f"e46-{sequence:04d}-{Path(relative).name}",
"source_path": relative,
"source_sha256": source_sha256,
"byte_length": byte_length,
"path": path,
}
def _validate_slot(
root: Path,
*,
expected_frame_count: int,
expected_classes: tuple[str, ...],
) -> None:
source_rows = tuple(_read_jsonl(root / "source-map.jsonl"))
if (
len(source_rows) != expected_frame_count
or len({row.get("file_name") for row in source_rows})
!= expected_frame_count
):
raise E46ReviewHandoffError("reviewer source map is invalid")
with zipfile.ZipFile(root / "images.zip") as archive:
names = archive.namelist()
if names != [row["file_name"] for row in source_rows]:
raise E46ReviewHandoffError("reviewer image archive order changed")
for row in source_rows:
payload = archive.read(str(row["file_name"]))
if (
len(payload) != row.get("byte_length")
or hashlib.sha256(payload).hexdigest()
!= row.get("source_sha256")
):
raise E46ReviewHandoffError("reviewer image payload changed")
coco = _read_zip_json(root / "cvat-empty-coco.zip")
if (
coco.get("annotations") != []
or len(_list(coco.get("images"), "handoff COCO images"))
!= expected_frame_count
or tuple(
_string(row.get("name"), "handoff COCO category")
for row in _list(coco.get("categories"), "handoff COCO categories")
)
!= expected_classes
):
raise E46ReviewHandoffError("blind CVAT payload changed")
def _read_cvat_coco(path: Path) -> dict[str, Any]:
resolved = path.resolve(strict=True)
if resolved.suffix.lower() == ".zip":
return _read_zip_json(resolved)
return _read_json(resolved)
def _read_zip_json(path: Path) -> dict[str, Any]:
with zipfile.ZipFile(path) as archive:
json_names = [
name
for name in archive.namelist()
if PurePosixPath(name).suffix.lower() == ".json"
]
if len(json_names) != 1:
raise E46ReviewHandoffError(
"CVAT COCO archive must contain one JSON document"
)
try:
value = json.loads(archive.read(json_names[0]))
except (json.JSONDecodeError, UnicodeDecodeError) as reason:
raise E46ReviewHandoffError("CVAT COCO JSON is invalid") from reason
return _object(value, "CVAT COCO document")
def _coco_categories(
value: object,
*,
allowed: frozenset[str],
) -> dict[object, str]:
result: dict[object, str] = {}
for raw in _list(value, "COCO categories"):
row = _object(raw, "COCO category")
category_id = row.get("id")
name = row.get("name")
if (
isinstance(category_id, bool)
or not isinstance(category_id, (int, str))
or not isinstance(name, str)
or name not in allowed
or category_id in result
):
raise E46ReviewHandoffError("COCO category is invalid")
result[category_id] = name
if not result:
raise E46ReviewHandoffError("COCO categories are empty")
return result
def _coco_images(
value: object,
*,
expected: set[str],
) -> dict[object, str]:
result: dict[object, str] = {}
observed: set[str] = set()
for raw in _list(value, "COCO images"):
row = _object(raw, "COCO image")
image_id = row.get("id")
name = Path(_string(row.get("file_name"), "COCO file name")).name
if (
isinstance(image_id, bool)
or not isinstance(image_id, (int, str))
or image_id in result
or name in observed
or row.get("width") != 800
or row.get("height") != 600
):
raise E46ReviewHandoffError("COCO image is invalid")
result[image_id] = name
observed.add(name)
if observed != expected:
raise E46ReviewHandoffError("COCO image coverage is incomplete")
return result
def _annotation_attributes(annotation: dict[str, Any]) -> dict[str, Any]:
raw = annotation.get("attributes")
if isinstance(raw, dict):
result = dict(raw)
elif isinstance(raw, list):
result = {}
for item in raw:
row = _object(item, "COCO annotation attribute")
name = row.get("name")
if not isinstance(name, str) or name in result:
raise E46ReviewHandoffError("COCO annotation attribute is invalid")
result[name] = row.get("value")
elif raw is None:
result = {}
else:
raise E46ReviewHandoffError("COCO annotation attributes are invalid")
for name in ("occluded", "truncated"):
if name not in result and name in annotation:
result[name] = annotation[name]
return result
def _required_bool(attributes: dict[str, Any], name: str) -> bool:
value = attributes.get(name)
if isinstance(value, bool):
return value
if isinstance(value, str) and value.lower() in {"true", "false"}:
return value.lower() == "true"
raise E46ReviewHandoffError(f"COCO annotation {name} flag is missing")
def _coco_box(value: object) -> list[float]:
raw = _list(value, "COCO bbox")
if len(raw) != 4:
raise E46ReviewHandoffError("COCO bbox is invalid")
numbers: list[float] = []
for item in raw:
if isinstance(item, bool) or not isinstance(item, (int, float)):
raise E46ReviewHandoffError("COCO bbox is invalid")
number = float(item)
if not math.isfinite(number):
raise E46ReviewHandoffError("COCO bbox is invalid")
numbers.append(number)
x, y, width, height = numbers
if (
x < 0.0
or y < 0.0
or width <= 0.0
or height <= 0.0
or x + width > 800.0
or y + height > 600.0
):
raise E46ReviewHandoffError("COCO bbox is outside the source image")
return [x, y, x + width, y + height]
def _safe_relative(root: Path, relative: str) -> Path:
pure = PurePosixPath(relative)
if pure.is_absolute() or ".." in pure.parts or not pure.parts:
raise E46ReviewHandoffError("relative artifact path is unsafe")
resolved = (root / Path(*pure.parts)).resolve(strict=True)
try:
resolved.relative_to(root.resolve(strict=True))
except ValueError as reason:
raise E46ReviewHandoffError(
"relative artifact path escapes its root"
) from reason
return resolved
def _write_zip(path: Path, entries: tuple[tuple[str, bytes], ...]) -> None:
with zipfile.ZipFile(
path,
mode="w",
compression=zipfile.ZIP_DEFLATED,
compresslevel=6,
) as archive:
for name, payload in entries:
info = zipfile.ZipInfo(name, date_time=_ZIP_TIMESTAMP)
info.compress_type = zipfile.ZIP_DEFLATED
info.external_attr = 0o600 << 16
archive.writestr(info, payload)
os.chmod(path, 0o600)
def _write_json_atomic(path: Path, value: dict[str, Any]) -> None:
path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
temporary = path.parent / f".{path.name}.{uuid.uuid4().hex}.tmp"
_write_json(temporary, value)
os.replace(temporary, path)
_fsync_directory(path.parent)
def _write_json(path: Path, value: object) -> None:
path.write_bytes(_canonical_json(value) + b"\n")
os.chmod(path, 0o600)
def _write_jsonl(path: Path, rows: list[dict[str, Any]]) -> None:
path.write_bytes(b"".join(_canonical_json(row) + b"\n" for row in rows))
os.chmod(path, 0o600)
def _write_text(path: Path, value: str) -> None:
path.write_text(value, encoding="utf-8", newline="\n")
os.chmod(path, 0o600)
def _artifact(path: Path, root: Path) -> dict[str, Any]:
return {
"path": path.relative_to(root).as_posix(),
"byte_length": path.stat().st_size,
"sha256": _sha256(path),
}
def _read_json(path: Path) -> dict[str, Any]:
try:
value = json.loads(path.read_bytes())
except (OSError, json.JSONDecodeError, UnicodeDecodeError) as reason:
raise E46ReviewHandoffError(f"invalid JSON: {path.name}") from reason
return _object(value, path.name)
def _read_jsonl(path: Path) -> list[dict[str, Any]]:
rows: list[dict[str, Any]] = []
try:
for line in path.read_text(encoding="utf-8").splitlines():
rows.append(_object(json.loads(line), path.name))
except (OSError, json.JSONDecodeError, UnicodeDecodeError) as reason:
raise E46ReviewHandoffError(f"invalid JSONL: {path.name}") from reason
return rows
def _canonical_json(value: object) -> bytes:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
).encode("utf-8")
def _sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
for chunk in iter(lambda: stream.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def _object(value: object, label: str) -> dict[str, Any]:
if not isinstance(value, dict):
raise E46ReviewHandoffError(f"{label} must be an object")
return value
def _list(value: object, label: str) -> list[Any]:
if not isinstance(value, list):
raise E46ReviewHandoffError(f"{label} must be a list")
return value
def _string(value: object, label: str) -> str:
if not isinstance(value, str) or not value:
raise E46ReviewHandoffError(f"{label} must be a string")
return value
def _positive_int(value: object, label: str) -> int:
if not isinstance(value, int) or isinstance(value, bool) or value <= 0:
raise E46ReviewHandoffError(f"{label} must be a positive integer")
return value
def _optional_text(value: object) -> str | None:
if value is None:
return None
if not isinstance(value, str) or len(value) > 1000:
raise E46ReviewHandoffError("COCO annotation notes are invalid")
return value
def _utc_timestamp(value: object, label: str) -> str:
if not isinstance(value, str):
raise E46ReviewHandoffError(f"{label} is invalid")
try:
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
except ValueError as reason:
raise E46ReviewHandoffError(f"{label} is invalid") from reason
if parsed.tzinfo is None or parsed.utcoffset() != UTC.utcoffset(parsed):
raise E46ReviewHandoffError(f"{label} must be UTC")
return parsed.astimezone(UTC).isoformat().replace("+00:00", "Z")
def _utc_now() -> str:
return datetime.now(UTC).isoformat().replace("+00:00", "Z")
def _fsync_tree(root: Path) -> None:
for path in sorted(root.rglob("*"), reverse=True):
if path.is_file():
with path.open("rb") as stream:
os.fsync(stream.fileno())
elif path.is_dir():
_fsync_directory(path)
_fsync_directory(root)
def _fsync_directory(path: Path) -> None:
descriptor = os.open(path, os.O_RDONLY)
try:
os.fsync(descriptor)
finally:
os.close(descriptor)
def _handoff_readme(truth_island_id: str) -> str:
return f"""# Mission Core E46 blind detector review
Truth island: `{truth_island_id}`
This handoff contains two equivalent reviewer slots. Give each slot to a
different human reviewer. Reviewers must not see E47 predictions, scores,
candidate identities, model prelabels or each other's annotations.
After both independent reviews are complete, convert each CVAT COCO export with
`experiments/perception/convert_e46_cvat_review.py`. Adjudication and E48 sealing
happen only after both converted submissions pass validation.
"""
def _slot_readme(slot: str) -> str:
return f"""# E46 blind review slot: {slot}
1. Create a new CVAT image task with the seven labels in `labels.json`.
2. Add boolean `occluded` and `truncated` attributes to every label.
3. Upload `images.zip` as task data.
4. Import `cvat-empty-coco.zip` as COCO 1.0 annotations. It intentionally
contains zero objects and no model material.
5. Draw every identifiable in-FOV instance. Frames with no objects remain empty.
6. Set both attributes explicitly for every object.
7. Export the completed task as COCO 1.0.
Do not use model assistance, prelabels, E47 predictions or another review.
"""
@@ -55,6 +55,36 @@ class E48DetectorTruthSealError(RuntimeError):
"""An independent review or truth-seal artifact is invalid."""
def validate_e48_detector_review_submission(
*,
truth_island_root: Path,
review_path: Path,
) -> dict[str, Any]:
"""Validate one completed blind review before adjudication."""
try:
truth_island = read_e46_detector_truth_island(truth_island_root)
except E46DetectorTruthIslandError as reason:
raise E48DetectorTruthSealError("E46 truth island is invalid") from reason
references = tuple(
_read_jsonl(truth_island.result_root / E46_REFERENCES_NAME)
)
contract = _read_json(truth_island.result_root / E46_CONTRACT_NAME)
annotation = _object(contract.get("annotation"), "E46 annotation contract")
raw_classes = annotation.get("classes")
if not isinstance(raw_classes, list) or not all(
isinstance(value, str) for value in raw_classes
):
raise E48DetectorTruthSealError("E46 target classes are invalid")
document = _read_json(review_path.resolve(strict=True))
return _validate_review(
document,
truth_island_id=truth_island.result_id,
references=references,
target_classes=frozenset(raw_classes),
)
def build_e48_detector_truth_seal(
*,
truth_island_root: Path,