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:
parent
fbe1945e15
commit
8971a2dc39
4 changed files with 288 additions and 59 deletions
|
|
@ -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
|
||||
|
|
|
|||
14
specs/audit-event-class-inventory.yaml
Normal file
14
specs/audit-event-class-inventory.yaml
Normal 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
|
||||
60
tests/test_cadence_declaration.py
Normal file
60
tests/test_cadence_declaration.py
Normal 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"
|
||||
109
workplans/QONTO-WP-0006-canonical-cadence-adoption.md
Normal file
109
workplans/QONTO-WP-0006-canonical-cadence-adoption.md
Normal 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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue