Skip to content

Commit f4f09ce

Browse files
docs: say what the engine is for, and give the laws a home
The root README was a status page: a migration note, a two-engines-converging paragraph, and a flat crate list. It told a reader what is in the tree and never what the engine is for, which is the one thing a stranger arrives wanting. It now leads with the position. `n0` is the renderer half of a browser — embeddable, deterministic, eventually editable — and the refusals (scripting, navigation, network, the user-agent surface, an ambient clock) are stated as what buys determinism, a single binary, a small attack surface, and animation where dropped frames are structurally impossible. "Nothing but drawing" stays, promoted from a subtitle to the thesis, because it is the same refusal the rest of the page spells out. Stylo and Skia are named for the first time: the two borrowed pieces are Firefox's cascade and Chromium's rasterizer, and n0 is the architecture between them — which is also why byte-comparing against a Chromium bake is a reasonable bar rather than a boast. Honesty is structural rather than per-sentence. The engine's goals are stated as goals; **Where it is today** carries the admitted slice, the corpus, and the explicit not-yet list; and the legacy engine plus the parked `.grida`/`.n0.xml` formats get one bounded section that explains *why* they are parked, instead of appearing as an asterisk on every claim. FLIP is respected throughout: the corpus is described exactly as `svg-engine-of-record.md` describes it, followed by a statement that it is not a conformance claim and no score exists. **The laws had no home.** The mode rule lived in `topology.md`, refusal-as- identity in three crate READMEs, reuse-≡-fresh only in the naming convention of `crates/n0/tests/cache.rs` — so every session re-derived them. `AGENTS.md` now states seven, each with the mechanism that enforces it, including the tripwire that a mode/budget/quality flag must never become readable from `websem`, `rframe`, or resolve, and the note that `DirtyClass` is referenced zero times on purpose and must not be "finished" without the reuse gate. It also gains a **Current state and caveats** section — two live engines, nothing publishable, no n0 wasm target, Taffy as today's layout engine and the wall that would force a house one — and a **Provenance** section that absorbs the migration history the README shed. Two claims were corrected against the code rather than carried over. The CLI transcript in the README is verbatim output from both admissions, not a paraphrase. And a draft line claiming no crate on this path is publishable was dropped: `n0`, `math2`, and `fonts` carry no `publish` key and therefore default to publishable. The true statement — there are no releases, and the only shipped artifact is the frozen v1 wasm package — is what shipped. Not done, deliberately: `www/app/(home)/page.tsx` still carries the older, weaker description and now reads inconsistently with this page; #43 is still the position pointer AGENTS.md sends sessions to, and is still stale; and the false Skia-confinement comment in `crates/n0/Cargo.toml`, found while checking whether the README could claim it, is filed rather than fixed here.
1 parent ae50a86 commit f4f09ce

2 files changed

Lines changed: 293 additions & 37 deletions

File tree

AGENTS.md

