⚠ Deprecated: `gz gates` will be removed in a future release. Use `gz closeout` instead.
  ✓ Gate 1 (ADR): PASS
(docs/design/adr/foundation/ADR-0.0.17-adr-taxonomy-mechanical/ADR-0.0.17-adr-ta
xonomy-mechanical.md)
Gate 2 (TDD): uv run gz test
Running unit tests...
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
......................s.........................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
................................................................................
....
----------------------------------------------------------------------
Ran 3284 tests in 20.689s

OK (skipped=1)

Unit tests passed.

  ✓ Gate 2 (TDD): PASS
  ↳ Eval delta: skipped (no baselines) — 5 surfaces scored, overall 2.7/4.0
  → Gate 3 (Docs): uv run mkdocs build --strict
INFO    -  Cleaning site directory
INFO    -  Building documentation to directory:
/Users/jeff/Documents/Code/gzkit/site
INFO    -  The following pages exist in the docs directory, but are not included
in the "nav" configuration:
  - AGENTS.md
  - drafts/claude-code-inventory.md
  - drafts/claude-code-vs-codex-control-surface-parity.md
  - drafts/sprint-and-drift-harness-governance.md
  - examples/presentations/index.md
  - examples/presentations/complete-series-script.md
  - examples/presentations/part1-script.md
  - examples/presentations/part2-script.md
  - examples/presentations/part3-script.md
  - examples/presentations/part4-script.md
  - examples/presentations/part5-script.md
  - examples/presentations/part6-script.md
  - examples/templates/req-template.md
  - governance/advisory-rules-audit.md
  - governance/ai-governance-brief.md
  - governance/ai-governance-research-gaps.md
  - governance/ai-governance-research-literature.md
  - governance/ai-governance-research-practice.md
  - governance/ai-governance-research-regulation.md
  - governance/ai-governance-verification.md
  - governance/ai-governance.md
  - governance/ai-governance.provenance.md
  - governance/layer-three-derived-views.md
  - governance/model-regression-taxonomy.md
  - governance/trust-doctrine.md
  - governance/GovZero/obpi-pipeline-runbook.md
  - governance/research_sources/0900001680afb122.md
  -
governance/research_sources/1-introduction-the-imperative-of-public-values-in-ai
.md
  - governance/research_sources/1680afaeba.md
  -
governance/research_sources/a-closer-look-at-the-existing-risks-of-generative-ai
-mapping-the-who-what-and-how-of-real-world-inci.md
  - governance/research_sources/advancing-accountability-in-ai-en.md
  -
governance/research_sources/ai-risk-management-framework-japanese-translation.md
  -
governance/research_sources/anthropics-responsible-scaling-policy-version-30.md
  -
governance/research_sources/artificial-intelligence-risk-management-framework-ai
-rmf-10.md
  -
governance/research_sources/artificial-intelligence-risk-management-framework-ge
nerative-artificial-intelligence-profile.md
  - governance/research_sources/arxiv-240614713.md
  - governance/research_sources/arxiv-240714981.md
  -
governance/research_sources/auditing-work-exploring-the-new-york-city-algorithmi
c-bias-audit-regime.md
  - governance/research_sources/claude-4-system-card.md
  - governance/research_sources/hai-ai-index-report-2025-chapter3-final.md
  - governance/research_sources/hls20white20paper20final-v3.md
  -
governance/research_sources/introducing-the-oecd-ai-capability-indicators-en.md
  -
governance/research_sources/m-24-10-advancing-governance-innovation-and-risk-man
agement-for-agency-use-of-artificial-intelligenc.md
  -
governance/research_sources/m-25-21-accelerating-federal-use-of-ai-through-innov
ation-governance-and-public-trust.md
  -
governance/research_sources/m-25-22-driving-efficient-acquisition-of-artificial-
intelligence-in-government.md
  -
governance/research_sources/microsoft-word-ai-governance-tiimalasipaperi-arxivdo
cx.md
  - governance/research_sources/nistai600-1genai-profileipd.md
  - governance/research_sources/operator-system-card.md
  - governance/research_sources/preparedness-framework-v2.md
  - governance/research_sources/quantifying-detection-rates.md
  -
governance/research_sources/responsible-ai-governance-a-systematic-literature-re
view.md
  -
governance/research_sources/responsible-ai-governance-in-the-public-sector-expla
ining-contextual-dynamics-through-a-realist-synt.md
  - governance/research_sources/responsible-use-guide.md
  - governance/research_sources/steering-ais-future-en.md
  -
governance/research_sources/the-bureaucratic-challenge-to-ai-governance-an-empir
ical-assessment-of-implementation-at-us-federal.md
  -
governance/research_sources/the-governance-of-ai-companies-reconciling-purpose-w
ith-profits.md
  - harness-docs/GZK-GOV-007-ultrareview-and-gate-5.md
  - harness-docs/expanding-the-bitter-lesson-for-agentic-software-development.md
  - harness-docs/xhigh-measurement-protocol.md
  - releases/PATCH-v0.24.3.md
  - releases/PATCH-v0.25.10.md
  - releases/PATCH-v0.25.11.md
  - releases/PATCH-v0.25.12.md
  - releases/PATCH-v0.25.13.md
  - releases/PATCH-v0.25.7.md
  - releases/PATCH-v0.25.8.md
  - releases/PATCH-v0.25.9.md
  - superpowers/plans/2026-03-26-pipeline-reliability-improvements.md
  - superpowers/specs/2026-03-26-pipeline-reliability-improvements-design.md
  - user/commands/adr-evaluate.md
  - user/commands/adr-report.md
  - user/commands/arb-advise.md
  - user/commands/arb-coverage.md
  - user/commands/arb-patterns.md
  - user/commands/arb-ruff.md
  - user/commands/arb-step.md
  - user/commands/arb-ty.md
  - user/commands/arb-typecheck.md
  - user/commands/arb-validate.md
  - user/commands/arb.md
  - user/commands/check.md
  - user/commands/chores-advise.md
  - user/commands/chores-show.md
  - user/commands/drift.md
  - user/commands/format.md
  - user/commands/frontmatter-reconcile.md
  - user/commands/interview.md
  - user/commands/lint.md
  - user/commands/obpi-audit.md
  - user/commands/obpi-complete.md
  - user/commands/obpi-lock-check.md
  - user/commands/obpi-lock-claim.md
  - user/commands/obpi-lock-list.md
  - user/commands/obpi-lock-release.md
  - user/commands/obpi-lock-status.md
  - user/commands/obpi-withdraw.md
  - user/commands/personas-drift.md
  - user/commands/plan.md
  - user/commands/preflight.md
  - user/commands/readiness-evaluate.md
  - user/commands/roles.md
  - user/commands/skill-list.md
  - user/commands/skill-new.md
  - user/commands/test.md
  - user/commands/tidy.md
  - user/commands/typecheck.md
  - user/commands/validate.md
  - user/concepts/reporter-architecture.md
  - user/concepts/subagent-pipeline.md
  - user/concepts/task-overview.md
  - user/manpages/arb.md
  - user/manpages/closeout.md
  - user/manpages/gz-personas.md
  - user/manpages/patch-release.md
  - user/skills/_TEMPLATE.md
  - user/skills/airlineops-parity-scan.md
  - user/skills/gz-adr-autolink.md
  - user/skills/gz-adr-promote.md
  - user/skills/gz-adr-recon.md
  - user/skills/gz-adr-sync.md
  - user/skills/gz-agent-sync.md
  - user/skills/gz-check-config-paths.md
  - user/skills/gz-migrate-semver.md
  - user/skills/gz-skill-router.md
  - user/skills/gz-tidy.md
INFO    -  Documentation built in 2.04 seconds

  ✓ Docs build: PASS
  ✓ Skill Audit: PASS (skills=56 blocking=0 warnings=0)
  ✓ Gate 3 (Docs): PASS
  → Gate 4 (BDD): uv run -m behave features/
USING RUNNER: behave.runner:Runner
Feature: gz adr promote --kind taxonomy enforcement (ADR-0.0.17 /
OBPI-0.0.17-03) # features/adr_promote.feature:1
  As an operator promoting a pool ADR,
  I want --kind to declare and enforce taxonomic intent at promotion time,
  so that pool->canonical promotion is explicit, atomic, and audit-recorded.
  Feature: gz adr promote --kind taxonomy enforcement (ADR-0.0.17 /
OBPI-0.0.17-03)  # features/adr_promote.feature:1

  @REQ-0.0.17-03-01
  Scenario: --kind appears in --help with three choices            #
features/adr_promote.feature:11
    Given the workspace is initialized                             #
features/steps/gz_steps.py:67
    And a pool ADR "ADR-pool.sample-work" with target scope exists #
features/steps/gz_steps.py:271
    When I run the gz command "adr promote --help"                 #
features/steps/gz_steps.py:209
    Then the command exits with code 0                             #
features/steps/gz_steps.py:220
    And the output contains "--kind"                               #
features/steps/gz_steps.py:225
    And the output contains "foundation"                           #
features/steps/gz_steps.py:225
    And the output contains "feature"                              #
features/steps/gz_steps.py:225
    And the output contains "pool"                                 #
features/steps/gz_steps.py:225

  @REQ-0.0.17-03-01
  Scenario: --kind pool is rejected with exit 1
# features/adr_promote.feature:20
    Given the workspace is initialized
# features/steps/gz_steps.py:67
    And a pool ADR "ADR-pool.sample-work" with target scope exists
# features/steps/gz_steps.py:271
    When I run the gz command "adr promote ADR-pool.sample-work --semver 0.6.0
--kind pool" # features/steps/gz_steps.py:209
    Then the command exits with code 1
# features/steps/gz_steps.py:220
    And the output contains "source"
# features/steps/gz_steps.py:225

  @REQ-0.0.17-03-02
  Scenario: --kind foundation rejects non-0.0.x semver
# features/adr_promote.feature:26
    Given the workspace is initialized
# features/steps/gz_steps.py:67
    And a pool ADR "ADR-pool.sample-work" with target scope exists
# features/steps/gz_steps.py:271
    When I run the gz command "adr promote ADR-pool.sample-work --semver 0.6.0
--kind foundation"      # features/steps/gz_steps.py:209
    Then the command exits with code 1
# features/steps/gz_steps.py:220
    And the output contains "0.0."
# features/steps/gz_steps.py:225
    And the file
"design/adr/foundation/ADR-0.6.0-sample-work/ADR-0.6.0-sample-work.md" does not
exist # features/steps/gz_steps.py:250

  @REQ-0.0.17-03-03
  Scenario: --kind feature rejects 0.0.x semver
# features/adr_promote.feature:33
    Given the workspace is initialized
# features/steps/gz_steps.py:67
    And a pool ADR "ADR-pool.sample-work" with target scope exists
# features/steps/gz_steps.py:271
    When I run the gz command "adr promote ADR-pool.sample-work --semver 0.0.18
--kind feature"           # features/steps/gz_steps.py:209
    Then the command exits with code 1
# features/steps/gz_steps.py:220
    And the file
"design/adr/pre-release/ADR-0.0.18-sample-work/ADR-0.0.18-sample-work.md" does
not exist # features/steps/gz_steps.py:250

  @REQ-0.0.17-03-04
  Scenario: validation failure leaves no governance artifact behind
# features/adr_promote.feature:39
    Given the workspace is initialized
