# Plan — OBPI-0.0.22-06-rule-file-matrix-scorecard

**Parent ADR:** ADR-0.0.22-security-sensitivity-doctrine
**Lane:** Heavy
**Kind:** foundation
**Status:** Draft

## Context

OBPIs 01–05 of ADR-0.0.22 are all `attested_completed`. They have already
landed:

- the `sensitivity` schema field (OBPI-01)
- `data/security_surfaces.json` registry (OBPI-02)
- `gz validate --sensitivity` scope (OBPI-03)
- `_requires_security_review_attestation` OR-branch into
  `_requires_human_obpi_attestation` (OBPI-04)
- Gate 5 walkthrough extension + ARB canonical command slot reservation
  (OBPI-05)

OBPI-06 is the documentation-and-surface-binding closeout for the doctrine.
Every mechanical surface exists; this brief authors the canonical rule file,
confirms the AGENTS.md matrix is final and cites the rule, lands the advisory
scorecard entry, and propagates to vendor mirrors.

## Destination-in-mind

I had already formed the conclusion that the rule file is a thin canonical
authorship surface that names the invariants and points at the existing
mechanical enforcers (`gz validate --sensitivity`, the surface registry, the
audit OR-branch, the walkthrough). The rule file does not invent new doctrine
— it consolidates what OBPIs 01–05 already shipped into a single addressable
location. The plan is therefore documentation-bounded: no schema changes, no
validator changes, no audit changes. Just the rule, the matrix citation, the
scorecard row, the mirror sync, and the existing-test sweep.

## Rejected alternatives

1. **Re-asserting the matrix in the rule file body.** Considered but rejected
   — the matrix is the AGENTS.md projection; duplicating it in the rule file
   would create the doctrine-drift surface this entire ADR exists to close
   (a doctrine matrix in two places is doctrine drift waiting to happen).
   The rule file *cites* the matrix; AGENTS.md is the single source of
   projection truth.
2. **Adding a new `gz validate --rule-version` scope to enforce the body
   marker.** Considered but rejected — out of scope per REQ-08, and rule
   versioning is already covered by `skill-surface-sync.md` discipline.
3. **Editing `src/gzkit/sync_surfaces.py` to add a security-sensitivity
   special case.** Considered and confirmed unnecessary — `load_rules` /
   `render_rules_to_dir` are generic over any `.gzkit/rules/*.md`, so the
   new file flows through sync without code change.
4. **Extending `src/gzkit/templates/agents.md` with a citation that does not
   exist in the rendered AGENTS.md.** Rejected because AGENTS.md is the
   authoritative surface for the matrix today; the template is what produces
   AGENTS.md on regeneration. Both must agree. The plan updates both.

## Files

**Authored (new):**

- `.gzkit/rules/security-sensitivity.md` — canonical rule file (REQ-01, REQ-02)
- `tests/governance/test_security_sensitivity_rule.py` — REQ-derived
  fixture tests for rule existence, frontmatter validity, body marker,
  required sections (REQ-01, REQ-02 coverage)

**Modified:**

- `AGENTS.md` — append a one-line citation to `.gzkit/rules/security-sensitivity.md`
  in the matrix section (REQ-03)
- `src/gzkit/templates/agents.md` — same citation in the template source so
  the next regeneration of AGENTS.md preserves it (REQ-03 mirror)
- `docs/governance/advisory-rules-audit.md` — add a scorecard entry for
  `security-sensitivity.md` classified **Mechanical**, citing
  `gz validate --sensitivity` (REQ-04)

**Generated by `gz agent sync control-surfaces` (no hand edits):**

- `.claude/rules/security-sensitivity.md` (REQ-05)
- `.agents/rules/security-sensitivity.md` (REQ-05, conditional on surface
  existence — currently empty in this repo, so possibly a no-op)
- `.github/instructions/security_sensitivity.instructions.md` (REQ-05)

## Steps

### Step 1 — Author canonical rule file (REQ-01, REQ-02)

Write `.gzkit/rules/security-sensitivity.md` with:

- YAML frontmatter conforming to `RuleFrontmatter`:
  - `id: security-sensitivity`
  - `paths`: glob list covering brief frontmatter surfaces
    (`docs/design/adr/**/obpis/**`, `docs/design/adr/**/briefs/**`) and the
    registry (`data/security_surfaces.json`)
  - `description`: one line citing `gz validate --sensitivity`
- Body-level `<!-- rule-version: 0.1.0 -->` marker immediately after
  frontmatter
- Visible `> **Rule version:** \`0.1.0\`` block quote with rationale
  per `skill-surface-sync.md`
- Sections (each with explicit `### …` heading):
  1. **Invariant** — security work needs heightened review regardless of
     lane or kind
  2. **Registry contract** — `data/security_surfaces.json` is
     self-bootstrapping; edits require `sensitivity: security`; cite OBPI-02
  3. **`gz validate --sensitivity` scope behavior** — auto-detect floor +
     escalate-not-escape; cite the validator at trust_audits.py
  4. **Heightened walkthrough enumeration** — what `gz obpi complete` adds
     for security-sensitivity briefs; cite OBPI-05
  5. **Scanner-unavailable failure mode** — fail-closed; cite the canonical
     ARB step slot
  6. **Anti-patterns** — escape attempts, registry edits without
     `sensitivity: security`, narrative-only attestation in lieu of receipt
  7. **Related** — links to ADR-0.0.22, AGENTS.md matrix, scorecard,
     `attestation-enrichment.md`, `gh-cli.md`

### Step 2 — Add Red-derived tests (Gate 2 TDD)

Author `tests/governance/test_security_sensitivity_rule.py` with one
`unittest.TestCase` carrying these REQ-derived assertions decorated with
`@covers`:

- `@covers REQ-0.0.22-06-01`:
  `test_rule_file_exists_with_valid_frontmatter` — load via
  `load_rules`; assert frontmatter id, paths non-empty, description present
- `@covers REQ-0.0.22-06-01`:
  `test_rule_body_carries_version_marker_and_block_quote` — body contains
  `<!-- rule-version: 0.1.0 -->` and `> **Rule version:** \`0.1.0\``
- `@covers REQ-0.0.22-06-02`:
  `test_rule_body_documents_required_sections` — assert each labelled
  section heading is present (Invariant / Registry / `gz validate
  --sensitivity` / walkthrough / scanner-unavailable)

Run them RED (file does not exist yet) → GREEN (file landed by Step 1) →
record observation in evidence. Then proceed to remaining steps.

### Step 3 — AGENTS.md citation (REQ-03)

Append a citation line to AGENTS.md § Lane & Kind & Sensitivity Attestation
Matrix (immediately after the Mechanical-enforcement paragraph at line 312)
naming `.gzkit/rules/security-sensitivity.md` as the rule-file home for the
sensitivity axis. Update the same paragraph in
`src/gzkit/templates/agents.md` so the next regeneration is idempotent.

### Step 4 — Advisory scorecard entry (REQ-04)

Add a new section to `docs/governance/advisory-rules-audit.md`:

```text
### Security Sensitivity (`.gzkit/rules/security-sensitivity.md`)

| # | Rule | Score | Notes |
|---|------|-------|-------|
| <next-rule-number> | Security-sensitivity floor + escalate-not-escape | **Mechanical** | Enforced by `gz validate --sensitivity` (ADR-0.0.22 / OBPIs 01–05) — registry at `data/security_surfaces.json`; audit OR-branch at `_requires_security_review_attestation`; Gate 5 walkthrough fires for `sensitivity: security` briefs |
```

Update the **Summary** counts (Mechanical floor) and add a **Recommended
promotion order** row pointing at ADR-0.0.22 as the landing reference.

### Step 5 — Sync mirrors (REQ-05)

