---
schema_version: 1
id: RAC-KTQAAPF5ZDZX
type: roadmap
---
# RAC v0.7.14 — Audit Hardening

## Status

Planned

## Context

A full technical audit of the repository (2026-06-09) preceded v0.8.x scoping.
The product code is healthy: clean CLI → services → core layering with no
upward or circular imports, a deterministic core, full type-hint coverage,
zero security findings, and accurate documentation. Every gap found lives in
the trust infrastructure rather than the product:

- Eight of thirty test files (~1,306 lines, 21.6% of the suite) are missing
  from the CI battery matrix in `.github/workflows/tests.yml` and never run
  in CI: `test_create.py`, `test_resolve.py`, `test_migrate.py`,
  `test_init.py`, `test_frontmatter.py`, `test_metadata_identity.py`,
  `test_templates.py`, `test_idgen.py`. This contradicts the matrix's own
  "no orphans" comment and leaves the only two file-mutating commands
  (`rac new`, `rac migrate`) without any CI coverage.
- No lint, format, or type-check gate exists anywhere, although the codebase
  is fully type-hinted and would pass such gates today with minimal change.
- The walk → parse → classify loop is duplicated near-identically in six
  services (validate, portfolio, inspect, stats, relationships, index, plus
  migrate). v0.8.0's service API and the Explorer would multiply its
  consumers.
- A `.DS_Store` file is committed despite being ignored, and no coverage
  signal exists to catch gaps like the orphaned batteries.

v0.8.x builds an interactive Explorer on top of the service layer. These gaps
close first: gates that do not run cannot gate, and the corpus traversal seam
should exist once — in core — before the Explorer consumes it.

## Outcomes

- Every `tests/test_*.py` provably runs in CI, enforced by a meta-test
  rather than a comment.
- CI fails on lint, format, and type errors.
- Repository corpus traversal exists exactly once, in core, and every
  repository command consumes it.
- Test coverage is visible on every CI run.
- The repository contains no committed junk files.
- ADR-027's trigger policy is re-priced now that the full suite actually
  runs post-merge, and reaffirmed or amended deliberately.

## Initiatives

### Initiative 1 — CI Battery Integrity

Restore the eight orphaned test files to the battery matrix, one battery per
service (ADR-027):

- `create`: `tests/test_create.py tests/test_templates.py`
- `init`: `tests/test_init.py`
- `resolve`: `tests/test_resolve.py`
- `migrate`: `tests/test_migrate.py`
- `core` (extended): `tests/test_frontmatter.py`,
  `tests/test_metadata_identity.py`, `tests/test_idgen.py`

Add `tests/test_ci_batteries.py`: parse `tests.yml` with `yaml.safe_load`
(PyYAML is already a core dependency), collect every battery path, glob
`tests/test_*.py`, and assert set equality. The meta-test joins the `core`
battery, so the guard guards itself. Any rot discovered in the eight
unexercised files is repaired inside this initiative.

### Initiative 2 — Static Quality Gates

Adopt ruff (lint plus format) and mypy:

- `[tool.ruff]` and `[tool.mypy]` configured in `pyproject.toml`,
  targeting Python 3.11.
- A lint job added to `tests.yml` (single Python version) that gates CI
  alongside the batteries: `ruff check`, `ruff format --check`, `mypy src/`.
- Golden tests pin output as contract: this release regenerates no golden
  file. Formatting that would rewrap user-visible strings is excluded
  rather than accepted.

### Initiative 3 — Canonical Corpus Traversal

Extract the walk → parse → classify loop into core, where deterministic
analysis lives (ADR-015). Representative interface:

    walk_corpus(directory, recursive=True) -> Iterator[CorpusEntry]

where `CorpusEntry` carries the path, parsed `Product`, and classified
artifact type. The helper composes only existing core functions
(`find_markdown_files`, `parse_file`, `classify`) and iterates lazily so
memory behavior is unchanged.

Migrate the six duplicated service loops one service per commit, running
that service's battery plus the golden battery after each. This is the
traversal seam that v0.8.0's service API stabilizes.

### Initiative 4 — Hygiene and Visibility

- Remove the committed `.DS_Store` (`git rm --cached`); `.gitignore`
  already prevents recurrence.
- Add `pytest-cov` to the dev extras and report coverage in CI. No
  threshold gate this release: measure first, gate later.
- Amend or reaffirm ADR-027 with the battery gap repaired: decide whether
  lint plus a smoke battery should also run on pull requests, or whether
  main-only remains the priced trade-off.

## Implementation Contract

The following decisions are pinned for v0.7.14.

### Battery assignments

Exactly the assignments listed in Initiative 1; every current and future
`tests/test_*.py` must belong to exactly one battery, enforced by
`tests/test_ci_batteries.py`.

### Tooling baselines

- ruff rule families to start: `E, F, W, I, UP, B`; violations fixed
  mechanically, suppressions used only where a fix would change behavior.
- mypy: `disallow_untyped_defs` for `src/`; tests exempt this release.

### Output stability

All 24 golden files remain byte-identical through every initiative. A
golden diff in any commit of this release is a defect, not an update.

## Success Measures

- Removing any battery entry from `tests.yml` fails the test suite via the
  meta-test.
- All thirty test files appear in CI logs across Python 3.11–3.13.
- `ruff check`, `ruff format --check`, and `mypy src/` pass locally and
  gate CI.
- `git ls-files` reports no `.DS_Store`.
- The parse-and-classify loop has one definition; six services import it.
- Golden files are byte-identical to their state before this release.

## Assumptions

- The eight orphaned test files largely still pass; any rot is small enough
  to repair within Initiative 1.
- ruff and mypy findings are mechanical and do not require behavioral
  rewrites.
- v0.8.x scoping can treat `walk_corpus` as the stable traversal seam.

## Risks

- Tests unexercised in CI may have rotted unnoticed: run all eight locally
  before touching the matrix; repairs are in scope.
- `ruff format` may rewrap human-output template strings in
  `output/human.py`: goldens gate this; prefer targeted excludes over
  regenerating goldens.
- Traversal consolidation touches six services: one service per commit,
  each verified by its battery plus goldens, bounds the blast radius.

## Non-Goals

- Splitting `services/relationships.py` (552 lines, three concerns) —
  deferred to v0.8.0 service-API shaping.
- Caching the repository index for repeated resolve/find calls — designed
  into the Explorer's long-lived runtime (v0.8.2), not retrofitted here.
- Shipping a `py.typed` marker — decided alongside v0.8.0's public service
  API surface.
- Coverage threshold gates — visibility first.
- Restructuring `cli.py` (925 lines of linear parser registration: large,
  not complex).

## Related Decisions

- ADR-027
- ADR-015
- ADR-007

## Dependencies

- v0.7.13 Metadata Migration (merged)

## Follow-On Work

- v0.8.0 service API: relationships package split, `py.typed`, public
  surface definition.
- v0.8.2 interactive runtime: a held repository index with invalidation,
  replacing per-call `build_repository_index()` in resolve and find.
