---
id: OBPI-0.0.38-03-retroactive-classification
parent: ADR-0.0.38-evidence-authority-projection-doctrine
item: 3
lane: Heavy
status: Draft
---

# OBPI-0.0.38-03-retroactive-classification: Retroactive Surface-Axis Classification

## ADR Item

- **Source ADR:** `docs/design/adr/foundation/ADR-0.0.38-evidence-authority-projection-doctrine/ADR-0.0.38-evidence-authority-projection-doctrine.md`
- **Checklist Item:** #3 — `retroactive-classification` — Classify every existing surface; emit `surface_axis_classified` ledger event per surface; produce `artifacts/audits/surface-axis-2026-05-06.md` baseline.

**Status:** Draft

## Objective

One-time audit pass: enumerate every existing gzkit surface (skills, rules, code-level receipt-emitting modules, validator scopes, CLI verbs, Layer-3 derived views), assign each a function axis (Authoritative / Evidentiary / Projection) with cited rationale, edit the declaration site to record the axis (skill frontmatter, rule body marker, module-level constant, scope registration, parser registration), emit a `surface_axis_classified` ledger event per surface, produce the canonical baseline audit at `artifacts/audits/surface-axis-2026-05-06.md`, and remove the OBPI-0.0.38-02 opt-out so that `uv run gz check` enforces `--surface-axis` going forward.

## Lane

**Heavy** - This OBPI changes a command/API/schema/runtime contract surface.

> Heavy is reserved for command/API/schema/runtime-contract changes. Process,
> documentation, and template-only work stays Lite unless it changes one of
> those external surfaces.

## Allowed Paths

<!-- What files/directories are IN SCOPE? Be explicit with paths. -->

- `docs/design/adr/foundation/ADR-0.0.38-evidence-authority-projection-doctrine/**` — parent ADR package
- `.gzkit/skills/**/SKILL.md` — frontmatter `surface_axis:` field added per skill
- `.gzkit/rules/**.md` — body marker `<!-- surface-axis: <axis> -->` added per rule
- `src/gzkit/**/*.py` — module-level `SURFACE_AXIS: Final[str]` constant added to receipt-emitting / fail-closed-emitting modules
- `src/gzkit/cli/parser_*.py` — `axis` field added to validator-scope and CLI-verb registrations
- `artifacts/audits/surface-axis-2026-05-06.md` (new) — canonical baseline audit
- `tests/governance/test_surface_axis_baseline.py` (new) — REQ-derived assertions on completeness of the audit
- `data/surface_axis_inventory.json` (new) — frozen baseline inventory snapshot, machine-readable mirror of the audit
- Mirror surfaces auto-generated by `gz agent sync control-surfaces`: `.claude/skills/**`, `.claude/rules/**`, `.github/skills/**`, `.github/instructions/**` — these update mechanically when canonical surfaces change

## Denied Paths

<!-- What files/directories are OUT OF SCOPE? Agents will not touch these. -->

- `.gzkit/rules/evidence-vs-authority.md` — authored under OBPI-0.0.38-01; this OBPI may not edit it
- `src/gzkit/governance/surface_axis*.py` — landed under OBPI-0.0.38-01 and -02; this OBPI may not modify the validator or the enum
- `src/gzkit/governance/trust_audits.py` — `audit_surface_axis_*` functions landed under OBPI-0.0.38-02; this OBPI may not modify them
- `data/surface_axis_waivers.json` — registry shape landed under -02; this OBPI may seed waivers ONLY when a Projection-as-input violation surfaces during classification AND the operator approves the waiver in the Gate 5 walkthrough
- New runtime dependencies
- CI files, lockfiles

## Requirements (FAIL-CLOSED)

<!-- Constraints that MUST hold. Numbered list. NEVER/ALWAYS language.
     These are the rules agents ground against. If not met, OBPI fails. -->

