From 21c7a5507c8b00a7417b2de6c39f474a758d069d Mon Sep 17 00:00:00 2001 From: DCCONSTRUCTIONS Date: Sat, 25 Jul 2026 21:26:02 +0300 Subject: [PATCH] fix: distinguish admitted RELLIS previews from smoke --- .../src/core/lidar/datasetGateway.ts | 6 ++- .../workspaces/DatasetGatewayWorkspace.tsx | 3 ++ .../src/workspaces/RellisDatasetReview.tsx | 23 +++++++--- src/k1link/datasets/gateway.py | 44 +++++++++++++------ src/k1link/datasets/rellis_admission.py | 1 + src/k1link/datasets/rellis_smoke.py | 3 +- tests/test_dataset_gateway.py | 33 +++++++++++++- 7 files changed, 91 insertions(+), 22 deletions(-) diff --git a/apps/control-station/src/core/lidar/datasetGateway.ts b/apps/control-station/src/core/lidar/datasetGateway.ts index 65653d8..9ca599b 100644 --- a/apps/control-station/src/core/lidar/datasetGateway.ts +++ b/apps/control-station/src/core/lidar/datasetGateway.ts @@ -601,7 +601,7 @@ export function parseDatasetNativeScanPreview( if ( safety.visualization_only !== true || safety.navigation_or_safety_accepted !== false - || (isRellis && safety.compatibility_smoke_only !== true) + || (isRellis && typeof safety.compatibility_smoke_only !== "boolean") ) { throw new DatasetGatewayContractError("Dataset preview safety boundary нарушен"); } @@ -639,7 +639,9 @@ export function parseDatasetNativeScanPreview( evaluationMask, classes, coordinateFrame, - compatibilitySmokeOnly: isRellis, + compatibilitySmokeOnly: isRellis + ? safety.compatibility_smoke_only as boolean + : false, }; } diff --git a/apps/control-station/src/workspaces/DatasetGatewayWorkspace.tsx b/apps/control-station/src/workspaces/DatasetGatewayWorkspace.tsx index 8790bf2..3b3cb36 100644 --- a/apps/control-station/src/workspaces/DatasetGatewayWorkspace.tsx +++ b/apps/control-station/src/workspaces/DatasetGatewayWorkspace.tsx @@ -78,6 +78,9 @@ function sourceDescription(source: DatasetSource): string { if (source.sourceKind === "goose") { return "Восемь фрагментов и 961 разреженный размеченный оборот VLS-128. Здесь уже опубликовано покадровое сравнение текущего ground baseline и Patchwork++."; } + if (source.admissionStatus === "dataset-ready") { + return "Пять последовательностей Ouster OS1 64: 13 556 scans выровнены с labels, официальными split и pose index. Validation используется для независимой проверки переносимости алгоритмов."; + } return "Независимый off-road источник с Ouster OS1 64. Сейчас открыт официальный frame 000104 для проверки reader, осей, ontology и ground-policy; полный benchmark ещё не выполнялся."; } diff --git a/apps/control-station/src/workspaces/RellisDatasetReview.tsx b/apps/control-station/src/workspaces/RellisDatasetReview.tsx index bf1a45d..a890184 100644 --- a/apps/control-station/src/workspaces/RellisDatasetReview.tsx +++ b/apps/control-station/src/workspaces/RellisDatasetReview.tsx @@ -150,12 +150,22 @@ function RellisSmokeReview() { >
- RELLIS S0 · COMPATIBILITY SMOKE -

Официальный Ouster scan читается без конвертации формата

+ + {preview.compatibilitySmokeOnly + ? "RELLIS S0 · COMPATIBILITY SMOKE" + : "RELLIS · ADMITTED VALIDATION SCAN"} + +

+ {preview.compatibilitySmokeOnly + ? "Официальный Ouster scan читается без конвертации формата" + : "Полный официальный release принят на worker D"} +

Frame {preview.frameId}: исходные XYZI и point-wise labels остались - выровнены. Это проверка reader, осей, цветов и ground-policy — ещё не - сравнение алгоритмов и не основание для навигации. + выровнены. {preview.compatibilitySmokeOnly + ? "Это проверка reader, осей, цветов и ground-policy — ещё не сравнение алгоритмов." + : "Scan входит в официальный validation split; результат алгоритмов публикуется отдельным квалификационным run."} + {" "}Это не основание для навигации.

@@ -177,7 +187,10 @@ function RellisSmokeReview() {
- OFFICIAL EXAMPLE · FRAME 000104 + + {preview.compatibilitySmokeOnly ? "OFFICIAL EXAMPLE" : "VALIDATION"} + {" · FRAME "}{preview.frameId} + Ouster OS1 64 · X forward · Y left · Z up
diff --git a/src/k1link/datasets/gateway.py b/src/k1link/datasets/gateway.py index 08a48e9..3d2911e 100644 --- a/src/k1link/datasets/gateway.py +++ b/src/k1link/datasets/gateway.py @@ -624,7 +624,10 @@ def read_dataset_native_scan_preview(path: Path) -> dict[str, Any]: "navigation_or_safety_accepted": False, } if identity[1] == "rellis-3d/v1.1": - expected_safety["compatibility_smoke_only"] = True + compatibility_smoke_only = safety.get("compatibility_smoke_only") + if not isinstance(compatibility_smoke_only, bool): + raise DatasetAdmissionError("RELLIS preview scope is incompatible") + expected_safety["compatibility_smoke_only"] = compatibility_smoke_only if safety != expected_safety: raise DatasetAdmissionError("dataset preview safety boundary is incompatible") if not isinstance(document.get("frame_id"), str) or not document["frame_id"]: @@ -670,9 +673,9 @@ def _validate_rellis_preview_metadata( ): raise DatasetAdmissionError("RELLIS class catalog is incompatible") evidence = _object(document.get("source_evidence"), "source_evidence") - if ( + common_evidence_valid = ( set(evidence) - != { + == { "repository_url", "repository_commit", "label_config_sha256", @@ -680,16 +683,31 @@ def _validate_rellis_preview_metadata( "label_sha256", "license", } - or evidence.get("repository_url") != "https://github.com/unmannedlab/RELLIS-3D" - or evidence.get("repository_commit") - != "c17a118fcaed1559f03cc32cc3a91dedc557f8b8" - or evidence.get("label_config_sha256") - != "573379a232ac561805987466c391a61fd5ad338be7fb9c4c2842f3f28067e0ad" - or evidence.get("point_sha256") - != "ed81a9c3636d55b17d78058c72545d5d22419beecf174d50596d23ae178752af" - or evidence.get("label_sha256") - != "9b8c65b710873e931af4ac6dfc7d3dd2298696514ab721e50300bd55ad5b634e" - or evidence.get("license") != "CC-BY-NC-SA-3.0" + and evidence.get("repository_url") == "https://github.com/unmannedlab/RELLIS-3D" + and evidence.get("repository_commit") + == "c17a118fcaed1559f03cc32cc3a91dedc557f8b8" + and evidence.get("label_config_sha256") + == "573379a232ac561805987466c391a61fd5ad338be7fb9c4c2842f3f28067e0ad" + and evidence.get("license") == "CC-BY-NC-SA-3.0" + ) + digest_values = (evidence.get("point_sha256"), evidence.get("label_sha256")) + digests_valid = all( + isinstance(value, str) + and len(value) == 64 + and all(character in "0123456789abcdef" for character in value) + for value in digest_values + ) + smoke_only = document["safety"]["compatibility_smoke_only"] + smoke_digests_valid = ( + evidence.get("point_sha256") + == "ed81a9c3636d55b17d78058c72545d5d22419beecf174d50596d23ae178752af" + and evidence.get("label_sha256") + == "9b8c65b710873e931af4ac6dfc7d3dd2298696514ab721e50300bd55ad5b634e" + ) + if ( + not common_evidence_valid + or not digests_valid + or (smoke_only and not smoke_digests_valid) ): raise DatasetAdmissionError("RELLIS source evidence is incompatible") diff --git a/src/k1link/datasets/rellis_admission.py b/src/k1link/datasets/rellis_admission.py index 1f8ba9b..23ad1f0 100644 --- a/src/k1link/datasets/rellis_admission.py +++ b/src/k1link/datasets/rellis_admission.py @@ -195,6 +195,7 @@ def admit_rellis_release( "label_sha256": hashlib.sha256(label_bytes).hexdigest(), "license": RELLIS_LICENSE, }, + compatibility_smoke_only=False, ) except (OSError, KeyError, zipfile.BadZipFile, DatasetFrameError, RellisSmokeError) as exc: raise RellisAdmissionError("RELLIS archives could not be verified") from exc diff --git a/src/k1link/datasets/rellis_smoke.py b/src/k1link/datasets/rellis_smoke.py index 78cf4e2..d3bf960 100644 --- a/src/k1link/datasets/rellis_smoke.py +++ b/src/k1link/datasets/rellis_smoke.py @@ -143,6 +143,7 @@ def rellis_native_scan_preview( frame_id: str, maximum_points: int, source_evidence: dict[str, str], + compatibility_smoke_only: bool = True, ) -> dict[str, Any]: """Build the common viewer artifact from a validated RELLIS native frame.""" @@ -243,7 +244,7 @@ def rellis_native_scan_preview( "source_evidence": source_evidence, "safety": { "visualization_only": True, - "compatibility_smoke_only": True, + "compatibility_smoke_only": compatibility_smoke_only, "navigation_or_safety_accepted": False, }, } diff --git a/tests/test_dataset_gateway.py b/tests/test_dataset_gateway.py index c4e5af8..be232e4 100644 --- a/tests/test_dataset_gateway.py +++ b/tests/test_dataset_gateway.py @@ -10,9 +10,9 @@ from fastapi import APIRouter from fastapi.routing import APIRoute from k1link.datasets import ( + RELLIS_SOURCE_ID, DatasetAdmissionError, DatasetFrameError, - RELLIS_SOURCE_ID, dataset_gateway_catalog, goose_admission, goose_benchmark, @@ -355,6 +355,37 @@ def test_rellis_smoke_preview_keeps_semantickitti_alignment_and_ignore_policy( rellis = catalog["sources"][1] # type: ignore[index] assert rellis["admission"]["status"] == "smoke-ready" # type: ignore[index] + admission_path = tmp_path / "rellis-admission.json" + admission_path.write_text( + json.dumps( + { + "schema_version": "missioncore.rellis-admission/v1", + "source_id": "rellis-3d/v1.1", + "status": "dataset-ready", + "storage": { + "policy": "worker-d-only", + "admitted": True, + "path_exposed": False, + }, + "alignment": { + "annotated_scan_count": 13_556, + "point_label_alignment": "complete", + "pose_index_coverage": "complete", + }, + "release": {"identity_sha256": "a" * 64}, + } + ), + encoding="utf-8", + ) + admitted = dataset_gateway_catalog( + Path("/mnt/d/NDC_MISSIONCORE/datasets"), + rellis_preview_path=preview_path, + rellis_admission_path=admission_path, + ) + admitted_rellis = admitted["sources"][1] # type: ignore[index] + assert admitted_rellis["admission"]["status"] == "dataset-ready" # type: ignore[index] + assert admitted["next_action"] == "qualify-rellis-current-vs-patchworkpp" + def test_goose_current_ground_is_scored_against_independent_labels( tmp_path: Path,