feat(perception): integrate calibrated operator pipeline

Add calibrated K1 projection, recorded and near-live perception qualification, unified Rerun operator layers, bounded replay admission, audited viewer controls, worker experiments, and lab evidence.
This commit is contained in:
DCCONSTRUCTIONS
2026-07-23 00:23:28 +03:00
parent ada2a55ee6
commit b53d6d5a45
221 changed files with 55923 additions and 1357 deletions
@@ -0,0 +1,62 @@
# Mission Core CVAT D-only profile
This profile installs the annotation control plane in a dedicated WSL2
distribution named `MissionCore-CVAT`. Its VHDX, Docker image store, CVAT
source, persistent volumes, reports, and imported datasets live below
`D:\NDC_MISSIONCORE`. It does not use the Docker Desktop image store that backs
the Triton, Frigate, and Ollama containers.
Pinned inputs:
- Ubuntu 24.04.4 WSL AMD64 image, SHA-256
`9b2f7730dc68227dd04a9f3e5eab86ad85caf556b8606ad94f1f29ff5c4fd3f5`
- CVAT `v2.70.0`
- D free-space floor: `360 GiB`
- WSL VHD logical ceiling: `32 GB` (sparse allocation)
The official CVAT Compose topology is retained. The override only replaces its
named volumes with explicit bind-backed directories inside the D-hosted WSL
VHD. Images are pulled serially and the D free-space floor is checked before and
after every image.
Run from the Windows host:
```powershell
wsl -d MissionCore-CVAT -u root -- bash /mnt/d/NDC_MISSIONCORE/workspace/mission-core-compute/cvat/provision_cvat_wsl.sh
wsl -d MissionCore-CVAT -u root -- bash /mnt/d/NDC_MISSIONCORE/workspace/mission-core-compute/cvat/ensure_cvat_admin.sh
wsl -d MissionCore-CVAT -u root -- bash /mnt/d/NDC_MISSIONCORE/workspace/mission-core-compute/cvat/import_e2_workspace.sh
```
After a Windows reboot, start the existing deployment without pulling images or
re-provisioning it:
```powershell
& D:\NDC_MISSIONCORE\workspace\mission-core-compute\cvat\Start-Cvat.ps1
```
The launcher checks the `360 GiB` D-drive floor, starts the pinned Compose
topology in the D-hosted WSL distribution, waits for the API, and keeps the WSL
runtime alive. It writes startup logs only below
`D:\NDC_MISSIONCORE\runtime\annotation\cvat\logs`.
From the Mission Core repository on the Mac, start CVAT if needed, discover the
current WSL address, and open the SSH tunnel without a hard-coded IP:
```bash
bash experiments/perception/worker/cvat/open_cvat_tunnel.sh
```
Keep that terminal open and use `http://localhost:18080`. The same SSH session
keeps the WSL runtime alive. Pass `--background` when detached runtime and
tunnel sessions are preferred; rerun the helper after a Mac or Windows reboot.
The generated administrator password is stored only in
`D:\NDC_MISSIONCORE\secrets\cvat\admin.env`; it is not printed by the script or
committed to Git.
Traefik binds inside the dedicated WSL environment to `127.0.0.1:8080` and
`127.0.0.1:8090`. It also publishes container port `8080` as WSL-internal port
`18080` so the existing Windows SSH service can forward it without relying on
Windows-to-WSL localhost forwarding. Remote review still uses the exact SSH
host alias and a local forward; this profile does not add routes, Windows port
proxies, DNS changes, or firewall rules.
@@ -0,0 +1,58 @@
[CmdletBinding()]
param(
[int]$TimeoutSeconds = 180
)
$ErrorActionPreference = 'Stop'
$Distro = 'MissionCore-CVAT'
$Root = 'D:\NDC_MISSIONCORE'
$RuntimeRoot = Join-Path $Root 'runtime\annotation\cvat'
$LogRoot = Join-Path $RuntimeRoot 'logs'
$LinuxStartScript = '/mnt/d/NDC_MISSIONCORE/workspace/mission-core-compute/cvat/start_cvat_runtime.sh'
$FreeGiBFloor = 360
$drive = Get-PSDrive -Name D
$freeGiB = [math]::Floor($drive.Free / 1GB)
if ($freeGiB -lt $FreeGiBFloor) {
throw "D: free-space floor crossed ($freeGiB GiB free; $FreeGiBFloor GiB required)"
}
try {
$about = Invoke-RestMethod -Uri 'http://localhost:8080/api/server/about' -TimeoutSec 3
if ($about.version -eq '2.70.0') {
Write-Output "CVAT already ready version=$($about.version) free_gib=$freeGiB"
exit 0
}
} catch {
# A stopped WSL distribution is the normal condition after a Windows reboot.
}
New-Item -ItemType Directory -Path $LogRoot -Force | Out-Null
$timestamp = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssZ')
$stdoutPath = Join-Path $LogRoot "start-$timestamp.stdout.log"
$stderrPath = Join-Path $LogRoot "start-$timestamp.stderr.log"
$arguments = @(
'-d', $Distro,
'-u', 'root',
'--', 'bash', $LinuxStartScript, '--keepalive'
)
Start-Process -FilePath 'wsl.exe' -ArgumentList $arguments -WindowStyle Hidden `
-RedirectStandardOutput $stdoutPath -RedirectStandardError $stderrPath
$deadline = (Get-Date).AddSeconds($TimeoutSeconds)
do {
Start-Sleep -Seconds 2
try {
$about = Invoke-RestMethod -Uri 'http://localhost:8080/api/server/about' -TimeoutSec 3
if ($about.version -eq '2.70.0') {
Write-Output "CVAT ready version=$($about.version) free_gib=$freeGiB"
Write-Output "startup_log=$stdoutPath"
exit 0
}
} catch {
# Continue until the complete CVAT Compose topology is ready.
}
} while ((Get-Date) -lt $deadline)
throw "CVAT did not become ready within $TimeoutSeconds seconds; inspect $stderrPath"
@@ -0,0 +1,50 @@
services:
traefik:
ports: !override
- 127.0.0.1:8080:8080
- 127.0.0.1:8090:8090
- 0.0.0.0:18080:8080
volumes:
cvat_db:
driver: local
driver_opts:
type: none
o: bind
device: /srv/mission-core-cvat/volumes/cvat_db
cvat_data:
driver: local
driver_opts:
type: none
o: bind
device: /srv/mission-core-cvat/volumes/cvat_data
cvat_keys:
driver: local
driver_opts:
type: none
o: bind
device: /srv/mission-core-cvat/volumes/cvat_keys
cvat_logs:
driver: local
driver_opts:
type: none
o: bind
device: /srv/mission-core-cvat/volumes/cvat_logs
cvat_inmem_db:
driver: local
driver_opts:
type: none
o: bind
device: /srv/mission-core-cvat/volumes/cvat_inmem_db
cvat_events_db:
driver: local
driver_opts:
type: none
o: bind
device: /srv/mission-core-cvat/volumes/cvat_events_db
cvat_cache_db:
driver: local
driver_opts:
type: none
o: bind
device: /srv/mission-core-cvat/volumes/cvat_cache_db
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
set -Eeuo pipefail
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
readonly SECRET_ROOT="/mnt/d/NDC_MISSIONCORE/secrets/cvat"
readonly SECRET_FILE="${SECRET_ROOT}/admin.env"
if [[ "${EUID}" -ne 0 ]]; then
echo "ensure_cvat_admin.sh must run as root" >&2
exit 2
fi
install -d -m 0700 "${SECRET_ROOT}"
if [[ ! -f "${SECRET_FILE}" ]]; then
umask 077
password="$(openssl rand -hex 24)"
{
printf 'CVAT_ADMIN_USERNAME=missioncore\n'
printf 'CVAT_ADMIN_EMAIL=missioncore@local.invalid\n'
printf 'CVAT_ADMIN_PASSWORD=%s\n' "${password}"
} >"${SECRET_FILE}"
fi
set -a
# shellcheck disable=SC1090
. "${SECRET_FILE}"
set +a
docker exec \
-e "CVAT_ADMIN_USERNAME=${CVAT_ADMIN_USERNAME}" \
-e "CVAT_ADMIN_EMAIL=${CVAT_ADMIN_EMAIL}" \
-e "CVAT_ADMIN_PASSWORD=${CVAT_ADMIN_PASSWORD}" \
cvat_server \
python3 /home/django/manage.py shell -c \
'import os; from django.contrib.auth import get_user_model; User = get_user_model(); user, _ = User.objects.get_or_create(username=os.environ["CVAT_ADMIN_USERNAME"]); user.email = os.environ["CVAT_ADMIN_EMAIL"]; user.is_staff = True; user.is_superuser = True; user.set_password(os.environ["CVAT_ADMIN_PASSWORD"]); user.save()'
printf 'admin_ready=true\nusername=%s\nsecret_file=%s\n' \
"${CVAT_ADMIN_USERNAME}" "${SECRET_FILE}"
@@ -0,0 +1,284 @@
from __future__ import annotations
import argparse
import hashlib
import json
import os
import time
from collections import Counter
from datetime import UTC, datetime
from pathlib import Path
from typing import Any
from cvat_sdk import make_client, models
from cvat_sdk.api_client.exceptions import ServiceException
from cvat_sdk.core.proxies.tasks import ResourceType
WORKSPACE_ID = (
"annotation-workspace-"
"9a950d1c37d56dc12cc285b13c5addd7795285879cbcb1fbb2d5811c3c69821a"
)
EVALUATION_PACK_ID = (
"evaluation-pack-"
"7a983bba75d46c7c260252cb2d461e1384dcb92cda9e164397e841e6ebb37789"
)
EXPECTED_FRAME_COUNT = 64
EXPECTED_INSTANCE_COUNT = 775
BACKGROUND_LABEL = {
"name": "background",
"color": "#000000",
"attributes": [],
}
def _sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as source:
for chunk in iter(lambda: source.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def _label_specs(raw_labels: list[dict[str, Any]]) -> list[dict[str, Any]]:
labels = [
{
"name": item["name"],
"color": item["color"],
"attributes": [],
}
for item in raw_labels
]
if not any(label["name"] == BACKGROUND_LABEL["name"] for label in labels):
labels.insert(0, dict(BACKGROUND_LABEL))
return labels
def _annotation_counts(task: Any) -> dict[str, int]:
annotations = task.get_annotations()
return {
"shape_count": len(annotations.shapes),
"tag_count": len(annotations.tags),
"track_count": len(annotations.tracks),
}
def _list_tasks_when_ready(client: Any, *, attempts: int = 60) -> list[Any]:
for attempt in range(1, attempts + 1):
try:
return list(client.tasks.list())
except ServiceException as error:
if error.status not in {500, 502, 503} or attempt == attempts:
raise
time.sleep(2)
raise AssertionError("unreachable")
def _task_record(task: Any, *, disposition: str, expected_labels: list[str]) -> dict[str, Any]:
task.fetch()
task_labels = list(task.get_labels())
actual_labels = sorted(label.name for label in task_labels)
if task.size != EXPECTED_FRAME_COUNT:
raise RuntimeError(
f"Task {task.id} has {task.size} frames, expected {EXPECTED_FRAME_COUNT}"
)
if actual_labels != sorted(expected_labels):
raise RuntimeError(
f"Task {task.id} labels differ: actual={actual_labels!r} "
f"expected={sorted(expected_labels)!r}"
)
annotation_counts = _annotation_counts(task)
label_names_by_id = {label.id: label.name for label in task_labels}
annotations = task.get_annotations()
shape_counts_by_label = dict(
sorted(
Counter(
label_names_by_id.get(shape.label_id, f"unknown:{shape.label_id}")
for shape in annotations.shapes
).items()
)
)
return {
"id": task.id,
"name": task.name,
"disposition": disposition,
"frame_count": task.size,
"label_names": actual_labels,
**annotation_counts,
"shape_counts_by_label": shape_counts_by_label,
"url_path": f"/tasks/{task.id}",
}
def _ensure_task(
client: Any,
*,
name: str,
labels: list[dict[str, Any]],
images_path: Path,
annotation_path: Path,
annotation_format: str,
expected_shape_count: int | None,
) -> dict[str, Any]:
matches = [task for task in _list_tasks_when_ready(client) if task.name == name]
if len(matches) > 1:
raise RuntimeError(f"Multiple CVAT tasks have the reserved name {name!r}")
expected_labels = [label["name"] for label in labels]
if matches:
task = matches[0]
task.fetch()
actual_labels = sorted(label.name for label in task.get_labels())
expected_without_background = sorted(
label for label in expected_labels if label != BACKGROUND_LABEL["name"]
)
disposition = "reused"
if (
actual_labels == expected_without_background
and BACKGROUND_LABEL["name"] in expected_labels
):
task.update(
models.PatchedTaskWriteRequest(
labels=[models.PatchedLabelRequest(**BACKGROUND_LABEL)]
)
)
disposition = "reused-and-background-label-added"
record = _task_record(
task,
disposition=disposition,
expected_labels=expected_labels,
)
if record["shape_count"] == 0:
task.import_annotations(annotation_format, annotation_path)
record = _task_record(
task,
disposition=f"{disposition}-and-annotations-imported",
expected_labels=expected_labels,
)
if expected_shape_count is not None and record["shape_count"] != expected_shape_count:
raise RuntimeError(
f"Task {task.id} has {record['shape_count']} shapes, "
f"expected {expected_shape_count}"
)
return record
task = client.tasks.create_from_data(
spec=models.TaskWriteRequest(
name=name,
labels=labels,
segment_size=EXPECTED_FRAME_COUNT,
overlap=0,
),
resources=[images_path],
resource_type=ResourceType.LOCAL,
data_params={
"image_quality": 100,
"sorting_method": "lexicographical",
"use_cache": False,
},
annotation_path=annotation_path,
annotation_format=annotation_format,
status_check_period=2,
)
record = _task_record(task, disposition="created", expected_labels=expected_labels)
if expected_shape_count is not None and record["shape_count"] != expected_shape_count:
raise RuntimeError(
f"Task {task.id} has {record['shape_count']} shapes, "
f"expected {expected_shape_count}"
)
return record
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--server", required=True)
parser.add_argument("--username", required=True)
parser.add_argument("--password-env", required=True)
parser.add_argument("--workspace", type=Path, required=True)
parser.add_argument("--report", type=Path, required=True)
args = parser.parse_args()
password = os.environ.get(args.password_env)
if not password:
raise RuntimeError(f"Password environment variable {args.password_env!r} is empty")
workspace = args.workspace.resolve()
if workspace.name != WORKSPACE_ID:
raise RuntimeError(f"Unexpected annotation workspace: {workspace}")
manifest_path = workspace / "manifest.json"
labels_path = workspace / "cvat" / "labels.json"
images_path = workspace / "cvat" / "images.zip"
instance_path = workspace / "cvat" / "instance-coco.zip"
semantic_path = workspace / "cvat" / "semantic-mask.zip"
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
labels = json.loads(labels_path.read_text(encoding="utf-8"))
if manifest["ground_truth"] is not False:
raise RuntimeError("LAB E2 import must remain an unreviewed model draft")
if manifest["identity"]["frame_count"] != EXPECTED_FRAME_COUNT:
raise RuntimeError("Unexpected LAB E2 frame count")
if manifest["identity"]["draft_instance_count"] != EXPECTED_INSTANCE_COUNT:
raise RuntimeError("Unexpected LAB E2 instance count")
task_specs = [
{
"name": "LAB E2 | K1 | instance prelabels | pack 7a983bba",
"labels": _label_specs(labels["instance_task"]),
"annotation_path": instance_path,
"annotation_format": "COCO 1.0",
"expected_shape_count": EXPECTED_INSTANCE_COUNT,
},
{
"name": "LAB E2 | K1 | dense semantic prelabels | pack 7a983bba",
"labels": _label_specs(labels["semantic_task"]),
"annotation_path": semantic_path,
"annotation_format": "Segmentation mask 1.1",
"expected_shape_count": None,
},
]
with make_client(args.server, credentials=(args.username, password)) as client:
client.check_server_version(fail_if_unsupported=True)
task_records = [
_ensure_task(
client,
name=task_spec["name"],
labels=task_spec["labels"],
images_path=images_path,
annotation_path=task_spec["annotation_path"],
annotation_format=task_spec["annotation_format"],
expected_shape_count=task_spec["expected_shape_count"],
)
for task_spec in task_specs
]
report = {
"schema_version": "missioncore.lab-e2-cvat-import/v1",
"created_at_utc": datetime.now(UTC).isoformat(timespec="milliseconds").replace(
"+00:00", "Z"
),
"server": args.server,
"cvat_version": "v2.70.0",
"workspace_id": WORKSPACE_ID,
"evaluation_pack_id": EVALUATION_PACK_ID,
"workspace_manifest_sha256": _sha256(manifest_path),
"ground_truth": False,
"inputs": {
"images_zip_sha256": _sha256(images_path),
"instance_coco_zip_sha256": _sha256(instance_path),
"semantic_mask_zip_sha256": _sha256(semantic_path),
},
"tasks": task_records,
"next_gate": (
"two-pass human review and reviewed export; "
"do not treat drafts as accuracy evidence"
),
}
args.report.parent.mkdir(parents=True, exist_ok=True)
args.report.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
print(json.dumps(report, indent=2))
if __name__ == "__main__":
main()
@@ -0,0 +1,89 @@
#!/usr/bin/env bash
set -Eeuo pipefail
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
readonly FREE_GIB_FLOOR="${FREE_GIB_FLOOR:-360}"
readonly WINDOWS_ROOT="/mnt/d/NDC_MISSIONCORE"
readonly COMPUTE_ROOT="${WINDOWS_ROOT}/workspace/mission-core-compute"
readonly TOOL_ROOT="/srv/mission-core-cvat/tools/e2-import"
readonly SECRET_FILE="${WINDOWS_ROOT}/secrets/cvat/admin.env"
readonly WORKSPACE_ROOT="${WINDOWS_ROOT}/runtime/annotation/imports/LAB-E2/annotation-workspace-9a950d1c37d56dc12cc285b13c5addd7795285879cbcb1fbb2d5811c3c69821a"
readonly REPORT_ROOT="${WINDOWS_ROOT}/runtime/annotation/cvat/reports"
if [[ "${EUID}" -ne 0 ]]; then
echo "import_e2_workspace.sh must run as root" >&2
exit 2
fi
free_gib() {
df -BG --output=avail "${WINDOWS_ROOT}" | tail -n 1 | tr -dc '0-9'
}
guard_disk() {
local stage="$1"
local free
free="$(free_gib)"
printf 'disk_guard stage=%s free_gib=%s floor_gib=%s\n' \
"${stage}" "${free}" "${FREE_GIB_FLOOR}"
if (( free < FREE_GIB_FLOOR )); then
echo "D: free-space floor crossed; refusing further writes" >&2
exit 3
fi
}
wait_for_cvat() {
local attempt http_code
for attempt in $(seq 1 60); do
http_code="$(
curl --silent --output /dev/null --write-out '%{http_code}' \
"http://localhost:8080/api/server/about" || true
)"
if [[ "${http_code}" == "200" ]]; then
http_code="$(
curl --silent --output /dev/null --write-out '%{http_code}' \
"http://localhost:8080/api/tasks" || true
)"
if [[ "${http_code}" == "200" || "${http_code}" == "401" || "${http_code}" == "403" ]]; then
printf 'cvat_ready attempt=%s tasks_http=%s\n' "${attempt}" "${http_code}"
return 0
fi
fi
sleep 2
done
echo "CVAT API did not become ready within 120 seconds" >&2
return 1
}
test -f "${SECRET_FILE}"
test -f "${WORKSPACE_ROOT}/manifest.json"
test -f "${COMPUTE_ROOT}/cvat/import_e2_workspace.py"
guard_disk preflight
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y python3-venv
install -d -m 0750 "${TOOL_ROOT}" "${REPORT_ROOT}"
if [[ ! -x "${TOOL_ROOT}/venv/bin/python" ]]; then
python3 -m venv "${TOOL_ROOT}/venv"
"${TOOL_ROOT}/venv/bin/pip" install --disable-pip-version-check \
"cvat-sdk==2.70.0"
fi
set -a
# shellcheck disable=SC1090
. "${SECRET_FILE}"
set +a
wait_for_cvat
report="${REPORT_ROOT}/lab-e2-cvat-import-$(date -u +%Y%m%dT%H%M%SZ).json"
"${TOOL_ROOT}/venv/bin/python" "${COMPUTE_ROOT}/cvat/import_e2_workspace.py" \
--server "http://localhost:8080" \
--username "${CVAT_ADMIN_USERNAME}" \
--password-env CVAT_ADMIN_PASSWORD \
--workspace "${WORKSPACE_ROOT}" \
--report "${report}"
guard_disk imported
printf 'import_report=%s\n' "${report}"
@@ -0,0 +1,179 @@
from __future__ import annotations
import argparse
import hashlib
import json
import os
from datetime import UTC, datetime
from pathlib import Path, PurePosixPath
from typing import Any
from cvat_sdk import make_client
from import_e2_workspace import _ensure_task, _label_specs, _sha256
WORKSPACE_SCHEMA = "missioncore.lab-e3-cvat-review-workspace/v1"
IDENTITY_SCHEMA = "missioncore.lab-e3-cvat-review-identity/v1"
EXPECTED_PACK_ID = (
"evaluation-pack-"
"7a983bba75d46c7c260252cb2d461e1384dcb92cda9e164397e841e6ebb37789"
)
EXPECTED_RESULT_ID = (
"e3-segmentation-"
"01bd497c44c2b940add145ec784d3418010327bce0baddd4420b0925317e8a16"
)
EXPECTED_FRAMES = 64
def _canonical_json(value: object) -> bytes:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode()
def _valid_sha256(value: object) -> bool:
return (
isinstance(value, str)
and len(value) == 64
and all(character in "0123456789abcdef" for character in value)
)
def _read_object(path: Path) -> dict[str, Any]:
value = json.loads(path.read_text(encoding="utf-8"))
if not isinstance(value, dict):
raise RuntimeError(f"JSON root is not an object: {path}")
return value
def _safe_artifact(root: Path, encoded: object) -> Path:
if not isinstance(encoded, str):
raise RuntimeError("artifact path is not a string")
relative = PurePosixPath(encoded)
if relative.is_absolute() or ".." in relative.parts or not relative.parts:
raise RuntimeError("artifact path is unsafe")
path = root.joinpath(*relative.parts).resolve(strict=True)
if not path.is_file() or not path.is_relative_to(root):
raise RuntimeError("artifact path escaped its root")
return path
def _workspace(root: Path, images_zip: Path) -> tuple[dict[str, Any], dict[str, Path]]:
manifest_path = root / "manifest.json"
manifest = _read_object(manifest_path)
identity = manifest.get("identity")
identity_sha256 = manifest.get("identity_sha256")
if (
manifest.get("schema_version") != WORKSPACE_SCHEMA
or manifest.get("ground_truth") is not False
or not isinstance(identity, dict)
or identity.get("schema_version") != IDENTITY_SCHEMA
or not _valid_sha256(identity_sha256)
or hashlib.sha256(_canonical_json(identity)).hexdigest() != identity_sha256
or manifest.get("workspace_id") != f"e3-cvat-review-{identity_sha256}"
or root.name != manifest.get("workspace_id")
or identity.get("evaluation_pack_id") != EXPECTED_PACK_ID
or identity.get("e3_result_id") != EXPECTED_RESULT_ID
or identity.get("frame_count") != EXPECTED_FRAMES
or _sha256(images_zip) != identity.get("images_zip_sha256")
):
raise RuntimeError("LAB E3 CVAT review workspace is incompatible")
artifacts: dict[str, Path] = {}
descriptors = manifest.get("artifacts")
if not isinstance(descriptors, list):
raise RuntimeError("LAB E3 CVAT artifact list is invalid")
for descriptor in descriptors:
if not isinstance(descriptor, dict):
raise RuntimeError("LAB E3 CVAT artifact descriptor is invalid")
path = _safe_artifact(root, descriptor.get("path"))
if (
path.stat().st_size != descriptor.get("bytes")
or not _valid_sha256(descriptor.get("sha256"))
or _sha256(path) != descriptor["sha256"]
):
raise RuntimeError(f"LAB E3 CVAT artifact changed: {path.name}")
artifacts[path.name] = path
if set(artifacts) != {
"labels.json",
"control-fisheye-mask.zip",
"challenger-kb4-cubemap5.zip",
}:
raise RuntimeError("LAB E3 CVAT artifact set changed")
return manifest, artifacts
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--server", required=True)
parser.add_argument("--username", required=True)
parser.add_argument("--password-env", required=True)
parser.add_argument("--workspace", type=Path, required=True)
parser.add_argument("--images-zip", type=Path, required=True)
parser.add_argument("--report", type=Path, required=True)
args = parser.parse_args()
password = os.environ.get(args.password_env)
if not password:
raise RuntimeError(f"Password environment variable {args.password_env!r} is empty")
workspace = args.workspace.resolve(strict=True)
images_zip = args.images_zip.resolve(strict=True)
manifest, artifacts = _workspace(workspace, images_zip)
labels_document = _read_object(artifacts["labels.json"])
semantic_labels = labels_document.get("semantic_task")
if not isinstance(semantic_labels, list):
raise RuntimeError("semantic labels are absent")
labels = _label_specs(semantic_labels)
task_specs = (
{
"name": "LAB E3 | K1 | EoMT fisheye control | pack 7a983bba",
"annotation": artifacts["control-fisheye-mask.zip"],
"role": "control",
},
{
"name": "LAB E3 | K1 | EoMT KB4 cubemap5 challenger | pack 7a983bba",
"annotation": artifacts["challenger-kb4-cubemap5.zip"],
"role": "challenger",
},
)
with make_client(args.server, credentials=(args.username, password)) as client:
client.check_server_version(fail_if_unsupported=True)
tasks = []
for spec in task_specs:
record = _ensure_task(
client,
name=spec["name"],
labels=labels,
images_path=images_zip,
annotation_path=spec["annotation"],
annotation_format="Segmentation mask 1.1",
expected_shape_count=None,
)
record["role"] = spec["role"]
tasks.append(record)
report = {
"schema_version": "missioncore.lab-e3-cvat-import/v1",
"created_at_utc": datetime.now(UTC)
.isoformat(timespec="milliseconds")
.replace("+00:00", "Z"),
"server": args.server,
"cvat_version": "v2.70.0",
"workspace_id": manifest["workspace_id"],
"workspace_manifest_sha256": _sha256(workspace / "manifest.json"),
"evaluation_pack_id": EXPECTED_PACK_ID,
"e3_result_id": EXPECTED_RESULT_ID,
"ground_truth": False,
"priority_image_ids": manifest["identity"]["priority_image_ids"],
"tasks": tasks,
"next_gate": "two-pass human review and reviewed export",
}
args.report.parent.mkdir(parents=True, exist_ok=True)
args.report.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
print(json.dumps(report, indent=2))
if __name__ == "__main__":
main()
@@ -0,0 +1,79 @@
#!/usr/bin/env bash
set -Eeuo pipefail
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
readonly FREE_GIB_FLOOR="${FREE_GIB_FLOOR:-360}"
readonly WINDOWS_ROOT="/mnt/d/NDC_MISSIONCORE"
readonly COMPUTE_ROOT="${WINDOWS_ROOT}/workspace/mission-core-compute"
readonly TOOL_ROOT="/srv/mission-core-cvat/tools/e2-import"
readonly SECRET_FILE="${WINDOWS_ROOT}/secrets/cvat/admin.env"
readonly WORKSPACE_ID="e3-cvat-review-ca599521e345446ca8e9af5a9013062099278e7317f83ff89740c6b092ddc52f"
readonly WORKSPACE_ROOT="${WINDOWS_ROOT}/runtime/annotation/imports/LAB-E3/${WORKSPACE_ID}"
readonly E2_WORKSPACE_ID="annotation-workspace-9a950d1c37d56dc12cc285b13c5addd7795285879cbcb1fbb2d5811c3c69821a"
readonly IMAGES_ZIP="${WINDOWS_ROOT}/runtime/annotation/imports/LAB-E2/${E2_WORKSPACE_ID}/cvat/images.zip"
readonly REPORT_ROOT="${WINDOWS_ROOT}/runtime/annotation/cvat/reports"
if [[ "${EUID}" -ne 0 ]]; then
echo "import_e3_review.sh must run as root" >&2
exit 2
fi
free_gib() {
df -BG --output=avail "${WINDOWS_ROOT}" | tail -n 1 | tr -dc '0-9'
}
guard_disk() {
local stage="$1"
local free
free="$(free_gib)"
printf 'disk_guard stage=%s free_gib=%s floor_gib=%s\n' \
"${stage}" "${free}" "${FREE_GIB_FLOOR}"
if (( free < FREE_GIB_FLOOR )); then
echo "D: free-space floor crossed; refusing further writes" >&2
exit 3
fi
}
wait_for_cvat() {
local attempt http_code
for attempt in $(seq 1 60); do
http_code="$(
curl --silent --output /dev/null --write-out '%{http_code}' \
"http://localhost:8080/api/server/about" || true
)"
if [[ "${http_code}" == "200" ]]; then
printf 'cvat_ready attempt=%s\n' "${attempt}"
return 0
fi
sleep 2
done
echo "CVAT API did not become ready within 120 seconds" >&2
return 1
}
test -x "${TOOL_ROOT}/venv/bin/python"
test -f "${SECRET_FILE}"
test -f "${WORKSPACE_ROOT}/manifest.json"
test -f "${IMAGES_ZIP}"
test -f "${COMPUTE_ROOT}/cvat/import_e2_workspace.py"
test -f "${COMPUTE_ROOT}/cvat/import_e3_review.py"
guard_disk preflight
set -a
# shellcheck disable=SC1090
. "${SECRET_FILE}"
set +a
wait_for_cvat
report="${REPORT_ROOT}/lab-e3-cvat-import-$(date -u +%Y%m%dT%H%M%SZ).json"
"${TOOL_ROOT}/venv/bin/python" "${COMPUTE_ROOT}/cvat/import_e3_review.py" \
--server "http://localhost:8080" \
--username "${CVAT_ADMIN_USERNAME}" \
--password-env CVAT_ADMIN_PASSWORD \
--workspace "${WORKSPACE_ROOT}" \
--images-zip "${IMAGES_ZIP}" \
--report "${report}"
guard_disk imported
printf 'import_report=%s\n' "${report}"
@@ -0,0 +1,91 @@
#!/usr/bin/env bash
set -Eeuo pipefail
readonly SSH_ALIAS="mission-gpu"
readonly LOCAL_PORT="${CVAT_LOCAL_PORT:-18080}"
readonly REMOTE_WSL_PORT="18080"
readonly LINUX_START_SCRIPT='/mnt/d/NDC_MISSIONCORE/workspace/mission-core-compute/cvat/start_cvat_runtime.sh'
if curl --max-time 2 --fail --silent \
"http://localhost:${LOCAL_PORT}/api/server/about" >/dev/null 2>&1; then
printf 'cvat_tunnel_ready url=http://localhost:%s\n' "${LOCAL_PORT}"
exit 0
fi
if command -v lsof >/dev/null \
&& lsof -nP -iTCP:"${LOCAL_PORT}" -sTCP:LISTEN >/dev/null 2>&1; then
echo "Local port ${LOCAL_PORT} is already occupied by a non-responsive process" >&2
exit 2
fi
runtime_command="wsl.exe -d MissionCore-CVAT -u root -- bash ${LINUX_START_SCRIPT} --keepalive"
background=false
if [[ "${1:-}" == "--background" ]]; then
background=true
ssh -f "${SSH_ALIAS}" "${runtime_command}"
else
ssh "${SSH_ALIAS}" "${runtime_command}" &
runtime_ssh_pid=$!
cleanup() {
if [[ -n "${tunnel_ssh_pid:-}" ]]; then
kill "${tunnel_ssh_pid}" 2>/dev/null || true
fi
kill "${runtime_ssh_pid}" 2>/dev/null || true
}
trap cleanup EXIT INT TERM
fi
for attempt in $(seq 1 30); do
cvat_ip="$(
ssh "${SSH_ALIAS}" \
"wsl.exe -d MissionCore-CVAT -u root -- hostname -I" \
| tr -d '\r' | awk '{print $1}'
)"
if [[ "${cvat_ip}" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
break
fi
sleep 1
done
if [[ ! "${cvat_ip:-}" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "MissionCore-CVAT did not return a valid WSL address" >&2
if [[ "${background}" == false ]]; then
kill "${runtime_ssh_pid}" 2>/dev/null || true
fi
exit 3
fi
forward=(
-N
-L "${LOCAL_PORT}:${cvat_ip}:${REMOTE_WSL_PORT}"
-o ExitOnForwardFailure=yes
"${SSH_ALIAS}"
)
if [[ "${background}" == true ]]; then
ssh -f "${forward[@]}"
else
ssh "${forward[@]}" &
tunnel_ssh_pid=$!
fi
for attempt in $(seq 1 60); do
if curl --max-time 2 --fail --silent \
"http://localhost:${LOCAL_PORT}/api/server/about" >/dev/null 2>&1; then
printf 'cvat_tunnel_ready url=http://localhost:%s wsl_ip=%s\n' \
"${LOCAL_PORT}" "${cvat_ip}"
if [[ "${background}" == true ]]; then
exit 0
fi
wait "${tunnel_ssh_pid}"
tunnel_status=$?
kill "${runtime_ssh_pid}" 2>/dev/null || true
exit "${tunnel_status}"
fi
sleep 2
done
echo "CVAT tunnel did not become ready within 120 seconds" >&2
if [[ "${background}" == false ]]; then
kill "${tunnel_ssh_pid}" "${runtime_ssh_pid}" 2>/dev/null || true
fi
exit 4
@@ -0,0 +1,132 @@
#!/usr/bin/env bash
set -Eeuo pipefail
# Do not inherit Windows executables into this D-only runtime. In particular,
# Docker Desktop's docker.exe belongs to a different image store.
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
readonly CVAT_TAG="${CVAT_TAG:-v2.70.0}"
readonly FREE_GIB_FLOOR="${FREE_GIB_FLOOR:-360}"
readonly WINDOWS_ROOT="/mnt/d/NDC_MISSIONCORE"
readonly COMPUTE_ROOT="${WINDOWS_ROOT}/workspace/mission-core-compute"
readonly RUNTIME_ROOT="${WINDOWS_ROOT}/runtime/annotation/cvat"
readonly CVAT_ROOT="${RUNTIME_ROOT}/source/cvat-${CVAT_TAG}"
readonly STATE_ROOT="/srv/mission-core-cvat"
readonly OVERRIDE_FILE="${COMPUTE_ROOT}/cvat/docker-compose.override.yml"
if [[ "${EUID}" -ne 0 ]]; then
echo "provision_cvat_wsl.sh must run as root" >&2
exit 2
fi
free_gib() {
df -BG --output=avail "${WINDOWS_ROOT}" | tail -n 1 | tr -dc '0-9'
}
guard_disk() {
local stage="$1"
local free
free="$(free_gib)"
printf 'disk_guard stage=%s free_gib=%s floor_gib=%s\n' \
"${stage}" "${free}" "${FREE_GIB_FLOOR}"
if (( free < FREE_GIB_FLOOR )); then
echo "D: free-space floor crossed; refusing further writes" >&2
exit 3
fi
}
guard_disk preflight
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y ca-certificates curl git
if ! dpkg-query -W -f='${Status}' docker-ce 2>/dev/null \
| grep -qx 'install ok installed'; then
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
-o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
# shellcheck disable=SC1091
. /etc/os-release
arch="$(dpkg --print-architecture)"
printf 'deb [arch=%s signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu %s stable\n' \
"${arch}" "${VERSION_CODENAME}" >/etc/apt/sources.list.d/docker.list
apt-get update
apt-get install -y \
docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
fi
systemctl enable --now docker
docker version
docker compose version
guard_disk docker-engine
install -d -m 0750 \
"${STATE_ROOT}/volumes/cvat_db" \
"${STATE_ROOT}/volumes/cvat_data" \
"${STATE_ROOT}/volumes/cvat_keys" \
"${STATE_ROOT}/volumes/cvat_logs" \
"${STATE_ROOT}/volumes/cvat_inmem_db" \
"${STATE_ROOT}/volumes/cvat_events_db" \
"${STATE_ROOT}/volumes/cvat_cache_db" \
"${RUNTIME_ROOT}/source" \
"${RUNTIME_ROOT}/reports"
if [[ ! -d "${CVAT_ROOT}/.git" ]]; then
git clone --depth 1 --branch "${CVAT_TAG}" \
https://github.com/cvat-ai/cvat.git "${CVAT_ROOT}"
fi
test "$(git -C "${CVAT_ROOT}" describe --tags --exact-match)" = "${CVAT_TAG}"
test -f "${OVERRIDE_FILE}"
guard_disk cvat-source
export CVAT_VERSION="${CVAT_TAG}"
export CVAT_HOST="localhost"
export CVAT_HTTP_PORT="8080"
export COMPOSE_PROJECT_NAME="missioncore-cvat"
compose=(
docker compose
--project-directory "${CVAT_ROOT}"
-f "${CVAT_ROOT}/docker-compose.yml"
-f "${OVERRIDE_FILE}"
)
"${compose[@]}" config --quiet
mapfile -t images < <("${compose[@]}" config --images | sort -u)
for image in "${images[@]}"; do
guard_disk "before-pull:${image}"
docker pull "${image}"
guard_disk "after-pull:${image}"
done
"${compose[@]}" up -d --no-build
guard_disk cvat-started
deadline=$((SECONDS + 600))
until curl --fail --silent --show-error http://localhost:8080/api/server/about \
>/dev/null; do
if (( SECONDS >= deadline )); then
"${compose[@]}" ps
echo "CVAT did not become ready within 600 seconds" >&2
exit 4
fi
sleep 5
done
report="${RUNTIME_ROOT}/reports/deployment-$(date -u +%Y%m%dT%H%M%SZ).txt"
{
printf 'cvat_tag=%s\n' "${CVAT_TAG}"
printf 'cvat_commit=%s\n' "$(git -C "${CVAT_ROOT}" rev-parse HEAD)"
printf 'docker_version=%s\n' "$(docker version --format '{{.Server.Version}}')"
printf 'compose_version=%s\n' "$(docker compose version --short)"
printf 'free_gib=%s\n' "$(free_gib)"
printf 'generated_at_utc=%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
docker image inspect "${images[@]}" \
--format 'image={{index .RepoTags 0}} id={{.Id}} size={{.Size}}'
"${compose[@]}" ps
} | tee "${report}"
printf 'CVAT ready at http://localhost:8080\nreport=%s\n' "${report}"
@@ -0,0 +1,69 @@
#!/usr/bin/env bash
set -Eeuo pipefail
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
readonly FREE_GIB_FLOOR="${FREE_GIB_FLOOR:-360}"
readonly WINDOWS_ROOT="/mnt/d/NDC_MISSIONCORE"
readonly COMPUTE_ROOT="${WINDOWS_ROOT}/workspace/mission-core-compute"
readonly CVAT_ROOT="${WINDOWS_ROOT}/runtime/annotation/cvat/source/cvat-v2.70.0"
readonly OVERRIDE_FILE="${COMPUTE_ROOT}/cvat/docker-compose.override.yml"
if [[ "${EUID}" -ne 0 ]]; then
echo "start_cvat_runtime.sh must run as root" >&2
exit 2
fi
free_gib="$(df -BG --output=avail "${WINDOWS_ROOT}" | tail -n 1 | tr -dc '0-9')"
printf 'disk_guard stage=start free_gib=%s floor_gib=%s\n' \
"${free_gib}" "${FREE_GIB_FLOOR}"
if (( free_gib < FREE_GIB_FLOOR )); then
echo "D: free-space floor crossed; refusing to start CVAT" >&2
exit 3
fi
test -f "${CVAT_ROOT}/docker-compose.yml"
test -f "${OVERRIDE_FILE}"
systemctl start docker
export CVAT_VERSION="v2.70.0"
export CVAT_HOST="localhost"
export CVAT_HTTP_PORT="8080"
export COMPOSE_PROJECT_NAME="missioncore-cvat"
compose=(
docker compose
--project-directory "${CVAT_ROOT}"
-f "${CVAT_ROOT}/docker-compose.yml"
-f "${OVERRIDE_FILE}"
)
"${compose[@]}" up -d --no-build
deadline=$((SECONDS + 180))
while true; do
about_http="$(
curl --silent --output /dev/null --write-out '%{http_code}' \
http://localhost:8080/api/server/about || true
)"
tasks_http="$(
curl --silent --output /dev/null --write-out '%{http_code}' \
http://localhost:8080/api/tasks || true
)"
if [[ "${about_http}" == "200" ]] \
&& [[ "${tasks_http}" == "200" || "${tasks_http}" == "401" || "${tasks_http}" == "403" ]]; then
break
fi
if (( SECONDS >= deadline )); then
"${compose[@]}" ps
echo "CVAT did not become ready within 180 seconds" >&2
exit 4
fi
sleep 2
done
printf 'cvat_ready about_http=%s tasks_http=%s free_gib=%s\n' \
"${about_http}" "${tasks_http}" "${free_gib}"
if [[ "${1:-}" == "--keepalive" ]]; then
exec sleep infinity
fi