---
id: OBPI-0.0.30-05-justify-integration
parent: ADR-0.0.30
item: 5
lane: Heavy
status: Completed
---

# OBPI-0.0.30-05-justify-integration: gz justify Integration

## ADR Item

- **Source ADR:** `docs/design/adr/foundation/ADR-0.0.30-complexity-authoring-guidance/ADR-0.0.30-complexity-authoring-guidance.md`
- **Checklist Item:** #5 — "`gz justify` integration (amend `.gzkit/skills/gz-justify/SKILL.md` so authoring-guidance hints for an OBPI's .py allowed-paths surface in the justification scaffold's evidence section)"

**Status:** Draft

## Objective

Amend the existing `gz-justify` skill (ADR-0.0.19) so that when an operator runs `gz justify` for an OBPI whose `Allowed Paths` include `.py` files, the authoring-guidance hints for those files surface in the justification scaffold's evidence section. This closes the ADR-0.0.19 ↔ ADR-0.0.30 forward reference at brief level. The amendment is additive — the existing reasoning-walkthrough structure is preserved.

## Lane

**Heavy** — Skill amendment + extension to `gz justify` rendering pipeline = two contract-touching changes. Foundation-kind brief-level Gate 5 attestation.

## Allowed Paths

- `.gzkit/skills/gz-justify/SKILL.md` — additive amendment only (existing structure preserved); vendor mirrors at `.claude/skills/gz-justify/`, `.agents/skills/gz-justify/`, `.github/skills/gz-justify/` are auto-generated by `gz agent sync control-surfaces` (do not edit directly)
- `src/gzkit/justify/cli.py` — extend `handle_justify` to invoke complexity hints integration
- `src/gzkit/justify/walkthrough.py` — add `complexity_hints_md` field to `Walkthrough` model
- `src/gzkit/justify/templates/walkthrough.md.j2` — render hints block conditionally
- `src/gzkit/justify/complexity_hints.py` (new) — integration module: path extraction, engine invocation, fail-open logging
- `tests/commands/test_justify_authoring_hints.py`, `tests/skills/test_gz_justify_complexity_amendment.py`
- `features/justify_complexity_hints.feature` — behave scenarios tagged with REQ IDs
- `docs/user/runbook.md` — entry under "Complexity doctrine surfaces" describing the integration
- `docs/design/adr/foundation/ADR-0.0.30-complexity-authoring-guidance/obpis/OBPI-0.0.30-05-justify-integration.md` — this brief's evidence section only

## Denied Paths

- `src/gzkit/complexity/authoring/**` — engine/protocol/hint are OBPI-03/04 (consumed, not edited)
- `src/gzkit/commands/complexity_guide.py` — CLI is OBPI-01
- `.gzkit/skills/complexity-guide/**` — skill is OBPI-02
- `src/gzkit/justify/reasoning_structure.py` (or equivalent) — the existing reasoning-walkthrough structure is NOT redesigned
- Any path not listed in Allowed Paths

## Requirements (FAIL-CLOSED)

