S.O.F.T. C.O.D.E.

The 8 Layers of Soft Code Architecture — A Reference for Agents and Humans

SOFT = Core

Pure Python. No I/O. No frameworks. All business logic lives here. Every function either validates, decides, or sequences — never touches the outside world.

CODE = Shell

Transport, I/O, governance. HTTP routes, file operations, presenter mapping, and mechanical enforcement. Thin wrappers that call Core through the Contract wall.

SOFT — Core Layers (pure, no side effects)

LayerFilesGuiding QuestionHard Rule
S Shapes domain/models.py, domain/specs.py, schema_checklist.md What does the data look like? Frozen dataclasses only. No methods with side effects. All constants, thresholds, and vocabulary here.
O Operations application/decision_logic.py, input_validation.py, output_mapping.py What decisions does the tool make? Pure functions: data in → data out. No open(), no os, no requests. Every if lives here.
F Flow application/orchestrator.py (+ optional workflow.py) What order do operations run in? Zero branching. No if/for/while/try/with/match. ≤60 lines. A recipe card, not a chef.
T Terms contracts.py How do other systems call this tool? ToolInput, ToolOutput, run(). Versioned. The only import adapters may touch.

CODE — Shell Layers (I/O, transport, governance)

LayerFilesGuiding QuestionHard Rule
C Channel adapters/flask/<tool>/app.py How does the request arrive? HTTP routes only. Calls core exclusively via contracts.run(). No business logic.
O Outward adapters/flask/<tool>/presenter.py How is the result shaped for the user? Maps HTTP params → contract payload, and contract result → template context. No domain rules.
D Disk adapters/flask/<tool>/io.py Where do side effects happen? File reads, file writes, downloads, in-memory caching. The only place side effects live.
E Enforce scripts/preflight.py, tools_registry.json, DECISIONS.md How do we know it’s correct? Mechanical validation. No discretion. Preflight passes or it doesn’t ship.

Data Flow Diagram

USER REQUEST ↓ ┌───────────└ │ Channel │ app.py — receives HTTP, extracts files/params ┐───────────┘┌───────────└ │ Outward │ presenter.py — builds contract payload from HTTP ┐───────────┘┌───────────└ │ Terms │ contracts.py — ToolInput → run() → ToolOutput ┐───────────┘──── THE WALL ───── nothing below does I/O ↓ ┌───────────└ │ Flow │ orchestrator.py — calls operations in sequence ┐───────────┘┌───────────└ │ Operat’s │ decision_logic / input_validation / output_mapping ┐───────────┘┌───────────└ │ Shapes │ models.py (dataclasses) + specs.py (constants) ┐───────────┘──── THE WALL ───── back through to the shell ↓ ┌───────────└ │ Outward │ presenter.py — formats result for template ┐───────────┘┌───────────└ │ Disk │ io.py — writes files, stores sessions ┐───────────┘┌───────────└ │ Channel │ app.py — returns HTTP response ┐───────────┘ ↓ USER RESPONSE

The “Which Layer?” Decision Tree

When you’re about to write code, ask:

  1. Is it a dataclass or a constant? S — Shapes
  2. Does it contain if/for/while business logic? O — Operations
  3. Does it sequence function calls with no branching? F — Flow
  4. Is it ToolInput/ToolOutput/run()? T — Terms
  5. Does it handle HTTP requests/responses? C — Channel
  6. Does it convert between HTTP and domain? O — Outward
  7. Does it touch the filesystem or network? D — Disk
  8. Does it validate architecture rules? E — Enforce

Why the Contract (Terms) Is the Load-Bearing Seam

Build Order Maps to the Acronym

StepLayerWhat You Produce
1. Decision listE — EnforceDECISIONS.md
2. Data shapesS — Shapesmodels.py, specs.py, contracts.py, examples
3. Decision functionsO — Operationsdecision_logic.py, input_validation.py, output_mapping.py + tests
4. OrchestratorF — Floworchestrator.py
5. AdaptersC + O + Dapp.py, presenter.py, io.py
6. VerificationE — Enforcepreflight passes, registry updated

Prompt-Ready Reminder

Paste this into CLAUDE.md or a system prompt to keep the agent on-track:

SOFT CODE LAYERS — put code in the right layer: S — Shapes: domain/models.py, specs.py (dataclasses, constants) O — Operations: application/decision_logic.py, input_validation.py, output_mapping.py (pure functions, all branching here) F — Flow: application/orchestrator.py (zero branching, ≤60 lines) T — Terms: contracts.py (ToolInput/ToolOutput/run — the API wall) ——— THE WALL: nothing above does I/O ——— C — Channel: adapters/app.py (HTTP routes, calls contracts.run only) O — Outward: adapters/presenter.py (HTTP ↔ domain mapping) D — Disk: adapters/io.py (all side effects here and only here) E — Enforce: scripts/preflight.py (mechanical rule checking)