# features/steps/gz_steps.py:67
    And a pool ADR "ADR-pool.sample-work" with target scope exists
# features/steps/gz_steps.py:271
    When I run the gz command "adr promote ADR-pool.sample-work --semver 0.6.0
--kind foundation"       # features/steps/gz_steps.py:209
    Then the command exits with code 1
# features/steps/gz_steps.py:220
    And the file
"design/adr/foundation/ADR-0.6.0-sample-work/ADR-0.6.0-sample-work.md" does not
exist  # features/steps/gz_steps.py:250
    And the file
"design/adr/pre-release/ADR-0.6.0-sample-work/ADR-0.6.0-sample-work.md" does not
exist # features/steps/gz_steps.py:250

  @REQ-0.0.17-03-05
  Scenario: promoted ADR carries kind: in frontmatter
# features/adr_promote.feature:46
    Given the workspace is initialized
# features/steps/gz_steps.py:67
    And a pool ADR "ADR-pool.sample-work" with target scope exists
# features/steps/gz_steps.py:271
    When I run the gz command "adr promote ADR-pool.sample-work --semver 0.0.18
--kind foundation --force"                      # features/steps/gz_steps.py:209
    Then the command exits with code 0
# features/steps/gz_steps.py:220
    And the file
"design/adr/foundation/ADR-0.0.18-sample-work/ADR-0.0.18-sample-work.md"
contains "kind: foundation"           # features/steps/gz_steps.py:235
    And the file
"design/adr/foundation/ADR-0.0.18-sample-work/ADR-0.0.18-sample-work.md"
contains "id: ADR-0.0.18-sample-work" # features/steps/gz_steps.py:235

  @REQ-0.0.17-03-06
  Scenario: --kind feature lands the promoted ADR in pre-release/
# features/adr_promote.feature:53
    Given the workspace is initialized
# features/steps/gz_steps.py:67
    And a pool ADR "ADR-pool.sample-work" with target scope exists
# features/steps/gz_steps.py:271
    When I run the gz command "adr promote ADR-pool.sample-work --semver 0.6.0
--kind feature --force" # features/steps/gz_steps.py:209
    Then the command exits with code 0
# features/steps/gz_steps.py:220
    And the file
"design/adr/pre-release/ADR-0.6.0-sample-work/ADR-0.6.0-sample-work.md" exists
# features/steps/gz_steps.py:230

  @REQ-0.0.17-03-07
  Scenario: ledger artifact_renamed event records kind and semver
# features/adr_promote.feature:59
    Given the workspace is initialized
# features/steps/gz_steps.py:67
    And a pool ADR "ADR-pool.sample-work" with target scope exists
# features/steps/gz_steps.py:271
    When I run the gz command "adr promote ADR-pool.sample-work --semver 0.6.0
--kind feature --force" # features/steps/gz_steps.py:209
    Then the command exits with code 0
# features/steps/gz_steps.py:220
    And ledger event "artifact_renamed" has field "kind" equal to "feature"
# features/steps/gz_steps.py:255
    And ledger event "artifact_renamed" has field "semver" equal to "0.6.0"
# features/steps/gz_steps.py:255
    And ledger event "artifact_renamed" has field "reason" equal to
"pool_promotion"                   # features/steps/gz_steps.py:255
    And ledger event "artifact_renamed" has field "new_id" equal to
"ADR-0.6.0-sample-work"            # features/steps/gz_steps.py:255

Feature: ARB self-reporting middleware # features/arb.feature:2
  As an agent producing verification evidence for Heavy-lane attestations
  I want a CLI surface that wraps QA commands and emits validated receipts
  So that attestation-enrichment claims can cite deterministic artifacts.
  @REQ-0.25.0-33-05
  Scenario: arb surface exposes every rule-declared verb  #
features/arb.feature:8
    When I run the gz command "arb --help"                #
features/steps/gz_steps.py:209
    Then the command exits with code 0                    #
features/steps/gz_steps.py:220
    And the output contains "ruff"                        #
features/steps/gz_steps.py:225
    And the output contains "step"                        #
features/steps/gz_steps.py:225
    And the output contains "ty"                          #
features/steps/gz_steps.py:225
    And the output contains "coverage"                    #
features/steps/gz_steps.py:225
    And the output contains "validate"                    #
features/steps/gz_steps.py:225
    And the output contains "advise"                      #
features/steps/gz_steps.py:225
    And the output contains "patterns"                    #
features/steps/gz_steps.py:225

  Scenario: arb validate returns zero-state cleanly on empty receipts dir  #
features/arb.feature:19
    When I run the gz command "arb validate --limit 5"                     #
features/steps/gz_steps.py:209
    Then the command exits with code 0                                     #
features/steps/gz_steps.py:220
    And the output contains "ARB Receipt Validation"                       #
features/steps/gz_steps.py:225
    And the output contains "Receipts scanned"                             #
features/steps/gz_steps.py:225

  Scenario: arb advise returns zero-state cleanly on empty receipts dir  #
features/arb.feature:25
    When I run the gz command "arb advise --limit 5"                     #
features/steps/gz_steps.py:209
    Then the command exits with code 0                                   #
features/steps/gz_steps.py:220
    And the output contains "ARB Advice"                                 #
features/steps/gz_steps.py:225
    And the output contains "Recommendations"                            #
features/steps/gz_steps.py:225
    And the output contains "No findings in recent receipts"             #
features/steps/gz_steps.py:225

  Scenario: arb patterns returns zero-state cleanly on empty receipts dir  #
features/arb.feature:32
    When I run the gz command "arb patterns --limit 5"                     #
features/steps/gz_steps.py:209
    Then the command exits with code 0                                     #
features/steps/gz_steps.py:220
    And the output contains "ARB Pattern Extraction Report"                #
features/steps/gz_steps.py:225

  Scenario: arb patterns compact mode emits single-line summary  #
features/arb.feature:37
    When I run the gz command "arb patterns --compact --limit 5" #
features/steps/gz_steps.py:209
    Then the command exits with code 0                           #
features/steps/gz_steps.py:220
    And the output contains "arb patterns:"                      #
features/steps/gz_steps.py:225

  Scenario: arb validate JSON output is machine-readable      #
features/arb.feature:42
    When I run the gz command "arb validate --json --limit 5" #
features/steps/gz_steps.py:209
    Then the command exits with code 0                        #
features/steps/gz_steps.py:220
    And the output contains "scanned"                         #
features/steps/gz_steps.py:225
    And the output contains "valid"                           #
features/steps/gz_steps.py:225
    And the output contains "unknown_schema"                  #
features/steps/gz_steps.py:225

Feature: Advisory drift detection in gz check #
features/check_drift_advisory.feature:1
  The gz check command includes drift detection as an advisory (non-blocking)
  check that runs after all blocking quality checks complete.
  Scenario: Check help shows json flag       #
features/check_drift_advisory.feature:5
    When I run the gz command "check --help" # features/steps/gz_steps.py:209
    Then the command exits with code 0       # features/steps/gz_steps.py:220
    And the output contains "--json"         # features/steps/gz_steps.py:225
    And the output contains "advisory drift" # features/steps/gz_steps.py:225

Feature: Closeout ceremony enforcement # features/closeout_ceremony.feature:1
  The closeout ceremony presents a Defense Brief with closing arguments,
  product proof, and reviewer assessment. It blocks when evidence is missing.
  Scenario: Closeout dry-run shows Defense Brief section     #
features/closeout_ceremony.feature:5
    Given the workspace is initialized in heavy mode         #
features/steps/gz_steps.py:60
    And a heavy ADR exists with an OBPI brief                #
features/steps/closeout_product_proof_steps.py:51
    And the OBPI source file has public docstrings           #
features/steps/closeout_product_proof_steps.py:139
    And the OBPI brief has a closing argument                #
features/steps/closeout_ceremony_steps.py:12
    When I run the gz command "closeout ADR-0.1.0 --dry-run" #
features/steps/gz_steps.py:209
    Then the command exits with code 0                       #
features/steps/gz_steps.py:220
    And the output contains "Defense Brief"                  #
features/steps/gz_steps.py:225
    And the output contains "Closing Arguments"              #
features/steps/gz_steps.py:225

  Scenario: Closeout form includes Defense Brief when rendered  #
features/closeout_ceremony.feature:15
    Given the workspace is initialized in heavy mode            #
features/steps/gz_steps.py:60
    And a heavy ADR exists with an OBPI brief                   #
features/steps/closeout_product_proof_steps.py:51
    And the OBPI source file has public docstrings              #
features/steps/closeout_product_proof_steps.py:139
    And the OBPI brief has a closing argument                   #
features/steps/closeout_ceremony_steps.py:12
    When I run the gz command "closeout ADR-0.1.0 --dry-run"    #
features/steps/gz_steps.py:209
    Then the command exits with code 0                          #
features/steps/gz_steps.py:220
    And the output contains "Product Proof"                     #
features/steps/gz_steps.py:225

  Scenario: Defense Brief shows reviewer assessment when present  #
features/closeout_ceremony.feature:24
    Given the workspace is initialized in heavy mode              #
features/steps/gz_steps.py:60
    And a heavy ADR exists with an OBPI brief                     #
features/steps/closeout_product_proof_steps.py:51
    And the OBPI source file has public docstrings                #
features/steps/closeout_product_proof_steps.py:139
    And the OBPI brief has a closing argument                     #
features/steps/closeout_ceremony_steps.py:12
    And a reviewer assessment exists for the OBPI                 #
features/steps/closeout_ceremony_steps.py:35
    When I run the gz command "closeout ADR-0.1.0 --dry-run"      #
features/steps/gz_steps.py:209
    Then the command exits with code 0                            #
features/steps/gz_steps.py:220
    And the output contains "Reviewer Assessment"                 #
features/steps/gz_steps.py:225
    And the output contains "PASS"                                #
features/steps/gz_steps.py:225

Feature: Closeout product proof gate # features/closeout_product_proof.feature:1
  The closeout command validates that each OBPI has operator-facing
  documentation proof before allowing ADR closeout to proceed.
  Scenario: Closeout blocked when OBPI has no product proof  #
features/closeout_product_proof.feature:5
    Given the workspace is initialized in heavy mode         #
features/steps/gz_steps.py:60
    And a heavy ADR exists with an OBPI brief                #
features/steps/closeout_product_proof_steps.py:51
    When I run the gz command "closeout ADR-0.1.0 --dry-run" #
features/steps/gz_steps.py:209
    Then the command exits non-zero                          #
features/steps/gz_steps.py:215
    And the output contains "MISSING"                        #
features/steps/gz_steps.py:225
    And the output contains "missing product proof"          #
features/steps/gz_steps.py:225

  Scenario: Closeout allowed when OBPI has docstring proof   #
features/closeout_product_proof.feature:13
    Given the workspace is initialized in heavy mode         #
features/steps/gz_steps.py:60
    And a heavy ADR exists with an OBPI brief                #
features/steps/closeout_product_proof_steps.py:51
    And the OBPI source file has public docstrings           #
features/steps/closeout_product_proof_steps.py:139
    When I run the gz command "closeout ADR-0.1.0 --dry-run" #
features/steps/gz_steps.py:209
    Then the command exits with code 0                       #
features/steps/gz_steps.py:220
    And the output contains "docstring"                      #
