Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
06af883
docs(roadmap): define v0.7.14 audit hardening scope [roadmap:v0.7.14]
tcballard Jun 9, 2026
4f8c77a
docs(prompt): pin agent commit identity and footer rules
tcballard Jun 9, 2026
ca8d1d7
docs(prompt): consolidate session-start and instructions prompts
tcballard Jun 10, 2026
a74fa1c
docs(prompt): route agent guidance through root CLAUDE.md
tcballard Jun 10, 2026
85aa8d8
test(ci): restore orphaned test batteries with battery guard [roadmap…
tcballard Jun 10, 2026
c108fe8
chore(lint): adopt ruff and mypy baselines [roadmap:v0.7.14]
tcballard Jun 10, 2026
318e983
chore(ci): gate lint, format, and types in CI [roadmap:v0.7.14]
tcballard Jun 10, 2026
cb6e3df
docs(decisions): amend ADR-027 with pre-merge lint and smoke tier [ro…
tcballard Jun 10, 2026
c59c3a0
chore(ci): add pre-merge lint and smoke checks [roadmap:v0.7.14]
tcballard Jun 10, 2026
ed9e0e0
refactor(core): add walk_corpus traversal seam [roadmap:v0.7.14]
tcballard Jun 10, 2026
2c65e3e
refactor(validate): consume walk_corpus [roadmap:v0.7.14]
tcballard Jun 10, 2026
75cc0da
refactor(index): consume walk_corpus [roadmap:v0.7.14]
tcballard Jun 10, 2026
821036c
refactor(inspect): consume walk_corpus [roadmap:v0.7.14]
tcballard Jun 10, 2026
068f9a2
refactor(stats): consume walk_corpus [roadmap:v0.7.14]
tcballard Jun 10, 2026
5f07a94
refactor(portfolio): consume walk_corpus [roadmap:v0.7.14]
tcballard Jun 10, 2026
7a048f0
refactor(relationships): consume walk_corpus [roadmap:v0.7.14]
tcballard Jun 10, 2026
3ba95d7
refactor(migrate): consume walk_corpus [roadmap:v0.7.14]
tcballard Jun 10, 2026
40b1e83
chore(repo): drop committed .DS_Store [roadmap:v0.7.14]
tcballard Jun 10, 2026
18ad332
chore(ci): report test coverage [roadmap:v0.7.14]
tcballard Jun 10, 2026
f30d2ed
docs(testing): document lint, type, and coverage gates [roadmap:v0.7.14]
tcballard Jun 10, 2026
604ae3c
chore(release): add v0.7.14 changelog entries [roadmap:v0.7.14]
tcballard Jun 10, 2026
3bd3e53
chore(repo): ignore coverage data files [roadmap:v0.7.14]
tcballard Jun 10, 2026
86ad25c
docs(prompt): require stripping platform attribution from PR bodies
tcballard Jun 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed .DS_Store
Binary file not shown.
69 changes: 69 additions & 0 deletions .github/workflows/pr-checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: PR Checks

# Pre-merge tier (v0.7.14, ADR-027 rule 1): pull requests run the static
# quality gates plus a small smoke battery — core, golden, and dogfood on one
# Python version. The full battery × version grid stays merge-gated on main
# (ci.yml); this tier exists to catch lint/type breakage, output-contract
# drift, and corpus damage before merge, in about two minutes.
on:
pull_request:

permissions:
contents: read

concurrency:
group: pr-checks-${{ github.ref }}
cancel-in-progress: true

jobs:
lint:
name: lint (ruff + mypy)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install package with dev extras
run: |
python -m pip install --upgrade pip
python -m pip install -e .[dev]

- name: ruff check
run: python -m ruff check src/ tests/

- name: ruff format --check
run: python -m ruff format --check src/ tests/

- name: mypy
run: python -m mypy src/

smoke:
name: smoke (core + golden + dogfood, py3.11)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install package with dev extras
run: |
python -m pip install --upgrade pip
python -m pip install -e .[dev]

- name: Run smoke battery
run: >
python -m pytest -q
tests/test_validate.py tests/test_parser.py tests/test_schema.py
tests/test_identity.py tests/test_frontmatter.py
tests/test_metadata_identity.py tests/test_idgen.py
tests/test_ci_batteries.py tests/test_corpus.py
tests/test_golden.py tests/test_dogfood.py
64 changes: 62 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,34 @@ permissions:
contents: read

jobs:
# Static quality gates (v0.7.14): lint, format, and types fail the suite
# just like a battery. One Python version is enough — the rules target 3.11.
lint:
name: lint (ruff + mypy)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install package with dev extras
run: |
python -m pip install --upgrade pip
python -m pip install -e .[dev]

- name: ruff check
run: python -m ruff check src/ tests/

- name: ruff format --check
run: python -m ruff format --check src/ tests/

- name: mypy
run: python -m mypy src/

pytest:
name: ${{ matrix.battery.name }} (py${{ matrix.python-version }})
runs-on: ubuntu-latest
Expand All @@ -24,14 +52,17 @@ jobs:
matrix:
python-version: ["3.11", "3.12", "3.13"]
# One battery per .py service, plus grouped core / cli / artifacts.
# Every tests/test_*.py belongs to exactly one battery (no orphans).
# Every tests/test_*.py belongs to exactly one battery (no orphans) —
# enforced by tests/test_ci_batteries.py in the core battery.
battery:
- name: core
paths: "tests/test_validate.py tests/test_parser.py tests/test_schema.py tests/test_identity.py"
paths: "tests/test_validate.py tests/test_parser.py tests/test_schema.py tests/test_identity.py tests/test_frontmatter.py tests/test_metadata_identity.py tests/test_idgen.py tests/test_ci_batteries.py tests/test_corpus.py"
- name: cli
paths: "tests/test_cli.py"
- name: artifacts
paths: "tests/test_design.py tests/test_roadmap.py tests/test_prompt.py tests/test_decision_metadata.py"
- name: create
paths: "tests/test_create.py tests/test_templates.py"
- name: diff
paths: "tests/test_diff.py"
- name: improve
Expand All @@ -40,12 +71,18 @@ jobs:
paths: "tests/test_index.py"
- name: ingest
paths: "tests/test_ingest.py"
- name: init
paths: "tests/test_init.py"
- name: inspect
paths: "tests/test_inspect.py"
- name: migrate
paths: "tests/test_migrate.py"
- name: portfolio
paths: "tests/test_portfolio.py"
- name: relationships
paths: "tests/test_relationships.py tests/test_relationships_cmd.py tests/test_relationship_validation.py"
- name: resolve
paths: "tests/test_resolve.py"
- name: review
paths: "tests/test_review.py"
- name: stats
Expand Down Expand Up @@ -76,3 +113,26 @@ jobs:

- name: Run ${{ matrix.battery.name }} battery
run: python -m pytest -q ${{ matrix.battery.paths }}

# Coverage visibility (v0.7.14): one full-suite run on py3.11 with a
# term-missing report in the job log. Report-only — no threshold gate yet
# (measure first, gate later; see the v0.7.14 roadmap Non-Goals).
coverage:
name: coverage (py3.11, report-only)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install package with dev extras
run: |
python -m pip install --upgrade pip
python -m pip install -e .[dev]

- name: Run full suite with coverage
run: python -m pytest -q --cov=src/rac --cov-report=term-missing
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ __pycache__/
build/
dist/
.pytest_cache/

# Coverage data (pytest --cov; reported in CI, never committed)
.coverage
.coverage.*
htmlcov/
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,23 @@ details, release history over commit history.

### Added

- CI battery integrity (v0.7.14): eight test files (~1,300 lines, including
all coverage for `rac new` and `rac migrate`) were missing from the CI
battery matrix and never ran; they are restored, and a new guard test
fails the suite if any test file is ever orphaned again.
- Static quality gates (v0.7.14): ruff (lint + format) and mypy now gate CI;
pull requests run the gates plus a fast smoke battery (ADR-027 amended),
while the full battery grid stays merge-gated on `main`. CLI output is
unchanged — all golden files are byte-identical.
- Test coverage is reported on every CI run (report-only, currently 97%)
(v0.7.14).

### Changed

- Repository corpus traversal is defined once in core (`walk_corpus`) and
consumed by every repository command — behavior and output unchanged
(v0.7.14).

- `rac migrate metadata <directory>` — migrate existing recognized artifacts
onto canonical frontmatter identity: idempotent, byte-preserving, with
`--dry-run` preview; unrecognized documents are reported, never guessed at
Expand Down
24 changes: 24 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# RAC — agent session context

This file is a router. Canonical agent guidance lives in `rac/prompts/`,
where the RAC corpus gates validate it. Do not add rules here — add them
to the corpus artifact and they load through the imports below.

## Loaded every session

@rac/prompts/rac-agent-session-start.md
@rac/prompts/rac-agent-commit-guidelines.md

## Situational prompts — read when the task calls for it, do not import

- Pull request preparation: `rac/prompts/rac-agent-pr-guidelines.md`
- Minor release gate: `rac/prompts/rac-agent-release-gate-minor.md`
- Major release gate: `rac/prompts/rac-agent-release-gate-major.md`
- Refactoring and simplification: `rac/prompts/rac-agent-simplification-guidelines.md`
- Context compression: `rac/prompts/rac-agent-compression.md`

## Working corpus

- Current series: `rac/roadmaps/v0.7.x-trust/` (next up: v0.7.14)
- Next series under scoping: `rac/roadmaps/v0.8.*.md`
- Decisions (ADRs): `rac/decisions/`
6 changes: 5 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ pip install -e '.[dev]'

## Verify a change

Three commands; all must pass before you open a pull request:
These commands must all pass before you open a pull request:

```bash
pytest

ruff check src/ tests/
ruff format --check src/ tests/
mypy src/

rac validate rac/

rac relationships rac/ --validate
Expand Down
27 changes: 26 additions & 1 deletion docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,28 @@ Tests are organized per capability — `test_validate.py`, `test_inspect.py`,
including negative cases (an invalid file, or a neighboring artifact type that must
*not* classify as the new one).

Every `tests/test_*.py` must belong to exactly one CI battery in
`.github/workflows/tests.yml` — `tests/test_ci_batteries.py` fails the suite if a
file is orphaned, duplicated, or stale, so add new test files to the matrix as you
create them.

## Lint, format, and types

CI gates on ruff and mypy (configured in `pyproject.toml`); run them locally
before pushing:

```bash
.venv/bin/python -m ruff check src/ tests/
.venv/bin/python -m ruff format --check src/ tests/ # or without --check to apply
.venv/bin/python -m mypy src/
```

Coverage is reported (not gated) in CI; the same view locally:

```bash
.venv/bin/python -m pytest -q --cov=src/rac --cov-report=term-missing
```

## Source layout

The package uses a `src/` layout. The import package `rac` is organized into layers
Expand All @@ -57,10 +79,13 @@ src/rac/

## Verify before a pull request

1. **Run the suite** — it must pass:
1. **Run the suite and the static gates** — all must pass:

```bash
.venv/bin/python -m pytest
.venv/bin/python -m ruff check src/ tests/
.venv/bin/python -m ruff format --check src/ tests/
.venv/bin/python -m mypy src/
```

2. **Review your artifact changes** with RAC's own tooling:
Expand Down
26 changes: 26 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ ingest-all = ["markitdown[docx,pdf,pptx,xlsx,xls]"] # every supported format
# dev also pulls the libraries used to *generate* fixture files in the tests.
dev = [
"pytest>=7.0",
"pytest-cov",
"ruff",
"mypy",
"types-PyYAML",
"markitdown[docx,pdf,pptx,xlsx,xls]",
"python-docx",
"python-pptx",
Expand Down Expand Up @@ -74,3 +78,25 @@ where = ["src"]

[tool.pytest.ini_options]
testpaths = ["tests"]

[tool.ruff]
# Quality gates pinned by v0.7.14 (Initiative 2). Golden tests pin CLI output
# byte-for-byte, so lint/format fixes must never change string contents.
target-version = "py311"
line-length = 100

[tool.ruff.lint]
select = ["E", "F", "W", "I", "UP", "B"]

[tool.mypy]
python_version = "3.11"
files = ["src"]
disallow_untyped_defs = true
check_untyped_defs = true
no_implicit_optional = true
warn_unused_ignores = true

[[tool.mypy.overrides]]
# MarkItDown ships no type stubs; it is imported lazily inside rac.services.ingest.
module = "markitdown.*"
ignore_missing_imports = true
40 changes: 26 additions & 14 deletions rac/decisions/adr-027-ci-test-topology.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,24 @@ deliberate.

RAC's CI test topology is governed by three rules.

### 1. Tests run on merge to `main`, not on pull requests
### 1. The full suite runs on merge to `main`; pull requests get a light pre-merge tier

`ci.yml` triggers on `push:` to `main` (a merged PR or a direct push) and on
`workflow_dispatch` (manual). It does **not** trigger on `pull_request`.
*Amended by v0.7.14. As originally accepted, pull requests received no automated
feedback at all; the full battery grid ran only post-merge.*

The explicit, accepted consequence: **a pull request receives no automated test
feedback before it merges.** A regression is caught by the post-merge run on `main`
and then blocks the next release through the gate (rule 2), rather than being caught
on the PR itself. `workflow_dispatch` is the escape hatch — the full battery grid can
be run against any branch on demand from the Actions tab.
`ci.yml` triggers on `push:` to `main` (a merged PR or a direct push) and on
`workflow_dispatch` (manual). It does **not** trigger on `pull_request` — the full
battery × version grid remains merge-gated.

Pull requests run a deliberately small pre-merge tier instead
(`.github/workflows/pr-checks.yml`, v0.7.14): the static quality gates (`ruff
check`, `ruff format --check`, `mypy src/`) plus a smoke battery (core, golden,
dogfood on Python 3.11). This catches lint/type breakage, output-contract drift,
and corpus damage before merge for roughly two minutes of Actions time, while the
exhaustive grid still runs on `main`. A regression the smoke tier misses is caught
by the post-merge run and then blocks the next release through the gate (rule 2).
`workflow_dispatch` remains the escape hatch — the full grid can be run against any
branch on demand from the Actions tab.

Runs on `main` use `concurrency` with `cancel-in-progress: false`, so every merge is
fully tested and a later merge does not cancel an in-flight run.
Expand Down Expand Up @@ -118,13 +126,16 @@ changes it on purpose, with this context in view, rather than by accident.

### Negative

- Pull requests get no pre-merge test signal by default; regressions surface
post-merge on `main`. Mitigated by `workflow_dispatch` and the release gate.
- Pull requests get only the smoke tier, not the full grid (v0.7.14; originally
no signal at all); a version-specific or service-specific regression outside the
smoke set still surfaces post-merge on `main`. Mitigated by `workflow_dispatch`
and the release gate.
- More jobs per run (batteries × versions ≈ 33). They are short and run in parallel,
but the checks list is longer.
- The battery list must be kept in sync: a new `tests/test_*.py` that is not added to a
battery will not run in CI. Guarded by a coverage check at change time; a CI
self-check could enforce it later.
battery will not run in CI. Enforced since v0.7.14 by `tests/test_ci_batteries.py`
(in the core battery), which fails on orphaned, duplicated, or stale entries —
this gap went unnoticed for eight test files before the check existed.

## Alternatives Considered

Expand All @@ -142,8 +153,9 @@ Keep the `pull_request` trigger so PRs are checked before they merge.
- Doubles runs (push + PR) and spends Actions minutes on branches that may be rebased
or abandoned.

Deferred, not rejected — to be reconsidered if outside contributors need pre-merge
gating (see Review Date).
Partially adopted by v0.7.14: pull requests run lint plus a smoke battery (rule 1),
not the full grid. Full pre-merge gating remains deferred — to be reconsidered if
outside contributors need it (see Review Date).

### Single job parameterized only by Python version (the prior shape)

Expand Down
Loading
Loading