x4d-devkit 0.43.0rc2__tar.gz → 0.43.0rc4__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 (128) hide show
  1. {x4d_devkit-0.43.0rc2/x4d_devkit.egg-info → x4d_devkit-0.43.0rc4}/PKG-INFO +1 -1
  2. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/pyproject.toml +1 -1
  3. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_clean_basemap.py +54 -0
  4. x4d_devkit-0.43.0rc4/tests/test_clean_basemap_preannotation.py +96 -0
  5. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_sdk_api.py +1 -1
  6. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/clean_basemap.py +128 -7
  7. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4/x4d_devkit.egg-info}/PKG-INFO +1 -1
  8. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit.egg-info/SOURCES.txt +1 -0
  9. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/LICENSE +0 -0
  10. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/README.md +0 -0
  11. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/setup.cfg +0 -0
  12. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_annotation_contract.py +0 -0
  13. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_annotation_reference.py +0 -0
  14. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_assets_client.py +0 -0
  15. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_boundary.py +0 -0
  16. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_box.py +0 -0
  17. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_build_info.py +0 -0
  18. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_clean_basemap_client.py +0 -0
  19. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_cli_auth.py +0 -0
  20. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_client_json_contract.py +0 -0
  21. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_client_projects.py +0 -0
  22. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_clip_archives.py +0 -0
  23. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_contract_validation.py +0 -0
  24. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_coordinate_frames.py +0 -0
  25. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_current_state.py +0 -0
  26. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_current_state_identity_consumers_example.py +0 -0
  27. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_dataset_contract.py +0 -0
  28. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_dataset_identity.py +0 -0
  29. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_detection_eval.py +0 -0
  30. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_evaluate.py +0 -0
  31. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_export_bundle.py +0 -0
  32. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_image_review.py +0 -0
  33. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_inference_input.py +0 -0
  34. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_inference_schemas.py +0 -0
  35. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_inference_service.py +0 -0
  36. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_label_schemas.py +0 -0
  37. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_lidar_materialization.py +0 -0
  38. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_loader.py +0 -0
  39. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_matching.py +0 -0
  40. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_metrics.py +0 -0
  41. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_models.py +0 -0
  42. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_platform_pose.py +0 -0
  43. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_preannotation_client.py +0 -0
  44. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_reconstruction_client.py +0 -0
  45. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_release_gate.py +0 -0
  46. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_roadmark_preview.py +0 -0
  47. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_semantic_scene_authoring.py +0 -0
  48. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_semantic_scene_client.py +0 -0
  49. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_semantic_scene_contract.py +0 -0
  50. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_semantic_scene_native_clip.py +0 -0
  51. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_time_contract.py +0 -0
  52. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_token.py +0 -0
  53. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_tracking_eval.py +0 -0
  54. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_training_identity.py +0 -0
  55. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_transform.py +0 -0
  56. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_transform_chain.py +0 -0
  57. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_validation.py +0 -0
  58. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/tests/test_work_session.py +0 -0
  59. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/__init__.py +0 -0
  60. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/annotation_contract.py +0 -0
  61. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/annotation_reference.py +0 -0
  62. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/build_info.py +0 -0
  63. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/cli.py +0 -0
  64. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/__init__.py +0 -0
  65. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/_origin.py +0 -0
  66. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/annotation_reference.py +0 -0
  67. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/assets.py +0 -0
  68. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/bags.py +0 -0
  69. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/capabilities.py +0 -0
  70. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/checkpoints.py +0 -0
  71. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/clean_basemap.py +0 -0
  72. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/client.py +0 -0
  73. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/clip_work_sessions.py +0 -0
  74. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/clips.py +0 -0
  75. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/dataset_identity.py +0 -0
  76. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/image_review.py +0 -0
  77. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/label_schemas.py +0 -0
  78. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/preannotation.py +0 -0
  79. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/projects.py +0 -0
  80. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/reconstruction.py +0 -0
  81. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/semantic_scene.py +0 -0
  82. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/semantic_scene_authoring.py +0 -0
  83. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/services.py +0 -0
  84. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/test_sets.py +0 -0
  85. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/consumer_identity.py +0 -0
  86. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/coordinate_contract.py +0 -0
  87. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/core/__init__.py +0 -0
  88. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/core/loader.py +0 -0
  89. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/core/models.py +0 -0
  90. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/core/token.py +0 -0
  91. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/core/transform.py +0 -0
  92. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/current_state.py +0 -0
  93. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/dataset_contract.py +0 -0
  94. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/dataset_identity.py +0 -0
  95. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/eval/__init__.py +0 -0
  96. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/eval/api.py +0 -0
  97. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/eval/box.py +0 -0
  98. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/eval/detection.py +0 -0
  99. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/eval/matching.py +0 -0
  100. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/eval/metrics.py +0 -0
  101. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/eval/tracking.py +0 -0
  102. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/export_bundle.py +0 -0
  103. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/image_review.py +0 -0
  104. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/inference/__init__.py +0 -0
  105. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/inference/input.py +0 -0
  106. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/inference/models.py +0 -0
  107. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/inference/schemas.py +0 -0
  108. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/inference/service.py +0 -0
  109. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/inference/tasks.py +0 -0
  110. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/json_contract.py +0 -0
  111. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/label_schema.py +0 -0
  112. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/lidar_materialization.py +0 -0
  113. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/materialize.py +0 -0
  114. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/roadmark_preview.py +0 -0
  115. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/semantic_scene_authoring.py +0 -0
  116. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/semantic_scene_contract.py +0 -0
  117. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/time_contract.py +0 -0
  118. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/training_identity.py +0 -0
  119. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/validation/__init__.py +0 -0
  120. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/validation/contract.py +0 -0
  121. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/validation/platform_pose.py +0 -0
  122. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/validation/report.py +0 -0
  123. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/validation/validator.py +0 -0
  124. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit/work_session.py +0 -0
  125. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit.egg-info/dependency_links.txt +0 -0
  126. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit.egg-info/entry_points.txt +0 -0
  127. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/x4d_devkit.egg-info/requires.txt +0 -0
  128. {x4d_devkit-0.43.0rc2 → x4d_devkit-0.43.0rc4}/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.43.0rc2
