Skip to content

Phase E1: semantic domain profiles for StreamSpec and CapabilitySpec - #66

Merged
txmed82 merged 4 commits into
mainfrom
feat/phase-e1-semantic-domain-profiles
Sep 10, 2026
Merged

Phase E1: semantic domain profiles for StreamSpec and CapabilitySpec#66
txmed82 merged 4 commits into
mainfrom
feat/phase-e1-semantic-domain-profiles

Conversation

@txmed82

@txmed82 txmed82 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

What

  • Extends StreamSpec with domain-semantic profile fields: dtype, shape, unit, coordinate_frame, valid_range, controller_id, camera_calibration, joint_order, invalid_depth_encoding, and privileged.
  • Extends CapabilitySpec with stream_profiles and accepts_privileged.
  • Enforces semantic profile validation in 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.
  • Adds comprehensive unit tests in tests/test_multi_modality_contracts.py.
  • Updates docs/NEXT_STATUS.md ledger.

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 mypy clean.
  • Test suites green (87 passed).

@txmed82

txmed82 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

@droid review

@factory-droid

factory-droid Bot commented Sep 10, 2026

Copy link
Copy Markdown

Droid finished @txmed82's task —— View job


Security Review

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):

  • [P1] src/or_audit/eval/contracts.py:198cap_profiles is keyed only by StreamSpec.id, so the cap_profiles.get(intf_stream.schema_id) fallback never matches; schema_id-based profiles are silently skipped and semantic mismatches can bind when they should be refused. Suggestion block included in the inline comment.

@factory-droid

factory-droid Bot commented Sep 10, 2026

Copy link
Copy Markdown

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).

@txmed82

txmed82 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

@droid review

@factory-droid

factory-droid Bot commented Sep 10, 2026

Copy link
Copy Markdown

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:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

Suggested change
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:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

Suggested change
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})

@txmed82

txmed82 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

@droid review

@factory-droid

factory-droid Bot commented Sep 10, 2026

Copy link
Copy Markdown

Droid encountered an error —— View job

Droid Exec exited with code 1:
Tool "Create" skipped (GPT-5.2 uses ApplyPatch instead).
Tool "Edit" skipped (GPT-5.2 uses ApplyPatch instead).

coordinate_frame: str = ""
valid_range: tuple[float, float] | None = None
controller_id: str = ""
camera_calibration: dict[str, Any] = Field(default_factory=dict)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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] = {}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

@txmed82

txmed82 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

@droid review

@factory-droid

factory-droid Bot commented Sep 10, 2026

Copy link
Copy Markdown

Droid encountered an error —— View job

Droid Exec exited with code 1:
Tool "Edit" skipped (GPT-5.2 uses ApplyPatch instead).
Tool "Create" skipped (GPT-5.2 uses ApplyPatch instead).

@txmed82
txmed82 merged commit 805563f into main Sep 10, 2026
6 checks passed
@txmed82
txmed82 deleted the feat/phase-e1-semantic-domain-profiles branch September 10, 2026 23:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants