For AI coding agents
Where the change goes.
Humans ask when architecture is unclear. Agents infer and continue. So this
page states where things go rather than describing qualities to aim for —
and the repository's AGENTS.md is the machine-readable version of it.
Governance
Three systems, three questions.
They do not overlap. When one answers, the others do not override it.
| System | Governs | What it asks of you |
|---|---|---|
| Ordo / SDE | Engineering legality and evidence | Know the current state, the legal transition, and the evidence required before you change anything. The installed method is in .sde/. |
| Praxis / ROS | Work lifecycle and attribution | Start a work item before editing and complete it with real evidence. CI rejects changes no work item accounts for. |
| Limen | Architectural placement | Put each change in the one layer allowed to own it. The rest of this page is that rule. |
Contract and guardrails
Do not widen the boundary to finish a task.
If an implementation is hard, that is not permission to weaken a rule.
Rules
- One language-neutral contract is the source of truth. No guest language is canonical.
- Generated contract code is read-only output: regenerate it, never hand-edit it.
- No handwritten parallel wire types beside generated ones.
- No untyped escape hatch at the boundary: no
any,dynamic,objectbag, or string-typed operation. - Feature work never weakens the guardrail that is blocking it. A wrong guardrail is changed in its own work item, with evidence.
- Every task declares its architectural placement and stays inside that scope.
- Compiler enforcement differs by language — F# unions, Rust enums, C# generated closed types with analyzers — but each maps the same neutral contract.
- If the architecture blocks the work, create an explicit work item. Do not broaden the boundary quietly.
Status on main
Implemented today: engine-purity checks (scripts/check-architecture.ts),
F# builds with warnings as errors, and work-item attribution in CI.
Accepted and in progress: the neutral contract source and generated bindings (#52), dependency and path-scope checks (#53), the restricted TypeScript subset (#54), C# and Rust guest enforcement (#55).
Until generation lands, src/protocol.ts is the contract and this
site’s F# engine mirrors it by hand. That mirror is the known
exception to the no-parallel-types rule, not a pattern to copy.
Contract
Ten rules, all MUST.
Violating one is a defect regardless of whether tests pass.
Before you change anything
Read the boundary first.
The architecture here is unusual, and code that looks reasonable by mainstream front-end conventions is often wrong. The single most useful file is the protocol — about eighty lines, and it is the architecture.
- 01Authoritative application state lives in the engine, nowhere else
- 02Do not introduce application state in JavaScript outside the engine
- 03Browser events enter only through
data-eventand the kernel - 04Validation, legality and what-to-show are engine decisions
- 05Browser capabilities are reached only through an effect request
- 06HTML stays structural; CSS stays presentational
- 07The kernel stays generic — never branch on domain meaning in it
- 08All four HTTP outcomes are represented;
OutcomeUnknownis not a failure - 08bA browser-initiated navigation is adopted, never answered with another push
- 09Existing public contracts are preserved
- 10The repository's full check passes before you are done
Routing
The decision tree.
Most ambiguity resolves in the first three branches.
I need to add behavior.
│
├─ Is it purely visual (color, spacing, motion, hover)?
│ └─ YES → CSS. Stop. Do not route it through the engine.
│
├─ Is it document structure (an element, a label, a landmark)?
│ └─ YES → HTML. Add data-* only if the engine must drive it.
│
├─ Does it decide, validate, remember, or compute anything
│ about the application?
│ └─ YES → the engine. Add a state, a command, a transition,
│ and a projected key.
│
├─ Does it need HTTP, local storage, the clipboard, or the
│ browser’s history?
│ └─ YES → the engine returns an EffectRequest. It does NOT
│ call fetch, navigator.clipboard or history.pushState.
│
├─ Is it what a URL MEANS?
│ └─ YES → the engine. The kernel splits a URL into
│ path/query/hash and stops.
│
├─ Did the browser move on its own (Back, Forward)?
│ └─ YES → adopt the location. Request NOTHING: pushing again
│ traps the user on the page.
│
├─ Does it need a browser capability that does not exist yet
│ (files, timers, focus, clipboard read)?
│ └─ YES → a protocol extension. A bigger change: propose it,
│ do not slip it in.
│
└─ Am I about to hold application state in JavaScript,
outside the engine?
└─ STOP. That is rule 1. Go back to the third branch.
The test that resolves most cases in one line: would the application behave differently because of this? Yes means the engine. No means HTML or CSS. “Which tab is showing” passes that test — it changes what is legal and what matters, so it is application state. “What color the active tab is” fails it, so it is CSS.
Try it on eight real tasks: Demo 04 on the demos page.
Recognition
Stop if you are writing these.
Each is a boundary breaking, not a shortcut.
| You are writing | The problem |
|---|---|
document.querySelector outside the kernel | Reading the DOM as truth |
fetch( in the engine | A hidden effect — and the build will fail |
let currentX in page JavaScript | A second state store |
switch (event.name) in the kernel | The kernel learning domain meaning |
innerHTML, or markup in a template string | Markup generated by application logic |
.classList.add to reflect application state | A capability re-derived in the DOM |
setTimeout to “wait for” a projection | Racing the round trip |
case "Failure": case "OutcomeUnknown": together | Conflating two genuinely different outcomes |
| An array index as a list key | Unstable identity; focus and input land on the wrong row |
Checklist
Before and after.
Answer 1–8 before writing; confirm 9–13 before finishing.
Before
- What state changes? Which union member, in which state type?
- What event causes it, and from which element?
- Is the transition legal from every state it can be requested in? What happens when it is not?
- Is an external effect required, and of which kind?
- Who performs it? (The answer is: the kernel.)
- How does the result return, and how is a stale one rejected?
- Which projected keys change? Are capabilities projected explicitly?
- What HTML or CSS is needed? (Usually: none.)
After
- Am I creating duplicate state anywhere?
- Am I adding application logic outside the engine?
- Am I bypassing a boundary for convenience?
- What tests prove this — including the illegal case and the stale result?
- Does the full repository check pass?
This repository also enforces a work-item protocol in CI. Open one before
editing, or the pull request is rejected regardless of whether the code is
correct — see AGENTS.md.
If the documentation does not cover your case
Reason from the boundary, in order.
And if you conclude the architecture itself is the obstacle, say so explicitly and record it. Do not route around it silently — that is the one failure mode this design is least tolerant of.
- 01Does this decide anything about the application? → the engine
- 02Does this touch the outside world? → an effect request; if no capability exists, it is a protocol change
- 03Is this about how something looks? → CSS
- 04Is this about what something is? → HTML
- 05Still unsure? → ask. Do not infer a new mechanism and continue.
- 06Blocked by the architecture? → record a work item. Do not weaken the check.