propose an edit → · view source →

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. This page is preserved as background reading.

A — Architecture

Rule: the number is the layer; the layer is the contract. Pick the layer first, then the name.

The second letter. Where Sorted says "files line up by prefix", Architecture says what each prefix means. A file's cXX_*.ts prefix tells the agent — and the reviewer — exactly what kind of code is allowed inside it.

#The layers

prefix layer what's allowed what's not
c11_* server entry env, Bun.serve(), port wiring route logic, SQL, HTML
c13_* database SQLite queries, schema HTTP, HTML, route handling
c14_* secondary I/O HTTP clients (GitHub, Forgejo, mail) SQL, business logic
c21_* handlers route handlers, composes lower layers direct SQL, raw HTTP, model definitions
c31_* models types, parsers, pure data helpers I/O of any kind, side effects
c32_* logic pure business logic, deterministic transforms I/O, randomness, time without injection
c51_* UI HTML rendering, page chrome data fetching, mutations

Numbers are spaced (no c12, no c22) so future layers can land between existing ones without renaming the world.

#Why prefix-as-contract

The alternative — folder names like src/handlers/, src/models/, src/db/ — looks tidier but pays a hidden tax: an agent moving between projects has to relearn the folder layout each time. The SAMA prefix is one rule, three projects, identical everywhere. Once you know c31 means "model", you know it forever.

The prefix also keeps the import grep simple. Folder-based layouts force you to check every import against a multi-line config of "which folder may import which folder". The number does the same job in one comparison.

#Picking the right prefix

Decide in this order:

  1. Does it perform I/O? Yes → c13 (SQL) or c14 (HTTP). No → continue.
  2. Is it pure types or parsers, with no logic? Yes → c31.
  3. Is it pure logic that derives one value from others? Yes → c32.
  4. Does it produce HTML? Yes → c51.
  5. Is it a route handler that composes the above? Yes → c21.

If you can't fit the file into one of those buckets, the file is doing more than one job — split it. Atomic covers the split rule.

#Examples from this codebase

c11_server.ts          PORT + Bun.serve(createApp())
c13_database.ts        SQLite (runs + projects tables)
c14_github.ts          GitHub OAuth + commits API + raw-content fetch
c14_forgejo.ts         Forgejo HTTP + proxy
c21_app.ts             routes literal + dispatcher
c21_handlers_agents.ts route handlers for /agents/*
c31_blog.ts            ALL_POSTS registry (pure data)
c31_commits.ts         parseCommit + computeProgress (parser + helper)
c31_project_config.ts  .tdd-md.json schema + parser
c32_judge.ts           kata-judging logic (pure)
c32_session.ts         HMAC sign/verify + cookie helpers (pure)
c51_render_layout.ts   page chrome, escape, htmlResponse
c51_render_reports.ts  /reports body builders

Reading top-to-bottom = reading the dependency graph. c21_app.ts imports things from its row and above. c11_server.ts imports from c21 only. Nothing reaches "down" past its own number.

#Common mistakes

  • A handler doing its own SQL. If c21_handlers_*.ts calls db.query(...) directly, the database concern leaked. Move the query into c13_* and have the handler call the helper.
  • A model that imports fetch. c31_* is pure. If you find yourself adding I/O, the file becomes c14_* or splits in two: a c31_* model + a c14_* client that produces it.
  • A renderer that parses input. c51_* produces HTML from already-shaped data. Parsing belongs in c31_*, never inside the renderer.

When you spot a violation, the fix is always the same shape: split the file along the layer line. Two right files beat one wrong file every time.

#What this gives you

  • A reviewer can read a diff and tell whether a change belongs in this file just from the prefix. No need to re-derive what should go where.
  • An agent asked to "add a route" knows it lives in c21_* without thinking.
  • "Where do the types go?" has one answer: c31_*.

← S — Sorted · /sama · next: M — Modeled →