# Plan: OBPI-0.0.27-01-selection-methodology

**OBPI:** OBPI-0.0.27-01-selection-methodology
**Parent ADR:** ADR-0.0.27 (foundation, heavy lane)
**Objective:** Author `.gzkit/rules/complexity-doctrine.md` codifying the seven selection criteria, seven corpus anti-patterns, distillation cadence triggers, citation contract, and project-doctrine-fitness criterion as a Mechanical-class rule, with the corresponding scorecard entry in `docs/governance/advisory-rules-audit.md`.

## Allowed Files

- `.gzkit/rules/complexity-doctrine.md` — new canonical rule file
- `.claude/rules/complexity-doctrine.md` — vendor mirror (generated by sync)
- `.agents/rules/complexity-doctrine.md` — vendor mirror (generated by sync)
- `.github/instructions/complexity-doctrine.md` — vendor mirror (generated by sync)
- `docs/governance/advisory-rules-audit.md` — scorecard entry
- `tests/governance/test_complexity_doctrine_rule.py` — REQ-derived tests
- `data/behave_coverage_waivers.json` — BDD waiver registration (note: not in brief Allowed Paths — administrative gap; proceeding per Gate 4 requirement)
- `docs/design/adr/foundation/ADR-0.0.27-exemplar-corpus-doctrine/**` — brief evidence updates only

## Context

### Key constraints from rules.py (RuleFrontmatter schema)
- Required frontmatter fields: `id` (kebab-case str), `paths` (list[str], min_length=1), `description` (str)
- `extra="forbid"` — do NOT add `skill-version:` to rule frontmatter
- Body-level version marker: `<!-- rule-version: 0.1.0 -->` HTML comment + `> **Rule version:** \`0.1.0\`` block quote

### Seven selection criteria (from ADR-0.0.27 § Decision — verbatim)
1. Longevity: ≥ 5 years active development OR explicitly archived as a reference
2. Maintenance health: active releases in last 12 months OR project explicitly declares done state
3. Practitioner reputation: cited in PEPs, in published reference works (Fluent Python, Effective Python, Architecture Patterns with Python), OR by recurring conference talks (PyCon, EuroPython, PyData). Specifically NOT by GitHub-star count.
4. Pure-Python predominance: Python content is the primary artifact (≥ 80% of LOC). Excludes thin wrappers around C/Rust where the Python part is glue.
5. Author craftsmanship signal: maintainer history shows design discipline (PEP authorship, well-known design talks, mentorship reputation). The most subjective criterion; mitigated by the agent-drafted-then-operator-audited pattern.
6. Project doctrine fitness: the project does not violate gzkit's existing doctrinal commitments. A project whose foundational design choices contradict Stdlib-First or other gzkit canon is excluded regardless of other strengths. The pytest-mention demerit during this session's design dialogue was the canonical failure this criterion closes.
7. Pinned to a specific commit SHA at corpus-authoring time — distributions are reproducible from the SHA.

### Seven corpus anti-patterns (from ADR-0.0.27 § Decision — verbatim)
1. Selecting projects that confirm a pre-decided threshold (post-hoc fitting)
2. Selecting by GitHub-star count (popularity ≠ design quality)
3. Selecting only modern projects (loses the 'test of time' signal)
4. Selecting only legacy projects (misses current best-practice idioms)
5. Selecting projects all from the same domain (monoculture; over-fits to one idiom)
6. Agent supplying the project list from training memory without operator audit (the corpus is doctrine and must be operator-witnessed)
7. Including any project that violates gzkit's existing doctrinal commitments (project doctrine fitness)

### Cadence triggers (from ADR-0.0.27 § Decision)
- Annual calendar default
- Signal trigger: advisor verdict-frequency drift > 25% from baseline, with minimum re-distillation interval of 6 months
- Judgment trigger: operator ad-hoc when a ground-breaking project emerges

### Citation contract
- Downstream ADRs cite distilled-characteristics document (file path + section anchor + corpus revision number)
- Raw distributions and corpus NOT cited directly

## Steps

### Step 1: Update brief Allowed Paths (administrative fix)
Add `data/behave_coverage_waivers.json` to the OBPI-0.0.27-01 brief Allowed Paths section to match the Gate 4 requirement.

