syntaxai/tdd.md · commit 4d9225e

Rewrite /sama: v2-first framing + three images + v1 content preserved below hr

Applies the SV "latest is default, legacy is preserved" pattern (Stripe,
Vue, React): the canonical /sama URL stays unchanged, but the first
paint is now v2-framing (SAMA wordmark + "Architecture as code, for
code AI writes" tagline + hero image + prominent v2 spec + live verifier
links). The v1 practitioner content (four-disciplines table, reading
order, sama CLI, blog references) is preserved below a horizontal rule
under the heading "v1 practitioner pages (preserved)", with the v1
sama CLI section honest about being the v1 four-discipline checker
(the v2 verifier with the seven §4 checks runs on the web).

Three new images so the page isn't text-only:

- public/sama-hero.{svg,png} (1200x630) — variant of og.png with the
  new tagline + a one-line live-state strip: "7/7 ✓ live · §5 metrics
  measured · n=8 cross-repo datapoints". Dark theme matching og.png.

- public/sama-layers.{svg,png} (1200x600) — visual of the four
  canonical layers (Pure 0 / Core 1 / Adapter 2 / Entry 3) stacked
  with green downward import-direction arrows and the §1.2 Law as
  caption. Hard to grok the architecture-as-code concept in prose;
  trivial visually.

- public/sama-metrics.{svg,png} (1200x600) — horizontal bar chart of
  measured workingSetFit for n=8 datapoints (tdd.md 80.00% highlighted
  green as SAMA-disciplined; cli/cli, fd, lazygit, eza, ripgrep, dive,
  bat as non-SAMA baseline colored by language). Mean 60.68%, range
  27pp, stddev 10.13pp legend.

Each discipline page (/sama/sorted, /sama/architecture, /sama/modeled,
/sama/atomic) gains a Stripe-style older-version banner prepended to
its body via samaSlugHandler — a blockquote callout pointing readers
to /sama/v2 as the formal/normative version.

Six new static routes added to d21_app.ts (3 SVG + 3 PNG) mirroring
the existing /og.{svg,png} pattern.

Anti-fudge: URLs unchanged; v1 content preserved verbatim (just
relocated under an hr); og.png NOT touched (just shipped at v=3,
social caches still warming); no §4 verifier logic changed.
367/367 tests pass. /sama/v2/verify continues to report 7/7 ✓.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
author
syntaxai <[email protected]>
date
2026-05-24 11:34:56 +01:00
parent
10b5c80
commit
4d9225e6a11d92fe8465abe39fde0022e998aee4

8 files changed · +242 −39