features/steps/gz_steps.py:225

  Scenario: Closeout product proof shown in JSON mode               #
features/closeout_product_proof.feature:21
    Given the workspace is initialized in heavy mode                #
features/steps/gz_steps.py:60
    And a heavy ADR exists with an OBPI brief                       #
features/steps/closeout_product_proof_steps.py:51
    And the OBPI source file has public docstrings                  #
features/steps/closeout_product_proof_steps.py:139
    When I run the gz command "closeout ADR-0.1.0 --dry-run --json" #
features/steps/gz_steps.py:209
    Then the command exits with code 0                              #
features/steps/gz_steps.py:220
    And the output contains "product_proof"                         #
features/steps/gz_steps.py:225

Feature: gz frontmatter reconcile ledger-wins reconciliation (ADR-0.0.16 /
OBPI-0.0.16-03) # features/frontmatter_reconcile.feature:1
  As an operator remediating frontmatter drift,
  I want gz frontmatter reconcile to rewrite drifted fields to match the ledger
  so that frontmatter stays consistent with its source of truth without
hand-editing.
  Feature: gz frontmatter reconcile ledger-wins reconciliation (ADR-0.0.16 /
OBPI-0.0.16-03)  # features/frontmatter_reconcile.feature:1

  @REQ-0.0.16-03-02
  Scenario: Reconcile rewrites drifted lane and emits a receipt  #
features/frontmatter_reconcile.feature:11
    Given the workspace is initialized                           #
features/steps/gz_steps.py:67
    And ADR-0.1.0 exists                                         #
features/steps/gz_steps.py:90
    Given ADR-0.1.0 has drifted lane frontmatter "heavy"         #
features/steps/frontmatter_reconcile_steps.py:52
    When I run the gz command "frontmatter reconcile"            #
features/steps/gz_steps.py:209
    Then the command exits with code 0                           #
features/steps/gz_steps.py:220
    And ADR-0.1.0 frontmatter "lane" equals "lite"               #
features/steps/frontmatter_reconcile_steps.py:58
    And a frontmatter-coherence receipt exists                   #
features/steps/frontmatter_reconcile_steps.py:67

  @REQ-0.0.16-03-03
  Scenario: Dry-run leaves ADR files untouched but emits the receipt  #
features/frontmatter_reconcile.feature:19
    Given the workspace is initialized                                #
features/steps/gz_steps.py:67
    And ADR-0.1.0 exists                                              #
features/steps/gz_steps.py:90
    Given ADR-0.1.0 has drifted lane frontmatter "heavy"              #
features/steps/frontmatter_reconcile_steps.py:52
    When I run the gz command "frontmatter reconcile --dry-run"       #
features/steps/gz_steps.py:209
    Then the command exits with code 0                                #
features/steps/gz_steps.py:220
    And ADR-0.1.0 frontmatter "lane" equals "heavy"                   #
features/steps/frontmatter_reconcile_steps.py:58
    And a frontmatter-coherence receipt exists                        #
features/steps/frontmatter_reconcile_steps.py:67

  @REQ-0.0.16-03-07
  Scenario: Unmapped status term exits with policy-breach code  #
features/frontmatter_reconcile.feature:27
    Given the workspace is initialized                          #
features/steps/gz_steps.py:67
    And ADR-0.1.0 exists                                        #
features/steps/gz_steps.py:90
    Given ADR-0.1.0 has drifted status frontmatter "Nonsense"   #
features/steps/gates_frontmatter_steps.py:17
    When I run the gz command "frontmatter reconcile"           #
features/steps/gz_steps.py:209
    Then the command exits with code 3                          #
features/steps/gz_steps.py:220
    And the output contains "Nonsense"                          #
features/steps/gz_steps.py:225

Feature: gz gates frontmatter integration (ADR-0.0.16 / OBPI-0.0.16-02) #
features/gates.feature:1
  As an operator running governance gates,
  I want Gate 1 to mechanically block on frontmatter-ledger drift,
  so that stale frontmatter never masquerades as truth during attestation.
  Feature: gz gates frontmatter integration (ADR-0.0.16 / OBPI-0.0.16-02)  #
features/gates.feature:1

  @REQ-0.0.16-02-02 @REQ-0.0.16-02-03
  Scenario: Gate 1 blocks on status frontmatter drift with exit 3        #
features/gates.feature:12
    Given the workspace is initialized                                   #
features/steps/gz_steps.py:67
    And ADR-0.1.0 exists                                                 #
features/steps/gz_steps.py:90
    Given ADR-0.1.0 has drifted status frontmatter "Completed"           #
features/steps/gates_frontmatter_steps.py:17
    When I run the gz command "gates --gate 1 --adr ADR-0.1.0"           #
features/steps/gz_steps.py:209
    Then the command exits with code 3                                   #
features/steps/gz_steps.py:220
    And the output contains "status"                                     #
features/steps/gz_steps.py:225
    And the output contains "gz chores run frontmatter-ledger-coherence" #
features/steps/gz_steps.py:225

  @REQ-0.0.16-02-04
  Scenario: gz gates rejects the --skip-frontmatter bypass flag          #
features/gates.feature:20
    Given the workspace is initialized                                   #
features/steps/gz_steps.py:67
    And ADR-0.1.0 exists                                                 #
features/steps/gz_steps.py:90
    When I run the gz command "gates --skip-frontmatter --adr ADR-0.1.0" #
features/steps/gz_steps.py:209
    Then the command exits non-zero                                      #
features/steps/gz_steps.py:215
    And the output contains "unrecognized arguments"                     #
features/steps/gz_steps.py:225

Feature: Heavy lane Gate 4 governance # features/heavy_lane_gate4.feature:1
  Heavy-lane ADR workflows must enforce Gate 4 BDD checks.
  Scenario: Attestation is blocked until Gate 4 passes              #
features/heavy_lane_gate4.feature:4
    Given the workspace is initialized in heavy mode                #
features/steps/gz_steps.py:60
    And a heavy ADR exists                                          #
features/steps/gz_steps.py:84
    And gate 2 and gate 3 are marked pass for ADR-0.1.0             #
features/steps/gz_steps.py:96
    When I run the gz command "attest ADR-0.1.0 --status completed" #
features/steps/gz_steps.py:209
    Then the command exits non-zero                                 #
features/steps/gz_steps.py:215
    And the output contains "Gate 4 must pass"                      #
features/steps/gz_steps.py:225

  Scenario: Closeout guidance includes Gate 4 BDD command              #
features/heavy_lane_gate4.feature:12
    Given the workspace is initialized in heavy mode                   #
features/steps/gz_steps.py:60
    And a heavy ADR exists                                             #
features/steps/gz_steps.py:84
    When I run the gz command "closeout ADR-0.1.0 --dry-run"           #
features/steps/gz_steps.py:209
    Then the command exits with code 0                                 #
features/steps/gz_steps.py:220
    And the output contains "Gate 4 (BDD): uv run -m behave features/" #
features/steps/gz_steps.py:225

  Scenario: Heavy ADR status reports Gate 4 as pending when not checked  #
features/heavy_lane_gate4.feature:19
    Given the workspace is initialized in heavy mode                     #
features/steps/gz_steps.py:60
    And a heavy ADR exists                                               #
features/steps/gz_steps.py:84
    When I run the gz command "adr status ADR-0.1.0 --json"              #
features/steps/gz_steps.py:209
    Then the command exits with code 0                                   #
features/steps/gz_steps.py:220
    And JSON path "gates.4" equals "pending"                             #
features/steps/gz_steps.py:241

  Scenario: Pipeline guidance requires guarded git sync before completion
accounting  # features/heavy_lane_gate4.feature:26
    Given the workspace is initialized with agent surfaces in heavy mode
# features/steps/gz_steps.py:74
    Then the file "AGENTS.md" contains "guarded git sync -> completion"
# features/steps/gz_steps.py:235
    And the file "AGENTS.md" contains "uv run gz git-sync --apply --lint --test"
# features/steps/gz_steps.py:235

  Scenario: Canonical lane doctrine narrows Heavy to runtime-contract changes
# features/heavy_lane_gate4.feature:31
    Given the workspace is initialized with agent surfaces in heavy mode
# features/steps/gz_steps.py:74
    Then the file "AGENTS.md" contains "Documentation/process/template-only
changes stay" # features/steps/gz_steps.py:235
    And the file "AGENTS.md" contains "command/API/schema/runtime-contract
changes"       # features/steps/gz_steps.py:235
    And the file "AGENTS.md" contains "uv run gz check"
# features/steps/gz_steps.py:235

Feature: OBPI anchor drift reconciliation # features/obpi_anchor_drift.feature:1
  Completed OBPIs should preserve lifecycle state while reporting superseded
anchors
  when later siblings or ADR closeout commit on top of the anchor.
  Scenario: Reconcile preserves completion state while reporting superseded
anchor  # features/obpi_anchor_drift.feature:5
    Given the workspace is initialized
# features/steps/gz_steps.py:67
    And ADR-0.1.0 exists
# features/steps/gz_steps.py:90
    And a completed OBPI with anchor-tracked receipt exists for
OBPI-0.1.0-01-demo  # features/steps/gz_steps.py:103
    And the tracked module changes after the completion anchor
# features/steps/gz_steps.py:195
    When I run the gz command "obpi reconcile OBPI-0.1.0-01-demo --json"
# features/steps/gz_steps.py:209
    Then the command exits with code 0
# features/steps/gz_steps.py:220
    And JSON path "runtime_state" equals "completed"
# features/steps/gz_steps.py:241
    And JSON path "anchor_state" equals "superseded"
# features/steps/gz_steps.py:241

Feature: OBPI atomic completion # features/obpi_complete.feature:1
  gz obpi complete atomically validates, writes evidence, flips status,
  records attestation, and emits a completion receipt in a single
  all-or-nothing transaction.
  Scenario: Missing OBPI exits 1
# features/obpi_complete.feature:6
    Given the workspace is initialized
# features/steps/gz_steps.py:67
    When I run "gz obpi complete NONEXISTENT-99 --attestor jeff
--attestation-text Verified" # features/steps/obpi_lock_steps.py:61
    Then it exits with code 1
# features/steps/obpi_lock_steps.py:71

  Scenario: Help text shows required flags       #
features/obpi_complete.feature:11
    Given the workspace is initialized           # features/steps/gz_steps.py:67
    When I run "gz obpi complete -h"             #
features/steps/obpi_lock_steps.py:61
    Then it exits with code 0                    #
features/steps/obpi_lock_steps.py:71
    And the output contains "--attestor"         #
features/steps/gz_steps.py:225
    And the output contains "--attestation-text" #
features/steps/gz_steps.py:225

Feature: OBPI lock management # features/obpi_lock.feature:1
  Multi-agent work locks for OBPI coordination via gz obpi lock commands.
  Scenario: Claim creates a lock file                    #
features/obpi_lock.feature:4
    Given the workspace is initialized                   #
features/steps/gz_steps.py:67
    When I run "gz obpi lock claim OBPI-0.1.0-01 --json" #
features/steps/obpi_lock_steps.py:61
    Then it exits with code 0                            #
features/steps/obpi_lock_steps.py:71
    And the JSON output field "status" is "claimed"      #
features/steps/obpi_lock_steps.py:79

  Scenario: Claim fails when held by another agent                    #
features/obpi_lock.feature:10
    Given the workspace is initialized                                #
features/steps/gz_steps.py:67
    And an OBPI lock exists for "OBPI-0.1.0-01" held by agent "codex" #
features/steps/obpi_lock_steps.py:30
    When I run "gz obpi lock claim OBPI-0.1.0-01 --json"              #
features/steps/obpi_lock_steps.py:61
    Then it exits with code 1                                         #
features/steps/obpi_lock_steps.py:71
    And the JSON output field "status" is "conflict"                  #
features/steps/obpi_lock_steps.py:79

  Scenario: Release removes lock                          #
features/obpi_lock.feature:17
    Given the workspace is initialized                    #
features/steps/gz_steps.py:67
    When I run "gz obpi lock claim OBPI-0.1.0-01"         #
features/steps/obpi_lock_steps.py:61
    And I run "gz obpi lock release OBPI-0.1.0-01 --json" #
features/steps/obpi_lock_steps.py:61
    Then it exits with code 0                             #
features/steps/obpi_lock_steps.py:71
    And the JSON output field "status" is "released"      #
features/steps/obpi_lock_steps.py:79

  Scenario: Release validates ownership                               #
features/obpi_lock.feature:24
    Given the workspace is initialized                                #
features/steps/gz_steps.py:67
    And an OBPI lock exists for "OBPI-0.1.0-01" held by agent "codex" #
features/steps/obpi_lock_steps.py:30
    When I run "gz obpi lock release OBPI-0.1.0-01 --json"            #
features/steps/obpi_lock_steps.py:61
    Then it exits with code 1                                         #
features/steps/obpi_lock_steps.py:71
    And the JSON output field "status" is "ownership_error"           #
features/steps/obpi_lock_steps.py:79

  Scenario: Release with force overrides ownership                    #
features/obpi_lock.feature:31
    Given the workspace is initialized                                #
features/steps/gz_steps.py:67
    And an OBPI lock exists for "OBPI-0.1.0-01" held by agent "codex" #
features/steps/obpi_lock_steps.py:30
    When I run "gz obpi lock release OBPI-0.1.0-01 --force --json"    #
features/steps/obpi_lock_steps.py:61
    Then it exits with code 0                                         #
features/steps/obpi_lock_steps.py:71
    And the JSON output field "status" is "released"                  #
features/steps/obpi_lock_steps.py:79

  Scenario: Check exits 0 when held                     #
features/obpi_lock.feature:38
    Given the workspace is initialized                  #
features/steps/gz_steps.py:67
    When I run "gz obpi lock claim OBPI-0.1.0-01"       #
features/steps/obpi_lock_steps.py:61
    And I run "gz obpi lock check OBPI-0.1.0-01 --json" #
features/steps/obpi_lock_steps.py:61
    Then it exits with code 0                           #
features/steps/obpi_lock_steps.py:71
    And the JSON output field "status" is "held"        #
features/steps/obpi_lock_steps.py:79

  Scenario: Check exits 1 when free                      #
features/obpi_lock.feature:45
    Given the workspace is initialized                   #
features/steps/gz_steps.py:67
    When I run "gz obpi lock check OBPI-0.1.0-01 --json" #
features/steps/obpi_lock_steps.py:61
    Then it exits with code 1                            #
features/steps/obpi_lock_steps.py:71
    And the JSON output field "status" is "free"         #
features/steps/obpi_lock_steps.py:79

  Scenario: List shows active locks               #
features/obpi_lock.feature:51
    Given the workspace is initialized            #
features/steps/gz_steps.py:67
    When I run "gz obpi lock claim OBPI-0.1.0-01" #
features/steps/obpi_lock_steps.py:61
    And I run "gz obpi lock list --json"          #
features/steps/obpi_lock_steps.py:61
    Then it exits with code 0                     #
features/steps/obpi_lock_steps.py:71
    And the JSON output field "count" is "1"      #
features/steps/obpi_lock_steps.py:79

  Scenario: List auto-reaps expired locks               #
features/obpi_lock.feature:58
    Given the workspace is initialized                  #
features/steps/gz_steps.py:67
    And an expired OBPI lock exists for "OBPI-0.1.0-01" #
features/steps/obpi_lock_steps.py:45
    When I run "gz obpi lock list --json"               #
features/steps/obpi_lock_steps.py:61
    Then it exits with code 0                           #
features/steps/obpi_lock_steps.py:71
    And the JSON output field "count" is "0"            #
features/steps/obpi_lock_steps.py:79

  Scenario: Deprecated lock-claim alias works            #
features/obpi_lock.feature:65
    Given the workspace is initialized                   #
features/steps/gz_steps.py:67
    When I run "gz obpi lock-claim OBPI-0.1.0-01 --json" #
features/steps/obpi_lock_steps.py:61
    Then it exits with code 0                            #
features/steps/obpi_lock_steps.py:71
    And the JSON output field "status" is "claimed"      #
features/steps/obpi_lock_steps.py:79

Feature: Patch release discovery CLI # features/patch_release.feature:1
  The gz patch release command discovers qualifying GHIs since the last tag,
  computes the next patch version, and (unless --dry-run) writes a manifest
  and ledger event.
  Scenario: Help text exits zero and lists flags     #
features/patch_release.feature:6
    Given the workspace is initialized in heavy mode #
features/steps/gz_steps.py:60
    When I run the gz command "patch release --help" #
features/steps/gz_steps.py:209
    Then the command exits with code 0               #
features/steps/gz_steps.py:220
    And the output contains "--dry-run"              #
features/steps/gz_steps.py:225
    And the output contains "--json"                 #
features/steps/gz_steps.py:225

  Scenario: Dry run reports discovery without executing  #
features/patch_release.feature:13
    Given the workspace is initialized in heavy mode     #
features/steps/gz_steps.py:60
    When I run the gz command "patch release --dry-run"  #
features/steps/gz_steps.py:209
    Then the command exits with code 0                   #
features/steps/gz_steps.py:220
    And the output contains "Patch Release Discovery"    #
features/steps/gz_steps.py:225
    And the output contains "GHIs discovered"            #
features/steps/gz_steps.py:225

  Scenario: Dry run JSON output is well-formed                 #
features/patch_release.feature:20
    Given the workspace is initialized in heavy mode           #
features/steps/gz_steps.py:60
    When I run the gz command "patch release --dry-run --json" #
features/steps/gz_steps.py:209
    Then the command exits with code 0                         #
features/steps/gz_steps.py:220
    And JSON path "ghi_count" equals "0"                       #
features/steps/gz_steps.py:241
    And JSON path "tag" equals "None"                          #
features/steps/gz_steps.py:241

Feature: Persona control surface # features/persona.feature:1
  Agent personas define behavioral identity frames stored in
  .gzkit/personas/ and loaded at dispatch boundaries (ADR-0.0.11).
  Scenario: List personas in initialized workspace with no files  #
features/persona.feature:5
    Given the workspace is initialized                            #
features/steps/gz_steps.py:67
    When I run the gz command "personas list"                     #
features/steps/gz_steps.py:209
    Then the command exits with code 0                            #
features/steps/gz_steps.py:220

  Scenario: List personas shows implementer when file exists  #
features/persona.feature:10
    Given the workspace is initialized                        #
features/steps/gz_steps.py:67
    And a persona file "implementer" exists                   #
features/steps/persona_steps.py:11
    When I run the gz command "personas list --json"          #
features/steps/gz_steps.py:209
    Then the command exits with code 0                        #
features/steps/gz_steps.py:220
    And the output contains "implementer"                     #
features/steps/gz_steps.py:225
    And the output contains "methodical"                      #
features/steps/gz_steps.py:225

  Scenario: List personas shows main-session when file exists  #
features/persona.feature:18
    Given the workspace is initialized                         #
features/steps/gz_steps.py:67
    And a persona file "main-session" exists                   #
features/steps/persona_steps.py:11
    When I run the gz command "personas list --json"           #
features/steps/gz_steps.py:209
    Then the command exits with code 0                         #
features/steps/gz_steps.py:220
    And the output contains "main-session"                     #
features/steps/gz_steps.py:225
    And the output contains "methodical"                       #
features/steps/gz_steps.py:225

  Scenario: AGENTS.md persona section references main-session grounding  #
features/persona.feature:26
    Given the workspace is initialized with agent surfaces               #
features/steps/gz_steps.py:79
    Then the file "AGENTS.md" contains "main-session"                    #
features/steps/gz_steps.py:235
    And the file "AGENTS.md" contains "craftsperson"                     #
features/steps/gz_steps.py:235
    And the file "AGENTS.md" contains "governance not as overhead"       #
features/steps/gz_steps.py:235

  Scenario: AGENTS.md persona section lists available personas with roles  #
features/persona.feature:32
    Given the workspace is initialized with agent surfaces                 #
features/steps/gz_steps.py:79
    Then the file "AGENTS.md" contains "implementer"                       #
features/steps/gz_steps.py:235
    And the file "AGENTS.md" contains "narrator"                           #
features/steps/gz_steps.py:235
    And the file "AGENTS.md" contains "pipeline-orchestrator"              #
features/steps/gz_steps.py:235
    And the file "AGENTS.md" contains "quality-reviewer"                   #
features/steps/gz_steps.py:235
    And the file "AGENTS.md" contains "spec-reviewer"                      #
features/steps/gz_steps.py:235

  Scenario: Personas list is read-only        # features/persona.feature:40
    Given the workspace is initialized        # features/steps/gz_steps.py:67
    And a persona file "implementer" exists   #
features/steps/persona_steps.py:11
    When I run the gz command "personas list" # features/steps/gz_steps.py:209
    Then the command exits with code 0        # features/steps/gz_steps.py:220
    And the output contains "implementer"     # features/steps/gz_steps.py:225

  Scenario: Persona drift reports with governance evidence                    #
features/persona.feature:47
    Given the workspace is initialized                                        #
features/steps/gz_steps.py:67
    And the ledger contains governance events                                 #
features/steps/persona_steps.py:31
    When I run the gz command "personas drift --persona default-agent --json" #
features/steps/gz_steps.py:209
    Then the command exits with code 0                                        #
features/steps/gz_steps.py:220
    And the output is valid JSON                                              #
features/steps/task_governance_steps.py:45

  Scenario: Persona drift filters to single persona                           #
features/persona.feature:54
    Given the workspace is initialized                                        #
features/steps/gz_steps.py:67
    And the ledger contains governance events                                 #
features/steps/persona_steps.py:31
    When I run the gz command "personas drift --persona default-agent --json" #
features/steps/gz_steps.py:209
    Then the command exits with code 0                                        #
features/steps/gz_steps.py:220
    And the output contains "default-agent"                                   #
features/steps/gz_steps.py:225

  Scenario: Persona drift help includes description   #
features/persona.feature:61
    Given the workspace is initialized                #
features/steps/gz_steps.py:67
    When I run the gz command "personas drift --help" #
features/steps/gz_steps.py:209
    Then the command exits with code 0                #
features/steps/gz_steps.py:220
    And the output contains "behavioral proxies"      #
features/steps/gz_steps.py:225

Feature: Persona sync to vendor mirrors # features/persona_sync.feature:1
  Persona files in .gzkit/personas/ are mirrored to vendor surfaces
  by gz agent sync control-surfaces (ADR-0.0.13 item 3).
  Scenario: Sync mirrors personas to Claude surface         #
features/persona_sync.feature:5
    Given the workspace is initialized with agent surfaces  #
features/steps/gz_steps.py:79
    And a persona file "implementer" exists                 #
features/steps/persona_steps.py:11
    When I run the gz command "agent sync control-surfaces" #
features/steps/gz_steps.py:209
    Then the command exits with code 0                      #
features/steps/gz_steps.py:220
    And the file ".claude/personas/implementer.md" exists   #
features/steps/gz_steps.py:230

  Scenario: Manifest includes personas control surface      #
features/persona_sync.feature:12
    Given the workspace is initialized with agent surfaces  #
features/steps/gz_steps.py:79
    When I run the gz command "agent sync control-surfaces" #
features/steps/gz_steps.py:209
    Then the command exits with code 0                      #
features/steps/gz_steps.py:220
    And the file ".gzkit/manifest.json" contains "personas" #
features/steps/gz_steps.py:235

  Scenario: Init scaffolds default personas in clean workspace  #
features/persona_sync.feature:20
    Given the workspace is initialized                          #
features/steps/gz_steps.py:67
    Then the file ".gzkit/personas/default-agent.md" exists     #
features/steps/gz_steps.py:230
    And the file ".gzkit/personas/default-reviewer.md" exists   #
features/steps/gz_steps.py:230

  Scenario: Scaffolded personas pass surface validation    #
features/persona_sync.feature:25
    Given the workspace is initialized with agent surfaces #
features/steps/gz_steps.py:79
    When I run the gz command "validate --surfaces"        #
features/steps/gz_steps.py:209
    Then the command exits with code 0                     #
features/steps/gz_steps.py:220

Feature: Reporter rendering presets # features/reporter_rendering.feature:1
  The reporter module provides consistent Rich rendering for CLI tables and
panels.
  Scenario: status_table renders governance table
# features/reporter_rendering.feature:4
    Given a status_table with title "ADR Status" and columns "ADR,Lane,Status"
and 2 rows # features/steps/reporter_steps.py:13
    When the table is rendered to text
# features/steps/reporter_steps.py:66
    Then the rendered output contains "ADR Status"
# features/steps/reporter_steps.py:75
    And the rendered output contains "ADR-0.1.0"
# features/steps/reporter_steps.py:75

  Scenario: status_table renders empty state                                  #
features/reporter_rendering.feature:10
    Given a status_table with title "ADR Status" and columns "ADR" and 0 rows #
features/steps/reporter_steps.py:13
    When the table is rendered to text                                        #
features/steps/reporter_steps.py:66
    Then the rendered output contains "No data."                              #
features/steps/reporter_steps.py:75

  Scenario: kv_table renders key-value pairs           #
features/reporter_rendering.feature:15
    Given a kv_table with title "Overview" and 3 pairs #
features/steps/reporter_steps.py:32
    When the table is rendered to text                 #
features/steps/reporter_steps.py:66
    Then the rendered output contains "Overview"       #
features/steps/reporter_steps.py:75
    And the rendered output contains "Lane"            #
features/steps/reporter_steps.py:75
    And the rendered output contains "heavy"           #
features/steps/reporter_steps.py:75

  Scenario: ceremony_panel renders with double border        #
features/reporter_rendering.feature:22
    Given a ceremony_panel with title "Closeout" and 2 items #
features/steps/reporter_steps.py:38
    When the panel is rendered to text                       #
features/steps/reporter_steps.py:66
    Then the rendered output contains "Closeout"             #
features/steps/reporter_steps.py:75
    And the rendered output contains "Step 1"                #
features/steps/reporter_steps.py:75

  Scenario: list_table renders simple catalog                                  #
features/reporter_rendering.feature:28
    Given a list_table with title "Chores" and columns "Slug,Title" and 2 rows #
features/steps/reporter_steps.py:47
    When the table is rendered to text                                         #
features/steps/reporter_steps.py:66
    Then the rendered output contains "Chores"                                 #
features/steps/reporter_steps.py:75
    And the rendered output contains "chore-a"                                 #
features/steps/reporter_steps.py:75

  Scenario: list_table renders empty state                               #
features/reporter_rendering.feature:34
    Given a list_table with title "Skills" and columns "Name" and 0 rows #
features/steps/reporter_steps.py:47
    When the table is rendered to text                                   #
features/steps/reporter_steps.py:66
    Then the rendered output contains "No items found."                  #
features/steps/reporter_steps.py:75

Feature: OBPI reviewer agent dispatch  # OBPI-0.23.0-03 #
features/reviewer_agent.feature:1
  An independent reviewer agent verifies delivered work against OBPI
  promises with fresh-eyes assessment of promises-met, docs-quality,
  and closing-argument-quality.
  @review @dispatch
  Scenario: Reviewer prompt contains brief and closing argument           #
features/reviewer_agent.feature:7
    Given an OBPI brief with 2 requirements                               #
features/steps/reviewer_agent_steps.py:28
    And a closing argument "This work earned its closure by delivering X" #
features/steps/reviewer_agent_steps.py:35
    And 2 changed files and 1 doc file                                    #
features/steps/reviewer_agent_steps.py:45
    When I compose the reviewer prompt                                    #
features/steps/reviewer_agent_steps.py:52
    Then the prompt contains the OBPI identifier                          #
features/steps/reviewer_agent_steps.py:63
    And the prompt contains the brief content                             #
features/steps/reviewer_agent_steps.py:68
    And the prompt contains the closing argument                          #
features/steps/reviewer_agent_steps.py:73
    And the prompt contains all changed files                             #
features/steps/reviewer_agent_steps.py:78
    And the prompt contains the doc file                                  #
features/steps/reviewer_agent_steps.py:84
    And the prompt contains the assessment JSON schema                    #
features/steps/reviewer_agent_steps.py:90

  @review @dispatch
  Scenario: Reviewer prompt handles missing closing argument         #
features/reviewer_agent.feature:20
    Given an OBPI brief with 1 requirements                          #
features/steps/reviewer_agent_steps.py:28
    And no closing argument                                          #
features/steps/reviewer_agent_steps.py:40
    And 1 changed files and 0 doc file                               #
features/steps/reviewer_agent_steps.py:45
    When I compose the reviewer prompt                               #
features/steps/reviewer_agent_steps.py:52
    Then the reviewer prompt contains "No closing argument provided" #
features/steps/reviewer_agent_steps.py:99

  @review @parse
  Scenario: Valid PASS assessment is parsed from agent output          #
features/reviewer_agent.feature:28
    Given agent output with a valid PASS assessment for 2 requirements #
features/steps/reviewer_agent_steps.py:113
    When I parse the reviewer assessment                               #
features/steps/reviewer_agent_steps.py:170
    Then the assessment verdict is "PASS"                              #
features/steps/reviewer_agent_steps.py:175
    And 2 promise assessments are returned                             #
features/steps/reviewer_agent_steps.py:183
    And docs quality is "substantive"                                  #
features/steps/reviewer_agent_steps.py:191
    And closing argument quality is "earned"                           #
features/steps/reviewer_agent_steps.py:199

  @review @parse
  Scenario: Valid FAIL assessment is parsed from agent output  #
features/reviewer_agent.feature:37
    Given agent output with a valid FAIL assessment            #
features/steps/reviewer_agent_steps.py:133
    When I parse the reviewer assessment                       #
features/steps/reviewer_agent_steps.py:170
    Then the assessment verdict is "FAIL"                      #
features/steps/reviewer_agent_steps.py:175
    And docs quality is "missing"                              #
features/steps/reviewer_agent_steps.py:191
    And closing argument quality is "missing"                  #
features/steps/reviewer_agent_steps.py:199

  @review @parse
  Scenario: CONCERNS assessment with mixed promises                  #
features/reviewer_agent.feature:45
    Given agent output with a CONCERNS assessment and mixed promises #
features/steps/reviewer_agent_steps.py:147
    When I parse the reviewer assessment                             #
features/steps/reviewer_agent_steps.py:170
    Then the assessment verdict is "CONCERNS"                        #
features/steps/reviewer_agent_steps.py:175
    And promise 1 is met                                             #
features/steps/reviewer_agent_steps.py:207
    And promise 2 is not met                                         #
features/steps/reviewer_agent_steps.py:214

  @review @parse
  Scenario: Invalid agent output returns no assessment  #
features/reviewer_agent.feature:53
    Given agent output with no JSON block               #
features/steps/reviewer_agent_steps.py:164
    When I parse the reviewer assessment                #
features/steps/reviewer_agent_steps.py:170
    Then no assessment is returned                      #
features/steps/reviewer_agent_steps.py:221

  @review @artifact
  Scenario: Assessment artifact is stored alongside the brief  #
features/reviewer_agent.feature:59
    Given a parsed PASS reviewer assessment                    #
features/steps/reviewer_agent_steps.py:231
    And a temporary ADR package directory                      #
features/steps/reviewer_agent_steps.py:247
    When I store the reviewer assessment                       #
features/steps/reviewer_agent_steps.py:253
    Then a REVIEW artifact file exists in the briefs directory #
features/steps/reviewer_agent_steps.py:258
    And the artifact contains the verdict                      #
features/steps/reviewer_agent_steps.py:265
    And the artifact contains promise assessments              #
features/steps/reviewer_agent_steps.py:271

  @review @ceremony
  Scenario: Assessment is formatted for ceremony display        #
features/reviewer_agent.feature:68
    Given a parsed CONCERNS reviewer assessment with 3 promises #
features/steps/reviewer_agent_steps.py:282
    When I format the assessment for ceremony                   #
features/steps/reviewer_agent_steps.py:298
    Then the output contains a promise table with 3 rows        #
features/steps/reviewer_agent_steps.py:303
    And the output contains the docs quality                    #
features/steps/reviewer_agent_steps.py:314
    And the output contains the closing argument quality        #
features/steps/reviewer_agent_steps.py:319
    And the output contains the verdict                         #
features/steps/reviewer_agent_steps.py:324

Feature: State repair force-reconciliation # features/state_repair.feature:1
  The gz state --repair command force-reconciles all OBPI frontmatter
  status from ledger-derived state (ADR-0.0.9, OBPI-03).
  Scenario: Repair updates drifted frontmatter to match ledger  #
features/state_repair.feature:5
    Given the workspace is initialized                          #
features/steps/gz_steps.py:67
    And ADR-0.1.0 exists                                        #
features/steps/gz_steps.py:90
    And an OBPI brief exists with frontmatter status "Draft"    #
features/steps/state_repair_steps.py:14
    And the ledger marks OBPI-0.1.0-01 as completed             #
features/steps/state_repair_steps.py:29
    When I run the gz command "state --repair"                  #
features/steps/gz_steps.py:209
    Then the command exits with code 0                          #
features/steps/gz_steps.py:220
    And the OBPI brief frontmatter status is "Completed"        #
features/steps/state_repair_steps.py:48

  Scenario: Repair is idempotent                                 #
features/state_repair.feature:14
    Given the workspace is initialized                           #
features/steps/gz_steps.py:67
    And ADR-0.1.0 exists                                         #
features/steps/gz_steps.py:90
    And an OBPI brief exists with frontmatter status "Completed" #
features/steps/state_repair_steps.py:14
    And the ledger marks OBPI-0.1.0-01 as completed              #
features/steps/state_repair_steps.py:29
    When I run the gz command "state --repair"                   #
features/steps/gz_steps.py:209
    And I run the gz command "state --repair --json"             #
features/steps/gz_steps.py:209
    Then the command exits with code 0                           #
features/steps/gz_steps.py:220
    And the JSON output field "total" equals 0                   #
features/steps/state_repair_steps.py:56

  Scenario: Repair reports changes in JSON mode              #
features/state_repair.feature:24
    Given the workspace is initialized                       #
features/steps/gz_steps.py:67
    And ADR-0.1.0 exists                                     #
features/steps/gz_steps.py:90
    And an OBPI brief exists with frontmatter status "Draft" #
features/steps/state_repair_steps.py:14
    And the ledger marks OBPI-0.1.0-01 as completed          #
features/steps/state_repair_steps.py:29
    When I run the gz command "state --repair --json"        #
features/steps/gz_steps.py:209
    Then the command exits with code 0                       #
features/steps/gz_steps.py:220
    And the JSON output field "total" equals 1               #
features/steps/state_repair_steps.py:56

Feature: Subagent pipeline dispatch lifecycle #
features/subagent_pipeline.feature:1
  Stage 2 controller dispatches implementer subagents per plan task
  with model-aware routing and structured result handling.
  @dispatch
  Scenario: Tasks are extracted from a heading-format plan  #
features/subagent_pipeline.feature:6
    Given a plan with heading-format tasks                  #
features/steps/subagent_pipeline_steps.py:36
    When I extract plan tasks                               #
features/steps/subagent_pipeline_steps.py:53
    Then 3 tasks are found                                  #
features/steps/subagent_pipeline_steps.py:58
    And task 1 description is "Add the model"               #
features/steps/subagent_pipeline_steps.py:65

  @dispatch
  Scenario: Tasks are extracted from a numbered-list plan  #
features/subagent_pipeline.feature:13
    Given a plan with numbered-list tasks                  #
features/steps/subagent_pipeline_steps.py:43
    When I extract plan tasks                              #
features/steps/subagent_pipeline_steps.py:53
    Then 2 tasks are found                                 #
features/steps/subagent_pipeline_steps.py:58

  @dispatch
  Scenario: Empty plan yields zero tasks  #
features/subagent_pipeline.feature:19
    Given an empty plan                   #
features/steps/subagent_pipeline_steps.py:48
    When I extract plan tasks             #
features/steps/subagent_pipeline_steps.py:53
    Then 0 tasks are found                #
features/steps/subagent_pipeline_steps.py:58

  @dispatch
  Scenario: Simple task routes to haiku model              #
features/subagent_pipeline.feature:25
    Given a dispatch state with 1 task and 2 allowed paths #
features/steps/subagent_pipeline_steps.py:76
    Then task 1 model is "haiku"                           #
features/steps/subagent_pipeline_steps.py:99
    And task 1 complexity is "simple"                      #
features/steps/subagent_pipeline_steps.py:105

  @dispatch
  Scenario: Standard task routes to sonnet model           #
features/subagent_pipeline.feature:31
    Given a dispatch state with 1 task and 4 allowed paths #
features/steps/subagent_pipeline_steps.py:76
    Then task 1 model is "sonnet"                          #
features/steps/subagent_pipeline_steps.py:99
    And task 1 complexity is "standard"                    #
features/steps/subagent_pipeline_steps.py:105

  @dispatch
  Scenario: Complex task routes to opus model              #
features/subagent_pipeline.feature:37
    Given a dispatch state with 1 task and 7 allowed paths #
features/steps/subagent_pipeline_steps.py:76
    Then task 1 model is "opus"                            #
features/steps/subagent_pipeline_steps.py:99
    And task 1 complexity is "complex"                     #
features/steps/subagent_pipeline_steps.py:105

  @dispatch
  Scenario: DONE result advances to next task               #
features/subagent_pipeline.feature:43
    Given a dispatch state with 2 tasks and 1 allowed paths #
features/steps/subagent_pipeline_steps.py:76
    And task 1 is dispatched                                #
features/steps/subagent_pipeline_steps.py:84
    When task 1 returns DONE                                #
features/steps/subagent_pipeline_steps.py:118
    Then the dispatch action is "advance"                   #
features/steps/subagent_pipeline_steps.py:142
    And task 1 status is "done"                             #
features/steps/subagent_pipeline_steps.py:149

  @dispatch
  Scenario: DONE on last task completes dispatch           #
features/subagent_pipeline.feature:51
    Given a dispatch state with 1 task and 1 allowed paths #
features/steps/subagent_pipeline_steps.py:76
    And task 1 is dispatched                               #
features/steps/subagent_pipeline_steps.py:84
    When task 1 returns DONE                               #
features/steps/subagent_pipeline_steps.py:118
    Then the dispatch action is "complete"                 #
features/steps/subagent_pipeline_steps.py:142

  @dispatch
  Scenario: DONE_WITH_CONCERNS logs concerns and advances               #
features/subagent_pipeline.feature:58
    Given a dispatch state with 2 tasks and 1 allowed paths             #
features/steps/subagent_pipeline_steps.py:76
    And task 1 is dispatched                                            #
features/steps/subagent_pipeline_steps.py:84
    When task 1 returns DONE_WITH_CONCERNS with concern "might break X" #
features/steps/subagent_pipeline_steps.py:136
    Then the dispatch action is "advance"                               #
features/steps/subagent_pipeline_steps.py:142
    And all concerns include "might break X"                            #
features/steps/subagent_pipeline_steps.py:155

  @dispatch
  Scenario: NEEDS_CONTEXT triggers redispatch              #
features/subagent_pipeline.feature:66
    Given a dispatch state with 1 task and 1 allowed paths #
features/steps/subagent_pipeline_steps.py:76
    And task 1 is dispatched                               #
features/steps/subagent_pipeline_steps.py:84
    When task 1 returns NEEDS_CONTEXT                      #
features/steps/subagent_pipeline_steps.py:124
    Then the dispatch action is "redispatch"               #
features/steps/subagent_pipeline_steps.py:142

  @dispatch
  Scenario: NEEDS_CONTEXT circuit breaker after max retries  #
features/subagent_pipeline.feature:73
    Given a dispatch state with 1 task and 1 allowed paths   #
features/steps/subagent_pipeline_steps.py:76
    And task 1 has been dispatched 2 times                   #
features/steps/subagent_pipeline_steps.py:90
    When task 1 returns NEEDS_CONTEXT                        #
features/steps/subagent_pipeline_steps.py:124
    Then the dispatch action is "handoff"                    #
features/steps/subagent_pipeline_steps.py:142
    And task 1 status is "blocked"                           #
features/steps/subagent_pipeline_steps.py:149

  @dispatch
  Scenario: BLOCKED triggers fix attempt                   #
features/subagent_pipeline.feature:81
    Given a dispatch state with 1 task and 1 allowed paths #
features/steps/subagent_pipeline_steps.py:76
    And task 1 is dispatched                               #
features/steps/subagent_pipeline_steps.py:84
    When task 1 returns BLOCKED                            #
features/steps/subagent_pipeline_steps.py:130
    Then the dispatch action is "fix"                      #
features/steps/subagent_pipeline_steps.py:142

  @dispatch
  Scenario: BLOCKED after max fix attempts triggers handoff  #
features/subagent_pipeline.feature:88
    Given a dispatch state with 1 task and 1 allowed paths   #
features/steps/subagent_pipeline_steps.py:76
    And task 1 has been dispatched 2 times                   #
features/steps/subagent_pipeline_steps.py:90
    When task 1 returns BLOCKED                              #
features/steps/subagent_pipeline_steps.py:130
    Then the dispatch action is "handoff"                    #
features/steps/subagent_pipeline_steps.py:142
    And task 1 status is "blocked"                           #
features/steps/subagent_pipeline_steps.py:149

  @dispatch
  Scenario: Prompt includes allowed files and rules                           #
features/subagent_pipeline.feature:96
    Given a dispatch task for "Add validation" with paths "src/a.py,src/b.py" #
features/steps/subagent_pipeline_steps.py:167
    When I compose the implementer prompt                                     #
features/steps/subagent_pipeline_steps.py:179
    Then the prompt contains "src/a.py"                                       #
features/steps/subagent_pipeline_steps.py:184
    And the prompt contains "### Rules"                                       #
features/steps/subagent_pipeline_steps.py:184

  @dispatch
  Scenario: Result JSON is parsed from agent output  #
features/subagent_pipeline.feature:103
    Given agent output with a DONE result JSON block #
features/steps/subagent_pipeline_steps.py:194
    When I parse the handoff result                  #
features/steps/subagent_pipeline_steps.py:205
    Then the parsed status is "DONE"                 #
features/steps/subagent_pipeline_steps.py:210
    And the parsed files changed include "src/x.py"  #
features/steps/subagent_pipeline_steps.py:218

  @dispatch
  Scenario: Dispatch record tracks subagent lifecycle
# features/subagent_pipeline.feature:110
    Given a subagent dispatch record for task 1 as "Implementer" with model
"sonnet" # features/steps/subagent_pipeline_steps.py:230
    When the dispatch record is completed with status "done"
# features/steps/subagent_pipeline_steps.py:235
    Then the completed record has a completion timestamp
# features/steps/subagent_pipeline_steps.py:240
    And the completed record status is "done"
# features/steps/subagent_pipeline_steps.py:245

  @dispatch
  Scenario: Dispatch aggregation computes correct totals
# features/subagent_pipeline.feature:117
    Given 3 completed dispatch records with statuses
"done,blocked,done_with_concerns" #
features/steps/subagent_pipeline_steps.py:250
    When I aggregate dispatch results
# features/steps/subagent_pipeline_steps.py:260
    Then aggregation shows 2 completed and 1 blocked
# features/steps/subagent_pipeline_steps.py:265

  @dispatch
  Scenario: Model routing loads defaults     #
features/subagent_pipeline.feature:123
    Given no pipeline config file            #
features/steps/subagent_pipeline_steps.py:275
    When I load model routing config         #
features/steps/subagent_pipeline_steps.py:281
    Then implementer simple model is "haiku" #
features/steps/subagent_pipeline_steps.py:286
    And reviewer complex model is "opus"     #
features/steps/subagent_pipeline_steps.py:291

  @dispatch
  Scenario: Agent file validation detects missing files  #
features/subagent_pipeline.feature:130
    Given a project directory with no agent files        #
features/steps/subagent_pipeline_steps.py:296
    When I validate agent files                          #
features/steps/subagent_pipeline_steps.py:302
    Then validation finds 4 errors                       #
features/steps/subagent_pipeline_steps.py:307

  @dispatch @stage2
  Scenario: Stage 2 dispatch loop executes tasks sequentially
# features/subagent_pipeline.feature:136
    Given a plan with 3 tasks
# features/steps/subagent_pipeline_steps.py:319
    And allowed paths ["src/a.py", "src/b.py"]
