---
id: OBPI-0.19.0-04-audit-includes-attestation-record-gate-results-evidence-links
parent: ADR-0.19.0-closeout-audit-processes
item: 4
lane: Lite
status: Completed
---

# OBPI-0.19.0-04: Audit Includes Attestation Record, Gate Results, Evidence Links

## ADR Item

- **Source ADR:** `docs/design/adr/pre-release/ADR-0.19.0-closeout-audit-processes/ADR-0.19.0-closeout-audit-processes.md`
- **Checklist Item:** #4 - "OBPI-0.19.0-04: Audit includes attestation record, gate results, evidence links"

**Status:** Completed

## Objective

Enrich the AUDIT.md report generated by `audit_cmd()` in `src/gzkit/cli.py` to include the attestation record (attestor, status, timestamp) extracted from ledger `attested` events, the latest gate check results from ledger `gate_checked` events, and hyperlinks to the OBPI brief files and closeout form so that the audit report is a self-contained proof document rather than a bare pass/fail list.

## Lane

**Lite** - Inherited from parent ADR-0.19.0-closeout-audit-processes (lite per ledger `adr_created` event).

> This OBPI modifies internal report content within an existing command. It does not change CLI flags, exit codes, subcommand surface, or output schema contracts. The AUDIT.md format is an internal artifact, not a machine-consumed API.

## Allowed Paths

- `src/gzkit/cli.py` - `audit_cmd()` function (line ~2634): add ledger queries for attestation, gate results, and OBPI brief paths; enrich AUDIT.md and AUDIT_PLAN.md content
- `tests/test_audit_pipeline.py` - New test module covering the enriched audit report content, attestation section rendering, gate results section rendering, and evidence link generation

## Denied Paths

- `src/gzkit/ledger.py` - Ledger event factories and graph builder are not modified; this OBPI only reads existing events
- `src/gzkit/commands/common.py` - No changes to shared CLI utilities
- `docs/user/commands/audit.md` - Command docs unchanged (no CLI contract change)
- New dependencies
- CI files, lockfiles

## Requirements (FAIL-CLOSED)

1. REQUIREMENT: AUDIT.md MUST contain an "Attestation Record" section that includes the attestor name, attestation status, and timestamp extracted from the ledger `attested` event for the target ADR.
2. REQUIREMENT: AUDIT.md MUST contain a "Gate Results" section that lists each gate check event from the ledger (`gate_checked` events) with gate number, status (pass/fail), command, and returncode.
3. REQUIREMENT: AUDIT.md MUST contain an "Evidence Links" section that lists relative paths to each OBPI brief file and the closeout form (if it exists) under the ADR directory.
4. NEVER: The enriched report MUST NOT change the exit code behavior of `audit_cmd()` — exit 0 on all-pass, exit 1 on any failure, unchanged.
5. NEVER: The `--json` output shape MUST NOT remove existing keys (`adr`, `audit_file`, `audit_plan_file`, `results`, `passed`); new keys are additive only.
6. ALWAYS: All file paths written into AUDIT.md MUST be relative to the project root, using forward slashes.
7. ALWAYS: If no `attested` event exists for the ADR (audit blocked), the existing blocker logic MUST remain unchanged.

> STOP-on-BLOCKERS: if `audit_cmd()` does not exist in `src/gzkit/cli.py` or `Ledger.query()` is unavailable, print a BLOCKERS list and halt.

## Discovery Checklist

**Governance (read once, cache):**

- [ ] `AGENTS.md` or `CLAUDE.md` - agent operating contract
- [ ] Parent ADR - understand full context

**Context:**

- [ ] Parent ADR: `docs/design/adr/pre-release/ADR-0.19.0-closeout-audit-processes/ADR-0.19.0-closeout-audit-processes.md`
- [ ] Related OBPIs: OBPI-0.19.0-02 (end-to-end audit pipeline), OBPI-0.19.0-05 (audit_generated event), OBPI-0.19.0-06 (templates)

**Prerequisites (check existence, STOP if missing):**

- [ ] `src/gzkit/cli.py` contains `audit_cmd()` at line ~2634
- [ ] `src/gzkit/ledger.py` contains `Ledger.query()` and `Ledger.get_artifact_graph()`
- [ ] `src/gzkit/ledger.py` contains `attested_event()` and `gate_checked_event()` factory functions

**Existing Code (understand current state):**

- [ ] `src/gzkit/cli.py` lines 2634-2774: current `audit_cmd()` creates AUDIT.md with bare pass/fail rows
- [ ] `src/gzkit/ledger.py` lines 1192-1202: `_apply_attestation_metadata` shows attestation graph fields
- [ ] `src/gzkit/ledger.py` lines 1080-1117: `gate_status_for_adr` returns latest gate statuses

## Quality Gates

### Gate 1: ADR

- [ ] Intent and scope recorded in this OBPI brief
- [ ] Parent ADR checklist item quoted

### Gate 2: TDD

- [ ] Tests written before/with implementation
- [ ] Tests pass: `uv run gz test`
- [ ] Validation commands recorded in evidence with real outputs

### Code Quality

- [ ] Lint clean: `uv run gz lint`
- [ ] Type check clean: `uv run gz typecheck`

## Verification

```bash
uv run gz lint
uv run gz typecheck
uv run gz test

# Specific verification: audit report contains new sections
uv run python -c "
from pathlib import Path
# Verify test module exists
assert Path('tests/test_audit_pipeline.py').exists(), 'test module missing'
print('PASS: test_audit_pipeline.py exists')
"

# Run specific tests
uv run -m unittest tests.test_audit_pipeline -v
```

## Acceptance Criteria

- [x] REQ-0.19.0-04-01: Given an ADR with a ledger `attested` event, when `gz audit ADR-X.Y.Z` runs, then AUDIT.md contains an "Attestation Record" section showing the attestor, status, and timestamp from the ledger.
- [x] REQ-0.19.0-04-02: Given an ADR with ledger `gate_checked` events, when `gz audit ADR-X.Y.Z` runs, then AUDIT.md contains a "Gate Results" section listing each gate's number, status, command, and returncode from the ledger.
- [x] REQ-0.19.0-04-03: Given an ADR directory containing OBPI brief files and a closeout form, when `gz audit ADR-X.Y.Z` runs, then AUDIT.md contains an "Evidence Links" section with relative paths to each OBPI brief and the closeout form.
- [x] REQ-0.19.0-04-04: Given `--json` output mode, when `gz audit ADR-X.Y.Z --json` runs, then the JSON output includes `attestation_record`, `gate_results`, and `evidence_links` keys alongside existing keys without removing any.
- [x] REQ-0.19.0-04-05: Given an ADR with no gate_checked events in the ledger, when `gz audit ADR-X.Y.Z` runs, then the "Gate Results" section renders with a "No prior gate results recorded" note instead of an empty section.

## Completion Checklist

- [x] **Gate 1 (ADR):** Intent recorded in brief
- [x] **Gate 2 (TDD):** Tests pass, coverage maintained
- [x] **Code Quality:** Lint, format, type checks clean
- [x] **Value Narrative:** Problem-before vs capability-now is documented
- [x] **Key Proof:** One concrete usage example is included
- [x] **OBPI Acceptance:** Evidence recorded below

> For ceremony steps and lane-inheritance attestation rules, see `AGENTS.md` section `OBPI Acceptance Protocol`.

## Evidence

### Gate 1 (ADR)

- [x] Intent and scope recorded

### Gate 2 (TDD)

```text
Ran 21 tests in 1.242s — OK
```

### Code Quality

```text
uv run gz lint — All checks passed!
uv run gz typecheck — All checks passed!
uv run gz test — 1052 tests OK
```

### Value Narrative

Before this OBPI, the AUDIT.md report generated by `gz audit` contained only a flat list of verification command pass/fail results with no governance context. An auditor reading the report had to manually cross-reference the ledger to find who attested, what gate results existed, and which OBPI briefs provided evidence. After this OBPI, AUDIT.md is a self-contained audit document that includes the attestation record, historical gate results, and direct links to OBPI brief evidence, making post-hoc governance review possible without ledger querying.

### Key Proof

```bash
$ uv run -m unittest tests.test_audit_pipeline.TestAuditMdAttestationRecord -v
test_attestation_section_fallback_when_no_event ... ok
test_attestation_section_rendered_with_event ... ok

$ uv run -m unittest tests.test_audit_pipeline.TestAuditMdGateResults -v
test_gate_results_note_when_no_events ... ok
test_gate_results_table_rendered ... ok

$ uv run -m unittest tests.test_audit_pipeline.TestAuditMdEvidenceLinks -v
test_closeout_form_listed_when_present ... ok
test_evidence_links_use_forward_slashes ... ok
test_no_evidence_links_note_when_empty ... ok
test_obpi_files_listed ... ok

$ uv run -m unittest tests.test_audit_pipeline.TestAuditEnrichmentJsonKeys -v
test_attestation_record_has_correct_fields ... ok
test_gate_results_list_has_correct_structure ... ok
test_json_output_contains_enrichment_keys ... ok
test_json_output_preserves_existing_keys ... ok
```

### Implementation Summary

- Files created/modified: `src/gzkit/cli.py`, `tests/test_audit_pipeline.py`
- Tests added: 13 enrichment tests (21 total in module)
- Date completed: 2026-03-22
- Attestation status: human attested completed
- Defects noted: none

## Tracked Defects

_No defects tracked._

## Human Attestation

- Attestor: `human:Jeff`
- Attestation: completed
- Date: 2026-03-22

---

**Brief Status:** Completed

**Date Completed:** 2026-03-22

**Evidence Hash:** -