### Step 2: Write failing tests — TDD RED phase
Create `tests/governance/test_complexity_doctrine_rule.py` with tests for all 12 REQs:
- REQ-0.0.27-01-01: rule file loads and frontmatter validates (id, paths, description); body-level rule-version marker matches visible block quote
- REQ-0.0.27-01-02: all seven selection criteria present in rule body (named with their key terms)
- REQ-0.0.27-01-03: all seven corpus anti-patterns present in rule body
- REQ-0.0.27-01-04: three cadence triggers present (annual, drift > 25%, judgment) with 6-month minimum guard
- REQ-0.0.27-01-05: citation contract present — names "distilled-characteristics" as cited artifact, excludes raw distributions / corpus from direct citation
- REQ-0.0.27-01-06: project-doctrine-fitness criterion present, pytest-mention demerit lesson cited
- REQ-0.0.27-01-07: advisory-scorecard validates (exit 0) with complexity-doctrine entry classified Mechanical
- REQ-0.0.27-01-08: agent sync propagation (all four vendor mirrors carry identical rule content)

Each test decorated with `@covers("REQ-0.0.27-01-NN")`. Tests use file-system-loaded rule content, not pinned strings.

### Step 3: Write `.gzkit/rules/complexity-doctrine.md` — TDD GREEN phase
Craft the rule file to pass all tests:
- YAML frontmatter: id, paths (scoped to complexity-adjacent surfaces), description
- Body-level version markers (HTML comment + visible block quote)
- Structured sections for: selection criteria (7), anti-patterns (7), cadence triggers (3 + 6-month minimum), citation contract, project-doctrine-fitness criterion with pytest-mention demerit lesson

### Step 4: Update scorecard in `docs/governance/advisory-rules-audit.md`
Add new section after the last rule section:

```
### Exemplar Corpus Doctrine (`.gzkit/rules/complexity-doctrine.md`)

| # | Rule | Score | Why |
|---|------|-------|-----|
| 50 | <rule text> | **Mechanical** | Enforced by <citation to ADR-0.0.27 and OBPI-0.0.27-07> |
```

Update Summary table: Mechanical count 35 → 36, Total 59 → 60, recalculate %.

### Step 5: Register BDD waiver in `data/behave_coverage_waivers.json`
Add to `waivers` dict:
```json
"OBPI-0.0.27-01-selection-methodology": {
  "rationale": "adr-0.0.27-foundation-bdd-deferred",
  "status_at_landing": "Draft"
}
```
Add named rationale to `default_rationale`:
```json
"adr-0.0.27-foundation-bdd-deferred": "ADR-0.0.27 (exemplar-corpus-doctrine) is foundation/heavy-kind doctrine codifying the corpus selection methodology. OBPI-01 is a rule-only OBPI with no CLI surface to scenario-test; verification is via Python unit tests asserting rule-body content. BDD coverage for the CLI surface (gz validate --complexity-doctrine-links) is deferred to OBPI-07 (link-integrity validator), which provides the scenario-testable surface. Same pattern as ADR-0.0.22 foundation-bdd-deferred."
```

### Step 6: Run `uv run gz agent sync control-surfaces`
Propagate `.gzkit/rules/complexity-doctrine.md` to all four vendor mirrors. Verify post-sync diff is empty.

### Step 7: Validate
```bash
uv run gz lint
uv run gz typecheck
uv run gz test
uv run gz validate --advisory-scorecard
uv run gz validate --documents --surfaces
uv run gz agent sync control-surfaces  # diff must be empty
uv run gz arb step --name unittest -- uv run -m unittest tests/governance/test_complexity_doctrine_rule.py -v
```

## Verification

```bash
uv run gz lint
uv run gz typecheck
uv run gz test
uv run gz validate --advisory-scorecard
uv run gz validate --documents --surfaces
uv run gz agent sync control-surfaces
uv run gz arb step --name unittest -- uv run -m unittest tests/governance/test_complexity_doctrine_rule.py -v
```

## Notes

- Rule 50 is the next available rule number in the scorecard (current highest: 49)
- Scope collision advisory: `tests/governance/test_complexity_doctrine_rule.py` is contested by many sibling OBPIs whose test files also share the `tests/governance/` directory — this is expected for governance-layer tests and is advisory-only, not blocking
- `data/behave_coverage_waivers.json` administrative gap flagged in alignment audit; resolved by Step 1 (brief edit) and Step 5 (waiver registration)
