Summary
We’ve agreed that:
The tests ARE the spec.
and that all Newton-style merges and future work on main should be:
- Test-first (failing test → implementation → passing test).
- Main-only (no new feature branches).
- History-safe (no rebases, no force pushes, no amend).
This issue tracks mechanizing that policy so we don’t have to rely on memory or vibes.
Goals
- Enforce commit intent via commit message prefixes (
feat:, fix:, refactor:, chore:, docs:).
- Detect behavior changes without tests:
- If code (
src/**) changes under a feat:/fix: commit, require tests to change, too.
- Allow refactors/chore commits without forcing tests, while still running the full test suite.
- (Future) Tie coverage to changes:
- Ensure new/changed code is exercised by tests (coverage doesn’t silently drop where it matters).
Policy
1. Commit Intent
- Behavior commits:
- Prefix:
feat:, fix:.
- Expectations:
- Some test files change (
src/**/*.{test,spec}.{ts,tsx} or src/**/__tests__/**), or
- Coverage for the changed code improves or changes in a meaningful way.
- Non-behavior commits:
- Prefix:
refactor:, chore:, docs:.
- Expectations:
- Tests may or may not change, but:
- No intentional behavior change is allowed.
- The full test suite must still pass.
2. Diff-Based Guardrails
On every push to main (or PR targeting main):
-
Compute diff vs origin/main:
git diff --name-only origin/main...HEAD
-
Classify changed files:
- Code files: src/** minus:
- src//tests/
- src/**/*.{test,spec}.{ts,tsx}
- Test files:
- src//tests/
- src/**/*.{test,spec}.{ts,tsx}
-
Enforcement:
- If there are code changes and no test changes:
- AND the commit message starts with feat: or fix:
- → Fail the check: “Behavioral commit with src changes but no tests.”
- AND the commit message starts with refactor:, chore:, or docs:
- → Allowed; rely on passing tests as guardrail.
This doesn’t perfectly detect behavior changes, but it catches most “changed behavior, forgot tests” cases and
forces the author to state intent in the commit message.
3. Coverage (Future)
Once we have coverage reports from Vitest (e.g., npm test -- --coverage):
- For each changed code file, check:
- Coverage is above some threshold (e.g., ≥ 80%), or
- Coverage for that file did not drop from a previous baseline.
This step is more expensive and can be added later; for now, the diff + intent approach is the MVP.
———
Implementation Plan
Phase 1 – Diff + Commit Prefix Heuristic
Phase 2 – Coverage Integration (Optional)
Phase 3 – Refinements
———
Acceptance Criteria
Summary
We’ve agreed that:
and that all Newton-style merges and future work on
mainshould be:This issue tracks mechanizing that policy so we don’t have to rely on memory or vibes.
Goals
feat:,fix:,refactor:,chore:,docs:).src/**) changes under afeat:/fix:commit, require tests to change, too.Policy
1. Commit Intent
feat:,fix:.src/**/*.{test,spec}.{ts,tsx}orsrc/**/__tests__/**), orrefactor:,chore:,docs:.2. Diff-Based Guardrails
On every push to
main(or PR targetingmain):Compute diff vs
origin/main:Classify changed files:
Enforcement:
This doesn’t perfectly detect behavior changes, but it catches most “changed behavior, forgot tests” cases and
forces the author to state intent in the commit message.
3. Coverage (Future)
Once we have coverage reports from Vitest (e.g., npm test -- --coverage):
This step is more expensive and can be added later; for now, the diff + intent approach is the MVP.
———
Implementation Plan
Phase 1 – Diff + Commit Prefix Heuristic
Phase 2 – Coverage Integration (Optional)
Phase 3 – Refinements
narrative” in the commit message (for human review).
———
Acceptance Criteria
suite passes).