simulo-interfaces 0.12.0__tar.gz → 0.13.0__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 (55) hide show
  1. {simulo_interfaces-0.12.0/src/simulo_interfaces.egg-info → simulo_interfaces-0.13.0}/PKG-INFO +1 -1
  2. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/pyproject.toml +1 -1
  3. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/authoring/record.py +3 -3
  4. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/platform/artifacts.py +3 -1
  5. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/platform/asset_catalog.py +57 -49
  6. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/platform/runs.py +39 -5
  7. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/platform/submit.py +37 -4
  8. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0/src/simulo_interfaces.egg-info}/PKG-INFO +1 -1
  9. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/MANIFEST.in +0 -0
  10. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/PYPI.md +0 -0
  11. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/setup.cfg +0 -0
  12. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/__init__.py +0 -0
  13. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/authoring/__init__.py +0 -0
  14. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/authoring/actuator.py +0 -0
  15. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/authoring/asset.py +0 -0
  16. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/authoring/entity.py +0 -0
  17. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/authoring/light.py +0 -0
  18. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/authoring/materials.py +0 -0
  19. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/authoring/physics.py +0 -0
  20. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/authoring/pose.py +0 -0
  21. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/authoring/primitives.py +0 -0
  22. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/authoring/prop.py +0 -0
  23. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/authoring/robot.py +0 -0
  24. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/authoring/scene.py +0 -0
  25. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/authoring/sensor.py +0 -0
  26. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/authoring/terrain.py +0 -0
  27. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/authoring/visual.py +0 -0
  28. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/authoring/world.py +0 -0
  29. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/exceptions.py +0 -0
  30. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/ids.py +0 -0
  31. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/platform/__init__.py +0 -0
  32. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/platform/app.py +0 -0
  33. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/platform/asset.py +0 -0
  34. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/platform/callbacks.py +0 -0
  35. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/platform/debug.py +0 -0
  36. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/platform/domain.py +0 -0
  37. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/platform/enums.py +0 -0
  38. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/platform/manifest.py +0 -0
  39. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/platform/runtime.py +0 -0
  40. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/platform/volume.py +0 -0
  41. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/py.typed +0 -0
  42. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/runtime/__init__.py +0 -0
  43. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/runtime/anomaly.py +0 -0
  44. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/runtime/components.py +0 -0
  45. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/runtime/env.py +0 -0
  46. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/runtime/player.py +0 -0
  47. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/runtime/policy.py +0 -0
  48. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/runtime/scenario.py +0 -0
  49. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/runtime/task.py +0 -0
  50. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/runtime/tensors.py +0 -0
  51. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo/interfaces/runtime/trainer.py +0 -0
  52. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo_interfaces.egg-info/SOURCES.txt +0 -0
  53. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo_interfaces.egg-info/dependency_links.txt +0 -0
  54. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo_interfaces.egg-info/requires.txt +0 -0
  55. {simulo_interfaces-0.12.0 → simulo_interfaces-0.13.0}/src/simulo_interfaces.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simulo-interfaces
3
- Version: 0.12.0
3
+ Version: 0.13.0
4
4
  Summary: Type contracts for the Simulo SDK. Installed automatically as a dependency of the simulo package.
5
5
  Author-email: Simulo Team <team@simulo.ai>
6
6
  License: BSD-3-Clause
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "simulo-interfaces"
7
- version = "0.12.0"
7
+ version = "0.13.0"
8
8
  description = "Type contracts for the Simulo SDK. Installed automatically as a dependency of the simulo package."
9
9
  readme = "PYPI.md"
10
10
  requires-python = ">=3.11"
@@ -88,10 +88,10 @@ ProfileName = Literal["core", "standard", "full"]
88
88
  VALID_PROFILES: Tuple[str, ...] = ("core", "standard", "full")
89
89
 
90
90
  # Profile defaults table. Each profile lists ONLY the fields it turns on; any
91
- # field absent from a profile's dict defaults to False. SRD §10.1 mandates
91
+ # field absent from a profile's dict defaults to False. MCAP recording mandates
92
92
  # Phase 1 robot/contact/sensor/video stay off; the `full` row below holds the
93
93
  # line by not enabling those streams in Phase 1. Wave 2 may flip them on
94
- # (plan §6 Wave 2 task F) — that edit is intentionally scoped to this table.
94
+ # when enabled — that edit is intentionally scoped to this table.
95
95
  #
96
96
  # Wave Policy-Eval-Completeness (2026-06-21) — three changes to defaults:
97
97
  #
