# Conflict Matrix (Pass A)

Rule-pair conflicts where both sides can fire on the same concrete case and
prescribe opposing behavior. Each row carries a worked example grounded in
either session evidence (a GHI commit under the #141–#268 window) or
verbatim rule text that prescribes opposite behavior on a shared input.

Pairs surveyed: all C(21,2) = 210 unordered combinations walked for
opposing prescriptions on a shared input. Rows below are **only** the pairs
where a concrete conflict was found; plausible-overlap pairs without a
concrete example were discarded per Pass A anti-patterns.

| # | Rule A + § | Rule B + § | Worked Example | Wins Today | Suggested Resolution |
|---|---|---|---|---|---|
| 1 | `tests.md` § Red-Green-Refactor ("Tests assert semantics, not strings… assertions must derive from the REQ or the skill's declared contract, not from a run of the code") | `tool-skill-runbook-alignment.md` § Invariant 3 ("every skill whose contract declares 'table' should have at least one test asserting the `gz_command` target's default human-readable output contains table markers") plus `test_adr_status_renders_shared_table_via_deterministic_renderer` in `tests/commands/test_status.py` | Agent writes a unit test for `gz adr status` rendering. `tests.md` says the assertion must pin operator semantics (e.g. "ADR lifecycle is exposed for review"). Invariant 3 says the assertion must contain exact table-marker strings (`╭`, `├`, `╰`). Pinning both collapses into the same test; pinning only the semantic side leaves Invariant 3 unenforced at the unit tier; pinning only the string side violates `tests.md` and GHI #227's relocation of Invariant 6f to its canonical home. The single test named in Invariant 3 is itself a string-pin (`.check.*table` regex), which `tests.md` explicitly names as a wrong-shape test. | Invariant 3 wins today at the unit tier — the named test exists and runs GREEN. `tests.md` § Red-Green-Refactor is the more binding general rule but is not fail-closed at CI today. | Split output-form contract testing out of the unit tier into a per-skill "output-form fixture" invoked by a separate validator (`gz validate --skill-output-contract`). Unit tests retain semantic assertions only; the fixture owns the string-shape check and cites the skill's declared Output Contract as its REQ. GHI #229 partially addressed this class via "cross-surface contradictions" work, but the fixture-layer separation is not yet mechanized — the scorecard (`docs/governance/advisory-rules-audit.md` rows 29–30) still marks Invariants 2 and 3 as **Promotable**. |
| 2 | `arb.md` § When to Use ARB ("Mandatory" for Heavy-lane attestation and defect GHIs with QA evidence; "Optional" for one-off interactive commands with no attestation downstream) | `attestation-enrichment.md` § Lane behavior ("Heavy lane: missing receipt IDs are fail-closed") + § Canonical invocations | Agent runs `uv run -m unittest -q` during a Lite-lane OBPI to debug a failing test, then drafts the OBPI completion attestation. `arb.md` categorizes this as "Lite-lane attestation → Warned" (narrative acceptable). `attestation-enrichment.md` § Applies to lists `gz obpi complete --attestation-text` unconditionally and requires receipts per the canonical table. The two rules agree on Heavy but the Lite column is warned-only in `arb.md` and unspecified in `attestation-enrichment.md` for the same operator moment. The `arb.md` header note ("resolve scope contradiction with attestation-enrichment.md; GHI #229") confirms a prior scope collision was patched but the lane-by-lane cell text is still authored in two independent tables. | `attestation-enrichment.md` § Canonical invocations wins (it is named as canonical by `arb.md` itself: *"When this file's examples drift, the canonical table wins and this file is the defect"*). | Collapse the Lite/Heavy cells into a single table — either cite `attestation-enrichment.md` § Lane behavior from `arb.md` by reference (no independent cell text), or move the Heavy/Lite matrix into `arb.md` and drop the duplicate from `attestation-enrichment.md`. Track under a follow-up GHI that completes the GHI #229 scope-collision fix. |
| 3 | `behavioral-invariants.md` § Ownership Invariants 2 & 4 ("Complete all work fully — including adjacent files"; "Scope expansion is not scope creep") | `behavioral-invariants.md` § Ownership § Scope boundary for Invariants 2 and 4 ("They do not license: tangential refactors of adjacent code that is not broken… style improvements… speculative abstraction extraction… deleting pre-existing dead code") + `defect-fix-routing.md` § Anti-patterns ("Adding a Surface Boundary scorecard split + WBS item + brief + audit + attest + sync for a 5-line filter change") | Agent discovers pool ADRs receiving `gate_checked` events mid-implementation. Invariants 2/4 read in isolation license "fix it now" across scope. The scope-boundary subsection and `defect-fix-routing.md` routing thresholds say: if it's pre-existing and not triggered by the primary change, file a GHI (don't expand). The same rule file holds both the expand-scope invariant and its scope boundary; a time-pressured agent reads Invariants 2/4 in a tool call and skips the subsection. This was the GHI #195 triggering pattern — OBPI-0.0.16-04 → OBPI-06 → revert sequence (commits `4d14ebf9` → `d2ed160b`). | `defect-fix-routing.md` wins today (it was authored specifically to close this drift per GHI #195) but the conflict recurs because Invariants 2/4 are compact headline rules and the scope boundary is a subsection. | Fold the scope-boundary test ("did your primary change break this, or is it independently broken and you just noticed?") into the Invariant 2 and Invariant 4 row cells themselves so no compact-table reading can omit it. OR promote the routing thresholds to a mechanical `gz validate --scope-expansion` check against the current diff vs. the active OBPI's ALLOWED PATHS. |
| 4 | `brief-heading-conventions.md` ("OBPI brief evidence sections MUST use H3 (`###`), not H2 (`##`)… `## Acceptance Criteria` (H2) is the canonical top-level brief section") | `tests.md` § Two runners (behave scenarios tagged by REQ) + generic brief discipline treating Acceptance Criteria as a canonical H2 | Agent authoring an OBPI brief for a heavy-lane REQ creates a `### Acceptance Criteria` section inside a per-pass evidence block and a separate `## Acceptance Criteria` at the top level. The rule file permits H3 "### ACCEPTANCE" evidence sub-sections *"which, if present, is H3"* while keeping `## Acceptance Criteria` at H2. Two sections named the same at different heading levels in the same file is exactly the heading-match ambiguity that `brief-heading-conventions.md` § Why H3, not H2 was authored to prevent (the extractor stops at the next H2 boundary and returns an empty body). The rule resolves the conflict in favor of H3 for evidence sub-blocks, but the rule itself creates the conflict by overloading the section name. | H3 per `gz validate --brief-headings` (GHI #238, row 15 of the advisory scorecard). | Rename the evidence sub-block to avoid reusing the top-level canonical heading — e.g. `### Acceptance Evidence` instead of `### ACCEPTANCE` — so the two levels are lexically distinguishable, not just structurally. |
| 5 | `cross-platform.md` § Console Output (runtime guard at `uv run gz` entrypoint handles UTF-8) | `cross-platform.md` § Scope boundary of the runtime guard (binding) ("runtime guard does not cover `python -c`, `python tools/<helper>.py`, `jq`/`awk`/`sed`…") + the "Never prefix `uv run gz` with `PYTHONUTF8=1`" local agent rule (`AGENTS.md` § Local Agent Rules) | Agent pipes `uv run gz status --json` through `python -c "import sys, json; …"` on Windows. The outer rule says "the runtime handles encoding" and the AGENTS.md rule says "never prefix `PYTHONUTF8=1`." The inner rule says the runtime guard covers only `uv run gz`, and the helper `python -c` has no reconfigure. The combined effect is: an agent obeying the headline rule and not reading the scope-boundary subsection will get `UnicodeEncodeError: 'charmap' codec can't encode character '→'` on `jq`-style pipelines — exactly the failure class that produced GHI #234. | Scope boundary wins (binding; landed under GHI #234, commit `5e8174ba`), but the headline rule and the compact AGENTS.md rule still read as categorical. | Lift the scope-boundary note into `AGENTS.md` § Local Agent Rules alongside the "never prefix PYTHONUTF8=1" line so both constraints surface in the same always-loaded paragraph. Promote to mechanical via `gz validate --utf8-helper-scripts` (scorecard row 45a marks this as **Promotable**). |
| 6 | `behavioral-invariants.md` § Process Invariant 10 ("Preserve human intent across context boundaries") + Invariant 10a ("When a skill step names a tool to invoke, invoke it in the same turn") | `behavioral-invariants.md` § Judgment Invariant 11 ("If <90% sure, ask the human") + Invariant 13 ("On inconsistencies: STOP, name confusion, present tradeoff, wait") | Agent is inside a `gz-obpi-pipeline` ceremony that names `EnterPlanMode` as the next tool, but the brief's ALLOWED PATHS appears to conflict with the plan the agent would enter. Invariant 10a says invoke `EnterPlanMode` in the same turn. Invariants 11/13 say STOP and ask the human. Both are binding; the agent cannot simultaneously "invoke the named tool in the same turn" and "STOP, wait for the human." | Unresolved — both invariants are mechanical-sounding and both live in the same rule file. In practice 10a is the more recent (GHI #227-era) addition, so recency is the weak tiebreaker, but this is not written down. | Add a precedence cell to each invariant row: "defers to Invariant 13 when an inconsistency is detected." OR codify "the invariant list is ordered — lower-numbered invariants take precedence" as an explicit preamble. Current text has no precedence rule; the conflict is latent until an agent hits it under load. |
| 7 | `defect-fix-routing.md` § Direct fix ("≤10 source lines… Scope well-bounded… ≥3 `fix(` commits in last 60 days") | `cli.md` § Adding CLI Features § New Subcommand (Heavy Lane) ("ADR or brief documenting purpose… Behave smoke test… Manpage") + `defect-fix-routing.md` § OBPI ceremony required when ANY hold ("Adds or changes a CLI surface, schema, public contract, or runtime invariant") | Agent notices that `gz validate` help text uses "run" singular instead of plural "gz chores run." Fix is 1 line; it is a user-facing CLI string. `defect-fix-routing.md` § Direct fix triggers on size (≤10 lines) AND precedent (e.g. GHI #189 itself, which is listed in the rule's "baseline precedent" as a 1-line fix). `cli.md` § Adding CLI Features calls any CLI subcommand/flag/output change Heavy-lane. Pre-GHI #189 the rule's "CLI surface" clause in the OBPI-ceremony trigger would have read as mandating ceremony. The rule file itself names GHI #189 as direct-fix precedent, but `cli.md` does not qualify "CLI contract change" to exclude trivial string fixes. | `defect-fix-routing.md` wins by precedent citation, but a fresh agent reading `cli.md` in isolation would route to ceremony. | Add a cross-reference in `cli.md` § Adding CLI Features: "Trivial string/help-text fixes are direct-fix per `defect-fix-routing.md`; schema/contract changes trigger Heavy lane." Worked examples on both sides of the boundary (GHI #189 direct-fix vs GHI #194 contract fix) would close the ambiguity. |
| 8 | `arb.md` § When to Use ARB ("Mandatory for filing a defect GHI with QA evidence") | `tests.md` § Red-Green-Refactor § RED evidence ("Do not author ARB receipts with `exit_status=1` as 'RED receipts' — ARB treats intentional REDs as defects") + `attestation-enrichment.md` § Anti-patterns ("Authoring `arb-step-*` receipts with `exit_status=1` as 'RED receipts' — pollutes the ARB corpus") | Agent runs a failing unit test while diagnosing GHI #253 (test performance). The failing run is deterministic evidence of a defect. `arb.md` says filing a defect GHI with QA evidence → Mandatory ARB receipt. `tests.md` § RED evidence and `attestation-enrichment.md` § Anti-patterns forbid authoring `arb-step-*` receipts with exit_status=1 as RED receipts. The agent cannot simultaneously (a) use ARB to capture the failing-test receipt as defect evidence for the GHI, and (b) avoid authoring an exit_status=1 `arb-step-*` receipt. | Unresolved as written. In practice agents paste observed-output into the GHI body (per `tests.md` § RED evidence) and do not author the ARB receipt — but this means the "Mandatory" cell of `arb.md` is effectively warned-only for failing-test defect GHIs. | Split `arb.md`'s "defect GHI with QA evidence" row by failure mode: **passing QA** runs (lint violation, type error that resolves to clean after fix) → ARB receipt Mandatory; **failing QA** runs that capture a RED test → paste observed output into the GHI body per `tests.md` § RED evidence. Track under the RED/GREEN receipt stream (`ADR-pool.tdd-receipt-stream`, GHI #157) when that lands, but fix the rule-text ambiguity now. |
| 9 | `skill-surface-sync.md` § Non-negotiable rules ("Never edit vendor mirrors directly") | `skill-surface-sync.md` § Conflict resolution § Resolution rules ("Mirror version > canonical version: An agent edited the mirror directly. Promote the mirror content to canonical… The higher version wins") | Agent finds `.claude/rules/tests.md` has `skill-version: 6.2.0` and `.gzkit/rules/tests.md` has `skill-version: 6.1.0`. The Non-negotiable rules section says "never edit vendor mirrors directly" (the edit should never have happened). The Conflict resolution section says the higher version wins — promote the mirror content to canonical. The rule simultaneously forbids the state and prescribes the recovery. | Conflict-resolution wins (this is explicit recovery text), but an agent applying the Non-negotiable rules in isolation would refuse to promote and re-open the drift. | Re-word § Non-negotiable rule 4 to: "Never edit vendor mirrors directly as a first-class action. If you discover a mirror-only edit from a prior session, apply § Conflict resolution." OR make the conflict-resolution section the canonical statement and demote rule 4 to a derived guideline. |
| 10 | `tool-skill-runbook-alignment.md` § Commit-message discipline for skill-routing changes ("commit body MUST include… Observed-output evidence OR Test reference") | `tests.md` § Red-Green-Refactor ("Tests assert semantics, not strings") | Agent modifies `gz adr status` rendering and must cite evidence per the commit-discipline rule. Option 1 (paste observed ASCII-art table into commit body) is fine. Option 2 (cite a test) is the conflict: the named test example (`test_adr_status_renders_shared_table_via_deterministic_renderer`) asserts on string markers — exactly what `tests.md` says a test should not do. An agent trying to satisfy both constraints must either skip the test-reference option (making Option 1 the only path) or author a test that violates `tests.md`. GHI #227 relocated Invariant 6f to `tests.md` specifically to stop this class of drift, but the cross-rule citation in `tool-skill-runbook-alignment.md` still points at a string-asserting test. | Unresolved — the two options in the commit-discipline rule have asymmetric compliance cost under `tests.md`. Option 1 (observed output in commit body) is the only clean path. | Amend `tool-skill-runbook-alignment.md` § Commit-message discipline Option 2 to require the test cite the skill's Output Contract REQ and assert table-form via the output-form fixture (row 1 of this matrix), not against raw bytes. Track with the same GHI that splits unit tests from the output-form fixture. |
| 11 | `governance-core.md` § Non-negotiable rules ("Do not bypass Gate 5 when lane requirements require human attestation") + `AGENTS.md` § OBPI Acceptance Protocol ("Agent MUST NOT mark an OBPI brief as Completed without explicit human attestation when the parent ADR lane is heavy") | `AGENTS.md` § OBPI Acceptance Protocol § Lane Inheritance Rule ("lite: May be self-closeable after evidence is presented") + § Foundation-kind rigor ("A lite-lane foundation ADR is still self-closeable at the brief level, but ADR closeout follows the foundation walkthrough protocol") | Agent has a Lite-lane foundation-kind OBPI (e.g. OBPI-0.0.18-01). The governance-core "do not bypass Gate 5 when lane requirements require" is satisfied (lane is lite). OBPI Acceptance Protocol's compact rule says "heavy lane → Gate 5 required" (read contrapositively: lite → not required). Lane Inheritance Rule confirms "lite: May be self-closeable." The Foundation-kind rigor subsection says the OBPI is self-closeable *at the brief level* but ADR closeout follows the foundation walkthrough. A time-pressured agent reads the compact cells, self-closes at the brief, and then proceeds to ADR closeout without the walkthrough — the foundation-kind rigor note was in a separate subsection and not surfaced at the decision point. | Foundation-kind rigor wins (binding subsection) but the compact lane table overshadows it. | Add a third column to the Lane Inheritance Rule table: "Kind." Row for `lite` + `foundation` explicitly reads: "Brief: self-closeable. ADR: walkthrough required." Eliminate the separate subsection. |
| 12 | `cross-platform.md` § Subprocess (List Form) + `tests.md` § Unit-tier contract (binding) ("Mock every subprocess boundary it touches… < 200ms… no real git") | `tests.md` § End-to-end coverage lives in behave ("Tests that touch real git state… may use `_init_git_repo` from the common helper — a deliberate exception where real git history is load-bearing") | Agent writing a test for `gz adr emit-receipt` needs real git history to verify trailer-parsing. Unit-tier contract says mock every subprocess; end-to-end section and the exception clause permit `_init_git_repo`. Which tier does the test belong to? If it lands under `tests/` it is under the unit-tier contract; if it lands under `features/` it is BDD and pays the behave cost. The exception clause ("deliberate exception") has no mechanical threshold — any test can claim "git history is load-bearing" and escape the mock rule. | Unit-tier contract wins by default (the exception is discretionary), but `tests/commands/test_closeout_ceremony.py` uses `_init_git_repo` today without a documented justification comment — silent drift. | Require an inline justification comment format at every `_init_git_repo` callsite: `# real-git-required: <REQ-ID> — <one-line reason>`. Promote to mechanical via an AST scan that fails-closed if the call lacks the comment. Would attach to GHI #209 (test-tiers audit) as an extension. |

---

## Pairs surveyed and rejected (no concrete worked example)

The following plausible-overlap pairs were walked and discarded because
rule A and rule B do not prescribe opposing behavior on a shared input:

- `pythonic.md` vs `models.md` — both agree on Pydantic/type-hint form;
  models.md is the data-model-specific refinement of pythonic.md's general
  principle 7.
- `gh-cli.md` § Prohibited vs `AGENTS.md` § Behavior Rules § Never — both
  forbid force-push, unauthorized merges, secret mutations. Same direction.
- `chores.md` § Plan-first vs `defect-fix-routing.md` § Decision protocol —
  chores and defect-fix are disjoint workflows (chores don't surface defects
  the way in-flight OBPI work does).
- `adr-audit.md` § Audit sequence vs `governance-core.md` § Required
  workflow order — both define sequential gates and cite each other; they
  overlap but do not contradict on the same input.
- `cli.md` § Exit Codes vs `arb.md` § Exit Codes — arb uses 0/1/2; cli uses
  0/1/2/3. Different scopes; arb receipts are wrapped QA exit codes, cli
  exit codes are the top-level CLI. No collision on a shared input.
- `constraints.md` (all sections) vs the source rules it mirrors — by
  construction `constraints.md` is a compact mirror and any conflict with
  a source rule would be a sync defect, not a rule-pair contradiction.
  Noted but not matrix-fodder.
- `brief-heading-conventions.md` vs `gate5-runbook-code-covenant.md` — one
  governs OBPI brief headings, the other governs user-runbook headings.
  Disjoint path globs (`docs/design/adr/**/obpis/**` vs `docs/**` minus
  adr).
- `cross-platform.md` § File Paths vs `pythonic.md` § Core Principles 5
  (Context managers) — both reinforce `pathlib.Path` + `with tempfile…`;
  same direction.
- `AGENTS.md` § Prime Directive § SCOPE EXPANSION IS NOT SCOPE CREEP vs
  `constraints.md` § Pipeline Lifecycle § "Do not bundle two briefs' worth
  of work" — the scope-boundary subsection in `behavioral-invariants.md`
  already resolves this (captured as row 3 of the matrix above; not
  double-counted).
