Adopt canonical audit cadence and record observer delivery requirements

Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a1029d-2c93-7500-a0e0-e3e089668b29
This commit is contained in:
tegwick 2026-10-03 22:48:16 +02:00
parent fbe1945e15
commit 8971a2dc39
4 changed files with 288 additions and 59 deletions

View file

@ -1,66 +1,112 @@
# Source-owned declaration for the qonto-assistant structured audit stream.
# The semantic shape follows the Taxonomy draft in
# kings-guard/specs/EmissionCadenceDeclaration.md. This repository owns the
# concrete emission claim even while the fleet-wide schema remains a draft.
audit_emission_cadence:
schema_version: "0.1"
source: qonto-assistant
stream_id: qonto-assistant.audit
transport: structured-stdout
instance_boundary: process
ordering:
instance_field: stream_instance_id
sequence_field: stream_sequence
sequence_starts_at: 1
gap: finding
reset: permitted-only-when-stream_instance_id-changes
event_classes:
audit.allow:
evidence_class: attributive
completeness_claimed: false
audit.deny:
evidence_class: load-bearing
form: heartbeat-or-reconciliation
rate_monitoring: forbidden
completeness_claimed: true
audit.heartbeat:
evidence_class: load-bearing
role: positive-liveness-and-reconciliation-claim
# Source-owned InfoTechCanon declaration; intended cadence, not delivery proof.
# Generic document 0.2.0 / schema 0.1; owner bundle b08b4d95fc4b0bd3.
schema_version: '0.1'
declaration_id: qonto-assistant.audit.v1
source: qonto-assistant
stream_id: qonto-assistant.audit
sources:
- source_id: qonto-assistant.audit.allow
source_system: qonto-assistant
event_class: audit.allow
form: heartbeat-or-reconciliation
heartbeat:
event_class: audit.heartbeat
interval: PT24H
interval_config: QONTO_AUDIT_HEARTBEAT_INTERVAL_SECONDS
default_interval_seconds: 86400
lifecycle:
startup: required
periodic_while_process_active: required
shutdown: best-effort
quiet_assertion: nothing-to-report
active_instance_missing: finding
scale_to_zero_semantics: >
No periodic heartbeat is promised while no process instance exists.
Observers track each stream_instance_id from its startup heartbeat until
a shutdown heartbeat or expiry after a missing active-instance cadence.
assertion: nothing-to-report
missing: finding
extensions: &id001
qonto-assistant:
assertion_scope: no-deny-transitions-in-window
active_instance_only: true
extensions:
netkingdom:
evidence_class: attributive
completeness_claimed: false
reconciliation:
source_counts_field: source_transition_counts
window_counts_field: window_transition_counts
counted_classes:
- audit.allow
- audit.deny
snapshot_endpoint: /v1/audit/reconciliation
compare_observed: >
Count received request events by event_class and stream_instance_id, then
compare them with the heartbeat source counts and stream sequence.
compare_local: source_transition_counts.audit.allow
compare_observed: evidence_counts.audit.allow
divergence: finding
undrained_local: lag-not-divergence
persistence: process-local
- source_id: qonto-assistant.audit.deny
source_system: qonto-assistant
event_class: audit.deny
form: heartbeat-or-reconciliation
heartbeat:
event_class: audit.heartbeat
interval: PT24H
assertion: nothing-to-report
missing: finding
extensions: *id001
extensions:
netkingdom:
evidence_class: load-bearing
completeness_claimed: false
rate_monitoring: forbidden
reconciliation:
compare_local: source_transition_counts.audit.deny
compare_observed: evidence_counts.audit.deny
divergence: finding
undrained_local: lag-not-divergence
- source_id: qonto-assistant.audit.heartbeat
source_system: qonto-assistant
event_class: audit.heartbeat
form: heartbeat-or-reconciliation
heartbeat:
event_class: audit.heartbeat
interval: PT24H
assertion: nothing-to-report
missing: finding
extensions: *id001
extensions:
netkingdom:
evidence_class: load-bearing
completeness_claimed: false
extensions:
netkingdom:
contract_digest: b08b4d95fc4b0bd3
contract_document_version: 0.2.0
qonto-assistant:
transport: structured-stdout
instance_boundary: process
ordering:
instance_field: stream_instance_id
sequence_field: stream_sequence
sequence_starts_at: 1
gap: finding
reset: permitted-only-when-stream_instance_id-changes
heartbeat:
event_class: audit.heartbeat
interval: PT24H
interval_config: QONTO_AUDIT_HEARTBEAT_INTERVAL_SECONDS
default_interval_seconds: 86400
lifecycle:
startup: required
periodic_while_process_active: required
shutdown: best-effort
quiet_assertion: nothing-to-report
active_instance_missing: finding
scale_to_zero_semantics: 'No periodic heartbeat is promised while no process
instance exists. Observers track each stream_instance_id from its startup
heartbeat until a shutdown heartbeat or expiry after a missing active-instance
cadence.
event_context:
identity_binding_field: identity_binding
egress_destination_field: egress_destination
qonto_egress_destination: qonto-thirdparty-api
'
reconciliation:
source_counts_field: source_transition_counts
window_counts_field: window_transition_counts
counted_classes:
- audit.allow
- audit.deny
snapshot_endpoint: /v1/audit/reconciliation
compare_observed: 'Count received request events by event_class and stream_instance_id,
then compare them with the heartbeat source counts and stream sequence.
'
divergence: finding
undrained_local: lag-not-divergence
persistence: process-local
event_context:
identity_binding_field: identity_binding
egress_destination_field: egress_destination
qonto_egress_destination: qonto-thirdparty-api
operational_acceptance: pending-audit-core-sender-mapping-and-independent-observer