3
+ Version: 0.43.0rc4
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.43.0rc2"
7
+ version = "0.43.0rc4"
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"
@@ -10,6 +10,7 @@ from x4d_devkit.clean_basemap import (
10
10
  CLEAN_BASEMAP_CONTRACT_VERSION,
11
11
  CLEAN_BASEMAP_INPUT_FIELDS,
12
12
  DECISION_CODES,
13
+ KEEP_CODES,
13
14
  KEEP_GROUND,
14
15
  MAPPING_DTYPE,
15
16
  REMOVE_MODEL_DYNAMIC,
@@ -24,6 +25,7 @@ from x4d_devkit.clean_basemap import (
24
25
  validate_clean_basemap_annotation,
25
26
  validate_clean_basemap_native_binding,
26
27
  validate_clean_basemap_resource,
28
+ compute_clean_basemap_preannotation_revision,
27
29
  )
28
30
  from x4d_devkit.semantic_scene_contract import (
29
31
  compute_annotation_dependency_revisions,
@@ -96,6 +98,9 @@ def fixture():
96
98
  },
97
99
  }
98
100
  inputs = {key: "sha256:" + "b" * 64 for key in CLEAN_BASEMAP_INPUT_FIELDS}
101
+ inputs["preannotation_revision"] = compute_clean_basemap_preannotation_revision(
102
+ None
103
+ )
99
104
  dependencies = compute_annotation_dependency_revisions(native["annotation"])
100
105
  for key in inputs:
101
106
  if key in dependencies:
@@ -109,6 +114,7 @@ def fixture():
109
114
  inputs, profile
110
115
  ),
111
116
  "input_identity": inputs,
117
+ "preannotation_evidence": None,
112
118
  "profile": profile,
113
119
  "point_count": 4,
114
120
  "bounds": [xyz.min(0).tolist(), xyz.max(0).tolist()],
@@ -308,6 +314,54 @@ def test_previous_contract_and_decision_bytes_are_rejected():
308
314
  )
309
315
 
310
316
 
317
+ @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
321
+ if family == "decisions":
322
+ carrier = encode_clean_basemap_decisions(
323
+ asset_sha256=anchor, decisions=np.full(count, KEEP_GROUND)
324
+ )
325
+ domain = DECISION_CODES
326
+ decode = decode_clean_basemap_decisions
327
+ arguments = dict(asset_sha256=anchor, point_count=count)
328
+ byte_offset = -1
329
+ else:
330
+ carrier = encode_clean_basemap_mapping(
331
+ geometry_raw_sha256=anchor,
332
+ sweep_indices=np.zeros(count, dtype=np.uint32),
333
+ raw_point_ordinals=np.arange(count),
334
+ timestamps_ns=np.full(count, 100),
335
+ owner_source_indices=np.zeros(count, dtype=np.uint32),
336
+ owner_point_ordinals=np.arange(count),
337
+ decisions=np.full(count, KEEP_GROUND),
338
+ )
339
+ domain = KEEP_CODES
340
+ decode = decode_clean_basemap_mapping
341
+ arguments = dict(geometry_raw_sha256=anchor, point_count=count)
342
+ byte_offset = -MAPPING_DTYPE.itemsize + MAPPING_DTYPE.fields["decision"][1]
343
+ raw = bytearray(zstandard.ZstdDecompressor().decompress(carrier.payload))
344
+ for code in range(256):
345
+ raw[byte_offset] = code
346
+ payload = zstandard.ZstdCompressor().compress(raw)
347
+ descriptor = carrier.descriptor(family)
348
+ descriptor.update(
349
+ path=f"generated/reconstruction/clean_basemap/{family}/{sha256_bytes(payload)[7:]}.zst",
350
+ sha256=sha256_bytes(payload),
351
+ raw_sha256=sha256_bytes(bytes(raw)),
352
+ size_bytes=len(payload),
353
+ )
354
+ if code in domain:
355
+ 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)
359
+ else:
360
+ message = "decision" if family == "decisions" else "mapping records"
361
+ with pytest.raises(ValueError, match=message):
362
+ decode(payload, descriptor=descriptor, **arguments)
363
+
364
+
311
365
  def test_pose_coverage_rejections_are_accounted_and_cannot_be_mapped():
312
366
  manifest, assets, _ = fixture()
313
367
  sweep = manifest["sweep_sources"][0]