1. REQUIREMENT: Every skill under `.gzkit/skills/**/SKILL.md` declares `surface_axis: {authoritative,evidentiary,projection}` in its YAML frontmatter. Skills that produce no decision-influencing output (purely informational scaffolding, ceremony narration) are still classified — likely as Evidentiary if they emit findings, or by self-reference to a parent skill. NEVER skip a skill.
2. REQUIREMENT: Every rule under `.gzkit/rules/**.md` declares the body marker `<!-- surface-axis: <axis> -->` immediately after the rule version marker. Default classification for rule files is `authoritative` (rules bind agent behavior); explicit Evidentiary or Projection requires cited rationale in the audit document.
3. REQUIREMENT: Every Python module under `src/gzkit/**` that emits an ARB receipt OR a fail-closed exit code OR a `passed: bool` field consumed by a caller declares `SURFACE_AXIS: Final[str] = "<axis>"` at module scope. Modules that emit only logging or only data-transformation outputs are not in scope.
4. REQUIREMENT: Every registered validator scope in `src/gzkit/cli/parser_validate.py` (or active equivalent) carries an `axis` field in its registration; default is `authoritative` for all `gz validate --*` scopes unless explicitly overridden with cited rationale.
5. REQUIREMENT: Every CLI verb in `src/gzkit/cli/parser_artifacts.py` and sibling parser files whose default human-readable output an operator acts on declares an `axis` field. Layer-3 derived views (`gz status`, `gz state`, `gz adr report` if it renders ledger projections) classify as `projection`; commands that bind decisions (`gz audit`, `gz attest`) classify as `authoritative`; commands that produce findings (`gz adr evaluate`, `gz plan-audit`) classify as `evidentiary`.
6. REQUIREMENT: `artifacts/audits/surface-axis-2026-05-06.md` is the canonical baseline. It contains a table of every surface (one row per surface) with columns: `surface_path`, `surface_kind` (skill/rule/module/scope/verb), `axis`, `rationale_anchor` (file:line or commit-hash citation), `classifier_decision_quote` (1–3 sentences naming the binding character observed). The audit's row count MUST equal `surface_axis_inventory.enumerate_surfaces()` count from OBPI-0.0.38-02.
7. REQUIREMENT: `data/surface_axis_inventory.json` is a frozen Pydantic-serialized snapshot of the same inventory in machine-readable form, with a `generated_at` timestamp and a `source_commit` field. The JSON file's content MUST be byte-derivable from re-running the enumeration helper — i.e., regenerable, not hand-edited.
8. REQUIREMENT: For each surface classified, a `surface_axis_classified` ledger event is appended to `.gzkit/ledger.jsonl` via the canonical event-emit helper (NEVER hand-write to ledger.jsonl per AGENTS.md § Behavior Rules — Never #2). Event payload: `{surface_path, surface_kind, axis, rationale, classifier, source_commit}`.
9. REQUIREMENT: The `surface_axis_classified` event family is added to `.gzkit/schemas/ledger_events.json` in the same OBPI; no new event family lands without a schema entry.
10. REQUIREMENT: After classification, `uv run gz validate --surface-axis` exits 0 on a clean repo. The OBPI-0.0.38-02 opt-out from the default `gz check` scope set is removed in this OBPI's terminal commit; `gz check` enforces `--surface-axis` from this point forward.
11. REQUIREMENT: `tests/governance/test_surface_axis_baseline.py` asserts: (a) the baseline audit row count matches the inventory; (b) every classified surface has a non-empty rationale anchor; (c) the JSON inventory regenerates byte-identically from the enumeration helper; (d) the count of `surface_axis_classified` ledger events equals the audit row count.
12. REQUIREMENT: When classification is genuinely ambiguous on a hybrid surface (e.g. `gz-plan-audit` produces advisory findings most of the time but exits non-zero on a small set of structural failures), the OBPI MUST split the surface into two declared surfaces (the advisory branch is Evidentiary; the structural-fail branch is Authoritative) OR document the ambiguity with a `surface_axis_undecidable` ledger event naming the named ambiguity, the proposed resolution path, and the GHI tracking the resolution. `surface_axis_undecidable` events MUST be remediated within 30 days; expired events fail-close `gz validate --surface-axis`.
13. REQUIREMENT: For each high-stakes Authoritative classification (any `gz validate --*` scope, any `_enforce_*` function, any schema validator), the audit document includes a "tested-by" anchor citing a regression test that observably fail-closes when the surface's binding behavior is broken. Surfaces lacking such a regression test trigger a follow-up GHI per AGENTS.md § Prime Directive (defects must be trackable).
14. REQUIREMENT: Gate 5 attestation is mandatory (parent ADR is foundation-kind heavy-lane). Operator walkthrough at OBPI close MUST include explicit confirmation of: (a) sample classification of three high-stakes Authoritative surfaces (operator quotes the rationale anchor); (b) Projection classification of `gz status`, `adr-status.md`, and one other named derived view; (c) Evidentiary classification of `gz-plan-audit`, `gz-adr-evaluate`, `advisor()`, and one other named advisory surface. The attestation text cites the audit document by path and one canonical-step ARB receipt name (`arb-step-surface-axis-baseline-*`).
15. REQUIREMENT: `gz agent sync control-surfaces` runs cleanly after all classification edits land; mirrors update without divergence. Skill-version frontmatter is bumped per `.gzkit/rules/skill-surface-sync.md` for every edited skill (minor bump, governance rule change).
16. REQUIREMENT: NEVER classify a surface as `authoritative` without observable evidence of fail-closed behavior. NEVER classify a Projection surface that an existing caller relies on as fail-closed gate input — the caller is the defect, not the projection. Such cases route to either a waiver (when call site is genuinely benign smoke-test) or a follow-up fix GHI (when call site is Boundary-6 drift to be repaired).

> STOP-on-BLOCKERS: if prerequisites are missing, print a BLOCKERS list and halt.

## Discovery Checklist

<!-- What to read before implementation. Complete this checklist first.
     Order matters: read the structured input (parent ADR § Decision)
     before the unstructured one (allowed paths, prerequisites). -->

**Parent ADR (read first; order pinned — GHI #321):**

- [ ] **Parent ADR § Decision item — quote the line this OBPI implements** verbatim into the brief's Implementation Summary. The Decision item is the contract; everything else hangs off it.
- [ ] Parent ADR § Intent — the why-frame for the Decision read above.
- [ ] Parent ADR file: `docs/design/adr/foundation/ADR-0.0.38-evidence-authority-projection-doctrine/ADR-0.0.38-evidence-authority-projection-doctrine.md`

> **STOP:** If you cannot quote the parent ADR § Decision item that this OBPI implements, STOP and re-read. Do not proceed to Allowed Paths, Prerequisites, or implementation until the Decision quote is in hand.

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

- [ ] `.github/discovery-index.json` - repo structure
- [ ] `AGENTS.md` or `CLAUDE.md` - agent operating contract

**Context:**

- [ ] Related OBPIs in same ADR

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

- [ ] Required path exists or is intentionally created in this OBPI: `docs/design/adr/foundation/ADR-0.0.38-evidence-authority-projection-doctrine/ADR-0.0.38-evidence-authority-projection-doctrine.md`
- [ ] Required path exists or is intentionally created in this OBPI: `docs/governance/GovZero/adr-status.md`
- [ ] Parent ADR evidence artifacts referenced by this brief are present

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

- [ ] Existing tests adjacent to the Allowed Paths reviewed before implementation
- [ ] Parent ADR integration points reviewed for local conventions

## Quality Gates

<!-- Which gates apply and how to verify them. -->

### Gate 1: ADR

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

### Gate 2: TDD (Red-Green-Refactor)

- [ ] Tests derived from brief acceptance criteria, not from implementation
- [ ] Red-Green-Refactor cycle followed per behavior increment
- [ ] 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`

<!-- Heavy lane only: -->
### Gate 3: Docs (Heavy only)

- [ ] Docs build: `uv run mkdocs build --strict`
- [ ] Relevant docs updated

### Gate 4: BDD (Heavy only)

- [ ] Acceptance scenarios pass: `uv run -m behave features/`

### Gate 5: Human (Heavy only)

- [ ] Human attestation recorded

## Verification

<!-- What commands verify this work? Use real repo commands, then paste the
     outputs into Evidence. -->

```bash
# OBPI-specific tests
uv run -m unittest tests/governance/test_surface_axis_baseline.py -v

# The validator must now pass on the clean repo (closes the loop with -02)
uv run gz validate --surface-axis
uv run gz validate --surface-axis --json

# Confirm gz check now includes surface-axis (opt-out removed)
uv run gz check --list-scopes

# Standard heavy-lane gates
uv run gz lint
uv run gz typecheck
uv run gz validate --documents
uv run gz validate --advisory-scorecard
uv run mkdocs build --strict
uv run -m behave features/

# Sync control-surfaces clean (skill-version bumps must propagate)
uv run gz agent sync control-surfaces

# ARB receipt for attestation
uv run gz arb step --name surface-axis-baseline -- uv run gz validate --surface-axis

# Confirm canonical artifacts exist
test -f artifacts/audits/surface-axis-2026-05-06.md
test -f data/surface_axis_inventory.json
test -f tests/governance/test_surface_axis_baseline.py

# Confirm ledger events emitted
uv run gz events --type surface_axis_classified --json

# Confirm at least three sampled high-stakes classifications
grep -E "_enforce_human_attestation_authenticity|_requires_human_obpi_attestation|gz validate --documents" artifacts/audits/surface-axis-2026-05-06.md
```

## Acceptance Criteria

<!--
Specific, testable criteria for completion.
Each checkbox MUST carry a deterministic REQ ID:
REQ-<semver>-<obpi_item>-<criterion_index>
-->

- [ ] REQ-0.0.38-03-01: Given the canonical inventory enumerator from OBPI-0.0.38-02, when classification is complete, then every enumerated surface has an axis declaration site populated with a non-default value (or a default with cited rationale).
- [ ] REQ-0.0.38-03-02: Given `artifacts/audits/surface-axis-2026-05-06.md`, when read, then the row count equals the inventory enumerator's count and every row has non-empty `axis`, `rationale_anchor`, and `classifier_decision_quote` fields.
- [ ] REQ-0.0.38-03-03: Given `data/surface_axis_inventory.json`, when regenerated by the enumeration helper, then the regenerated content is byte-identical to the committed file (excluding the `generated_at` timestamp).
- [ ] REQ-0.0.38-03-04: Given the ledger after classification, when filtered for `surface_axis_classified` events, then the count equals the audit row count.
- [ ] REQ-0.0.38-03-05: Given `uv run gz validate --surface-axis` on the clean repo at OBPI close, when run, then exit code is 0 and no violations or unexpired waivers are emitted.
- [ ] REQ-0.0.38-03-06: Given the OBPI-0.0.38-02 opt-out of `--surface-axis` from the default `gz check` scope set, when -03 closes, then the opt-out is removed and `uv run gz check --list-scopes` includes `surface-axis`.
- [ ] REQ-0.0.38-03-07: Given a hybrid surface (e.g. `gz-plan-audit`), when classified, then either the surface is split into two declared surfaces with separate axis declarations OR a `surface_axis_undecidable` ledger event is emitted naming the ambiguity and tracking GHI.
- [ ] REQ-0.0.38-03-08: Given a high-stakes Authoritative classification (validator scope, `_enforce_*` function, schema validator), when the audit row is read, then a `tested_by` anchor cites a regression test or a follow-up GHI tracking test creation.
- [ ] REQ-0.0.38-03-09: Given the Gate 5 walkthrough, when the operator attests, then the attestation text quotes (a) three high-stakes Authoritative rationale anchors, (b) three Projection classifications including `gz status` and `adr-status.md`, and (c) four Evidentiary classifications including `gz-adr-evaluate`, `gz-plan-audit`, `advisor()`, and one other.
- [ ] REQ-0.0.38-03-10: Given the attestation text, when validated, then it cites at least one canonical-step receipt prefixed `arb-step-surface-axis-baseline-` per AGENTS.md § Attestation.
- [ ] REQ-0.0.38-03-11: Given the post-classification repo, when `uv run gz agent sync control-surfaces` runs, then exit code is 0 and no mirror is divergent. Skill-version bumps follow `.gzkit/rules/skill-surface-sync.md`.
- [ ] REQ-0.0.38-03-12: Given the Denied Paths boundary, when this OBPI's diff is reviewed, then no validator implementation files are modified, no rule body of `evidence-vs-authority.md` is edited, and no waiver is added to `surface_axis_waivers.json` without operator-attested rationale in the brief evidence.

## Completion Checklist

<!-- Verify all gates before marking OBPI accepted. -->

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

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

## Evidence

<!-- Record observations during/after implementation.
     Command outputs, file:line references, dates. -->

### Gate 1 (ADR)

- [ ] Intent and scope recorded

### Gate 2 (TDD — Red-Green-Refactor)

```text
# Paste test output here
```

### Code Quality

```text
# Paste lint/format/type check output here
```

### Gate 3 (Docs)

```text
# Paste docs-build output here when Gate 3 applies
```

### Gate 4 (BDD)

```text
# Paste behave output here when Gate 4 applies
```

### Gate 5 (Human)

```text
# Record attestation text here when required by parent lane
```

### Value Narrative

<!-- What problem existed before this OBPI, and what capability exists now? -->

### Key Proof

<!-- One concrete usage example, command, or before/after behavior. -->

### Implementation Summary

- Files created/modified:
- Tests added:
- Date completed:
- Attestation status:
- Defects noted:

## Tracked Defects

<!-- Record GitHub defect linkage when defects are discovered during this OBPI.
     Use one bullet per issue so status surfaces can preserve traceability. -->

_No defects tracked._

## Human Attestation

- Attestor: `<name>` when required, otherwise `n/a`
- Attestation: substantive attestation text or `n/a`
- Date: YYYY-MM-DD or `n/a`

---

**Brief Status:** Draft

**Date Completed:** -

**Evidence Hash:** -
