Skip to content

Commit 884dc96

Browse files
committed
Add Uncle Bob's AI coding style
1 parent 4b17df0 commit 884dc96

4 files changed

Lines changed: 557 additions & 11 deletions

File tree

ai-code-harness.el

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -177,18 +177,20 @@ When INLINE is non-nil, use the inline-formatted diagnostics instruction."
177177
(_ (ai-code--auto-test-inline-suffix-for-type type))))
178178

179179
(defun ai-code--ensure-auto-test-harness-file (type)
180-
"Write and return the package prompt file path for auto test TYPE."
181-
(when-let ((content (ai-code--auto-test-harness-text-for-type type)))
182-
(let* ((directory (ai-code--ensure-auto-test-harness-prompt-directory))
183-
(file-path (expand-file-name
184-
(ai-code--auto-test-harness-file-name type)
185-
directory)))
186-
(unless (file-exists-p file-path)
180+
"Return the package prompt file for TYPE, generating it when needed."
181+
(let* ((directory (ai-code--auto-test-harness-directory))
182+
(file-path (expand-file-name
183+
(ai-code--auto-test-harness-file-name type)
184+
directory)))
185+
(if (file-exists-p file-path)
186+
file-path
187+
(when-let ((content (ai-code--auto-test-harness-text-for-type type)))
188+
(ai-code--ensure-auto-test-harness-prompt-directory)
187189
(with-temp-file file-path
188190
(insert content)
189191
(unless (bolp)
190-
(insert "\n"))))
191-
file-path)))
192+
(insert "\n")))
193+
file-path))))
192194

193195
(defun ai-code--auto-test-harness-reference-suffix (type)
194196
"Return a short suffix that references the package prompt file for TYPE.
@@ -208,7 +210,8 @@ If the harness file cannot be prepared, fall back to the inline suffix."
208210
(defun ai-code--auto-test-suffix-for-type (type)
209211
"Return prompt suffix for auto test TYPE."
210212
(pcase type
211-
((or 'test-after-change 'tdd 'tdd-with-refactoring)
213+
((or 'test-after-change 'tdd 'tdd-with-refactoring
214+
'uncle-bob-ai-coding-style)
212215
(ai-code--auto-test-harness-reference-suffix type))
213216
('no-test "Do not write or run any test.")
214217
(_ nil)))
@@ -327,11 +330,16 @@ See the later `defcustom' for user-facing documentation and default.")
327330
"Forward declaration for `ai-code-discussion-auto-follow-up-on-code-change'.
328331
See the later `defcustom' for user-facing documentation and default.")
329332

333+
;; TODO DONE: Given skills inside /home/tninja/git/old-coder/skills/old-coder
334+
;; (both SKILL.md and gauntlet.md), append a new choice: Uncle Bob's AI coding
335+
;; style, to integrate that skill into the auto-test harness without depend on
336+
;; that repo.
330337
(defconst ai-code--auto-test-type-ask-choices
331338
'(("Run tests after code change" . test-after-change)
332339
("Do not write or run tests" . no-test)
333340
("TDD Red + Green (write failing test, then make it pass)" . tdd)
334-
("TDD Red + Green + Blue (refactor after Green)" . tdd-with-refactoring))
341+
("TDD Red + Green + Blue (refactor after Green)" . tdd-with-refactoring)
342+
("Uncle Bob's AI coding style" . uncle-bob-ai-coding-style))
335343
"Resolve auto test suffix choices for `ask-me` mode.")
336344

337345
(defconst ai-code--auto-test-type-persistent-choices
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
# Uncle Bob's AI Coding Style
2+
3+
Use evidence-first development: surround the implementation with an executable
4+
specification and a gauntlet of constraints so confidence comes from auditable
5+
evidence instead of line-by-line review.
6+
7+
The trust model has two primary artifacts: an executable specification approved
8+
before implementation, and an evidence report produced after real verification.
9+
The gauntlet proves only the constraints expressed by the specification, so be
10+
explicit about assumptions, invariants, skipped checks, and remaining risk.
11+
12+
## Diagnostics-first constraint
13+
14+
Before editing, record a diagnostics baseline by calling the
15+
`diagnostics_baseline` MCP tool. After each edit, call the `get_diagnostics` MCP
16+
tool with `since="baseline"` for every touched file. Do not finish until its
17+
status is `clean`, meaning no new diagnostics versus the baseline.
18+
19+
## Required loop
20+
21+
SPEC → (human approves spec, not code) → RED → GREEN → REFACTOR → GAUNTLET → EVIDENCE
22+
23+
Repeat RED through REFACTOR for each behavior. Never weaken the gauntlet to make
24+
the implementation appear successful.
25+
26+
### 1. SPEC
27+
28+
Before changing implementation files, turn the request into executable
29+
acceptance criteria:
30+
31+
- Describe concrete inputs, outputs, edge cases, and error cases as Gherkin
32+
scenarios or a named test list.
33+
- Include negative constraints: existing behavior, public APIs, data integrity,
34+
performance budgets, and anything else that must not change.
35+
- Include the setup plan: tools to install, files to add, git/checkpoint usage,
36+
and every new dependency with a one-line justification.
37+
- Show the specification to the human and obtain approval before implementation.
38+
In autonomous mode, proceed only if allowed, and record that approval was not
39+
obtained so the final confidence claim is correspondingly weaker.
40+
- Treat the specification as append-only. If it is wrong, revise it visibly and
41+
explain why; never let implementation silently redefine it.
42+
43+
### 2. RED
44+
45+
Write the smallest test for one approved behavior and run it before changing the
46+
implementation. Observe the expected assertion failure. A collection or import
47+
failure is weaker evidence; create a minimal stub when needed so the failure is
48+
about behavior. If the new test already passes, use a temporary mutant to prove
49+
the test can fail, restore the source, and record the behavior as pre-existing.
50+
51+
### 3. GREEN
52+
53+
Write the least implementation needed to pass the failing test, then run the
54+
full suite. Do not refactor or expand the feature during Green.
55+
56+
### 4. REFACTOR
57+
58+
With the suite green, improve naming, cohesion, duplication, and control flow
59+
without changing behavior. Implementation refactors must not edit tests.
60+
Test-structure refactors are a separate step: keep assertions unchanged, run
61+
the suite before and after, and rerun mutation checks. Any assertion change is a
62+
behavior change and returns to SPEC.
63+
64+
### 5. GAUNTLET
65+
66+
Run every applicable constraint layer. Scale the effort using Calibration, but
67+
never skip a layer silently.
68+
69+
| Layer | Constraint |
70+
|---|---|
71+
| Full test suite | Zero new failures; record any pre-existing baseline failures verbatim. |
72+
| Static types | Zero new compiler or type-checker errors. |
73+
| Lint and format | Zero new warnings or formatting drift. |
74+
| Changed-line coverage | Every changed behavior-bearing line and branch is exercised; do not chase a global percentage. |
75+
| Mutation testing | Use a mutation tool or 3-5 scripted manual mutants; every non-equivalent mutant must be killed. |
76+
| Property tests | Add invariant-based tests for parsing, math, serialization, ordering, or round trips when applicable. |
77+
| Complexity budget | Keep new functions small, cohesive, and easy to explain. |
78+
| Real execution | Run the application, CLI, or endpoint once with realistic input. |
79+
| Supply chain and secrets | Audit dependency changes, licenses, secrets, and newly introduced capabilities. |
80+
| Suite health | Check determinism, randomized order where supported, and suspected flakes. |
81+
82+
Mutation kills validate the suite as a whole unless a layer is run separately.
83+
Classify tool-generated equivalent mutants honestly. Hand-written mutants must
84+
represent real bugs and receive no equivalent-mutant exemption.
85+
86+
### 6. EVIDENCE
87+
88+
Finish with a reproducible report containing:
89+
90+
- The approved specification and a scenario-to-test mapping.
91+
- Every gauntlet command and its actual numeric result from one fresh run after
92+
the final edit.
93+
- A single persisted entry-point command that reruns every applicable layer,
94+
with tool versions pinned or recorded.
95+
- The source state, using a commit SHA or a reproducible tree hash.
96+
- Every skipped layer and the reason.
97+
- Failures encountered and how they were resolved.
98+
- Remaining risks and limits, without claiming absolute proof.
99+
100+
## Anti-gaming rules
101+
102+
1. Never weaken, skip, broaden, or delete a test to make it pass.
103+
2. Never edit a test and its implementation in the same step on the path to
104+
Green. Change one, run it, then change the other.
105+
3. Never mock the unit under test. Mock only true boundaries such as network,
106+
clock, filesystem, or process execution.
107+
4. Never add vacuous tests merely to raise coverage.
108+
5. Never report a layer that was not run.
109+
6. A failing applicable gauntlet layer blocks completion. If blocked, report the
110+
exact failure instead of weakening the constraint.
111+
112+
## Calibration
113+
114+
- Tier 1, trivial: full suite plus lint. Explain why a new test is unnecessary
115+
or why existing coverage is sufficient.
116+
- Tier 2, normal feature or bug fix: the full SPEC, RED, GREEN, REFACTOR,
117+
GAUNTLET, EVIDENCE loop. Bug fixes start with a regression test.
118+
- Tier 3, high stakes: first write a failure model for risks such as money,
119+
authentication, data loss, concurrency, migrations, public API compatibility,
120+
unbounded growth, or silent production failure. Add targeted stress, fuzz,
121+
rollback, contract, observability, compatibility, or benchmark layers. Also
122+
require property tests, mutation testing, and an adversarial pass.
123+
124+
## Setup rules
125+
126+
Prefer the repository's current tools. If essential tooling is missing, put its
127+
installation and every environment change in the approved SPEC. Prefer standard
128+
libraries and existing dependencies. Do not initialize git, install packages, or
129+
create checkpoint commits without authorization. If tooling is declined or
130+
unavailable, use the best manual layer and record the reduced confidence.
131+
132+
# Gauntlet Tooling by Ecosystem
133+
134+
Use project-native commands when they exist. The following are defaults only.
135+
136+
## Python
137+
138+
| Layer | Default |
139+
|---|---|
140+
| Tests | `pytest -q` |
141+
| Types | `mypy <pkg>` or pyright |
142+
| Lint and format | `ruff check .` and `ruff format --check .` |
143+
| Coverage | pytest-cov with branch coverage; use diff-cover when configured |
144+
| Mutation | mutmut scoped to changed modules |
145+
| Property tests | hypothesis |
146+
147+
## JavaScript and TypeScript
148+
149+
| Layer | Default |
150+
|---|---|
151+
| Tests | `npx vitest run` or `npx jest` |
152+
| Types | `npx tsc --noEmit` |
153+
| Lint | `npx eslint .` |
154+
| Coverage | Vitest or Jest coverage, checked against changed lines |
155+
| Mutation | Stryker scoped to changed files |
156+
| Property tests | fast-check |
157+
158+
## Go
159+
160+
| Layer | Default |
161+
|---|---|
162+
| Tests | `go test ./... -race` |
163+
| Types and build | `go build ./...` |
164+
| Lint | `go vet ./...` and staticcheck |
165+
| Coverage | `go test -coverprofile=c.out ./...` then `go tool cover -func=c.out` |
166+
| Mutation | scripted manual mutation |
167+
| Property tests | testing/quick or rapid |
168+
169+
## Rust
170+
171+
| Layer | Default |
172+
|---|---|
173+
| Tests | `cargo test` |
174+
| Types | `cargo check` |
175+
| Lint | `cargo clippy -- -D warnings` |
176+
| Coverage | cargo-llvm-cov with branch coverage |
177+
| Mutation | cargo-mutants scoped to changed files |
178+
| Property tests | proptest |
179+
180+
## Extended layer menu
181+
182+
Select additional layers from the failure model:
183+
184+
- Dependency and license audit whenever dependencies change.
185+
- Secret scan and a manual capability diff for new network, subprocess,
186+
filesystem, or environment access.
187+
- Randomized test order and repeated runs for suite-health concerns.
188+
- API compatibility checks when a public API changes.
189+
- Race detectors and stress tests for concurrency.
190+
- Benchmarks only when the SPEC states a measurable performance budget.
191+
- Accessibility, screenshot, and browser checks for user-facing UI.
192+
- Version-matrix checks when the project claims multiple supported versions.
193+
- Log or metric assertions when silent production failure is a risk.
194+
195+
## Manual mutation procedure
196+
197+
When no mutation tool is available, persist a repository script that saves the
198+
original source, applies one plausible bug at a time, runs the relevant suite,
199+
and restores the source. Use 3-5 mutants such as a flipped comparison, off-by-one
200+
bound, removed branch, swapped boolean operator, or constant return. Every mutant
201+
must fail at least one test. Verify restoration with the final diff and suite,
202+
then report `manual mutation: N/N killed`.
203+
204+
## Reproducible gauntlet entry point
205+
206+
Persist one command that removes stale artifacts, runs every applicable layer in
207+
sequence, and fails on the first broken layer. Pin or record development-tool
208+
versions. The final evidence numbers must come from one fresh execution of this
209+
entry point after the last edit.
210+
211+
## Executable specification template
212+
213+
```gherkin
214+
Feature: <capability in user language>
215+
Scenario: <one concrete behavior>
216+
Given <concrete starting state>
217+
When <concrete action with concrete input>
218+
Then <concrete observable outcome>
219+
220+
Scenario: <error or invariant case>
221+
Given <concrete starting state>
222+
When <invalid, hostile, or boundary input>
223+
Then <exact error and state that must not change>
224+
```
225+
226+
## Evidence report template
227+
228+
```markdown
229+
## Evidence Report — <task name> (Tier <1|2|3>)
230+
231+
- Spec approval: <obtained | not obtained, confidence downgraded>
232+
- Source state: <commit SHA | reproducible tree hash>
233+
- Toolchain: <versions file or recorded versions>
234+
- Entry point: <one command that reruns the gauntlet>
235+
236+
### Spec to test mapping
237+
| Scenario or invariant | Test or layer | Status |
238+
|---|---|---|
239+
| <behavior> | <test name> | pass, fail, unverified, or n-a |
240+
241+
### Fresh gauntlet results
242+
| Layer | Command | Numeric result |
243+
|---|---|---|
244+
| Tests | <command> | <passed and failed counts> |
245+
| Types | <command> | <error count> |
246+
| Lint | <command> | <warning count> |
247+
| Changed-line coverage | <command> | <covered/total> |
248+
| Mutation | <command> | <killed/total> |
249+
| Real execution | <command> | <observed result> |
250+
251+
### Skipped layers and honest notes
252+
- <layer>: <reason>
253+
- <failures, fixes, and remaining risks>
254+
```

0 commit comments

Comments
 (0)