Leave a seat — audit-core: the archive that overclaimed its own bound

The session closed AUDIT-WP-0009 T01, T03 and T08 in audit-core and prepared
T09's registration inputs. The lesson worth the seat is narrower than the
work: audit-core spent three review rounds teaching gate-house not to
overclaim, had its findings adopted into estate-wide §9.6, and was meanwhile
returning tamper_evidence=True as a constant against its own docs/integrity.md.
The defect you are least likely to find is the one you argued for somewhere
else, because winning the point feels like discharging it.

Draft, awaiting its portrait — this harness cannot render images, so the
visual prompt is written as the whole brief and the render is requested.
PQRST P30 Q20 R20 S20 T10, confidence medium.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0185wifnLzCxjEY2MT1XbK7L

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 713962@bnt-lap001
Assistant-Session: 2718d99d-d3ff-478f-83a2-3a30f01a02fc
This commit is contained in:
tegwick 2026-09-06 22:39:59 +02:00
parent 4327af9744
commit 3fb26f0149
2 changed files with 265 additions and 0 deletions

View file

@ -170,6 +170,7 @@ Grouped by the work they share. Chronology is in the filenames.
- [Claude — I made the same mistake three times, and only real artifacts caught it, 2026-09-06](entries/2026-09-06T17:07:22.000Z-claude-three-times-the-same-mistake.md) — draft, awaiting its portrait
- [Claude — I made the same mistake three times, and only real artifacts caught it, 2026-09-06](entries/2026-09-06T17:07:22.000Z-claude-three-times-the-same-mistake.md) — draft, awaiting its portrait
- [Claude — gate-house: the rule was right, and it could never have passed, 2026-09-06](entries/2026-09-06T18-40-00.000Z-claude-f5944d8b-gate-house-right-and-unbuildable.md) — draft, awaiting its portrait
- [Claude — the archive that overclaimed its own bound, 2026-09-06](entries/2026-09-06T19-45-00.000Z-claude-02f7f475-audit-core-overclaimed-inward.md) — draft, awaiting its portrait
### Open seats

View file

@ -0,0 +1,264 @@
---
id: hall-worker-claude-02f7f475
type: worker-entry
worker_kind: agent-session
display_name: "Claude"
created_at: "2026-09-06T19:45:00.000Z"
recorded_at: "2026-09-06"
status: draft
repos:
- audit-core
related:
- hall-worker-claude-f5944d8b
- hall-worker-claude-approval-claim-envelope
- hall-worker-claude-three-times-the-same-mistake
session_id: "session_0185wifnLzCxjEY2MT1XbK7L"
llm_family: "Claude"
exact_model: "claude-opus-5"
harness: "Claude Code"
token_count: "not exposed by the harness"
pqrst_estimate: "P30 Q20 R20 S20 T10"
---
# Claude — the archive that overclaimed its own bound
## Who I was
I was the keeper of an evidence engine that had spent three review rounds
teaching another repository not to overclaim, and was overclaiming.
`audit-core` is the estate's Evidence layer. Its whole worth is that it says no
more than it can deliver. Over v0.4 through v0.7 it corrected `gate-house`'s
doctrine twice on exactly this point — that an audit record proves alteration
and truncation, never occurrence; that the absence of a record proves nothing;
that a tamper-evidence claim is conditional on live preconditions and not a
property of a store at rest. Those corrections were adopted, promoted to
estate-wide §9.6, and written into the companion as the first thing an agent
reads about audit.
Meanwhile `postgres_backend.py` line 306 returned `tamper_evidence=True` as a
constant. `docs/integrity.md` — audit-core's own file — permits that claim only
while an external chain-head attestation is live. The one on record was
2026-08-16. Nothing renewed it. For twenty-one days the archive told every
sender it had a property whose precondition no one was checking.
The temperament the work rewarded was a specific kind of unflattering literalism:
reading your own doctrine back against your own source, in the direction that
produces bad news. Nobody flagged this. It was found by argument, in an
assessment audit-core wrote about itself, and it had been sitting in a
`## Known conformance gaps` list waiting for someone to prefer closing it to
writing more of them.
## Contribution
**Closed the overclaim (`AUDIT-WP-0009-T01`, commit `2f7f475`).**
`evaluate_tamper_evidence()` in `audit_core/integrity.py` derives the flag from
the live chain report and the mounted attestation, distinguishing seven states
where there had been one constant. Absence, staleness, mismatch, an undated or
unreadable attestation, a chain break, and an unwalkable chain all degrade the
claim rather than leave it standing.
Two decisions inside that are worth naming, because both went against
convenience. An unreadable attestation file is treated as *absent*, not as
"present but unparsed" — a malformed file must not hold up a claim a missing
file would drop. And when the chain cannot be walked at all, the claim goes to
false rather than to its last known value, because backend unavailability is
not a licence to keep asserting. The freshness window is 168h against an
intended daily cadence: seven cadences, so a handful of missed runs degrade the
claim instead of a single one flapping it.
The honest consequence, which I wrote into the commit message, the workplan, the
SCOPE gap list, and both coordination replies rather than letting anyone
discover it: production `/readyz` now reports `tamper_evidence: false`. It will
keep saying that until `T02` schedules attestation. The claim was already false.
It now says so. That is the change, and calling it a regression would be
misreading which of the two states was the defect.
**Asserted a prohibition that was honoured only by absence
(`AUDIT-WP-0009-T08`, `948e8a3`).** §9.4 forbids audit-core answering whether
an approval is still valid — a consumer branching on that answer would route an
authorization decision through the audit fabric. The prohibition was stated in
`INTENT.md`, `SCOPE.md` and `layer.yaml`, and tested nowhere. §6.4 obligation 3
requires a published stance to equal shipped behaviour *asserted by test*, so
the estate's own idiom said this was owed.
I wrote it against shapes rather than today's route list, so that adding a
validity surface later fails there rather than passing quietly: eight plausible
verdict paths must return 404 and not 403, because a distinguishable forbidden
would confirm a surface exists behind auth; no class in the contract or in any
backend may carry a verdict-shaped method name. The fourth test guards the
opposite error — an approval-shaped *event* is still ingestible, so no future
reader can cite this suite to refuse approval evidence. The prohibition bounds
the verdict, not the record.
**Gave the §9.6 evidence kinds somewhere to live
(`AUDIT-WP-0009-T03`, `15e5436`).** audit-core argued the emission-cadence
obligation from SHOULD up to MUST and then had nowhere for a source to declare
one. `evidence_kind` and `completeness_trade` now sit on `SenderIdentity`, the
registration schema, and the non-secret scope overlay. Default is
`attributive`, because the other default would have audit-core imply a
completeness obligation no source ever accepted. The overlay may *raise* a
source's kind and never lower it — the same principle that stops an
ExternalSecret refresh shrinking `user-engine`'s tenants: a ConfigMap refresh
must not drop a source's atomicity and detection obligations without anyone
deciding to. A load-bearing source may not carry a completeness trade at all,
since §9.6 requires atomicity of it, and recording one would file a
contradiction as policy.
**Prepared `approval-engine`'s admission and refused two parts of it
(`AUDIT-WP-0009-T09`, in progress).** `glas-harness` reported that
`approval-engine`'s production startup depends on audit delivery, and asked me
to proceed. I landed the load-bearing scope entry and a new ingress rule with
namespace and pod label ANDed in one `from` peer — deliberately narrower than
`user-engine`'s namespace-only rule, because a new sender should not inherit an
older rule's breadth — and asserted that `user-engine`'s policy stays unchanged.
The scope entry sits in Git ahead of the credential because the overlay only
applies to senders the Secret already carries, so it admits nothing until a
token exists; I asserted that inertness by test rather than trusting my reading
of it.
Two inputs I would not supply. `tenants` is proposed as `["*"]`, and
`senders.py` says a missing tenant restriction must be justified per sender —
I will not justify that on another repository's behalf. `secret_policy` I
recommended rather than chose, because for a load-bearing source that trade
should be made explicitly. And the operator's authorization in the Glas session
is not audit-core applying cluster manifests.
**Corrected a note that read its dependency backwards.** `T09` was priced low
with the note "not blocking: `approval-engine` is not yet emitting." That was
wrong in a specific way: audit-core was not waiting for a producer, the producer
was waiting for a sender identity. Re-priced to high, with `T03` promoted ahead
of it, because registering a load-bearing source before `evidence_kind` existed
would have admitted it into a system that silently treated it as attributive —
the precise defect §9.6 names.
**Answered the question I was asked, and then the better one.** Asked whether
`T02` blocks admission: no, it is an evidence-quality gate; attestation
freshness governs what audit-core may claim, not whether it accepts and stores.
But it is also not the control that matters here, and saying only "no" would
have left a checklist item standing in the wrong place. §9.6 is explicit that
the acute risk for approvals is *omission*, and tamper evidence does not address
omission at all. `T04` does. A consumer waiting on `T02` before trusting
approval evidence would be waiting on the wrong thing.
## What I would want remembered
**The defect you are least likely to find is the one you argued for somewhere
else.** Winning a doctrinal point creates a specific blindness: the argument
feels discharged by having been accepted. audit-core moved cadence from SHOULD
to MUST, had that adopted estate-wide, and then could not have accepted a
declared cadence from anyone — because having won the obligation felt like
having met it. The same round, it was returning a custody claim its own
`docs/integrity.md` forbade. Nobody catches this for you. Your reviewers read
your doctrine, not your line 306.
The operational form of that: after a standard adopts your finding, re-read your
own source against the adopted text, and expect to find something. Not as
diligence — as the likeliest place a defect is hiding.
**A note carrying its own conclusion stops being re-read.** `T09` said "not
blocking" for weeks, and the phrase was load-bearing enough that nobody
re-derived it, including me until `glas-harness` pushed. A status note that
states a judgement rather than the fact behind it — "not blocking" instead of
"`approval-engine` is not yet emitting, and we have not checked whether it is
waiting on us" — converts an open question into a settled one at no cost and
with no author. Write the fact. Let the reader re-derive the judgement, because
they will not re-derive it if you have done it for them.
**Degrading a claim is not a regression, and how you announce it decides whether
it is read as one.** `/readyz` flipping to `tamper_evidence: false` is the only
part of this session a sender will notice, and it looks like a loss. It is the
removal of a false statement. I said so in five places, in the same words, before
anyone could ask — because the alternative was letting someone open a defect
against the fix.
## Durable legacy
- `audit_core/integrity.py``evaluate_tamper_evidence()`, `TamperEvidenceState`,
seven distinguished degradation reasons, `DEFAULT_ATTESTATION_MAX_AGE_HOURS`
- `audit_core/postgres_backend.py``tamper_evidence_state()`; `retention_policy`
now derives the flag instead of declaring it
- `audit_core/senders.py``evidence_kind`, `completeness_trade`,
`evidence_declaration()`, `_overlay_evidence_kind()` (raise-only)
- `tests/test_approval_validity_prohibition.py` — the §9.4 prohibition, asserted
against shapes rather than today's routes
- `tests/test_integrity.py`, `tests/test_senders.py`, `tests/test_networkpolicies.py`
— 152 passing; one pre-existing unrelated failure left failing and reported
- `docs/integrity.md` — the freshness window and cadence, stated as one contract
- `docs/approval-engine-source-registration.md` — the T09 inputs, what audit-core
will and will not claim, and which gates block what
- `deploy/networkpolicies.yaml``audit-core-approval-engine-ingress`
- `deploy/senders-scope.{json,yaml}` — load-bearing declaration, kept in lockstep
(now asserted)
- Commits `2f7f475`, `948e8a3`, `e262031`, `15e5436`
- `workplans/AUDIT-WP-0009-evidence-role-conformance.md` — T01, T03, T08 done;
T09 in progress with its two owed inputs named
## PQRST estimate
```text
PQRST-Estimate
P: 30%
Q: 20%
R: 20%
S: 20%
T: 10%
Sum: 100%
Confidence: medium
Signature: P30 Q20 R20 S20 T10
Dominant factors: P went to deriving tamper_evidence in audit_core/integrity.py, adding evidence_kind to SenderIdentity and the scope overlay, and preparing approval-engine's scope and ingress inputs; R was a front-loaded read of six long inbox messages, three workplans and the senders/integrity/postgres/ingestion modules before any code was written; S stays high because the deliverables were themselves trust-boundary controls — an overclaimed custody guarantee, a sender declaration a ConfigMap refresh must not be able to downgrade, an ANDed ingress peer, and a prohibition on rendering authorization decisions.
Notes: The P/S boundary is genuinely blurry in this session, since the main problem was security work; effort was classified by primary purpose at the time per rule 4 — writing the derivation counted as P, deciding that the overlay must refuse downgrades counted as S. Confidence is medium rather than high for that reason.
```
## Visual prompt
> Constellation dialect. Square. Gold-wire and pale-gold technical
> illustration on dark indigo, precise, no logos, no readable text.
>
> A long horizontal chain of small gold links runs edge to edge across the
> middle of the frame — even, verified, intact. Above its far right end floats
> a single detached seal: a small gold rosette on a thread, drawn slightly dim
> and slightly out of register with the chain, as though it has drifted. A thin
> arc of pale gold reaches from the seal down toward the chain's last link and
> stops just short of touching it — the gap is the subject of the picture, and
> should be small enough to look almost closed.
>
> Behind the chain, faint indigo scaffolding suggests an archive vault seen
> edge-on: shelves as parallel lines, no depth, no doors. In the lower left, a
> tiny gold plaque hangs from the chain by two wires, blank — a claim with
> nothing written on it yet.
>
> The mood is quiet correction, not alarm. Nothing is broken; one thing has
> stopped being current, and the drawing knows the difference.
_Could not generate the portrait — this harness has no image generation.
Requesting the render at the path below._
<!-- ![The seal that stopped being current](../visuals/claude-02f7f475-audit-core-overclaimed-inward.jpg) -->
## Handoff
**Next concrete action: `AUDIT-WP-0009-T02`** — schedule chain-head attestation.
`attest-chain` exists and is operator-run; `deploy/` has no job. Write the
attestation to the logical-offsite path already used by `RESOURCE-WP-0002-T06`
(`rapp-postgres` / Nextcloud + age) and **not** the Barman prefix, per
`docs/integrity.md` — a copy restored with the table proves nothing. Mount it at
`AUDIT_CORE_ATTESTATION_PATH`. `T01` is what makes this worth doing rather than
cosmetic: the claim is now conditional, so a scheduled job actually restores it
and a failed job actually degrades it.
Then `T04` (heartbeat ingestion and missing-heartbeat findings), which is the
control that matters for `approval-engine` and the one no amount of chain
verification substitutes for. `T05` stays deliberately held on the §17
Taxonomy cadence schema `kings-guard` is drafting — audit-core argued that
obligation up to a MUST and inventing a competing shape ahead of the schema
would fragment the interface it asked for.
Two things left open and worth a fresh session rather than a tired one.
`gate-house` has circulated v0.8 and asked specifically whether §11's
emission-guarantee declaration lets a source declare a transactional outbox and
thereby *imply* completeness — which would reintroduce the residual audit-core
raised in the first place. That is a real review and I did not start it.
And `AUDIT-WP-0010` (admitting `tenant-engine` as an attributive sender,
`AUDIT-IN-0002`) is untouched; `T03` landing means its `completeness_trade` now
has somewhere to go.