d2574fd967f823d9df2b5ee13b5d186f41ccb642 diff --git a/content/sama/v2.md b/content/sama/v2.md new file mode 100644 index 0000000000000000000000000000000000000000..b1d47e9a5ec21d5f176b8b8295bd85462fd304a1 --- /dev/null +++ b/content/sama/v2.md @@ -0,0 +1,159 @@ +# SAMA v2 — Core Specification + +> **Status:** Draft for v2.0. This document defines the *frozen core* and the *profile mechanism*. The core is normative and stable; profiles are the only extension point. Anything a CI job cannot check deterministically does not belong in this spec — it belongs in `AGENTS.md`. + +--- + +## 0. Design contract + +SAMA separates **the law** (how layers may depend on each other — frozen, language-neutral, identical in every repo) from **the vocabulary** (which named sublayers a given domain uses — supplied by a profile). + +Two SAMA repositories in different languages and different profiles remain comparable at the core level. That comparability is what makes cross-repo empirical measurement possible: every repo, regardless of language or profile, emits the same core metrics. + +A conformant verifier is a **deterministic program**. No LLM judgment sits in the enforcement loop. An agent may *use* SAMA to decide where a file goes; it may never be the *referee* that decides whether a file conforms. + +--- + +## 1. The frozen core + +### 1.1 The four canonical layers + +Every file in a SAMA repository belongs to exactly one of four layers. This set is **frozen**: no profile, repo, or version may add, remove, renumber, or rename a canonical layer. + +| Layer | Name | Contains | May import | +|---|---|---|---| +| 0 | Pure | Types, constants, pure functions, domain models. No I/O, no side effects. | nothing above 0 (i.e. only other Layer 0) | +| 1 | Core | Domain logic and decisions. No network, disk, clock, or framework. | 0 | +| 2 | Adapter | The boundary. External input is **parsed here and only here** (never cast). DB, network, filesystem, framework bindings. | 0, 1 | +| 3 | Entry | Outermost shell: `main`, CLI handler, HTTP route, UI mount, job entry. | 0, 1, 2 | + +Layer 0 depends on nothing. Layer 3 is depended on by nothing. + +### 1.2 The Law (frozen, one sentence) + +> **Imports always point to a strictly lower layer number — never upward, never sideways across a higher number, never cyclic.** + +Formally, for any import edge `A → B`: `layer(B) < layer(A)`, OR `A` and `B` are in the same layer **and** the active profile declares a sublayer ordering that permits `A → B` (see §2.2). The whole-program import graph must be acyclic. + +This single law is what makes the **Sorted** pillar enforceable: the layer number is the lexicographic sort key, so file order *is* dependency direction. + +### 1.3 Why exactly four + +Four rings is the minimal set that captures the only relation that matters for context rot: *what is allowed to depend on what.* Fewer cannot express the parse-at-the-boundary rule (it needs a distinct Adapter layer). More reintroduce ambiguity about where a file belongs — which is the drift SAMA exists to kill. This is the "as simple as possible, but not simpler" line. + +--- + +## 2. Profiles + +A profile is the **only** extension mechanism. It does exactly one thing: + +> A profile MAY subdivide a canonical layer into named, ordered sublayers. A profile MUST NOT introduce, remove, reverse, or otherwise alter any dependency relation between the four canonical layers. + +If a proposed profile rule cannot be expressed as "subdivide layer N into ordered sublayers," it is not a profile rule. It is either core (and frozen) or out of scope (and belongs in `AGENTS.md`). + +### 2.1 What a profile may and may not do + +| Allowed | Forbidden | +|---|---| +| Split Layer 2 into `repository → gateway → controller` | Let Layer 1 import Layer 2 | +| Leave a canonical layer empty (e.g. CLI has no DB) | Add a fifth canonical layer | +| Define intra-layer sublayer ordering | Make Layer 0 perform I/O | +| Map sublayer names to filename prefixes | Reverse the import direction between core layers | + +### 2.2 Sublayer ordering + +Within a layer, sublayers are totally ordered. An import between two files in the same canonical layer is legal only if it points to an equal-or-lower sublayer in the profile's declared order. Cross-layer imports are governed solely by §1.2 and ignore sublayer order. + +### 2.3 Profile declaration format + +A profile is a single machine-readable file the verifier ingests (`sama.profile.toml`). Example — an HTTP service: + +```toml +sama_version = "2.0" +profile = "http-service" + +# Map each canonical layer to ordered sublayers and their filename prefixes. +# Order in the array = dependency order (later may import earlier, never reverse). + +[layers.0] # Pure — not subdivided +prefixes = ["p0_"] + +[layers.1] # Core +sublayers = [ + { name = "policy", prefix = "c1a_" }, + { name = "service", prefix = "c1b_" }, # service may import policy +] + +[layers.2] # Adapter +sublayers = [ + { name = "repository", prefix = "a2a_" }, + { name = "gateway", prefix = "a2b_" }, + { name = "controller", prefix = "a2c_" }, # controller → gateway → repository +] + +[layers.3] # Entry +prefixes = ["e3_"] +``` + +A `cli` profile would leave `[layers.2]` minimal and subdivide `[layers.3]` into `arg-parser → dispatch`. A `frontend` profile would subdivide Layer 1 into `store` vs `view-logic`. Same law, different dialect. + +--- + +## 3. Layer assignment & the consistency check + +The hard step is not checking the law — it is knowing each file's layer. SAMA uses **prefix as the source of truth, with a consistency check against actual imports.** + +1. **Declared layer** = the canonical layer implied by the file's prefix (per the active profile's prefix map). +2. **Observed layer ceiling** = the highest layer any of the file's imports resolves to. +3. **Consistency rule:** the verifier FAILS if a file imports from a layer that its declared layer is not permitted to import — i.e. if the prefix claims something the imports contradict. + +This gives a deterministic gate *and* protection against a misdeclared (or dishonest) prefix. A file cannot launder a forbidden dependency by lying about its layer: the import graph exposes it. + +--- + +## 4. Conformance — the binary gate + +A repository **conforms to SAMA v2** if and only if all of the following pass. Each is a deterministic check; the result is binary. + +1. **Sorted** — every file carries a profile-recognized prefix; lexicographic prefix order equals layer order. +2. **Architecture** — every file maps to exactly one canonical layer via §2.3; no file is unprefixed or maps to two layers. +3. **Modeled (tests)** — every Layer 1 and Layer 2 behavior file has a sibling test file. +4. **Modeled (boundary)** — external input is parsed only in Layer 2. (Verifier support is profile-dependent; see §6.) +5. **Atomic** — no file exceeds the line cap (default ~700; profile may lower, never raise). No barrel re-export files. +6. **The Law** — the import graph is acyclic and every edge satisfies §1.2. +7. **Consistency** — no file's imports contradict its declared layer (§3). + +If any check fails, the repository does not conform. There is no partial pass, no score-to-taste. (Profiles and *measurement* are graded; **conformance** is binary.) + +--- + +## 5. Core metrics (the SAMA-independent outcome) + +Every conformant repo emits these, identically, regardless of language or profile. These are the variables for A/B measurement (`SAMA on` vs `off`) — and crucially, **none of them is a compliance score.** They measure properties an agent's task performance should correlate with: + +- **Graph depth** — longest path in the import DAG. +- **Fan-in / fan-out distribution** per layer. +- **Boundary ratio** — share of external-input parsing that occurs in Layer 2. +- **Working-set fit** — share of files within the editor LOC sweet spot. +- **Violation count over time** — emitted even on conforming repos as a trailing signal (which rules agents *almost* break). + +Report the **delta** between SAMA-on and SAMA-off runs on these metrics — not the compliance rate. Compliance proves the rules were followed; the delta is what proves the rules were *worth* following. + +--- + +## 6. Evolution policy (how the standard stays alive without rotting) + +- **The core (§1) is frozen.** Changing the four layers or the Law requires a major version and an extraordinarily high evidentiary bar: cross-repo data showing the current core measurably harms agent performance. +- **Profiles are the moving edge.** A new profile is a *falsifiable hypothesis*: "this sublayer split lowers context cost for this domain." It is admitted provisionally, measured against §5, and promoted to "official" only if the delta holds across multiple repos. +- **A rule agents structurally violate is a signal — to be triaged, not auto-relaxed.** Either the rule is right and the agent must improve (signal to agent-builders), or the rule is impractical and the *profile* adapts (never the core). The feedback loop tunes profiles; it does not erode the law. + +--- + +## Appendix A — Mapping to the four pillars + +| Pillar | Where it lives in v2 | +|---|---| +| **S** — Sorted | §1.2 Law + §4.1; prefix = layer = sort key | +| **A** — Architecture | §1.1 four layers + §2 profiles (the fix for the weak A) | +| **M** — Modeled | §4.3 sibling tests + §4.4 boundary parsing (Layer 2) | +| **A** — Atomic | §4.5 line cap + no barrels | diff --git a/src/c21_app.ts b/src/c21_app.ts index 9b4185bb63af9ebbc3ede62e18a66da41e29b40a..ca0d9f6af37c9e33e040f248a43ef4207f1f4a63 100644 --- a/src/c21_app.ts +++ b/src/c21_app.ts @@ -32,6 +32,7 @@ import { skillsSamaMdHandler, samaCliResponse, samaSkillHandler, + samaV2Handler, samaVerifyHandler, samaLandingHandler, samaSlugHandler, @@ -359,6 +360,8 @@ ${rows} "/sama/skill": samaSkillHandler, + "/sama/v2": samaV2Handler, + "/sama/verify": samaVerifyHandler, "/sama": samaLandingHandler, diff --git a/src/c21_handlers_sama.ts b/src/c21_handlers_sama.ts index a7af480cf0f4130c57f688eae4b6e9d3e39b1b06..238395fceb829a79afb606be000fa9eeff7de994 100644 --- a/src/c21_handlers_sama.ts +++ b/src/c21_handlers_sama.ts @@ -61,6 +61,22 @@ export const samaSkillHandler = async (): Promise => { return htmlResponse(html); }; +// -------- /sama/v2 (the SAMA v2 Core Specification — draft) -------- + +export const samaV2Handler = async (): Promise => { + const md = await Bun.file("./content/sama/v2.md").text(); + const html = await renderDocsPage({ + title: "SAMA v2 — Core Specification (draft) — tdd.md", + description: + "Draft of the SAMA v2 Core Specification: four canonical layers (Pure / Core / Adapter / Entry), one frozen import law, profiles as the only extension mechanism. Defines the binary conformance gate and the SAMA-independent core metrics for cross-repo empirical measurement.", + bodyMarkdown: md, + ogPath: "https://tdd.md/sama/v2", + active: "sama", + pathForDocs: "/sama/v2", + }); + return htmlResponse(html); +}; + // -------- /sama/verify (form + report + dogfood short-circuit) -------- const VERIFY_FORM_MD = `# SAMA verify @@ -244,6 +260,10 @@ If you're new to this: Each page is short, opinionated, and ends with the common mistakes you'll see if the discipline lapses. +## the v2 specification (draft) + +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. + ## drop into your agent For agents that load skills from \`~/.claude/skills/\` (Claude Code, obra/superpowers, etc.), grab the SKILL.md version: