x4d-devkit 0.22.0rc1__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 (98) hide show
  1. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/PKG-INFO +16 -2
  2. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/README.md +15 -1
  3. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/pyproject.toml +4 -1
  4. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_contract_validation.py +30 -0
  5. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_sdk_api.py +1 -1
  6. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_validation.py +31 -0
  7. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/validation/contract.py +19 -2
  8. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/validation/validator.py +31 -1
  9. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit.egg-info/PKG-INFO +16 -2
  10. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/LICENSE +0 -0
  11. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/setup.cfg +0 -0
  12. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_assets_client.py +0 -0
  13. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_boundary.py +0 -0
  14. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_box.py +0 -0
  15. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_build_info.py +0 -0
  16. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_cli_auth.py +0 -0
  17. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_client_json_contract.py +0 -0
  18. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_client_projects.py +0 -0
  19. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_clip_archives.py +0 -0
  20. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_coordinate_frames.py +0 -0
  21. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_current_state.py +0 -0
  22. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_current_state_identity_consumers_example.py +0 -0
  23. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_dataset_contract.py +0 -0
  24. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_dataset_identity.py +0 -0
  25. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_detection_eval.py +0 -0
  26. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_evaluate.py +0 -0
  27. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_export_bundle.py +0 -0
  28. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_inference_builders.py +0 -0
  29. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_inference_schemas.py +0 -0
  30. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_inference_service.py +0 -0
  31. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_label_schemas.py +0 -0
  32. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_loader.py +0 -0
  33. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_matching.py +0 -0
  34. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_metrics.py +0 -0
  35. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_models.py +0 -0
  36. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_nuscenes_converter.py +0 -0
  37. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_platform_pose.py +0 -0
  38. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_release_gate.py +0 -0
  39. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_token.py +0 -0
  40. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_training_identity.py +0 -0
  41. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_transform.py +0 -0
  42. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_transform_chain.py +0 -0
  43. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/tests/test_work_session.py +0 -0
  44. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/__init__.py +0 -0
  45. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/build_info.py +0 -0
  46. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/cli.py +0 -0
  47. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/__init__.py +0 -0
  48. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/_origin.py +0 -0
  49. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/assets.py +0 -0
  50. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/bags.py +0 -0
  51. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/capabilities.py +0 -0
  52. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/checkpoints.py +0 -0
  53. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/client.py +0 -0
  54. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/clip_work_sessions.py +0 -0
  55. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/clips.py +0 -0
  56. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/dataset_identity.py +0 -0
  57. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/label_schemas.py +0 -0
  58. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/projects.py +0 -0
  59. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/services.py +0 -0
  60. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/client/test_sets.py +0 -0
  61. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/consumer_identity.py +0 -0
  62. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/converters/__init__.py +0 -0
  63. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/converters/nuscenes.py +0 -0
  64. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/coordinate_contract.py +0 -0
  65. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/core/__init__.py +0 -0
  66. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/core/loader.py +0 -0
  67. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/core/models.py +0 -0
  68. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/core/token.py +0 -0
  69. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/core/transform.py +0 -0
  70. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/current_state.py +0 -0
  71. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/dataset_contract.py +0 -0
  72. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/dataset_identity.py +0 -0
  73. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/eval/__init__.py +0 -0
  74. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/eval/api.py +0 -0
  75. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/eval/box.py +0 -0
  76. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/eval/detection.py +0 -0
  77. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/eval/matching.py +0 -0
  78. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/eval/metrics.py +0 -0
  79. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/export_bundle.py +0 -0
  80. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/inference/__init__.py +0 -0
  81. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/inference/builders.py +0 -0
  82. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/inference/models.py +0 -0
  83. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/inference/schemas.py +0 -0
  84. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/inference/service.py +0 -0
  85. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/inference/tasks.py +0 -0
  86. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/json_contract.py +0 -0
  87. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/label_schema.py +0 -0
  88. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/materialize.py +0 -0
  89. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/training_identity.py +0 -0
  90. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/validation/__init__.py +0 -0
  91. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/validation/platform_pose.py +0 -0
  92. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/validation/report.py +0 -0
  93. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit/work_session.py +0 -0
  94. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit.egg-info/SOURCES.txt +0 -0
  95. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit.egg-info/dependency_links.txt +0 -0
  96. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit.egg-info/entry_points.txt +0 -0
  97. {x4d_devkit-0.22.0rc1 → x4d_devkit-0.22.0rc2}/x4d_devkit.egg-info/requires.txt +0 -0
  98. {x4d_devkit-0.22.0rc1 → 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.22.0rc1
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
@@ -66,7 +66,7 @@ Design decisions for this boundary are tracked in GitHub issues, starting with:
66
66
  ## Installation
67
67
 
68
68
  ```bash
69
- pip install x4d-devkit==0.22.0rc1
69
+ pip install x4d-devkit==0.22.0rc2
70
70
  ```
71
71
 
72
72
  Devkit is released independently as an immutable wheel. X-4D, XPoints, and
@@ -473,6 +473,20 @@ converter = NuScenesConverter("/path/to/nuscenes")
473
473
  converter.convert_scene("scene-0001", output_dir="/path/to/output")
474
474
  ```
475
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
+
476
490
  ## Modules
477
491
 
478
492
  | Module | Description |
@@ -23,7 +23,7 @@ Design decisions for this boundary are tracked in GitHub issues, starting with:
23
23
  ## Installation
24
24
 
25
25
  ```bash
26
- pip install x4d-devkit==0.22.0rc1
26
+ pip install x4d-devkit==0.22.0rc2
27
27
  ```
28
28
 
29
29
  Devkit is released independently as an immutable wheel. X-4D, XPoints, and
@@ -430,6 +430,20 @@ converter = NuScenesConverter("/path/to/nuscenes")
430
430
  converter.convert_scene("scene-0001", output_dir="/path/to/output")
431
431
  ```
432
432
 
433
+ ## Development
434
+
435
+ Development and verification run in the repository-owned Docker environment.
436
+ A host Python installation is not part of the project workflow.
437
+
438
+ ```bash
439
+ make verify
440
+ ```
441
+
442
+ Use `make test`, `make lint`, `make docs`, `make package`, or `make shell` for
443
+ individual tasks. Dependency changes are resolved and locked with `make lock`.
444
+ See [the development environment guide](docs/development.md) for the ownership
445
+ boundary and complete workflow.
446
+
433
447
  ## Modules
434
448
 
435
449
  | Module | Description |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "x4d-devkit"
7
- version = "0.22.0rc1"
7
+ version = "0.22.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"
@@ -58,3 +58,6 @@ exclude = ["site*", "tests*", "docs*", "examples*"]
58
58
 
59
59
  [tool.pytest.ini_options]
60
60
  testpaths = ["tests"]
61
+
62
+ [tool.uv]
63
+ required-version = "==0.9.20"
@@ -291,6 +291,36 @@ def test_final_sensor_coverage_requires_every_declared_channel():
291
291
  assert "incomplete_sensor_coverage" in _codes(report)
292
292
 
293
293
 
294
+ def test_final_sensor_coverage_does_not_count_sweeps():
295
+ meta = _meta()
296
+ meta["sensors"]["cam_front"] = {"modality": "camera", "frame_id": "cam_front"}
297
+ sweep = _sample_data()
298
+ sweep.update(
299
+ sample_data_token="cam-sweep",
300
+ calibrated_sensor_token="cs-camera",
301
+ channel="cam_front",
302
+ file_path="sweeps/cam_front/1.jpg",
303
+ is_keyframe=False,
304
+ width=640,
305
+ height=480,
306
+ )
307
+
308
+ report = validate_clip_contract(_context(meta=meta, sample_data=[_sample_data(), sweep]))
309
+
310
+ assert "incomplete_sensor_coverage" in _codes(report)
311
+
312
+
313
+ def test_annotation_must_reference_a_keyframe_sample():
314
+ context = _context()
315
+ context.samples[0]["is_keyframe"] = False
316
+ context.sample_data[0]["is_keyframe"] = False
317
+ context.sample_data[0]["file_path"] = "sweeps/lidar/1.bin"
318
+
319
+ report = validate_clip_contract(context)
320
+
321
+ assert "annotation_sample_not_keyframe" in _codes(report)
322
+
323
+
294
324
  def test_context_rejects_non_list_payloads_and_invalid_meta():
295
325
  with pytest.raises(TypeError):
296
326
  validate_annotation_payload(_context(), annotations=(), instances=[])
@@ -12,7 +12,7 @@ from x4d_devkit.eval.matching import MetricData
12
12
 
13
13
 
14
14
  def test_package_version_is_current_release():
15
- assert x4d_devkit.__version__ == "0.22.0rc1"
15
+ assert x4d_devkit.__version__ == "0.22.0rc2"
16
16
 
17
17
 
18
18
  def test_loader_exposes_and_enforces_only_current_schema(tmp_path):
@@ -223,3 +223,34 @@ def test_explicit_schema_context_must_match_meta_identity():
223
223
 
224
224
  assert not report.ok
225
225
  assert "label_schema_identity_mismatch" in _codes(report)
226
+
227
+
228
+ def test_camera_frame_must_not_be_shared_with_point_cloud_sensor():
229
+ payload = current_clip_tables(include_camera=True, include_fused=True)
230
+ payload["meta.json"]["sensors"]["cam_front"]["frame_id"] = "base_link"
231
+ payload["calibrated_sensor.json"] = [
232
+ row
233
+ for row in payload["calibrated_sensor.json"]
234
+ if row["channel"] != "cam_front"
235
+ ]
236
+ for row in payload["sample_data.json"]:
237
+ if row["channel"] == "cam_front":
238
+ row["calibrated_sensor_token"] = "cs-root"
239
+
240
+ report = _validate_payload(payload)
241
+
242
+ assert not report.ok
243
+ assert "calibration_shape" in _codes(report)
244
+
245
+
246
+ def test_camera_sample_dimensions_must_match_calibration_intrinsics():
247
+ payload = current_clip_tables(include_camera=True)
248
+ camera = next(
249
+ row for row in payload["sample_data.json"] if row["channel"] == "cam_front"
250
+ )
251
+ camera["width"] = 320
252
+
253
+ report = _validate_payload(payload)
254
+
255
+ assert not report.ok
256
+ assert "sample_data_shape" in _codes(report)
@@ -370,9 +370,15 @@ def validate_annotation_payload(
370
370
  _validate_context(context, builder)
371
371
 
372
372
  sample_tokens = {
373
- _strict_token(row.get("sample_token"))
373
+ token
374
374
  for row in context.samples
375
- if _strict_token(row.get("sample_token"))
375
+ if (token := _strict_token(row.get("sample_token"))) is not None
376
+ }
377
+ keyframe_sample_tokens = {
378
+ token
379
+ for row in context.samples
380
+ if row.get("is_keyframe") is True
381
+ if (token := _strict_token(row.get("sample_token"))) is not None
376
382
  }
377
383
  class_map = class_rule_map(context.label_schema)
378
384
 
@@ -428,6 +434,15 @@ def validate_annotation_payload(
428
434
  related_token=sample_token,
429
435
  details={"sample_token": sample_token},
430
436
  )
437
+ elif sample_token not in keyframe_sample_tokens:
438
+ builder.issue(
439
+ "annotation_sample_not_keyframe",
440
+ f"{path}.sample_token",
441
+ "annotation sample_token must identify a keyframe sample",
442
+ row=ann,
443
+ related_token=sample_token,
444
+ details={"sample_token": sample_token},
445
+ )
431
446
 
432
447
  if not instance_token:
433
448
  builder.issue(
@@ -1401,6 +1416,8 @@ def _validate_sensor_coverage(builder: _ReportBuilder, context: ClipValidationCo
1401
1416
 
1402
1417
  channels_by_sample: dict[str, set[str]] = {}
1403
1418
  for row in context.sample_data:
1419
+ if row.get("is_keyframe") is not True:
1420
+ continue
1404
1421
  sample_token = _strict_token(row.get("sample_token"))
1405
1422
  channel = _strict_token(row.get("channel"))
1406
1423
  if sample_token and channel:
@@ -401,7 +401,14 @@ def _validate_calibrated_sensors(
401
401
  intrinsics = row.get("intrinsics")
402
402
  distortion = row.get("distortion")
403
403
  frame_modality = _modality_for_frame(meta, channel)
404
- if frame_modality == "camera":
404
+ if frame_modality == "ambiguous":
405
+ _append_error(
406
+ issues,
407
+ "calibration_shape",
408
+ "a camera frame must not be shared with a non-camera sensor",
409
+ f"{path}.channel",
410
+ )
411
+ elif frame_modality == "camera":
405
412
  _validate_camera_calibration(
406
413
  intrinsics,
407
414
  distortion,
@@ -612,6 +619,29 @@ def _validate_sample_data(
612
619
  f"camera {field} must be a positive integer",
613
620
  f"{path}.{field}",
614
621
  )
622
+ calibration = calibrated_sensors.get(calibrated_token)
623
+ intrinsics = (
624
+ calibration.get("intrinsics")
625
+ if isinstance(calibration, dict)
626
+ else None
627
+ )
628
+ if isinstance(intrinsics, dict):
629
+ expected_dimensions = {
630
+ "width": intrinsics.get("image_width"),
631
+ "height": intrinsics.get("image_height"),
632
+ }
633
+ actual_dimensions = {"width": width, "height": height}
634
+ if actual_dimensions != expected_dimensions:
635
+ _append_error(
636
+ issues,
637
+ "sample_data_shape",
638
+ "camera dimensions must match resolved calibration intrinsics",
639
+ path,
640
+ {
641
+ "expected": expected_dimensions,
642
+ "actual": actual_dimensions,
643
+ },
644
+ )
615
645
  elif width is not None or height is not None:
616
646
  _append_error(
617
647
  issues,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: x4d-devkit
3
- Version: 0.22.0rc1
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
@@ -66,7 +66,7 @@ Design decisions for this boundary are tracked in GitHub issues, starting with:
66
66
  ## Installation
67
67
 
68
68
  ```bash
69
- pip install x4d-devkit==0.22.0rc1
69
+ pip install x4d-devkit==0.22.0rc2
70
70
  ```
71
71
 
72
72
  Devkit is released independently as an immutable wheel. X-4D, XPoints, and
@@ -473,6 +473,20 @@ converter = NuScenesConverter("/path/to/nuscenes")
473
473
  converter.convert_scene("scene-0001", output_dir="/path/to/output")
474
474
  ```
475
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
+
476
490
  ## Modules
477
491
 
478
492
  | Module | Description |
File without changes
File without changes