Phase E1: semantic domain profiles for StreamSpec and CapabilitySpec - #66
Conversation
|
@droid review |
|
Droid finished @txmed82's task —— View job Review complete. The semantic domain profile additions look solid and the tests cover the key mismatch cases. There is one correctness issue in stream profile matching that can silently skip schema_id-based profiles, so it's worth fixing before merge. Findings (1):
|
|
Droid finished @txmed82's task —— View job Droid review complete Adds semantic stream profile fields, enforces matching in CapabilitySpec.satisfies(), and includes good mismatch tests. One correctness issue: the schema_id fallback lookup for stream_profiles is currently ineffective, so schema-based profile matching will not work as intended. Posted 1 inline comment (1 P1). |
…ce stream semantics
|
@droid review |
|
Droid finished @txmed82's task —— View job Review complete — 2 comments posted. The core satisfies() enforcement is sound and the new tests accurately cover the declared semantics (units, frames, controllers, joint orders, privileged streams, wildcard interaction). Two gaps: camera_calibration is added as a semantic profile field but is never enforced or even read, so calibration-only differences silently bind; and duplicate stream_profiles ids silently overwrite rather than being rejected the way InterfaceSpec rejects duplicate stream ids. |
| ): | ||
| return False | ||
| cap_profiles = {s.id: s for s in self.stream_profiles} if self.stream_profiles else {} | ||
| for intf_stream in interface.streams: |
There was a problem hiding this comment.
[P1] Fix stream_profiles lookup by schema_id
CapabilitySpec.satisfies() falls back to cap_profiles.get(intf_stream.schema_id), but cap_profiles is keyed only by StreamSpec.id, so schema-based lookups will never match (unless id == schema_id by coincidence) and semantic mismatches can bind when they should be refused.
| for intf_stream in interface.streams: | |
| cap_profiles: dict[str, StreamSpec] = {} | |
| for s in self.stream_profiles: | |
| cap_profiles[s.id] = s | |
| cap_profiles[s.schema_id] = s |
| ): | ||
| return False | ||
| cap_profiles = {s.id: s for s in self.stream_profiles} if self.stream_profiles else {} | ||
| for intf_stream in interface.streams: |
There was a problem hiding this comment.
[P1] Fix schema_id matching for stream_profiles
In CapabilitySpec.satisfies(), cap_profiles is keyed only by StreamSpec.id, but the lookup falls back to intf_stream.schema_id; unless a profile’s id was set equal to its schema_id, schema-based matching can never succeed and intended semantic checks can be skipped.
| for intf_stream in interface.streams: | |
| cap_profiles = {s.id: s for s in self.stream_profiles} | |
| cap_profiles.update({s.schema_id: s for s in self.stream_profiles}) |
|
@droid review |
|
Droid encountered an error —— View job |
| coordinate_frame: str = "" | ||
| valid_range: tuple[float, float] | None = None | ||
| controller_id: str = "" | ||
| camera_calibration: dict[str, Any] = Field(default_factory=dict) |
There was a problem hiding this comment.
[P1] camera_calibration is declared as a semantic profile field but never enforced
StreamSpec gains camera_calibration: dict[str, Any] as one of the PR's domain-semantic profile fields, but the field is read nowhere else in the repo: it is missing from the has_semantics computation in CapabilitySpec.satisfies() and there is no matching_profile.camera_calibration != intf_stream.camera_calibration comparison alongside the other per-field checks. As a result, an interface stream whose only semantic declaration is a camera calibration has has_semantics == False and binds to a capability with no stream profile at all, and two profiles with different calibrations bind as equivalent. That contradicts the PR's stated guarantee that equal tensor shapes with different physical meaning must refuse binding, and leaves the field dead in production code.
| ) | ||
| ): | ||
| return False | ||
| cap_profiles: dict[Slug, StreamSpec] = {} |
There was a problem hiding this comment.
[P3] Duplicate stream_profiles ids silently overwrite each other
cap_profiles = {s.id: s for s in self.stream_profiles} silently keeps only the last profile when a capability declares two stream_profiles with the same id. The sibling model InterfaceSpec explicitly rejects duplicate stream ids as a contract error ("declares duplicate stream id" in _shape_matches_mode), so this asymmetry means a malformed capability is accepted and deterministically binds against whichever profile happens to be last in the tuple instead of being rejected. A model validator on CapabilitySpec mirroring the InterfaceSpec duplicate check would close the gap.
|
@droid review |
|
Droid encountered an error —— View job |
What
StreamSpecwith domain-semantic profile fields:dtype,shape,unit,coordinate_frame,valid_range,controller_id,camera_calibration,joint_order,invalid_depth_encoding, andprivileged.CapabilitySpecwithstream_profilesandaccepts_privileged.CapabilitySpec.satisfies(): equal shapes with different physical units (e.g."mm"vs"m"), mismatched coordinate frames (e.g."world"vs"tool_tip"), mismatched controller identities, joint orders, or unauthorized privileged streams refuse binding.tests/test_multi_modality_contracts.py.docs/NEXT_STATUS.mdledger.Why
Phase E1 requires that equal observation or action tensor shapes with different physical meanings (e.g. units or frames of reference) must not bind as equivalent, establishing formal semantic profiles between task interfaces and agent capabilities.
Verify
uv run ruff check --fix && uv run ruff format --check && uv run mypyclean.