x4d-devkit 0.17.0__tar.gz → 0.22.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 (107) hide show
  1. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/PKG-INFO +102 -134
  2. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/README.md +92 -132
  3. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/pyproject.toml +19 -3
  4. x4d_devkit-0.22.0rc2/tests/test_assets_client.py +124 -0
  5. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_boundary.py +21 -0
  6. x4d_devkit-0.22.0rc2/tests/test_client_json_contract.py +42 -0
  7. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_contract_validation.py +30 -0
  8. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_coordinate_frames.py +2 -1
  9. x4d_devkit-0.22.0rc2/tests/test_current_state.py +202 -0
  10. x4d_devkit-0.22.0rc2/tests/test_dataset_contract.py +66 -0
  11. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_dataset_identity.py +491 -188
  12. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_evaluate.py +0 -1
  13. x4d_devkit-0.22.0rc2/tests/test_export_bundle.py +358 -0
  14. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_label_schemas.py +308 -4
  15. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_loader.py +15 -5
  16. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_nuscenes_converter.py +114 -11
  17. x4d_devkit-0.22.0rc2/tests/test_platform_pose.py +136 -0
  18. x4d_devkit-0.22.0rc2/tests/test_release_gate.py +50 -0
  19. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_sdk_api.py +11 -3
  20. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_token.py +35 -3
  21. x4d_devkit-0.22.0rc2/tests/test_training_identity.py +384 -0
  22. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_transform.py +0 -1
  23. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_transform_chain.py +3 -1
  24. x4d_devkit-0.22.0rc2/tests/test_validation.py +256 -0
  25. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_work_session.py +57 -4
  26. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/__init__.py +26 -1
  27. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/cli.py +39 -312
  28. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/__init__.py +3 -0
  29. x4d_devkit-0.22.0rc2/x4d_devkit/client/_origin.py +41 -0
  30. x4d_devkit-0.22.0rc2/x4d_devkit/client/assets.py +97 -0
  31. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/client.py +15 -22
  32. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/clip_work_sessions.py +6 -2
  33. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/dataset_identity.py +12 -2
  34. x4d_devkit-0.22.0rc2/x4d_devkit/client/label_schemas.py +130 -0
  35. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/projects.py +0 -15
  36. x4d_devkit-0.22.0rc2/x4d_devkit/consumer_identity.py +219 -0
  37. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/converters/nuscenes.py +13 -12
  38. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/core/__init__.py +2 -1
  39. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/core/loader.py +24 -10
  40. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/core/models.py +3 -3
  41. x4d_devkit-0.22.0rc2/x4d_devkit/core/token.py +49 -0
  42. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/core/transform.py +1 -2
  43. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/current_state.py +86 -48
  44. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/dataset_contract.py +29 -0
  45. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/dataset_identity.py +362 -138
  46. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/eval/box.py +4 -4
  47. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/eval/detection.py +2 -2
  48. x4d_devkit-0.22.0rc2/x4d_devkit/export_bundle.py +502 -0
  49. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/inference/service.py +2 -19
  50. x4d_devkit-0.22.0rc2/x4d_devkit/json_contract.py +58 -0
  51. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/label_schema.py +58 -16
  52. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/materialize.py +31 -25
  53. x4d_devkit-0.22.0rc2/x4d_devkit/training_identity.py +650 -0
  54. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/validation/__init__.py +6 -0
  55. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/validation/contract.py +25 -8
  56. x4d_devkit-0.22.0rc2/x4d_devkit/validation/platform_pose.py +136 -0
  57. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/validation/validator.py +168 -30
  58. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/work_session.py +6 -5
  59. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit.egg-info/PKG-INFO +102 -134
  60. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit.egg-info/SOURCES.txt +12 -2
  61. x4d_devkit-0.22.0rc2/x4d_devkit.egg-info/requires.txt +27 -0
  62. x4d_devkit-0.17.0/tests/test_current_state.py +0 -111
  63. x4d_devkit-0.17.0/tests/test_training_identity.py +0 -207
  64. x4d_devkit-0.17.0/tests/test_training_manifest.py +0 -455
  65. x4d_devkit-0.17.0/tests/test_validation.py +0 -92
  66. x4d_devkit-0.17.0/x4d_devkit/client/label_schemas.py +0 -70
  67. x4d_devkit-0.17.0/x4d_devkit/core/token.py +0 -26
  68. x4d_devkit-0.17.0/x4d_devkit/manifest.py +0 -960
  69. x4d_devkit-0.17.0/x4d_devkit/training_identity.py +0 -398
  70. x4d_devkit-0.17.0/x4d_devkit.egg-info/requires.txt +0 -15
  71. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/LICENSE +0 -0
  72. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/setup.cfg +0 -0
  73. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_box.py +0 -0
  74. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_build_info.py +0 -0
  75. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_cli_auth.py +0 -0
  76. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_client_projects.py +0 -0
  77. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_clip_archives.py +0 -0
  78. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_current_state_identity_consumers_example.py +0 -0
  79. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_detection_eval.py +0 -0
  80. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_inference_builders.py +0 -0
  81. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_inference_schemas.py +0 -0
  82. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_inference_service.py +0 -0
  83. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_matching.py +0 -0
  84. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_metrics.py +0 -0
  85. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/tests/test_models.py +0 -0
  86. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/build_info.py +0 -0
  87. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/bags.py +0 -0
  88. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/capabilities.py +0 -0
  89. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/checkpoints.py +0 -0
  90. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/clips.py +0 -0
  91. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/services.py +0 -0
  92. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/test_sets.py +0 -0
  93. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/converters/__init__.py +0 -0
  94. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/coordinate_contract.py +0 -0
  95. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/eval/__init__.py +0 -0
  96. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/eval/api.py +0 -0
  97. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/eval/matching.py +0 -0
  98. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/eval/metrics.py +0 -0
  99. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/inference/__init__.py +0 -0
  100. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/inference/builders.py +0 -0
  101. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/inference/models.py +0 -0
  102. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/inference/schemas.py +0 -0
  103. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/inference/tasks.py +0 -0
  104. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit/validation/report.py +0 -0
  105. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit.egg-info/dependency_links.txt +0 -0
  106. {x4d_devkit-0.17.0 → x4d_devkit-0.22.0rc2}/x4d_devkit.egg-info/entry_points.txt +0 -0
  107. {x4d_devkit-0.17.0 → x4d_devkit-0.22.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.17.0
3
+ Version: 0.22.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
@@ -23,7 +23,7 @@ Requires-Dist: numpy>=1.24
23
23
  Requires-Dist: httpx>=0.27
24
24
  Requires-Dist: rfc8785==0.1.4
25
25
  Provides-Extra: converters
26
- Requires-Dist: nuscenes-devkit>=1.1.0; extra == "converters"
26
+ Requires-Dist: nuscenes-devkit>=1.2.0; extra == "converters"
27
27
  Provides-Extra: inference
28
28
  Requires-Dist: pydantic>=2.0; extra == "inference"
29
29
  Requires-Dist: fastapi>=0.110; extra == "inference"
@@ -31,6 +31,14 @@ Requires-Dist: uvicorn>=0.27; extra == "inference"
31
31
  Provides-Extra: dev
32
32
  Requires-Dist: pytest>=7.0; extra == "dev"
33
33
  Requires-Dist: pytest-cov; extra == "dev"
34
+ Requires-Dist: ruff>=0.9; extra == "dev"
35
+ Requires-Dist: tomli>=2.0; python_version < "3.11" and extra == "dev"
36
+ Provides-Extra: docs
37
+ Requires-Dist: mkdocs-material>=9.5; extra == "docs"
38
+ Requires-Dist: mkdocstrings[python]>=0.27; extra == "docs"
39
+ Provides-Extra: release
40
+ Requires-Dist: build>=1.2; extra == "release"
41
+ Requires-Dist: twine>=6.0; extra == "release"
34
42
  Dynamic: license-file
35
43
 
36
44
  # x4d-devkit
@@ -52,19 +60,20 @@ modules or mutate DB/MinIO internals.
52
60
  Design decisions for this boundary are tracked in GitHub issues, starting with:
53
61
 
54
62
  - [#42](https://github.com/windzu/x4d-devkit/issues/42) - official external SDK and CLI boundary
55
- - [#43](https://github.com/windzu/x4d-devkit/issues/43) - private source install in X-4D runtime
63
+ - [#43](https://github.com/windzu/x4d-devkit/issues/43) - historical private-source runtime integration
56
64
  - [#44](https://github.com/windzu/x4d-devkit/issues/44) - capability registry and CLI sync
57
65
 
58
66
  ## Installation
59
67
 
60
68
  ```bash
61
- pip install x4d-devkit
69
+ pip install x4d-devkit==0.22.0rc2
62
70
  ```
63
71
 
64
- X-4D platform runtime installs this package from the checked-out private source
65
- repo/submodule, not from an unconstrained package-index version. External
66
- environments may install from a pinned package release or directly from GitHub,
67
- depending on deployment policy.
72
+ Devkit is released independently as an immutable wheel. X-4D, XPoints, and
73
+ other consumers must install one exact package version during image builds.
74
+ Runtime source copies, submodule installs, editable installs, VCS `main`
75
+ installs, and fallback dependency paths are not supported deployment modes.
76
+ Use a published prerelease for coordinated cross-repository development.
68
77
 
69
78
  Optional heavy dependencies:
70
79
 
@@ -77,6 +86,8 @@ pip install x4d-devkit[inference]
77
86
  ```
78
87
 
79
88
  The platform API client and `x4d-devkit` CLI are base package features.
89
+ See [the release process](docs/release-process.md) for the immutable artifact
90
+ and consumer cutover rules.
80
91
 
81
92
  ## Scope
82
93
 
@@ -88,24 +99,27 @@ platform-to-platform data flow. See
88
99
  [docs/current-state-identity.md](docs/current-state-identity.md) for the
89
100
  generic identity model, and
90
101
  [docs/training-current-state-data.md](docs/training-current-state-data.md) for
91
- diff/sync, work-session materialization, training manifests, and conditional
92
- annotation writeback flows built on that model.
102
+ work-session materialization and conditional annotation writeback. Label-schema
103
+ bodies are resolved from X-4D and passed explicitly at every clip loading and
104
+ validation boundary.
93
105
  Shared validation rules for platform submit, X-Points preflight, local clip
94
106
  validation, and training consumers are documented in
95
107
  [docs/shared-contract-validation.md](docs/shared-contract-validation.md).
108
+ Portable export producers and consumers share the exact `0.11` manifest
109
+ parser documented in
110
+ [docs/export-bundle-contract.md](docs/export-bundle-contract.md).
96
111
  `examples/current_state_identity_consumers.py` provides an offline downstream
97
- fixture for OpenPCDet-style, mmdetection3d-style, and annotation/review
98
- consumers.
112
+ fixture for read-only, read-write, and materialization consumers.
99
113
 
100
114
  Included:
101
115
 
102
116
  - Local X-4D dataset loading, validation, transforms, annotations, calibration,
103
- ego pose, manifests, converters, and evaluation helpers.
117
+ ego pose, converters, and evaluation helpers.
104
118
  - Platform API client and public `x4d-devkit` CLI commands for external
105
119
  workflows such as clip listing/download, screening preview, model/checkpoint
106
120
  registration, status queries, and capability discovery.
107
- - Training bridge workflows such as multi-project manifests, archive download,
108
- split validation, and dataloader-friendly metadata.
121
+ - Strict validation of `x4d-training-identity-v1` manifests.
122
+ - Strict producer-option and manifest parsing for export bundle `0.11`.
109
123
  - External inference service wrappers for third-party model-serving projects.
110
124
 
111
125
  Excluded:
@@ -166,93 +180,27 @@ complete clip payload including `sweeps/`. If the requested profile has not been
166
180
  built, is stale, or failed to build, the devkit reports that profile-specific
167
181
  reason instead of falling back to another tar.
168
182
 
169
- ### Create a multi-project training manifest
183
+ ### Validate a training identity manifest
170
184
 
171
- Training projects can combine multiple X-4D projects by downloading standard
172
- clip archives and generating one local manifest. The downstream training
173
- framework consumes only the manifest and local clip files; it does not need
174
- database, MinIO, or platform-private file paths.
185
+ The `manifest` command has one contract and one operation. It validates an
186
+ exact `x4d-training-identity-v1` record; it does not create datasets, download
187
+ clips, or translate earlier manifest shapes.
175
188
 
176
189
  ```bash
177
- x4d-devkit manifest create \
178
- --projects hg1.0 hg1.5 \
179
- --data-root /data/x4d/cache/hg_mix \
180
- --output /data/x4d/cache/hg_mix/manifest.json \
181
- --download-missing \
182
- --archive-profile keyframes_only \
183
- --split-policy by_clip \
184
- --val-ratio 0.2 \
185
- --seed 42
186
-
187
190
  x4d-devkit manifest validate \
188
- --manifest /data/x4d/cache/hg_mix/manifest.json \
189
- --data-root /data/x4d/cache/hg_mix
190
- ```
191
-
192
- Internal current-state consumers can snapshot remote clip identity before
193
- reviewing diffs or syncing local caches:
194
-
195
- ```bash
196
- x4d-devkit dataset inspect \
197
- --project-name hg1.5 \
198
- --include-asset-revision \
199
- --out remote_identity.json
200
- ```
201
-
202
- Training runs can validate their consumed-data identity record:
203
-
204
- ```bash
205
- x4d-devkit manifest identity-validate --manifest training_identity.json
206
- ```
207
-
208
- Dataset update planning can also compare consumer-owned training identity
209
- fields, such as split membership, class-mapping fingerprint, data policy, or a
210
- generic consumer identity fingerprint:
211
-
212
- ```bash
213
- x4d-devkit dataset diff \
214
- --local-manifest current_training_identity.json \
215
- --previous-training-identity previous_training_identity.json \
216
- --out update_plan.json
217
- ```
218
-
219
- `manifest validate` prints bounded warning output by default. It reports
220
- warning totals grouped by type, category, and project/category, then shows a
221
- small set of examples. Use `--max-warnings`, `--all-warnings`, or
222
- `--warnings-output warnings.jsonl` when full warning details are needed.
223
-
224
- Downloaded clips are stored under
225
- `projects/<project-name>/clips/<clip-id>/`. Manifest file paths are relative to
226
- `--data-root`, so the dataset can be moved as a directory.
227
-
228
- The platform is expected to assign globally unique `clip_id` values. The devkit
229
- validator checks duplicate clip identities and split leakage, but it does not
230
- perform expensive semantic near-duplicate detection across projects.
231
-
232
- ```python
233
- from x4d_devkit import X4DClient
234
-
235
- client = X4DClient.from_config()
236
- manifest = client.create_training_manifest(
237
- projects=["hg1.0", "hg1.5"],
238
- data_root="/data/x4d/cache/hg_mix",
239
- output="/data/x4d/cache/hg_mix/manifest.json",
240
- download_missing=True,
241
- split_policy="by_clip",
242
- val_ratio=0.2,
243
- seed=42,
244
- )
191
+ --manifest training_identity.json \
192
+ --format json
245
193
  ```
246
194
 
247
- The detection manifest keeps boxes in their native X-4D annotation frame
248
- (`meta.sensors[meta.annotation_source_channel].frame_id`). It does not align
249
- poses across clips; each clip's `clip_world` remains local to that clip.
195
+ Unsupported versions, removed aliases, and malformed clip/schema identities
196
+ fail validation. Producers own manifest construction and must emit the current
197
+ contract directly.
250
198
 
251
199
  ### Consume the project label schema
252
200
 
253
- X-4D is the source of truth for label schemas. External training and inference
254
- projects should read the effective project schema from the platform instead of
255
- maintaining their own category list.
201
+ X-4D is the only source of truth for label-schema bodies. Consumers resolve the
202
+ effective project record from the platform instead of maintaining their own
203
+ category list or copying the record into each clip.
256
204
 
257
205
  ```bash
258
206
  x4d-devkit label-schemas project-get --project-id 1 --format json
@@ -269,11 +217,13 @@ from x4d_devkit.label_schema import compare_project_label_schema_snapshot
269
217
  client = X4DClient.from_config()
270
218
  project = client.projects.resolve(project_name="nuscenes-mini")
271
219
  project_schema = client.label_schemas.resolve_project(project_name="nuscenes-mini")
220
+ project_schema_view = client.label_schemas.get_project_view(project["id"])
272
221
  label_schema = project_schema.label_schema
273
222
 
274
223
  print(project["id"], project["name"])
275
224
  print(project_schema.binding.to_dict())
276
225
  print(sorted(label_schema.class_ids))
226
+ print([category["id"] for category in project_schema_view.to_dict()["categories"]])
277
227
 
278
228
  label_schema.validate_category("tanker_truck")
279
229
 
@@ -283,40 +233,35 @@ comparison = compare_project_label_schema_snapshot(stored_snapshot, current)
283
233
  assert comparison.status == "same"
284
234
  ```
285
235
 
286
- The registry schema is the fine-grained annotation contract. Training code may
287
- still define experiment-specific class mappings, but those mappings should be
288
- validated against the project schema before a job starts.
289
-
290
- Training manifests keep the immutable project contract separate from the
291
- observed/configured class catalog:
236
+ The native `0.11` clip stores only the immutable identity in
237
+ `meta.label_schema`. It must not contain `label_schema.json`. The complete
238
+ registry record is explicit runtime context for `ClipLoader`, `validate_clip`,
239
+ conversion, and work-session parsing; its identity must exactly match
240
+ `meta.label_schema`.
292
241
 
293
- ```text
294
- label_schema.projects.<project>.contract # binding + full registry record
295
- label_schema.projects.<project>.class_catalog # training/observed class catalog
296
- ```
242
+ Training code may still define experiment-specific class mappings, but those
243
+ mappings must be validated against the resolved project record before use.
297
244
 
298
- Consumers must parse `contract` with
299
- `parse_project_label_schema_snapshot()`. `class_catalog` is
300
- non-authoritative observation metadata; there are no per-project
301
- `classes`, `source`, or `schema_version` aliases.
245
+ ### Inspect or diff current state
302
246
 
303
- This strict parser/view contract is introduced in x4d-devkit `0.17.0`.
304
- Consumers tracking `main` should still record `get_build_info()["source"]` in
305
- training identity so an untagged development run remains reproducible by exact
306
- commit. Deployments that copy a source tree without its Git directory must set
307
- `X4D_DEVKIT_COMMIT` to the copied 40- or 64-hex commit; `get_build_info()` records
308
- that value with `commit_source=environment`.
247
+ Dataset identity clients, local manifests, portable cache identities, remote
248
+ identity manifests, and update plans use one schema field only:
249
+ `label_schema`, containing the complete
250
+ `x4d-label-schema-identity-v1` object. `label_schema_fingerprint` and
251
+ `clip_label_schema` are not aliases and are rejected. A ready platform record
252
+ without the complete identity is also rejected.
309
253
 
310
- Project resolution by name/key is exact and handles platform pagination
311
- internally; ambiguous or missing project references raise structured resolver
312
- errors instead of requiring callers to scan project lists themselves.
254
+ `x4d-devkit dataset inspect` writes `x4d-remote-identity-v2`, and
255
+ `x4d-devkit dataset diff` writes `x4d-update-plan-v2`. These artifacts contain
256
+ normalized clip records once; they do not embed a duplicate raw platform
257
+ response. V1 artifacts are rejected rather than migrated.
313
258
 
314
259
  ### Load a clip
315
260
 
316
261
  ```python
317
262
  from x4d_devkit import ClipLoader
318
263
 
319
- loader = ClipLoader("/path/to/clip")
264
+ loader = ClipLoader("/path/to/clip", label_schema=label_schema)
320
265
  print(loader.meta)
321
266
 
322
267
  for sample in loader.samples:
@@ -340,7 +285,7 @@ materialize_work_session_clip(
340
285
  manifest,
341
286
  output_dir="/data/x4d_cache/clips/clip-id",
342
287
  client=client,
343
- asset_policy="keyframes_only",
288
+ materialization_policy="keyframes_only",
344
289
  )
345
290
 
346
291
  loader = ClipLoader.from_work_session(manifest, api_url=client.api_url)
@@ -359,20 +304,21 @@ except WorkSessionConflictError as exc:
359
304
  raise
360
305
  ```
361
306
 
362
- Materialization preserves both schema facts atomically: the clip's recorded
363
- identity remains in `meta.label_schema`, while the effective full registry
364
- record is written to `label_schema.json`. Full and annotation-only refreshes
365
- write the same schema snapshot, so local validation does not require a live
366
- registry lookup.
307
+ The work-session envelope may carry the complete registry record so
308
+ `ClipLoader.from_work_session()` can validate without another request. That
309
+ record is transient request context, not a second persisted schema source.
310
+ Materialization writes the native tables with the identity in
311
+ `meta.label_schema` and never writes `label_schema.json`. Loading or validating
312
+ the materialized directory later therefore requires the caller to resolve and
313
+ pass the complete registry record explicitly.
367
314
 
368
- A ready work session is accepted only when `label_schema_fingerprint`, the full
369
- registry record, `clip_label_schema`, and `meta.label_schema` declare the same
370
- identity. A not-ready session may expose a stale clip identity alongside the
371
- current project schema so migration tooling can report the mismatch, but it
372
- cannot be materialized.
315
+ A ready work session is accepted only when its full registry record,
316
+ `clip_label_schema`, and the resulting native `meta.label_schema` declare the
317
+ same identity. A mismatch fails closed; there is no legacy work-session shape
318
+ or schema-file fallback.
373
319
 
374
320
  Submit is conditional on the manifest's `source_revision`,
375
- `annotation_revision`, and `label_schema_fingerprint`. If the platform state
321
+ `annotation_revision`, and `base_label_schema_fingerprint`. If the platform state
376
322
  changed after the session was opened, the client raises
377
323
  `WorkSessionConflictError` with machine-readable `conflicts`,
378
324
  `base_identities`, and `current_identities`.
@@ -405,7 +351,7 @@ from x4d_devkit import COORDINATE_CONTRACT_VERSION, get_training_coordinate_cont
405
351
  from x4d_devkit import ClipLoader
406
352
  from x4d_devkit.core.loader import CLIP_WORLD_FRAME_ID
407
353
 
408
- loader = ClipLoader("/path/to/clip")
354
+ loader = ClipLoader("/path/to/clip", label_schema=label_schema)
409
355
  sd = loader.sample_data_for_channel("LIDAR_TOP")[0]
410
356
 
411
357
  # Load point cloud in different frames
@@ -427,16 +373,20 @@ pts_world = T.apply(pts_sensor[:, :3]) # or use T.as_matrix for 4x4
427
373
  ### Validate a clip
428
374
 
429
375
  ```bash
430
- x4d-devkit validate /path/to/clip
376
+ x4d-devkit validate /path/to/clip --label-schema project_schema_record.json
431
377
  ```
432
378
 
433
379
  ```python
434
380
  from x4d_devkit import validate_clip
435
381
 
436
- report = validate_clip("/path/to/clip")
382
+ report = validate_clip("/path/to/clip", label_schema=label_schema)
437
383
  print(report)
438
384
  ```
439
385
 
386
+ For a remote clip, call `validate_clip_payload(..., asset_sizes=...)` with a
387
+ mapping of clip-relative object paths to byte sizes. This applies the same
388
+ asset existence and point-cloud layout checks without downloading the assets.
389
+
440
390
  ### Serve an external inference backend
441
391
 
442
392
  Third-party model projects should use `x4d-devkit[inference]` instead of
@@ -464,7 +414,11 @@ class MyDetector(DetectionModel):
464
414
  ]
465
415
 
466
416
 
467
- InferenceService(service_name="my-detector", models=[MyDetector()]).run(port=9000)
417
+ InferenceService(
418
+ service_name="my-detector",
419
+ models=[MyDetector()],
420
+ clip_resolver=resolve_clip,
421
+ ).run(port=9000)
468
422
  ```
469
423
 
470
424
  See [docs/external-inference.md](docs/external-inference.md) and
@@ -519,6 +473,20 @@ converter = NuScenesConverter("/path/to/nuscenes")
519
473
  converter.convert_scene("scene-0001", output_dir="/path/to/output")
520
474
  ```
521
475
 
476
+ ## Development
477
+
478
+ Development and verification run in the repository-owned Docker environment.
479
+ A host Python installation is not part of the project workflow.
480
+
481
+ ```bash
482
+ make verify
483
+ ```
484
+
485
+ Use `make test`, `make lint`, `make docs`, `make package`, or `make shell` for
486
+ individual tasks. Dependency changes are resolved and locked with `make lock`.
487
+ See [the development environment guide](docs/development.md) for the ownership
488
+ boundary and complete workflow.
489
+
522
490
  ## Modules
523
491
 
524
492
  | Module | Description |
@@ -526,7 +494,7 @@ converter.convert_scene("scene-0001", output_dir="/path/to/output")
526
494
  | `core` | Data models, token generation, coordinate transforms, clip loader |
527
495
  | `eval` | Detection evaluation (mAP, TP metrics, NDS) |
528
496
  | `converters` | Format converters (NuScenes → X4D) |
529
- | `manifest` | Training manifest creation, validation, and clip archive download |
497
+ | `training_identity` | Strict `x4d-training-identity-v1` parsing and validation |
530
498
  | `validation` | Clip structure and data validation |
531
499
  | `client` | X-4D platform API client |
532
500