---
applyTo: "tests/**"
---
<!-- Generated by gz agent sync — do not edit -->

<!-- rule-version: 0.8.0 -->

# Test Policy (canonical)

> **Rule version:** `0.8.0` — added § Verification exit-code integrity (GHI #589): never pipe a verifier through `tail`/`head`/`grep`; the shell reports the filter's exit, not the verifier's — read the ARB receipt `exit_status`. Prior `0.7.0` — producer reconciled to GHI #552: `gz git-sync` now stamps `Task: TASK-gz-git-sync` (previously only `Ceremony:`, which #552 stopped accepting on src/tests scope — leaving every sync commit silently non-compliant), and the direct-fix slug's `-#<ghi>` anchor is now OPTIONAL (operator moratorium on reflexive GHI-filing, 2026-06-01). Prior `0.6.0`: GHI #552 strict-mode — src/tests commits MUST carry `Task:`; `Ceremony:`/`Eval-feedback-source:` no longer substitute. Surfaces TASK as the leaf vertebra of the PRD → Constitution → ADR → OBPI → REQ → TASK → Attestation governance spine (per AGENTS.md § Workflow).

## General Rules (binding)

- Use **stdlib `unittest`**; no pytest.
- Prefer **table-driven** tests with deterministic seeds; no network/external services.
- **Smoke/BVT <=60s**; cover current-scope surfaces only.
- Fixtures: local, small, reproducible; avoid huge goldens.
- **Database isolation:** unit tests MUST use `tempfile` temp DBs; NEVER use live/production databases.
- **Cleanup:** NEVER use raw `shutil.rmtree()` in tearDown. Use `tempfile.TemporaryDirectory()` context manager.

## Coverage Floor (binding)

- **Minimum line coverage: 40.00%**
- Before closing any brief, verify coverage has not regressed.

## Run / Verify

```bash
uv run ruff check . --fix && uv run ruff format .
uv run -m unittest -v
```

- All tests PASS; smoke suite <=60s.
- Coverage >=40.00%.

## Red-Green-Refactor (TDD Discipline — binding)

Gate 2 is named TDD. Red-Green-Refactor is a repeating cycle per behavior increment:

- **Red:** Write a test for a behavior required by the OBPI brief (`REQ-*` identifier). Run it. Watch it fail for the right reason. A test that passes on first run is not Red.
- **Green:** Write the **simplest code** that makes the test pass.
- **Refactor:** Improve the code's structure without changing its behavior. Adding new behavior is not refactoring — that starts a new Red.

**Derivation rule:** Test cases derive from OBPI brief acceptance criteria, not from the implementation. When adding tests outside a pipeline run, locate the governing OBPI brief and derive from its requirements.

**Tests assert semantics, not strings (invariant 6f).** Assertions derive from the REQ or skill contract, not from a run of the code.

**Eval-awareness corollary.** Audit-helper names MUST NOT pattern-match as audit-step names — name them by behavior, not audit role (e.g. `assert_receipt_id_resolves` not `assert_audit_passes`).

**Output-form fixture carve-out.** Output-form assertions are permitted in dedicated fixture tests per `.gzkit/rules/tool-skill-runbook-alignment.md` § Invariant 3. Keep them in separate test classes from REQ-derived unit tests. *(GHI #270 reconciliation: output-form fixture tests are **BEHAVIOR** REQ proofs under the REQ Scope Discipline taxonomy — they test CLI render-code behavior, not file content. The apparent contradiction between § 6f's prose-content prohibition and Invariant 3's render-form requirement dissolves once REQ kind is named.)*

**Per-increment rhythm:** One test → one observed RED → minimum code to GREEN → next increment.

**RED evidence:** Do not author ARB receipts with `exit_status=1` as "RED receipts". Gate 2 TDD claims cite only GREEN-side receipts (`arb-step-unittest-*`).

**Verification exit-code integrity (binding, GHI #589).** A verifier's truth is its own exit code, never a downstream filter's. NEVER pipe `unittest`/`behave`/`mkdocs --strict` (or any ARB-wrapped verifier) through `tail`/`head`/`grep`/`Select-Object`: the shell reports the *last* process's exit (the filter's — always 0), masking a failing suite as a green run. Capture to a file (`> out.log 2>&1`) and read the ARB receipt's `exit_status` (GHI #317). A harness "exit code 0" notification on a piped command attests the filter, not the verifier.

## TASK-Driven Workflow (binding)

TASK is the leaf vertebra of the governance spine `PRD → Constitution → ADR → OBPI → REQ → TASK → Attestation` (AGENTS.md § Workflow; `docs/governance/governance_runbook.md` § "fourth tier: ADR > OBPI > REQ > TASK"; `docs/governance/agent-contract-rationale.md` § TASK-driven workflow).

**src/tests commits MUST carry a `Task:` trailer.** Enforced by `gz validate --commit-trailers`. `Ceremony:` and `Eval-feedback-source:` no longer substitute for `Task:` on src/tests scope — they remain valid for non-src/tests commits (sync artifacts under `.claude/`, roadmap notes, ledger reconciles, rule-edit metadata).

**Task: trailer forms (all accepted):**

| Form | Use when | Example |
|------|----------|---------|
| Formal `TASK-X.Y.Z-NN-MM-PP` | OBPI-scoped work; minted via `gz task start` against an active brief + REQ | `Task: TASK-0.22.0-01-01-01` |
| Slug `TASK-<kebab-slug>` (optional `-#<ghi>`) | Direct-fix work outside OBPI scope (per AGENTS.md § Defect-fix routing). The `-#<ghi>` anchor is OPTIONAL — append it only when a GHI already exists; filing a GHI *to satisfy the trailer* is a moratorium violation (operator directive 2026-06-01) | `Task: TASK-fix-git-log-encoding` or `Task: TASK-task-spine-restoration-#552` |
| Ceremony `TASK-gz-git-sync` | Auto-stamped by `gz git-sync` on every sync commit; attributes bundled surface-sync to the git-sync ceremony rather than a synthetic per-REQ id | `Task: TASK-gz-git-sync` |

**OBPI-scoped steps:** `gz covers` → `gz task start TASK-X.Y.Z-NN-MM-PP` → TDD cycle → commit with `Task:` trailer → `gz task complete TASK-X.Y.Z-NN-MM-PP` → `@covers(REQ-...)` decorator.

**Direct-fix steps:** `git log --since='60 days ago' --oneline --grep='^fix('` (precedent check per AGENTS.md § Defect-fix routing) → TDD cycle → commit with `Task: TASK-<slug>` trailer (append `-#<ghi>` only when a GHI already exists; do not file one to satisfy the trailer — operator moratorium).

## Two runners, one test surface

| Runner | Location | Purpose | Contract |
|--------|----------|---------|----------|
| `unittest` | `tests/` | Pure Python behavior, command contracts | Mocked subprocess boundaries; deterministic; fast |
| `behave` | `features/` | End-to-end CLI and governance scenarios | Real operator flows, Gherkin-readable |

`gz test` runs `unittest` over `tests/` then `behave` over `features/`. Both gates must pass for `gz check`.

### Unit-tier contract (binding)

- Mock every subprocess boundary (`_uv_sync_patcher`, `_git_subprocess_patcher`, `_quick_init` in `tests/commands/common.py`)
- Complete in < 200ms; deterministic; `tempfile` temp DBs only
- E2E scenarios requiring real subprocess → `features/*.feature`

### Behave scenario tagging

Behave scenarios covering a REQ carry `@REQ-X.Y.Z-NN-MM` as a scenario tag. Enforced by `gz validate --behave-req-tags` (fires on `Completed`/`Validated` briefs only, GHI #276/#323). Waivers in `data/behave_coverage_waivers.json`.

## REQ Scope Discipline (binding)

Every REQ in an OBPI brief's `## Acceptance Criteria` carries exactly one of three kinds,
declared as a bracketed inline tag: `REQ-X.Y.Z-NN-NN [kind]: claim text`.

### Three-kind taxonomy

| Kind | What it covers | Proof channel |
|------|---------------|---------------|
| **BEHAVIOR** | Code behavior — functions, commands, CLI outputs, state transitions | `@covers`-decorated test in `tests/**` (existing pattern, unchanged) |
| **SUPPORT** | Governance artifacts, doctrine docs, rule files, data files that *support* behavior but are not behavior themselves | Ledger `artifact_edited` event citing the artifact path **AND** structural validator scope (e.g. `gz validate --documents`) admitting the artifact's shape |
| **STRUCTURAL-FENCE** | Integration-state properties scoped to the parent ADR's boundary — cross-OBPI invariants that audit at ADR closeout, not per-OBPI | Parent-ADR `## Boundary Invariants` entry, audited at ADR closeout layer |

### Brief-authoring tag syntax

```text
REQ-X.Y.Z-NN-01 [behavior]: the system does X when Y
REQ-X.Y.Z-NN-02 [support]: the rule file carries subsection Z
REQ-X.Y.Z-NN-03 [structural-fence]: cross-OBPI boundary invariant P holds
```

### Proof-channel matrix

| Kind | Test `@covers`? | Ledger event? | Structural validator? | Parent-ADR invariant? |
|------|:--------------:|:------------:|:--------------------:|:--------------------:|
| BEHAVIOR | **required** | — | — | — |
| SUPPORT | — | **required** | **required** | — |
| STRUCTURAL-FENCE | — | — | — | **required** |

### What this replaces

Before ADR-0.0.59: every REQ used the BEHAVIOR proof channel uniformly, producing
tautological filesystem-grep tests for content REQs (32% project-wide / 42% governance).
SUPPORT-kind REQs are now witnessed by the ledger + structural validator; no `@covers`
test is required or appropriate for them — authoring one is the anti-pattern this rule
names.

> See [`docs/governance/req-scope-discipline.md`](../../docs/governance/req-scope-discipline.md)
> for canonical expansion: problem framing, proof-channel detail, GHI #270 reconciliation,
> quantification, and consequences.

> See [`docs/governance/tests-rationale.md`](../../docs/governance/tests-rationale.md) for TDD anti-patterns, eval-awareness corollary details, behave enforcement direction, runner anti-patterns, TASK workflow details, and code patterns.
