Audit Reassignment and Evidence Ownership
A Cloudflare coverage-ledger replay checks how reassignment archives old evidence, requires fresh owners, and separates valid metadata from verified artifact contents.
I reassigned a synthetic audit unit from worker-1 to worker-2, then copied the earlier worker's check into the current evidence. The validator rejected the record twice: the prior owner's evidence belonged in its archived attempt, and the archived artifact path could not be reused.
Keeping the old check in history and supplying a fresh check passed. So did that record when none of its referenced artifact files existed. These results define two different boundaries. The validator enforces the ledger's provenance and state rules; the workflow must establish whether the recorded evidence is real and supports its claim.
I examined Cloudflare's security-audit-skill at revision c1c8a8c1471069fb0e188eeaff69b8e8db6564a8, using Node 24.19.0 and Python 3.12.14 on Linux. All ledger records below are synthetic. I ran the unchanged coverage validator, not the audit workflow. No agent was launched, no target repository was audited, and no vulnerability was reproduced.
Coverage Is a Record With State
The project contains audit instructions, a findings schema, and two executable validators with test files. There is no package-install step for those validators: they are CommonJS programs using Node built-ins. The project overview separates coverage-led work from candidate validation and final-record verification.
My subject is validate-coverage-ledger.cjs. It checks more than field types. A coverage unit has canonical references for its surface, boundary, subsystem, and attack class. Its canonical ID identifies that unit across assignments. Wave, owner, status, reviewed paths, checks, blockers, and archived attempts describe what happened to it.
The tested reassignment preserves the canonical references and coverage ID. It increments the wave, archives the prior attempt, and starts a new current assignment:
| Field | Earlier attempt | Fresh current assignment |
|---|---|---|
| Wave | 1 | 2 |
| Assignment owner | worker-1 | worker-2 |
| Status | blocked | in_progress |
| Reviewed paths and checks | Retained in attempts[0] | Empty |
| Unresolved fact | Retained with the old attempt | Empty while work is in progress |
| Reassignment reason | Required on the archived attempt | Explained by that history |
This prevents an ordinary edit to the owner field from making old evidence look like the new assignment's work. It also preserves a blocked or candidate-producing attempt instead of erasing it when another worker takes over.
History Constrains Current Evidence
The validator walks archived attempts in order. Their waves must increase strictly and precede the current wave. Assignment owners must be fresh across attempts. It accumulates prior owner IDs and local-artifact paths, then checks the current record against those sets.
For a local check, the recorded artifact path must be safe and lie under:
agents/<check-agent-id>/artifacts/<file>
Changing only the check's agent ID does not transfer its old artifact. A check labeled worker-2 that still points to agents/worker-1/artifacts/probe.txt fails path ownership and archived-artifact reuse checks.
Status adds another set of constraints. An in_progress unit needs an owner but keeps current evidence empty. A covered unit needs reviewed paths and checks, with no unresolved entries. A blocked unit carries evidence and a blocker. A deferred unit is unassigned and leaves current evidence empty, while its history can retain an earlier candidate fingerprint.
Those distinctions make a coverage ledger useful to an orchestrator. It can distinguish work waiting to happen, work that stopped on a missing fact, and work whose current attempt recorded a check. A generic done: true field would hide those differences.
Eight Cases Through the Actual CLI
The replay probe verifies the validator's SHA-256 digest, asks its exported canonical-ID function to identify the synthetic unit, and writes eight JSON fixtures. Python launches the actual Node CLI separately for each file. It checks exit codes and diagnostics.
From the portfolio checkout, I ran:
python public/blog/audit-handoff-evidence/ledger-probe.py \
/tmp/cloudflare-audit-note/validate-coverage-ledger.cjs
The probe exited zero. Each case uses the same coverage ID:
| Fixture | Validator exit | Observed decision |
|---|---|---|
| Old attempt archived, fresh owner in progress | 0 | Accepted |
| Fresh covered record, referenced files absent | 0 | Accepted |
| Current assignment reuses archived owner ID | 1 | Fresh-owner requirement failed |
| Current evidence copies archived worker's check | 1 | Old owner and artifact reuse rejected |
| Check owner relabeled, old artifact path retained | 1 | Ownership and artifact reuse rejected |
| Earlier candidate archived, current work deferred | 0 | Accepted with candidate history retained |
| Covered record still has an unresolved fact | 1 | Covered-state invariant failed |
| Current assignment includes another fresh check owner | 0 | Accepted |
The copied-check case produced:
ERROR: $[0].local_checks[0].agent_id: prior assignment owner evidence must remain in its archived attempt
ERROR: $[0].local_checks[0].artifact: artifact from an archived attempt cannot be reused
FAIL: 2 validation error(s)
The final probe line was:
PASS: 8 CLI cases; coverage ID preserved; referenced artifact files were never created
The last table row is an important scope detail. The current assignment belonged to worker-2, while its check belonged to fresh worker-3 and used that check owner's artifact namespace. The validator accepted it. It does not require every check's agent ID to equal the unit's current assignment owner. Its historical non-reuse rules and per-check artifact namespace are narrower than that policy.
Metadata Ownership Has Limits
The word “owned” here describes agent IDs and path strings. The coverage validator reads the ledger file, but does not open the files named by local checks. It does not verify their contents, filesystem UID, command exit status, or which process created them.
That limitation was directly exercised: the successful covered fixture named a local artifact, and the probe asserted that no agents directory existed beside the fixture. Passing established record consistency. It could not establish that a local check had actually run.
The verification instructions put additional responsibilities on the workflow: isolated verifiers, source re-reading, bounded local execution, and trusted promotion of retained artifacts. I inspected those instructions; I did not execute or validate that larger system. A path convention helps identify provenance, while actual evidence handling needs its own enforced boundary.
Focused Tests Passed; the Full Suite Did Not
I ran five focused upstream tests:
cd /tmp/cloudflare-audit-note
node --test --test-isolation=none --test-reporter=spec \
--test-name-pattern='archives prior evidence|preserves candidate provenance|rejects reassignment owner reuse|requires owned artifacts|enforces state evidence' \
validate-coverage-ledger.test.cjs
ℹ tests 5
ℹ pass 5
ℹ fail 0
That command exited zero. The complete two-file suite exited one: 65 tests, 41 passed, and 24 failed. Many failing CLI assertions expected a diagnostic but captured empty output. A separate minimal Node spawnSync check reported EPERM, empty output, and status zero. I did not establish the cause of every suite failure, so this is not a claim that the whole release passed validation here.
The Python-launched eight-case replay avoids that synchronous Node child-process path and verifies the relevant validator CLI behavior directly. Together with the focused tests, it supports the reassignment finding without treating the broader failures as green.
The useful contract is explicit history: a stable coverage identity can survive a handoff while old evidence keeps its original attribution. Keep that contract distinct from proof that the evidence exists and is correct. The ledger can check the former; the surrounding audit system must establish the latter.