diff --git a/ace/application/__init__.py b/ace/application/__init__.py index 16e785e..27aabf9 100644 --- a/ace/application/__init__.py +++ b/ace/application/__init__.py @@ -315,6 +315,7 @@ AuthorizedIntelligenceBuild, IntelligenceBuildEffect, IntelligenceBuildExecutor, + IntelligenceBuildFirstBriefPort, IntelligenceBuildHostServices, IntelligenceBuildPreparedDerivationPort, IntelligenceBuildRecordedSourcePort, @@ -323,6 +324,15 @@ ProductScopedImmutableRecordStore, RecordedSourceReferenceV1, ) +from ace.application.intelligence_build_first_brief import ( + CREATE_FIRST_BRIEF_EFFECT, + INTELLIGENCE_BUILD_FIRST_BRIEF_REQUEST_VERSION, + CoreIntelligenceBuildFirstBriefService, + IntelligenceBuildFirstBriefCognition, + IntelligenceBuildFirstBriefError, + IntelligenceBuildFirstBriefOutcome, + IntelligenceBuildFirstBriefRequestV1Alpha1, +) from ace.application.intelligence_builder import ( ConnectionAgent, ConnectionAgentError, @@ -833,6 +843,7 @@ "IntelligenceBuilderSessionService", "IntelligenceBuildEffect", "IntelligenceBuildExecutor", + "IntelligenceBuildFirstBriefPort", "IntelligenceBuildHostServices", "IntelligenceBuildPreparedDerivationPort", "IntelligenceBuildResourcePagePort", @@ -842,6 +853,13 @@ "ProductScopedImmutableRecordStore", "CoreRecordedSourceAdmissionService", "CorePreparedShiftSignalDerivationService", + "CoreIntelligenceBuildFirstBriefService", + "CREATE_FIRST_BRIEF_EFFECT", + "INTELLIGENCE_BUILD_FIRST_BRIEF_REQUEST_VERSION", + "IntelligenceBuildFirstBriefCognition", + "IntelligenceBuildFirstBriefError", + "IntelligenceBuildFirstBriefOutcome", + "IntelligenceBuildFirstBriefRequestV1Alpha1", "REQUIRED_INTELLIGENCE_BUILD_EFFECTS", "RecordedSourceAcquisitionReceiptV1Alpha1", "RecordedSourceAdmission", diff --git a/ace/application/intelligence_build_execution.py b/ace/application/intelligence_build_execution.py index 7500462..e8b84a7 100644 --- a/ace/application/intelligence_build_execution.py +++ b/ace/application/intelligence_build_execution.py @@ -29,6 +29,10 @@ from ace.intelligence.contracts.common import validate_digest, validate_reference, validate_slug if TYPE_CHECKING: + from ace.application.intelligence_build_first_brief import ( + IntelligenceBuildFirstBriefOutcome, + IntelligenceBuildFirstBriefRequestV1Alpha1, + ) from ace.application.prepared_shift_signal import ( PreparedShiftSignalDerivationOutcome, PreparedShiftSignalDerivationRequestV1Alpha1, @@ -238,6 +242,15 @@ async def derive( ) -> "PreparedShiftSignalDerivationOutcome": ... +class IntelligenceBuildFirstBriefPort(Protocol): + """Narrow host capability for one exact routed canonical first Brief.""" + + async def create_first_brief( + self, + request: "IntelligenceBuildFirstBriefRequestV1Alpha1", + ) -> "IntelligenceBuildFirstBriefOutcome": ... + + @dataclass(frozen=True, slots=True) class IntelligenceBuildHostServices: """Invocation-scoped capabilities Core grants to one trusted executor.""" @@ -247,6 +260,7 @@ class IntelligenceBuildHostServices: activation_authority: CoreAuthorityResolver recorded_sources: IntelligenceBuildRecordedSourcePort | None = None prepared_derivations: IntelligenceBuildPreparedDerivationPort | None = None + first_brief: IntelligenceBuildFirstBriefPort | None = None class IntelligenceBuildExecutor(Protocol): @@ -262,6 +276,7 @@ async def start( "IntelligenceBuildEffect", "IntelligenceBuildExecutor", "IntelligenceBuildHostServices", + "IntelligenceBuildFirstBriefPort", "IntelligenceBuildPreparedDerivationPort", "IntelligenceBuildResourcePagePort", "IntelligenceBuildRecordedSourcePort", diff --git a/ace/application/intelligence_build_first_brief.py b/ace/application/intelligence_build_first_brief.py new file mode 100644 index 0000000..c6bb26a --- /dev/null +++ b/ace/application/intelligence_build_first_brief.py @@ -0,0 +1,445 @@ +"""Canonical first-Brief composition for one authorized Intelligence build.""" + +from __future__ import annotations + +from dataclasses import dataclass +from datetime import UTC, datetime +from typing import Literal, Self + +from pydantic import ConfigDict, Field, field_validator, model_validator + +from ace.application.brief_synthesis import ( + BriefSynthesisError, + BriefSynthesisService, + PreparedBriefAppendAdmission, +) +from ace.application.domain_activation import ( + CommittedActivationBinding, + DomainActivationAdmissionService, + bind_committed_activation, +) +from ace.application.intelligence_build_execution import ( + AuthorizedIntelligenceBuild, + IntelligenceBuildFirstBriefPort, +) +from ace.application.intelligence_builder import IntelligenceBuilderSessionService +from ace.application.intelligence_builder_activation import ExactCompiledPackResolver +from ace.application.intelligence_builder_activation_contracts import ( + BuilderActivationPlanArtifactV1, + BuilderActivationReceiptArtifactV1, +) +from ace.application.intelligence_builder_contracts import ( + IntelligenceBuilderSessionRevisionV1, + OnboardingArtifactKind, + OnboardingStage, +) +from ace.application.intelligence_ledger import PreparedIntelligenceLedgerService +from ace.core.contracts import FrozenContract, canonical_hash +from ace.core.reasoning import ( + GovernedOperationBindingV1Alpha1, + GovernedReasoningService, + ReasoningExecutionBindingV1Alpha1, +) +from ace.core.records import ImmutableRecordStore +from ace.core.runtime_use import AuthorityUseReceiptV1Alpha1, RuntimeUseResolver +from ace.intelligence.contracts.common import validate_digest, validate_reference +from ace.intelligence.contracts.ledger import AttentionDisposition, resource_reference +from ace.intelligence.contracts.resources import SignalV1Alpha1 +from ace.intelligence.contracts.synthesis import BriefSynthesisRequestV1Alpha1 +from ace.intelligence.packs.runtime import ( + PreparedActivationBindingError, + resolve_brief_synthesis_policy, +) + +INTELLIGENCE_BUILD_FIRST_BRIEF_REQUEST_VERSION = "ace.application.intelligence-build-first-brief-request/v1alpha1" +CREATE_FIRST_BRIEF_EFFECT = "create_first_brief" +INTELLIGENCE_BUILD_OPERATION = "start_intelligence_build" +INTELLIGENCE_BUILD_AUTHORITY = "intelligence_build" + + +class IntelligenceBuildFirstBriefError(RuntimeError): + """The canonical first Brief could not be safely synthesized or replayed.""" + + +class IntelligenceBuildFirstBriefRequestV1Alpha1(FrozenContract): + """Exact durable Builder and routed-derivation coordinates selected for synthesis.""" + + model_config = ConfigDict( + extra="forbid", + frozen=True, + strict=True, + revalidate_instances="always", + validate_default=True, + allow_inf_nan=False, + ) + + contract: Literal["ace.application.intelligence-build-first-brief-request/v1alpha1"] = ( + INTELLIGENCE_BUILD_FIRST_BRIEF_REQUEST_VERSION + ) + session_id: str + session_revision_id: str + session_revision_digest: str + derivation_key: str + attention_receipt_id: str + attention_receipt_digest: str + requested_at: datetime + request_id: str | None = Field(default=None, max_length=240) + request_digest: str | None = Field(default=None, pattern=r"^sha256:[a-f0-9]{64}$") + + @field_validator( + "session_id", + "session_revision_id", + "derivation_key", + "attention_receipt_id", + "request_id", + ) + @classmethod + def validate_refs(cls, value: str | None, info) -> str | None: + return validate_reference(value, name=info.field_name) if value is not None else None + + @field_validator("session_revision_digest", "attention_receipt_digest", "request_digest") + @classmethod + def validate_digests(cls, value: str | None, info) -> str | None: + return validate_digest(value) if value is not None else None + + @field_validator("requested_at") + @classmethod + def normalize_requested_at(cls, value: datetime) -> datetime: + if value.tzinfo is None or value.utcoffset() is None: + raise ValueError("requested_at must include a timezone") + return value.astimezone(UTC) + + @model_validator(mode="after") + def derive_identity(self) -> Self: + material = self.model_dump(mode="json", exclude={"request_id", "request_digest"}) + digest = canonical_hash(material) + expected_id = f"intelligence_build_first_brief:{digest[:32]}" + expected_digest = f"sha256:{digest}" + if self.request_id is not None and self.request_id != expected_id: + raise ValueError("request_id does not match exact first-Brief selection") + if self.request_digest is not None and self.request_digest != expected_digest: + raise ValueError("request_digest does not match exact first-Brief selection") + object.__setattr__(self, "request_id", expected_id) + object.__setattr__(self, "request_digest", expected_digest) + return self + + +@dataclass(frozen=True, slots=True) +class IntelligenceBuildFirstBriefCognition: + """Existing governed cognition and append bindings selected by the Core host.""" + + reasoning: GovernedReasoningService + execution_binding: ReasoningExecutionBindingV1Alpha1 + append_binding: GovernedOperationBindingV1Alpha1 + + +@dataclass(frozen=True, slots=True) +class IntelligenceBuildFirstBriefOutcome: + request: IntelligenceBuildFirstBriefRequestV1Alpha1 + session: IntelligenceBuilderSessionRevisionV1 + binding: CommittedActivationBinding + admission: PreparedBriefAppendAdmission + + @property + def replayed(self) -> bool: + return self.admission.replayed + + +class CoreIntelligenceBuildFirstBriefService(IntelligenceBuildFirstBriefPort): + """Resolve durable Builder policy and delegate one canonical synthesis route.""" + + def __init__( + self, + *, + build: AuthorizedIntelligenceBuild, + sessions: IntelligenceBuilderSessionService, + activations: DomainActivationAdmissionService, + packs: ExactCompiledPackResolver, + records: ImmutableRecordStore, + runtime_use: RuntimeUseResolver, + cognition: IntelligenceBuildFirstBriefCognition | None, + ) -> None: + self.build = build + self.sessions = sessions + self.activations = activations + self.packs = packs + self.records = records + self.runtime_use = runtime_use + self.cognition = self._validate_cognition(cognition) + self._validate_build() + + def _validate_build(self) -> None: + authority = self.build.authority_use + if ( + authority.product_id != self.build.product_id + or authority.actor_ref != self.build.actor_ref + or authority.use_subject_ref != self.build.build_id + or authority.use_subject_digest != self.build.request_digest + or authority.operation != INTELLIGENCE_BUILD_OPERATION + or authority.authority != INTELLIGENCE_BUILD_AUTHORITY + or authority.grant_ref != self.build.request.authority_grant_ref + or CREATE_FIRST_BRIEF_EFFECT not in self.build.request.approved_effects + ): + raise IntelligenceBuildFirstBriefError("authorized build does not cover the exact first-Brief operation") + + def _validate_cognition( + self, + cognition: IntelligenceBuildFirstBriefCognition | None, + ) -> IntelligenceBuildFirstBriefCognition | None: + if cognition is None: + return None + try: + execution = ReasoningExecutionBindingV1Alpha1.model_validate( + cognition.execution_binding.model_dump(mode="python") + ) + append = GovernedOperationBindingV1Alpha1.model_validate(cognition.append_binding.model_dump(mode="python")) + except Exception: + raise IntelligenceBuildFirstBriefError("host cognition bindings failed exact revalidation") from None + if execution.product_id != self.build.product_id or append.product_id != self.build.product_id: + raise IntelligenceBuildFirstBriefError("host cognition bindings crossed authorized product scope") + return IntelligenceBuildFirstBriefCognition( + reasoning=cognition.reasoning, + execution_binding=execution, + append_binding=append, + ) + + async def _resolve_current_build_authority( + self, + request: IntelligenceBuildFirstBriefRequestV1Alpha1, + ) -> AuthorityUseReceiptV1Alpha1: + original = self.build.authority_use + try: + fresh = AuthorityUseReceiptV1Alpha1.model_validate( + ( + await self.runtime_use.resolve_authority_use( + context=original.authenticated_context, + use_subject_ref=self.build.build_id, + use_subject_digest=self.build.request_digest, + operation=INTELLIGENCE_BUILD_OPERATION, + authority=INTELLIGENCE_BUILD_AUTHORITY, + grant_ref=self.build.request.authority_grant_ref, + evaluated_at=request.requested_at, + ) + ).model_dump(mode="python") + ) + except Exception: + raise IntelligenceBuildFirstBriefError("current build authority denied first-Brief synthesis") from None + if ( + fresh.product_id != self.build.product_id + or fresh.actor_ref != self.build.actor_ref + or fresh.authenticated_context != original.authenticated_context + or fresh.use_subject_ref != self.build.build_id + or fresh.use_subject_digest != self.build.request_digest + or fresh.operation != original.operation + or fresh.authority != original.authority + or fresh.grant_ref != original.grant_ref + or fresh.grant_hash != original.grant_hash + or fresh.state_head_precondition != original.state_head_precondition + or fresh.evaluated_at != request.requested_at + ): + raise IntelligenceBuildFirstBriefError("current build authority changed exact authorized material") + return fresh + + async def _load_current_session( + self, + request: IntelligenceBuildFirstBriefRequestV1Alpha1, + ) -> IntelligenceBuilderSessionRevisionV1: + try: + session = await self.sessions.load_latest( + product_id=self.build.product_id, + session_id=request.session_id, + available_at=request.requested_at, + ) + if session is None: + raise IntelligenceBuildFirstBriefError("current Builder session is missing") + await self.sessions.reload_admission(session) + except IntelligenceBuildFirstBriefError: + raise + except Exception: + raise IntelligenceBuildFirstBriefError("current Builder session failed exact durable reload") from None + if ( + session.stage is not OnboardingStage.ACTIVE + or session.revision_id != request.session_revision_id + or session.revision_digest != request.session_revision_digest + or session.occurred_at > request.requested_at + ): + raise IntelligenceBuildFirstBriefError("first Brief requires the exact current active Builder revision") + if session.approval_receipt_ref != self.build.request.activation_approval_receipt_ref: + raise IntelligenceBuildFirstBriefError("active Builder revision crossed the reviewed activation approval") + return session + + @staticmethod + def _one_artifact(session: IntelligenceBuilderSessionRevisionV1, kind: OnboardingArtifactKind): + matches = [item for item in session.artifacts if item.artifact_kind is kind] + if len(matches) != 1: + raise IntelligenceBuildFirstBriefError(f"active Builder revision must bind one exact {kind.value} artifact") + return matches[0] + + async def _load_binding( + self, + *, + session: IntelligenceBuilderSessionRevisionV1, + available_at: datetime, + ) -> CommittedActivationBinding: + plan_ref = self._one_artifact(session, OnboardingArtifactKind.ACTIVATION_PLAN) + receipt_ref = self._one_artifact(session, OnboardingArtifactKind.ACTIVATION_RECEIPT) + try: + plan = await self.sessions.load_artifact( + product_id=self.build.product_id, + reference=plan_ref, + artifact_type=BuilderActivationPlanArtifactV1, + available_at=available_at, + ) + receipt = await self.sessions.load_artifact( + product_id=self.build.product_id, + reference=receipt_ref, + artifact_type=BuilderActivationReceiptArtifactV1, + available_at=available_at, + ) + pack = await self.packs.load_exact(reference=plan.pack) + committed = await self.activations.reload( + product_id=self.build.product_id, + activation_key=receipt.canonical_revision.activation_key, + ) + if pack is None or committed is None: + raise IntelligenceBuildFirstBriefError("exact active Pack or canonical activation is unavailable") + binding = bind_committed_activation(pack=pack, committed=committed) + except IntelligenceBuildFirstBriefError: + raise + except Exception: + raise IntelligenceBuildFirstBriefError("active Builder Pack and activation failed exact reload") from None + commit = binding.commit_receipt + specification = binding.prepared_binding.revision.spec + if ( + plan.session_id != session.session_id + or receipt.session_id != session.session_id + or receipt.activation_plan_artifact_id != plan.artifact_id + or receipt.activation_plan_artifact_digest != plan.artifact_digest + or receipt.source_commit != plan.source_commit + or receipt.canonical_revision != binding.prepared_binding.reference + or receipt.canonical_state_kind != commit.state_kind + or receipt.canonical_commit_receipt_id != commit.receipt_id + or receipt.canonical_commit_receipt_digest != f"sha256:{commit.receipt_hash}" + or plan.spec_id != specification.spec_id + or plan.spec_digest != f"sha256:{specification.spec_hash}" + or plan.pack != specification.pack + or receipt.activated_at != commit.committed_at + or receipt.activated_at > available_at + or commit.approval != self.build.activation_approval + or str(commit.approval.receipt_ref) != self.build.request.activation_approval_receipt_ref + or commit.approval.subject_ref != specification.spec_id + or commit.approval.subject_ref != self.build.request.activation_approval_subject_ref + or str(commit.approval.receipt_ref) != session.approval_receipt_ref + ): + raise IntelligenceBuildFirstBriefError("active Builder artifacts crossed exact canonical material") + return binding + + async def create_first_brief( + self, + request: IntelligenceBuildFirstBriefRequestV1Alpha1, + ) -> IntelligenceBuildFirstBriefOutcome: + try: + exact = IntelligenceBuildFirstBriefRequestV1Alpha1.model_validate(request.model_dump(mode="python")) + except (AttributeError, TypeError, ValueError) as exc: + raise IntelligenceBuildFirstBriefError("first-Brief request failed exact revalidation") from exc + await self._resolve_current_build_authority(exact) + session = await self._load_current_session(exact) + binding = await self._load_binding(session=session, available_at=exact.requested_at) + if self.cognition is None: + raise IntelligenceBuildFirstBriefError( + "governed first-Brief cognition and append composition is not installed" + ) + ledger = PreparedIntelligenceLedgerService(binding=binding, store=self.records) + try: + derivation = await ledger.replay(derivation_key=exact.derivation_key) + if derivation is None: + raise IntelligenceBuildFirstBriefError("exact routed PREPARED derivation is missing") + attention = derivation.attention_receipt + if ( + attention.receipt_id != exact.attention_receipt_id + or attention.receipt_digest != exact.attention_receipt_digest + or attention.disposition is not AttentionDisposition.ROUTE + or attention.brief_template_id is None + or not attention.persona_ids + or attention.activation_revision != binding.prepared_binding.reference + or attention.pack != binding.prepared_binding.revision.spec.pack + or attention.evaluated_at > exact.requested_at + ): + raise IntelligenceBuildFirstBriefError( + "first Brief requires one exact current routed attention receipt" + ) + signals = [item for item in derivation.resources if isinstance(item, SignalV1Alpha1)] + if len(signals) != 1 or attention.signal != resource_reference(signals[0]): + raise IntelligenceBuildFirstBriefError("routed derivation does not bind one exact Signal") + resolve_brief_synthesis_policy( + binding.prepared_binding, + template_id=attention.brief_template_id, + persona_ids=attention.persona_ids, + ) + except IntelligenceBuildFirstBriefError: + raise + except PreparedActivationBindingError: + raise IntelligenceBuildFirstBriefError( + "routed template, personas, or Pack policy failed exact resolution" + ) from None + except Exception: + raise IntelligenceBuildFirstBriefError("routed PREPARED material failed exact replay") from None + signal = signals[0] + synthesis_material = { + "build_id": self.build.build_id, + "request_id": exact.request_id, + "session_revision_id": session.revision_id, + "derivation_key": exact.derivation_key, + "attention_receipt_id": exact.attention_receipt_id, + "activation_revision_id": binding.prepared_binding.reference.revision_id, + "pack_digest": binding.prepared_binding.revision.spec.pack.pack_digest, + } + identity = canonical_hash(synthesis_material) + synthesis_request = BriefSynthesisRequestV1Alpha1( + synthesis_key=f"first_brief_synthesis:{identity[:32]}", + reasoning_attempt_key=f"first_brief_reasoning:{identity[:32]}", + derivation_key=exact.derivation_key, + product_id=self.build.product_id, + authenticated_context=self.build.authority_use.authenticated_context, + activation_revision=binding.prepared_binding.reference, + pack=binding.prepared_binding.revision.spec.pack, + attention_receipt_id=exact.attention_receipt_id, + attention_receipt_digest=exact.attention_receipt_digest, + brief_as_of=signal.as_of, + context_cutoff_at=attention.evaluated_at, + requested_at=exact.requested_at, + ) + service = BriefSynthesisService( + activation_service=self.activations, + pack=binding.prepared_binding.pack, + pack_resolver=self.packs, + store=self.records, + reasoning=self.cognition.reasoning, + execution_binding=self.cognition.execution_binding, + append_binding=self.cognition.append_binding, + clock=lambda: exact.requested_at, + ) + try: + admission = await service.synthesize( + synthesis_request, + delivery_context=self.build.authority_use.authenticated_context, + ) + except BriefSynthesisError as exc: + raise IntelligenceBuildFirstBriefError("canonical first-Brief synthesis failed closed") from exc + return IntelligenceBuildFirstBriefOutcome( + request=exact, + session=session, + binding=binding, + admission=admission, + ) + + +__all__ = [ + "CREATE_FIRST_BRIEF_EFFECT", + "CoreIntelligenceBuildFirstBriefService", + "INTELLIGENCE_BUILD_FIRST_BRIEF_REQUEST_VERSION", + "IntelligenceBuildFirstBriefCognition", + "IntelligenceBuildFirstBriefError", + "IntelligenceBuildFirstBriefOutcome", + "IntelligenceBuildFirstBriefRequestV1Alpha1", +] diff --git a/ace/application/intelligence_build_host.py b/ace/application/intelligence_build_host.py index 8e19399..20fe3c1 100644 --- a/ace/application/intelligence_build_host.py +++ b/ace/application/intelligence_build_host.py @@ -16,6 +16,10 @@ IntelligenceBuildHostServices, IntelligenceBuildResourcePagePort, ) +from ace.application.intelligence_build_first_brief import ( + CoreIntelligenceBuildFirstBriefService, + IntelligenceBuildFirstBriefCognition, +) from ace.application.intelligence_builder import ( INTELLIGENCE_BUILDER_RECORD_SPACE, ONBOARDING_ARTIFACT_RECORD_KIND, @@ -86,10 +90,12 @@ def __init__( governed_state: GovernedStateStore, runtime_use: RuntimeUseResolver, packs: ExactCompiledPackResolver, + first_brief_cognition: IntelligenceBuildFirstBriefCognition | None = None, ) -> None: self.governed_state = governed_state self.runtime_use = runtime_use self.packs = packs + self.first_brief_cognition = first_brief_cognition async def _matching_candidates( self, @@ -341,6 +347,10 @@ async def compose( resources=resources, activation_authority=activation_authority, ) + canonical = DomainActivationAdmissionService( + store=self.governed_state, + authority=activation_authority, + ) return IntelligenceBuildHostServices( records=records, resources=resources, @@ -357,6 +367,19 @@ async def compose( governed_state=self.governed_state, runtime_use=self.runtime_use, ), + first_brief=( + CoreIntelligenceBuildFirstBriefService( + build=build, + sessions=IntelligenceBuilderSessionService(store=records), + activations=canonical, + packs=self.packs, + records=records, + runtime_use=self.runtime_use, + cognition=self.first_brief_cognition, + ) + if self.first_brief_cognition is not None + else None + ), ) diff --git a/tests/intelligence/test_intelligence_build_first_brief.py b/tests/intelligence/test_intelligence_build_first_brief.py new file mode 100644 index 0000000..e89d4be --- /dev/null +++ b/tests/intelligence/test_intelligence_build_first_brief.py @@ -0,0 +1,361 @@ +from __future__ import annotations + +from dataclasses import replace +from datetime import timedelta + +import pytest + +import ace.application as application_api +from ace.application import ( + BuilderActivationPlanArtifactV1, + BuilderActivationReceiptArtifactV1, + CoreIntelligenceBuildFirstBriefService, + IntelligenceBuilderSessionService, + IntelligenceBuildFirstBriefCognition, + IntelligenceBuildFirstBriefError, + IntelligenceBuildFirstBriefRequestV1Alpha1, + OnboardingArtifactKind, + OnboardingArtifactReferenceV1, + OnboardingStage, + OnboardingTransitionAuthority, + ProductScopedImmutableRecordStore, + bind_committed_activation, +) +from ace.application.domain_activation_plan_contracts import ( + ActivationRuntimeState, + DomainActivationCommitReferenceV1Alpha2, +) +from ace.application.intelligence_build_host import DurableIntelligenceBuildHostComposer +from ace.core import ( + AuthenticatedRuntimeContextV1Alpha1, + AuthorityUseReceiptV1Alpha1, + GovernedStateHeadV1, +) +from ace.intelligence import ActivationRevisionReferenceV1Alpha1 +from tests.intelligence.test_brief_synthesis import ( + ACTIVATED_AT, + PRODUCT, + REQUESTED_AT, + _Environment, + _environment, + _PackArchive, +) +from tests.intelligence.test_prepared_shift_signal import ( + _build, + _CurrentBuildAuthority, +) + +pytestmark = pytest.mark.unit + + +async def _binding(env: _Environment): + committed = await env.activation_service.reload( + product_id=PRODUCT, + activation_key=env.request.activation_revision.activation_key, + ) + assert committed is not None + return bind_committed_activation(pack=env.pack, committed=committed) + + +async def _active_session(env: _Environment, binding): + sessions = IntelligenceBuilderSessionService(store=env.store) + actor = env.request.authenticated_context.actor_ref + at = ACTIVATED_AT - timedelta(minutes=2) + current = ( + await sessions.start( + product_id=PRODUCT, + correlation_id="builder_correlation:first-brief", + goal_ref="builder_goal:first-brief", + actor_ref=actor, + occurred_at=at, + ) + ).revision + transitions = ( + (OnboardingStage.SOURCES_CONNECTING, OnboardingTransitionAuthority.AGENT_PROPOSAL, None), + ( + OnboardingStage.SOURCES_READY, + OnboardingTransitionAuthority.HUMAN_CORE_DISPOSITION, + "approval:source-scope", + ), + (OnboardingStage.CONCEPT_MODEL_PROPOSED, OnboardingTransitionAuthority.AGENT_PROPOSAL, None), + ( + OnboardingStage.CONCEPT_MODEL_APPROVED, + OnboardingTransitionAuthority.HUMAN_CORE_DISPOSITION, + "approval:concept-model", + ), + ( + OnboardingStage.INTELLIGENCE_MODEL_PROPOSED, + OnboardingTransitionAuthority.AGENT_PROPOSAL, + None, + ), + ( + OnboardingStage.INTELLIGENCE_MODEL_APPROVED, + OnboardingTransitionAuthority.HUMAN_CORE_DISPOSITION, + "approval:intelligence-model", + ), + (OnboardingStage.FIRST_BRIEFING_READY, OnboardingTransitionAuthority.AGENT_PROPOSAL, None), + ) + for offset, (stage, authority, approval) in enumerate(transitions, start=1): + current = ( + await sessions.advance( + current, + stage=stage, + authority=authority, + actor_ref=actor, + occurred_at=at + timedelta(seconds=offset), + approval_receipt_ref=approval, + ) + ).revision + + source_commit = DomainActivationCommitReferenceV1Alpha2( + product_id=PRODUCT, + activation_key=binding.prepared_binding.reference.activation_key, + activation_id=binding.prepared_binding.reference.activation_id, + state=ActivationRuntimeState.ACTIVE, + plan_id="activation_plan:first-brief", + plan_digest="sha256:" + "1" * 64, + revision=1, + revision_id="activation_revision_v1alpha2:first-brief", + revision_digest="sha256:" + "2" * 64, + commit_receipt_id="activation_commit_v1alpha2:first-brief", + commit_receipt_digest="sha256:" + "3" * 64, + committed_at=ACTIVATED_AT - timedelta(seconds=1), + ) + plan = BuilderActivationPlanArtifactV1( + session_id=current.session_id, + session_revision_id=str(current.revision_id), + session_revision_digest=str(current.revision_digest), + source_commit=source_commit, + spec_id=str(binding.prepared_binding.revision.spec.spec_id), + spec_digest=f"sha256:{binding.prepared_binding.revision.spec.spec_hash}", + pack=binding.prepared_binding.revision.spec.pack, + created_at=ACTIVATED_AT, + ) + await sessions.persist_artifact(product_id=PRODUCT, artifact=plan) + plan_ref = OnboardingArtifactReferenceV1( + artifact_kind=OnboardingArtifactKind.ACTIVATION_PLAN, + artifact_id=str(plan.artifact_id), + artifact_digest=str(plan.artifact_digest), + ) + current = ( + await sessions.advance( + current, + stage=OnboardingStage.ACTIVATION_PENDING, + authority=OnboardingTransitionAuthority.AGENT_PROPOSAL, + actor_ref=actor, + occurred_at=ACTIVATED_AT + timedelta(seconds=2), + artifacts=(plan_ref,), + ) + ).revision + canonical = binding.commit_receipt + receipt = BuilderActivationReceiptArtifactV1( + session_id=current.session_id, + activation_plan_artifact_id=str(plan.artifact_id), + activation_plan_artifact_digest=str(plan.artifact_digest), + source_commit=source_commit, + canonical_revision=ActivationRevisionReferenceV1Alpha1.model_validate( + binding.prepared_binding.reference.model_dump(mode="python") + ), + canonical_state_kind=canonical.state_kind, + canonical_commit_receipt_id=str(canonical.receipt_id), + canonical_commit_receipt_digest=f"sha256:{canonical.receipt_hash}", + activated_at=canonical.committed_at, + ) + await sessions.persist_artifact(product_id=PRODUCT, artifact=receipt) + receipt_ref = OnboardingArtifactReferenceV1( + artifact_kind=OnboardingArtifactKind.ACTIVATION_RECEIPT, + artifact_id=str(receipt.artifact_id), + artifact_digest=str(receipt.artifact_digest), + ) + current = ( + await sessions.advance( + current, + stage=OnboardingStage.ACTIVE, + authority=OnboardingTransitionAuthority.CORE_ACTIVATION, + actor_ref=actor, + occurred_at=ACTIVATED_AT + timedelta(seconds=3), + artifacts=(plan_ref, receipt_ref), + approval_receipt_ref=str(canonical.approval.receipt_ref), + ) + ).revision + return sessions, current + + +async def _stack(*, cognition: bool = True): + env = await _environment() + binding = await _binding(env) + sessions, session = await _active_session(env, binding) + grant = GovernedStateHeadV1( + state_kind="authority_grant", + product_id=PRODUCT, + state_id="authority_grant:atrium-intelligence-build", + sequence=1, + revision_id="authority_grant_revision:first-brief", + commit_receipt_id="governed_state_commit:first-brief", + updated_at=ACTIVATED_AT, + ) + env.store.set_governed_state_head(grant) + build = _build(binding, grant, evaluated_at=REQUESTED_AT) + actor = binding.commit_receipt.actor_ref + original_context = build.authority_use.authenticated_context + context = AuthenticatedRuntimeContextV1Alpha1( + **original_context.model_dump(mode="python", exclude={"actor_ref"}), + actor_ref=actor, + ) + original_authority = build.authority_use + authority_use = AuthorityUseReceiptV1Alpha1( + **original_authority.model_dump( + mode="python", + exclude={ + "actor_ref", + "authenticated_context", + "receipt_id", + "receipt_digest", + }, + ), + actor_ref=actor, + authenticated_context=context, + ) + build = replace(build, actor_ref=actor, authority_use=authority_use) + request = IntelligenceBuildFirstBriefRequestV1Alpha1( + session_id=session.session_id, + session_revision_id=str(session.revision_id), + session_revision_digest=str(session.revision_digest), + derivation_key=env.request.derivation_key, + attention_receipt_id=str(env.attention.receipt_id), + attention_receipt_digest=str(env.attention.receipt_digest), + requested_at=REQUESTED_AT, + ) + composition = ( + IntelligenceBuildFirstBriefCognition( + reasoning=env.service.reasoning, + execution_binding=env.execution_binding, + append_binding=env.append_binding, + ) + if cognition + else None + ) + service = CoreIntelligenceBuildFirstBriefService( + build=build, + sessions=sessions, + activations=env.activation_service, + packs=_PackArchive(env.pack), + records=env.store, + runtime_use=_CurrentBuildAuthority(build.authority_use), + cognition=composition, + ) + return env, build, sessions, session, request, service + + +class _Resources: + async def query(self, **_kwargs): + raise AssertionError("first-Brief host composition must not query projections") + + +@pytest.mark.asyncio +async def test_durable_host_exposes_first_brief_only_with_injected_governed_cognition(): + env, build, _, _, _, service = await _stack() + scoped = ProductScopedImmutableRecordStore(product_id=PRODUCT, store=env.store) + + host = await DurableIntelligenceBuildHostComposer( + governed_state=env.activation_service.store, + runtime_use=_CurrentBuildAuthority(build.authority_use), + packs=_PackArchive(env.pack), + first_brief_cognition=service.cognition, + ).compose( + build=build, + records=scoped, + resources=_Resources(), + activation_authority=env.activation_service.authority, + ) + unavailable = await DurableIntelligenceBuildHostComposer( + governed_state=env.activation_service.store, + runtime_use=_CurrentBuildAuthority(build.authority_use), + packs=_PackArchive(env.pack), + ).compose( + build=build, + records=scoped, + resources=_Resources(), + activation_authority=env.activation_service.authority, + ) + + assert isinstance(host.first_brief, CoreIntelligenceBuildFirstBriefService) + assert unavailable.first_brief is None + + +@pytest.mark.asyncio +async def test_canonical_first_brief_reopens_and_replays_without_second_provider_call(): + env, build, sessions, _, request, service = await _stack() + + first = await service.create_first_brief(request) + restarted = CoreIntelligenceBuildFirstBriefService( + build=build, + sessions=IntelligenceBuilderSessionService(store=env.store), + activations=env.activation_service, + packs=_PackArchive(env.pack), + records=env.store, + runtime_use=_CurrentBuildAuthority(build.authority_use), + cognition=IntelligenceBuildFirstBriefCognition( + reasoning=env.service.reasoning, + execution_binding=env.execution_binding, + append_binding=env.append_binding, + ), + ) + replay = await restarted.create_first_brief(request) + + assert replay == replace(first, admission=replace(first.admission, replayed=True)) + assert replay.session == await sessions.load_latest( + product_id=PRODUCT, + session_id=request.session_id, + available_at=REQUESTED_AT, + ) + assert env.provider.calls == 1 + assert replay.admission.brief.citations + assert replay.admission.synthesis_receipt.template_id == "price_brief" + assert replay.admission.synthesis_receipt.persona_ids == ("pricing_reviewer",) + + +@pytest.mark.asyncio +async def test_missing_cognition_setup_fails_closed_before_provider_or_append(): + env, _, _, _, request, service = await _stack(cognition=False) + + with pytest.raises(IntelligenceBuildFirstBriefError, match="composition is not installed"): + await service.create_first_brief(request) + + assert env.provider.calls == 0 + + +@pytest.mark.asyncio +async def test_stale_session_wrong_attention_and_revoked_build_authority_fail_closed(): + env, build, sessions, session, request, service = await _stack() + material = request.model_dump(mode="python", exclude={"request_id", "request_digest"}) + stale = IntelligenceBuildFirstBriefRequestV1Alpha1( + **{**material, "session_revision_id": str(session.prior_revision_id)} + ) + with pytest.raises(IntelligenceBuildFirstBriefError, match="exact current active Builder revision"): + await service.create_first_brief(stale) + + wrong_attention = IntelligenceBuildFirstBriefRequestV1Alpha1( + **{**material, "attention_receipt_digest": "sha256:" + "9" * 64} + ) + with pytest.raises(IntelligenceBuildFirstBriefError, match="exact current routed attention receipt"): + await service.create_first_brief(wrong_attention) + + denied = CoreIntelligenceBuildFirstBriefService( + build=build, + sessions=sessions, + activations=env.activation_service, + packs=_PackArchive(env.pack), + records=env.store, + runtime_use=_CurrentBuildAuthority(build.authority_use, denied=True), + cognition=service.cognition, + ) + with pytest.raises(IntelligenceBuildFirstBriefError, match="current build authority denied"): + await denied.create_first_brief(request) + assert env.provider.calls == 0 + + +def test_first_brief_contracts_are_public_without_expanding_core_or_intelligence(): + assert application_api.IntelligenceBuildFirstBriefPort + assert application_api.CoreIntelligenceBuildFirstBriefService + assert application_api.IntelligenceBuildFirstBriefRequestV1Alpha1 diff --git a/tests/test_intelligence_build_host_composition.py b/tests/test_intelligence_build_host_composition.py index db81365..c52e1d1 100644 --- a/tests/test_intelligence_build_host_composition.py +++ b/tests/test_intelligence_build_host_composition.py @@ -206,6 +206,7 @@ async def test_exact_durable_bootstrap_composes_fresh_product_fenced_ports() -> assert isinstance(first.recorded_sources, CoreRecordedSourceAdmissionService) assert first.prepared_derivations is not None + assert first.first_brief is None assert first.records.product_id == build.product_id assert first.recorded_sources.store is first.records assert first.prepared_derivations.ledger.store is first.records