View file

@ -0,0 +1,14 @@
# Source classifications for the supplied-inventory NetKingdom assessment.
source: qonto-assistant
classes:
audit.allow:
evidence_class: attributive
rare: false
audit.deny:
evidence_class: load-bearing
rare: true
rate_monitoring: forbidden
audit.heartbeat:
evidence_class: load-bearing
rare: false
role: positive-liveness-and-reconciliation-claim

View file

@ -0,0 +1,60 @@
"""Validate source claims against existing emitter and owner-supplied contracts."""
import json
import subprocess
import sys
from pathlib import Path
import pytest
import yaml
from qonto_assistant.audit import AUDIT_STREAM_ID, AuditLogger
ROOT = Path(__file__).resolve().parents[1]
def test_declaration_preserves_emitter_boundary_and_counters():
declaration = yaml.safe_load((ROOT / "specs/audit-emission-cadence.yaml").read_text())
source = declaration["extensions"]["qonto-assistant"]
assert declaration["stream_id"] == AUDIT_STREAM_ID
assert source["instance_boundary"] == "process"
assert source["heartbeat"]["default_interval_seconds"] == 86400
assert source["reconciliation"]["persistence"] == "process-local"
events = []
audit = AuditLogger(sink=events.append)
audit.emit({"decision": "deny"})
heartbeat = audit.emit_heartbeat()
for entry in declaration["sources"]:
assert entry["heartbeat"]["event_class"] == heartbeat["event_class"]
assert entry["extensions"]["netkingdom"]["completeness_claimed"] is False
if "reconciliation" in entry:
field, event_class = entry["reconciliation"]["compare_local"].split(".", 1)
assert event_class in heartbeat[field]
assert heartbeat["source_transition_counts"]["audit.deny"] == 1
assert heartbeat["assertion"] == "transitions-reported"
def test_declaration_passes_supplied_owner_inventory_against_canonical_schema():
checker = ROOT.parent / "net-kingdom/tools/emission-cadence-profile/emission_cadence_profile.py"
schema = ROOT.parent / "info-tech-canon/infospace/schemas/emission-cadence.schema.yaml"
if not checker.is_file() or not schema.is_file():
pytest.skip("Owner schema and profile checkout required; no fallback contract")
pytest.importorskip("jsonschema")
inventory = yaml.safe_load((ROOT / "specs/audit-event-class-inventory.yaml").read_text())
command = [sys.executable, str(checker), "--contract-schema", str(schema)]
for event_class, classification in inventory["classes"].items():
flag = (
"--attributive"
if classification["evidence_class"] == "attributive"
else "--load-bearing"
)
command.extend([flag, event_class])
if classification.get("rare") and classification["evidence_class"] == "load-bearing":
command.extend(["--rare-load-bearing", event_class])
command.append(str(ROOT / "specs/audit-emission-cadence.yaml"))
completed = subprocess.run(command, capture_output=True, text=True, check=False)
assert completed.returncode == 0, completed.stdout + completed.stderr
report = json.loads(completed.stdout)
assert report["contract_valid"] is True
assert report["profile_assessed"] is True
assert report["conformant"] is True
assert report["assessment_scope"] == "supplied-inventory"

View file

