x4d-devkit 0.43.0rc3__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.0rc3/x4d_devkit.egg-info → x4d_devkit-0.43.0rc4}/PKG-INFO +1 -1
  2. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/pyproject.toml +1 -1
  3. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_clean_basemap.py +5 -0
  4. x4d_devkit-0.43.0rc4/tests/test_clean_basemap_preannotation.py +96 -0
  5. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_sdk_api.py +1 -1
  6. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/clean_basemap.py +125 -4
  7. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4/x4d_devkit.egg-info}/PKG-INFO +1 -1
  8. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit.egg-info/SOURCES.txt +1 -0
  9. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/LICENSE +0 -0
  10. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/README.md +0 -0
  11. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/setup.cfg +0 -0
  12. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_annotation_contract.py +0 -0
  13. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_annotation_reference.py +0 -0
  14. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_assets_client.py +0 -0
  15. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_boundary.py +0 -0
  16. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_box.py +0 -0
  17. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_build_info.py +0 -0
  18. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_clean_basemap_client.py +0 -0
  19. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_cli_auth.py +0 -0
  20. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_client_json_contract.py +0 -0
  21. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_client_projects.py +0 -0
  22. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_clip_archives.py +0 -0
  23. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_contract_validation.py +0 -0
  24. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_coordinate_frames.py +0 -0
  25. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_current_state.py +0 -0
  26. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_current_state_identity_consumers_example.py +0 -0
  27. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_dataset_contract.py +0 -0
  28. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_dataset_identity.py +0 -0
  29. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_detection_eval.py +0 -0
  30. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_evaluate.py +0 -0
  31. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_export_bundle.py +0 -0
  32. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_image_review.py +0 -0
  33. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_inference_input.py +0 -0
  34. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_inference_schemas.py +0 -0
  35. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_inference_service.py +0 -0
  36. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_label_schemas.py +0 -0
  37. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_lidar_materialization.py +0 -0
  38. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_loader.py +0 -0
  39. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_matching.py +0 -0
  40. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_metrics.py +0 -0
  41. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_models.py +0 -0
  42. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_platform_pose.py +0 -0
  43. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_preannotation_client.py +0 -0
  44. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_reconstruction_client.py +0 -0
  45. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_release_gate.py +0 -0
  46. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_roadmark_preview.py +0 -0
  47. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_semantic_scene_authoring.py +0 -0
  48. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_semantic_scene_client.py +0 -0
  49. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_semantic_scene_contract.py +0 -0
  50. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_semantic_scene_native_clip.py +0 -0
  51. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_time_contract.py +0 -0
  52. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_token.py +0 -0
  53. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_tracking_eval.py +0 -0
  54. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_training_identity.py +0 -0
  55. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_transform.py +0 -0
  56. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_transform_chain.py +0 -0
  57. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_validation.py +0 -0
  58. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/tests/test_work_session.py +0 -0
  59. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/__init__.py +0 -0
  60. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/annotation_contract.py +0 -0
  61. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/annotation_reference.py +0 -0
  62. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/build_info.py +0 -0
  63. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/cli.py +0 -0
  64. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/__init__.py +0 -0
  65. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/_origin.py +0 -0
  66. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/annotation_reference.py +0 -0
  67. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/assets.py +0 -0
  68. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/bags.py +0 -0
  69. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/capabilities.py +0 -0
  70. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/checkpoints.py +0 -0
  71. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/clean_basemap.py +0 -0
  72. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/client.py +0 -0
  73. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/clip_work_sessions.py +0 -0
  74. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/clips.py +0 -0
  75. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/dataset_identity.py +0 -0
  76. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/image_review.py +0 -0
  77. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/label_schemas.py +0 -0
  78. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/preannotation.py +0 -0
  79. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/projects.py +0 -0
  80. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/reconstruction.py +0 -0
  81. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/semantic_scene.py +0 -0
  82. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/semantic_scene_authoring.py +0 -0
  83. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/services.py +0 -0
  84. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/client/test_sets.py +0 -0
  85. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/consumer_identity.py +0 -0
  86. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/coordinate_contract.py +0 -0
  87. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/core/__init__.py +0 -0
  88. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/core/loader.py +0 -0
  89. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/core/models.py +0 -0
  90. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/core/token.py +0 -0
  91. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/core/transform.py +0 -0
  92. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/current_state.py +0 -0
  93. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/dataset_contract.py +0 -0
  94. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/dataset_identity.py +0 -0
  95. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/eval/__init__.py +0 -0
  96. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/eval/api.py +0 -0
  97. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/eval/box.py +0 -0
  98. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/eval/detection.py +0 -0
  99. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/eval/matching.py +0 -0
  100. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/eval/metrics.py +0 -0
  101. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/eval/tracking.py +0 -0
  102. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/export_bundle.py +0 -0
  103. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/image_review.py +0 -0
  104. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/inference/__init__.py +0 -0
  105. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/inference/input.py +0 -0
  106. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/inference/models.py +0 -0
  107. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/inference/schemas.py +0 -0
  108. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/inference/service.py +0 -0
  109. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/inference/tasks.py +0 -0
  110. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/json_contract.py +0 -0
  111. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/label_schema.py +0 -0
  112. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/lidar_materialization.py +0 -0
  113. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/materialize.py +0 -0
  114. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/roadmark_preview.py +0 -0
  115. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/semantic_scene_authoring.py +0 -0
  116. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/semantic_scene_contract.py +0 -0
  117. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/time_contract.py +0 -0
  118. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/training_identity.py +0 -0
  119. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/validation/__init__.py +0 -0
  120. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/validation/contract.py +0 -0
  121. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/validation/platform_pose.py +0 -0
  122. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/validation/report.py +0 -0
  123. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/validation/validator.py +0 -0
  124. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit/work_session.py +0 -0
  125. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit.egg-info/dependency_links.txt +0 -0
  126. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit.egg-info/entry_points.txt +0 -0
  127. {x4d_devkit-0.43.0rc3 → x4d_devkit-0.43.0rc4}/x4d_devkit.egg-info/requires.txt +0 -0
  128. {x4d_devkit-0.43.0rc3 → 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.0rc3
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.0rc3"
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"
@@ -25,6 +25,7 @@ from x4d_devkit.clean_basemap import (
25
25
  validate_clean_basemap_annotation,
26
26
  validate_clean_basemap_native_binding,
27
27
  validate_clean_basemap_resource,
28
+ compute_clean_basemap_preannotation_revision,
28
29
  )
29
30
  from x4d_devkit.semantic_scene_contract import (
30
31
  compute_annotation_dependency_revisions,
@@ -97,6 +98,9 @@ def fixture():
97
98
  },
98
99
  }
99
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
+ )
100
104
  dependencies = compute_annotation_dependency_revisions(native["annotation"])
101
105
  for key in inputs:
102
106
  if key in dependencies:
@@ -110,6 +114,7 @@ def fixture():
110
114
  inputs, profile
111
115
  ),
112
116
  "input_identity": inputs,
117
+ "preannotation_evidence": None,
113
118
  "profile": profile,
114
119
  "point_count": 4,
115
120
  "bounds": [xyz.min(0).tolist(), xyz.max(0).tolist()],
@@ -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.0rc3"
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 np.any(~np.isin(vectors["decision"], tuple(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"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: x4d-devkit
3
- Version: 0.43.0rc3
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