Run `uv run gz agent sync control-surfaces`. Verify the generated mirror files
exist and carry the same body-level `<!-- rule-version: 0.1.0 -->` marker. No
warnings expected.

### Step 6 — Validation sweep (REQ-06, REQ-07)

Run the full Heavy-lane Gate 2/Gate 3 set, ARB-wrapped per Stage 3 contract:

- `uv run gz arb ruff` (lint)
- `uv run gz arb typecheck` (type check)
- `uv run gz arb step --name unittest -- uv run -m unittest -q` (full unittest)
- `uv run gz arb step --name unittest -- uv run -m unittest tests.governance.test_security_sensitivity_rule -v` (REQ-scoped)
- `uv run gz arb step --name mkdocs -- uv run mkdocs build --strict`
- `uv run gz validate --documents`
- `uv run gz validate --advisory-scorecard`
- `uv run gz validate --sensitivity`

REQ-07 (foundation-kind closeout walkthrough) is enforced by
`gz obpi complete` at Stage 5 — no additional plan step needed; the
walkthrough fires automatically because the parent ADR matches
`^ADR-0\.0\.\d+`.

### Step 7 — Stage 4 ceremony + Stage 5 sync

Stage 4 presents evidence to the operator. Stage 5 runs `gz obpi complete
--attestor-present` (since the pipeline marker is active), reconciles, and
git-syncs.

## Verification

```bash
# REQ-01, REQ-02
test -f .gzkit/rules/security-sensitivity.md
uv run -m unittest tests.governance.test_security_sensitivity_rule -v

# REQ-03
rg -n 'security-sensitivity' AGENTS.md src/gzkit/templates/agents.md

# REQ-04
rg -n 'security-sensitivity' docs/governance/advisory-rules-audit.md

# REQ-05
test -f .claude/rules/security-sensitivity.md
test -f .github/instructions/security_sensitivity.instructions.md

# REQ-06
uv run gz validate --documents
uv run gz validate --advisory-scorecard

# REQ-07: foundation-kind walkthrough fires automatically at gz obpi complete
```

## Notes

- Brief is **Heavy + foundation-kind** ⇒ Stage 5 attestation is required (the
  three-axis matrix' top-left cells); operator already attested in Stage 4.
- Pipeline runtime should pass `--attestor-present` because Stage 1 will write
  the per-OBPI active marker.
- The advisory-scorecard self-test (`gz validate --advisory-scorecard`) will
  fail until Step 4 lands — author tests for the rule file in Step 2 with the
  rule file landing in Step 1, then add the scorecard row before any cross-
  cutting validators run.
- `tests/governance/test_security_sensitivity_rule.py` lives alongside other
  governance fixtures and uses standard `unittest.TestCase` with
  `tempfile`-free reads (loads the canonical file in-place).

## Coverage rationale

| REQ | Plan step(s) | Test |
|-----|-------------|------|
| REQ-0.0.22-06-01 | Step 1, Step 2 | `test_rule_file_exists_with_valid_frontmatter`, `test_rule_body_carries_version_marker_and_block_quote` |
| REQ-0.0.22-06-02 | Step 1, Step 2 | `test_rule_body_documents_required_sections` |
| REQ-0.0.22-06-03 | Step 3 | covered by `audit_advisory_scorecard` self-test (existing) + `gz validate --documents`; manual rg verification |
| REQ-0.0.22-06-04 | Step 4 | `audit_advisory_scorecard` flips from clean → still-clean (existing test) once the row lands |
| REQ-0.0.22-06-05 | Step 5 | `gz agent sync control-surfaces` exits clean; mirror files exist post-sync |
| REQ-0.0.22-06-06 | Step 6 | full validate sweep |
| REQ-0.0.22-06-07 | Step 7 | enforced by Stage 5 runtime; pipeline marker verifies it ran |
| REQ-0.0.22-06-08 | (whole plan) | Allowed-paths scope check; no edits to schema/registry/validator/audit/walkthrough/ARB |
