Coding Agent Quality Rules (Galahad Principle)
Based on Jonathan Lange’s “The Galahad Principle”:
https://jml.io/galahad-principle/
Core idea: getting to 100% yields disproportionate value—especially simplicity and trust. When checks are truly “all green”, any new failure is a strong, unambiguous signal; “absence of evidence becomes evidence of absence”.
Non-negotiables: never evade feedback
Treat type errors, test failures, pre-commit hooks, lint errors, and coverage warnings as helpful feedback. Fix root causes.
Absolutely forbidden (unless the user explicitly orders it)
- Type escapes / silencing
any, sketchy unknown laundering, unchecked casts, as any, @ts-ignore, # type: ignore, noqa, disabling strict mode, weakening compiler flags, etc.
- Coverage gaming
- Ignoring/excluding lines/branches/files just to hit targets (
/* istanbul ignore */, # pragma: no cover, “generated” tricks, config exclusions, decorator/macro suppression).
- Faking results
- Skipping CI steps and claiming success; “snapshotting” coverage; lowering thresholds; marking tests flaky to ignore them.
Priorities
Type safety is part of correctness and outranks tests.
When tradeoffs exist, prioritize in this order:
- Type safety / soundness
- Correctness + meaningful tests
- Clarity / maintainability
- Performance
- Backwards compatibility (lowest)
Breaking changes are acceptable when they improve verifiability and simplify the system.
Default workflow (when anything fails)
- Read the failure output carefully.
- Restate the real invariant being violated in plain English.
- Fix the root cause (not the symptom).
- Improve tests so the behavior is pinned and regressions get caught.
- Refactor production code if needed to make it easy to type-check and validate.
Run checks in this order
- Typecheck
- Unit tests
- Integration tests
- Lint / pre-commit
- Coverage
Goal: a repo where “all green” is normal, and any new red is a loud, trustworthy signal.
“Hard to test” means refactor
If something is hard to test or hard to type:
- Treat it as a design smell.
- Refactor towards:
- smaller pure functions
- explicit data flow, minimal global state
- clear boundaries between logic and side effects
- typed domain models over stringly-typed blobs
Mocks: don’t overuse them
Avoid injecting mocks via monkeypatching or replacing system utilities by default.
Preferred approach:
- Make the function under test able to operate in multiple environments by passing in the substitutable operations explicitly (usually as function parameters or small interfaces).
- Only do this for operations that genuinely need substitution in tests (time, randomness, network, filesystem, process execution, etc.).
- This makes the injection point explicit, documents what varies, and keeps tests honest without fragile mocking.
What “good” looks like
- Types encode invariants; no “trust me” casts.
- Tests assert observable behavior (not implementation trivia).
- Coverage comes from exercising real behavior, not exclusions.
- If a thing can’t be verified cleanly, refactor until it can.
1---2name: galahad3description: how to approach tests, types and coverage4---5
6# Coding Agent Quality Rules (Galahad Principle)
7
8Based on Jonathan Lange’s “The Galahad Principle”:
9https://jml.io/galahad-principle/
10
11Core idea: **getting to 100% yields disproportionate value**—especially **simplicity** and **trust**. When checks are truly “all green”, any new failure is a strong, unambiguous signal; “absence of evidence becomes evidence of absence”.
12
13## Non-negotiables: never evade feedback
14
15Treat **type errors, test failures, pre-commit hooks, lint errors, and coverage warnings** as helpful feedback. Fix root causes.
16
17### Absolutely forbidden (unless the user explicitly orders it)
18- **Type escapes / silencing**
19 - `any`, sketchy `unknown` laundering, unchecked casts, `as any`, `@ts-ignore`, `# type: ignore`, `noqa`, disabling strict mode, weakening compiler flags, etc.
20- **Coverage gaming**
21 - Ignoring/excluding lines/branches/files just to hit targets (`/* istanbul ignore */`, `# pragma: no cover`, “generated” tricks, config exclusions, decorator/macro suppression).
22- **Faking results**
23 - Skipping CI steps and claiming success; “snapshotting” coverage; lowering thresholds; marking tests flaky to ignore them.
24
25## Priorities
26
27Type safety is part of correctness and **outranks tests**.
28
29When tradeoffs exist, prioritize in this order:
301. **Type safety / soundness**
312. **Correctness + meaningful tests**
323. **Clarity / maintainability**
334. **Performance**
345. **Backwards compatibility** (lowest)
35
36Breaking changes are acceptable when they improve verifiability and simplify the system.
37
38## Default workflow (when anything fails)
39
401. Read the failure output carefully.
412. Restate the real invariant being violated in plain English.
423. Fix the root cause (not the symptom).
434. Improve tests so the behavior is pinned and regressions get caught.
445. Refactor production code if needed to make it easy to type-check and validate.
45
46### Run checks in this order
471. **Typecheck**
482. **Unit tests**
493. **Integration tests**
504. **Lint / pre-commit**
515. **Coverage**
52
53Goal: a repo where “all green” is normal, and any new red is a loud, trustworthy signal.
54
55## “Hard to test” means refactor
56
57If something is hard to test or hard to type:
58- Treat it as a **design smell**.
59- Refactor towards:
60 - smaller pure functions
61 - explicit data flow, minimal global state
62 - clear boundaries between logic and side effects
63 - typed domain models over stringly-typed blobs
64
65## Mocks: don’t overuse them
66
67Avoid injecting mocks via monkeypatching or replacing system utilities by default.
68
69Preferred approach:
70- Make the function under test able to operate in multiple environments by **passing in the substitutable operations explicitly** (usually as function parameters or small interfaces).
71- Only do this for operations that genuinely need substitution in tests (time, randomness, network, filesystem, process execution, etc.).
72- This makes the injection point **explicit**, documents what varies, and keeps tests honest without fragile mocking.
73
74## What “good” looks like
75
76- Types encode invariants; no “trust me” casts.
77- Tests assert observable behavior (not implementation trivia).
78- Coverage comes from exercising real behavior, not exclusions.
79- If a thing can’t be verified cleanly, refactor until it can.
80