Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions ace/intelligence/contracts/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -524,8 +524,8 @@ def prevent_fixture_as_live(self) -> Self:
raise ValueError("a live Observation requires live acquisition")
if self.observed_at > self.ingested_at:
raise ValueError("Observation ingested_at cannot precede observed_at")
if self.ingested_at > self.as_of:
raise ValueError("Observation as_of cannot precede ingested_at")
if self.as_of > self.ingested_at:
raise ValueError("Observation as_of cannot follow ingested_at")
if self.source_published_at is not None and self.source_published_at > self.observed_at:
raise ValueError("Observation source_published_at cannot follow observed_at")
if self.source_mapping is not None and self.source_mapping.activation_revision != self.activation_revision:
Expand Down
8 changes: 4 additions & 4 deletions ace/intelligence/detection/categorical_transition.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,9 @@ def _validate_pair(
)
if baseline.as_of >= current.as_of:
raise CategoricalTransitionDetectionError("categorical transition baseline must precede current state")
if baseline.as_of < binding.revision.occurred_at:
if min(baseline.projected_at, current.projected_at) < binding.revision.occurred_at:
raise CategoricalTransitionDetectionError(
"categorical transition baseline predates the prepared activation revision"
"categorical transition snapshot projection predates the prepared activation revision"
)
available_at = max(baseline.projected_at, current.projected_at)
if _aware_utc(detected_at, label="detected_at") < available_at:
Expand Down Expand Up @@ -388,8 +388,8 @@ def _route_categorical_shift_as_signal(
raise CategoricalTransitionDetectionError("the Shift does not use the exact bound activation revision")
if validated_shift.product_id != validated_binding.revision.spec.product_id:
raise CategoricalTransitionDetectionError("the Shift is outside the bound product scope")
if validated_shift.as_of < validated_binding.revision.occurred_at:
raise CategoricalTransitionDetectionError("the Shift predates the prepared activation revision")
if validated_shift.detected_at < validated_binding.revision.occurred_at:
raise CategoricalTransitionDetectionError("the Shift detection predates the prepared activation revision")
if validated_shift.shift_type_ref != rule.shift_type:
raise CategoricalTransitionDetectionError("the Shift does not match the bound detector rule")
delta = validated_shift.delta.parsed_value()
Expand Down
8 changes: 4 additions & 4 deletions ace/intelligence/detection/numeric_delta.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,8 @@ def _validate_pair(
raise NumericDeltaDetectionError("numeric delta rule does not target the snapshot entity type")
if baseline.as_of >= current.as_of:
raise NumericDeltaDetectionError("numeric delta baseline must precede current state")
if baseline.as_of < binding.revision.occurred_at:
raise NumericDeltaDetectionError("numeric delta baseline predates the prepared activation revision")
if min(baseline.projected_at, current.projected_at) < binding.revision.occurred_at:
raise NumericDeltaDetectionError("numeric delta snapshot projection predates the prepared activation revision")
available_at = max(baseline.projected_at, current.projected_at)
if _aware_utc(detected_at, label="detected_at") < available_at:
raise NumericDeltaDetectionError("numeric delta cannot be detected before both snapshots were projected")
Expand Down Expand Up @@ -423,8 +423,8 @@ def _route_shift_as_signal(
raise NumericDeltaDetectionError("the Shift does not use the exact bound activation revision")
if validated_shift.product_id != validated_binding.revision.spec.product_id:
raise NumericDeltaDetectionError("the Shift is outside the bound product scope")
if validated_shift.as_of < validated_binding.revision.occurred_at:
raise NumericDeltaDetectionError("the Shift predates the prepared activation revision")
if validated_shift.detected_at < validated_binding.revision.occurred_at:
raise NumericDeltaDetectionError("the Shift detection predates the prepared activation revision")
if validated_shift.shift_type_ref != rule.shift_type:
raise NumericDeltaDetectionError("the Shift does not match the bound detector rule")
delta = validated_shift.delta.parsed_value()
Expand Down
4 changes: 2 additions & 2 deletions ace/intelligence/routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ def _eligible_signal_routes(
raise SignalRoutingError("Signal does not use the exact bound activation revision")
if validated_signal.product_id != validated_binding.revision.spec.product_id:
raise SignalRoutingError("Signal is outside the bound product scope")
if validated_signal.as_of < validated_binding.revision.occurred_at:
raise SignalRoutingError("Signal predates the prepared activation revision")
if validated_signal.detected_at < validated_binding.revision.occurred_at:
raise SignalRoutingError("Signal detection predates the prepared activation revision")

routes = (
route
Expand Down
5 changes: 3 additions & 2 deletions ace/intelligence/source_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,11 +368,12 @@ def _interpret_source_mapping(
mapping_id=mapping.mapping_id,
mapping_digest=f"sha256:{canonical_hash(mapping)}",
)
state_as_of = snapshot.event_effective_at or snapshot.source_published_at or snapshot.observed_at
observation = ObservationV1Alpha1(
product_id=product_id,
mode=mode,
activation_revision=validated_binding.reference,
as_of=snapshot.ingested_at,
as_of=state_as_of,
source_ref=snapshot.source_snapshot_ref,
source_digest=snapshot.source_snapshot_digest,
acquisition_mode=acquisition_mode,
Expand All @@ -393,7 +394,7 @@ def _interpret_source_mapping(
product_id=product_id,
mode=mode,
activation_revision=validated_binding.reference,
as_of=snapshot.ingested_at,
as_of=state_as_of,
lineage=(
LineageReferenceV1Alpha1(
resource_kind=LineageResourceKind.OBSERVATION,
Expand Down
2 changes: 1 addition & 1 deletion ace/intelligence/synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ def _lineage(resource) -> LineageReferenceV1Alpha1:


def _citation(observation: ObservationV1Alpha1) -> CitationV1Alpha1:
source_as_of = observation.source_published_at or observation.event_effective_at or observation.observed_at
source_as_of = observation.event_effective_at or observation.source_published_at or observation.observed_at
return CitationV1Alpha1(
source_ref=observation.source_ref,
source_digest=observation.source_digest,
Expand Down
23 changes: 21 additions & 2 deletions docs/evidence/platform-p1c1-declarative-source-mapping-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ Observation and one exact-lineage Entity Snapshot.
executable.
- Product, activation, mode, source and receipt identities, and all times come from the host
envelope and resolved binding. Source payload labels cannot override them.
- Mapped intelligence state time is the first available semantic timestamp in the strict order
event-effective, source-published, then observed. Ingestion and projection retain actual
availability time. This bitemporal correction was reverified on 2026-08-13 and intentionally
changes mapped Observation and Entity Snapshot identities.
- Detection and routing compare semantic state in `as_of`, but activation admissibility is checked
against projection and detection availability. Historical source state may predate installation;
projecting, detecting, or routing it before the exact activation remains fail-closed.
- Observation provenance durably pins the activation revision, compiled Pack ID and digest,
source-mapping module ID and digest, and mapping ID and digest. The Entity Snapshot's single
lineage edge pins that exact Observation without adding domain data to its attributes.
Expand Down Expand Up @@ -58,8 +65,8 @@ branches. Their pinned outputs are:

| Fixture | Observation | Entity Snapshot |
|---|---|---|
| numeric | `observation:b5f4394738f8b5e50049d251a86c57e6` / `sha256:b5f4394738f8b5e50049d251a86c57e6ae2ef8df3adb2e620739d3c80c5b0244` | `entity_snapshot:34e55f3de2964b0ead40b4f3139069a7` / `sha256:34e55f3de2964b0ead40b4f3139069a73d70837eff877c8961b0f337f5b21f0f` |
| categorical | `observation:66426d907e83a36f6fe9eb8b619fade2` / `sha256:66426d907e83a36f6fe9eb8b619fade207c0a256f1eeec0089556f023e756912` | `entity_snapshot:c6103b731c5dd2e02694650c0d213e77` / `sha256:c6103b731c5dd2e02694650c0d213e77f136108562fdf9319647042f67a0905c` |
| numeric | `observation:1a12528799c8192eef0f450608da203b` / `sha256:1a12528799c8192eef0f450608da203b228c5dc220a19c267aaa14fe75efc035` | `entity_snapshot:935bb7668ce0cce0655877877793db66` / `sha256:935bb7668ce0cce0655877877793db663ee7aa3e7c3b6dcfc58264fed40f197e` |
| categorical | `observation:db365d16a6ed9a399d36f90a96a67621` / `sha256:db365d16a6ed9a399d36f90a96a676213f0aa37677f6b31be48324787addafe1` | `entity_snapshot:377c44ca271b576f3768768b47a23446` / `sha256:377c44ca271b576f3768768b47a234468b22288eb7e56da9c762f5923487bb84` |

Semantically reordered declarations and `0.0`/`-0.0` confidence compile to identical Pack IR and
identity; material changes alter Pack identity. Regression coverage also proves exact source and
Expand All @@ -70,6 +77,18 @@ normalization, and rejection of every LIVE attempt.

## Verification record

- 2026-08-13 bitemporal correction: **54 passed** across source mapping, resource contracts, and
recorded admission; **160 passed** across LIVE ingestion, numeric/categorical detection,
synthesis, resource projections, and public boundaries. The full supported gate reached
**7,976 passed, 50 skipped, 262 deselected** with one unrelated persistent-store transaction
failure; that exact failing test passed immediately in isolation. One recorded two-snapshot
transaction proves distinct semantic state times survive a shared admission time and are
accepted by the unchanged numeric detector.
- Follow-up activation-time audit: **109 passed, 2 skipped** across numeric and categorical
PREPARED/LIVE detection, routing, source mapping, recorded admission, solution Pack behavior,
and public boundaries. Positive and negative fixtures distinguish historical state from
post-activation projection/detection without weakening exact activation binding.

- Focused source-mapping, compiler, resource, ledger, boundary, artifact-contract, and build-backend
gate: **89 passed, 1 deselected**.
- Complete Intelligence suite: **132 passed**.
Expand Down
56 changes: 54 additions & 2 deletions tests/intelligence/test_categorical_transition_detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def _compiled_pack(
return compile_pack_document(_encoded(manifest), resources)


def _binding(**pack_changes: Any) -> PreparedActivationBinding:
def _binding(*, activated_at: datetime | None = None, **pack_changes: Any) -> PreparedActivationBinding:
pack = _compiled_pack(**pack_changes)
overlay = compile_overlay(
pack,
Expand All @@ -158,7 +158,7 @@ def _binding(**pack_changes: Any) -> PreparedActivationBinding:
state=ActivationState.ACTIVE,
actor_ref="principal:test-author",
approval_receipt_ref="receipt:prepared-approval",
occurred_at=AS_OF - timedelta(days=100),
occurred_at=activated_at or AS_OF - timedelta(days=100),
)
return bind_prepared_activation(pack=pack, revision=revision)

Expand Down Expand Up @@ -621,6 +621,58 @@ def test_snapshot_pair_time_and_scope_discipline_is_enforced() -> None:
)


def test_historical_categorical_state_requires_post_activation_processing() -> None:
from ace.intelligence import route_categorical_shift_as_signal
from ace.intelligence.detection import CategoricalTransitionDetectionError

activated_at = AS_OF - timedelta(hours=1)
binding = _binding(activated_at=activated_at)
baseline = _snapshot(
binding,
"draft",
as_of=AS_OF - timedelta(days=10),
projected_at=AS_OF,
)
current = _snapshot(
binding,
"active",
as_of=AS_OF - timedelta(days=5),
projected_at=AS_OF,
)

shift = _detect(binding, baseline, current, detected_at=AS_OF)
assert shift is not None
assert shift.as_of < activated_at < shift.detected_at
signal = route_categorical_shift_as_signal(
binding=binding,
detector_id=DETECTOR_ID,
shift=shift,
detected_at=AS_OF,
)
assert signal.as_of < activated_at <= signal.detected_at

preactivation_current = _snapshot(
binding,
"active",
as_of=AS_OF - timedelta(days=5),
projected_at=activated_at - timedelta(seconds=1),
)
with pytest.raises(CategoricalTransitionDetectionError, match="projection predates"):
_detect(binding, baseline, preactivation_current, detected_at=AS_OF)

shift_material = shift.model_dump(mode="python", exclude={"resource_id", "resource_digest"})
shift_material["detected_at"] = activated_at - timedelta(seconds=1)
shift_material["lineage"] = ()
preactivation_shift = shift.__class__.model_validate(shift_material)
with pytest.raises(CategoricalTransitionDetectionError, match="detection predates"):
route_categorical_shift_as_signal(
binding=binding,
detector_id=DETECTOR_ID,
shift=preactivation_shift,
detected_at=activated_at,
)


def test_cross_pack_policy_cannot_relabel_or_route_a_shift() -> None:
from ace.intelligence import route_categorical_shift_as_signal
from ace.intelligence.detection import CategoricalTransitionDetectionError
Expand Down
16 changes: 16 additions & 0 deletions tests/intelligence/test_intelligence_resource_contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,22 @@ def test_observation_cannot_precede_its_claimed_source_publication() -> None:
)


def test_observation_state_time_cannot_follow_its_ingestion_availability() -> None:
with pytest.raises(ValidationError, match="as_of cannot follow ingested_at"):
ObservationV1Alpha1(
**_common(),
source_ref="evidence:future-state",
source_digest="sha256:" + "4" * 64,
acquisition_mode=EvidenceAcquisitionMode.PREPARED_FIXTURE,
acquisition_receipt_ref="receipt:future-state-acquisition",
acquisition_receipt_digest="sha256:" + "4" * 64,
observed_at=AS_OF - timedelta(seconds=2),
ingested_at=AS_OF - timedelta(seconds=1),
payload=_json(),
confidence=0.5,
)


def test_explicit_inference_requires_basis_and_uncertainty_but_not_a_fake_citation() -> None:
basis_digest = "sha256:" + "3" * 64
basis_ref = "shift:" + "3" * 32
Expand Down
69 changes: 67 additions & 2 deletions tests/intelligence/test_numeric_delta_detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def _compiled_pack(
)


def _binding(**pack_changes) -> PreparedActivationBinding:
def _binding(*, activated_at: datetime | None = None, **pack_changes) -> PreparedActivationBinding:
pack = _compiled_pack(**pack_changes)
overlay = compile_overlay(
pack,
Expand All @@ -157,7 +157,7 @@ def _binding(**pack_changes) -> PreparedActivationBinding:
state=ActivationState.ACTIVE,
actor_ref="principal:test-author",
approval_receipt_ref="receipt:prepared-approval",
occurred_at=AS_OF - timedelta(days=100),
occurred_at=activated_at or AS_OF - timedelta(days=100),
)
return bind_prepared_activation(pack=pack, revision=revision)

Expand Down Expand Up @@ -345,6 +345,71 @@ def test_detection_and_signal_timestamps_follow_input_availability() -> None:
)


def test_semantic_state_may_predate_activation_but_processing_may_not() -> None:
activated_at = AS_OF - timedelta(hours=1)
binding = _binding(activated_at=activated_at)
baseline = _snapshot(
binding,
100.0,
as_of=AS_OF - timedelta(days=10),
projected_at=AS_OF,
)
current = _snapshot(
binding,
90.0,
as_of=AS_OF - timedelta(days=5),
projected_at=AS_OF,
)

shift = _detect(binding, baseline, current, detected_at=AS_OF)
assert shift is not None
assert shift.as_of < activated_at < shift.detected_at
signal = route_shift_as_signal(
binding=binding,
detector_id=DETECTOR_ID,
shift=shift,
detected_at=AS_OF,
)
assert signal.as_of < activated_at <= signal.detected_at
assert eligible_signal_routes(binding=binding, signal=signal) == ()

preactivation_baseline = _snapshot(
binding,
100.0,
as_of=AS_OF - timedelta(days=10),
projected_at=activated_at - timedelta(seconds=1),
)
with pytest.raises(NumericDeltaDetectionError, match="projection predates"):
_detect(binding, preactivation_baseline, current, detected_at=AS_OF)

shift_material = shift.model_dump(mode="python", exclude={"resource_id", "resource_digest"})
shift_material["detected_at"] = activated_at - timedelta(seconds=1)
shift_material["lineage"] = ()
preactivation_shift = shift.__class__.model_validate(shift_material)
with pytest.raises(NumericDeltaDetectionError, match="detection predates"):
route_shift_as_signal(
binding=binding,
detector_id=DETECTOR_ID,
shift=preactivation_shift,
detected_at=activated_at,
)

preactivation_signal = SignalV1Alpha1(
product_id=PRODUCT_ID,
mode=IntelligenceResourceMode.PREPARED,
activation_revision=binding.reference,
as_of=AS_OF - timedelta(days=5),
signal_type_ref="measure_attention",
title="Pre-activation processing is invalid",
summary="Historical state cannot be routed before the activation exists.",
details=CanonicalJsonValueV1Alpha1(value_json="{}"),
detected_at=activated_at - timedelta(seconds=1),
confidence=0.9,
)
with pytest.raises(SignalRoutingError, match="detection predates"):
eligible_signal_routes(binding=binding, signal=preactivation_signal)


def test_comparison_context_prevents_cross_unit_delta() -> None:
binding = _binding(context_attribute_ids=["unit"])
with pytest.raises(NumericDeltaDetectionError, match="context attribute unit changed"):
Expand Down
Loading