Evaluate declared emission cadence and close AUDIT-WP-0009 (T05)
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 4s

The contract T05 waited on is published: info-tech-canon
emission-cadence wire schema 0.1, contract digest b08b4d95fc4b0bd3.
A source-owned declaration now travels as emission_cadence on the
sender registration; expected-rate entries raise below_declared_cadence
on /v1/stream-findings from the same counts /v1/reconciliation returns.
heartbeat-or-reconciliation entries are validated and left to T04/T06.

Records the observer evaluation of net-kingdom's local-identity
declaration: structurally clean, not operationally evaluated, one
heartbeat event_class mapping incompatibility.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 151986@bnt-lap001
Assistant-Session: ccd02b6b-80ae-48e5-8cad-9c8f74d21a67
This commit is contained in:
tegwick 2026-09-22 22:44:45 +02:00
parent 3b7bedc8e0
commit 01468ffc41
8 changed files with 645 additions and 22 deletions

View file

@ -401,7 +401,8 @@ class IngestionApplication:
})
def _stream_findings(self, start_response):
"""Missing-heartbeat findings (AUDIT-WP-0009-T04, T07)."""
"""Missing-heartbeat and below-cadence findings (AUDIT-WP-0009-T04, T05, T07)."""
from audit_core.emission_cadence import evaluate as evaluate_cadence
from audit_core.stream_findings import evaluate
reader = getattr(self.backend, "last_heartbeats", None)
@ -417,9 +418,20 @@ class IngestionApplication:
continue
for event_class, at in reader(source).items():
last[(source, event_class)] = at
findings = evaluate(self.senders.identities, last)
findings = [f.as_dict() for f in evaluate(self.senders.identities, last)]
counter = getattr(self.backend, "event_counts", None)
if callable(counter):
# AUDIT-WP-0009-T05: a declared expected rate, read from the same
# counts /v1/reconciliation returns so the two cannot disagree.
findings += [
f.as_dict()
for f in evaluate_cadence(
self.senders.identities,
lambda source, since, until: counter(source, since, until, None),
)
]
return self._json(start_response, HTTPStatus.OK, {
"stream_findings": [finding.as_dict() for finding in findings],
"stream_findings": findings,
})
def _count_secrets(self, payload: Any, identity, outcome: str, findings) -> None: