feat(hooks): track writing contracts + linters in repo

comms-lint.py and comment-lint.py lived only in ~/.claude, which is not
a git repo. Contract prose lived in ~/.claude/CLAUDE.md. Neither survived
a machine rebuild.

Prose moves to claude-md/ fragments, imported via @name.md. Linters move
to hooks/. link.sh and home.nix distribute both. settings.json wiring
stays manual — it holds machine-local MCP/statusline config this repo
must not own.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VaGR6ERGCfzWTtuv2rebGe
This commit is contained in:
naps62
2026-07-27 14:34:29 +00:00
parent 715420d0a9
commit 08195c1cd4
8 changed files with 481 additions and 3 deletions
+40
View File
@@ -0,0 +1,40 @@
## Code comments
Governs VOLUME and PURPOSE. Caveman governs wording. Enforced by `~/.claude/hooks/comment-lint.py`.
**Assume the reader is an agent with the whole repo, not a human with one file open.** It can
read call sites, `docs/`, the tests and `git log` faster than it can read your prose. So a
comment is not for teaching. It exists for ONE job: to stop a confident wrong edit.
Write a comment only when an agent with repo access and thirty seconds of grep would still get
it wrong. Four cases qualify:
1. **Landmine.** The code looks redundant, wrong, or reorderable and is not, AND no test catches
breaking it. Say what breaks. This is the highest-value comment in the codebase.
`// MUST read lastGoodPrice before fetchPrice - same eth_call state; no test catches this.`
2. **Fact not in the repo.** Deployed bytecode, a chain quirk, a library's undocumented
behaviour. One line and a path into `docs/`.
3. **Unit, encoding or epoch** the type cannot carry - 18-dp, wei, ms, which instant a timestamp
is anchored to.
4. **Cross-file invariant a caller can break**, stated as MUST / MUST NOT.
5. **A rejected alternative that was actually tried**, one line: what was tried, why it lost. An
agent reading only the code sees an obvious improvement and re-does the failed work. Keep this
ONLY where the losing attempt is not recoverable elsewhere — if `docs/` or an ADR records it,
cite that instead. Subjective domains (game feel, UI, copy) usually have no such record, so the
comment is the only copy: `// Outward was tried: brightest when it fights the silhouette.`
**Delete everything else.** Mechanism the code already shows; what another module does; why this
design over another; measurement narratives; consequence chains; anything a test asserts;
anything `git log` records. The agent derives all of it on demand, and prose that duplicates code
is prose that will contradict it after the next refactor.
**Budget: 1-3 lines.** Past 3 lines you are teaching, not warning - the only exception is a
landmine that genuinely needs the mechanism spelled out to be actionable. Whole-file headers get
3 lines: what this is, and the one trap. Not a table of contents.
Prefer moving substance INTO `docs/` and leaving a path. An agent will follow the path; it costs
one read and the doc does not rot against the code.
**No emphasis.** No bold, no italics, no superlatives. ALL-CAPS only for MUST / NEVER on a real
invariant, or to name the trap.
**Deleting a comment is cheap and reversible - it is in git.** When unsure, delete.
+23
View File
@@ -0,0 +1,23 @@
## Public comms — issues, PRs, review comments
Overrides the caveman skill's "Code/commits/PRs: write normal" boundary. Normal prose, but these rules bind. Enforced by `~/.claude/hooks/comms-lint.py`.
**Structure (BLUF — bottom line up front):**
- Line 1 is the ask. What decision or action do I want, from whom. Never make the reader reach the end to find it.
- Line 2-3 is the consequence in plain words a non-engineer PM would follow. "If we find a bug after launch we cannot fix it" — not "nothing bound to a compliance is upgradeable".
- Then: problem, options, proposed work. In that order.
- ≤300 words above the fold. Everything else — file:line cites, version tables, upstream verification, transcript evidence — goes inside `<details><summary>Evidence</summary>` or a follow-up comment.
- State the appetite when proposing work: roughly how much time this is worth.
**Language:**
- One concept per sentence. Break compound sentences with two or more subordinate clauses.
- Every term of art gets a plain-language gloss on first use, or gets cut. Assume the curse of knowledge is operating — I am the last to notice which labels are load-bearing jargon.
- No self-invented shorthand ("degrade-don't-brick guard", "blocking-adjacent", "identity-aggregating"). Say the thing.
- Bold ≤4 spans per document. No italics for emphasis or tone.
- Headings name their contents (Problem / Options / Work / Evidence). Not essay headings ("What is actually the case", "The third option", "What this is not for").
**Content:**
- Three or more alternatives go in a table: Option | What we do | Cost | What we get. Never prose sections.
- Scope exclusions get one line. Do not re-litigate a decided ADR or pre-empt objections nobody raised.
- Body reflects current truth. Superseded reasoning moves to a comment — never leave a dead decision above the live one.
Refs: BLUF (US Army), inverted pyramid, Pinker's curse of knowledge, Google Technical Writing One, Shape Up pitch.