added public/sama-hero.png +0 −0
added public/sama-hero.svg +24 −0
@@ -0,0 +1,24 @@
1+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 630" width="1200" height="630">
2+ <rect width="1200" height="630" fill="#0a0a0a"/>
3+ <g font-family="ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace">
4+ <text x="80" y="240" font-size="180" font-weight="600" fill="#e8e8e8" letter-spacing="-2">SAMA</text>
5+ <text x="80" y="320" font-size="36" fill="#8a8a8a">Architecture as code, for code AI writes.</text>
6+ </g>
7+ <g font-family="ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace" font-size="40" font-weight="600">
8+ <text x="80" y="460" fill="#e8e8e8">Sorted</text>
9+ <text x="240" y="460" fill="#5a5a5a">·</text>
10+ <text x="275" y="460" fill="#e8e8e8">Architecture</text>
11+ <text x="565" y="460" fill="#5a5a5a">·</text>
12+ <text x="600" y="460" fill="#e8e8e8">Modeled</text>
13+ <text x="800" y="460" fill="#5a5a5a">·</text>
14+ <text x="835" y="460" fill="#e8e8e8">Atomic</text>
15+ </g>
16+ <g font-family="ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace" font-size="22">
17+ <text x="80" y="540" fill="#7ec77e">7/7 ✓ live</text>
18+ <text x="220" y="540" fill="#5a5a5a">·</text>
19+ <text x="245" y="540" fill="#a0a0a0">§5 metrics measured</text>
20+ <text x="510" y="540" fill="#5a5a5a">·</text>
21+ <text x="535" y="540" fill="#a0a0a0">n=8 cross-repo datapoints</text>
22+ </g>
23+ <text x="80" y="590" font-family="ui-sans-serif, system-ui, sans-serif" font-size="22" fill="#5a5a5a">https://tdd.md/sama</text>
24+</svg>
added public/sama-layers.png +0 −0
added public/sama-layers.svg +58 −0
@@ -0,0 +1,58 @@
1+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 600" width="1200" height="600">
2+ <rect width="1200" height="600" fill="#0a0a0a"/>
3+
4+ <g font-family="ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace">
5+ <text x="80" y="55" font-size="28" font-weight="600" fill="#e8e8e8">The four canonical layers</text>
6+ <text x="80" y="85" font-size="18" fill="#8a8a8a">SAMA v2 §1.1 — frozen; no profile may add, remove, renumber, or rename them.</text>
7+ </g>
8+
9+ <!-- Layer 3: Entry (top) -->
10+ <rect x="200" y="115" width="800" height="78" fill="#1a1a1a" stroke="#3a3a3a" stroke-width="1.5" rx="6"/>
11+ <g font-family="ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace">
12+ <text x="225" y="148" font-size="26" font-weight="600" fill="#e8e8e8">Layer 3 · Entry</text>
13+ <text x="225" y="178" font-size="17" fill="#8a8a8a">main · CLI handler · HTTP route · UI mount · job entry</text>
14+ <text x="975" y="158" font-size="14" fill="#5a5a5a" text-anchor="end">may import 0, 1, 2</text>
15+ </g>
16+
17+ <!-- Arrow 3→2 -->
18+ <line x1="600" y1="200" x2="600" y2="220" stroke="#4a8a4a" stroke-width="2"/>
19+ <polygon points="595,217 605,217 600,228" fill="#4a8a4a"/>
20+
21+ <!-- Layer 2: Adapter -->
22+ <rect x="200" y="232" width="800" height="78" fill="#1a1a1a" stroke="#3a3a3a" stroke-width="1.5" rx="6"/>
23+ <g font-family="ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace">
24+ <text x="225" y="265" font-size="26" font-weight="600" fill="#e8e8e8">Layer 2 · Adapter</text>
25+ <text x="225" y="295" font-size="17" fill="#8a8a8a">the boundary — DB · network · filesystem · framework bindings · external input parsed here</text>
26+ <text x="975" y="275" font-size="14" fill="#5a5a5a" text-anchor="end">may import 0, 1</text>
27+ </g>
28+
29+ <!-- Arrow 2→1 -->
30+ <line x1="600" y1="317" x2="600" y2="337" stroke="#4a8a4a" stroke-width="2"/>
31+ <polygon points="595,334 605,334 600,345" fill="#4a8a4a"/>
32+
33+ <!-- Layer 1: Core -->
34+ <rect x="200" y="349" width="800" height="78" fill="#1a1a1a" stroke="#3a3a3a" stroke-width="1.5" rx="6"/>
35+ <g font-family="ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace">
36+ <text x="225" y="382" font-size="26" font-weight="600" fill="#e8e8e8">Layer 1 · Core</text>
37+ <text x="225" y="412" font-size="17" fill="#8a8a8a">domain logic · decisions · pure render — no network, disk, clock, framework</text>
38+ <text x="975" y="392" font-size="14" fill="#5a5a5a" text-anchor="end">may import 0</text>
39+ </g>
40+
41+ <!-- Arrow 1→0 -->
42+ <line x1="600" y1="434" x2="600" y2="454" stroke="#4a8a4a" stroke-width="2"/>
43+ <polygon points="595,451 605,451 600,462" fill="#4a8a4a"/>
44+
45+ <!-- Layer 0: Pure (bottom) -->
46+ <rect x="200" y="466" width="800" height="78" fill="#1a1a1a" stroke="#3a3a3a" stroke-width="1.5" rx="6"/>
47+ <g font-family="ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace">
48+ <text x="225" y="499" font-size="26" font-weight="600" fill="#e8e8e8">Layer 0 · Pure</text>
49+ <text x="225" y="529" font-size="17" fill="#8a8a8a">types · constants · pure functions · domain models — no I/O, no side effects</text>
50+ <text x="975" y="509" font-size="14" fill="#5a5a5a" text-anchor="end">imports nothing above</text>
51+ </g>
52+
53+ <!-- Caption: the Law -->
54+ <g font-family="ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace">
55+ <text x="80" y="578" font-size="15" fill="#7ec77e">§1.2 the Law:</text>
56+ <text x="220" y="578" font-size="15" fill="#a0a0a0">imports always point to a strictly lower layer number — never upward, never sideways, never cyclic.</text>
57+ </g>
58+</svg>
added public/sama-metrics.png +0 −0
added public/sama-metrics.svg +86 −0
@@ -0,0 +1,86 @@
1+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 600" width="1200" height="600">
2+ <rect width="1200" height="600" fill="#0a0a0a"/>
3+
4+ <g font-family="ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace">
5+ <text x="80" y="50" font-size="26" font-weight="600" fill="#e8e8e8">§5 workingSetFit · measured · n=8 cross-repo datapoints</text>
6+ <text x="80" y="78" font-size="16" fill="#8a8a8a">share of source files in the [50, 500] LOC working-set band · same bounds, same emitter, same source-of-truth.</text>
7+ </g>
8+
9+ <!-- Chart geometry:
10+ label column x: 80..280 (200 wide, right-aligned text at x=275)
11+ bar area x: 290..1090 (800 wide = 100%)
12+ value column x: 1100..1180 (right-aligned text at x=1175)
13+ rows: 8 rows, y=120..540 (52px per row) -->
14+
15+ <!-- Background grid lines at 25%, 50%, 75% -->
16+ <line x1="490" y1="100" x2="490" y2="555" stroke="#1f1f1f" stroke-width="1"/>
17+ <line x1="690" y1="100" x2="690" y2="555" stroke="#1f1f1f" stroke-width="1"/>
18+ <line x1="890" y1="100" x2="890" y2="555" stroke="#1f1f1f" stroke-width="1"/>
19+ <line x1="1090" y1="100" x2="1090" y2="555" stroke="#1f1f1f" stroke-width="1"/>
20+ <!-- Baseline at 0% -->
21+ <line x1="290" y1="100" x2="290" y2="555" stroke="#3a3a3a" stroke-width="1.5"/>
22+
23+ <!-- x-axis labels -->
24+ <g font-family="ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace" font-size="13" fill="#6a6a6a" text-anchor="middle">
25+ <text x="290" y="572">0%</text>
26+ <text x="490" y="572">25%</text>
27+ <text x="690" y="572">50%</text>
28+ <text x="890" y="572">75%</text>
29+ <text x="1090" y="572">100%</text>
30+ </g>
31+
32+ <g font-family="ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace" font-size="18">
33+
34+ <!-- Row 1: tdd.md 80.00% (SAMA-disciplined - highlighted) -->
35+ <text x="275" y="139" text-anchor="end" fill="#7ec77e" font-weight="600">tdd.md</text>
36+ <rect x="290" y="121" width="640" height="26" fill="#7ec77e"/>
37+ <text x="1175" y="139" text-anchor="end" fill="#7ec77e" font-weight="600">80.00%</text>
38+ <text x="1175" y="155" text-anchor="end" font-size="11" fill="#7ec77e">SAMA-disciplined</text>
39+
40+ <!-- Row 2: cli/cli (gh) Go 73.59% -->
41+ <text x="275" y="190" text-anchor="end" fill="#c8c8c8">cli/cli (gh)</text>
42+ <rect x="290" y="174" width="589" height="22" fill="#6a8db5"/>
43+ <text x="1175" y="190" text-anchor="end" fill="#c8c8c8">73.59%</text>
44+
45+ <!-- Row 3: sharkdp/fd Rust 69.57% -->
46+ <text x="275" y="238" text-anchor="end" fill="#c8c8c8">sharkdp/fd</text>
47+ <rect x="290" y="222" width="557" height="22" fill="#a07a5a"/>
48+ <text x="1175" y="238" text-anchor="end" fill="#c8c8c8">69.57%</text>
49+
50+ <!-- Row 4: lazygit Go 67.38% -->
51+ <text x="275" y="286" text-anchor="end" fill="#c8c8c8">lazygit</text>
52+ <rect x="290" y="270" width="539" height="22" fill="#6a8db5"/>
53+ <text x="1175" y="286" text-anchor="end" fill="#c8c8c8">67.38%</text>
54+
55+ <!-- Row 5: eza Rust 61.76% -->
56+ <text x="275" y="334" text-anchor="end" fill="#c8c8c8">eza</text>
57+ <rect x="290" y="318" width="494" height="22" fill="#a07a5a"/>
58+ <text x="1175" y="334" text-anchor="end" fill="#c8c8c8">61.76%</text>
59+
60+ <!-- Row 6: ripgrep Rust 54.00% -->
61+ <text x="275" y="382" text-anchor="end" fill="#c8c8c8">ripgrep</text>
62+ <rect x="290" y="366" width="432" height="22" fill="#a07a5a"/>
63+ <text x="1175" y="382" text-anchor="end" fill="#c8c8c8">54.00%</text>
64+
65+ <!-- Row 7: dive Go 52.17% -->
66+ <text x="275" y="430" text-anchor="end" fill="#c8c8c8">dive</text>
67+ <rect x="290" y="414" width="417" height="22" fill="#6a8db5"/>
68+ <text x="1175" y="430" text-anchor="end" fill="#c8c8c8">52.17%</text>
69+
70+ <!-- Row 8: bat Rust 46.27% -->
71+ <text x="275" y="478" text-anchor="end" fill="#c8c8c8">sharkdp/bat</text>
72+ <rect x="290" y="462" width="370" height="22" fill="#a07a5a"/>
73+ <text x="1175" y="478" text-anchor="end" fill="#c8c8c8">46.27%</text>
74+ </g>
75+
76+ <!-- Legend strip -->
77+ <g font-family="ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace" font-size="13" fill="#8a8a8a">
78+ <rect x="80" y="520" width="14" height="14" fill="#7ec77e"/>
79+ <text x="100" y="532">SAMA-disciplined (n=1)</text>
80+ <rect x="290" y="520" width="14" height="14" fill="#6a8db5"/>
81+ <text x="310" y="532">Go (n=3)</text>
82+ <rect x="420" y="520" width="14" height="14" fill="#a07a5a"/>
83+ <text x="440" y="532">Rust (n=4)</text>
84+ <text x="560" y="532" fill="#6a6a6a">· non-SAMA baseline: mean 60.68%, stddev 10.13pp, range 27.32pp</text>
85+ </g>
86+</svg>
modified src/d21_app.ts +19 −0
@@ -231,6 +231,25 @@ ${url("https://tdd.md/leaderboard", "0.7")}
231231 },
232232 }),
233233
234+ "/sama-hero.svg": new Response(Bun.file("./public/sama-hero.svg"), {
235+ headers: { "Content-Type": "image/svg+xml", "Cache-Control": "public, max-age=3600" },
236+ }),
237+ "/sama-hero.png": new Response(Bun.file("./public/sama-hero.png"), {
238+ headers: { "Content-Type": "image/png", "Cache-Control": "public, max-age=3600" },
239+ }),
240+ "/sama-layers.svg": new Response(Bun.file("./public/sama-layers.svg"), {
241+ headers: { "Content-Type": "image/svg+xml", "Cache-Control": "public, max-age=3600" },
242+ }),
243+ "/sama-layers.png": new Response(Bun.file("./public/sama-layers.png"), {
244+ headers: { "Content-Type": "image/png", "Cache-Control": "public, max-age=3600" },
245+ }),
246+ "/sama-metrics.svg": new Response(Bun.file("./public/sama-metrics.svg"), {
247+ headers: { "Content-Type": "image/svg+xml", "Cache-Control": "public, max-age=3600" },
248+ }),
249+ "/sama-metrics.png": new Response(Bun.file("./public/sama-metrics.png"), {
250+ headers: { "Content-Type": "image/png", "Cache-Control": "public, max-age=3600" },
251+ }),
252+
234253 "/games": htmlResponse(GAMES_INDEX_HTML),
235254
236255 "/blog": async () => {
modified src/d21_handlers_sama.ts +55 −39
@@ -380,48 +380,67 @@ export const samaVerifyHandler = async (req: { url: string }): Promise<Response>
380380
381381 const SAMA_LANDING_MD = `# SAMA
382382
383-> **Sorted, Architecture, Modeled, Atomic.** Four properties of a codebase that an AI agent can navigate, change, and verify without drift. The acronym is the rule set; each letter has a one-paragraph definition and a verification you can run.
383+> **Architecture as code, for code AI writes.** A formal v2 specification, a deterministic verifier that runs against this very repo on every deploy, and an n=8 cross-repo measurement baseline. The v1 practitioner pages are preserved below.
384384
385-This is the file-naming and module-organisation convention this site is built on, shared across two other projects in my workspace. It exists to give an AI agent **one obvious place** for every change — and one mechanical check for every layer rule.
385+![SAMA — Architecture as code, for code AI writes — Sorted · Architecture · Modeled · Atomic — 7/7 ✓ live · §5 metrics measured · n=8 cross-repo datapoints](/sama-hero.png?v=1)
386386
387-## the four disciplines
387+## the v2 specification (current)
388388
389-| letter | discipline | one-line rule |
390-|---|---|---|
391-%ROWS%
389+The formal, normative spec — frozen core + profile mechanism, written so a deterministic verifier in any language can ingest it — lives at **[/sama/v2](/sama/v2)** (v2.0 draft). Four canonical layers, one import law, a binary §4 conformance gate, and §5 core metrics for cross-repo empirical measurement. The TypeScript verifier that implements §4 is itself checked by the verifier; the website is the spec is the verifier is the test suite.
392390
393-## reading order
391+The verifier at **[/sama/v2/verify](/sama/v2/verify)** runs the seven §4 conformance checks against this very repository's source on every deploy. Right now it reports **✓ conforms · 7/7 checks pass**.
394392
395-If you're new to this:
396-1. Start with **[Sorted](/sama/sorted)** — it has the verification grep that everything else is built around.
397-2. Then **[Architecture](/sama/architecture)** — what each layer prefix means.
398-3. Then **[Modeled](/sama/modeled)** — where types and tests live.
399-4. Then **[Atomic](/sama/atomic)** — the split rule that keeps the rest honest as the codebase grows.
393+![The four canonical layers — Pure 0 / Core 1 / Adapter 2 / Entry 3 — stacked with downward import-direction arrows; the §1.2 Law: imports always point to a strictly lower layer number](/sama-layers.png?v=1)
400394
401-Each page is short, opinionated, and ends with the common mistakes you'll see if the discipline lapses.
395+## the empirical chain
396+
397+Compliance is binary. The interesting question is the *delta* — what changes when the rules are followed, measured across multiple repos. §5 of the spec defines five language-neutral core metrics so the delta can be measured rather than asserted. The polyglot \`workingSetFit\` emitter has now been run against eight projects at pinned commit SHAs:
402398
403-## the v2 specification (draft)
399+![§5 workingSetFit measured across n=8 cross-repo datapoints, tdd.md highlighted at 80% as the only SAMA-disciplined repo, non-SAMA baseline ranges from 46.27% (sharkdp/bat) to 73.59% (cli/cli) with mean 60.68%](/sama-metrics.png?v=1)
404400
405-The four discipline pages above are the practitioner-facing version. The formal, normative version — frozen core + profile mechanism, written so a deterministic verifier in any language can ingest it — lives at **[/sama/v2](/sama/v2)** (draft for v2.0). That doc defines the four canonical layers (Pure / Core / Adapter / Entry), the single import law, the binary conformance gate, and the SAMA-independent core metrics for cross-repo empirical measurement.
401+- **[Seven measured workingSetFit datapoints — what the baseline distribution actually looks like](/blog/sama-v2-workingset-cross-repo-baseline)** — n=2 → n=7 across mature compiled-language CLI tools. Range 27pp, mean 60.68%, sample stddev 10.13pp.
402+- **[Pointing SAMA v2 at \`dive\`](/blog/sama-v2-go-project-dive)** · **[Pointing SAMA v2 at \`ripgrep\`](/blog/sama-v2-rust-project-ripgrep)** — two non-SAMA mature CLI codebases audited and scored on two §5 axes (workingSetFit + graphDepth) at pinned SHAs.
403+- **[The §5 metrics emitter post](/blog/sama-v2-metrics-emitter)** — why measurement matters more than compliance, with the original metric build-out.
404+- **[The three v2.1 dialects at /sama/v2 §6.A](/sama/v2#6a-v21-dialects-provisional)** — provisional extensions (directory-layout, inline-tests, declarative-exemption) that the Rust + Go audits surfaced.
405+
406+**tdd.md** (the only SAMA-disciplined repo measured to date) lands at **80.00%** — 6.4pp above the top of the non-SAMA baseline. Suggestive, but n=1 vs n=7 is far from a SAMA-worth-following claim. §6 of the spec is explicit that promotion requires cross-repo deltas across multiple SAMA-disciplined repos.
406407
407408 ## drop into your agent
408409
409-For agents that load skills from \`~/.claude/skills/\` (Claude Code, obra/superpowers, etc.), grab the SKILL.md version:
410+For agents that load skills from \`~/.claude/skills/\` (Claude Code, obra/superpowers, etc.):
410411
411412 \`\`\`bash
412413 mkdir -p ~/.claude/skills
413414 curl -fsSL https://tdd.md/skills/sama.md -o ~/.claude/skills/sama.md
414415 \`\`\`
415416
416-The skill is the same content as the four pages here, written in obra/superpowers SKILL.md format with frontmatter, an iron-rule statement, and a verification checklist your agent can run before merging. **[Read it formatted →](/sama/skill)** · **[Raw markdown →](/skills/sama.md)**
417+The skill captures the load-bearing parts of the v2 spec in obra/superpowers SKILL.md format with frontmatter, an iron-rule statement, and a verification checklist your agent runs before merging. **[Read it formatted →](/sama/skill)** · **[Raw markdown →](/skills/sama.md)**
418+
419+---
417420
418-## verify any public repo
421+## v1 practitioner pages (preserved)
419422
420-Want to know whether a repo follows SAMA without reading its source? Paste the \`owner/name\` and tdd.md will run all four checks against the default branch — *Sorted* (the import-direction grep), *Architecture* (known layer prefixes), *Modeled* (sibling tests), *Atomic* (700-line + placeholder-test detection). Pass/fail per discipline, with violation lists. **[verify a repo on the web →](/sama/verify)** · or try it on this site: [\`syntaxai/tdd.md\`](/sama/verify?repo=syntaxai/tdd.md).
423+The four discipline pages below are the v1 practitioner-facing version of SAMA. They predate the v2 specification and remain accessible as background reading; the formal, normative version of every rule lives in [/sama/v2](/sama/v2). The v1 pages frame the same four properties in less spec-flavoured prose, with one-page-per-discipline depth and a working CI grep at the bottom of *Sorted*.
421424
422-## the \`sama\` CLI
425+### the four disciplines
423426
424-The web verifier is good for ad-hoc checks. For CI and pre-commit, install the standalone CLI — same checks, no network needed for local repos:
427+| letter | discipline | one-line rule |
428+|---|---|---|
429+%ROWS%
430+
431+### reading order
432+
433+If you're new to this:
434+1. Start with **[Sorted](/sama/sorted)** — it has the verification grep that everything else is built around.
435+2. Then **[Architecture](/sama/architecture)** — what each layer prefix means.
436+3. Then **[Modeled](/sama/modeled)** — where types and tests live.
437+4. Then **[Atomic](/sama/atomic)** — the split rule that keeps the rest honest as the codebase grows.
438+
439+Each page is short, opinionated, and ends with the common mistakes you'll see if the discipline lapses.
440+
441+### the v1 \`sama\` CLI
442+
443+For local CI and pre-commit hooks. The v1 CLI verifies the original four disciplines; the v2 verifier with the seven §4 conformance checks runs on the web at [/sama/v2/verify](/sama/v2/verify).
425444
426445 \`\`\`bash
427446 mkdir -p ~/.local/bin
@@ -440,17 +459,15 @@ sama verify-repo owner/name # verify a public GitHub repo (no token)
440459
441460 Exit codes: \`0\` on pass, \`1\` if any check fails, \`2\` on error. The CLI is a single Bun bundle (~14 KB). [Bun](https://bun.sh) needs to be on \`PATH\`.
442461
443-### pre-commit hook
444-
445-Add to \`.git/hooks/pre-commit\` (or via \`husky\`, \`pre-commit\`, \`lefthook\`):
462+#### pre-commit hook
446463
447464 \`\`\`bash
448465 #!/usr/bin/env bash
449-# Block commits that violate SAMA layer/atomic/modeled rules.
466+# Block commits that violate the v1 SAMA disciplines.
450467 exec sama check
451468 \`\`\`
452469
453-### GitHub Action
470+#### GitHub Action
454471
455472 \`\`\`yaml
456473 # .github/workflows/sama.yml
@@ -468,18 +485,14 @@ jobs:
468485 ./sama check
469486 \`\`\`
470487
471-If the rule lives in a hook or an action that fails the build, the harness can't talk the agent out of it. That is the whole point of the [corpus post](/blog/agentic-coding-corpus-three-patterns) and the next step from the [from-rules-to-checks](/blog/from-rules-to-checks) wrap-up.
488+### the case behind it (v1 essays)
472489
473-## the case behind it
490+Two long-form pieces that argued *why* SAMA was shaped this way, written before the v2 spec drafted:
474491
475-Two long-form pieces that argue *why* SAMA is shaped this way:
492+- [**The Claude Code harness postmortem read through TDD + SAMA**](/blog/claude-code-harness-postmortem) — ThePaSch's r/ClaudeAI audit read against the iron law and the verification grep.
493+- [**Three patterns ten threads converge on**](/blog/agentic-coding-corpus-three-patterns) — a six-month corpus of r/ClaudeAI, r/ClaudeCode, r/AgentsOfAI failure-mode threads with per-pattern mitigation tables.
476494
477-- [**The Claude Code harness postmortem read through TDD + SAMA**](/blog/claude-code-harness-postmortem) — ThePaSch's r/ClaudeAI audit (40+ hidden reminders, 5 gag-order sites, 158 prompt versions in 11 days) read against the iron law and the verification grep. *The harness is loud; the diff doesn't have to be.*
478-- [**Three patterns ten threads converge on**](/blog/agentic-coding-corpus-three-patterns) — a six-month corpus of r/ClaudeAI, r/ClaudeCode, r/AgentsOfAI failure-mode threads. Per-pattern mitigation tables map each thread to the SAMA / iron-law rule that catches or prevents it.
479-
480-If you're reading these for the first time, the order to take them is harness postmortem → corpus → back here.
481-
482-## why these four together
495+### why these four together
483496
484497 Each property fixes a different failure mode:
485498
@@ -488,9 +501,7 @@ Each property fixes a different failure mode:
488501 - *Modeled* fails when types and tests scatter → siblings are mandatory.
489502 - *Atomic* fails when files swell → the ~700-line split keeps atoms small.
490503
491-Pick one and you'll claw back some clarity. Pick all four and the codebase becomes the kind an agent can be left alone with — there is exactly one right place for any change, and a one-line shell command that proves the layer rule.
492-
493-The blog post [*Red, tokens, atoms*](/blog/three-constraints-agentic-coding) argues SAMA also compounds with TDD and Claude Code's token-saving discipline; the four properties on this page are the *Atomic* / *Modeled* / *Architecture* / *Sorted* halves of that story.
504+The blog post [*Red, tokens, atoms*](/blog/three-constraints-agentic-coding) argues SAMA also compounds with TDD and Claude Code's token-saving discipline.
494505
495506 [← back to tdd.md](/) · [the blog](/blog) · [the guides](/guides)
496507 `;
@@ -526,7 +537,12 @@ export const samaSlugHandler = async (req: { params: { slug: string } }): Promis
526537 const html = await renderNotFound(`/sama/${slug}`);
527538 return htmlResponse(html, 404);
528539 }
529- const md = await file.text();
540+ const rawMd = await file.text();
541+ // Stripe-style "older version" banner — prepended to every v1
542+ // discipline page so a reader landing directly on /sama/sorted etc.
543+ // sees the v2-spec pointer before the v1 prose.
544+ const v1Banner = `> **You are reading the v1 practitioner version of this property.** The formal, normative v2 specification — frozen core, profile mechanism, deterministic verifier, and §5 cross-repo measurement chain — lives at **[/sama/v2](/sama/v2)**. This page is preserved as background reading.\n\n`;
545+ const md = v1Banner + rawMd;
530546 const html = await renderDocsPage({
531547 title: `SAMA · ${entry.letter} — ${entry.title} — tdd.md`,
532548 description: entry.description,