@@ -0,0 +1,96 @@
1
+ import copy
2
+
3
+ import pytest
4
+
5
+ from tests.test_clean_basemap import fixture
6
+ from x4d_devkit.clean_basemap import (
7
+ CLEAN_BASEMAP_PREANNOTATION_CONTRACT_VERSION,
8
+ clean_basemap_preannotation_reference,
9
+ compute_clean_basemap_dependency_identity,
10
+ compute_clean_basemap_preannotation_revision,
11
+ compute_clean_basemap_revision,
12
+ parse_clean_basemap_manifest,
13
+ validate_clean_basemap_preannotation_evidence,
14
+ )
15
+
16
+
17
+ def evidence_fixture():
18
+ manifest, _, native = fixture()
19
+ evidence = {
20
+ "contract_version": CLEAN_BASEMAP_PREANNOTATION_CONTRACT_VERSION,
21
+ "clip_id": manifest["clip_id"],
22
+ "candidate_id": "sha256:" + "c" * 64,
23
+ "profile_id": "detector-profile-1",
24
+ "pipeline_identity": "sha256:" + "d" * 64,
25
+ "checkpoint_identity": "sha256:" + "e" * 64,
26
+ "source_identity": {
27
+ key: manifest["input_identity"][key]
28
+ for key in (
29
+ "samples_revision",
30
+ "sweeps_revision",
31
+ "calibration_revision",
32
+ "pose_revision",
33
+ )
34
+ },
35
+ "annotation": native["annotation"],
36
+ }
37
+ manifest["preannotation_evidence"] = clean_basemap_preannotation_reference(evidence)
38
+ manifest["input_identity"]["preannotation_revision"] = (
39
+ compute_clean_basemap_preannotation_revision(evidence)
40
+ )
41
+ manifest["dependency_identity"] = compute_clean_basemap_dependency_identity(
42
+ manifest["input_identity"], manifest["profile"]
43
+ )
44
+ manifest["basemap_revision"] = compute_clean_basemap_revision(manifest)
45
+ return manifest, evidence
46
+
47
+
48
+ def test_evidence_is_bound_separately_and_does_not_add_native_instances():
49
+ manifest, evidence = evidence_fixture()
50
+ before = copy.deepcopy(manifest["instance_sources"])
51
+ parsed = parse_clean_basemap_manifest(manifest)
52
+ assert (
53
+ validate_clean_basemap_preannotation_evidence(
54
+ evidence, clip_id=parsed.clip_id, input_identity=manifest["input_identity"]
55
+ )
56
+ == evidence
57
+ )
58
+ assert parsed.to_dict()["instance_sources"] == before
59
+ assert "annotation" not in parsed.to_dict()["preannotation_evidence"]
60
+
61
+
62
+ @pytest.mark.parametrize(
63
+ "field",
64
+ ["samples_revision", "sweeps_revision", "calibration_revision", "pose_revision"],
65
+ )
66
+ def test_evidence_from_different_geometry_cannot_bind(field):
67
+ manifest, evidence = evidence_fixture()
68
+ evidence["source_identity"][field] = "sha256:" + "f" * 64
69
+ with pytest.raises(ValueError, match="source differs"):
70
+ validate_clean_basemap_preannotation_evidence(
71
+ evidence,
72
+ clip_id=manifest["clip_id"],
73
+ input_identity=manifest["input_identity"],
74
+ )
75
+
76
+
77
+ def test_different_candidate_or_absent_evidence_requires_new_identity():
78
+ manifest, evidence = evidence_fixture()
79
+ evidence["candidate_id"] = "sha256:" + "f" * 64
80
+ with pytest.raises(ValueError, match="revision differs"):
81
+ validate_clean_basemap_preannotation_evidence(
82
+ evidence,
83
+ clip_id=manifest["clip_id"],
84
+ input_identity=manifest["input_identity"],
85
+ )
86
+ with pytest.raises(ValueError, match="revision differs"):
87
+ validate_clean_basemap_preannotation_evidence(
88
+ None, clip_id=manifest["clip_id"], input_identity=manifest["input_identity"]
89
+ )
90
+
91
+
92
+ def test_historical_v2_is_rejected_even_with_current_fields():
93
+ manifest, _ = evidence_fixture()
94
+ manifest["contract_version"] = "x4d-clean-static-basemap-v2"
95
+ with pytest.raises(ValueError, match="current contract"):
96
+ parse_clean_basemap_manifest(manifest)
@@ -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.43.0rc2"
14
+ assert x4d_devkit.__version__ == "0.43.0rc4"
15
15
 
16
16
 
17
17
  def test_loader_exposes_and_enforces_only_current_schema(tmp_path):
@@ -36,7 +36,11 @@ from x4d_devkit.semantic_scene_contract import (
36
36
  )
37
37
  from x4d_devkit.time_contract import parse_time_ns
38
38
 
39
- CLEAN_BASEMAP_CONTRACT_VERSION = "x4d-clean-static-basemap-v2"
39
+ CLEAN_BASEMAP_CONTRACT_VERSION = "x4d-clean-static-basemap-v3"
40
+ CLEAN_BASEMAP_PREANNOTATION_CONTRACT_VERSION = "x4d-clean-basemap-preannotation-v1"
41
+ _GEOMETRY_INPUT_FIELDS = frozenset(
42
+ {"samples_revision", "sweeps_revision", "calibration_revision", "pose_revision"}
43
+ )
40
44
  CLEAN_BASEMAP_OPERATION = "reconstruction.prepare_clean_static_basemap"
41
45
  CLEAN_BASEMAP_MAPPING_FORMAT = "x4d-clean-static-basemap-mapping-v1"
42
46
  CLEAN_BASEMAP_DECISION_FORMAT = "x4d-clean-static-basemap-decisions-v2"
@@ -49,6 +53,7 @@ CLEAN_BASEMAP_INPUT_FIELDS = frozenset(
49
53
  "scene_ownership_revision",
50
54
  "instance_shape_input_revision",
51
55
  "image_input_revision",
56
+ "preannotation_revision",
52
57
  }
53
58
  )
54
59
  KEEP_STATIC = 0
@@ -183,6 +188,117 @@ def compute_clean_basemap_revision(value: Mapping[str, Any]) -> str:
183
188
  return canonical_json_sha256(data)
184
189
 
185
190
 
191
+ def parse_clean_basemap_preannotation_evidence(value: Any) -> dict | None:
192
+ """Validate supplemental hypotheses without granting native ownership."""
193
+ if value is None:
194
+ return None
195
+ data = _thaw(value)
196
+ _object(
197
+ data,
198
+ {
199
+ "contract_version",
200
+ "clip_id",
201
+ "candidate_id",
202
+ "profile_id",
203
+ "pipeline_identity",
204
+ "checkpoint_identity",
205
+ "source_identity",
206
+ "annotation",
207
+ },
208
+ "preannotation_evidence",
209
+ )
210
+ ensure_i_json(data, path="preannotation_evidence")
211
+ if data["contract_version"] != CLEAN_BASEMAP_PREANNOTATION_CONTRACT_VERSION:
212
+ raise ValueError("preannotation evidence requires the current contract")
213
+ parse_portable_token(data["clip_id"], path="preannotation_evidence.clip_id")
214
+ parse_portable_token(data["profile_id"], path="preannotation_evidence.profile_id")
215
+ for key in ("candidate_id", "pipeline_identity", "checkpoint_identity"):
216
+ _hash(data[key], f"preannotation_evidence.{key}")
217
+ _object(
218
+ data["source_identity"], _GEOMETRY_INPUT_FIELDS, "preannotation source_identity"
219
+ )
220
+ for key, identity in data["source_identity"].items():
221
+ _hash(identity, f"preannotation source_identity.{key}")
222
+ annotation = parse_clip_annotations(data["annotation"])
223
+ if annotation.clip_id != data["clip_id"]:
224
+ raise ValueError("preannotation evidence annotation belongs to another Clip")
225
+ data["annotation"] = annotation.to_dict()
226
+ return data
227
+
228
+
229
+ def clean_basemap_preannotation_reference(value: Any) -> dict | None:
230
+ data = parse_clean_basemap_preannotation_evidence(value)
231
+ if data is None:
232
+ return None
233
+ annotation = data.pop("annotation")
234
+ return {**data, "annotation_revision": annotation["annotation_revision"]}
235
+
236
+
237
+ def compute_clean_basemap_preannotation_revision(value: Any) -> str:
238
+ return canonical_json_sha256(
239
+ {"evidence": clean_basemap_preannotation_reference(value)}
240
+ )
241
+
242
+
243
+ def _validate_preannotation_reference(reference, *, clip_id, input_identity):
244
+ if reference is not None:
245
+ _object(
246
+ reference,
247
+ {
248
+ "contract_version",
249
+ "clip_id",
250
+ "candidate_id",
251
+ "profile_id",
252
+ "pipeline_identity",
253
+ "checkpoint_identity",
254
+ "source_identity",
255
+ "annotation_revision",
256
+ },
257
+ "preannotation_evidence",
258
+ )
259
+ if (
260
+ reference["contract_version"]
261
+ != CLEAN_BASEMAP_PREANNOTATION_CONTRACT_VERSION
262
+ or reference["clip_id"] != clip_id
263
+ ):
264
+ raise ValueError("preannotation evidence contract or Clip differs")
265
+ parse_portable_token(
266
+ reference["profile_id"], path="preannotation_evidence.profile_id"
267
+ )
268
+ for key in (
269
+ "candidate_id",
270
+ "pipeline_identity",
271
+ "checkpoint_identity",
272
+ "annotation_revision",
273
+ ):
274
+ _hash(reference[key], f"preannotation_evidence.{key}")
275
+ _object(
276
+ reference["source_identity"],
277
+ _GEOMETRY_INPUT_FIELDS,
278
+ "preannotation source_identity",
279
+ )
280
+ if reference["source_identity"] != {
281
+ key: input_identity[key] for key in _GEOMETRY_INPUT_FIELDS
282
+ }:
283
+ raise ValueError("preannotation evidence source differs from clean basemap")
284
+ if (
285
+ canonical_json_sha256({"evidence": reference})
286
+ != input_identity["preannotation_revision"]
287
+ ):
288
+ raise ValueError("preannotation evidence revision differs from clean basemap")
289
+
290
+
291
+ def validate_clean_basemap_preannotation_evidence(value, *, clip_id, input_identity):
292
+ """Validate the full frozen evidence against its manifest dependency."""
293
+ data = parse_clean_basemap_preannotation_evidence(value)
294
+ _validate_preannotation_reference(
295
+ clean_basemap_preannotation_reference(data),
296
+ clip_id=clip_id,
297
+ input_identity=input_identity,
298
+ )
299
+ return data
300
+
301
+
186
302
  def compute_clean_basemap_dependency_identity(
187
303
  input_identity: Mapping[str, str], profile: Mapping[str, Any]
188
304
  ) -> str:
@@ -285,6 +401,7 @@ def parse_clean_basemap_manifest(
285
401
  "basemap_revision",
286
402
  "dependency_identity",
287
403
  "input_identity",
404
+ "preannotation_evidence",
288
405
  "profile",
289
406
  "point_count",
290
407
  "bounds",
@@ -303,6 +420,11 @@ def parse_clean_basemap_manifest(
303
420
  ):
304
421
  raise ValueError("clean basemap requires the current contract in clip_world")
305
422
  parse_portable_token(data["clip_id"], path="clean_basemap.clip_id")
423
+ _validate_preannotation_reference(
424
+ data["preannotation_evidence"],
425
+ clip_id=data["clip_id"],
426
+ input_identity=data["input_identity"],
427
+ )
306
428
  _hash(data["basemap_revision"], "basemap_revision")
307
429
  if data["dependency_identity"] != compute_clean_basemap_dependency_identity(
308
430
  data["input_identity"], data["profile"]
@@ -528,9 +650,8 @@ def encode_clean_basemap_mapping(
528
650
  }
529
651
  count = len(vectors["sweep_index"])
530
652
  _integer(count, 1, SEMANTIC_SCENE_MAX_SCENE_POINTS_PER_CHUNK, "point_count")
531
- if (
532
- any(len(vector) != count for vector in vectors.values())
533
- or not set(int(v) for v in vectors["decision"]) <= KEEP_CODES
653
+ if any(len(vector) != count for vector in vectors.values()) or np.any(
654
+ ~np.isin(vectors["decision"], tuple(KEEP_CODES))
534
655
  ):
535
656
  raise ValueError(
536
657
  "mapping must have one retained decision and address per geometry point"
@@ -560,7 +681,7 @@ def decode_clean_basemap_mapping(
560
681
  )
561
682
  if (
562
683
  np.any(records["reserved"] != 0)
563
- or not set(int(v) for v in records["decision"]) <= KEEP_CODES
684
+ or np.any(~np.isin(records["decision"], tuple(KEEP_CODES)))
564
685
  or np.any(records["timestamp_ns"] > _MAX_I64)
565
686
  ):
566
687
  raise ValueError(
@@ -574,7 +695,7 @@ def encode_clean_basemap_decisions(
574
695
  ) -> CleanBasemapPayload:
575
696
  values = _vector(decisions, 255, "decisions")
576
697
  _integer(len(values), 1, 1 << 24, "source_point_count")
577
- if not set(int(v) for v in values) <= DECISION_CODES:
698
+ if np.any(~np.isin(values, tuple(DECISION_CODES))):
578
699
  raise ValueError("unknown clean basemap decision code")
579
700
  return _encode(_DECISION_MAGIC, asset_sha256, values.astype(np.uint8))
580
701
 
@@ -591,7 +712,7 @@ def decode_clean_basemap_decisions(
591
712
  values = _decode(
592
713
  payload, descriptor, _DECISION_MAGIC, asset_sha256, point_count, np.dtype("u1")
593
714
  )
594
- if not set(int(v) for v in values) <= DECISION_CODES:
715
+ if np.any(~np.isin(values, tuple(DECISION_CODES))):
595
716
  raise ValueError("unknown clean basemap decision code")
596
717
  return values
597
718
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: x4d-devkit
3
- Version: 0.43.0rc2
3
+ Version: 0.43.0rc4
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
@@ -9,6 +9,7 @@ tests/test_box.py
9
9
  tests/test_build_info.py
10
10
  tests/test_clean_basemap.py
11
11
  tests/test_clean_basemap_client.py
12
+ tests/test_clean_basemap_preannotation.py
12
13
  tests/test_cli_auth.py
13
14
  tests/test_client_json_contract.py
14
15
  tests/test_client_projects.py
File without changes
File without changes
File without changes