4d9225e6a11d92fe8465abe39fde0022e998aee4 diff --git a/public/sama-hero.png b/public/sama-hero.png new file mode 100644 index 0000000000000000000000000000000000000000..b0425500976fde5874f6613c9d01dddd49a6a108 Binary files /dev/null and b/public/sama-hero.png differ diff --git a/public/sama-hero.svg b/public/sama-hero.svg new file mode 100644 index 0000000000000000000000000000000000000000..903432f6234149e6078b1afa88e67363ffb08d46 --- /dev/null +++ b/public/sama-hero.svg @@ -0,0 +1,24 @@ + + + + SAMA + Architecture as code, for code AI writes. + + + Sorted + · + Architecture + · + Modeled + · + Atomic + + + 7/7 ✓ live + · + §5 metrics measured + · + n=8 cross-repo datapoints + + https://tdd.md/sama + diff --git a/public/sama-layers.png b/public/sama-layers.png new file mode 100644 index 0000000000000000000000000000000000000000..f5e5d26b794bd5fc96c3469efce93f0e6b040919 Binary files /dev/null and b/public/sama-layers.png differ diff --git a/public/sama-layers.svg b/public/sama-layers.svg new file mode 100644 index 0000000000000000000000000000000000000000..0fefb6e93929166b83d9ff86967217bd20d2a155 --- /dev/null +++ b/public/sama-layers.svg @@ -0,0 +1,58 @@ + + + + + The four canonical layers + SAMA v2 §1.1 — frozen; no profile may add, remove, renumber, or rename them. + + + + + + Layer 3 · Entry + main · CLI handler · HTTP route · UI mount · job entry + may import 0, 1, 2 + + + + + + + + + + Layer 2 · Adapter + the boundary — DB · network · filesystem · framework bindings · external input parsed here + may import 0, 1 + + + + + + + + + + Layer 1 · Core + domain logic · decisions · pure render — no network, disk, clock, framework + may import 0 + + + + + + + + + + Layer 0 · Pure + types · constants · pure functions · domain models — no I/O, no side effects + imports nothing above + + + + + §1.2 the Law: + imports always point to a strictly lower layer number — never upward, never sideways, never cyclic. + + diff --git a/public/sama-metrics.png b/public/sama-metrics.png new file mode 100644 index 0000000000000000000000000000000000000000..cd25f4d83e99a332efc81558b1d188ed4c69153d Binary files /dev/null and b/public/sama-metrics.png differ diff --git a/public/sama-metrics.svg b/public/sama-metrics.svg new file mode 100644 index 0000000000000000000000000000000000000000..fedd122bcac5afcfd1953721fa017198193a551d --- /dev/null +++ b/public/sama-metrics.svg @@ -0,0 +1,86 @@ + + + + + §5 workingSetFit · measured · n=8 cross-repo datapoints + share of source files in the [50, 500] LOC working-set band · same bounds, same emitter, same source-of-truth. + + + + + + + + + + + + + + + 0% + 25% + 50% + 75% + 100% + + + + + + tdd.md + + 80.00% + SAMA-disciplined + + + cli/cli (gh) + + 73.59% + + + sharkdp/fd + + 69.57% + + + lazygit + + 67.38% + + + eza + + 61.76% + + + ripgrep + + 54.00% + + + dive + + 52.17% + + + sharkdp/bat + + 46.27% + + + + + + SAMA-disciplined (n=1) + + Go (n=3) + + Rust (n=4) + · non-SAMA baseline: mean 60.68%, stddev 10.13pp, range 27.32pp + + diff --git a/src/d21_app.ts b/src/d21_app.ts index bca4ee3efac684049f09486ab98469218852fb7b..b8d83927cefe453543418cbf7d22629de64a9d62 100644 --- a/src/d21_app.ts +++ b/src/d21_app.ts @@ -231,6 +231,25 @@ ${url("https://tdd.md/leaderboard", "0.7")} }, }), + "/sama-hero.svg": new Response(Bun.file("./public/sama-hero.svg"), { + headers: { "Content-Type": "image/svg+xml", "Cache-Control": "public, max-age=3600" }, + }), + "/sama-hero.png": new Response(Bun.file("./public/sama-hero.png"), { + headers: { "Content-Type": "image/png", "Cache-Control": "public, max-age=3600" }, + }), + "/sama-layers.svg": new Response(Bun.file("./public/sama-layers.svg"), { + headers: { "Content-Type": "image/svg+xml", "Cache-Control": "public, max-age=3600" }, + }), + "/sama-layers.png": new Response(Bun.file("./public/sama-layers.png"), { + headers: { "Content-Type": "image/png", "Cache-Control": "public, max-age=3600" }, + }), + "/sama-metrics.svg": new Response(Bun.file("./public/sama-metrics.svg"), { + headers: { "Content-Type": "image/svg+xml", "Cache-Control": "public, max-age=3600" }, + }), + "/sama-metrics.png": new Response(Bun.file("./public/sama-metrics.png"), { + headers: { "Content-Type": "image/png", "Cache-Control": "public, max-age=3600" }, + }), + "/games": htmlResponse(GAMES_INDEX_HTML), "/blog": async () => { diff --git a/src/d21_handlers_sama.ts b/src/d21_handlers_sama.ts index 265019fb17e21db05a9b2fd949edf9d118f4ea23..af4430d813e99a9c9109a585a9dbf3363c81dc5e 100644 --- a/src/d21_handlers_sama.ts +++ b/src/d21_handlers_sama.ts @@ -380,48 +380,67 @@ export const samaVerifyHandler = async (req: { url: string }): Promise const SAMA_LANDING_MD = `# SAMA -> **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. +> **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. -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. +![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) -## the four disciplines +## the v2 specification (current) -| letter | discipline | one-line rule | -|---|---|---| -%ROWS% +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. -## reading order +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**. -If you're new to this: -1. Start with **[Sorted](/sama/sorted)** — it has the verification grep that everything else is built around. -2. Then **[Architecture](/sama/architecture)** — what each layer prefix means. -3. Then **[Modeled](/sama/modeled)** — where types and tests live. -4. Then **[Atomic](/sama/atomic)** — the split rule that keeps the rest honest as the codebase grows. +![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) -Each page is short, opinionated, and ends with the common mistakes you'll see if the discipline lapses. +## the empirical chain + +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: -## the v2 specification (draft) +![§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) -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. +- **[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. +- **[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. +- **[The §5 metrics emitter post](/blog/sama-v2-metrics-emitter)** — why measurement matters more than compliance, with the original metric build-out. +- **[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. + +**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. ## drop into your agent -For agents that load skills from \`~/.claude/skills/\` (Claude Code, obra/superpowers, etc.), grab the SKILL.md version: +For agents that load skills from \`~/.claude/skills/\` (Claude Code, obra/superpowers, etc.): \`\`\`bash mkdir -p ~/.claude/skills curl -fsSL https://tdd.md/skills/sama.md -o ~/.claude/skills/sama.md \`\`\` -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)** +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)** + +--- -## verify any public repo +## v1 practitioner pages (preserved) -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). +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*. -## the \`sama\` CLI +### the four disciplines -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: +| letter | discipline | one-line rule | +|---|---|---| +%ROWS% + +### reading order + +If you're new to this: +1. Start with **[Sorted](/sama/sorted)** — it has the verification grep that everything else is built around. +2. Then **[Architecture](/sama/architecture)** — what each layer prefix means. +3. Then **[Modeled](/sama/modeled)** — where types and tests live. +4. Then **[Atomic](/sama/atomic)** — the split rule that keeps the rest honest as the codebase grows. + +Each page is short, opinionated, and ends with the common mistakes you'll see if the discipline lapses. + +### the v1 \`sama\` CLI + +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). \`\`\`bash mkdir -p ~/.local/bin @@ -440,17 +459,15 @@ sama verify-repo owner/name # verify a public GitHub repo (no token) 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\`. -### pre-commit hook - -Add to \`.git/hooks/pre-commit\` (or via \`husky\`, \`pre-commit\`, \`lefthook\`): +#### pre-commit hook \`\`\`bash #!/usr/bin/env bash -# Block commits that violate SAMA layer/atomic/modeled rules. +# Block commits that violate the v1 SAMA disciplines. exec sama check \`\`\` -### GitHub Action +#### GitHub Action \`\`\`yaml # .github/workflows/sama.yml @@ -468,18 +485,14 @@ jobs: ./sama check \`\`\` -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. +### the case behind it (v1 essays) -## the case behind it +Two long-form pieces that argued *why* SAMA was shaped this way, written before the v2 spec drafted: -Two long-form pieces that argue *why* SAMA is shaped this way: +- [**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. +- [**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. -- [**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.* -- [**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. - -If you're reading these for the first time, the order to take them is harness postmortem → corpus → back here. - -## why these four together +### why these four together Each property fixes a different failure mode: @@ -488,9 +501,7 @@ Each property fixes a different failure mode: - *Modeled* fails when types and tests scatter → siblings are mandatory. - *Atomic* fails when files swell → the ~700-line split keeps atoms small. -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. - -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. +The blog post [*Red, tokens, atoms*](/blog/three-constraints-agentic-coding) argues SAMA also compounds with TDD and Claude Code's token-saving discipline. [← back to tdd.md](/) · [the blog](/blog) · [the guides](/guides) `; @@ -526,7 +537,12 @@ export const samaSlugHandler = async (req: { params: { slug: string } }): Promis const html = await renderNotFound(`/sama/${slug}`); return htmlResponse(html, 404); } - const md = await file.text(); + const rawMd = await file.text(); + // Stripe-style "older version" banner — prepended to every v1 + // discipline page so a reader landing directly on /sama/sorted etc. + // sees the v2-spec pointer before the v1 prose. + 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`; + const md = v1Banner + rawMd; const html = await renderDocsPage({ title: `SAMA · ${entry.letter} — ${entry.title} — tdd.md`, description: entry.description,