simulo-interfaces 0.10.0__tar.gz → 0.12.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.10.0/src/simulo_interfaces.egg-info → simulo_interfaces-0.12.0}/PKG-INFO +6 -5
  2. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/pyproject.toml +14 -2
  3. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/authoring/__init__.py +11 -8
  4. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/authoring/asset.py +24 -17
  5. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/authoring/entity.py +3 -3
  6. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/authoring/pose.py +2 -3
  7. simulo_interfaces-0.12.0/src/simulo/interfaces/authoring/prop.py +210 -0
  8. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/authoring/robot.py +2 -2
  9. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/platform/__init__.py +10 -14
  10. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/platform/asset.py +2 -2
  11. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/platform/runs.py +49 -1
  12. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/runtime/__init__.py +1 -2
  13. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0/src/simulo_interfaces.egg-info}/PKG-INFO +6 -5
  14. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo_interfaces.egg-info/SOURCES.txt +1 -0
  15. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo_interfaces.egg-info/requires.txt +5 -4
  16. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/MANIFEST.in +0 -0
  17. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/PYPI.md +0 -0
  18. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/setup.cfg +0 -0
  19. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/__init__.py +0 -0
  20. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/authoring/actuator.py +0 -0
  21. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/authoring/light.py +0 -0
  22. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/authoring/materials.py +0 -0
  23. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/authoring/physics.py +0 -0
  24. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/authoring/primitives.py +0 -0
  25. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/authoring/record.py +0 -0
  26. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/authoring/scene.py +0 -0
  27. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/authoring/sensor.py +0 -0
  28. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/authoring/terrain.py +0 -0
  29. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/authoring/visual.py +0 -0
  30. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/authoring/world.py +0 -0
  31. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/exceptions.py +0 -0
  32. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/ids.py +0 -0
  33. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/platform/app.py +0 -0
  34. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/platform/artifacts.py +0 -0
  35. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/platform/asset_catalog.py +0 -0
  36. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/platform/callbacks.py +0 -0
  37. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/platform/debug.py +0 -0
  38. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/platform/domain.py +0 -0
  39. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/platform/enums.py +0 -0
  40. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/platform/manifest.py +0 -0
  41. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/platform/runtime.py +0 -0
  42. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/platform/submit.py +0 -0
  43. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/platform/volume.py +0 -0
  44. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/py.typed +0 -0
  45. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/runtime/anomaly.py +0 -0
  46. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/runtime/components.py +0 -0
  47. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/runtime/env.py +0 -0
  48. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/runtime/player.py +0 -0
  49. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/runtime/policy.py +0 -0
  50. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/runtime/scenario.py +0 -0
  51. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/runtime/task.py +0 -0
  52. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/runtime/tensors.py +0 -0
  53. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo/interfaces/runtime/trainer.py +0 -0
  54. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.0}/src/simulo_interfaces.egg-info/dependency_links.txt +0 -0
  55. {simulo_interfaces-0.10.0 → simulo_interfaces-0.12.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.10.0
3
+ Version: 0.12.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
@@ -16,13 +16,14 @@ Classifier: Typing :: Typed
16
16
  Requires-Python: >=3.11
17
17
  Description-Content-Type: text/markdown
18
18
  Provides-Extra: dev
19
- Requires-Dist: black>=24.0; extra == "dev"
20
- Requires-Dist: isort>=5.13; extra == "dev"
21
- Requires-Dist: ruff>=0.5; extra == "dev"
22
- Requires-Dist: mypy>=1.10; extra == "dev"
19
+ Requires-Dist: black==26.5.1; extra == "dev"
20
+ Requires-Dist: isort==8.0.1; extra == "dev"
21
+ Requires-Dist: ruff==0.15.21; extra == "dev"
22
+ Requires-Dist: mypy==2.3.0; extra == "dev"
23
23
  Requires-Dist: pytest>=8.2; extra == "dev"
24
24
  Requires-Dist: build>=1.2; extra == "dev"
25
25
  Requires-Dist: twine>=5.1; extra == "dev"
26
+ Requires-Dist: packaging>=23; extra == "dev"
26
27
  Provides-Extra: release
27
28
  Requires-Dist: commitizen>=3.27; extra == "release"
28
29
  Requires-Dist: build>=1.2; extra == "release"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "simulo-interfaces"
7
- version = "0.10.0"
7
+ version = "0.12.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"
@@ -26,7 +26,19 @@ classifiers = [
26
26
  dependencies = []
27
27
 
28
28
  [project.optional-dependencies]
29
- dev = ["black>=24.0", "isort>=5.13", "ruff>=0.5", "mypy>=1.10", "pytest>=8.2", "build>=1.2", "twine>=5.1"]
29
+ # black/isort/ruff/mypy pinned EXACT, not >=: these are rule-set tools whose
30
+ # defaults change between releases, so an unbounded lower bound lets CI silently
31
+ # resolve a newer release than a developer's environment has — "clean locally" and
32
+ # "hundreds of errors in CI" both true of one commit (2026-08-01: ruff 0.15.21 ->
33
+ # 0.16.1 flagged 1088 findings from new default rules, zero of them real).
34
+ # Keep in lockstep with `.github/workflows/simulo-interfaces-ci.yml`'s
35
+ # `ruff`/`black`/`isort`/`mypy` steps, which resolve their versions from THIS extra
36
+ # (`pip install -e ".[dev]"`).
37
+ dev = ["black==26.5.1", "isort==8.0.1", "ruff==0.15.21", "mypy==2.3.0", "pytest>=8.2", "build>=1.2",
38
+ "twine>=5.1", "packaging>=23"]
39
+ # `packaging` is a direct test dependency, not incidental: the consumer-pin guard parses
40
+ # PEP 508 requirements with it. It arrives transitively via pytest today, which is exactly
41
+ # why it is declared here — a transitive dep that disappears takes the guard with it.
30
42
  # `dev` carries build/twine but deliberately not commitizen; `release` below
31
43
  # adds commitizen for cutting version bumps without a second build/twine copy.
32
44
  release = ["commitizen>=3.27", "build>=1.2", "twine>=5.1"]
@@ -11,9 +11,10 @@ working because only this definition exists.
11
11
 
12
12
  Relocated so far — Wave C1: ``pose`` (``Pose``), ``materials`` (``Material``,
13
13
  ``SurfaceMaterial``), ``physics`` (``Physics``, ``Collision``, ``RigidPhysics``,
14
- ``DeformableMaterial``, ``DeformablePhysics``), ``asset`` (``Asset``,
14
+ ``DeformableMaterial``, ``DeformablePhysics``), ``asset`` (``AssetSource``,
15
15
  ``USDAsset``, ``URDFAsset`` — the model-source specs, distinct from the thin
16
- client's registry-handle ``simulo.Asset``), and ``light`` (``Light``,
16
+ client's registry-handle ``simulo.Asset``; renamed from ``Asset`` — owner
17
+ decision — to resolve that exact name collision), and ``light`` (``Light``,
17
18
  ``LightConfig``, ``DistantLight``, ``DomeLight``). Wave C2: ``terrain``
18
19
  (``Terrain``, ``TerrainConfig``, ``GroundPlane``, ``SubTerrainConfig``,
19
20
  ``RoughTerrain``), ``primitives`` (``PrimitiveShape``, ``Cone``,
@@ -85,7 +86,7 @@ docstring, is the source of truth for what lands here and when.
85
86
  """
86
87
 
87
88
  from simulo.interfaces.authoring.actuator import ActuatorProtocol
88
- from simulo.interfaces.authoring.asset import Asset, URDFAsset, USDAsset
89
+ from simulo.interfaces.authoring.asset import AssetSource, URDFAsset, USDAsset
89
90
  from simulo.interfaces.authoring.entity import AssetEntity, Entity
90
91
  from simulo.interfaces.authoring.light import DistantLight, DomeLight, Light, LightConfig
91
92
  from simulo.interfaces.authoring.materials import Material, SurfaceMaterial
@@ -105,10 +106,11 @@ from simulo.interfaces.authoring.primitives import (
105
106
  Sphere,
106
107
  Transform,
107
108
  )
109
+ from simulo.interfaces.authoring.prop import PropProtocol, PropStateProtocol
108
110
  from simulo.interfaces.authoring.record import RecordConfig
109
111
  from simulo.interfaces.authoring.robot import RobotProtocol, RobotStateProtocol
110
112
  from simulo.interfaces.authoring.scene import SceneGroupProtocol, SceneProtocol
111
- from simulo.interfaces.authoring.sensor import CameraProtocol, SensorProtocol
113
+ from simulo.interfaces.authoring.sensor import CameraProtocol
112
114
  from simulo.interfaces.authoring.terrain import (
113
115
  GroundPlane,
114
116
  RoughTerrain,
@@ -139,7 +141,7 @@ __all__ = [
139
141
  "DeformablePhysics",
140
142
  "Physics",
141
143
  # asset (model-source specs)
142
- "Asset",
144
+ "AssetSource",
143
145
  "USDAsset",
144
146
  "URDFAsset",
145
147
  # light
@@ -179,11 +181,12 @@ __all__ = [
179
181
  # scene (MIRROR — the concrete classes live in the backend package)
180
182
  "SceneProtocol",
181
183
  "SceneGroupProtocol",
182
- # robot / sensor / actuator (MIRROR — the concrete classes live in the
183
- # backend package and explicitly inherit these Protocols)
184
+ # robot / prop / sensor / actuator (MIRROR — the concrete classes live in
185
+ # the backend package and explicitly inherit these Protocols)
184
186
  "RobotProtocol",
185
187
  "RobotStateProtocol",
186
- "SensorProtocol",
188
+ "PropProtocol",
189
+ "PropStateProtocol",
187
190
  "CameraProtocol",
188
191
  "ActuatorProtocol",
189
192
  ]
@@ -1,10 +1,16 @@
1
1
  """Model-source asset specs — USD/URDF sources a scene loads.
2
2
 
3
- Owns the canonical ``Asset`` / ``USDAsset`` / ``URDFAsset`` model-source
3
+ Owns the canonical ``AssetSource`` / ``USDAsset`` / ``URDFAsset`` model-source
4
4
  dataclasses. Distinct from the thin client's user-facing ``simulo.Asset``
5
5
  registry handle (a catalog ref like ``robot/so-arm-100:v3``): these classes
6
6
  name a USD/URDF *file source* to load into a scene, and the backend consumes
7
7
  them when it builds the simulation.
8
+
9
+ Renamed from ``Asset`` (owner decision): the old name collided with the
10
+ thin client's registry-handle ``simulo.Asset`` — same name, different
11
+ constructor, different members, both user-facing. Nothing failed on the
12
+ collision (both expose ``.usd()``/``.urdf()``, the handle's just returns
13
+ ``Any``), which is exactly why it was worth a rename rather than a shim.
8
14
  """
9
15
 
10
16
  from dataclasses import dataclass
@@ -12,16 +18,16 @@ from typing import TYPE_CHECKING
12
18
 
13
19
 
14
20
  @dataclass
15
- class Asset:
16
- """Base asset class."""
21
+ class AssetSource:
22
+ """Base model-source asset class."""
17
23
 
18
24
  if TYPE_CHECKING:
19
- # ``Asset.usd`` / ``Asset.urdf`` are attached dynamically below (they
20
- # return the ``USDAsset`` / ``URDFAsset`` subclasses, which are defined
21
- # after this class). These declarations never execute; they exist so a
22
- # type checker sees the convenience aliases with their precise
23
- # signatures instead of rejecting ``Asset.usd(...)`` as an unknown
24
- # attribute.
25
+ # ``AssetSource.usd`` / ``AssetSource.urdf`` are attached dynamically
26
+ # below (they return the ``USDAsset`` / ``URDFAsset`` subclasses,
27
+ # which are defined after this class). These declarations never
28
+ # execute; they exist so a type checker sees the convenience aliases
29
+ # with their precise signatures instead of rejecting
30
+ # ``AssetSource.usd(...)`` as an unknown attribute.
25
31
  @classmethod
26
32
  def usd(cls, path: str) -> "USDAsset": ...
27
33
 
@@ -30,7 +36,7 @@ class Asset:
30
36
 
31
37
 
32
38
  @dataclass
33
- class USDAsset(Asset):
39
+ class USDAsset(AssetSource):
34
40
  """USD file asset.
35
41
 
36
42
  Attributes:
@@ -59,7 +65,7 @@ class USDAsset(Asset):
59
65
 
60
66
 
61
67
  @dataclass
62
- class URDFAsset(Asset):
68
+ class URDFAsset(AssetSource):
63
69
  """URDF file asset.
64
70
 
65
71
  Attributes:
@@ -81,12 +87,13 @@ class URDFAsset(Asset):
81
87
  return cls(path=path)
82
88
 
83
89
 
84
- # Alias for convenience — callers reach for `Asset.usd(...)` / `Asset.urdf(...)`.
85
- # Attached dynamically; the TYPE_CHECKING declarations on Asset above are what
86
- # let a type checker see them.
87
- Asset.usd = USDAsset.usd # type: ignore[method-assign]
88
- Asset.urdf = URDFAsset.urdf # type: ignore[method-assign]
90
+ # Alias for convenience — callers reach for `AssetSource.usd(...)` /
91
+ # `AssetSource.urdf(...)`.
92
+ # Attached dynamically; the TYPE_CHECKING declarations on AssetSource above
93
+ # are what let a type checker see them.
94
+ AssetSource.usd = USDAsset.usd # type: ignore[method-assign]
95
+ AssetSource.urdf = URDFAsset.urdf # type: ignore[method-assign]
89
96
 
90
97
  # Explicit public surface — keeps the TYPE_CHECKING import (needed for the
91
98
  # static-only `usd`/`urdf` declarations above) off the star-import namespace.
92
- __all__ = ["Asset", "USDAsset", "URDFAsset"]
99
+ __all__ = ["AssetSource", "USDAsset", "URDFAsset"]
@@ -3,7 +3,7 @@
3
3
  from dataclasses import dataclass, field
4
4
  from typing import Optional, Tuple
5
5
 
6
- from simulo.interfaces.authoring.asset import Asset
6
+ from simulo.interfaces.authoring.asset import AssetSource
7
7
  from simulo.interfaces.authoring.materials import Material
8
8
  from simulo.interfaces.authoring.physics import DeformablePhysics, RigidPhysics
9
9
  from simulo.interfaces.authoring.pose import Pose
@@ -147,7 +147,7 @@ class Entity:
147
147
  @staticmethod
148
148
  def from_asset(
149
149
  name: str,
150
- asset: Asset,
150
+ asset: AssetSource,
151
151
  pose: Optional[Pose] = None,
152
152
  ) -> "AssetEntity":
153
153
  """Create an entity from a USD or URDF asset.
@@ -177,4 +177,4 @@ class AssetEntity(Entity):
177
177
  existing dataclass field ordering. Callers always supply this.
178
178
  """
179
179
 
180
- asset: Optional[Asset] = None
180
+ asset: Optional[AssetSource] = None
@@ -17,8 +17,8 @@ class Pose:
17
17
  xyz: Position as (x, y, z)
18
18
  quat_wxyz: Orientation as quaternion (w, x, y, z)
19
19
 
20
- Example:
21
- ```python
20
+ Example::
21
+
22
22
  # Using named arguments
23
23
  pose = Pose(position=[0.5, 0.3, 0.7], orientation=[1, 0, 0, 0])
24
24
 
@@ -27,7 +27,6 @@ class Pose:
27
27
 
28
28
  # Position only (identity rotation)
29
29
  pose = Pose.from_xyz(0.5, 0.3, 0.7)
30
- ```
31
30
  """
32
31
 
33
32
  xyz: Tuple[float, float, float] = (0.0, 0.0, 0.0)
@@ -0,0 +1,210 @@
1
+ """``PropProtocol`` — the declared contract for a movable scene prop.
2
+
3
+ A MIRROR, not a relocation (the same mechanism as the Robot and Scene
4
+ contracts in this package): the concrete ``Prop`` implementation stays in the
5
+ backend package, because reading and writing a prop's pose reaches through a
6
+ live engine handle and cannot exist in a zero-dependency package. What CAN
7
+ live here is the declared public surface — what a prop is constructed from,
8
+ how its live state is read, and how its pose is written.
9
+
10
+ Why this contract exists at all
11
+ -------------------------------
12
+
13
+ A scene holds two kinds of movable thing: robots, which the simulation drives
14
+ through joints, and *props* — the block on the table, the crate to push, the
15
+ ball to catch. Robots have carried a full pose surface since the beginning
16
+ (``robot.state.pose``, ``set_root_pose``, ``reset``). Props carried none: a
17
+ shape handed to ``scene.add(...)`` is pure authoring data, so the only way to
18
+ reach the spawned body's pose was the engine-private scene index.
19
+
20
+ That gap is what kept manipulation demos *scripted*. An episodic task must
21
+ re-randomise its object at the start of every episode and read the object's
22
+ height back to score the lift — both of which are prop pose access. This
23
+ contract closes it.
24
+
25
+ ``PrimitiveShape``, not every entity
26
+ ------------------------------------
27
+
28
+ A prop is built from a :class:`~simulo.interfaces.authoring.primitives.PrimitiveShape`
29
+ carrying **rigid** physics. That restriction is the honest one rather than a
30
+ convenience: a pose that can be read and written is the simulation's own
31
+ rigid-body state, and only a rigid body has one. A visual-only shape (no
32
+ physics) and a deformable one have no rigid-body pose to write, so the
33
+ implementation refuses them at construction, naming the prop — rather than
34
+ accepting them and silently doing nothing at reset time, which would surface
35
+ much later as "my object never moves".
36
+
37
+ A shape that only needs to be *visible* does not need a prop at all: hand it
38
+ straight to ``scene.add(shape, at=...)`` exactly as before. ``Prop`` is
39
+ strictly the opt-in for "I also need to read or write where this thing is".
40
+
41
+ Tensors, not ``Pose``
42
+ ---------------------
43
+
44
+ Every batched member here is ``TensorLike`` with the environment count as its
45
+ leading dimension, and the write methods take the same. ``Pose`` remains the
46
+ *authoring* type — where the prop spawns, written once as
47
+ ``Cuboid(pose=Pose.from_xyz(...))``. It is deliberately NOT the runtime type:
48
+ a ``Pose`` is one pose, and the entire purpose of this surface is giving each
49
+ of thousands of environments a *different* pose on the same call. See
50
+ ``PropProtocol.set_pose`` for the full reasoning.
51
+
52
+ Enforcement is the standard four layers for a mirror — the type checker alone
53
+ is NOT sufficient (measured on the Scene mirror): mypy catches signature
54
+ *shape* divergence at the backend definition site but is silent on parameter
55
+ renames and on a property degrading to a plain attribute. Those are caught by
56
+ the signature-conformance test in the backend package's suite (per-member,
57
+ both directions, plus member-set equality with a pinned omissions allowlist),
58
+ and the constructor is pinned separately by a ``__call__``-Protocol in the
59
+ backend's static conformance checks (type checkers exempt ``__init__`` from
60
+ override checks). The Protocol stays structural for any other implementer.
61
+ """
62
+
63
+ from typing import Optional, Protocol, runtime_checkable
64
+
65
+ from simulo.interfaces.authoring.primitives import PrimitiveShape
66
+ from simulo.interfaces.runtime.tensors import TensorLike
67
+
68
+
69
+ @runtime_checkable
70
+ class PropStateProtocol(Protocol):
71
+ """The live prop-state contract — batched, read-only, zero-copy.
72
+
73
+ Obtained from a prop as ``prop.state``; never constructed directly. The
74
+ three members are exactly the rigid-body half of
75
+ :class:`~simulo.interfaces.authoring.robot.RobotStateProtocol` and carry
76
+ the same names for the same quantities — a prop has no joints, so the
77
+ three joint members simply do not appear. Code that reads
78
+ ``robot.state.pose`` reads ``prop.state.pose`` identically.
79
+
80
+ Each member is a live view of the simulation's own state buffers: reading
81
+ one returns the current values without copying, at the same cost inside a
82
+ training loop's hot path as anywhere else. All members carry the
83
+ environment batch as their leading dimension (the scene's ``num_envs``).
84
+
85
+ **Read a member each time you need it — do not hold the returned tensor
86
+ across steps, and do not write into it.** The tensor IS the simulation's
87
+ own memory: the engine refreshes these buffers by rebinding them to new
88
+ tensors, so a tensor held across a step can silently serve stale values,
89
+ and writing into one writes into the simulation. The accessor
90
+ (``prop.state`` itself) may be held freely.
91
+
92
+ Members raise ``RuntimeError`` until the simulation runtime has attached
93
+ the prop — read them from ``on_start`` onward. The ``state`` accessor
94
+ itself is always available and is the same object on every access.
95
+ """
96
+
97
+ @property
98
+ def pose(self) -> TensorLike:
99
+ """Pose in the world frame, shape ``(num_envs, 7)``:
100
+ ``[x, y, z, qw, qx, qy, qz]`` (position + w-first quaternion)."""
101
+ ...
102
+
103
+ @property
104
+ def linear_velocity(self) -> TensorLike:
105
+ """Linear velocity in the world frame, shape ``(num_envs, 3)``:
106
+ ``[vx, vy, vz]``."""
107
+ ...
108
+
109
+ @property
110
+ def angular_velocity(self) -> TensorLike:
111
+ """Angular velocity in the world frame, shape ``(num_envs, 3)``:
112
+ ``[wx, wy, wz]``."""
113
+ ...
114
+
115
+
116
+ @runtime_checkable
117
+ class PropProtocol(Protocol):
118
+ """The prop authoring contract — a rigid body whose pose you can read and write.
119
+
120
+ This is the surface authoring code may rely on when it creates a prop,
121
+ adds it to a scene, and moves it: the shape it was built from, its
122
+ identity in the scene, its live state, its spawn pose, and the pose /
123
+ velocity writes an episodic reset needs.
124
+ """
125
+
126
+ # -- Construction-time configuration ----------------------------------
127
+
128
+ shape: PrimitiveShape
129
+ """The primitive shape this prop spawns, carrying its size, material, and
130
+ rigid physics. The shape's own ``pose`` is where the prop spawns."""
131
+
132
+ # -- Identity and attachment ------------------------------------------
133
+
134
+ @property
135
+ def name(self) -> Optional[str]:
136
+ """Prop name — taken from the shape, or set by the scene when added."""
137
+ ...
138
+
139
+ @property
140
+ def path(self) -> Optional[str]:
141
+ """Scene path — set by the scene when the prop is added."""
142
+ ...
143
+
144
+ @property
145
+ def state(self) -> PropStateProtocol:
146
+ """The grouped live prop state — the supported, typed state surface.
147
+
148
+ Always available (the accessor is the same object on every access);
149
+ its members read straight through to the simulation's state buffers
150
+ and raise ``RuntimeError`` until the simulation runtime has attached
151
+ the prop — read them from ``on_start`` onward.
152
+ """
153
+ ...
154
+
155
+ @property
156
+ def default_pose(self) -> TensorLike:
157
+ """The prop's spawn pose in the **world** frame, ``(num_envs, 7)``.
158
+
159
+ The pose the prop was authored at, already offset by each
160
+ environment's own origin — so it is directly comparable with
161
+ ``state.pose`` and directly usable as the base for a randomised
162
+ reset. Index it with the environments being reset
163
+ (``prop.default_pose[env_ids]``), perturb, and write it back with
164
+ :meth:`set_pose`.
165
+
166
+ Raises ``RuntimeError`` until the simulation runtime has attached the
167
+ prop.
168
+ """
169
+ ...
170
+
171
+ # -- Pose writes ------------------------------------------------------
172
+
173
+ def set_pose(self, pose: TensorLike, env_ids: Optional[TensorLike] = ...) -> None:
174
+ """Write the prop's pose directly (teleport, not control), world frame.
175
+
176
+ Args:
177
+ pose: ``(num_envs, 7)`` — or ``(len(env_ids), 7)`` when ``env_ids``
178
+ is given — as ``[x, y, z, qw, qx, qy, qz]``.
179
+ env_ids: Environment indices to write; ``None`` writes every
180
+ environment. Passing the subset being reset is what makes a
181
+ partial episodic reset possible: the environments that did not
182
+ finish keep the pose they had.
183
+ """
184
+ ...
185
+
186
+ def set_velocity(self, velocity: TensorLike, env_ids: Optional[TensorLike] = ...) -> None:
187
+ """Write the prop's velocity directly (teleport, not control), world frame.
188
+
189
+ Args:
190
+ velocity: ``(num_envs, 6)`` — or ``(len(env_ids), 6)`` when
191
+ ``env_ids`` is given — as ``[vx, vy, vz, wx, wy, wz]``.
192
+ env_ids: Environment indices to write; ``None`` writes every
193
+ environment.
194
+
195
+ A reset that moves a prop should zero its velocity too, or the body
196
+ arrives at its new pose still carrying the momentum it had at the end
197
+ of the previous episode.
198
+ """
199
+ ...
200
+
201
+ # -- Lifecycle --------------------------------------------------------
202
+
203
+ def reset(self, env_ids: Optional[TensorLike] = ...) -> None:
204
+ """Return the prop to its spawn pose with zero velocity.
205
+
206
+ Args:
207
+ env_ids: Environment indices to reset; ``None`` resets every
208
+ environment.
209
+ """
210
+ ...
@@ -61,7 +61,7 @@ entry in the backend conformance test's documented-omissions allowlist):
61
61
 
62
62
  from typing import Any, Dict, List, Optional, Protocol, Tuple, runtime_checkable
63
63
 
64
- from simulo.interfaces.authoring.asset import Asset
64
+ from simulo.interfaces.authoring.asset import AssetSource
65
65
  from simulo.interfaces.authoring.pose import Pose
66
66
  from simulo.interfaces.runtime.tensors import TensorLike
67
67
 
@@ -142,7 +142,7 @@ class RobotProtocol(Protocol):
142
142
  # -- Construction-time configuration (plain data members: genuinely
143
143
  # -- mutable attributes on the implementation, per mirror-fidelity rule).
144
144
 
145
- asset: Asset
145
+ asset: AssetSource
146
146
  """Model source (USD or URDF) defining the robot."""
147
147
 
148
148
  initial_pose: Pose
@@ -7,7 +7,6 @@ implementation-free — concrete behaviour lives in the future
7
7
  ``simulo`` client and the ``simulo-backend`` SDK.
8
8
  """
9
9
 
10
- from simulo.interfaces.platform.app import AppProtocol, JobFunctionProtocol, Mount
11
10
  from simulo.interfaces.platform.artifacts import (
12
11
  DECLARED_OUTPUT_MISSING_CODE,
13
12
  JOB_ARTIFACT_DOWNLOAD_ROUTE_TEMPLATE,
@@ -97,7 +96,7 @@ from simulo.interfaces.platform.asset_catalog import (
97
96
  from simulo.interfaces.platform.callbacks import JobCallbackProtocol
98
97
  from simulo.interfaces.platform.debug import DebugSessionProtocol, ReplaySessionProtocol, VisualizationSessionProtocol
99
98
  from simulo.interfaces.platform.domain import Checkpoint, Job, Package, Project, Resource, Tag, TrainedModel
100
- from simulo.interfaces.platform.enums import ArtifactKind, JobStatus, ResourceKind, ResumePolicy
99
+ from simulo.interfaces.platform.enums import ArtifactKind
101
100
  from simulo.interfaces.platform.manifest import (
102
101
  FINDING_CLASSIFICATIONS,
103
102
  MAX_PREFLIGHT_BODY_BYTES,
@@ -112,15 +111,18 @@ from simulo.interfaces.platform.manifest import (
112
111
  )
113
112
  from simulo.interfaces.platform.runs import (
114
113
  JOB_CANCEL_ROUTE_TEMPLATE,
114
+ JOB_LIST_SCOPES,
115
115
  JOB_LOGS_ROUTE_TEMPLATE,
116
116
  JOB_RESULT_ROUTE_TEMPLATE,
117
117
  JOB_ROUTE_TEMPLATE,
118
+ JOB_SCOPE_MINE,
119
+ JOB_SCOPE_ORG,
120
+ JOB_SCOPE_QUERY_PARAM,
118
121
  JOBS_API_DEFAULT_PORT,
119
122
  JOBS_ROUTE,
120
123
  LOG_CHUNK_MAX_BYTES,
121
124
  LOG_TAIL_MAX_BYTES,
122
125
  TERMINAL_JOB_STATUSES,
123
- JobRecord,
124
126
  )
125
127
  from simulo.interfaces.platform.runtime import RuntimeProtocol
126
128
  from simulo.interfaces.platform.submit import (
@@ -154,8 +156,6 @@ from simulo.interfaces.platform.submit import (
154
156
  WORKER_JOB_RECORDINGS_PRESIGN_ROUTE_TEMPLATE,
155
157
  WORKER_JOB_RECORDINGS_ROUTE_TEMPLATE,
156
158
  WORKER_LEASE_HEADER,
157
- ModelRecord,
158
- RecordingRecord,
159
159
  canonical_pypi_name,
160
160
  is_platform_pinned,
161
161
  )
@@ -163,9 +163,6 @@ from simulo.interfaces.platform.volume import VolumeProtocol
163
163
 
164
164
  __all__ = [
165
165
  # enums
166
- "JobStatus",
167
- "ResumePolicy",
168
- "ResourceKind",
169
166
  "ArtifactKind",
170
167
  # domain dataclasses
171
168
  "Tag",
@@ -206,7 +203,6 @@ __all__ = [
206
203
  "OUTPUT_FLAG_ANOMALY",
207
204
  "DECLARED_OUTPUT_MISSING_CODE",
208
205
  # job-run contract (run record schema + jobs HTTP API shape)
209
- "JobRecord",
210
206
  "TERMINAL_JOB_STATUSES",
211
207
  "JOBS_API_DEFAULT_PORT",
212
208
  "JOBS_ROUTE",
@@ -216,6 +212,11 @@ __all__ = [
216
212
  "JOB_RESULT_ROUTE_TEMPLATE",
217
213
  "LOG_CHUNK_MAX_BYTES",
218
214
  "LOG_TAIL_MAX_BYTES",
215
+ # user scoping (issue #651)
216
+ "JOB_SCOPE_QUERY_PARAM",
217
+ "JOB_SCOPE_MINE",
218
+ "JOB_SCOPE_ORG",
219
+ "JOB_LIST_SCOPES",
219
220
  # submit / worker / recordings wire contract (PR-0)
220
221
  "PACKAGES_ROUTE",
221
222
  "PACKAGE_ARCHIVE_ROUTE_TEMPLATE",
@@ -240,8 +241,6 @@ __all__ = [
240
241
  "MAX_COMPLETE_MESSAGE_CHARS",
241
242
  "MAX_COMPLETE_REASON_CODE_CHARS",
242
243
  "MODEL_KINDS",
243
- "ModelRecord",
244
- "RecordingRecord",
245
244
  "RESERVED_RUNTIME_ENV_KEYS",
246
245
  "RESERVED_RUNTIME_ENV_PREFIXES",
247
246
  "PLATFORM_PINNED_PACKAGES",
@@ -328,9 +327,6 @@ __all__ = [
328
327
  "AssetProtocol",
329
328
  "VolumeProtocol",
330
329
  "JobCallbackProtocol",
331
- "JobFunctionProtocol",
332
- "AppProtocol",
333
- "Mount",
334
330
  "DebugSessionProtocol",
335
331
  "ReplaySessionProtocol",
336
332
  "VisualizationSessionProtocol",
@@ -7,8 +7,8 @@ from typing import Protocol, runtime_checkable
7
7
  class AssetProtocol(Protocol):
8
8
  """Read-only mounted cloud resource handle (Rule #6). FORWARD.
9
9
 
10
- Distinct from ``simulo.interfaces.authoring.asset.Asset`` (a USD/URDF
11
- model-source simulation asset).
10
+ Distinct from ``simulo.interfaces.authoring.asset.AssetSource`` (a
11
+ USD/URDF model-source simulation asset).
12
12
 
13
13
  This is the user-facing *handle* protocol; the asset-catalog **wire**
14
14
  contract (ref grammar, routes, caps, wire dataclasses) lives in
@@ -92,6 +92,31 @@ That scoping is additive at the transport layer: the :class:`JobRecord`
92
92
  fields and route shapes defined here do not change, only the server-side
93
93
  filtering they are evaluated under.
94
94
 
95
+ **User scoping (``?scope=``, additive):** within one organization,
96
+ ``GET /v1/jobs`` and every per-job route (``{job_id}``, ``{job_id}/logs``,
97
+ ``{job_id}/result``, and the sibling recordings/models/artifacts/view-session
98
+ routes documented in ``submit.py``) additionally accept an optional
99
+ ``?scope=mine|org`` query parameter, resolved server-side:
100
+
101
+ * ``mine`` (the default — the value an old client that never sends the
102
+ parameter gets) — only jobs this caller themselves submitted
103
+ (``submitted_by_user_id`` on the job row).
104
+ * ``org`` — every job in the caller's organization. An explicit opt-in,
105
+ never the default, and available to any active member (no additional role
106
+ beyond org membership — the platform's existing flat-permission model for
107
+ 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).
110
+
111
+ ``POST /v1/jobs/{job_id}/cancel`` is a deliberate exception: it is NOT scope-
112
+ gated and keeps resolving org-wide regardless of ``?scope=`` — any active
113
+ member may cancel any job in their org, matching cancel's existing,
114
+ unchanged authorization model.
115
+
116
+ :class:`JobRecord` gains ``submitted_by_user_id`` (below) precisely so an
117
+ ``org``-scoped listing is attributable — a shared view of everyone's jobs
118
+ must say whose job each row is.
119
+
95
120
  Errors use the platform-standard shape (NFR contract)::
96
121
 
97
122
  {"error": {"code": "job_not_found", "message": "...", "request_id": "req_..."}}
@@ -125,9 +150,20 @@ JOB_RESULT_ROUTE_TEMPLATE = "/v1/jobs/{job_id}/result"
125
150
  #: executor); 202 ``{"cancel_requested": true}`` (running — the executor
126
151
  #: stops it within one heartbeat interval); 409 ``job_not_running``
127
152
  #: (already terminal); 404 ``job_not_found``. Full semantics in the module
128
- #: docstring above.
153
+ #: docstring above. NOT scope-gated (module docstring's "User scoping" note)
154
+ #: — cancel keeps resolving org-wide regardless of ``?scope=``.
129
155
  JOB_CANCEL_ROUTE_TEMPLATE = "/v1/jobs/{job_id}/cancel"
130
156
 
157
+ #: The ``?scope=`` query parameter name accepted by ``GET /v1/jobs`` and
158
+ #: every per-job read route (module docstring's "User scoping" section).
159
+ JOB_SCOPE_QUERY_PARAM = "scope"
160
+ #: Default / explicit-opt-in-only values for :data:`JOB_SCOPE_QUERY_PARAM`.
161
+ JOB_SCOPE_MINE = "mine"
162
+ 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).
165
+ JOB_LIST_SCOPES = (JOB_SCOPE_MINE, JOB_SCOPE_ORG)
166
+
131
167
  #: Upper bound on the log bytes a single ``/logs`` response carries. The server
132
168
  #: may return fewer bytes (never more); clients keep reading from ``next_offset``.
133
169
  #: This is a server-side response-size POLICY value, not a structural wire
@@ -247,6 +283,18 @@ class JobRecord:
247
283
  whenever :attr:`status_reason` is ``None``. Engine-neutral by contract (a user
248
284
  surface: never names the underlying simulation runtime)."""
249
285
 
286
+ submitted_by_user_id: Optional[str] = None
287
+ """Id of the user who submitted this run, or ``None`` when unknown (the
288
+ submitting user's account was later deleted — the id column is nullable
289
+ with ``ON DELETE SET NULL``, so a job's provenance can outlive the user).
290
+
291
+ Additive + tolerant like ``assets``/``seed``/``status_reason``: a control
292
+ plane that predates the field omits it and a consumer sees the ``None``
293
+ default. Populated on every record regardless of ``?scope=`` — it is what
294
+ makes an ``org``-scoped listing attributable (module docstring's "User
295
+ scoping" section): a shared view of everyone's jobs must say whose job
296
+ each row is, not just that it exists."""
297
+
250
298
  outputs: Optional[JobOutputsSummary] = None
251
299
  """Typed outputs summary of the run — a **rendering input,
252
300
  never a status**: ``completed ⚠ 1 anomaly`` is drawn from here while
@@ -28,14 +28,13 @@ from simulo.interfaces.runtime.policy import PolicyProtocol
28
28
  from simulo.interfaces.runtime.scenario import ScenarioProtocol
29
29
  from simulo.interfaces.runtime.task import TaskProtocol
30
30
  from simulo.interfaces.runtime.tensors import TensorLike
31
- from simulo.interfaces.runtime.trainer import PolicyExporterProtocol, TrainerProtocol
31
+ from simulo.interfaces.runtime.trainer import TrainerProtocol
32
32
 
33
33
  __all__ = [
34
34
  "TensorLike",
35
35
  "TaskProtocol",
36
36
  "PolicyProtocol",
37
37
  "TrainerProtocol",
38
- "PolicyExporterProtocol",
39
38
  "PlayerProtocol",
40
39
  "LearningEnvProtocol",
41
40
  "ScenarioProtocol",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simulo-interfaces
3
- Version: 0.10.0
3
+ Version: 0.12.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
@@ -16,13 +16,14 @@ Classifier: Typing :: Typed
16
16
  Requires-Python: >=3.11
17
17
  Description-Content-Type: text/markdown
18
18
  Provides-Extra: dev
19
- Requires-Dist: black>=24.0; extra == "dev"
20
- Requires-Dist: isort>=5.13; extra == "dev"
21
- Requires-Dist: ruff>=0.5; extra == "dev"
22
- Requires-Dist: mypy>=1.10; extra == "dev"
19
+ Requires-Dist: black==26.5.1; extra == "dev"
20
+ Requires-Dist: isort==8.0.1; extra == "dev"
21
+ Requires-Dist: ruff==0.15.21; extra == "dev"
22
+ Requires-Dist: mypy==2.3.0; extra == "dev"
23
23
  Requires-Dist: pytest>=8.2; extra == "dev"
24
24
  Requires-Dist: build>=1.2; extra == "dev"
25
25
  Requires-Dist: twine>=5.1; extra == "dev"
26
+ Requires-Dist: packaging>=23; extra == "dev"
26
27
  Provides-Extra: release
27
28
  Requires-Dist: commitizen>=3.27; extra == "release"
28
29
  Requires-Dist: build>=1.2; extra == "release"
@@ -14,6 +14,7 @@ src/simulo/interfaces/authoring/materials.py
14
14
  src/simulo/interfaces/authoring/physics.py
15
15
  src/simulo/interfaces/authoring/pose.py
16
16
  src/simulo/interfaces/authoring/primitives.py
17
+ src/simulo/interfaces/authoring/prop.py
17
18
  src/simulo/interfaces/authoring/record.py
18
19
  src/simulo/interfaces/authoring/robot.py
19
20
  src/simulo/interfaces/authoring/scene.py
@@ -1,12 +1,13 @@
1
1
 
2
2
  [dev]
3
- black>=24.0
4
- isort>=5.13
5
- ruff>=0.5
6
- mypy>=1.10
3
+ black==26.5.1
4
+ isort==8.0.1
5
+ ruff==0.15.21
6
+ mypy==2.3.0
7
7
  pytest>=8.2
8
8
  build>=1.2
9
9
  twine>=5.1
10
+ packaging>=23
10
11
 
11
12
  [release]
12
13
  commitizen>=3.27