feat(observatory): attest recording equipment and capture profiles

This commit is contained in:
DCCONSTRUCTIONS
2026-09-02 00:57:27 +03:00
parent 7025e173a3
commit d655d6998d
17 changed files with 1089 additions and 42 deletions
@@ -124,8 +124,8 @@ class ObservationOnlyAuthority:
@dataclass(frozen=True, slots=True)
class K1RecordedSourceRequirements:
"""Typed capability matcher for one class of recorded K1 sessions."""
class RecordedSourceRequirements:
"""Typed capability matcher for one exact recorded capture contract."""
plugin_id: str
archive_id: str
@@ -206,6 +206,11 @@ class K1RecordedSourceRequirements:
}
# Compatibility name retained for promoted package readers. Canonical JSON
# and all digests remain byte-for-byte unchanged.
K1RecordedSourceRequirements = RecordedSourceRequirements
@dataclass(frozen=True, slots=True)
class PortableSourceAdapterIdentity:
adapter_id: str
@@ -26,6 +26,7 @@ from k1link.observatory.source_admission import (
PortableRecordedSourceCapability,
PortableSourceAdmissionError,
)
from k1link.sessions.equipment import EquipmentCaptureRegistry
from k1link.sessions.models import SessionSummary
PORTABLE_LABORATORY_SETUP_CATALOG_SCHEMA: Final = (
@@ -93,12 +94,14 @@ type PortableSourceCapabilityProbe = Callable[[str], PortableRecordedSourceCapab
class _SourceCompatibility:
compatible: bool
capability: PortableRecordedSourceCapability | None
reason_code: str
reason: str
def as_dict(self) -> dict[str, object]:
return {
"outcome": "pass" if self.compatible else "blocked",
"compatible": self.compatible,
"reason_code": self.reason_code,
"reason": self.reason,
}
@@ -112,12 +115,14 @@ class PortableSetupProjector:
registry: PortableRunDefinitionRegistry,
capability_probe: PortableDefinitionCapabilityProbe,
dispatch_available: bool = False,
equipment_capture_registry: EquipmentCaptureRegistry | None = None,
) -> None:
if not hasattr(capability_probe, "probe"):
raise PortableSetupProjectionError("portable source capability probe is unavailable")
self._registry = registry
self._capability_probe = capability_probe
self._dispatch_available = dispatch_available
self._equipment_capture_registry = equipment_capture_registry
for definition in registry.definitions:
_validate_model_presentation(definition)
if definition.authority.as_dict() != _OBSERVATION_ONLY_AUTHORITY:
@@ -159,7 +164,7 @@ class PortableSetupProjector:
except PortableRunDefinitionRegistryError as exc:
raise PortableSetupProjectionError("portable setup is unavailable") from exc
presentation = _presentation(definition)
compatibility = self._probe_source(definition, source.session_id, presentation)
compatibility = self._probe_source(definition, source, presentation)
executor = definition.executor
submission_allowed = (
compatibility.compatible and executor.ready and self._dispatch_available
@@ -204,9 +209,51 @@ class PortableSetupProjector:
def _probe_source(
self,
definition: PortableRunDefinition,
source_session_id: str,
source: SessionSummary,
presentation: dict[str, str],
) -> _SourceCompatibility:
source_session_id = source.session_id
if self._equipment_capture_registry is not None:
expected_profile = (
self._equipment_capture_registry.compatible_profile_for_requirements(
definition.source_requirements.as_dict()
)
)
if expected_profile is None:
raise PortableSetupProjectionError(
"portable source requirements have no capture profile allowlist"
)
attestation = source.capture_attestation
if attestation is None:
return _SourceCompatibility(
compatible=False,
capability=None,
reason_code="capture-attestation-missing",
reason="У записи нет проверенной привязки к оборудованию и профилю записи.",
)
if (
attestation.equipment_model_id
!= expected_profile.equipment.equipment_model_id
or attestation.equipment_model_sha256
!= expected_profile.equipment.equipment_model_sha256
):
return _SourceCompatibility(
compatible=False,
capability=None,
reason_code="equipment-model-mismatch",
reason="Модель оборудования записи не поддерживается этим профилем.",
)
if (
attestation.capture_profile_id != expected_profile.capture_profile_id
or attestation.capture_profile_sha256
!= expected_profile.capture_profile_sha256
):
return _SourceCompatibility(
compatible=False,
capability=None,
reason_code="capture-profile-not-allowed",
reason="Профиль записи не входит в точный список совместимости.",
)
try:
capability = self._capability_probe.probe(
source_session_id=source_session_id,
@@ -217,6 +264,7 @@ class PortableSetupProjector:
return _SourceCompatibility(
compatible=False,
capability=None,
reason_code="source-contract-mismatch",
reason=presentation["incompatible"],
)
if not isinstance(capability, PortableRecordedSourceCapability):
@@ -230,6 +278,7 @@ class PortableSetupProjector:
return _SourceCompatibility(
compatible=True,
capability=capability,
reason_code="source-compatible",
reason=presentation["compatible"],
)
+7 -2
View File
@@ -76,8 +76,8 @@ class PortableSourceAdmissionStaleError(PortableSourceAdmissionIntegrityError):
@dataclass(frozen=True, slots=True)
class RecordedK1SourceRequirements:
"""Typed matcher owned by a portable RunDefinition.
class RecordedSourceRequirements:
"""Typed matcher owned by a portable recorded-source RunDefinition.
``camera_init_sha256`` is the exact ISO-BMFF initialization segment for the
admitted codec/resolution profile. Width and height are asserted by that
@@ -173,6 +173,11 @@ class RecordedK1SourceRequirements:
return _sha256(self.adapter_document())
# Compatibility import for already promoted packages. New code uses the
# equipment-neutral name; the serialized contract is unchanged.
RecordedK1SourceRequirements = RecordedSourceRequirements
@dataclass(frozen=True, slots=True)
class PortableRecordedSourceCapability:
"""Cheap catalog/summary attestation used by compatibility surfaces.