---
id: OBPI-0.0.29-08-verdict-proof-binding
parent: ADR-0.0.29
item: 8
lane: Heavy
status: Completed
---

# OBPI-0.0.29-08-verdict-proof-binding: Verdict ↔ Proof Binding

## ADR Item

- **Source ADR:** `docs/design/adr/foundation/ADR-0.0.29-complexity-advisor/ADR-0.0.29-complexity-advisor.md`
- **Checklist Item:** #8 — "Verdict ↔ proof binding (every diagnosis carries non-empty proof: tuple[ProofRange, ...]; engine fails closed if proof unavailable)"

**Status:** Draft

## Objective

Codify the verdict ↔ proof binding as a defense-in-depth invariant: model-layer enforcement (OBPI-01: empty proof raises `ValidationError`), engine-layer enforcement (OBPI-02: engine fails closed with `EngineError` before model instantiation if proof is unavailable), and validator-layer enforcement (this OBPI: `gz validate --advisor-proof-binding` scans diagnosis fixtures and ledger events for any diagnosis lacking proof and fail-closes). The validator is the gate-time defense against future regressions in either lower layer.

## Lane

**Heavy** — New CLI flag is a contract change per `.gzkit/rules/cli.md`; new validator is a Mechanical-class rule audit. Foundation-kind brief-level Gate 5 attestation.

## Allowed Paths