Lines changed: 108 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,64 @@
1-
# Hi robots, welcome to nothing — the Grida graphics engine.
2-
3-
`n0` ("nothing") is the 2D graphics engine. This is a **Rust-first Cargo
4-
workspace** (resolver 3; members in the root `Cargo.toml`). The Grida product
5-
monorepo — editor, packages, services — is
6-
[gridaco/grida](https://github.com/gridaco/grida); it consumes this repo
7-
**only** as the published `@grida/canvas-wasm` npm artifact. Do not add
8-
product/editor code here.
1+
# Hi robots, welcome to n0 ("nothing") — the Grida graphics engine.
2+
3+
[README.md](./README.md) says what this engine is and why. This file is the
4+
working map: the laws that bind every change, the commands, the layout, and the
5+
caveats that are true right now.
6+
7+
`n0` is a 2D graphics engine and a **Rust-first Cargo workspace** (resolver 3;
8+
members in the root `Cargo.toml`). The Grida product monorepo — editor,
9+
packages, services — is [gridaco/grida](https://github.com/gridaco/grida); it
10+
consumes this repo **only** as the published `@grida/canvas-wasm` npm artifact.
11+
Do not add product/editor code here.
12+
13+
## The laws (bind every change)
14+
15+
These are what keep two duty cycles, three source languages, and an editor from
16+
becoming three engines. Each has an enforcing mechanism — a law without one is
17+
a comment.
18+
19+
- **Never a silent wrong pixel.** A construct the compiler cannot honour must
20+
refuse loudly (`--strict`) or be declared by name at a stable node path
21+
(best-effort). A patrol that over-refuses beats one that lets a wrong pixel
22+
through. _Enforced by:_ the websem patrols, the refusal corpus, and the law
23+
that both admissions are frame-identical where nothing degrades.
24+
25+
- **One meaning, many policies.** Render modes may differ in _when and at what
26+
quality_ they paint — never in _what things mean_. Static is not a second
27+
renderer; it is the same pipeline with an empty temporal input set (no camera
28+
delta, no previous frame, no dirty set). _Tripwire:_ a mode, budget, or
29+
quality flag must never become readable from `websem`, `rframe`, or resolve.
30+
The moment one is, two pipelines have started growing.
31+
32+
- **Realtime in structure, static in policy.** The architecture that makes
33+
realtime possible ships from day one, because retrofitting it _is_ the
34+
rebuild. The optimizations realtime needs ship only once measured, because an
35+
optimization is a relative claim — _same as X, faster_ — and a cache built
36+
over an unverified X becomes a second place that believes the wrong answer.
37+
_In tree:_ `DirtyClass` is fully classified in `n0-model/src/ops.rs` and
38+
referenced **zero** times in `crates/n0/src`. The socket is shaped, the policy
39+
is deliberately absent. Do not "finish" it without the gate below.
40+
41+
- **Reuse ≡ fresh.** Any frame produced with reuse must be byte-identical to the
42+
same frame produced from scratch. _Enforced by:_ `crates/n0/tests/cache.rs`
43+
the `*_matches_fresh` naming _is_ the law. Every new cache, damage path, or
44+
incremental stage adds its own instance.
45+
46+
- **A module's identity is what it refuses**, and the refusal has a guarding
47+
test. `rframe` cannot express a gradient. `animation-sampling` owns no clock.
48+
`csscascade` adds no matcher of its own. _Enforced by:_ the architecture tests
49+
`rframe`'s backend-free lock, the model tier's skia-free lock, `n0_cli`'s
50+
lock against the retired `htmlcss` route. Before adding to a module, state
51+
what it refuses; if the addition violates no refusal, the name is too loose.
52+
53+
- **The oracle is external.** Chromium or declared consensus grades pixels —
54+
never the other engine in this tree. Stylo bounds what can be _supported_: a
55+
gap there is a declared hole, not a wrong pixel, and never a reason to add a
56+
second matcher.
57+
58+
- **Patrol before drop.** No deletion, replacement, or conflict resolution
59+
without a triage pass and a captured-essence ledger first. Load-bearing
60+
caveats are re-homed before the deletion merges; deliberate drops are named in
61+
the commit message.
962

1063
## Setup
1164

@@ -72,6 +125,36 @@ python3 bin/activate-flatc -- --rust -o crates/grida/src/io/generated format/gri
72125
| `third_party/` | vendored usvg (reference source) + emsdk submodule |
73126
| `bin/` | `activate-flatc`, `activate-emsdk` — pinned tool activators |
74127

128+
## Current state and caveats
129+
130+
What is true right now, so a session does not infer it from ambition.
131+
132+
- **Two engines are live, on purpose.** `crates/grida` still depends on
133+
`crates/htmlcss` and still renders Web sources; D-N permitted breaking that
134+
and the permission was never used. The n0 path (`websem → rframe → n0`) is the
135+
SVG engine of record for **new** work — `htmlcss` is a frozen semantics donor
136+
that evolution rungs mine, never extend.
137+
- **Nothing on the n0 path is publishable.** `n0_cli`, `n0-model`, `websem`, and
138+
`rframe` are all `publish = false`, and there are no releases. The only shipped
139+
artifact in the tree is the frozen v1 wasm package.
140+
- **There is no n0 WebAssembly target.** `grida-canvas-wasm` binds
141+
`crates/grida` only. The v2 port is priced work, not an assumption.
142+
- **Taffy is the layout engine.** A house-built layout engine is a stated goal,
143+
not a current fact. The wall that would force it is browser-grade intrinsic
144+
sizing across a namespace-aware tree — not flex.
145+
- **No conformance score may be produced or inspected.** The FLIP rule is
146+
unratified ([gridaco/nothing#49](https://github.com/gridaco/nothing/issues/49)).
147+
A corpus may be described; results may not be scored, aggregated, or presented
148+
as conformance.
149+
- **The admitted slice has one statement of record**
150+
[`crates/n0_cli/README.md`](./crates/n0_cli/README.md). Do not restate it
151+
elsewhere; link it. The same holds for the v1 capability inventory
152+
([`crates/grida/README.md`](./crates/grida/README.md)) and the realtime
153+
optimization estate ([`docs/wg/feat-2d/optimization.md`](./docs/wg/feat-2d/optimization.md)).
154+
- **Plans are `*.plan.md`** — gitignored scratch, never committed knowledge.
155+
Durable knowledge lands in `docs/wg/` or a crate README; work items land in
156+
issues.
157+
75158
## Skills
76159

77160
Agent skills live in `.agents/skills/` (`.claude/skills` symlinks to it):
@@ -98,6 +181,23 @@ This repo owns publishing and must never unpublish/deprecate that version.
98181
The `v1-freeze` branch pins the tree that built it, for emergency `canary.N+1`
99182
cuts.
100183

184+
## Provenance
185+
186+
The engine migrated from [gridaco/grida](https://github.com/gridaco/grida) with
187+
its full history (2025→); Grida remains the product monorepo. The v2 family was
188+
promoted from the `model-v2-anchor` research branch
189+
([gridaco/nothing#9](https://github.com/gridaco/nothing/issues/9)); its frozen
190+
workbench record lives in [`archive/model-v2/`](./archive/model-v2/README.md),
191+
and paths inside those frozen papers refer to the pre-promotion layout.
192+
193+
How two engines become one is the **consolidation program**
194+
[`docs/wg/consolidation/`](./docs/wg/consolidation/index.md). It owns the phases,
195+
the gates, and the owner decision registry (D-C, D-L, D-M, D-N taken; FLIP,
196+
NAME, D-D, D6, D-H, D-G(b), D-J, D-K, D-E/D-I open). Read
197+
[its index](./docs/wg/consolidation/index.md) before program work; the charter
198+
records the *route*, and the current *position* is tracked on
199+
[gridaco/nothing#43](https://github.com/gridaco/nothing/issues/43).
200+
101201
## Where work gets filed
102202

103203
- **This repo (gridaco/nothing)**: engine rendering, the node/document model,

README.md

Lines changed: 185 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,196 @@
22
<img src="./assets/logo.svg" alt="Nothing" width="474">
33
</p>
44

5-
# n0 ("nothing")
5+
# n0
66

7-
Nothing but drawing. An engine for everything drawable.
7+
**Nothing but drawing.** A 2D graphics engine that speaks the Web — browser-grade
8+
rendering, without a browser.
89

9-
`n0` (pronounced "nothing") is a 2D graphics engine.
10+
`n0` (pronounced "nothing") turns SVG and HTML/CSS into exact pixels: from a
11+
command line, from a library, and eventually onto an editable realtime canvas.
12+
No scripting, no navigation, no network, no user agent.
1013

11-
## Status
14+
## The claim
1215

13-
The graphics engine lives here. It migrated from the
14-
[Grida repository](https://github.com/gridaco/grida) with its full history
15-
(2025→) carried over; Grida remains the service/editor monorepo and consumes
16-
the engine only through published artifacts.
16+
n0 is the renderer half of a browser — embeddable, deterministic, and
17+
eventually editable. There is no agent half:
1718

18-
Two engines live side by side while the topology converges: the migrated
19-
production engine (`crates/grida`, shipping as `@grida/canvas-wasm`) and
20-
the v2 `n0` engine family (`crates/n0`), promoted from the
21-
`model-v2-anchor` research branch. The v2 model program is tracked in
22-
[gridaco/nothing#9](https://github.com/gridaco/nothing/issues/9).
19+
| n0 refuses | which buys |
20+
| ---------------------- | ------------------------------------------------------------------ |
21+
| scripting | determinism — same input, same declared time, same bytes |
22+
| navigation & network | one binary, no sandbox, no headless flags, no 200MB download |
23+
| the user-agent surface | a small attack surface and a core you can embed |
24+
| an ambient clock | frame-exact animation — dropped frames are structurally impossible |
25+
26+
## What it's for
27+
28+
- **Deterministic Web rendering.** SVG and HTML/CSS to exact pixels — in CI, in
29+
a container, on a machine that will never have a browser. Same input, same
30+
declared time, same bytes.
31+
- **Animation as exact time.** A source compiles once and is sampled at a
32+
declared signed-nanosecond instant. An exported sequence has no frame to drop.
33+
- **Realtime on heavy documents.** 60+ fps where an entire page is one frame
34+
inside an infinite canvas — panning, zooming, editing.
35+
- **Layout we own.** Browser-grade layout is where general-purpose layout
36+
libraries stop. Taffy carries flex until it doesn't.
37+
- **One engine, everywhere.** Native and WebAssembly, single-threaded — no
38+
worker threads to hide behind.
39+
40+
## The pipeline
41+
42+
One document, one cascade, one compiler, one contract, one kernel.
43+
44+
```text
45+
source bytes (.svg | .html)
46+
→ one namespace-aware document csscascade
47+
→ one Stylo cascade csscascade
48+
→ effective values: Base | Sample t websem
49+
→ rframe::Frame websem
50+
→ resolve → drawlist → paint n0
51+
```
52+
53+
Each stage owns one decision. `csscascade` resolves values and decides no
54+
meaning; `websem` decides what will and will not render, and never touches a
55+
canvas; `rframe` is a vocabulary whose value is what it *cannot* express; `n0`
56+
decides how to get pixels, never what they mean.
57+
58+
Two pieces are borrowed on purpose. The cascade is **Stylo** — Firefox's style
59+
engine, pinned to an upstream revision, not a CSS subset written here. The
60+
rasterizer is **Skia** — the same one Chromium rasterizes with, which is much of
61+
why byte-exact comparison against a Chromium bake is a reasonable bar at all.
62+
n0 is the architecture between them: it owns resolution, the drawlist, damage,
63+
caching, and time. It owns no cascade and no rasterizer.
64+
65+
## Declared holes, never guessed pixels
66+
67+
The engine refuses loudly, or it names the hole. It never guesses.
68+
69+
```console
70+
$ # best-effort (the default): render what is admitted, declare the rest by name
71+
$ cargo run -q -p n0_cli --bin n0 -- fixtures/test-svg/probe/polygon-fill-probe.svg out.png 64x64
72+
degraded: skipped svg/polygon[1]: unsupported element <polygon>
73+
rendered fixtures/test-svg/probe/polygon-fill-probe.svg -> out.png (64x64, base-shared-frame, 1 degraded, 223 bytes)
74+
75+
$ # --strict: refuse on the first construct outside the slice
76+
$ cargo run -q -p n0_cli --bin n0 -- fixtures/test-svg/probe/polygon-fill-probe.svg out.png 64x64 --strict
77+
error: render failed: unsupported element <polygon>
78+
```
79+
80+
Where nothing degrades, the two admissions are frame-identical, and a law
81+
checks that across the whole corpus.
82+
83+
```sh
84+
# a whole composition — containers, curves, strokes, one animated rect —
85+
# rendered at its authored state, and at exactly one second
86+
cargo run -p n0_cli --bin n0 -- \
87+
fixtures/web-first/animation/svg-scene-cub-animation.svg cub.png 96x96
88+
cargo run -p n0_cli --bin n0 -- \
89+
fixtures/web-first/animation/svg-scene-cub-animation.svg cub-1s.png 96x96 \
90+
--time-ns 1000000000
91+
```
92+
93+
## Where it is today
94+
95+
The Web path renders a standalone SVG, or an HTML document's first inline SVG:
96+
`<rect>`, `<circle>`, `<ellipse>`, `<path>` and `<line>` — filled and stroked —
97+
nested in `<g>` with the whole `transform` grammar; root sizing per SVG2 §8.2
98+
with the full `preserveAspectRatio` grammar; and one exact-time `<animate>`.
99+
[`crates/n0_cli/README.md`](./crates/n0_cli/README.md) is the statement of
100+
record for that slice and what it refuses.
101+
102+
Its corpus is 77 Chromium-baked cells and 10 sampled frames, byte-exact except
103+
six curved cells carrying a declared tolerance confined to the weighted
104+
rational conic. That describes one enumerated corpus — **it is not a
105+
conformance claim**, and no conformance score exists.
106+
107+
Not admitted yet: text, gradients, clips, masks, filters, and opacity. Not built
108+
yet: any layout engine, any editor host, any WebAssembly target.
109+
110+
**Nothing here is published.** There are no releases, and the only shipped
111+
artifact in the tree is the frozen v1 wasm package. Run it from a clone —
112+
[setup](./AGENTS.md#setup).
113+
114+
## One pipeline
115+
116+
2D has no perceptual slack: a 40px icon is either right or visibly wrong, and no
117+
level-of-detail trick hides it. So there is no fast renderer beside an exact one.
118+
Static is the same pipeline with an empty temporal input set — no camera delta,
119+
no previous frame, no dirty set. Realtime is the same pipeline allowed to reuse.
120+
121+
> Modes may differ in **when and at what quality** they paint — never in **what
122+
> things mean**.
123+
124+
The architecture that makes realtime possible ships from day one; the
125+
optimizations it needs ship only once measured. So the sockets ship empty:
126+
`DirtyClass` classifies every operation's invalidation in `n0-model`, and the
127+
engine references it exactly zero times. It full-resolves every frame until
128+
correctness has earned the right to reuse.
129+
130+
## The laws
131+
132+
- **Never a silent wrong pixel.** Refuse, or name the hole.
133+
- **A module's identity is what it refuses**, and the refusal has a guarding
134+
test. `rframe` cannot express a gradient. `animation-sampling` owns no clock.
135+
`n0_cli` is architecturally forbidden from calling the renderer it replaced.
136+
- **Reuse ≡ fresh.** Any frame produced with reuse is byte-identical to the same
137+
frame produced from scratch.
138+
- **The oracle is external.** Chromium, or declared consensus. Never this
139+
engine's other half. Stylo bounds what can be *supported*; a gap there is a
140+
declared hole, never a wrong pixel.
141+
142+
## Why the Web first
143+
144+
The destination is n0's own source language — SVG with layout, reimagined. It
145+
is deliberately not being built yet.
146+
147+
The Web is the only 2D graphics specification that is frozen, adversarially
148+
tested, and ships with a free executable oracle. A custom format has none of
149+
that. On the Web path every correctness question has an answer before anyone has
150+
an opinion — and the cascade, layout, paint, text, and animation built to satisfy
151+
it are the same ones the format will stand on.
152+
153+
The format is not deprioritized. It is waiting for a bar that can grade it.
154+
155+
## Also in this tree
156+
157+
**A second engine.** [`crates/grida`](./crates/grida/README.md) is the mature
158+
v1: 19 node types, gradients, image filters, shadows, masks, PDF and SVG export,
159+
and a realtime estate benchmarked to 135K-node documents. It ships
160+
as `@grida/canvas-wasm` and is frozen there. It is being succeeded, not
161+
extended — its know-how migrates into n0 through contracts, never by copying,
162+
and the measurements in
163+
[`docs/wg/feat-2d/optimization.md`](./docs/wg/feat-2d/optimization.md) are the
164+
best existing description of what a realtime 2D engine actually needs.
165+
166+
**Two custom formats, parked.** `.grida` (the v1 FlatBuffers binary) is frozen
167+
and read-only. `.n0.xml` (the authored source language) builds and is tested,
168+
and is deliberately not being expanded. Both are waiting on the same thing: a
169+
foundation proven against an oracle that can grade it.
23170

24171
## Workspace
25172

26-
- [`crates/grida`](./crates/grida) — the legacy engine compatibility consumer
27-
- [`crates/cg`](./crates/cg) — the backend-neutral canvas-graphics vocabulary
28-
- [`crates/htmlcss`](./crates/htmlcss) — the extracted mature static HTML/CSS/SVG renderer
29-
- [`crates/grida_editor`](./crates/grida_editor) — the editor core (document, history, commands)
30-
- [`crates/grida-canvas-wasm`](./crates/grida-canvas-wasm) — WASM bindings (`@grida/canvas-wasm`)
31-
- [`crates/math2`](./crates/math2) · [`crates/csscascade`](./crates/csscascade) · [`crates/fonts`](./crates/fonts) — foundations
32-
- [`crates/grida_dev`](./crates/grida_dev) · [`crates/grida_wpt`](./crates/grida_wpt) — dev tools, benchmarks, reftests
33-
- [`crates/n0`](./crates/n0) — the `n0` engine (v2): resolve → drawlist → paint
34-
- [`crates/n0-model`](./crates/n0-model) · [`crates/n0_dev`](./crates/n0_dev) — the skia-free `anchor` model · the v2 dev shell
35-
- [`crates/websem`](./crates/websem) · [`crates/rframe`](./crates/rframe) · [`crates/animation-sampling`](./crates/animation-sampling) — the Web semantic compiler · the resolved render contract it emits · the time axis both read
36-
- [`crates/n0_cli`](./crates/n0_cli) — the thin `n0` file-render command host
37-
- [`archive/model-v2/`](./archive/model-v2) — the frozen v2 workbench archive (decision record)
38-
- [`format/`](./format) — the FlatBuffers schema (source of truth)
39-
- [`docs/wg/`](./docs/wg) — the engine's normative working-group specs
40-
41-
The repository is a Rust-first Cargo workspace.
173+
The Web path — `csscascade``websem``rframe``n0`:
174+
175+
| crate | |
176+
| ------------------------------------------------------------- | ------------------------------------------------------------------ |
177+
| [`csscascade`](./crates/csscascade/README.md) | the Stylo bridge — one namespace-aware document, one cascade |
178+
| [`websem`](./crates/websem/README.md) | the Web semantic compiler — what will and will not render |
179+
| [`rframe`](./crates/rframe/README.md) | the resolved render contract — backend-free, provisional |
180+
| [`animation-sampling`](./crates/animation-sampling/README.md) | the time axis — no ambient clock |
181+
| [`n0`](./crates/n0/README.md) · [`n0-model`](./crates/n0-model/README.md) | the engine, and its skia-free model |
182+
| [`n0_cli`](./crates/n0_cli/README.md) | the `n0` binary |
183+
184+
The v1 engine and its satellites: `grida`, `htmlcss`, `cg`, `grida_editor`,
185+
`grida-canvas-wasm`, `grida_dev`, `grida_wpt`. Foundations: `math2`, `fonts`.
186+
The full map is in [AGENTS.md](./AGENTS.md).
187+
188+
## Docs
189+
190+
[`docs/wg/`](./docs/wg/index.md) is the engine's working group — normative
191+
specifications, domain studies, and research, including 41 documents on
192+
Chromium's rendering architecture. Start at
193+
[the consolidation program](./docs/wg/consolidation/index.md) for how the two
194+
engines become one.
195+
196+
Rust-first Cargo workspace. Licensed under [MIT](./LICENSE-MIT) or
197+
[Apache-2.0](./LICENSE-APACHE), at your option.

0 commit comments

Comments
 (0)