syntaxai/tdd.md · commit aca2b1e

Redesign sama-layers image based on feedback (v2)

Original image was strak/manifest-style but didn't differentiate the
layers visually or land the core message clearly. Five concrete pieces
of feedback addressed:

1. Core vs Pure disambiguation:
   Layer 1 Core → "domain decisions — business logic · policies"
   Layer 0 Pure → "deterministic primitives — types · constants"
   (Spec names stay; only the per-box subtitle is sharper.)

2. Visual differentiation between layers:
   Each box gets an 8px colored left-stripe forming a warm-to-cool
   gradient top to bottom — Entry amber, Adapter orange, Core teal,
   Pure green. Reads as "volatile/external at top, stable/foundation
   at bottom" without being garish.

3. Replaced "may import 0, 1, 2" labels:
   Per-box import-list labels removed. Direction is now told by
   simple "↓ uses" labels between boxes. Less cognitive load.

4. Concrete worked example threaded through all four boxes:
   POST /checkout → Stripe.charges.create() → applyPaymentRules()
                 → Money(amount, currency). Each italic line colored
   to match its layer's stripe, creating implicit visual chaining
   without explicit arrows between examples.

5. Main message scaled up:
   "Imports only flow downward." is now the 38pt headline. The full
   §1.2 Law lives below as a 16pt subtitle.

Image bumped to ?v=2 in the markdown reference so the new render
lands immediately on visitors who saw v=1.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
author
syntaxai <[email protected]>
date
2026-05-24 12:43:43 +01:00
parent
9f41d89
commit
aca2b1e133a0b37d4d0076f8913051982c868429

3 files changed · +36 −37

modified public/sama-layers.png +0 −0
modified public/sama-layers.svg +35 −36
@@ -1,58 +1,57 @@
11 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 600" width="1200" height="600">
22 <rect width="1200" height="600" fill="#0a0a0a"/>
33
4+ <!-- Header — the Law is the main message, scaled up -->
45 <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>
6+ <text x="120" y="46" font-size="20" font-weight="600" fill="#909090">The four canonical layers</text>
7+ <text x="120" y="92" font-size="38" font-weight="700" fill="#e8e8e8">Imports only flow downward.</text>
8+ <text x="120" y="122" font-size="16" fill="#7a7a7a">never upward · never sideways · never cyclic — SAMA v2 §1.2 the Law</text>
79 </g>
810
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+ <!-- Layer 3 · Entry (top — warm/volatile) -->
12+ <rect x="120" y="148" width="960" height="80" fill="#1a1a1a" stroke="#2a2a2a" stroke-width="1.5" rx="6"/>
13+ <rect x="120" y="148" width="8" height="80" fill="#c89a3a" rx="3"/>
1114 <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+ <text x="160" y="182" font-size="24" font-weight="600" fill="#e8e8e8">Layer 3 · Entry</text>
16+ <text x="160" y="210" font-size="15" fill="#8a8a8a">outermost shell — main · CLI handler · HTTP route · UI mount · job entry</text>
17+ <text x="1060" y="195" text-anchor="end" font-size="17" font-style="italic" fill="#c89a3a">POST /checkout</text>
1518 </g>
1619
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+ <text x="600" y="246" text-anchor="middle" font-family="ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace" font-size="13" fill="#6a6a6a">↓ uses</text>
2021
21- <!-- Layer 2: Adapter -->
22- <rect x="200" y="232" width="800" height="78" fill="#1a1a1a" stroke="#3a3a3a" stroke-width="1.5" rx="6"/>
22+ <!-- Layer 2 · Adapter (boundary — warm) -->
23+ <rect x="120" y="253" width="960" height="80" fill="#1a1a1a" stroke="#2a2a2a" stroke-width="1.5" rx="6"/>
24+ <rect x="120" y="253" width="8" height="80" fill="#b8794a" rx="3"/>
2325 <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>
26+ <text x="160" y="287" font-size="24" font-weight="600" fill="#e8e8e8">Layer 2 · Adapter</text>
27+ <text x="160" y="315" font-size="15" fill="#8a8a8a">the boundary — DB · network · filesystem · framework bindings · external input parsed here</text>
28+ <text x="1060" y="300" text-anchor="end" font-size="17" font-style="italic" fill="#b8794a">Stripe.charges.create()</text>
2729 </g>
2830
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"/>
31+ <text x="600" y="351" text-anchor="middle" font-family="ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace" font-size="13" fill="#6a6a6a">↓ uses</text>
3232
33- <!-- Layer 1: Core -->
34- <rect x="200" y="349" width="800" height="78" fill="#1a1a1a" stroke="#3a3a3a" stroke-width="1.5" rx="6"/>
33+ <!-- Layer 1 · Core (cool — stable) -->
34+ <rect x="120" y="358" width="960" height="80" fill="#1a1a1a" stroke="#2a2a2a" stroke-width="1.5" rx="6"/>
35+ <rect x="120" y="358" width="8" height="80" fill="#4a8a8a" rx="3"/>
3536 <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>
37+ <text x="160" y="392" font-size="24" font-weight="600" fill="#e8e8e8">Layer 1 · Core</text>
38+ <text x="160" y="420" font-size="15" fill="#8a8a8a">domain decisions — business logic · policies · pure render — no network, disk, clock, framework</text>
39+ <text x="1060" y="405" text-anchor="end" font-size="17" font-style="italic" fill="#4a8a8a">applyPaymentRules()</text>
3940 </g>
4041
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"/>
42+ <text x="600" y="456" text-anchor="middle" font-family="ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace" font-size="13" fill="#6a6a6a">↓ uses</text>
4443
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"/>
44+ <!-- Layer 0 · Pure (bottom — coolest, mathematical foundation) -->
45+ <rect x="120" y="463" width="960" height="80" fill="#1a1a1a" stroke="#2a2a2a" stroke-width="1.5" rx="6"/>
46+ <rect x="120" y="463" width="8" height="80" fill="#4a9a5a" rx="3"/>
4747 <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>
48+ <text x="160" y="497" font-size="24" font-weight="600" fill="#e8e8e8">Layer 0 · Pure</text>
49+ <text x="160" y="525" font-size="15" fill="#8a8a8a">deterministic primitives — types · constants · pure functions — no I/O, no side effects</text>
50+ <text x="1060" y="510" text-anchor="end" font-size="17" font-style="italic" fill="#4a9a5a">Money(amount, currency)</text>
5151 </g>
5252
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>
53+ <!-- Worked-example caption -->
54+ <text x="120" y="582" font-family="ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace" font-size="13" fill="#6a6a6a">
55+ Worked example, threaded through a checkout flow — each italic line shows what that layer would actually do.
56+ </text>
5857 </svg>
modified src/d21_handlers_sama.ts +1 −1
@@ -390,7 +390,7 @@ The formal, normative spec — frozen core + profile mechanism, written so a det
390390
391391 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**.
392392
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)
393+![The four canonical layers — Imports only flow downward. POST /checkout → Stripe.charges.create() → applyPaymentRules() → Money(amount, currency). Color-coded warm-to-cool, volatile-to-stable: Entry 3 amber, Adapter 2 orange, Core 1 teal, Pure 0 green.](/sama-layers.png?v=2)
394394
395395 ## the empirical chain
396396