kings-guard/workplans/KG-WP-0003-evidence-completeness-and-live-observation.md
tegwick 9daea96c43 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
2026-09-02 00:11:57 +02:00

324 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id: KG-WP-0003
type: workplan
title: "Evidence completeness and live observation"
domain: infotech
repo: kings-guard
status: finished
owner: kings-guard
topic_slug: netkingdom
created: "2026-08-29"
updated: "2026-09-01"
source_review: history/2026-08-29-layer-model-v0.7-scope-intent-review.md
standard: net-kingdom/canon/standards/security-layer-model_v0.7.md
state_hub_workstream_id: "a6c1c760-1386-58fa-9392-d9c3c4dadc41"
---
# Evidence completeness and live observation
Close the gap between what `INTENT.md` and `SCOPE.md` now claim under Security
Layer Model v0.7 and what `src/kings_guard/` actually does. Carries G1G8 of
`history/2026-08-29-layer-model-v0.7-scope-intent-review.md`.
Two things are being fixed. The repository consumes evidence and treats a quiet
stream as a healthy one, which the statute now forbids (§9.6). And statute §12's
fourth step — *kings-guard observes it in operation* — is unstaffed, because
every input is a fixture.
## Boundaries this workplan does not cross
- **No actuation.** kings-guard proposes containment and never performs it
(§9.2). T06 makes proposals reconstructable; it does not make them act.
- **No Tooling contact.** `dependencies = []` stays empty; `make check-layer`
must stay green on every task. `qonto-assistant` publishes its own stream and
is not a Tooling row, so T07 needs no engine in the path.
- **No engine gap worked around.** Identity and secret observation stay at zero.
## Dependency order
```text
T01 evidence classification
-> T02 emission-cadence declaration (draft, handed to Taxonomy)
-> T03 stream-completeness evaluation
-> T04 completeness in the posture output
T05 immune memory is not a state plane (independent)
T06 proposals carry their origin (independent)
T07 live observation (independent; makes the rest real)
T08 agent-principal conformance checks (after T05)
```
## Task: Classify consumed evidence load-bearing or attributive
```task
id: KG-WP-0003-T01
status: done
priority: high
state_hub_task_id: "dd660dbf-9148-5f35-aa4a-74deb10e6f8a"
```
Statute §9.6 attaches different obligations to each class, and the repository
currently records neither. `ImmuneObservation` carries `source_system` and no
class; `SecurityGenome` declares no sources at all.
Done when:
- the evidence class is expressible on a declared source and on an observation;
- `specs/ImmuneContracts.md` states the obligation difference — a load-bearing
source MUST declare a cadence, an attributive source SHOULD — and states that
the class is the source's declaration, not kings-guard's inference;
- the `qonto-assistant` fixture declares a class for its audit stream, with the
reasoning recorded;
- tests cover both classes.
**Done 2026-09-01:** `DeclaredEvidenceSource` on the genome and `evidence_class`
on `ImmuneObservation`. The adapter copies the source declaration and refuses
to infer class from event contents. qonto `audit.deny` / `audit.heartbeat` are
load-bearing; `audit.allow` is attributive, with reasoning in the fixture.
`tests/test_evidence_class.py` covers both classes.
## Task: Draft the emission-cadence declaration and hand it to Taxonomy
```task
id: KG-WP-0003-T02
status: done
priority: high
state_hub_task_id: "5b66aec0-a753-5039-bcbe-bc69cf1ad13e"
```
Statute §17 requires the artifact and records kings-guard as its drafter, being
its only consumer. Ownership stays with Taxonomy. Inventing a local shape is the
drift §17 exists to prevent, so this is a draft for handover, not an internal
schema.
Must cover both forms, because one does not substitute for the other:
- an **expected rate** for volume classes;
- **reconciliation or a heartbeat** for low-volume load-bearing classes —
revocations, denials, containment actions — where rate monitoring cannot work
because a suppressed month is indistinguishable from a quiet one. The required
property is a positive claim that can itself go missing.
Done when:
- the draft lives in `specs/` and is written against `qonto-assistant` as the
one real source;
- it explains where the declaration belongs — alongside the security genome, on
the argument that expected cadence is a claim of intent like the rest of the
genome — and why;
- `GH-WP-0002-T04` is referenced as the reference instance for the heartbeat
form;
- it is sent to gate-house and to the Taxonomy repositories for ownership, and
the handover is recorded.
**Done 2026-09-01:** Draft at `specs/EmissionCadenceDeclaration.md`, worked
example against `qonto-assistant`, both forms specified, genome-adjacent
rationale stated, `GH-WP-0002-T04` / `approval-engine/cadence.yaml` cited as
the heartbeat reference. Runtime consumer is
`src/kings_guard/fixtures/qonto_assistant_cadence.json` (draft-shaped, not a
fork). Handover messages sent to `gate-house`, `net-kingdom`, and
`info-tech-canon`.
## Task: Evaluate the stream, not only the observation
```task
id: KG-WP-0003-T03
status: done
priority: high
state_hub_task_id: "2cd878c0-8b70-5f2b-9f82-c4d74df5833c"
```
`PostureEvaluator.evaluate()` is stateless and per-observation. An event that is
never emitted is never evaluated, so the last posture stands and suppression
biases posture optimistic. Silence must become a finding in its own right.
Done when:
- a stream-level evaluation path exists alongside the per-observation one;
- an unmet declared cadence produces a finding;
- a missing heartbeat, or a reconciliation divergence between the source's own
state transitions and the evidence count per class, produces a finding;
- the findings are distinguishable from content findings — the stream observed,
not its contents;
- no Tooling contact is introduced; the source publishes its own stream.
**Done 2026-09-01:** `PostureEvaluator.evaluate_stream` / `evaluate_with_stream`
compare a declared cadence to arrived events. Unmet rate, missing heartbeat,
and reconciliation divergence emit `stream:*` findings, distinct from content
findings, and a `stream_completeness` signal. No Tooling client. Tests in
`tests/test_stream.py`.
## Task: Carry stream completeness in the posture output
```task
id: KG-WP-0003-T04
status: done
priority: high
state_hub_task_id: "73367acf-d888-5acc-ae85-7231a9cbe9fe"
```
`confidence_score` starts at 70 and rises with field richness of the record
received. It measures the record, never the stream: a well-formed observation
from a 90%-suppressed stream scores 85. The judgment must be able to say *this
rests on a stream I cannot vouch for.*
Done when:
- completeness is separated from richness in `PostureAssessment`;
- an unmet cadence or missing heartbeat degrades the completeness dimension;
- the rationale text says so in words, not only in a number;
- a posture derived from an incomplete stream can never read as more trustworthy
than one derived from a complete one.
**Done 2026-09-01:** `PostureAssessment` carries `stream_completeness` and
`completeness_reason` separately from `confidence_score`. Rationale states
completeness in words. `assessment_trust_key` orders completeness above
richness, so a 95-from-degraded stream cannot outrank a 70-from-complete one.
`tests/test_completeness.py`.
## Task: State and test that immune memory is not a state plane
```task
id: KG-WP-0003-T05
status: done
priority: medium
state_hub_task_id: "d7ed9faf-58ee-59a1-ad1e-b2fcf817317f"
```
Statute §3.4 rule 3: agent memory, tool-call traces and prompt caches MUST NOT
become state another layer depends on at runtime unless catalogued as Tooling.
`ImmuneMemoryEntry` has no such rule, and `INTENT.md` stage 5 (federated memory)
is exactly the shape that could drift into one.
Done when:
- `specs/ImmuneContracts.md` states it: immune memory informs kings-guard's own
judgment and may be published as evidence; no engine, PEP or workload may read
it as a runtime input, and making it one is a §4 Tooling catalog change;
- a test asserts the constraint rather than leaving it to prose;
- the constraint is reflected in the `Direction of Evolution` stage-5 entry.
**Done 2026-09-01:** `ImmuneMemoryEntry.runtime_input_for_other_layers` is
`forbidden`. `specs/ImmuneContracts.md` states the rule. `tests/test_immune_memory.py`
asserts the evaluator does not take memory as input and that INTENT stage 5
keeps the constraint.
## Task: Make containment proposals reconstructable to their origin
```task
id: KG-WP-0003-T06
status: done
priority: medium
state_hub_task_id: "98326ed2-ee5f-58ce-9f99-429e621117a6"
```
§9.2 rules that a containment action is a decision record, not a side channel.
`EffectorRequest` names a target and an action and carries no reference to what
produced it, so once a proposal leaves this repository it cannot be tied back to
the observation that caused it.
Done when:
- an effector request carries the originating observation and signal identity;
- the eventual decision record can name what the proposal was rendered for;
- `docs/AdjacentSystemBoundary.md` states the expectation on the receiving side;
- the authority boundary remains explicit and no value widens authority.
**Done 2026-09-01:** `EffectorRequest` carries originating observation and
signal ids, stream completeness, and a restrictive direction. Authority
boundaries in use remain `advisory_only` and `metadata_only`. Applied
`gate-house/docs/contracts/posture-findings-return.md` through this task.
`docs/AdjacentSystemBoundary.md` rule 7 states the receiving-side expectation.
`tests/test_effector_origin.py`.
## Task: Observe qonto-assistant in operation
```task
id: KG-WP-0003-T07
status: done
priority: high
state_hub_task_id: "ae516be8-5199-5f81-b47f-7843b5993f97"
```
Statute §12's fourth step is aspiration until this is done, and §19's verdict
names kings-guard as the watching half of *can propose and decide but cannot
watch or act*. Every input today is a hand-built fixture; ten tests pass and
none has met a real event.
Nothing blocks it. `qonto-assistant` publishes its own genome record and audit
stream, needs no engine in the path, and its genome has not drifted since it was
written.
Done when:
- real emitted audit events from `qonto-assistant` reach the evaluator;
- the observation mapping is confirmed against real events or corrected, and the
correction is reported back to `qonto-assistant`;
- output stays advisory — posture hints and metadata-only evidence, nothing that
actuates;
- the fixture is retained as a regression case rather than deleted;
- gate-house is told that step four is staffed, with what was found.
**Done 2026-09-01:** Real events from `qonto-assistant.audit.AuditLogger` via
`CapabilityService` (allow + deny) reach the evaluator. Mapping confirmed:
`request_id`, `capability``resource_scope`, `decision`, `protocol` hold.
Corrections reported to `qonto-assistant`: `AuditEvent` still omits
`identity_binding` and `egress_destination`; no heartbeat is published yet, so
deny-class completeness is unknown. Fixture retained. Output stays advisory.
gate-house notified that §12 step four is staffed for this lane.
`tests/test_live_qonto_observation.py`; `python3 -m kings_guard.main --pilot qonto-assistant --live`.
## Task: Check the agent-principal rules that can be checked
```task
id: KG-WP-0003-T08
status: done
priority: medium
state_hub_task_id: "e7003fb3-2e7c-5440-b489-b619b3f0c65d"
```
All four §3.4 rules are now claimed in `INTENT.md` and none is verified. The
no-Tooling-client claim showed the pattern: a claim in prose becomes a claim in
a test, and the test carries the reason.
Done when:
- `scripts/check_layer_conformance.py` covers what is mechanically checkable —
at minimum rule 1, no standing credential held in the repository or its
configuration;
- rule 3 is covered by T05;
- rules that remain assertion rather than test are recorded as such in
`layer.yaml`, honestly, rather than implied to be checked;
- `make check-layer` stays green.
**Done 2026-09-01:** `scripts/check_layer_conformance.py` scans for standing
credential files and secret literals (rule 1). Rule 3 is `tests/test_immune_memory.py`.
`layer.yaml` `agent_principal_rule_checks` records rule 2 as assertion and
rule 4 as mixed. `make check-layer` green; `pyproject.toml` `dependencies = []`.
## Success criteria
1. Every task above is `done`.
2. `make test` and `make check-layer` both pass.
3. `pyproject.toml` still declares `dependencies = []`.
4. `INTENT.md` and `SCOPE.md` claim nothing the implementation does not do.
5. The emission-cadence draft has been handed to Taxonomy and the handover
recorded.
6. gate-house has been told that §12's fourth step is staffed.
## Residuals
No leftover kings-guard work is parked only in prose. Source-side follow-ups
were sent as coordination messages, not new workplans here:
- `qonto-assistant` — emit `identity_binding` and `egress_destination` on
`AuditEvent` if they become known; publish the cadence declaration and a
deny-class heartbeat. Until then deny-class completeness stays unknown.
- Taxonomy (`net-kingdom`, `info-tech-canon`) — take ownership of
`specs/EmissionCadenceDeclaration.md`. kings-guard will consume what they
publish and will not fork it.
- `secrets-engine` secret-use evidence contract — assented as a proposed
Lifecycle surface; observation stays at zero until it ships. Not a WP-0003
residual.
Pre-existing open intake `KG-IN-0002` (architecture vocabulary sweep) is
unrelated and remains open.