x4d-devkit 0.45.0rc1__tar.gz → 0.46.0rc2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. {x4d_devkit-0.45.0rc1/x4d_devkit.egg-info → x4d_devkit-0.46.0rc2}/PKG-INFO +1 -1
  2. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/pyproject.toml +1 -1
  3. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_clean_basemap.py +132 -223
  4. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_clean_basemap_client.py +3 -3
  5. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_sdk_api.py +1 -1
  6. x4d_devkit-0.46.0rc2/tests/test_semantic_scene_independent.py +138 -0
  7. x4d_devkit-0.46.0rc2/tests/test_work_session_validation_reuse.py +109 -0
  8. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/clean_basemap.py +57 -357
  9. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/semantic_scene_authoring.py +235 -94
  10. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/semantic_scene_contract.py +389 -91
  11. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/work_session.py +85 -1
  12. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2/x4d_devkit.egg-info}/PKG-INFO +1 -1
  13. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit.egg-info/SOURCES.txt +2 -1
  14. x4d_devkit-0.45.0rc1/tests/test_clean_basemap_preannotation.py +0 -96
  15. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/LICENSE +0 -0
  16. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/README.md +0 -0
  17. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/setup.cfg +0 -0
  18. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_annotation_contract.py +0 -0
  19. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_annotation_reference.py +0 -0
  20. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_assets_client.py +0 -0
  21. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_boundary.py +0 -0
  22. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_box.py +0 -0
  23. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_build_info.py +0 -0
  24. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_cli_auth.py +0 -0
  25. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_client_json_contract.py +0 -0
  26. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_client_projects.py +0 -0
  27. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_clip_archives.py +0 -0
  28. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_contract_validation.py +0 -0
  29. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_coordinate_frames.py +0 -0
  30. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_current_state.py +0 -0
  31. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_current_state_identity_consumers_example.py +0 -0
  32. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_dataset_contract.py +0 -0
  33. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_dataset_identity.py +0 -0
  34. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_detection_eval.py +0 -0
  35. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_evaluate.py +0 -0
  36. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_export_bundle.py +0 -0
  37. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_image_review.py +0 -0
  38. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_inference_input.py +0 -0
  39. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_inference_schemas.py +0 -0
  40. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_inference_service.py +0 -0
  41. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_label_schemas.py +0 -0
  42. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_lidar_materialization.py +0 -0
  43. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_loader.py +0 -0
  44. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_matching.py +0 -0
  45. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_metrics.py +0 -0
  46. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_models.py +0 -0
  47. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_platform_pose.py +0 -0
  48. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_preannotation_client.py +0 -0
  49. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_reconstruction_client.py +0 -0
  50. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_release_gate.py +0 -0
  51. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_roadmark_preview.py +0 -0
  52. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_semantic_scene_authoring.py +0 -0
  53. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_semantic_scene_client.py +0 -0
  54. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_semantic_scene_contract.py +0 -0
  55. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_semantic_scene_native_clip.py +0 -0
  56. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_source_attributes.py +0 -0
  57. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_time_contract.py +0 -0
  58. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_token.py +0 -0
  59. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_tracking_eval.py +0 -0
  60. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_training_identity.py +0 -0
  61. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_transform.py +0 -0
  62. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_transform_chain.py +0 -0
  63. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_validation.py +0 -0
  64. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_vector_map.py +0 -0
  65. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_vector_map_geometry.py +0 -0
  66. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_vector_map_session.py +0 -0
  67. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/tests/test_work_session.py +0 -0
  68. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/__init__.py +0 -0
  69. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/annotation_contract.py +0 -0
  70. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/annotation_reference.py +0 -0
  71. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/build_info.py +0 -0
  72. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/cli.py +0 -0
  73. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/client/__init__.py +0 -0
  74. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/client/_origin.py +0 -0
  75. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/client/annotation_reference.py +0 -0
  76. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/client/assets.py +0 -0
  77. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/client/bags.py +0 -0
  78. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/client/capabilities.py +0 -0
  79. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/client/checkpoints.py +0 -0
  80. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/client/clean_basemap.py +0 -0
  81. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/client/client.py +0 -0
  82. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/client/clip_work_sessions.py +0 -0
  83. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/client/clips.py +0 -0
  84. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/client/dataset_identity.py +0 -0
  85. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/client/image_review.py +0 -0
  86. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/client/label_schemas.py +0 -0
  87. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/client/preannotation.py +0 -0
  88. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/client/projects.py +0 -0
  89. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/client/reconstruction.py +0 -0
  90. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/client/semantic_scene.py +0 -0
  91. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/client/semantic_scene_authoring.py +0 -0
  92. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/client/services.py +0 -0
  93. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/client/test_sets.py +0 -0
  94. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/consumer_identity.py +0 -0
  95. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/coordinate_contract.py +0 -0
  96. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/core/__init__.py +0 -0
  97. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/core/loader.py +0 -0
  98. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/core/models.py +0 -0
  99. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/core/token.py +0 -0
  100. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/core/transform.py +0 -0
  101. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/current_state.py +0 -0
  102. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/dataset_contract.py +0 -0
  103. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/dataset_identity.py +0 -0
  104. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/eval/__init__.py +0 -0
  105. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/eval/api.py +0 -0
  106. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/eval/box.py +0 -0
  107. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/eval/detection.py +0 -0
  108. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/eval/matching.py +0 -0
  109. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/eval/metrics.py +0 -0
  110. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/eval/tracking.py +0 -0
  111. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/export_bundle.py +0 -0
  112. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/image_review.py +0 -0
  113. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/inference/__init__.py +0 -0
  114. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/inference/input.py +0 -0
  115. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/inference/models.py +0 -0
  116. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/inference/schemas.py +0 -0
  117. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/inference/service.py +0 -0
  118. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/inference/tasks.py +0 -0
  119. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/json_contract.py +0 -0
  120. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/label_schema.py +0 -0
  121. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/lidar_materialization.py +0 -0
  122. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/materialize.py +0 -0
  123. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/roadmark_preview.py +0 -0
  124. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/time_contract.py +0 -0
  125. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/training_identity.py +0 -0
  126. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/validation/__init__.py +0 -0
  127. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/validation/contract.py +0 -0
  128. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/validation/platform_pose.py +0 -0
  129. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/validation/report.py +0 -0
  130. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/validation/validator.py +0 -0
  131. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/vector_map.py +0 -0
  132. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit/vector_map_geometry.py +0 -0
  133. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit.egg-info/dependency_links.txt +0 -0
  134. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit.egg-info/entry_points.txt +0 -0
  135. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit.egg-info/requires.txt +0 -0
  136. {x4d_devkit-0.45.0rc1 → x4d_devkit-0.46.0rc2}/x4d_devkit.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: x4d-devkit
3
- Version: 0.45.0rc1
3
+ Version: 0.46.0rc2
4
4
  Summary: Official X-4D external SDK and CLI for datasets, platform APIs, training bridges, and automation
5
5
  Author: windzu
6
6
  License-Expression: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "x4d-devkit"
7
- version = "0.45.0rc1"
7
+ version = "0.46.0rc2"
8
8
  description = "Official X-4D external SDK and CLI for datasets, platform APIs, training bridges, and automation"
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -5,7 +5,6 @@ import pytest
5
5
  import zstandard
6
6
 
7
7
  from tests.semantic_scene_contract_fixtures import current_semantic_scene_component
8
- from x4d_devkit.annotation_contract import build_clip_annotations
9
8
  from x4d_devkit.clean_basemap import (
10
9
  CLEAN_BASEMAP_CONTRACT_VERSION,
11
10
  CLEAN_BASEMAP_INPUT_FIELDS,
@@ -22,15 +21,12 @@ from x4d_devkit.clean_basemap import (
22
21
  encode_clean_basemap_decisions,
23
22
  encode_clean_basemap_mapping,
24
23
  parse_clean_basemap_manifest,
25
- validate_clean_basemap_annotation,
26
24
  validate_clean_basemap_native_binding,
27
25
  validate_clean_basemap_resource,
28
- compute_clean_basemap_preannotation_revision,
29
26
  )
30
27
  from x4d_devkit.semantic_scene_contract import (
31
- compute_annotation_dependency_revisions,
32
28
  decode_scene_geometry,
33
- encode_scene_geometry,
29
+ empty_semantic_scene_instance_index,
34
30
  sha256_bytes,
35
31
  )
36
32
 
@@ -39,72 +35,42 @@ def fixture():
39
35
  native = current_semantic_scene_component()
40
36
  scene = clean_basemap_scene_source(native["scene_index"])
41
37
  first = scene["chunks"][0]
42
- geometry = decode_scene_geometry(
43
- native["scene_geometry_payloads"][first["geometry"]],
44
- point_count=3,
45
- expected_payload_sha256=first["geometry_sha256"],
46
- expected_raw_sha256=first["geometry_raw_sha256"],
47
- )
48
- xyz = np.vstack([geometry.xyz, [9.5, 0, 0]]).astype(np.float32)
49
- rgb = np.vstack([geometry.rgb, [100, 110, 120]]).astype(np.uint8)
50
- encoded = encode_scene_geometry(xyz, rgb)
38
+ payload = native["scene_geometry_payloads"][first["geometry"]]
51
39
  descriptor = {
52
- "path": f"generated/reconstruction/clean_basemap/geometry/{encoded.payload_sha256[7:]}.spc.zst",
53
- "sha256": encoded.payload_sha256,
54
- "raw_sha256": encoded.raw_sha256,
55
- "size_bytes": len(encoded.payload),
40
+ "path": f"generated/reconstruction/clean_basemap/geometry/{first['geometry_sha256'][7:]}.spc.zst",
41
+ "sha256": first["geometry_sha256"],
42
+ "raw_sha256": first["geometry_raw_sha256"],
43
+ "size_bytes": len(payload),
56
44
  }
57
- decisions = [1, 0, 0, 2, 17]
45
+ decisions = [1, 0, 3, 18, 33]
58
46
  source_hash = "sha256:" + "a" * 64
59
47
  decision_payload = encode_clean_basemap_decisions(
60
48
  asset_sha256=source_hash, decisions=decisions
61
49
  )
62
50
  mapping = encode_clean_basemap_mapping(
63
- geometry_raw_sha256=encoded.raw_sha256,
64
- sweep_indices=[0, 0, 0, 0],
65
- raw_point_ordinals=[0, 1, 2, 3],
66
- timestamps_ns=[100, 100, 100, 100],
67
- owner_source_indices=[0, 0, 0, 1],
68
- owner_point_ordinals=[0, 1, 2, 0],
69
- decisions=decisions[:4],
51
+ geometry_raw_sha256=first["geometry_raw_sha256"],
52
+ sweep_indices=[0, 0, 0],
53
+ raw_point_ordinals=[0, 1, 2],
54
+ timestamps_ns=[100, 100, 100],
55
+ decisions=decisions[:3],
70
56
  )
71
- instance_sources = [
72
- {
73
- key: row[key]
74
- for key in (
75
- "instance_token",
76
- "point_count",
77
- "geometry",
78
- "geometry_sha256",
79
- "geometry_raw_sha256",
80
- )
81
- }
82
- | {"motion": "static"}
83
- for row in native["instance_index"]["instances"]
84
- ]
85
57
  profile = {
86
- "algorithm": {"id": "clean_static_basemap", "version": "1.0.0"},
58
+ "algorithm": {"id": "clean_static_basemap", "version": "0.6.0"},
87
59
  "voxel_size_m": 0.03,
88
60
  "min_range_m": 2.0,
89
61
  "max_range_m": 60.0,
90
62
  "lidar_channels": ["scanner-a"],
91
63
  "unknown_policy": "retain",
92
64
  "point_deskew": True,
93
- "foreground_policy": "joint_temporal_box_ground_v1",
65
+ "foreground_policy": "model_motion_ground_v1",
94
66
  "occlusion_policy": "original_observations",
95
67
  "policies": {
96
68
  name: {"id": name, "version": "1.0.0", "parameters": {}}
97
- for name in ("geometry", "ground", "motion", "rgb")
69
+ for name in ("geometry", "ground", "rgb")
98
70
  },
99
71
  }
100
72
  inputs = {key: "sha256:" + "b" * 64 for key in CLEAN_BASEMAP_INPUT_FIELDS}
101
- inputs["preannotation_revision"] = compute_clean_basemap_preannotation_revision(
102
- None
103
- )
104
- dependencies = compute_annotation_dependency_revisions(native["annotation"])
105
- for key in inputs:
106
- if key in dependencies:
107
- inputs[key] = dependencies[key]
73
+ bounds = [first["bounds_min_m"], first["bounds_max_m"]]
108
74
  manifest = {
109
75
  "contract_version": CLEAN_BASEMAP_CONTRACT_VERSION,
110
76
  "clip_id": native["annotation"]["clip_id"],
@@ -114,27 +80,25 @@ def fixture():
114
80
  inputs, profile
115
81
  ),
116
82
  "input_identity": inputs,
117
- "preannotation_evidence": None,
118
83
  "profile": profile,
119
- "point_count": 4,
120
- "bounds": [xyz.min(0).tolist(), xyz.max(0).tolist()],
84
+ "point_count": 3,
85
+ "bounds": bounds,
121
86
  "scene_source": scene,
122
- "instance_sources": instance_sources,
123
87
  "sweep_sources": [
124
88
  {
125
89
  "sample_data_token": "sweep-0",
126
90
  "channel": "scanner-a",
127
91
  "point_count": 5,
128
92
  "asset_sha256": source_hash,
129
- "time_bounds": [{"sec": 0, "nanosec": 100}, {"sec": 0, "nanosec": 100}],
93
+ "time_bounds": [{"sec": 0, "nanosec": 100}] * 2,
130
94
  "decisions": decision_payload.descriptor("decisions"),
131
95
  }
132
96
  ],
133
97
  "chunks": [
134
98
  {
135
99
  "ordinal": 0,
136
- "point_count": 4,
137
- "bounds": [xyz.min(0).tolist(), xyz.max(0).tolist()],
100
+ "point_count": 3,
101
+ "bounds": bounds,
138
102
  "geometry": descriptor,
139
103
  "mapping": mapping.descriptor("mapping"),
140
104
  }
@@ -147,10 +111,10 @@ def fixture():
147
111
  },
148
112
  }
149
113
  manifest["basemap_revision"] = compute_clean_basemap_revision(manifest)
150
- assets = dict(native["scene_geometry_payloads"], **native["instance_payloads"])
114
+ assets = dict(native["scene_geometry_payloads"])
151
115
  assets.update(
152
116
  {
153
- descriptor["path"]: encoded.payload,
117
+ descriptor["path"]: payload,
154
118
  mapping.descriptor("mapping")["path"]: mapping.payload,
155
119
  decision_payload.descriptor("decisions")["path"]: decision_payload.payload,
156
120
  }
@@ -158,19 +122,22 @@ def fixture():
158
122
  return manifest, assets, native
159
123
 
160
124
 
161
- def validate(manifest, assets):
125
+ def original_points(manifest, assets):
162
126
  geometry = manifest["chunks"][0]["geometry"]
163
- original_xyz = decode_scene_geometry(
127
+ return decode_scene_geometry(
164
128
  assets[geometry["path"]],
165
- point_count=4,
129
+ point_count=3,
166
130
  expected_payload_sha256=geometry["sha256"],
167
131
  expected_raw_sha256=geometry["raw_sha256"],
168
132
  ).xyz
133
+
134
+
135
+ def validate(manifest, assets):
136
+ original = original_points(manifest, assets)
169
137
  return validate_clean_basemap_resource(
170
138
  manifest,
171
139
  read_asset=assets.__getitem__,
172
- observation_to_world=lambda sweep, ordinals, times: original_xyz[ordinals],
173
- instance_to_world=lambda token, times, xyz: xyz + [10, 0, 0],
140
+ observation_to_world=lambda sweep, ordinals, times: original[ordinals],
174
141
  )
175
142
 
176
143
 
@@ -180,52 +147,56 @@ def remap(manifest, assets, **overrides):
180
147
  assets[chunk["mapping"]["path"]],
181
148
  descriptor=chunk["mapping"],
182
149
  geometry_raw_sha256=chunk["geometry"]["raw_sha256"],
183
- point_count=chunk["point_count"],
150
+ point_count=3,
184
151
  )
185
152
  arguments = {
186
153
  "sweep_indices": mapping["sweep_index"],
187
154
  "raw_point_ordinals": mapping["raw_point_ordinal"],
188
155
  "timestamps_ns": mapping["timestamp_ns"],
189
- "owner_source_indices": mapping["owner_source_index"],
190
- "owner_point_ordinals": mapping["owner_point_ordinal"],
191
156
  "decisions": mapping["decision"],
192
157
  }
193
- payload = encode_clean_basemap_mapping(
158
+ encoded = encode_clean_basemap_mapping(
194
159
  geometry_raw_sha256=chunk["geometry"]["raw_sha256"], **(arguments | overrides)
195
160
  )
196
- chunk["mapping"] = payload.descriptor("mapping")
197
- assets[chunk["mapping"]["path"]] = payload.payload
161
+ chunk["mapping"] = encoded.descriptor("mapping")
162
+ assets[chunk["mapping"]["path"]] = encoded.payload
198
163
  manifest["basemap_revision"] = compute_clean_basemap_revision(manifest)
199
164
 
200
165
 
201
- def test_complete_geometry_native_ownership_and_original_observation_roundtrip():
166
+ def test_complete_scene_and_exact_original_observation_roundtrip():
202
167
  manifest, assets, native = fixture()
203
168
  parsed = validate(manifest, assets)
204
169
  assert parsed.to_dict() == manifest
205
- validate_clean_basemap_annotation(parsed, native["annotation"])
206
170
  validate_clean_basemap_native_binding(
207
171
  parsed,
208
172
  scene_index=native["scene_index"],
209
- instance_index=native["instance_index"],
173
+ instance_index=empty_semantic_scene_instance_index(),
174
+ )
175
+ assert set(parsed.data["input_identity"]) == {
176
+ "samples_revision",
177
+ "sweeps_revision",
178
+ "calibration_revision",
179
+ "pose_revision",
180
+ }
181
+ assert (
182
+ "instance_sources" not in parsed.data
183
+ and "preannotation_evidence" not in parsed.data
210
184
  )
211
- manifest["chunks"][0]["point_count"] = 9
212
- assert parsed.data["chunks"][0]["point_count"] == 4
185
+ assert "owner_source_index" not in MAPPING_DTYPE.names
186
+ manifest["point_count"] = 9
187
+ assert parsed.data["point_count"] == 3
213
188
  with pytest.raises(TypeError):
214
189
  parsed.data["profile"]["voxel_size_m"] = 1
215
190
 
216
191
 
217
192
  @pytest.mark.parametrize(
218
- "overrides, message",
193
+ "overrides,message",
219
194
  [
220
- ({"sweep_indices": [1, 0, 0, 0]}, "source index"),
221
- ({"raw_point_ordinals": [5, 1, 2, 3]}, "original point ordinal"),
222
- ({"raw_point_ordinals": [0, 0, 2, 3]}, "duplicate original observation"),
223
- ({"raw_point_ordinals": [0, 1, 2, 4]}, "decision"),
224
- ({"timestamps_ns": [101, 100, 100, 100]}, "exact point time"),
225
- ({"owner_source_indices": [9, 0, 0, 1]}, "source index"),
226
- ({"owner_point_ordinals": [0, 1, 3, 0]}, "native point ordinal"),
227
- ({"owner_point_ordinals": [0, 1, 1, 0]}, "duplicate native scene"),
228
- ({"owner_source_indices": [0, 0, 1, 1]}, "instance correspondence"),
195
+ ({"sweep_indices": [1, 0, 0]}, "source index"),
196
+ ({"raw_point_ordinals": [5, 1, 2]}, "original point ordinal"),
197
+ ({"raw_point_ordinals": [0, 0, 2]}, "duplicate original observation"),
198
+ ({"raw_point_ordinals": [0, 1, 3]}, "decision"),
199
+ ({"timestamps_ns": [101, 100, 100]}, "exact point time"),
229
200
  ],
230
201
  )
231
202
  def test_valid_hash_cannot_hide_wrong_addresses_or_removed_sources(overrides, message):
@@ -235,77 +206,65 @@ def test_valid_hash_cannot_hide_wrong_addresses_or_removed_sources(overrides, me
235
206
  validate(manifest, assets)
236
207
 
237
208
 
238
- def test_dynamic_instance_support_is_not_static_display_evidence():
239
- manifest, assets, _ = fixture()
240
- manifest["instance_sources"][0]["motion"] = "dynamic"
241
- manifest["basemap_revision"] = compute_clean_basemap_revision(manifest)
242
- with pytest.raises(ValueError, match="dynamic instance"):
243
- validate(manifest, assets)
244
-
245
-
246
- @pytest.mark.parametrize("result", [np.zeros((2, 3)), np.full((1, 3), np.nan)])
247
- def test_instance_transform_rejects_incomplete_or_nonfinite_results(result):
209
+ @pytest.mark.parametrize("kind", ["fabricated", "incomplete", "nonfinite"])
210
+ def test_original_observation_projection_is_required(kind):
248
211
  manifest, assets, _ = fixture()
249
- descriptor = manifest["chunks"][0]["geometry"]
250
- original_xyz = decode_scene_geometry(
251
- assets[descriptor["path"]],
252
- point_count=4,
253
- expected_payload_sha256=descriptor["sha256"],
254
- expected_raw_sha256=descriptor["raw_sha256"],
255
- ).xyz
256
- with pytest.raises(ValueError, match="pinned time-aligned track"):
212
+ result = {
213
+ "fabricated": np.full((3, 3), 999.0),
214
+ "incomplete": np.zeros((2, 3)),
215
+ "nonfinite": np.full((3, 3), np.nan),
216
+ }[kind]
217
+ with pytest.raises(
218
+ ValueError, match="original observations|incomplete or nonfinite"
219
+ ):
257
220
  validate_clean_basemap_resource(
258
221
  manifest,
259
222
  read_asset=assets.__getitem__,
260
- observation_to_world=lambda sweep, ordinals, times: original_xyz[ordinals],
261
- instance_to_world=lambda token, times, xyz: result,
223
+ observation_to_world=lambda *args: result,
262
224
  )
263
225
 
264
226
 
265
- def test_native_geometry_agreement_cannot_hide_fabricated_original_observations():
266
- manifest, assets, _ = fixture()
267
- with pytest.raises(ValueError, match="differs from its original observations"):
268
- validate_clean_basemap_resource(
269
- manifest,
270
- read_asset=assets.__getitem__,
271
- observation_to_world=lambda sweep, ordinals, times: np.full(
272
- (len(ordinals), 3), 999.0
273
- ),
274
- instance_to_world=lambda token, times, xyz: xyz + [10, 0, 0],
275
- )
227
+ def test_native_payload_is_verified_even_with_correct_manifest_binding():
228
+ manifest, assets, native = fixture()
229
+ assets[native["scene_index"]["chunks"][0]["geometry"]] = b"tampered native geometry"
230
+ with pytest.raises(ValueError, match="byte-exact"):
231
+ validate(manifest, assets)
276
232
 
277
233
 
278
- def test_model_removal_is_accounted_and_cannot_be_mapped():
279
- manifest, assets, _ = fixture()
280
- sweep = manifest["sweep_sources"][0]
281
- carrier = encode_clean_basemap_decisions(
282
- asset_sha256=sweep["asset_sha256"], decisions=[1, 0, 0, 2, REMOVE_MODEL_DYNAMIC]
234
+ @pytest.mark.parametrize("key", sorted(CLEAN_BASEMAP_INPUT_FIELDS))
235
+ def test_actual_source_or_pose_change_changes_dependency(key):
236
+ manifest, _, _ = fixture()
237
+ inputs = dict(manifest["input_identity"], **{key: "sha256:" + "f" * 64})
238
+ assert (
239
+ compute_clean_basemap_dependency_identity(inputs, manifest["profile"])
240
+ != manifest["dependency_identity"]
283
241
  )
284
- sweep["decisions"] = carrier.descriptor("decisions")
285
- assets[sweep["decisions"]["path"]] = carrier.payload
286
- manifest["diagnostics"]["decision_counts"].update({"17": 0, "18": 1})
287
- manifest["basemap_revision"] = compute_clean_basemap_revision(manifest)
288
- validate(manifest, assets)
289
- remap(manifest, assets, raw_point_ordinals=[0, 1, 2, 4])
242
+
243
+
244
+ @pytest.mark.parametrize("code", [2, 16, 17])
245
+ def test_retired_box_and_geometry_decisions_are_rejected(code):
290
246
  with pytest.raises(ValueError, match="decision"):
291
- validate(manifest, assets)
247
+ encode_clean_basemap_decisions(
248
+ asset_sha256="sha256:" + "a" * 64, decisions=[code]
249
+ )
292
250
 
293
251
 
294
252
  def test_previous_contract_and_decision_bytes_are_rejected():
295
253
  manifest, _, _ = fixture()
296
- manifest["contract_version"] = "x4d-clean-static-basemap-v1"
254
+ manifest["contract_version"] = "x4d-clean-static-basemap-v3"
297
255
  with pytest.raises(ValueError, match="contract"):
298
256
  parse_clean_basemap_manifest(manifest)
299
257
  carrier = encode_clean_basemap_decisions(
300
258
  asset_sha256="sha256:" + "a" * 64, decisions=[REMOVE_MODEL_DYNAMIC]
301
259
  )
302
260
  raw = zstandard.ZstdDecompressor().decompress(carrier.payload)
303
- old = zstandard.ZstdCompressor().compress(b"X4DCBD1\0" + raw[8:])
261
+ old_raw = b"X4DCBD2\0" + raw[8:]
262
+ old = zstandard.ZstdCompressor().compress(old_raw)
304
263
  descriptor = carrier.descriptor("decisions")
305
264
  descriptor.update(
306
265
  path=f"generated/reconstruction/clean_basemap/decisions/{sha256_bytes(old)[7:]}.zst",
307
266
  sha256=sha256_bytes(old),
308
- raw_sha256=sha256_bytes(b"X4DCBD1\0" + raw[8:]),
267
+ raw_sha256=sha256_bytes(old_raw),
309
268
  size_bytes=len(old),
310
269
  )
311
270
  with pytest.raises(ValueError, match="bound to different"):
@@ -315,29 +274,23 @@ def test_previous_contract_and_decision_bytes_are_rejected():
315
274
 
316
275
 
317
276
  @pytest.mark.parametrize("family", ["decisions", "mapping"])
318
- def test_every_byte_in_the_last_record_obeys_its_exact_decision_domain(family):
319
- count = 4096
320
- anchor = "sha256:" + "a" * 64
277
+ def test_every_byte_in_last_record_obeys_exact_decision_domain(family):
278
+ count, anchor = 4096, "sha256:" + "a" * 64
321
279
  if family == "decisions":
322
280
  carrier = encode_clean_basemap_decisions(
323
281
  asset_sha256=anchor, decisions=np.full(count, KEEP_GROUND)
324
282
  )
325
- domain = DECISION_CODES
326
- decode = decode_clean_basemap_decisions
327
- arguments = dict(asset_sha256=anchor, point_count=count)
328
- byte_offset = -1
283
+ domain, decode = DECISION_CODES, decode_clean_basemap_decisions
284
+ arguments, byte_offset = dict(asset_sha256=anchor, point_count=count), -1
329
285
  else:
330
286
  carrier = encode_clean_basemap_mapping(
331
287
  geometry_raw_sha256=anchor,
332
288
  sweep_indices=np.zeros(count, dtype=np.uint32),
333
289
  raw_point_ordinals=np.arange(count),
334
290
  timestamps_ns=np.full(count, 100),
335
- owner_source_indices=np.zeros(count, dtype=np.uint32),
336
- owner_point_ordinals=np.arange(count),
337
291
  decisions=np.full(count, KEEP_GROUND),
338
292
  )
339
- domain = KEEP_CODES
340
- decode = decode_clean_basemap_mapping
293
+ domain, decode = KEEP_CODES, decode_clean_basemap_mapping
341
294
  arguments = dict(geometry_raw_sha256=anchor, point_count=count)
342
295
  byte_offset = -MAPPING_DTYPE.itemsize + MAPPING_DTYPE.fields["decision"][1]
343
296
  raw = bytearray(zstandard.ZstdDecompressor().decompress(carrier.payload))
@@ -353,87 +306,30 @@ def test_every_byte_in_the_last_record_obeys_its_exact_decision_domain(family):
353
306
  )
354
307
  if code in domain:
355
308
  values = decode(payload, descriptor=descriptor, **arguments)
356
- decisions = values if family == "decisions" else values["decision"]
357
- assert decisions[-1] == code
358
- assert np.all(decisions[:-1] == KEEP_GROUND)
309
+ assert (values if family == "decisions" else values["decision"])[-1] == code
359
310
  else:
360
- message = "decision" if family == "decisions" else "mapping records"
361
- with pytest.raises(ValueError, match=message):
311
+ with pytest.raises(ValueError):
362
312
  decode(payload, descriptor=descriptor, **arguments)
363
313
 
364
314
 
365
- def test_pose_coverage_rejections_are_accounted_and_cannot_be_mapped():
366
- manifest, assets, _ = fixture()
367
- sweep = manifest["sweep_sources"][0]
368
- carrier = encode_clean_basemap_decisions(
369
- asset_sha256=sweep["asset_sha256"], decisions=[1, 0, 0, 2, 34]
370
- )
371
- sweep["decisions"] = carrier.descriptor("decisions")
372
- assets[sweep["decisions"]["path"]] = carrier.payload
373
- manifest["diagnostics"]["decision_counts"].update({"17": 0, "34": 1})
374
- manifest["basemap_revision"] = compute_clean_basemap_revision(manifest)
375
- assert decode_clean_basemap_decisions(
376
- carrier.payload,
377
- descriptor=sweep["decisions"],
378
- asset_sha256=sweep["asset_sha256"],
379
- point_count=5,
380
- ).tolist() == [1, 0, 0, 2, 34]
381
- validate(manifest, assets)
382
- remap(manifest, assets, raw_point_ordinals=[0, 1, 2, 4])
383
- with pytest.raises(ValueError, match="decision"):
384
- validate(manifest, assets)
385
-
386
-
387
- def test_auto_track_regrouping_invalidates_temporal_ownership():
388
- _, _, native = fixture()
389
- original = compute_annotation_dependency_revisions(native["annotation"])
390
- rows = copy.deepcopy(native["annotation"]["annotations"])
391
- rows[0]["instance_token"] = "regrouped-track"
392
- edited = build_clip_annotations(
393
- native["annotation"]["clip_id"],
394
- native["annotation"]["annotation_frame_id"],
395
- rows,
396
- )
397
- assert (
398
- compute_annotation_dependency_revisions(edited)["scene_ownership_revision"]
399
- != original["scene_ownership_revision"]
400
- )
401
-
402
-
403
- def test_native_label_edit_keeps_geometry_binding_but_motion_or_source_edit_does_not():
404
- manifest, _, native = fixture()
405
- edited = copy.deepcopy(native["annotation"]["annotations"])
406
- edited[0]["motion"] = "dynamic"
407
- annotation = build_clip_annotations(
408
- native["annotation"]["clip_id"],
409
- native["annotation"]["annotation_frame_id"],
410
- edited,
411
- ).to_dict()
412
- with pytest.raises(ValueError, match="dependencies"):
413
- validate_clean_basemap_annotation(manifest, annotation)
414
- scene = copy.deepcopy(native["scene_index"])
415
- scene["chunks"][0]["semantic_histogram"] = [{"code": 0, "count": 3}]
416
- # Existing native parsing enforces the independent label revision too.
417
- from x4d_devkit.semantic_scene_contract import compute_scene_semantics_revision
418
-
419
- scene["scene_semantics_revision"] = compute_scene_semantics_revision(scene)
420
- assert clean_basemap_scene_source(scene) == manifest["scene_source"]
421
-
422
-
423
315
  @pytest.mark.parametrize(
424
316
  "edit",
425
317
  [
426
318
  lambda m: m.update(extra=True),
427
319
  lambda m: m.update(point_count=True),
428
- lambda m: m["input_identity"].update(legacy="sha256:" + "f" * 64),
320
+ lambda m: m["input_identity"].update(
321
+ scene_ownership_revision="sha256:" + "f" * 64
322
+ ),
323
+ lambda m: m.update(instance_sources=[]),
324
+ lambda m: m.update(preannotation_evidence=None),
429
325
  lambda m: m["chunks"][0]["geometry"].update(path="../other.spc.zst"),
430
326
  lambda m: m["chunks"][0].update(ordinal=1),
431
327
  lambda m: m["chunks"].append(copy.deepcopy(m["chunks"][0])),
432
328
  lambda m: m["diagnostics"]["decision_counts"].update({"17": 0}),
433
- lambda m: m["instance_sources"].reverse(),
434
329
  lambda m: m["sweep_sources"].append(copy.deepcopy(m["sweep_sources"][0])),
435
330
  lambda m: m["profile"].update(unknown_policy="delete"),
436
331
  lambda m: m["profile"].update(point_deskew=False),
332
+ lambda m: m["profile"]["policies"].update(motion={}),
437
333
  ],
438
334
  )
439
335
  def test_manifest_rejects_partial_unknown_and_noncanonical_shapes(edit):
@@ -444,6 +340,16 @@ def test_manifest_rejects_partial_unknown_and_noncanonical_shapes(edit):
444
340
  parse_clean_basemap_manifest(manifest)
445
341
 
446
342
 
343
+ def test_native_label_edit_keeps_geometry_binding():
344
+ manifest, _, native = fixture()
345
+ scene = copy.deepcopy(native["scene_index"])
346
+ scene["chunks"][0]["semantic_histogram"] = [{"code": 0, "count": 3}]
347
+ from x4d_devkit.semantic_scene_contract import compute_scene_semantics_revision
348
+
349
+ scene["scene_semantics_revision"] = compute_scene_semantics_revision(scene)
350
+ assert clean_basemap_scene_source(scene) == manifest["scene_source"]
351
+
352
+
447
353
  def test_decoder_rejects_cross_geometry_reserved_bytes_and_extra_frames():
448
354
  manifest, assets, _ = fixture()
449
355
  chunk = manifest["chunks"][0]
@@ -453,33 +359,36 @@ def test_decoder_rejects_cross_geometry_reserved_bytes_and_extra_frames():
453
359
  payload,
454
360
  descriptor=chunk["mapping"],
455
361
  geometry_raw_sha256="sha256:" + "f" * 64,
456
- point_count=4,
362
+ point_count=3,
457
363
  )
458
364
  raw = bytearray(zstandard.ZstdDecompressor().decompress(payload))
459
365
  raw[44 + MAPPING_DTYPE.fields["reserved"][1]] = 1
460
366
  malformed = zstandard.ZstdCompressor().compress(raw)
461
367
  for body in (malformed, payload + payload):
462
- descriptor = {
463
- "path": f"generated/reconstruction/clean_basemap/mapping/{sha256_bytes(body)[7:]}.zst",
464
- "sha256": sha256_bytes(body),
465
- "raw_sha256": sha256_bytes(bytes(raw)),
466
- "size_bytes": len(body),
467
- }
368
+ descriptor = dict(
369
+ path=f"generated/reconstruction/clean_basemap/mapping/{sha256_bytes(body)[7:]}.zst",
370
+ sha256=sha256_bytes(body),
371
+ raw_sha256=sha256_bytes(bytes(raw)),
372
+ size_bytes=len(body),
373
+ )
468
374
  with pytest.raises(ValueError):
469
375
  decode_clean_basemap_mapping(
470
376
  body,
471
377
  descriptor=descriptor,
472
378
  geometry_raw_sha256=chunk["geometry"]["raw_sha256"],
473
- point_count=4,
379
+ point_count=3,
474
380
  )
475
381
 
476
382
 
477
- def test_mapping_encoder_rejects_float_negative_and_removed_observations():
383
+ @pytest.mark.parametrize(
384
+ "changes",
385
+ [
386
+ {"raw_point_ordinals": [-1, 1, 2]},
387
+ {"timestamps_ns": [100.0] * 3},
388
+ {"decisions": [KEEP_GROUND, 0, 18]},
389
+ ],
390
+ )
391
+ def test_mapping_encoder_rejects_float_negative_and_removed_observations(changes):
478
392
  manifest, assets, _ = fixture()
479
- for changes in (
480
- {"raw_point_ordinals": [-1, 1, 2, 3]},
481
- {"timestamps_ns": [100.0] * 4},
482
- {"decisions": [KEEP_GROUND, 0, 0, 17]},
483
- ):
484
- with pytest.raises(ValueError):
485
- remap(manifest, assets, **changes)
393
+ with pytest.raises(ValueError):
394
+ remap(manifest, assets, **changes)
@@ -67,12 +67,12 @@ def test_typed_client_uses_scoped_routes_and_verified_geometry():
67
67
  geometry = client.reconstruction.fetch_clean_basemap_geometry(
68
68
  current, chunk_ordinal=0
69
69
  )
70
- assert geometry.xyz.shape == (4, 3)
71
- assert np.array_equal(geometry.rgb[-1], [100, 110, 120])
70
+ assert geometry.xyz.shape == (3, 3)
71
+ assert np.array_equal(geometry.rgb[-1], [70, 80, 90])
72
72
  mapping = client.reconstruction.fetch_clean_basemap_mapping(
73
73
  current, chunk_ordinal=0
74
74
  )
75
- assert mapping["owner_source_index"].tolist() == [0, 0, 0, 1]
75
+ assert mapping["raw_point_ordinal"].tolist() == [0, 1, 2]
76
76
  cancelled = client.reconstruction.cancel_clean_basemap_task(
77
77
  project_id=7, task_id="task-0"
78
78
  )
@@ -11,7 +11,7 @@ from x4d_devkit.eval.matching import MetricData
11
11
 
12
12
 
13
13
  def test_package_version_is_current_release():
14
- assert x4d_devkit.__version__ == "0.45.0rc1"
14
+ assert x4d_devkit.__version__ == "0.46.0rc2"
15
15
 
16
16
 
17
17
  def test_loader_exposes_and_enforces_only_current_schema(tmp_path):