gz check¶
Run the per-change quality gate (lint, typecheck, unit tests, validators) and advisory drift detection in a single pass. --full adds Behave and preflight, the heavy-lane and CI sweep.
Usage¶
Options¶
| Flag | Description |
|---|---|
--json |
Output results as JSON to stdout |
--full |
Full sweep: the default steps plus Behave and Preflight. Heavy-lane and CI scope; CI runs it on every commit |
--fast |
Inner-loop scope: run every lint/type/governance step, plus only the tests the working tree touches. Skips Test, Behave, and Docs build. Never satisfies the pre-push gate |
--reuse-verified |
Skip the run when this exact staged tree already passed a check covering the requested scope. Used by the pre-push gate |
--full and --fast are mutually exclusive.
Default scope: the per-change gate¶
Plain gz check runs the change scope declared in data/check_step_scopes.json:
every step except Behave and Preflight. AGENTS.md § Gate Covenant binds the
unit tier to every change and behave to heavy-lane OBPI work and CI, never to a
per-change gate (GHI #1088). Preflight is a janitorial scan whose verdict tracks
wall-clock age rather than the content being pushed. Both still run under
--full, and CI runs --full on every commit.
--full¶
Runs every registered step. Use it for heavy-lane closeout and whenever CI's verdict is wanted locally. Heavy-lane Gate 4 evidence comes from its own canonical behave invocation, not from this sweep.
--fast¶
Runs the full step list minus the three expensive steps, and substitutes a
Test (changed) step that runs only the test modules the working tree touches.
Measured 2026-08-22 on a 10-core host, against a 148 s full run: Test 44 s,
Behave 33 s, Docs build 4 s. Every other step stays, because the whole
remainder is cheaper than any one of the three and it is where the governance
value lives.
A --fast pass never records a verified fingerprint, so --reuse-verified
cannot be satisfied by one and the pre-push gate still runs its own scope. The test
selection is a name-match heuristic, not a dependency graph — it will miss a test
that exercises a module it is not named after. That is a convenience for the
inner loop, never a claim of coverage, and the output says so.
--reuse-verified¶
Skips the run when this exact staged tree already passed a check covering the
requested scope (GHI #835). A default-scope or --full pass satisfies the default
scope; only a --full pass satisfies --full --reuse-verified, because a
default-scope pass never ran Behave. Without it a fix pays the gate twice: once when it is verified,
then again when git push fires the pre-push gate over a tree that has not
changed. The second run cannot reach a different verdict.
Use it as: git add -A && uv run gz check && git commit && git push. The
staging step is not incidental — it is what makes the skip possible.
The fingerprint is the index tree, and both alternatives were tried and rejected against real measurement:
HEADfails because a commit is created between verify and push, so it always differs while the files do not.- The working tree fails for the mirror reason:
pre-commitstashes unstaged changes while hooks run, so a pre-push hook observes HEAD-plus-staged and never the working tree. Measured 2026-08-22 against this repository, where.gzkit/ledger.jsonlis dirty on essentially every run because governance commands append to it — the first implementation fingerprinted the working tree, passed all its own tests on clean fixtures, and skipped exactly zero real pushes.
A pass is recorded only when nothing is unstaged or untracked. The gate runs against the working tree while the fingerprint names the index tree, and those are the same object only when the tree is fully staged; recording otherwise would attest a tree that was never the one tested. When it declines to record, it says so on stdout.
Fail-open by construction: any git failure yields no fingerprint, no fingerprint ever matches, and the gate runs. A fingerprint mechanism that failed closed would refuse pushes on a repository it merely could not read.
Description¶
Runs the quality assurance suite: linting with Ruff, format check, static type checking with ty, unit tests with unittest, a strict mkdocs build --strict docs build (skipped when the project ships no mkdocs.yml), skill audit, parity check, readiness audit, CLI documentation audit and surface-fidelity validation. --full adds Behave scenarios and the preflight scan for stale pipeline markers and orphan plan-audit receipts. After all blocking checks complete, runs advisory drift detection using the same engine as gz drift.
The Surface fidelity step runs gz validate --surface-fidelity to verify all four surface-fidelity invariants (ADR-0.0.33-05).
The Lock-exchange coupling step runs gz validate --lock-exchange-coupling to
enforce the token-block discipline: every obpi_lock_released event in the
ledger (post-OBPI-02 cutover) must carry a valid handoff_path and satisfy
Sub-Invariant 2's minimum-information rule (ADR-0.0.41 / OBPI-0.0.41-04).
The CLI audit and Preflight steps catch workflow-integrity drift that would otherwise go undetected — a new subcommand missing from the operator runbook, or stale artifacts left behind from a previous pipeline session — and apply self-healing pressure on every canonical quality run (Preflight under --full).
Drift findings are advisory — they appear as warnings but do not affect the exit code. This surfaces spec-test-code drift early without blocking the development workflow.
Advisory Drift Output¶
When drift exists, gz check appends an advisory section after the blocking check results:
✓ Lint
✓ Format
✓ Typecheck
✓ Test
✓ All per-change checks passed. (Behave, Preflight are heavy-lane / CI scope: `gz check --full` runs them, and CI runs the full sweep)
⚠ Advisory: spec-test-code drift detected
Unlinked specs (REQs with no test):
advisory REQ-0.1.0-01-01
Total: 1 finding(s) (advisory — does not affect exit code)
JSON Output¶
gz check --json includes a drift object with advisory: true:
{
"success": true,
"scope": "change",
"checks": {
"Lint": true,
"Format": true,
"Typecheck": true,
"Test": true
},
"drift": {
"advisory": true,
"has_drift": true,
"unlinked_specs": ["REQ-0.1.0-01-01"],
"orphan_tests": [],
"unjustified_code_changes": [],
"total_drift_count": 1,
"scan_timestamp": "2026-03-27T00:00:00+00:00"
}
}
Exit Codes¶
| Code | Meaning |
|---|---|
| 0 | All blocking checks passed (drift is advisory, does not affect exit code) |
| 1 | One or more blocking checks failed |