@ -0,0 +1,109 @@
---
id: QONTO-WP-0006
type: workplan
title: "Adopt canonical cadence and establish observer delivery requirements"
domain: infotech
repo: qonto-assistant
status: blocked
owner: codex
topic_slug: qonto-canonical-cadence-adoption
created: "2026-10-03"
updated: "2026-10-03"
---
# Canonical cadence adoption
Source-only continuation of QONTO-WP-0005 and NetKingdom NK-WP-0035. The
existing sequenced process heartbeat and reconciliation implementation is reused.
No live deployment, credential, policy or finance API change is included.
## Migrate source declaration and validate its inventory
```task
id: QONTO-WP-0006-T01
status: done
priority: high
```
Migrated the draft wrapper to the InfoTechCanon 0.2.0 candidate contract,
schema 0.1 (bundle `b08b4d95fc4b0bd3`). Generic fields use the canonical envelope;
process lifecycle, ordering, counters, identity and egress remain namespaced.
Published a separate source-owned inventory. The deny class remains rare and
load-bearing, with rate monitoring forbidden. Heartbeat remains load-bearing;
allow remains attributive. Preserved PT24H/default 86400/configurable interval,
startup and best-effort shutdown, process-local counters and scale-to-zero.
The earlier completeness claim is explicitly false until independent delivery
and observation evidence exists. Tests and the sibling canonical profile checker
validate this declaration without substituting a compatibility schema.
## Establish actual sender and accepted-count mapping
```task
id: QONTO-WP-0006-T02
status: wait
priority: high
blocking_reason: "Runtime owner and audit-core must supply the structured-stdout delivery binding, accepted-class mapping and sender receipts. No actual audit-core sender exists in this source implementation."
```
Qonto runtime owner must provide the pinned collector/sender implementation,
Deployment/image/config coordinates, per-process stream identity and restart
semantics, redacted sender success/failure receipts, retry/drop behavior and any
bounded lag signal. Reuse audit-core `docs/event-envelope.md`, `docs/stream-completeness.md`,
registration `emission_cadence` and `heartbeat_classes`, owned-count
`GET /v1/reconciliation` and `GET /v1/stream-findings`; these already exist.
audit-core must provide the bounded Qonto sender registration/admission and
mapping from `audit.allow`, `audit.deny`, `audit.heartbeat`, preserving
`stream_instance_id`, `stream_sequence`, heartbeat timestamps and source counts.
`audit.heartbeat` currently differs from the receiver
`type: audit-core.heartbeat` with `data.class` naming the vouched-for class.
Supply an adapter or receiver change with both the declaration and per-class
registration reviewed together; do not rename the emitted event in documentation.
`evidence_counts.<class>` must mean audit-core accepted records for that same
instance/window, not request attempts or aggregate estate traffic. The existing
reconciliation route scopes by source, tenant and time rather than process
instance; provide an equivalent count derivation or observer feed retaining
instance boundaries and source sequences. Do not compare restart-reset source
counters to lifetime receiver counts. Source counters
are process-local transitions, not durable transactional commits or delivery
acknowledgements. A process crash can lose them; runtime evidence must state
that limitation. No bank key may enter audit payload or observer configuration.
A source fixture or stdout event alone does not close this task.
## Obtain independent observer acceptance
```task
id: QONTO-WP-0006-T03
status: wait
priority: high
blocking_reason: "King's Guard requires actual sender/mapping proof before independent missing-heartbeat, gap and count-divergence acceptance."
```
King's Guard must consume the canonical declaration and owner inventory, bind
its observation to the actual accepted audit-core stream, and return independently
correlated positive/negative evidence: quiet active instance, deny transition,
missing active-instance heartbeat, sequence gap, source/accepted count divergence,
lag distinguished from divergence, restart and scale-to-zero. Qonto supplies
non-secret process-local reconciliation snapshots. Evidence must include actual
configured interval (the default is 24h), observer grace/window and source/image
pins; neither declaration conformance nor a fixture proves live completeness.
NetKingdom imports these receipts for NK-WP-0035; its proposed profile status is
unchanged by this source migration. Risk Nexus owns the independent risk finding
closure after observer acceptance. No generic profile or canon approval is requested.
Orientation consulted: emergent-security start 0.2.5 verified-files digest
`9e186dbd6f2d5eb455bca24db4b03cd866c63c4e94660430dd15f2ca9856215f`;
local-work 0.2.6 verified-files digest
`8d1d6a9d4d07fec7b619618de7dfe2be2254c1790f85f4198c7fe19db9751c02`.
These are orientation-only receipts, not operational authority.
## Verification 2026-10-03
Full source suite: `90 passed` (including both new declaration tests). Changed-file
Ruff and `git diff --check` passed. Canonical profile report: `contract_valid: true`,
`profile_assessed: true`, `conformant: true`, `assessment_scope: supplied-inventory`,
zero MUST/SHOULD findings. Exact source-owned assertions: `--attributive audit.allow`,
`--load-bearing audit.deny`, `--rare-load-bearing audit.deny`,
`--load-bearing audit.heartbeat`. Schema SHA-256
`6d52692eb1e4d1325e0d6062d95acded98beddaae5bcfe24c6f0e91f1b6be6ae`;
reviewed contract/schema revision `4d0851c3fca306538b53838421f4499baf352778`.
This is static source adoption, not live runtime or observer acceptance.