Finish KG-WP-0003: stream completeness and live qonto observation
Classify evidence as load-bearing or attributive, draft the emission-cadence declaration for Taxonomy, treat silence as a stream finding, keep completeness separate from record richness, forbid immune memory as a state plane, and make containment proposals reconstructable to their origin. Observe real qonto-assistant audit events; deny-class completeness stays unknown until the source publishes a heartbeat. Assistant: grok Assistant-Session: 01a05ef1-9e5a-70f2-b0ff-0b05d6b38ae9
This commit is contained in:
parent
c85646dc3c
commit
9daea96c43
35 changed files with 2023 additions and 138 deletions
134
tests/test_stream.py
Normal file
134
tests/test_stream.py
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
from kings_guard.cadence import load_qonto_assistant_cadence
|
||||
from kings_guard.contracts import (
|
||||
ReconciliationView,
|
||||
SignalKind,
|
||||
StreamCompleteness,
|
||||
StreamHeartbeat,
|
||||
)
|
||||
from kings_guard.posture import PostureEvaluator, is_stream_finding
|
||||
|
||||
from helpers import load_pilot, observation_from_fixture
|
||||
|
||||
|
||||
def test_cadence_draft_covers_both_forms_against_qonto() -> None:
|
||||
cadence = load_qonto_assistant_cadence()
|
||||
assert cadence.status == "taxonomy-draft"
|
||||
assert cadence.owner == "Taxonomy"
|
||||
assert cadence.drafter == "kings-guard"
|
||||
assert cadence.source_system == "qonto-assistant"
|
||||
assert cadence.reference_instance == "GH-WP-0002-T04"
|
||||
assert cadence.forms() == {"expected-rate", "heartbeat-or-reconciliation"}
|
||||
|
||||
|
||||
def test_unmet_declared_rate_is_a_stream_finding() -> None:
|
||||
cadence = load_qonto_assistant_cadence()
|
||||
stream = PostureEvaluator().evaluate_stream(
|
||||
(),
|
||||
cadence,
|
||||
now="2026-07-24T09:10:00Z",
|
||||
watching_since="2026-07-22T09:10:00Z",
|
||||
heartbeats=(
|
||||
StreamHeartbeat(
|
||||
source_system="qonto-assistant",
|
||||
timestamp="2026-07-24T09:00:00Z",
|
||||
event_class="audit.heartbeat",
|
||||
assertion="nothing-to-report",
|
||||
counts={"audit.deny": 0},
|
||||
),
|
||||
),
|
||||
reconciliation=ReconciliationView(source_counts={"audit.deny": 0}, evidence_counts={"audit.deny": 0}),
|
||||
)
|
||||
|
||||
assert any(item.startswith("stream:cadence_unmet:audit.allow") for item in stream.findings)
|
||||
assert all(is_stream_finding(item) for item in stream.findings)
|
||||
assert stream.completeness is StreamCompleteness.DEGRADED
|
||||
|
||||
|
||||
def test_missing_heartbeat_is_a_stream_finding() -> None:
|
||||
fixture = load_pilot()
|
||||
observation = observation_from_fixture(fixture)
|
||||
cadence = load_qonto_assistant_cadence()
|
||||
stream = PostureEvaluator().evaluate_stream(
|
||||
(observation,),
|
||||
cadence,
|
||||
now="2026-07-24T10:10:00Z",
|
||||
watching_since="2026-07-22T09:10:00Z",
|
||||
heartbeats=(),
|
||||
reconciliation=ReconciliationView(source_counts={"audit.deny": 1}, evidence_counts={"audit.deny": 1}),
|
||||
)
|
||||
|
||||
assert any(item.startswith("stream:heartbeat_missing:audit.deny") for item in stream.findings)
|
||||
assert "credential_exfil_probe" not in stream.findings
|
||||
assert stream.completeness is StreamCompleteness.DEGRADED
|
||||
|
||||
|
||||
def test_reconciliation_divergence_is_a_stream_finding() -> None:
|
||||
fixture = load_pilot()
|
||||
observation = observation_from_fixture(fixture)
|
||||
cadence = load_qonto_assistant_cadence()
|
||||
stream = PostureEvaluator().evaluate_stream(
|
||||
(observation,),
|
||||
cadence,
|
||||
now="2026-07-23T10:10:00Z",
|
||||
watching_since="2026-07-23T09:10:00Z",
|
||||
heartbeats=(
|
||||
StreamHeartbeat(
|
||||
source_system="qonto-assistant",
|
||||
timestamp="2026-07-23T10:00:00Z",
|
||||
event_class="audit.heartbeat",
|
||||
assertion="nothing-to-report",
|
||||
counts={"audit.deny": 2},
|
||||
),
|
||||
),
|
||||
reconciliation=ReconciliationView(
|
||||
source_counts={"audit.deny": 2},
|
||||
evidence_counts={"audit.deny": 1},
|
||||
),
|
||||
)
|
||||
|
||||
assert any(
|
||||
item.startswith("stream:reconciliation_divergence:audit.deny") for item in stream.findings
|
||||
)
|
||||
assert stream.completeness is StreamCompleteness.DEGRADED
|
||||
|
||||
|
||||
def test_stream_findings_are_distinguishable_from_content_findings() -> None:
|
||||
fixture = load_pilot()
|
||||
observation = observation_from_fixture(fixture)
|
||||
cadence = load_qonto_assistant_cadence()
|
||||
evaluation = PostureEvaluator().evaluate_with_stream(
|
||||
fixture.genome,
|
||||
observation,
|
||||
cadence,
|
||||
now="2026-07-24T10:10:00Z",
|
||||
watching_since="2026-07-22T09:10:00Z",
|
||||
heartbeats=(),
|
||||
reconciliation=ReconciliationView(source_counts={"audit.deny": 1}, evidence_counts={"audit.deny": 1}),
|
||||
)
|
||||
|
||||
content_findings = set(evaluation.assessment.findings)
|
||||
stream_findings = set(evaluation.stream.findings if evaluation.stream else ())
|
||||
assert "credential_exfil_probe" in content_findings
|
||||
assert stream_findings
|
||||
assert content_findings.isdisjoint(stream_findings)
|
||||
assert all(is_stream_finding(item) for item in stream_findings)
|
||||
kinds = {signal.signal_kind for signal in evaluation.signals}
|
||||
assert SignalKind.POSTURE_HINT in kinds
|
||||
assert SignalKind.STREAM_COMPLETENESS in kinds
|
||||
|
||||
|
||||
def test_heartbeat_not_yet_due_does_not_false_alarm() -> None:
|
||||
fixture = load_pilot()
|
||||
observation = observation_from_fixture(fixture)
|
||||
cadence = load_qonto_assistant_cadence()
|
||||
stream = PostureEvaluator().evaluate_stream(
|
||||
(observation,),
|
||||
cadence,
|
||||
now=observation.timestamp,
|
||||
watching_since=observation.timestamp,
|
||||
heartbeats=(),
|
||||
reconciliation=ReconciliationView(source_counts={"audit.deny": 1}, evidence_counts={"audit.deny": 1}),
|
||||
)
|
||||
|
||||
assert not any(item.startswith("stream:heartbeat_missing") for item in stream.findings)
|
||||
assert stream.completeness is StreamCompleteness.UNKNOWN
|
||||
Loading…
Add table
Add a link
Reference in a new issue