Merge pull request #543 from AssetOverflow/docs/reconcile-sensorium-adr-status
Reconcile sensorium ADR statuses + fail-closed efferent gate (ADR-0198 §3)
This commit is contained in:
commit
4006a8eb1a
10 changed files with 118 additions and 12 deletions
|
|
@ -1,7 +1,8 @@
|
|||
# ADR-0181: CORE-native Audio Compiler over the Delta-CRDT Substrate
|
||||
|
||||
**Status:** Proposed
|
||||
**Status:** Accepted — implementation landed
|
||||
**Date:** 2026-05-29
|
||||
**Implementation:** `sensorium/audio/` + `evals/audio_sensorium/`. Proof obligations are covered by `tests/test_audio_compiler.py`, `tests/test_audio_crdt_merge.py`, `tests/test_audio_eval_gates.py`, `tests/test_audio_pack_manifest.py`, and `tests/test_audio_sensorium_mount.py`.
|
||||
**Authors:** Joshua M. Shay, Core R&D Engine
|
||||
**Domains:** `sensorium/audio/`, `sensorium/adapters/audio.py`, `packs/audio/`, `core-rs/src/vault.rs` (read-only contract), `evals/audio_sensorium/`
|
||||
**Depends on:** ADR-0013 (Sensorium Multimodal Protocol), ADR-0180 (Delta-CRDT Sharded Substrate)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
# ADR-0197: CORE-native Vision Compiler over the Delta-CRDT Substrate
|
||||
|
||||
**Status:** Proposed
|
||||
**Status:** Accepted — implementation landed
|
||||
**Date:** 2026-05-31
|
||||
**Implementation:** `sensorium/vision/` + `sensorium/adapters/vision.py`, `packs/vision/vision_core_v1/` (PR #537). Proof obligations are covered by `tests/test_vision_compiler.py`, `tests/test_vision_crdt_merge.py`, `tests/test_vision_sensorium_mount.py`, and `tests/test_vision_eval_gates.py`.
|
||||
**Authors:** Joshua M. Shay, Core R&D Engine
|
||||
**Domains:** `sensorium/vision/`, `sensorium/adapters/vision.py`, `packs/vision/`, `core-rs/src/vault.rs` (read-only contract), `evals/vision_sensorium/`
|
||||
**Depends on:** ADR-0013 (Sensorium Multimodal Protocol), ADR-0180 (Delta-CRDT Sharded Substrate), ADR-0181 (Audio Compiler — structural precedent)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# ADR-0198: Motor as Efferent Modality — Protocol Gap & Governance (Design Spike)
|
||||
|
||||
**Status:** Proposed (design spike — no implementation)
|
||||
**Status:** Accepted (design spike) — Gap A protocol change + a baseline efferent gate have landed; the §3 verdict-lowering and the motor compiler/decoder remain deferred. See **Implementation Status** below.
|
||||
**Date:** 2026-05-31
|
||||
**Authors:** Joshua M. Shay, Core R&D Engine
|
||||
**Domains:** `sensorium/protocol.py`, `sensorium/registry.py`, `packs/motor/` *(future)*, governance packs (ADR-0029/0033/0036/0037)
|
||||
|
|
@ -9,6 +9,16 @@
|
|||
|
||||
---
|
||||
|
||||
## Implementation Status (reconciled 2026-06-04)
|
||||
|
||||
**Gap A has landed.** `ModalityRegistry.decode(pack_id, mv, *, authority)` with a required, never-optional `AuthorityToken`, plus a baseline `DefaultEfferentGate` (`sensorium/efferent.py`, PR #541). The §1.2 Gap B ordering is **proven**: `tests/test_efferent_gate.py::test_registry_uses_default_efferent_gate_before_decoder` asserts `decoder.calls == 0` on refusal — the gate refuses *in the manifold*, before any command is formed. Traces are hash-only (no `mv`, no ndarray/bytes).
|
||||
|
||||
**Deferred — blocking obligations before any motor decoder mounts:**
|
||||
|
||||
1. **§3 verdict-lowering is NOT implemented — but is now enforced fail-closed.** `DefaultEfferentGate` admits on capability-token + `(32,)` vector-shape only (`enforces_action_verdicts` is `False`); it does **not** lower the decoded motor versor into the safety/ethics pack verdicts of ADR-0029/0033/0036/0037. To keep §1.2 Gap B from silently degrading, `ModalityRegistry.decode`/`decode_batch` now **refuse fail-closed** any emission through a gate whose `enforces_action_verdicts` is False, unless an explicit `allow_unverified_efferent=True` sandbox opt-in is set (tests only). A real motor decoder therefore *cannot* emit through the capability-only gate — the §3 verdict-lowering gate must be built and installed first. Proven by `tests/test_efferent_gate.py::test_registry_fails_closed_for_actuating_decode_through_capability_only_gate` (the decoder never runs) and `::test_registry_admits_decode_through_verdict_enforcing_gate`. Implementing the §3 lowering itself remains deferred behind the dedicated motor governance ADR (item 3).
|
||||
2. **The motor compiler/decoder itself remains out of scope** (per §5).
|
||||
3. **A dedicated motor governance ADR** ratifying the §3 lowering against ADR-0029/0033/0036/0037 remains a prerequisite (per §5).
|
||||
|
||||
## 1. Why motor is not "just another modality"
|
||||
|
||||
Every modality landed so far is **afferent**: signal comes *in*, crosses the `ProjectionHead` boundary, becomes a `(32,)` versor. Motor is **efferent**: meaning leaves the manifold and becomes action in the world. The protocol already anticipates this asymmetry — `sensorium/protocol.py` defines:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
# ADR-0208: Environmental Sensorium Loop
|
||||
|
||||
**Status:** Proposed
|
||||
**Status:** Accepted — implementation landed (afferent scope)
|
||||
**Date:** 2026-06-04
|
||||
**Implementation:** `sensorium/environment/frame.py` (PR #540) + `sensorium/environment/harness.py` (PR #541). Proof obligations are covered by falsifiable tests in `tests/test_observation_frame_contract.py` (order-invariant frame trace hash, `merge_key` dedup, `TypeError` on raw-payload units, `ValueError` on efferent units) and `tests/test_observation_frame_harness.py`. Disjoint from the GSM8K serving path (no import of `generate.derivation` / `core.reliability_gate`).
|
||||
**Domains:** `sensorium/environment/`, `sensorium/compiler/`, `sensorium/*`, future sensorimotor compilers
|
||||
**Depends on:** ADR-0013, ADR-0180, ADR-0181, ADR-0197, ADR-0198
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
# ADR-0209: Sensorimotor Feedback Is Afferent
|
||||
|
||||
**Status:** Proposed
|
||||
**Status:** Accepted — implementation landed (afferent scope)
|
||||
**Date:** 2026-06-04
|
||||
**Implementation:** `sensorium/sensorimotor/` (PR #540) + `sensorium/adapters/sensorimotor.py`, `packs/sensorimotor/sensorimotor_core_v1/` (PR #541). Proof obligations are covered by falsifiable tests in `tests/test_sensorimotor_contract.py` (deterministic unit + merge key, IR replay, idempotent delta merge, **no `decode`/`decode_batch` path**, hash-only trace with no command/trajectory payload) and `tests/test_sensorimotor_pack_manifest.py`.
|
||||
**Domains:** `sensorium/sensorimotor/`, `sensorium/protocol.py`, future robotics integrations
|
||||
**Depends on:** ADR-0013, ADR-0198, ADR-0208
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,15 @@ class DefaultEfferentGate:
|
|||
"shape": [CL41_DIM],
|
||||
})
|
||||
|
||||
@property
|
||||
def enforces_action_verdicts(self) -> bool:
|
||||
"""Capability/shape pre-filter only — does NOT lower the decoded action
|
||||
into the safety/ethics pack verdicts required by ADR-0198 §3. The
|
||||
registry refuses actuating emission through this gate unless an explicit
|
||||
sandbox opt-in is set. A future §3 verdict-enforcing gate returns True.
|
||||
"""
|
||||
return False
|
||||
|
||||
def admit(
|
||||
self,
|
||||
pack_id: str,
|
||||
|
|
|
|||
|
|
@ -107,7 +107,14 @@ class EfferentVerdict:
|
|||
|
||||
@runtime_checkable
|
||||
class EfferentGate(Protocol):
|
||||
"""Runtime gate for output actions. Runs before SurfaceDecoder.decode."""
|
||||
"""Runtime gate for output actions. Runs before SurfaceDecoder.decode.
|
||||
|
||||
Optional attribute ``enforces_action_verdicts: bool`` (absent ⇒ False):
|
||||
True only for a gate that lowers the decoded action into the safety/ethics
|
||||
pack verdicts required by ADR-0198 §3. ``ModalityRegistry`` fails closed and
|
||||
refuses actuating emission through any gate where this is False, unless an
|
||||
explicit ``allow_unverified_efferent`` sandbox opt-in is set.
|
||||
"""
|
||||
|
||||
def admit(
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -36,9 +36,19 @@ class ModalityRegistry:
|
|||
mv = registry.project("en", "beginning")
|
||||
"""
|
||||
|
||||
def __init__(self, *, efferent_gate: EfferentGate | None = None) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
efferent_gate: EfferentGate | None = None,
|
||||
allow_unverified_efferent: bool = False,
|
||||
) -> None:
|
||||
self._packs: dict[str, ModalityPack] = {}
|
||||
self._efferent_gate = efferent_gate
|
||||
# Fail-closed (ADR-0198 §3 / §1.2 Gap B): an efferent gate that does not
|
||||
# lower the decoded action into safety/ethics pack verdicts may not
|
||||
# authorize a real emission. This opt-in exercises the capability
|
||||
# pre-filter in isolation (tests only); never set it on an actuating path.
|
||||
self._allow_unverified_efferent = allow_unverified_efferent
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Mount
|
||||
|
|
@ -123,6 +133,28 @@ class ModalityRegistry:
|
|||
)
|
||||
return mvs.astype(np.float32)
|
||||
|
||||
def _refuse_unverified_efferent(self, pack_id: str, authority: AuthorityToken) -> None:
|
||||
"""Fail closed unless the gate enforces ADR-0198 §3 action verdicts.
|
||||
|
||||
``DefaultEfferentGate`` is a capability/shape pre-filter only; it does
|
||||
not lower the decoded action into the safety/ethics pack verdicts that
|
||||
§3 requires. Until a verdict-enforcing gate exists, an actuating
|
||||
emission must be refused — no motor decoder may rely on capability
|
||||
tokens alone. ``allow_unverified_efferent`` is a sandbox/testing escape
|
||||
hatch for exercising the pre-filter in isolation.
|
||||
"""
|
||||
if self._allow_unverified_efferent:
|
||||
return
|
||||
if getattr(self._efferent_gate, "enforces_action_verdicts", False):
|
||||
return
|
||||
verdict = EfferentVerdict(
|
||||
admitted=False,
|
||||
reason="efferent gate does not enforce ADR-0198 §3 action verdicts",
|
||||
authority_sha256=authority.authority_sha256,
|
||||
policy_sha256="deny-unverified-efferent",
|
||||
)
|
||||
raise EfferentRefusal(pack_id, verdict)
|
||||
|
||||
def decode(self, pack_id: str, mv: np.ndarray, *, authority: AuthorityToken) -> Any:
|
||||
"""Decode a manifold vector through a governed SurfaceDecoder.
|
||||
|
||||
|
|
@ -147,6 +179,7 @@ class ModalityRegistry:
|
|||
policy_sha256="deny-by-default",
|
||||
)
|
||||
raise EfferentRefusal(pack_id, verdict)
|
||||
self._refuse_unverified_efferent(pack_id, authority)
|
||||
verdict = self._efferent_gate.admit(pack_id, vec, authority)
|
||||
if not verdict.admitted:
|
||||
raise EfferentRefusal(pack_id, verdict)
|
||||
|
|
@ -179,6 +212,7 @@ class ModalityRegistry:
|
|||
policy_sha256="deny-by-default",
|
||||
)
|
||||
raise EfferentRefusal(pack_id, verdict)
|
||||
self._refuse_unverified_efferent(pack_id, authority)
|
||||
for vec in arr:
|
||||
verdict = self._efferent_gate.admit(pack_id, vec, authority)
|
||||
if not verdict.admitted:
|
||||
|
|
|
|||
|
|
@ -4,7 +4,14 @@ import numpy as np
|
|||
import pytest
|
||||
|
||||
from sensorium.efferent import DefaultEfferentGate
|
||||
from sensorium.protocol import AuthorityToken, EfferentRefusal, Modality, ModalityPack, ModalityVocabulary
|
||||
from sensorium.protocol import (
|
||||
AuthorityToken,
|
||||
EfferentRefusal,
|
||||
EfferentVerdict,
|
||||
Modality,
|
||||
ModalityPack,
|
||||
ModalityVocabulary,
|
||||
)
|
||||
from sensorium.registry import ModalityRegistry
|
||||
|
||||
|
||||
|
|
@ -81,7 +88,9 @@ def test_default_efferent_trace_is_hash_only():
|
|||
|
||||
def test_registry_uses_default_efferent_gate_before_decoder():
|
||||
decoder = _Decoder()
|
||||
reg = ModalityRegistry(efferent_gate=DefaultEfferentGate())
|
||||
# Exercises the capability pre-filter in isolation: the capability/shape
|
||||
# gate is not verdict-enforcing, so the sandbox opt-in is required here.
|
||||
reg = ModalityRegistry(efferent_gate=DefaultEfferentGate(), allow_unverified_efferent=True)
|
||||
reg.mount(_pack(decoder))
|
||||
|
||||
with pytest.raises(EfferentRefusal, match="missing capability"):
|
||||
|
|
@ -90,3 +99,36 @@ def test_registry_uses_default_efferent_gate_before_decoder():
|
|||
|
||||
assert reg.decode("motor_test", _mv(), authority=_authority("decode:motor_test")) == "decoded"
|
||||
assert decoder.calls == 1
|
||||
|
||||
|
||||
def test_registry_fails_closed_for_actuating_decode_through_capability_only_gate():
|
||||
"""ADR-0198 §3 / §1.2 Gap B: a capability/shape gate must not authorize a
|
||||
real emission. With no sandbox opt-in the decode fails closed and the
|
||||
decoder never runs."""
|
||||
decoder = _Decoder()
|
||||
reg = ModalityRegistry(efferent_gate=DefaultEfferentGate())
|
||||
reg.mount(_pack(decoder))
|
||||
with pytest.raises(EfferentRefusal, match="action verdicts"):
|
||||
reg.decode("motor_test", _mv(), authority=_authority("decode:motor_test"))
|
||||
assert decoder.calls == 0
|
||||
|
||||
|
||||
def test_registry_admits_decode_through_verdict_enforcing_gate():
|
||||
"""A gate that enforces ADR-0198 §3 action verdicts needs no sandbox opt-in."""
|
||||
|
||||
class _VerdictGate:
|
||||
enforces_action_verdicts = True
|
||||
|
||||
def admit(self, pack_id: str, mv: np.ndarray, authority: AuthorityToken) -> EfferentVerdict:
|
||||
return EfferentVerdict(
|
||||
admitted=True,
|
||||
reason="admitted",
|
||||
authority_sha256=authority.authority_sha256,
|
||||
policy_sha256="verdict-test",
|
||||
)
|
||||
|
||||
decoder = _Decoder()
|
||||
reg = ModalityRegistry(efferent_gate=_VerdictGate())
|
||||
reg.mount(_pack(decoder))
|
||||
assert reg.decode("motor_test", _mv(), authority=_authority("decode:motor_test")) == "decoded"
|
||||
assert decoder.calls == 1
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ def test_decode_denies_by_default_before_decoder_runs():
|
|||
def test_decode_requires_admitting_gate_before_surface_decode():
|
||||
decoder = _Decoder()
|
||||
gate = _Gate(admitted=True)
|
||||
reg = ModalityRegistry(efferent_gate=gate)
|
||||
reg = ModalityRegistry(efferent_gate=gate, allow_unverified_efferent=True)
|
||||
reg.mount(_pack(decoder))
|
||||
assert reg.decode("motor_test", _mv(), authority=_authority()) == "decoded:1.0"
|
||||
assert gate.calls == 1
|
||||
|
|
@ -89,7 +89,7 @@ def test_decode_requires_admitting_gate_before_surface_decode():
|
|||
|
||||
def test_decode_refusal_does_not_call_decoder():
|
||||
decoder = _Decoder()
|
||||
reg = ModalityRegistry(efferent_gate=_Gate(admitted=False))
|
||||
reg = ModalityRegistry(efferent_gate=_Gate(admitted=False), allow_unverified_efferent=True)
|
||||
reg.mount(_pack(decoder))
|
||||
with pytest.raises(EfferentRefusal, match="denied"):
|
||||
reg.decode("motor_test", _mv(), authority=_authority())
|
||||
|
|
@ -99,7 +99,7 @@ def test_decode_refusal_does_not_call_decoder():
|
|||
def test_decode_batch_admits_all_before_decoding_any_surface():
|
||||
decoder = _Decoder()
|
||||
gate = _Gate(admitted=True, deny_after=1)
|
||||
reg = ModalityRegistry(efferent_gate=gate)
|
||||
reg = ModalityRegistry(efferent_gate=gate, allow_unverified_efferent=True)
|
||||
reg.mount(_pack(decoder))
|
||||
batch = np.stack([_mv(), _mv()])
|
||||
with pytest.raises(EfferentRefusal, match="denied"):
|
||||
|
|
|
|||
Loading…
Reference in a new issue