@@ -161,7 +161,7 @@ _PROFILE_DEFAULTS: Dict[str, Dict[str, bool]] = {
161
161
  "include_terminations": True,
162
162
  "include_events": True,
163
163
  # Wave 2 — Phase 2 streams enabled in the `full` profile per plan
164
- # §6 Wave 2 task F + SRD §7 line 409. Phase 3 streams (video, depth,
164
+ # The full profile includes Phase 2 streams. Phase 3 streams (video, depth,
165
165
  # lidar) and `include_collisions` stay opt-in even under `full` to
166
166
  # avoid surprising users with high-volume captures.
167
167
  "include_robot_state": True,
@@ -133,7 +133,9 @@ and the narrow ``job_artifacts`` table (everything registered via
133
133
  appears in the union **exactly once**. The projection reuses the
134
134
  platform-standard paginated envelope (``items``/``total``/``page``/``limit``/
135
135
  ``pages``; default ``limit=20``, max 100, out-of-range → 422) and the standard
136
- error shape.
136
+ error shape — see :mod:`simulo.interfaces.platform.runs` for the full
137
+ contract, including the ``pages >= 1`` invariant that holds even for
138
+ ``total == 0``.
137
139
  """
138
140
 
139
141
  from collections.abc import Mapping
@@ -1,8 +1,8 @@
1
- """USD Asset Catalogs wire contract (USD Asset Catalogs Wave, PR-1).
1
+ """USD asset-catalog wire contract.
2
2
 
3
3
  This is the **canonical, single source of truth** for the reference grammar, the
4
4
  route constants, the size caps, the wire dataclasses, and the reserved-word /
5
- role / verdict enumerations that carry a USD *asset* (a versioned robot or world
5
+ role / verdict enumerations that carry a USD *asset* (a versioned robot, world, or prop
6
6
  package) from the laptop client to the cloud catalog and back into a job. It
7
7
  lands **before any consumer** — the control plane (customer, ``/admin/*`` and
8
8
  ``/internal/worker/*`` routes), the thin ``simulo`` client (``simulo asset ...``
@@ -28,8 +28,8 @@ stay decoupled (no import cycle). When you want the ref grammar, routes, or a
28
28
  wire record, reach here; when you want the mounted-handle protocol, reach
29
29
  ``simulo.interfaces.platform.asset``.
30
30
 
31
- The design intent behind every shape below follows the USD Asset Catalogs UX
32
- proposal; the frozen wire specifics are transcribed from the API contract-freeze doc.
31
+ The constants, parsers, dataclasses, and tests in this module own the exact
32
+ cross-package wire shape.
33
33
 
34
34
  ## Two catalogs, one grammar
35
35
 
@@ -37,15 +37,15 @@ proposal; the frozen wire specifics are transcribed from the API contract-freeze
37
37
 
38
38
  * **publisher** — omitted means *the caller's own organization catalog*.
39
39
  ``simulo`` is the reserved publisher for the read-only **global** catalog.
40
- Publishers are organization slugs, so a future org-to-org share is a
41
- *permissions* change, not a grammar change — this module therefore accepts
40
+ Publishers are organization slugs. Sharing is an authorization concern, not
41
+ a grammar concern, so this module accepts
42
42
  any syntactically valid org slug in the publisher position (server-side
43
43
  permission checks decide what is actually *usable*), and rejects only the
44
44
  reserved segments that can never be org slugs (``builtin``/``nucleus``/
45
45
  ``local``/``platform``; ``simulo`` is allowed because it names the global
46
46
  catalog). See :data:`RESERVED_ASSET_PUBLISHERS` / :data:`GLOBAL_ASSET_PUBLISHER`.
47
47
  * **kind** — :data:`ASSET_KINDS` = ``("robot", "world", "prop")``. ``prop``
48
- joined the grammar in W7 — furniture/mount/fixture assets that are neither
48
+ covers furniture/mount/fixture assets that are neither
49
49
  a robot nor a full environment (e.g. ``prop/table:v1``).
50
50
  * **name** — an org/project **slug** (:data:`ASSET_SLUG_PATTERN`), unique per
51
51
  ``(publisher, kind)``. A reserved-publisher word (:data:`RESERVED_ASSET_PUBLISHERS`)
@@ -121,7 +121,10 @@ opaque asset-version id (:data:`ASSET_VERSION_ROUTE_TEMPLATE` subtree, whose
121
121
  latest is hidden by default, and additionally surfaced (with its
122
122
  ``deprecated`` :attr:`AssetRecord.latest_version_status`) when
123
123
  ``include_deprecated=true`` (``simulo asset list --deprecated``). Global rows
124
- are visible to any authenticated user.
124
+ are visible to any authenticated user. Returns the platform-standard
125
+ paginated envelope (``items``/``total``/``page``/``limit``/``pages``) — see
126
+ :mod:`simulo.interfaces.platform.runs` for the full contract, including the
127
+ ``pages >= 1`` invariant that holds even for ``total == 0``.
125
128
  * :data:`ASSET_ROUTE_TEMPLATE` (``GET`` ``?publisher=``) — one asset with
126
129
  description/tags + version history; (``PATCH``) — description/tags, the only
127
130
  mutable metadata.
@@ -215,17 +218,17 @@ from simulo.interfaces.ids import AssetId, AssetVersionId, Digest, ResourceUri
215
218
  # Reference grammar — kinds, reserved words, slug rule
216
219
  # --------------------------------------------------------------------------
217
220
 
218
- #: The asset kinds valid in the ref grammar today (§2). ``robot`` is an
221
+ #: The asset kinds valid in the ref grammar today. ``robot`` is an
219
222
  #: articulated system instantiated through the Simulo robot APIs; ``world`` is
220
223
  #: an environment / facility / terrain / scene robots operate in ("world", not
221
224
  #: "environment", because "environment" is overloaded — ``LearningEnv``, Python
222
225
  #: envs, deploy envs); ``prop`` (W7) is a furniture/mount/fixture asset that is
223
226
  #: neither a robot nor a full environment (e.g. a lab table or camera stand) —
224
- #: the additive kind §13 anticipated. Adding a further kind later is an
227
+ #: the additive kind added most recently. Adding a further kind later is an
225
228
  #: additive change to this constant.
226
229
  ASSET_KINDS = ("robot", "world", "prop")
227
230
 
228
- #: Publisher segments that can never be organization slugs (§2). All are rejected
231
+ #: Publisher segments that can never be organization slugs. All are rejected
229
232
  #: in the publisher position **except** :data:`GLOBAL_ASSET_PUBLISHER`
230
233
  #: (``"simulo"``), which names the read-only global catalog. They are also
231
234
  #: rejected as asset *names*, so no ref segment reads ambiguously as a catalog
@@ -234,8 +237,8 @@ ASSET_KINDS = ("robot", "world", "prop")
234
237
  #: catalog-grammar reservation.)
235
238
  RESERVED_ASSET_PUBLISHERS = frozenset({"simulo", "builtin", "nucleus", "local", "platform"})
236
239
 
237
- #: The one reserved publisher that IS a valid publisher: the global catalog
238
- #: (§2, §11). Omitting the publisher means the caller's own org; ``simulo`` means
240
+ #: The one reserved publisher that IS a valid publisher: the global catalog.
241
+ #: Omitting the publisher means the caller's own org; ``simulo`` means
239
242
  #: the global catalog; nothing else is a reserved-but-valid publisher.
240
243
  GLOBAL_ASSET_PUBLISHER = "simulo"
241
244
 
@@ -252,8 +255,8 @@ GLOBAL_ASSET_PUBLISHER = "simulo"
252
255
  #: inner-group bound verbatim, which has a length-2 hole: 1 char matches via the
253
256
  #: optional group being absent, 3+ chars matches via the group, but exactly 2
254
257
  #: chars can match neither — incoherent for users. Fixed here to ``{0,61}``,
255
- #: which accepts every length 1–63 contiguously; see the PR-1 resolutions in
256
- #: ``context/implementation-plans/archive/usd-asset-catalogs/api-contract-freeze.md``.)
258
+ #: which accepts every length 1–63 contiguously; see
259
+ #: ``context/reference/asset-catalog-contract.md`` "Reference parsing".)
257
260
  #: Pinned here (rather than imported) because ``simulo-interfaces`` has zero
258
261
  #: dependencies on the control plane — the value is copied deliberately and this
259
262
  #: docstring is the drift anchor.
@@ -452,7 +455,7 @@ def format_asset_ref(
452
455
 
453
456
  #: ``GET`` — list/search assets (``?scope=&kind=&q=&tag=``); labeled scopes.
454
457
  ASSETS_ROUTE = "/v1/assets"
455
- #: ``POST`` — batch submit-time pin resolver (``{refs, runtime}``); §6.
458
+ #: ``POST`` — batch submit-time pin resolver (``{refs, runtime}``).
456
459
  ASSETS_RESOLVE_ROUTE = "/v1/assets/resolve"
457
460
  #: ``GET`` — the org storage meter (per-asset bytes, org total, quota).
458
461
  ASSETS_USAGE_ROUTE = "/v1/assets/usage"
@@ -462,7 +465,7 @@ ASSETS_USAGE_ROUTE = "/v1/assets/usage"
462
465
  ASSET_ROUTE_TEMPLATE = "/v1/assets/{kind}/{name}"
463
466
  #: ``POST`` — initiate a publish (create a new version). ``.format(kind=..., name=...)``.
464
467
  ASSET_VERSIONS_ROUTE_TEMPLATE = "/v1/assets/{kind}/{name}/versions"
465
- #: ``POST`` — new version from stored source with the current converter (§9.5).
468
+ #: ``POST`` — new version from stored source with the current converter.
466
469
  #: ``.format(kind=..., name=...)``.
467
470
  ASSET_RECONVERT_ROUTE_TEMPLATE = "/v1/assets/{kind}/{name}/reconvert"
468
471
  #: ``POST`` — deprecate one version by its ``vN`` number.
@@ -558,18 +561,18 @@ WORKER_JOB_ASSET_VALIDATION_ROUTE_TEMPLATE = "/internal/worker/jobs/{job_id}/ass
558
561
  #: into the ``args`` mapping that gets hashed. An old client that omits it submits
559
562
  #: a job with no pinned assets — the additive default. The control plane
560
563
  #: re-verifies every pin in the submit transaction and records the ``job_assets``
561
- #: rows (§6).
564
+ #: rows.
562
565
  ASSETS_FIELD = "assets"
563
566
 
564
567
  # --------------------------------------------------------------------------
565
568
  # Authorization roles, status / verdict / format enumerations
566
569
  # --------------------------------------------------------------------------
567
570
 
568
- #: Roles allowed to publish / reconvert an org asset (§3). Missing role → one line
571
+ #: Roles allowed to publish / reconvert an org asset. Missing role → one line
569
572
  #: naming the role and who can grant it.
570
573
  ASSET_PUBLISH_ROLES = frozenset({"owner", "admin", "member"})
571
- #: Roles allowed to deprecate / delete / edit metadata (§3). Identical value to
572
- #: :data:`ASSET_PUBLISH_ROLES` today — proposal §3 groups publish/deprecate/delete
574
+ #: Roles allowed to deprecate / delete / edit metadata. Identical value to
575
+ #: :data:`ASSET_PUBLISH_ROLES` today: publish/deprecate/delete
573
576
  #: under one publish capability (plan critic review, 2026-07-17 realignment) — kept
574
577
  #: as a separate constant so a future, narrower publish role can diverge from
575
578
  #: lifecycle without an API change.
@@ -592,22 +595,22 @@ ASSET_LIFECYCLE_ROLES = frozenset({"owner", "admin", "member"})
592
595
  ASSET_VERSION_STATUSES = ("uploading", "validating", "published", "failed", "deprecated", "deleted")
593
596
  #: The ``asset_validations.verdict`` domain — ``passed`` / ``failed`` are asset
594
597
  #: verdicts; ``error`` is an *infrastructure* failure (validator crash), rendered
595
- #: distinctly so it never reads as "your USD is broken" (§5.1, §8.2).
598
+ #: distinctly so it never reads as "your USD is broken".
596
599
  ASSET_VALIDATION_VERDICTS = ("passed", "failed", "error")
597
600
  #: The ``asset_versions.source_format`` domain — the two ingest formats of the
598
- #: first release (§9; MJCF and others extend the same machinery later).
601
+ #: first release; MJCF and others can extend the same machinery later.
599
602
  ASSET_SOURCE_FORMATS = ("usd", "urdf")
600
603
  #: The base-mode domain — whether a robot is bolted down (``fixed``, manipulators)
601
604
  #: or free (``floating``, mobile/legged). Inexpressible in URDF, so declared at
602
- #: publish (§9.1); ``None`` for worlds and where unset.
605
+ #: publish; ``None`` for worlds and where unset.
603
606
  ASSET_BASE_MODES = ("fixed", "floating")
604
- #: The ``asset_versions.origin_type`` domain (§11.2) — **admin-surface only**.
607
+ #: The ``asset_versions.origin_type`` domain — **admin-surface only**.
605
608
  #: ``customer_upload`` is recorded for org publishes (schema symmetry);
606
609
  #: ``simulo_authored`` / ``third_party_import`` are the global-catalog origins.
607
610
  #: These values NEVER appear in a customer-facing serializer (see
608
611
  #: :class:`AssetRecord` / :class:`AssetVersionRecord`, which carry no origin field).
609
612
  ASSET_ORIGIN_TYPES = ("customer_upload", "simulo_authored", "third_party_import")
610
- #: The ``?scope=`` list/search filter values (§5.3). ``org`` = the caller's org
613
+ #: The ``?scope=`` list/search filter values. ``org`` = the caller's org
611
614
  #: catalog, ``global`` = the ``simulo`` catalog, ``all`` = both (labeled rows).
612
615
  ASSET_LIST_SCOPES = ("org", "global", "all")
613
616
 
@@ -615,13 +618,13 @@ ASSET_LIST_SCOPES = ("org", "global", "all")
615
618
  # Size caps (security NFR — request-size limits; see nfr SKILL)
616
619
  # --------------------------------------------------------------------------
617
620
 
618
- #: Maximum total archive size of one published asset version (§10, §14). 413/422
621
+ #: Maximum total archive size of one published asset version. 413/422
619
622
  #: over this. Settings key: ``max_asset_version_bytes``.
620
623
  MAX_ASSET_VERSION_BYTES = 10 * 1024 * 1024 * 1024 # 10 GiB
621
- #: Maximum number of files in one asset package (§10, §14). Settings key:
624
+ #: Maximum number of files in one asset package. Settings key:
622
625
  #: ``max_asset_files``.
623
626
  MAX_ASSET_FILES = 2000
624
- #: Multipart upload part size (§10, §14). Settings key: ``asset_upload_part_bytes``.
627
+ #: Multipart upload part size. Settings key: ``asset_upload_part_bytes``.
625
628
  ASSET_UPLOAD_PART_BYTES = 64 * 1024 * 1024 # 64 MiB
626
629
 
627
630
  #: Maximum character length of an asset reference (security NFR — bounds an
@@ -637,22 +640,21 @@ MAX_ASSET_REF_CHARS = 256
637
640
 
638
641
  #: Maximum number of refs in one :data:`ASSETS_RESOLVE_ROUTE` batch (security NFR
639
642
  #: — bounds an otherwise-unbounded request array; over this ⇒ 422). Pinned as the
640
- #: frozen contract value (freeze doc "## PR-6 amendments": ``{refs: [<=64 strings],
641
- #: runtime}``). The client de-duplicates before counting, so this bounds the number
643
+ #: request-schema value: ``{refs: [<=64 strings], runtime}``. The client
644
+ #: de-duplicates before counting, so this bounds the number
642
645
  #: of *distinct* refs, not the raw authored occurrences.
643
646
  MAX_ASSETS_RESOLVE_BATCH = 64
644
647
 
645
648
  #: The runtime alias :data:`ASSETS_RESOLVE_ROUTE` / :class:`AssetsResolveRequest.runtime`
646
- #: uses when a caller has no other runtime to name (§6, §14 — one runtime today, no
647
- #: runtime registry yet; a future registry replaces this constant, proposal §11.3
648
- #: task-shaped aliases). Hoisted here (rather than left as a client-only constant) because
649
+ #: uses when a caller has no other runtime to name. Hoisted here rather than
650
+ #: left as a client-only constant because
649
651
  #: the ``validated_on_runtime`` match requires the CLIENT's resolve request and the
650
652
  #: VALIDATOR's finalize call to send the IDENTICAL string — one shared constant is the
651
- #: only way that can never drift (freeze doc "Runtime-string seam", 2026-07-17).
653
+ #: only way to prevent string drift.
652
654
  DEFAULT_ASSET_RUNTIME = "isaac"
653
655
 
654
656
  # --------------------------------------------------------------------------
655
- # Publish options envelope (PR-12 — freeze doc "## PR-7 amendments": --scale)
657
+ # Publish options envelope
656
658
  # --------------------------------------------------------------------------
657
659
 
658
660
  #: Bounds on :attr:`AssetPublishOptions.scale` (security NFR — a
@@ -668,20 +670,20 @@ MAX_ASSET_SCALE = 1e6
668
670
  # Facts envelope + wire error codes
669
671
  # --------------------------------------------------------------------------
670
672
 
671
- #: The schema version stamped on the asset-facts document (§8.2). **Additive-only**
673
+ #: The schema version stamped on the asset-facts document. **Additive-only**
672
674
  #: — new facts fields may be added under this version; a breaking change would mint
673
675
  #: ``simulo.asset_facts.v2``. Carried opaquely as ``Mapping[str, Any]`` on the wire
674
- #: (the exact field list is settling, §15.4).
676
+ #: (the exact field list is settling).
675
677
  ASSET_FACTS_SCHEMA_VERSION = "simulo.asset_facts.v1"
676
678
 
677
679
  #: Error code (``GET`` :data:`ASSET_VERSION_DOWNLOAD_ROUTE_TEMPLATE`, ``403``) when
678
680
  #: a **global** asset download is refused — global assets are referenceable and
679
- #: usable in jobs, never exportable (§5.4). Pinned as a constant (like
681
+ #: usable in jobs, never exportable. Pinned as a constant (like
680
682
  #: ``submit.SEED_SOURCE_CONFLICT_CODE``) because both the control plane raising it
681
683
  #: and the CLI mapping it to friendly copy switch on this exact string.
682
684
  ASSET_NOT_EXPORTABLE_CODE = "not_exportable"
683
685
  #: Error/verdict code the worker uses to **fail a job in place** when a claimed
684
- #: asset's archive is missing at mount time (§14 security posture; the worker
686
+ #: asset's archive is missing at mount time (the security posture; the worker
685
687
  #: verifies digests at mount). Pinned for the same reason as above.
686
688
  ASSET_MISSING_CODE = "asset_missing"
687
689
  #: The ``jobs.job_type`` value of a platform-internal asset-validation job
@@ -716,7 +718,7 @@ ASSET_CORRUPT_CODE = "asset_corrupt"
716
718
  #: The uniform per-ref miss code the batch resolver (:data:`ASSETS_RESOLVE_ROUTE`)
717
719
  #: and the job-submit pin re-verify both answer for a ref that does not resolve —
718
720
  #: unknown, cross-org, deleted, or malformed, **all reported identically**
719
- #: (anti-enumeration; freeze doc "## PR-6 amendments"). Carried on
721
+ #: (anti-enumeration; batch-resolve schema and tests). Carried on
720
722
  #: :class:`ResolveMiss.code`; the submit-pin path raises the same code as its
721
723
  #: ``404`` error ``code``. Pinned as a constant because the control plane raising
722
724
  #: it and the CLI mapping it to friendly copy switch on this exact string.
@@ -728,21 +730,21 @@ ASSET_NOT_FOUND_CODE = "asset_not_found"
728
730
  #
729
731
  # Hoisted here per the freeze's "## PR-7 amendments" ("CP friendly-error
730
732
  # codes used by the CLI hoist into interfaces/platform/asset_catalog.py —
731
- # single source; a CP rename can no longer silently degrade §5.1 copy").
733
+ # single source; a control-plane rename can no longer silently degrade CLI copy).
732
734
  # These mirror the control plane's OWN local constants
733
735
  # (`simulo_control_plane.assets.constants` / `assets/quota.py`) by VALUE —
734
736
  # the same "pinned here, not imported" discipline :data:`ASSET_SLUG_PATTERN`
735
737
  # documents above (simulo-interfaces has zero dependency on the control
736
738
  # plane); this docstring is the drift anchor for a future reconciliation
737
739
  # where the control plane imports these directly instead of re-declaring
738
- # them. The thin client's `simulo asset publish`/`validate` §5.1 error
740
+ # them. The thin client's `simulo asset publish`/`validate` error
739
741
  # rendering (`_client/cli.py::_friendly_asset_error`) matches these exact
740
742
  # strings, never a hand-typed literal.
741
743
  # --------------------------------------------------------------------------
742
744
 
743
745
  #: 403 — publishing this archive would exceed the org's storage quota
744
- #: (`assets/quota.py::check_asset_storage_quota`). The CLI appends the §5.1
745
- #: "two ways out" (delete or upgrade) to the server's own message.
746
+ #: (`assets/quota.py::check_asset_storage_quota`). The CLI appends two remedies
747
+ #: (delete assets or upgrade storage) to the server's own message.
746
748
  ASSET_STORAGE_QUOTA_EXCEEDED_CODE = "asset_storage_quota_exceeded"
747
749
  #: 422 — the archive exceeds :data:`MAX_ASSET_VERSION_BYTES`.
748
750
  ASSET_ARCHIVE_TOO_LARGE_CODE = "asset_archive_too_large"
@@ -767,14 +769,14 @@ ASSET_UPLOAD_INCOMPLETE_CODE = "asset_upload_incomplete"
767
769
  # this package (self-contained build discipline, see this module's own
768
770
  # "mirrored, not imported" precedent above), so this is a value-only
769
771
  # duplication, drift-anchored by this docstring. The thin client's
770
- # `simulo asset get`/`deprecate`/`delete` §5.4/§5.5 error rendering
772
+ # `simulo asset get`/`deprecate`/`delete` error rendering
771
773
  # (`_client/cli.py::_friendly_asset_error`) matches these exact strings so
772
774
  # the server's already user-language message renders verbatim, never a
773
775
  # code-prefixed wire dump.
774
776
  # --------------------------------------------------------------------------
775
777
 
776
778
  #: 409 — ``simulo asset delete`` refused because at least one non-validation
777
- #: job still references the version (§5.5's delete guard). The server's
779
+ #: job still references the version (the job-reference delete guard). The server's
778
780
  #: message already names the job count and the first job id, proposal-
779
781
  #: verbatim, and now also points to the ``--force`` override; the CLI renders
780
782
  #: it as-is.
@@ -943,6 +945,13 @@ class AssetVersionRecord:
943
945
  validation_attempts: int = 0
944
946
  """Number of validation attempts made for this version."""
945
947
 
948
+ validation_job_id: Optional[str] = None
949
+ """Exact id of the newest system validation attempt for an organization-owned
950
+ version, or ``None`` when unavailable. Additive and diagnostic-only: callers
951
+ may surface it as a support/correlation reference, but system validation jobs
952
+ are not part of the ordinary Jobs catalog, their runtime logs and free-form
953
+ results are not public surfaces, and global asset reads never expose the id."""
954
+
946
955
  published_by: Optional[str] = None
947
956
  """Display identity of the publishing user, or ``None`` while unpublished."""
948
957
 
@@ -1134,8 +1143,7 @@ class WorkerAssetInfo:
1134
1143
 
1135
1144
  @dataclass(**_DC)
1136
1145
  class AssetPublishOptions:
1137
- """The optional ``options`` envelope of a publish initiate (PR-12 —
1138
- freeze doc "## PR-7 amendments" executed at Wave 3).
1146
+ """The optional ``options`` envelope of a publish-initiate request.
1139
1147
 
1140
1148
  Carried on the initiate body (customer AND admin) as ``options:
1141
1149
  {scale?: float}``; persisted with the version; threaded verbatim into the
@@ -10,7 +10,7 @@ schema, enum reuse, endpoint constants, and semantics docstrings only.
10
10
  Field names and lifecycle align with the platform's Job use case: a job is one
11
11
  managed execution of exactly one package, with lifecycle states drawn from
12
12
  :class:`~simulo.interfaces.platform.enums.JobStatus` (``queued``/``running``/
13
- ``completed``/``failed``/``cancelled`` — Job.md's wording; the local executor
13
+ ``completed``/``failed``/``cancelled`` — job API's wording; the local executor
14
14
  uses the ``running`` → ``completed``|``failed`` subset, the cloud adds
15
15
  ``queued``/``cancelled``).
16
16
 
@@ -23,6 +23,23 @@ HTTP API shape (all responses JSON; all timestamps ISO 8601 UTC, e.g.
23
23
  {"items": [<JobRecord>, ...], "total": 3, "page": 1, "limit": 20, "pages": 1}
24
24
 
25
25
  ``limit`` defaults to 20, maximum 100; out-of-range ``page``/``limit`` → 422.
26
+
27
+ **``pages`` is always >= 1**, including for an empty list (``total: 0`` →
28
+ ``pages: 1``, not ``0``). Clients treat ``page > pages`` as "this page is out
29
+ of range" and recover by clamping to ``pages``; a ``pages: 0`` would make
30
+ ``page: 1`` permanently out of range and the clamp non-terminating. Both
31
+ producers of this envelope (the control plane and the local
32
+ ``simulo-backend serve`` stand-in) enforce the floor. This platform-standard
33
+ paginated envelope shape (``items``/``total``/``page``/``limit``/``pages``)
34
+ is reused verbatim by every other list endpoint in this package — recordings
35
+ and models in :mod:`simulo.interfaces.platform.submit`, the artifacts
36
+ projection in :mod:`simulo.interfaces.platform.artifacts`, and the asset
37
+ list/search route (``ASSETS_ROUTE``) in
38
+ :mod:`simulo.interfaces.platform.asset_catalog` — and the ``pages >= 1``
39
+ floor applies identically to all of them. That list is the complete set:
40
+ adding a paginated route means adding it here too, so a reader learning
41
+ which endpoints this invariant governs is never told a smaller number than
42
+ the truth.
26
43
  * ``GET /v1/jobs/{job_id}`` — one job record (the :class:`JobRecord` fields).
27
44
  * ``GET /v1/jobs/{job_id}/logs?offset=N`` — one log chunk::
28
45
 
@@ -105,8 +122,7 @@ routes documented in ``submit.py``) additionally accept an optional
105
122
  never the default, and available to any active member (no additional role
106
123
  beyond org membership — the platform's existing flat-permission model for
107
124
  jobs, ``JOB_CREATE_ROLES``/``JOB_CANCEL_ROLES``).
108
- * any other value → 422 (Job.md "List All Jobs Using CLI" AF-3, invalid
109
- filter option).
125
+ * any other value → 422 as an invalid filter option.
110
126
 
111
127
  ``POST /v1/jobs/{job_id}/cancel`` is a deliberate exception: it is NOT scope-
112
128
  gated and keeps resolving org-wide regardless of ``?scope=`` — any active
@@ -160,8 +176,7 @@ JOB_SCOPE_QUERY_PARAM = "scope"
160
176
  #: Default / explicit-opt-in-only values for :data:`JOB_SCOPE_QUERY_PARAM`.
161
177
  JOB_SCOPE_MINE = "mine"
162
178
  JOB_SCOPE_ORG = "org"
163
- #: The closed domain of ``?scope=`` — any other value is a 422 (Job.md "List
164
- #: All Jobs Using CLI" AF-3).
179
+ #: The closed domain of ``?scope=`` — any other value is a 422.
165
180
  JOB_LIST_SCOPES = (JOB_SCOPE_MINE, JOB_SCOPE_ORG)
166
181
 
167
182
  #: Upper bound on the log bytes a single ``/logs`` response carries. The server
@@ -245,6 +260,19 @@ class JobRecord:
245
260
  attempts: int = 0
246
261
  """Number of execution attempts started so far (retries make this > 1)."""
247
262
 
263
+ viewstream: bool = False
264
+ """Whether this run requested an interactive live view. Additive and
265
+ tolerant: older producers omit it and consumers see ``False``."""
266
+
267
+ seed: Optional[Mapping[str, Any]] = None
268
+ """Resolved seed provenance (``job_id``, ``model_id``, ``kind``, and
269
+ ``name``), or ``None`` for a fresh run. Kept mapping-shaped so a real JSON
270
+ response can be passed directly to ``JobRecord(**payload)``."""
271
+
272
+ job_type: str = "other"
273
+ """Producer-defined run category. ``"other"`` is an ordinary user job;
274
+ additive producer categories remain readable by older consumers."""
275
+
248
276
  assets: tuple[str, ...] = ()
249
277
  """Human-readable ref of every asset this run pinned, oldest first (the
250
278
  ``job_assets`` denormalized canonical ref, e.g. ``myorg/robot/tiny-arm:v1``).
@@ -295,6 +323,12 @@ class JobRecord:
295
323
  scoping" section): a shared view of everyone's jobs must say whose job
296
324
  each row is, not just that it exists."""
297
325
 
326
+ submitted_by_display: Optional[str] = None
327
+ """Human-readable identity for :attr:`submitted_by_user_id`, currently the
328
+ submitter's email. ``None`` covers system-owned runs, deleted accounts,
329
+ and producers predating the additive field; clients must never fall back
330
+ to displaying the raw user id."""
331
+
298
332
  outputs: Optional[JobOutputsSummary] = None
299
333
  """Typed outputs summary of the run — a **rendering input,
300
334
  never a status**: ``completed ⚠ 1 anomaly`` is drawn from here while
@@ -105,6 +105,16 @@ platform-standard shape: ``{"error": {"code": ..., "message": ..., "request_id":
105
105
  set directly on the top-level body dict — and MUST NOT fold them into the
106
106
  ``args`` mapping that gets hashed.
107
107
 
108
+ **App name (live-view wave, additive):** the request body
109
+ optionally carries ``app_name`` (``str``) — the submitting ``App``'s
110
+ declared name, display context the platform's live viewer shows next to
111
+ ``job_name``. The SAME top-level sibling-of-``args`` placement rule as
112
+ ``viewstream`` and the seed fields (a display property must never perturb
113
+ the content-addressed ``package_id``). Optional end to end: every client
114
+ published before the field omits it, an older control plane ignores the
115
+ extra key, and a job without one renders by ``job_name`` alone — never an
116
+ error in either direction.
117
+
108
118
  :data:`SEED_SOURCE_FIELDS` pins the two field names:
109
119
 
110
120
  - ``seed_from_job_id`` (``str``) — continue training from another job's
@@ -129,7 +139,10 @@ platform-standard shape: ``{"error": {"code": ..., "message": ..., "request_id":
129
139
  ## Client-facing: recordings
130
140
 
131
141
  * :data:`JOB_RECORDINGS_ROUTE_TEMPLATE` (``GET``) — paginated envelope of
132
- :class:`RecordingRecord`, same pagination shape as ``GET /v1/jobs``::
142
+ :class:`RecordingRecord`, same pagination shape as ``GET /v1/jobs``
143
+ (see :mod:`simulo.interfaces.platform.runs` for the full contract,
144
+ including the ``pages >= 1`` invariant that holds even for
145
+ ``total == 0``)::
133
146
 
134
147
  {"items": [<RecordingRecord>, ...], "total": 2, "page": 1, "limit": 20, "pages": 1}
135
148
 
@@ -218,7 +231,14 @@ platform-standard shape: ``{"error": {"code": ..., "message": ..., "request_id":
218
231
  exists yet).
219
232
 
220
233
  * :data:`WORKER_JOB_STREAM_READY_ROUTE_TEMPLATE` (``POST``) — request
221
- ``{"stream_host": "10.0.1.7", "stream_port": 49100}``. Registers the
234
+ ``{"stream_host": "10.0.1.7", "stream_port": 49100}``, optionally plus
235
+ ``"num_envs"`` (``int`` ≥ 1 — live-view wave): the run's actual
236
+ parallel-environment count, read by the worker from the SDK-written
237
+ runtime-facts sidecar and served to the viewer as display context only.
238
+ Optional end to end — an older worker omits it, an older control plane
239
+ ignores the extra key, and the worker DROPS (never sends) a value it
240
+ cannot validate as a sane positive int, so a garbage sidecar can never
241
+ fail the endpoint registration itself. Registers the
222
242
  claiming worker's Kit signaling endpoint ONCE, the moment Kit is actually
223
243
  listening — NOT at claim (Tier-1 1.1). Reporting at claim registered the
224
244
  endpoint minutes before Kit's signaling port opened during a cold Isaac
@@ -280,12 +300,15 @@ checkpoints a training job leaves in its result's ``checkpoint_dir``
280
300
  models; the list is empty, never an error.
281
301
 
282
302
  * :data:`JOB_MODELS_ROUTE_TEMPLATE` (``GET``) — paginated envelope of
283
- :class:`ModelRecord`, same pagination shape as ``GET /v1/jobs``::
303
+ :class:`ModelRecord`, same pagination shape as ``GET /v1/jobs``
304
+ (see :mod:`simulo.interfaces.platform.runs` for the full contract,
305
+ including the ``pages >= 1`` invariant that holds even for
306
+ ``total == 0``)::
284
307
 
285
308
  {"items": [<ModelRecord>, ...], "total": 2, "page": 1, "limit": 20, "pages": 1}
286
309
 
287
310
  * :data:`JOB_MODEL_ROUTE_TEMPLATE` (``GET``) — one :class:`ModelRecord`
288
- (Job.md "Retrieve Model Information Using CLI"). ``404 model_not_found``
311
+ for model metadata. ``404 model_not_found``
289
312
  for an unknown/foreign/malformed id — never 403 (anti-enumeration).
290
313
 
291
314
  * :data:`JOB_MODEL_DOWNLOAD_ROUTE_TEMPLATE` (``GET``) — resolves one model
@@ -671,6 +694,15 @@ class ModelRecord:
671
694
  # `--viewstream` CLI flag at submit time, never from a package manifest) —
672
695
  # so a manifest-borne override could otherwise force streaming on/off
673
696
  # against the job's actual (server-recorded) submission intent.
697
+ # ``SIMULO_RUNTIME_FACTS_PATH`` (live-view wave) is its sidecar sibling:
698
+ # the worker mints a per-attempt runtime-facts path (``worker.py``,
699
+ # ``tempfile.mkstemp``) and exports it via ``_child_env`` so the SDK
700
+ # inside the job subprocess can record display facts (e.g. the
701
+ # parallel-environment count) the worker reads back and reports on
702
+ # ``stream-ready``. It is a resolved filesystem path the platform then
703
+ # TRUSTS AND READS — the same runner-owned class as
704
+ # ``SIMULO_CHECKPOINT_DIR`` / ``SIMULO_ANOMALY_DEBUG_DIR`` — so nothing
705
+ # user-authored may ever aim it elsewhere.
674
706
  #
675
707
  # * **User-tunable** (deliberately NOT reserved): ``_setup_checkpointing``
676
708
  # exports ``SIMULO_CHECKPOINT_EVERY`` / ``SIMULO_CHECKPOINT_KEEP_LAST`` /
@@ -728,6 +760,7 @@ RESERVED_RUNTIME_ENV_KEYS = frozenset(
728
760
  "SIMULO_SHUTDOWN_GRACE_S",
729
761
  "SIMULO_FAST_EXIT_AFTER_RESULT",
730
762
  "SIMULO_VIEWSTREAM_ENABLED",
763
+ "SIMULO_RUNTIME_FACTS_PATH",
731
764
  "MAX_JOB_TIMEOUT_S",
732
765
  }
733
766
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simulo-interfaces
3
- Version: 0.12.0
3
+ Version: 0.13.0
4
4
  Summary: Type contracts for the Simulo SDK. Installed automatically as a dependency of the simulo package.
5
5
  Author-email: Simulo Team <team@simulo.ai>
6
6
  License: BSD-3-Clause