name the layer, not only the primitive
The constitution asks which primitive a feature is. It never asked *where* the feature belongs, and that let a whole feature get built at the wrong layer. Four layers, outermost wins: content on disk, engine (facts only the engine can produce), theme (markup), browser (CSS, then JS). Two rules fall out — the engine never edits authored text to change how it looks, and a feature needing no engine fact is not an engine feature. architecture.md carries the table and the test; CLAUDE.md carries the one-line version, since it is the file always loaded. The example is named in the docs on purpose. A rule with a scar attached is one an agent can apply; a rule stated in the abstract gets reasoned around.
This commit is contained in:
@@ -35,6 +35,12 @@ Definitions and STATUS: `docs/architecture.md`.
|
|||||||
Name the primitive before writing code. If it reduces to none it is a **trunk** (wants a permanent
|
Name the primitive before writing code. If it reduces to none it is a **trunk** (wants a permanent
|
||||||
service or a core-model change): stop, say so in a paragraph, propose the leaf, wait.
|
service or a core-model change): stop, say so in a paragraph, propose the leaf, wait.
|
||||||
|
|
||||||
|
**Name the layer too.** Content on disk · engine (data only the engine can produce) · theme (markup) ·
|
||||||
|
browser (CSS, and only then JS). Build it at the outermost layer that can do the job: a line-breaking or
|
||||||
|
spacing problem CSS solves is not an engine feature, and code that edits an author's text to fix how it
|
||||||
|
*looks* is at the wrong layer by definition (ADR-0045 — a whole feature was deleted for this). Layers and
|
||||||
|
the test: `docs/architecture.md`.
|
||||||
|
|
||||||
## 3. Hard rules
|
## 3. Hard rules
|
||||||
|
|
||||||
1. **No abstraction before its second concrete use** — pipeline, resolver, interface, generic, config
|
1. **No abstraction before its second concrete use** — pipeline, resolver, interface, generic, config
|
||||||
|
|||||||
@@ -52,6 +52,13 @@ must do — not the theme. `verify.sh` fails if the embedded reference theme cha
|
|||||||
changing, because in practice those two drift together — and it fails on a `<script>` tag in that theme,
|
changing, because in practice those two drift together — and it fails on a `<script>` tag in that theme,
|
||||||
because a reference theme that grows taste stops being a reference (ADR-0026).
|
because a reference theme that grows taste stops being a reference (ADR-0026).
|
||||||
|
|
||||||
|
**The agent names the layer as well as the primitive.** Content on disk, engine, theme, browser — and it builds
|
||||||
|
at the outermost layer that can do the job (`CLAUDE.md` §2, `docs/architecture.md`). This exists because a whole
|
||||||
|
feature was built at the wrong one: widow prevention as a Markdown transform that inserted a non-breaking space
|
||||||
|
into an author's prose. It worked and it had tests; `text-wrap: pretty` does it better with no bytes in the
|
||||||
|
content, so the feature was deleted (ADR-0045). If a feature only rearranges how something looks, expect the
|
||||||
|
agent to push back toward CSS or a template.
|
||||||
|
|
||||||
**The core ceiling moved once, on purpose.** `CORE_LOC_MAX` went 2000 → 2800 (ADR-0041) because the original
|
**The core ceiling moved once, on purpose.** `CORE_LOC_MAX` went 2000 → 2800 (ADR-0041) because the original
|
||||||
figure was costed before any code existed and never budgeted what `cmd/` would hold — `check`, `new`, `-dev`,
|
figure was costed before any code existed and never budgeted what `cmd/` would hold — `check`, `new`, `-dev`,
|
||||||
change detection. Treat a *second* raise as evidence that something belongs in `internal/ext/` instead: the
|
change detection. Treat a *second* raise as evidence that something belongs in `internal/ext/` instead: the
|
||||||
|
|||||||
@@ -70,6 +70,27 @@ URL shape so the mux has one entry.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Layers — build at the outermost one that can do the job
|
||||||
|
|
||||||
|
A primitive says *what* a feature is. This says *where* it belongs, and it is the question that catches a
|
||||||
|
feature nothing else does (ADR-0045).
|
||||||
|
|
||||||
|
| Layer | Owns | Test |
|
||||||
|
|---|---|---|
|
||||||
|
| Content | bytes an author wrote | Would a reader's own words change? Then only the author may touch it |
|
||||||
|
| Engine | facts only the engine can produce — which bundles exist, an image's size, a derivative's name, a permalink | Could anything downstream work this out for itself? If yes, it does not belong here |
|
||||||
|
| Theme | markup and which fields go where | Is the answer "how it looks"? Then it is a template, not Go |
|
||||||
|
| Browser | line breaking, spacing, colour, motion — CSS, and only then JS | Does the browser already know something the engine cannot, like the line box? Then leave it there |
|
||||||
|
|
||||||
|
Two rules fall out. **The engine never edits authored text to change how it looks**: it may transform text
|
||||||
|
(`--` into an en dash is a transformation no stylesheet can express) but never lay it out. And **a feature
|
||||||
|
that needs no engine fact is not an engine feature** — it is a stylesheet, a template, or nothing.
|
||||||
|
|
||||||
|
The failure this exists to prevent: widow prevention was built as a Markdown transform that inserted a
|
||||||
|
non-breaking space into an author's prose. It worked, it had tests, and it was wrong at the layer —
|
||||||
|
`text-wrap: pretty` does it better with no bytes in the content, and the engine could never see the line box
|
||||||
|
it was guessing about.
|
||||||
|
|
||||||
## Invariants — violating one is a stop condition
|
## Invariants — violating one is a stop condition
|
||||||
|
|
||||||
1. **Open page object.** Known fields as struct members plus a `Meta`/`Extra` bag. Absence equals
|
1. **Open page object.** Known fields as struct members plus a `Meta`/`Extra` bag. Absence equals
|
||||||
|
|||||||
Reference in New Issue
Block a user