# features/steps/subagent_pipeline_steps.py:324
    And brief requirements ["Config MUST parse TOML", "Validation MUST reject
nulls"]  # features/steps/subagent_pipeline_steps.py:329
    When the controller creates dispatch state for "OBPI-0.18.0-06" under
"ADR-0.18.0" # features/steps/subagent_pipeline_steps.py:334
    And dispatches each task sequentially with DONE results
# features/steps/subagent_pipeline_steps.py:341
    Then all 3 tasks are completed
# features/steps/subagent_pipeline_steps.py:356
    And dispatch state is finished
# features/steps/subagent_pipeline_steps.py:363
    And each task prompt includes brief requirements
# features/steps/subagent_pipeline_steps.py:368

  @dispatch @stage2
  Scenario: Stage 2 halts on BLOCKED task after fix attempts              #
features/subagent_pipeline.feature:147
    Given a plan with 2 tasks                                             #
features/steps/subagent_pipeline_steps.py:319
    And allowed paths ["src/a.py"]                                        #
features/steps/subagent_pipeline_steps.py:324
    When the controller creates dispatch state for "OBPI-X" under "ADR-X" #
features/steps/subagent_pipeline_steps.py:334
    And task 1 is dispatched                                              #
features/steps/subagent_pipeline_steps.py:84
    And task 1 returns BLOCKED                                            #
features/steps/subagent_pipeline_steps.py:130
    Then the dispatch action is "fix"                                     #
features/steps/subagent_pipeline_steps.py:142
    When task 1 is dispatched                                             #
features/steps/subagent_pipeline_steps.py:84
    And task 1 returns BLOCKED                                            #
features/steps/subagent_pipeline_steps.py:130
    Then the dispatch action is "handoff"                                 #
features/steps/subagent_pipeline_steps.py:142
    And task 2 remains pending                                            #
features/steps/subagent_pipeline_steps.py:376
    And dispatch state has 1 blocked task                                 #
features/steps/subagent_pipeline_steps.py:386

  @dispatch @review
  Scenario: Review dispatched after DONE task with both passing  #
features/subagent_pipeline.feature:161
    Given a dispatch state with 2 tasks and 2 allowed paths      #
features/steps/subagent_pipeline_steps.py:76
    And task 1 is dispatched                                     #
features/steps/subagent_pipeline_steps.py:84
    When task 1 returns DONE                                     #
features/steps/subagent_pipeline_steps.py:118
    Then review should be dispatched for task 1                  #
features/steps/subagent_pipeline_steps.py:398
    When both reviews pass for task 1                            #
features/steps/subagent_pipeline_steps.py:420
    Then the review action is "advance"                          #
features/steps/subagent_pipeline_steps.py:432

  @dispatch @review
  Scenario: Review not dispatched for BLOCKED task         #
features/subagent_pipeline.feature:170
    Given a dispatch state with 1 task and 1 allowed paths #
features/steps/subagent_pipeline_steps.py:76
    And task 1 is dispatched                               #
features/steps/subagent_pipeline_steps.py:84
    When task 1 returns BLOCKED                            #
features/steps/subagent_pipeline_steps.py:130
    Then review should not be dispatched for task 1        #
features/steps/subagent_pipeline_steps.py:409

  @dispatch @review
  Scenario: Critical spec finding triggers fix cycle       #
features/subagent_pipeline.feature:177
    Given a dispatch state with 1 task and 2 allowed paths #
features/steps/subagent_pipeline_steps.py:76
    And task 1 is dispatched                               #
features/steps/subagent_pipeline_steps.py:84
    When task 1 returns DONE                               #
features/steps/subagent_pipeline_steps.py:118
    And spec review finds critical issue for task 1        #
features/steps/subagent_pipeline_steps.py:439
    Then the review action is "fix"                        #
features/steps/subagent_pipeline_steps.py:432
    And task 1 review fix count is 1                       #
features/steps/subagent_pipeline_steps.py:458

  @dispatch @review
  Scenario: Review blocked after max fix cycles            #
features/subagent_pipeline.feature:186
    Given a dispatch state with 1 task and 2 allowed paths #
features/steps/subagent_pipeline_steps.py:76
    And task 1 is dispatched                               #
features/steps/subagent_pipeline_steps.py:84
    When task 1 returns DONE                               #
features/steps/subagent_pipeline_steps.py:118
    And spec review finds critical issue for task 1        #
features/steps/subagent_pipeline_steps.py:439
    Then the review action is "fix"                        #
features/steps/subagent_pipeline_steps.py:432
    When spec review finds critical issue for task 1       #
features/steps/subagent_pipeline_steps.py:439
    Then the review action is "fix"                        #
features/steps/subagent_pipeline_steps.py:432
    When spec review finds critical issue for task 1       #
features/steps/subagent_pipeline_steps.py:439
    Then the review action is "blocked"                    #
features/steps/subagent_pipeline_steps.py:432

  @dispatch @stage3
  Scenario: Stage 3 builds verification plan from brief requirements  #
features/subagent_pipeline.feature:198
    Given a brief with 2 requirements and distinct test paths         #
features/steps/subagent_pipeline_steps.py:471
    When I prepare Stage 3 verification                               #
features/steps/subagent_pipeline_steps.py:498
    Then the verification plan has 2 scopes                           #
features/steps/subagent_pipeline_steps.py:509
    And the verification strategy is "parallel"                       #
features/steps/subagent_pipeline_steps.py:516

  @dispatch @stage3
  Scenario: Stage 3 falls back to sequential with no test paths  #
features/subagent_pipeline.feature:205
    Given a brief with 2 requirements and no test paths          #
features/steps/subagent_pipeline_steps.py:488
    When I prepare Stage 3 verification                          #
features/steps/subagent_pipeline_steps.py:498
    Then the verification strategy is "sequential"               #
features/steps/subagent_pipeline_steps.py:516

  @dispatch @stage3
  Scenario: Stage 3 records timing metrics
# features/subagent_pipeline.feature:211
    Given a verification run of 5 seconds with strategy "parallel" and 3 groups
# features/steps/subagent_pipeline_steps.py:523
    When I compute verification timing
# features/steps/subagent_pipeline_steps.py:531
    Then elapsed seconds is 5.0
# features/steps/subagent_pipeline_steps.py:543
    And time saved is greater than 0
# features/steps/subagent_pipeline_steps.py:550

  @dispatch @stage3
  Scenario: Stage 3 creates dispatch records from verification results  #
features/subagent_pipeline.feature:218
    Given a verification plan with 2 scopes and PASS results            #
features/steps/subagent_pipeline_steps.py:557
    When I create verification dispatch records                         #
features/steps/subagent_pipeline_steps.py:589
    Then 2 dispatch records are created                                 #
features/steps/subagent_pipeline_steps.py:598
    And all dispatch records have role "Verifier"                       #
features/steps/subagent_pipeline_steps.py:605
    And all dispatch records have stage 3                               #
features/steps/subagent_pipeline_steps.py:611

Feature: Task lifecycle governance # features/task_governance.feature:1
  TASK entities have lifecycle commands via gz task CLI.
  Scenario: gz task --help shows subcommands  #
features/task_governance.feature:4
    Given the workspace is initialized        # features/steps/gz_steps.py:67
    When I run the gz command "task --help"   # features/steps/gz_steps.py:209
    Then the command exits with code 0        # features/steps/gz_steps.py:220
    And the output contains "list"            # features/steps/gz_steps.py:225
    And the output contains "start"           # features/steps/gz_steps.py:225
    And the output contains "complete"        # features/steps/gz_steps.py:225
    And the output contains "block"           # features/steps/gz_steps.py:225
    And the output contains "escalate"        # features/steps/gz_steps.py:225

  Scenario: gz task list shows no tasks when none exist  #
features/task_governance.feature:14
    Given the workspace is initialized                   #
features/steps/gz_steps.py:67
    And ADR-0.1.0 exists                                 #
features/steps/gz_steps.py:90
    And an OBPI exists for ADR-0.1.0                     #
features/steps/task_governance_steps.py:28
    When I run the gz command "task list OBPI-0.1.0-01"  #
features/steps/gz_steps.py:209
    Then the command exits with code 0                   #
features/steps/gz_steps.py:220
    And the output contains "No tasks found"             #
features/steps/gz_steps.py:225

  Scenario: gz task start transitions pending to in_progress   #
features/task_governance.feature:22
    Given the workspace is initialized                         #
features/steps/gz_steps.py:67
    And ADR-0.1.0 exists                                       #
features/steps/gz_steps.py:90
    And an OBPI exists for ADR-0.1.0                           #
features/steps/task_governance_steps.py:28
    And a pending task TASK-0.1.0-01-01-01 exists              #
features/steps/task_governance_steps.py:40
    When I run the gz command "task start TASK-0.1.0-01-01-01" #
features/steps/gz_steps.py:209
    Then the command exits with code 0                         #
features/steps/gz_steps.py:220
    And the output contains "Started"                          #
features/steps/gz_steps.py:225

  Scenario: gz task complete on pending task fails                #
features/task_governance.feature:31
    Given the workspace is initialized                            #
features/steps/gz_steps.py:67
    And ADR-0.1.0 exists                                          #
features/steps/gz_steps.py:90
    And an OBPI exists for ADR-0.1.0                              #
features/steps/task_governance_steps.py:28
    When I run the gz command "task complete TASK-0.1.0-01-01-01" #
features/steps/gz_steps.py:209
    Then the command exits non-zero                               #
features/steps/gz_steps.py:215
    And the output contains "Invalid TASK transition"             #
features/steps/gz_steps.py:225

  Scenario: gz task block records reason
# features/task_governance.feature:39
    Given the workspace is initialized
# features/steps/gz_steps.py:67
    And ADR-0.1.0 exists
# features/steps/gz_steps.py:90
    And an OBPI exists for ADR-0.1.0
# features/steps/task_governance_steps.py:28
    And a pending task TASK-0.1.0-01-01-01 exists
# features/steps/task_governance_steps.py:40
    And I run the gz command "task start TASK-0.1.0-01-01-01"
# features/steps/task_governance_steps.py:34
    When I run the gz command "task block TASK-0.1.0-01-01-01 --reason
Missing_API" # features/steps/gz_steps.py:209
    Then the command exits with code 0
# features/steps/gz_steps.py:220
    And the output contains "Blocked"
# features/steps/gz_steps.py:225

  Scenario: gz task start resumes a blocked task                               #
features/task_governance.feature:49
    Given the workspace is initialized                                         #
features/steps/gz_steps.py:67
    And ADR-0.1.0 exists                                                       #
features/steps/gz_steps.py:90
    And an OBPI exists for ADR-0.1.0                                           #
features/steps/task_governance_steps.py:28
    And a pending task TASK-0.1.0-01-01-01 exists                              #
features/steps/task_governance_steps.py:40
    And I run the gz command "task start TASK-0.1.0-01-01-01"                  #
features/steps/task_governance_steps.py:34
    And I run the gz command "task block TASK-0.1.0-01-01-01 --reason blocked" #
features/steps/task_governance_steps.py:34
    When I run the gz command "task start TASK-0.1.0-01-01-01"                 #
features/steps/gz_steps.py:209
    Then the command exits with code 0                                         #
features/steps/gz_steps.py:220
    And the output contains "Resumed"                                          #
features/steps/gz_steps.py:225

  Scenario: gz task list --json returns valid JSON             #
features/task_governance.feature:60
    Given the workspace is initialized                         #
features/steps/gz_steps.py:67
    And ADR-0.1.0 exists                                       #
features/steps/gz_steps.py:90
    And an OBPI exists for ADR-0.1.0                           #
features/steps/task_governance_steps.py:28
    And a pending task TASK-0.1.0-01-01-01 exists              #
features/steps/task_governance_steps.py:40
    And I run the gz command "task start TASK-0.1.0-01-01-01"  #
features/steps/task_governance_steps.py:34
    When I run the gz command "task list OBPI-0.1.0-01 --json" #
features/steps/gz_steps.py:209
    Then the command exits with code 0                         #
features/steps/gz_steps.py:220
    And the output is valid JSON                               #
features/steps/task_governance_steps.py:45

  Scenario: gz task escalate records reason
# features/task_governance.feature:70
    Given the workspace is initialized
# features/steps/gz_steps.py:67
    And ADR-0.1.0 exists
# features/steps/gz_steps.py:90
    And an OBPI exists for ADR-0.1.0
# features/steps/task_governance_steps.py:28
    And a pending task TASK-0.1.0-01-01-01 exists
# features/steps/task_governance_steps.py:40
    And I run the gz command "task start TASK-0.1.0-01-01-01"
# features/steps/task_governance_steps.py:34
    When I run the gz command "task escalate TASK-0.1.0-01-01-01 --reason
Needs_human_decision" # features/steps/gz_steps.py:209
    Then the command exits with code 0
# features/steps/gz_steps.py:220
    And the output contains "Escalated"
# features/steps/gz_steps.py:225

  Scenario: gz status shows task summary when tasks exist     #
features/task_governance.feature:81
    Given the workspace is initialized                        #
features/steps/gz_steps.py:67
    And ADR-0.1.0 exists                                      #
features/steps/gz_steps.py:90
    And an OBPI exists for ADR-0.1.0                          #
features/steps/task_governance_steps.py:28
    And a pending task TASK-0.1.0-01-01-01 exists             #
features/steps/task_governance_steps.py:40
    And I run the gz command "task start TASK-0.1.0-01-01-01" #
features/steps/task_governance_steps.py:34
    When I run the gz command "status"                        #
features/steps/gz_steps.py:209
    Then the command exits with code 0                        #
features/steps/gz_steps.py:220
    And the output contains "Tasks:"                          #
features/steps/gz_steps.py:225

  Scenario: gz status omits task section when no tasks exist  #
features/task_governance.feature:91
    Given the workspace is initialized                        #
features/steps/gz_steps.py:67
    And ADR-0.1.0 exists                                      #
features/steps/gz_steps.py:90
    And an OBPI exists for ADR-0.1.0                          #
features/steps/task_governance_steps.py:28
    When I run the gz command "status"                        #
features/steps/gz_steps.py:209
    Then the command exits with code 0                        #
features/steps/gz_steps.py:220

  Scenario: gz status shows escalated count
# features/task_governance.feature:98
    Given the workspace is initialized
# features/steps/gz_steps.py:67
    And ADR-0.1.0 exists
# features/steps/gz_steps.py:90
    And an OBPI exists for ADR-0.1.0
# features/steps/task_governance_steps.py:28
    And a pending task TASK-0.1.0-01-01-01 exists
# features/steps/task_governance_steps.py:40
    And I run the gz command "task start TASK-0.1.0-01-01-01"
# features/steps/task_governance_steps.py:34
    And I run the gz command "task escalate TASK-0.1.0-01-01-01 --reason
Needs_review" # features/steps/task_governance_steps.py:34
    When I run the gz command "status"
# features/steps/gz_steps.py:209
    Then the command exits with code 0
# features/steps/gz_steps.py:220
    And the output contains "escalated"
# features/steps/gz_steps.py:225

  Scenario: gz state --json includes task data                #
features/task_governance.feature:109
    Given the workspace is initialized                        #
features/steps/gz_steps.py:67
    And ADR-0.1.0 exists                                      #
features/steps/gz_steps.py:90
    And an OBPI exists for ADR-0.1.0                          #
features/steps/task_governance_steps.py:28
    And a pending task TASK-0.1.0-01-01-01 exists             #
features/steps/task_governance_steps.py:40
    And I run the gz command "task start TASK-0.1.0-01-01-01" #
features/steps/task_governance_steps.py:34
    When I run the gz command "state --json"                  #
features/steps/gz_steps.py:209
    Then the command exits with code 0                        #
features/steps/gz_steps.py:220
    And the output is valid JSON                              #
features/steps/task_governance_steps.py:45

Feature: Requirement coverage reporting CLI #
features/test_traceability.feature:1
  The gz covers command reports requirement coverage from @covers
  annotations at ADR, OBPI, and REQ granularity.
  Scenario: All-REQ coverage summary exits with code 0
# features/test_traceability.feature:5
    Given the workspace is initialized
# features/steps/gz_steps.py:67
    And ADR-0.1.0 exists
# features/steps/gz_steps.py:90
    And an OBPI brief with one REQ and a decorator-covered test exists
# features/steps/test_traceability_steps.py:13
    When I run the gz command "covers --json --adr-dir design/adr --test-dir
tests" # features/steps/gz_steps.py:209
    Then the command exits with code 0
# features/steps/gz_steps.py:220
    And JSON path "summary.total_reqs" equals "1"
# features/steps/gz_steps.py:241
    And JSON path "summary.covered_reqs" equals "1"
# features/steps/gz_steps.py:241

  Scenario: Filter by ADR shows only matching REQs
# features/test_traceability.feature:14
    Given the workspace is initialized
# features/steps/gz_steps.py:67
    And ADR-0.1.0 exists
# features/steps/gz_steps.py:90
    And an OBPI brief with one REQ and a decorator-covered test exists
# features/steps/test_traceability_steps.py:13
    When I run the gz command "covers ADR-0.1.0 --json --adr-dir design/adr
--test-dir tests" # features/steps/gz_steps.py:209
    Then the command exits with code 0
# features/steps/gz_steps.py:220
    And JSON path "summary.total_reqs" equals "1"
# features/steps/gz_steps.py:241

  Scenario: Plain output is one-per-line
# features/test_traceability.feature:22
    Given the workspace is initialized
# features/steps/gz_steps.py:67
    And ADR-0.1.0 exists
# features/steps/gz_steps.py:90
    And an OBPI brief with one REQ and a decorator-covered test exists
# features/steps/test_traceability_steps.py:13
    When I run the gz command "covers --plain --adr-dir design/adr --test-dir
tests" # features/steps/gz_steps.py:209
    Then the command exits with code 0
# features/steps/gz_steps.py:220
    And the output contains "covered"
# features/steps/gz_steps.py:225

  Scenario: Help text shows description and options  #
features/test_traceability.feature:30
    When I run the gz command "covers --help"        #
features/steps/gz_steps.py:209
    Then the command exits with code 0               #
features/steps/gz_steps.py:220
    And the output contains "--json"                 #
features/steps/gz_steps.py:225
    And the output contains "--plain"                #
features/steps/gz_steps.py:225

  Scenario: No REQs found returns empty summary
# features/test_traceability.feature:36
    Given the workspace is initialized
# features/steps/gz_steps.py:67
    And ADR-0.1.0 exists
# features/steps/gz_steps.py:90
    When I run the gz command "covers --json --adr-dir design/adr --test-dir
tests" # features/steps/gz_steps.py:209
    Then the command exits with code 0
# features/steps/gz_steps.py:220
    And JSON path "summary.total_reqs" equals "0"
# features/steps/gz_steps.py:241

  Scenario: Audit-check includes coverage section in JSON output  #
features/test_traceability.feature:43
    Given the workspace is initialized                            #
features/steps/gz_steps.py:67
    And ADR-0.1.0 exists                                          #
features/steps/gz_steps.py:90
    And an OBPI brief with covered and uncovered REQs exists      #
features/steps/test_traceability_steps.py:50
    When I run the gz command "adr audit-check ADR-0.1.0 --json"  #
features/steps/gz_steps.py:209
    Then JSON path "coverage.total_reqs" equals "2"               #
features/steps/gz_steps.py:241
    And JSON path "coverage.covered_reqs" equals "1"              #
features/steps/gz_steps.py:241
    And JSON path "coverage.uncovered_reqs" equals "1"            #
features/steps/gz_steps.py:241

  Scenario: Audit-check flags uncovered REQs as blocking coverage findings  #
features/test_traceability.feature:52
    Given the workspace is initialized                                      #
features/steps/gz_steps.py:67
    And ADR-0.1.0 exists                                                    #
features/steps/gz_steps.py:90
    And an OBPI brief with covered and uncovered REQs exists                #
features/steps/test_traceability_steps.py:50
    When I run the gz command "adr audit-check ADR-0.1.0 --json"            #
features/steps/gz_steps.py:209
    Then the command exits with code 1                                      #
features/steps/gz_steps.py:220
    And JSON path "coverage_findings" is not empty                          #
features/steps/test_traceability_steps.py:88
    And JSON path "passed" equals "False"                                   #
features/steps/gz_steps.py:241

Feature: Spec-test-code drift detection CLI # features/triangle_drift.feature:1
  The gz drift command detects governance drift by scanning OBPI briefs,
  test @covers references, and the active code change set.
  Scenario: No drift detected exits with code 0
# features/triangle_drift.feature:5
    Given the workspace is initialized
# features/steps/gz_steps.py:67
    And ADR-0.1.0 exists
# features/steps/gz_steps.py:90
    And an OBPI brief with one REQ and a matching test exists
# features/steps/triangle_drift_steps.py:12
    When I run the gz command "drift --json --adr-dir design/adr --test-dir
tests" # features/steps/gz_steps.py:209
    Then the command exits with code 0
# features/steps/gz_steps.py:220
    And JSON path "summary.total_drift_count" equals "0"
# features/steps/gz_steps.py:241

  Scenario: Unlinked spec exits with code 1
# features/triangle_drift.feature:13
    Given the workspace is initialized
# features/steps/gz_steps.py:67
    And ADR-0.1.0 exists
# features/steps/gz_steps.py:90
    And an OBPI brief with one REQ and no matching test exists
# features/steps/triangle_drift_steps.py:47
    When I run the gz command "drift --json --adr-dir design/adr --test-dir
tests" # features/steps/gz_steps.py:209
    Then the command exits with code 1
# features/steps/gz_steps.py:220
    And JSON path "summary.unlinked_spec_count" equals "1"
# features/steps/gz_steps.py:241

  Scenario: Plain output is one-per-line
# features/triangle_drift.feature:21
    Given the workspace is initialized
# features/steps/gz_steps.py:67
    And ADR-0.1.0 exists
# features/steps/gz_steps.py:90
    And an OBPI brief with one REQ and no matching test exists
# features/steps/triangle_drift_steps.py:47
    When I run the gz command "drift --plain --adr-dir design/adr --test-dir
tests" # features/steps/gz_steps.py:209
    Then the command exits with code 1
# features/steps/gz_steps.py:220
    And the output contains "unlinked"
# features/steps/gz_steps.py:225

  Scenario: Help text shows description and options  #
features/triangle_drift.feature:29
    When I run the gz command "drift --help"         #
features/steps/gz_steps.py:209
    Then the command exits with code 0               #
features/steps/gz_steps.py:220
    And the output contains "--json"                 #
features/steps/gz_steps.py:225
    And the output contains "--plain"                #
features/steps/gz_steps.py:225

21 features passed, 0 failed, 0 skipped
129 scenarios passed, 0 failed, 0 skipped
694 steps passed, 0 failed, 0 skipped
Took 0min 3.033s

  ✓ Gate 4 (BDD): PASS
  ⚠ Gate 5 (Human): PENDING (manual)
