# ADR-0001 — Pass-naming grammar lives in `core/pass_naming.py`

**Status:** Accepted
**Date:** 2026-05-03
**Deciders:** JT, Claude (in dialogue)
**Supersedes:** none
**Superseded by:** none

> *This ADR was drafted by AI during the post-Phase-D cleanup conversation.*

## Context

The `PASS_PATTERN` regex and `parse_pass_filename` function (the pass-filename grammar) live behind a few different surfaces in the engine. Their history:

- **Pre-Phase-D:** body lived in `workspace/tree.py`. Outside callers (`pipeline/tools/migrate_pass_names.py`, `tests/test_pass_naming.py`) reached the symbols by importing private aliases (`_PASS_PATTERN`, `_parse_pass_filename`) from `workspace/server.py`. Phase 1 of the engine audit flagged this as a Tenet 5 boundaries violation — outside callers reaching across the package boundary into private symbols.
- **Phase D — Phase 6:** introduced `workspace/pass_naming.py` as a re-export shim. Public surface for outside callers; body kept at `workspace/tree.py` per Phase D's "promotion is a rename, not a refactor" scope discipline (a harness-execution convenience, not an architectural law). Outside callers shifted to `from workspace.pass_naming import PASS_PATTERN`.
- **Phase D Phase 9 Gate 4:** strict reading of "no upward layer-skipping imports" flagged the surviving `pipeline.tools.migrate_pass_names → workspace.pass_naming` import edge.

The Phase D handoff queued this as an open decision: leave at `workspace/` and amend Gate 4, or relocate to `core/`. The "2-3 line clean relocate" framing in `MORNING_STATUS.md` was wrong — moving just the shim would have introduced the first `core → workspace` edge in the codebase (the shim re-imports from `workspace.tree`), strictly worsening layering.

## Decision

The pass-filename grammar's canonical home is `recoil/core/pass_naming.py`.

- The body of `PASS_PATTERN` and `parse_pass_filename` lives in `core/pass_naming.py` and nowhere else.
- `workspace/tree.py` re-imports both symbols for its internal callers (`PASS_PATTERN.match` at line 104, `parse_pass_filename` at line 485) and keeps them in its `__all__` as backwards-compat re-exports.
- The shim at `workspace/pass_naming.py` is deleted. Its only outside caller (`pipeline/tools/migrate_pass_names.py:46`) imports from `core.pass_naming` directly.

`recoil/core/` is foundational — no `workspace`, `pipeline`, or `execution` dependencies. The pass-filename regex is exactly the kind of layer-foundational utility that belongs there.

## Consequences

**Layer hygiene preserved.** The codebase has zero `core → workspace` imports both before and after this change. `workspace → core` (9 occurrences) and `pipeline → core` (many) remain the canonical directions. Phase 9 Gate 4 stays strict — no amendment needed.

**Single source of truth.** `core.pass_naming.PASS_PATTERN is workspace.tree.PASS_PATTERN` (identity equality verified post-relocate). One canonical compiled regex, no risk of drift across modules.

**Test baseline preserved.** 1280 passed / 11 skipped — byte-identical to Phase E's converged baseline.

**Phase D's "promotion is a rename, not a refactor" rule was relaxed for this work.** That rule was a Phase D scope-discipline rule (mixing rename-only and body-moving work in the same harness phase makes debug-round attribution noisy), not an architectural law. Doing the body-move outside Phase D's harness, with JT awake and a 5-minute pytest verification, is fine.

**Out of scope:** the broken-pytest-collection issue at `recoil/tests/test_pass_naming.py` is unrelated and pre-existing (sys.path / namespace shadowing — see `BACKLOG.md` lib-collision entry).

## Implementation

- **Created:** `recoil/core/pass_naming.py` (~58 lines, body + module docstring)
- **Modified:** `recoil/workspace/tree.py` — body stripped (-32 lines), 1-line re-import added
- **Modified:** `recoil/pipeline/tools/migrate_pass_names.py:46` — import path changed
- **Deleted:** `recoil/workspace/pass_naming.py`

## Verification

```
$ PYTHONPATH=. python3 -c "from core.pass_naming import PASS_PATTERN; from workspace.tree import PASS_PATTERN as W; assert PASS_PATTERN is W"
$ PYTHONPATH=. python3 -m pytest -q
1280 passed, 11 skipped, 4 warnings in 8.34s
```