1. REQUIREMENT: The amendment to `.gzkit/skills/gz-justify/SKILL.md` is additive: a new section names the complexity-hints integration; the existing skill body, frontmatter (other than `skill-version` bump), and operator workflow are preserved unchanged. The skill-version marker bumps per `.gzkit/rules/skill-surface-sync.md` (e.g. `0.5.0` → `0.6.0`).
2. REQUIREMENT: The `gz justify` rendering pipeline detects when the active OBPI's `Allowed Paths` include `.py` glob patterns and, if so, invokes `engine.analyze(path)` from OBPI-03 against each matching path. Hints are injected into the justification scaffold's evidence section under a new sub-heading `### Authoring-time complexity hints`.
3. REQUIREMENT: When no `.py` paths are in scope OR no advise-band crossings are found, the authoring-time complexity hints sub-heading is NOT injected (silent absence, not empty section).
4. REQUIREMENT: The hints rendering shows: per-hint metric + precedence band + archetype + doctrinal-frame headline + recommended-move headline + file:line range. Format consistent with OBPI-01's CLI default human-readable form for visual coherence.
5. REQUIREMENT: The integration uses the OBPI-03 `engine.analyze` interface directly (Python import, not subprocess shellout); the integration is in-process for performance and to avoid subprocess fragility.
6. REQUIREMENT: A failure in the authoring engine (e.g. OBPI-0.0.27-04 distilled-characteristics document missing) MUST NOT block `gz justify`; the integration fails open, omits the complexity-hints sub-heading, and logs a warning to `.gzkit/insights/justify-failures.jsonl` (re-using the failure-log shape from OBPI-0.0.29-09 if compatible; otherwise the integration defines its own log shape with the same envelope).
7. REQUIREMENT: Tests cover: justify on an OBPI with `.py` allowed-paths and advise-band crossings injects the hints sub-heading; justify on an OBPI with no `.py` paths skips the sub-heading; justify on an OBPI with `.py` paths but no crossings skips the sub-heading; engine failure fails open with logged warning; skill amendment adds the new section without modifying existing content; vendor mirrors are byte-identical after sync. Each test decorated with `@covers(REQ-0.0.30-05-NN)`.
8. REQUIREMENT: A behave scenario at `features/justify_complexity_hints.feature` tagged `@REQ-0.0.30-05-{01,02,03}` covers: justify with hints; justify without hints (no .py paths); justify-engine-failure fail-open path.
9. REQUIREMENT: Runbook entry under "Complexity doctrine surfaces" describes the integration: when it fires, where the hints land in the justification scaffold, and the fail-open behavior.
10. REQUIREMENT: TDD discipline; `tempfile`-backed fixtures simulate ADR + OBPI + brief structure for justify invocation.
11. REQUIREMENT: NEVER include the operator's personal email in skill body, code, fixtures, runbook, or commit messages.

> STOP-on-BLOCKERS: if OBPI-03's `engine.analyze` interface is not landed, OR if the existing `gz justify` skill's frontmatter / structure has changed since this OBPI was authored, STOP and reconcile.

## Discovery Checklist

**Prerequisites**

- [x] OBPI-0.0.30-03 engine landed: `src/gzkit/complexity/authoring/engine.py` exposes `analyze(path, table=None) -> tuple[AuthoringHint, ...]` (Completed; receipts in ADR audit ledger)
- [x] OBPI-0.0.30-03 hint model landed: `src/gzkit/complexity/authoring/hint.py` exposes `AuthoringHint` with metric, precedence_band, archetype, doctrinal_frame_headline, recommended_move, file_path, start_line, end_line
- [x] ADR-0.0.19 pre-execution reasoning walkthrough doctrine in place: `src/gzkit/justify/{cli,walkthrough,evidence,anchors,models}.py` and `templates/walkthrough.md.j2`
- [x] `.gzkit/rules/skill-surface-sync.md` skill-version bump discipline confirmed (minor bump for governance integration)
- [x] OBPI-0.0.29-09 failure-log envelope inspected at `src/gzkit/complexity/advisor/timeout.py` and `src/gzkit/hooks/install_complexity_advisor.py:33` — sister-shape JSONL log adopted at `.gzkit/insights/justify-failures.jsonl`

**Existing Code**