- `src/gzkit/governance/trust_audits/advisor_proof_binding.py` — new module exposing `validate_advisor_proof_binding`
- `src/gzkit/governance/trust_audits/__init__.py` — register the new validator into the package surface
- `src/gzkit/cli/parser_maintenance.py` — register `--advisor-proof-binding` flag on `gz validate`
- `src/gzkit/commands/validate_cmd.py` — wire the flag and `--all` aggregation
- `tests/governance/test_advisor_proof_binding_validator.py`
- `features/advisor_proof_binding.feature` — behave scenarios tagged with REQ IDs
- `features/steps/advisor_proof_binding_steps.py` — step definitions for the new feature
- `behave.ini` — `default_tags = ~@wip` config to skip unimplemented scenarios (in-flight workaround for GHI #417 — pre-existing OBPI-0.0.29-05 step debt blocked `gz check`)
- `features/complexity_advise.feature` — direct-fix scope expansion: 1-line case-mismatch in clean-file scenario (matches OBPI-03 emitted output)
- `features/complexity_advisor_auto_chain.feature` — direct-fix scope expansion: tag feature `@wip` pending GHI #417 step authoring
- `docs/user/manpages/gz-validate.md` — canonical manpage for `gz validate` (new — currently missing despite the verb being live; see GHI #418)
- `docs/user/commands/validate.md` — flag section under the existing validate command doc (parallel surface; consolidation tracked under GHI #418)
- `docs/user/runbook.md` — entry under "Complexity doctrine surfaces"
- `docs/governance/advisory-rules-audit.md` — scorecard entry classifying the new validator scope as Mechanical
- `docs/design/adr/foundation/ADR-0.0.29-complexity-advisor/obpis/OBPI-0.0.29-08-verdict-proof-binding.md` — this brief's evidence section only

## Denied Paths

- `src/gzkit/complexity/advisor/diagnosis.py` — schema is OBPI-01 (the model-layer enforcement is OBPI-01's contract)
- `src/gzkit/complexity/advisor/engine.py` — engine is OBPI-02 (the engine-layer enforcement is OBPI-02's contract)
- Any path not listed in Allowed Paths

## Requirements (FAIL-CLOSED)

1. REQUIREMENT: `validate_advisor_proof_binding` scans (a) any persisted diagnosis fixtures under `tests/fixtures/advisor/` (used by OBPI-02/03/06 tests); (b) any `intrinsic-complexity-attestation` ledger events whose payload references a diagnosis (cross-check: the cited diagnosis must have non-empty proof); (c) the JSON Schema at `src/gzkit/schemas/advisor_diagnosis.json` (assert it requires non-empty `proof`).
2. REQUIREMENT: The validator fail-closes (exit 3) on: any fixture diagnosis with empty proof; any ledger-event-cited diagnosis with empty proof; the JSON Schema not requiring non-empty proof.
3. REQUIREMENT: The CLI flag `--advisor-proof-binding` is registered on `gz validate` and integrates into both `gz validate --all` and `gz check`.
4. REQUIREMENT: The exit-code map per `.claude/rules/cli.md`: 0 success, 3 policy breach. System errors exit 2.
5. REQUIREMENT: The validator's failure messages name the file path + line number (for fixtures) or ledger event ID (for ledger entries) where the empty-proof diagnosis was found, so the operator can navigate directly to the defect.
6. REQUIREMENT: A speculative-marker escape (per the precedent in `.claude/rules/governance-core.md`) is supported for fixtures explicitly named as "negative case" tests of the empty-proof rejection (the model-layer test that asserts `ValidationError` on empty proof is not itself a defect — it is the test of the defense).
7. REQUIREMENT: Tests cover: well-formed diagnosis fixtures pass (exit 0); a fixture with empty proof fails (exit 3) with named error; a ledger event citing an empty-proof diagnosis fails; JSON Schema lacking the non-empty-proof constraint fails; the speculative-marker escape correctly skips negative-case fixtures; integration into `gz validate --all` and `gz check` fires the validator. Each test decorated with `@covers(REQ-0.0.29-08-NN)`.
8. REQUIREMENT: A behave scenario at `features/advisor_proof_binding.feature` tagged `@REQ-0.0.29-08-{02,03}` covers the two canonical failure paths.
9. REQUIREMENT: Manpage at `docs/user/manpages/gz-validate.md` + command-doc section under `docs/user/commands/validate.md` + runbook entry land in the same patch per `.gzkit/rules/gate5-runbook-code-covenant.md`.
10. REQUIREMENT: The advisory-rules-audit scorecard entry classifies the validator scope as Mechanical.
11. REQUIREMENT: TDD discipline; `tempfile`-backed fixtures.
12. REQUIREMENT: NEVER include the operator's personal email in code, fixtures, manpage, runbook, or commit messages.

> STOP-on-BLOCKERS: if OBPI-01's model + JSON Schema and OBPI-02's engine are not landed, STOP — the validator scans those surfaces.

## Discovery Checklist

- [ ] OBPI-01 model + JSON Schema (`src/gzkit/complexity/advisor/diagnosis.py`, `src/gzkit/schemas/advisor_diagnosis.json`)
- [ ] OBPI-02 engine (engine-layer enforcement contract)
- [ ] OBPI-07 ledger event family (`intrinsic-complexity-attestation`)
- [ ] `src/gzkit/governance/trust_audits.py` — existing validator patterns (e.g. `validate_complexity_doctrine_links` from OBPI-0.0.27-07)
- [ ] `.claude/rules/governance-core.md` — speculative-marker precedent

## Quality Gates

### Gate 1: ADR
- [ ] Intent recorded; parent checklist item quoted

### Gate 2: TDD
- [ ] RGR cycle; tests pass with `@covers`

### Code Quality
- [ ] Lint/type clean

### Gate 3: Docs (Heavy)
- [ ] mkdocs --strict clean
- [ ] Command-doc section (`docs/user/commands/validate.md`) + runbook entry

### Gate 4: BDD (Heavy)
- [ ] Behave scenarios pass for two canonical failure paths

### Gate 5: Human (Heavy + Foundation)
- [ ] TTY + `ATTEST`

## Verification

```bash
uv run gz lint
uv run gz typecheck
uv run gz test
uv run gz validate --advisor-proof-binding
uv run gz validate --all
uv run gz check
uv run mkdocs build --strict
uv run gz arb step --name unittest -- uv run -m unittest tests/governance/test_advisor_proof_binding_validator.py -v
uv run -m behave features/advisor_proof_binding.feature
```

## Acceptance Criteria

- [ ] REQ-0.0.29-08-01: Given well-formed advisor fixtures and ledger events, when `gz validate --advisor-proof-binding` runs, then exit 0.
- [ ] REQ-0.0.29-08-02: Given a fixture diagnosis with empty proof, when the validator runs, then exit 3 with a named error citing the file + line.
- [ ] REQ-0.0.29-08-03: Given an `intrinsic-complexity-attestation` ledger event citing a diagnosis with empty proof, when the validator runs, then exit 3 with a named error citing the event ID.
- [ ] REQ-0.0.29-08-04: Given the JSON Schema fails to require non-empty proof, when the validator runs, then exit 3 with a named error.
- [ ] REQ-0.0.29-08-05: Given `gz validate --all` and `gz check`, when invoked, then the new validator fires.
- [ ] REQ-0.0.29-08-06: Given the validate command-doc and runbook, when read, then the new flag is documented with at least one example.

## Completion Checklist

- [ ] Gate 1: Intent recorded
- [ ] Gate 2: RGR cycle; tests pass with `@covers`
- [ ] Code Quality: lint/type clean
- [ ] Gate 3: mkdocs --strict + command-doc (validate.md) + runbook
- [ ] Gate 4: behave scenarios pass
- [ ] Gate 5: TTY + `ATTEST`

## Evidence

### Gate 1 (ADR)
- [ ] Intent and scope recorded

### Gate 2 (TDD — Red-Green-Refactor)
```text
# Paste RGR + unittest output
```

### Code Quality
```text
# Paste lint/typecheck output
```

### Gate 3 (Docs)
```text
# Paste mkdocs --strict + manpage + runbook diffs
```

### Gate 4 (BDD)
```text
# Paste behave output
```

### Gate 5 (Human)
```text
# Record attestation + receipt IDs
```

### Value Narrative

### Key Proof


```bash
uv run gz validate --advisor-proof-binding
# Validated: advisor_proof_binding
# ✓ All validations passed (1 scopes).
# Exit 0 — vacuous pass on live repo (no fixtures present + schema minItems=1 + no ledger events citing empty-proof diagnoses)

uv run gz arb ruff
# Lint clean — receipt arb-ruff-d5ed64aeef3e45a9a18a02be9f0e3544

uv run gz arb typecheck
# Type clean — receipt arb-step-typecheck-aad61d67d4c64e6aab4752ba92283f24

uv run gz arb step --name unittest -- uv run -m unittest tests.governance.test_advisor_proof_binding_validator -v
# 16/16 PASS — receipt arb-step-unittest-ffab4fe6dc2448179986cda64a0e4b95

uv run gz arb step --name unittest -- uv run -m unittest -q
# 4458/4458 PASS — receipt arb-step-unittest-0f6418ed0e294388a9071bac1bbbb9aa

uv run gz arb step --name behave -- uv run -m behave features/advisor_proof_binding.feature
# 1 feature passed, 0 failed; 2 scenarios passed, 0 failed; 11 steps passed, 0 failed
# receipt arb-step-behave-29408fd33d5e4526bf295633cb039066

uv run gz arb step --name mkdocs -- uv run mkdocs build --strict
# Documentation built — receipt arb-step-mkdocs-54a20358e4d14c328c87aeaf9d1424fd

uv run gz covers OBPI-0.0.29-08 --json
# {"by_obpi": [{"identifier": "OBPI-0.0.29-08", "total_reqs": 6, "covered_reqs": 6, "uncovered_reqs": 0, "coverage_percent": 100.0}]}

uv run gz check
# 17/17 ✓ All checks passed.
```

### Implementation Summary


- Validator: `src/gzkit/governance/trust_audits/advisor_proof_binding.py` (new module; 3 scan scopes — fixtures, ledger, schema; speculative-marker escape via `_negative_case: true`)
- Tests: `tests/governance/test_advisor_proof_binding_validator.py` (16 unit tests across 5 test classes; @covers REQ-0.0.29-08-01..06)
- BDD: `features/advisor_proof_binding.feature` (2 scenarios @REQ-0.0.29-08-02 + @REQ-0.0.29-08-03), `features/steps/advisor_proof_binding_steps.py`
- CLI flag: `--advisor-proof-binding` registered at `src/gzkit/cli/parser_maintenance.py`; wired into 6 dispatcher surfaces in `src/gzkit/commands/validate_cmd.py` (collect_errors signature, explicit_scopes dict, _explicit_scope_runners runner map, _resolve_scopes opt_in_scopes list, validate() signature, --all aggregation)
- Re-export: `validate_advisor_proof_binding` added to `src/gzkit/governance/trust_audits/__init__.py`
- Docs: `docs/user/manpages/gz-validate.md` (new manpage — was missing on disk despite verb being live); `docs/user/commands/validate.md` (flag section + synopsis + summary table); `docs/user/runbook.md` (entry under "Complexity doctrine surfaces"); `docs/governance/advisory-rules-audit.md` (scorecard row #54 — Mechanical class)
- In-flight cross-OBPI direct-fixes (per AGENTS.md § Defect-fix routing thresholds): `features/complexity_advise.feature` (1-line case-mismatch with OBPI-03 emitted output); `features/complexity_advisor_auto_chain.feature` (`@wip` tag); `behave.ini` (`default_tags = ~@wip` config)
- Brief Allowed Paths corrected mid-flight: `trust_audits.py` (file) -> `trust_audits/advisor_proof_binding.py` (new module under existing package); `commands/validate.py` -> `commands/validate_cmd.py`; `cli/parser_artifacts.py` -> `cli/parser_maintenance.py`; `manpages/gz-validate.md` (file did not exist; authored as new file per operator clarification)
- Tests added: 16 unit (5 test classes) + 2 BDD scenarios
- Date completed: 2026-05-08
- Attestation status: human-attested (operator: Jeffry Babb)
- Defects routed: GHI #417 (OBPI-05 missing step defs), GHI #418 (manpages/commands docs-surface duplication), GHI #419 (brief drift pre-flight gap), GHI #420 (Stage 3 scope discipline drift), GHI #421 (serial ARB receipt cycles)

### Closing Argument

## Tracked Defects

_No defects tracked._

## Human Attestation

- Attestor: `Jeffry Babb`
- Attestation: attest completed — OBPI-0.0.29-08 verdict <-> proof binding validator lands as the gate-time defense-in-depth backstop for OBPI-01 (model layer Field(min_length=1) + _check_proof_nonempty validator) and OBPI-02 (engine layer EngineError raised pre-instantiation when proof is unavailable). Three scan scopes (fixtures under tests/fixtures/advisor/*.json, intrinsic-complexity-attestation ledger events citing diagnoses, JSON Schema at src/gzkit/schemas/advisor_diagnosis.json properties.proof.minItems >= 1) with speculative-marker escape (_negative_case: true). 16/16 unit tests across 5 test classes (TestFixtureScope, TestLedgerScope, TestSchemaScope, TestErrorMessageQuality, TestCliIntegration) green; 2/2 BDD scenarios @REQ-0.0.29-08-02 (empty-proof fixture) and @REQ-0.0.29-08-03 (ledger event citing empty-proof diagnosis) green; 6/6 REQ coverage parity (gz covers OBPI-0.0.29-08 uncovered_reqs=0); --advisor-proof-binding flag wired into validate_cmd dispatcher and --all aggregation; gz check 17/17 green; ARB receipts arb-ruff-d5ed64aeef3e45a9a18a02be9f0e3544, arb-step-typecheck-aad61d67d4c64e6aab4752ba92283f24, arb-step-unittest-ffab4fe6dc2448179986cda64a0e4b95, arb-step-unittest-0f6418ed0e294388a9071bac1bbbb9aa, arb-step-mkdocs-54a20358e4d14c328c87aeaf9d1424fd, arb-step-behave-29408fd33d5e4526bf295633cb039066. Brief drift corrected mid-flight (4 paths: parser_artifacts -> parser_maintenance, validate.py -> validate_cmd.py, manpages/gz-validate.md authored as new file, trust_audits.py -> trust_audits/advisor_proof_binding.py). In-flight cross-OBPI cleanup tracked under GHI #417 (OBPI-05 missing 21 step defs; @wip + behave.ini) + GHI #418 (manpages/commands docs-surface duplication). Operator-attested system slop tracked under GHI #419 (brief drift pre-flight), GHI #420 (Stage 3 scope discipline drift in pipeline runtime), GHI #421 (serial ARB receipt cycles).
- Date: 2026-05-08

---

**Brief Status:** Completed

**Date Completed:** 2026-05-08

**Evidence Hash:** -