- [x] `src/gzkit/justify/cli.py` — `handle_justify` is the rendering dispatch; `gather_evidence` runs before `render_scaffold`. Integration point: after evidence gather, before scaffold render.
- [x] `src/gzkit/justify/walkthrough.py` — `Walkthrough` is a Pydantic frozen model; `render_scaffold(anchor, evidence, now)` is the high-level entry point. Model_validator only checks ordinals/headings — adding optional `complexity_hints_md` field is backward-compatible.
- [x] `src/gzkit/justify/templates/walkthrough.md.j2` — Jinja2 template renders sections in a loop; appending a conditional block at end is a single-line addition.
- [x] `src/gzkit/justify/evidence.py:_extract_allowed_paths_from_brief` — existing helper for parsing `## Allowed Paths` from a brief; canonical pattern is reused in `complexity_hints.py`.
- [x] `src/gzkit/commands/complexity_guide.py:_render_hints_prose` — OBPI-01 CLI prose form; the new integration's markdown format mirrors its field order (file:line, archetype/band, guidance, move).

## 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
- [ ] Runbook entry

### Gate 4: BDD (Heavy)
- [ ] Behave scenarios cover three canonical paths

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

## Verification

```bash
uv run gz lint
uv run gz typecheck
uv run gz test
uv run mkdocs build --strict
uv run gz validate --documents --surfaces
uv run gz agent sync control-surfaces  # post-sync diff empty
uv run gz arb step --name unittest -- uv run -m unittest tests/commands/test_justify_authoring_hints.py tests/skills/test_gz_justify_complexity_amendment.py -v
uv run -m behave features/justify_complexity_hints.feature
```

## Demo

The yielded product — `gz justify` augmented so OBPIs whose Allowed Paths include `.py` files surface authoring-time hints in the scaffold's evidence section.

```bash
# 1. Hits the integration: this OBPI's Allowed Paths include .py modules
#    (justify/cli.py, justify/walkthrough.py), so the rendered scaffold
#    appends a live "### Authoring-time complexity hints" section.
uv run gz justify OBPI-0.0.30-05

# 2. Silent-skip path (REQ-0.0.30-05-02): an OBPI with no .py paths
#    renders without the complexity-hints sub-heading.
uv run gz justify OBPI-0.0.30-02 | grep -c "Authoring-time complexity hints" || echo "0 hits — silent skip honored"

# 3. Behave coverage of all three canonical paths (hits / skip / engine-failure).
uv run -m behave features/justify_complexity_hints.feature
```

## Acceptance Criteria

- [ ] REQ-0.0.30-05-01: Given an OBPI with `.py` allowed-paths and advise-band crossings, when `gz justify` runs, then the justification scaffold's evidence section contains the `### Authoring-time complexity hints` sub-heading with one block per hint.
- [ ] REQ-0.0.30-05-02: Given an OBPI with no `.py` allowed-paths, when `gz justify` runs, then the complexity-hints sub-heading is absent (silent skip).
- [ ] REQ-0.0.30-05-03: Given an OBPI with `.py` paths but no advise-band crossings, when `gz justify` runs, then the complexity-hints sub-heading is absent.
- [ ] REQ-0.0.30-05-04: Given the OBPI-03 engine raises (e.g. distilled-characteristics document missing), when `gz justify` runs, then it completes successfully with the complexity-hints sub-heading omitted and a warning logged.
- [ ] REQ-0.0.30-05-05: Given the amended `gz-justify` skill, when frontmatter is parsed, then `skill-version` has been bumped and the existing structure (other than the new section) is preserved verbatim.
- [ ] REQ-0.0.30-05-06: Given a clean working tree, when `uv run gz agent sync control-surfaces` runs, then the three vendor mirrors of `gz-justify` are byte-identical.

## Completion Checklist

- [ ] Gate 1: Intent recorded
- [ ] Gate 2: RGR cycle; tests pass with `@covers`
- [ ] Code Quality: lint/type clean
- [ ] Gate 3: mkdocs --strict + runbook entry
- [ ] 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 + runbook diff
```

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

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

### Value Narrative

### Key Proof


- Lint: clean -- receipt `arb-ruff-5cd8e776c91d4a26b8fa7cab66be80e7`
- Typecheck: clean -- receipt `arb-step-typecheck-ade473b65cfc48afb7a35878394b8f01`
- Tests (full suite): pass -- receipt `arb-step-unittest-e09b369c3be446359c77be0ac80967bc`
- Tests (OBPI-scoped, 8/8): pass -- receipt `arb-step-unittest-952fc84cf4f245b186327612e88cac2c`
- Docs: mkdocs --strict clean -- receipt `arb-step-mkdocs-68fa7e86bbeb42b29cddc8c882850a46`
- BDD: 4 scenarios pass via `uv run -m behave features/justify_complexity_hints.feature` (REQ-01..06 tagged)
- REQ coverage: 6/6 covered via `uv run gz covers OBPI-0.0.30-05-justify-integration --json` reporting `uncovered_reqs: 0`
- Vendor mirror parity: byte-identical post-sync, verified by `tests/skills/test_gz_justify_complexity_amendment.py::test_vendor_mirrors_byte_identical` and the BDD scenario tagged `@REQ-0.0.30-05-06`
- Closure-narrative observable proof: running `gz justify` on an OBPI with `.py` allowed-paths and an advise-band Python source produces a scaffold containing `### Authoring-time complexity hints` (Scenario REQ-0.0.30-05-01); same command on an OBPI without `.py` paths or with an unavailable engine produces a scaffold without the heading and exit code 0 (Scenarios REQ-0.0.30-05-02, REQ-0.0.30-05-03/04)

### Implementation Summary


- Files created: `src/gzkit/justify/complexity_hints.py`, `tests/commands/test_justify_authoring_hints.py`, `tests/skills/test_gz_justify_complexity_amendment.py`, `features/justify_complexity_hints.feature`, `features/steps/justify_complexity_hints_steps.py`
- Files modified: `src/gzkit/justify/walkthrough.py` (added `complexity_hints_md` field to Walkthrough), `src/gzkit/justify/templates/walkthrough.md.j2` (conditional hints block at end), `src/gzkit/justify/cli.py` (invoke `gather_hints_markdown` after evidence gather), `.gzkit/skills/gz-justify/SKILL.md` (skill-version 6.0.1 → 6.1.0; new "Authoring-time complexity hints" section), `docs/user/runbook.md` (entry under Complexity doctrine surfaces)
- Tests added: 8 unittest tests + 4 behave scenarios; all 6 REQs covered (verified by `gz covers OBPI-0.0.30-05-justify-integration --json` reporting `uncovered_reqs: 0`)
- Date completed: 2026-05-10
- Attestation status: operator attestation received (Stage 4); agent-relayed via `--attestor-present` (GHI #292)
- Defects noted: brief Allowed Paths corrected during plan-audit (`src/gzkit/commands/justify.py` -> `src/gzkit/justify/{cli,walkthrough,complexity_hints}.py` + template); vendor mirror entries removed (auto-generated by `gz agent sync control-surfaces`); Discovery Checklist Prerequisites/Existing Code filled with substantive entries.

### Closing Argument

## Tracked Defects

_No defects tracked._

## Human Attestation

- Attestor: `Jeffry Babb`
- Attestation: attest completed — Stage 4 evidence reviewed: 8/8 unittest tests pass (arb-step-unittest-952fc84cf4f245b186327612e88cac2c), 4/4 behave scenarios pass, lint/typecheck/mkdocs clean (arb-ruff-5cd8e776c91d4a26b8fa7cab66be80e7, arb-step-typecheck-ade473b65cfc48afb7a35878394b8f01, arb-step-mkdocs-68fa7e86bbeb42b29cddc8c882850a46), all 6 REQs covered (uncovered_reqs=0), vendor mirrors byte-identical after sync, ADR-0.0.19 ↔ ADR-0.0.30 forward reference closed via additive SKILL.md amendment (skill-version 6.0.1 → 6.1.0).
- Date: 2026-05-10

---

**Brief Status:** Completed

**Date Completed:** 2026-05-10

**Evidence Hash:** -
