Primitives and vocabulary — the adversarial review, in plain language
Four decisions (P7, P12, P13, P14) about what Crosswalker calls its own building blocks were held back from ratification on 2026-08-19 and sent to two independent adversarial reviews, on the grounds that the primitives layer is load-bearing and a wrong call there is expensive to unwind.
All four recommendations turned out to be wrong in some way. Holding them back was the right call. This page explains each one without jargon, with the trade-offs made explicit.
P12 — “we said five building blocks but really there are nine”
Section titled “P12 — “we said five building blocks but really there are nine””What this is actually about
Section titled “What this is actually about”There are two different questions you can ask about an import, and they have different answers. The confusion is that both answers got called “primitives.”
Question 1: where does the note go? A row in a spreadsheet has to become a note somewhere. The ways to place and address it:
| Way | What it means |
|---|---|
| folder | the note goes in a folder named from this column |
| file | this column becomes the note’s filename |
| heading | this becomes a heading inside a parent note |
| tag | reserved, not built yet |
| wikilink | reserved, not built yet |
That is five, and it is a closed list on purpose. It is what makes “given a recipe and a concept, compute its address” a single, predictable step.
Question 2: what can I send a column to? Once a note exists, a column’s value can also fill in things that are not locations at all:
| Destination | Is it a location? |
|---|---|
| folder, name, heading | yes — these are questions 1’s mechanisms |
| tag, property, alias | no — these are labels and fields on the note |
| link | no — this is a relationship to another note |
| body | no — this is text inside the note |
| note | no — this decides whether a separate note gets made at all |
That is nine. The workbench shows nine because nine is the honest answer to question 2.
These are not competing counts of one list — they are different axes. The architecture already says so, in the commitment itself: the grammar is five layout mechanisms x ordered layout x also_emit x graph_edges. That is a product of dimensions, not a flat inventory. Asking “so how many primitives are there?” is like asking how many cells a spreadsheet has without saying whether you mean rows or columns. The honest answer names the axis first.
What was proposed
Section titled “What was proposed”That the public claim of “five” is false because nine exist, and the docs should be restated on a nine-primitive palette.
What the review found
Section titled “What the review found”The claim is false, and it is a category error. Verified directly against the machine contract, not the docs:
spec/recipe.schema.jsondefines the mechanism list as a closed set of exactly five.- The generated TypeScript has exactly five variants.
- The renderer handles those five and rejects anything else.
- The schema file says, in its own text, that body is “not a sixth layout mechanism.”
And two of the “nine” are not new things at all:
nameisfile. Same mechanism, different label in the UI.linkis notwikilink. One emits a relationship; the other is a reserved addressing mechanism. Different jobs.notecannot even be written into a portable recipe — it blocks serialization today.
Only three of the nine are layout mechanisms. The rest are content, and content was always a declared, separate dimension of the design.
The real defect is different, and it is real: the docs assert twelve different counts in different places (3, 4, 5, 6, 6, 9, 9, 8, 7, 13, ~40, and “12 senses”), almost none of which say what they are counting. That is what makes the docs feel contradictory — not a broken contract.
What each option costs
Section titled “What each option costs”| Option | Cost | Risk | What you get |
|---|---|---|---|
| Defend five, scope every count | One docs pass. No code, no schema change | None to code | Public claim becomes true and legible. Every count says what it counts |
| Declare nine canonical | Either rewrite the schema so nine is actually true (large, breaks anyone already emitting recipes) or publish docs that contradict the code | High | Nothing identified |
| Defer | Free today | Readers keep hitting contradictory numbers | Nothing |
The call
Section titled “The call”Keep five, and fix the counts by saying what each one counts: five layout mechanisms (three built, two reserved), nine workbench destinations, four hierarchy carriers, eight query verbs, nine graph-change operations.
P13 — the word “primitive” means five different things
Section titled “P13 — the word “primitive” means five different things”What this is actually about
Section titled “What this is actually about”Yes — this is mostly a communication problem, and the intuitive fix is the right one. But one part of it is not just words, and that distinction is the whole decision.
The word “primitive” is currently used for: layout mechanisms, workbench destinations, query verbs, view settings, and change operations. A reader meeting the bare word cannot tell which is meant.
The two halves
Section titled “The two halves”| Half | What it is | Risk to change |
|---|---|---|
| Words in the docs | Prose saying “primitive” where it should say “layout mechanism” or “query verb” | None. Reversible, no code |
| Names inside saved files | The query.primitives field written into saved recipes, and TablePrimitives in the schema | Real. Renaming a field breaks recipes people already saved, and needs a version migration |
What the review found
Section titled “What the review found”The proposal bundled both halves and assumed they could ride one schema version bump. That plan does not work, because it names the wrong file — TablePrimitives lives in the recipe schema, not the preset schema. There are actually three independent renames, on three different compatibility clocks, one of which breaks saved recipes.
Also worth knowing: the proposal is titled “give it exactly one meaning,” but what it actually delivers is qualified names — several meanings, each clearly labelled. That is the correct fix. The title oversells it.
The risk that turned out not to exist
Section titled “The risk that turned out not to exist”The “breaks saved recipes” concern assumed there are saved recipes in the wild. There are not. The project is pre-alpha, unannounced, and has no external users, which the owner confirmed on 2026-08-19. Nothing outside this repository consumes the schema URI or the field names.
That collapses the entire compatibility argument. What is left to update is inside the repo and mechanical:
| What changes | Scope |
|---|---|
| Prose in the docs | A sweep |
query.primitives field name and the $defs names in the recipe schema | 33 files across src/, spec/, tests/, recipes/ reference the term |
| Bundled recipes | 10 import recipes plus 7 query recipes; regenerated, not hand-edited |
| Fixtures | bun run fixtures regenerates them |
What each option costs
Section titled “What each option costs”| Option | Cost | Risk | What you get |
|---|---|---|---|
| Full sweep now: words and field names | One coordinated pass over 33 files plus a fixtures regen | Low — no external consumers exist, and the fixture-drift gate catches misses | The names finally match what the things actually are, with no migration debt carried forward |
| Words only, defer field names | A docs sweep | None | Docs read correctly while the file format keeps the misleading names |
| Defer everything | Free | The word stays ambiguous | Nothing |
The call
Section titled “The call”Do the full sweep now, field names included. The window where renaming is free closes the moment someone saves a recipe outside this repo. Renaming now costs one coordinated pass; renaming later costs a versioned migration and a compatibility shim forever.
P7 — cutting the internal vocabulary from 16 names to 7
Section titled “P7 — cutting the internal vocabulary from 16 names to 7”What “pruning” actually means in practice
Section titled “What “pruning” actually means in practice”This is the question that was never answered, and without it the decision is undecidable. Pruning a noun does not delete anything from the product, and changes no code. It changes one thing only:
| A kept noun | A cut noun |
|---|---|
| Has a glossary entry with a definition | Has no glossary entry |
| Is used deliberately and means the same thing everywhere | Is just ordinary English; you can still write the words |
| Is a candidate to become a real type or schema in code | Is not |
| A reader is expected to learn it before the docs make sense | A reader never has to look it up |
So the real question per term is: is this a thing with its own boundary that a reader must learn, or is it a handy phrase? Handy phrases are free. Defined terms cost the reader.
That reframes the instinct “if these are just useful terms to describe things, I do not see the problem” as exactly right, with one caveat: a capitalized, defined term implies a thing with a boundary. When the boundary is not real, readers waste effort hunting for a distinction that does not exist. That is the cost, and it is the only cost.
What each of the 16 functionally does
Section titled “What each of the 16 functionally does”Grouped by the job it performs, rather than by whether it survives:
| The job | Terms | Verdict |
|---|---|---|
| Decides whether something is valid — the rulebook a file is checked against | Schema Contract | Keep |
| Claims something is true about the world — content someone asserts and can be held to | Knowledge Set (a body of asserted content, e.g. NIST CSF 2.0), MappingSet (one published crosswalk release), MappingAssertion (one row inside it) | Keep all three |
| Specifies what should happen — instructions, before anything runs | Recipe (the reusable instructions), Recipe binding (those instructions pinned to one exact input) | Keep both |
| Records what did happen — evidence after a run | Execution Record | Keep (planned contract) |
| Moves bytes to someone else | Package (the resolved release), Package Manifest (the signed list of what is inside), Carrier (the zip or folder it travels in) | Keep 2, cut Carrier |
| Derived views over data you already have | Live view (computed on read), Materialized snapshot (computed once and stored), Projection (the mapping from notes into the query database) | Keep all three, qualified |
| Reusable starting points — a shipped default you copy and adapt | Recipe template | Keep (not a root artifact) |
| Not separate things at all | Recipe specification, Recipe execution | Cut both |
12 keep, 1 defer, 3 cut = 16. Package is the defer: it is probably real, but its boundary against Package Manifest is not settled until v0.1.8 defines both.
The two that are not separate things, and the one that is
Section titled “The two that are not separate things, and the one that is”- Recipe specification vs Recipe — the same thing. “Specification” only means the Recipe written out in canonical form. There is no second artifact. Cut.
- Recipe execution — an event, not something you can hold. The durable facts about a run belong to Execution Record. Cut.
- Recipe template — genuinely different, and genuinely used. A template is a shipped starting point you copy; a specification is the finished thing in canonical form. The template one is backed by real code. Keep as a defined term, not as a root artifact.
And Carrier: the “vague” read is correct. It means “the zip or folder the bytes travel in.” That deserves one sentence of warning — signing a folder name is not the same as signing its contents — but it does not need to be a defined term. Cut the capital C, keep the sentence.
Why the proposed seven were the wrong seven
Section titled “Why the proposed seven were the wrong seven”The proposal kept Projection, Live view, and Materialized snapshot — all derived views, and the authority page itself says Projection “does not gain independent semantic authority.” Meanwhile it cut Execution Record and Package Manifest, the two primary artifacts of v0.1.8, the next milestone. That is backwards: it drops the things with authority and keeps the things without it.
It also cut Knowledge Set, the only term separating “this file is valid” from “NIST actually says this.” Those are different claims, and merging them lets “passes validation” quietly start to read as “is authoritative.”
What each option costs
Section titled “What each option costs”| Option | Cost | Risk | What you get |
|---|---|---|---|
| Selective prune (12 keep, 1 defer, 3 cut) | A modest docs pass | Low | Three genuinely empty terms stop costing readers; nothing the next milestone needs is lost |
| Prune to 7 | Re-introduce two terms next milestone across docs, schemas, types, UI labels, and tests | Audit and replay code may encode the wrong boundary permanently | A smaller number, for its own sake |
| Defer all pruning | Free | Three empty terms keep costing readers | Prune once later against real contracts |
The call
Section titled “The call”Selective prune, on the rule above: cut the terms that name no boundary, keep the ones that do.
P14 — splitting the glossary into two pages
Section titled “P14 — splitting the glossary into two pages”What this is actually about
Section titled “What this is actually about”The terminology page is hard to read. The proposal assumed the cause is length and prescribed splitting it into a short reader glossary plus a deep internals reference.
What the review found
Section titled “What the review found”The cause is not length. Measured:
- Current page: 221 lines. Proposed reader page: about 180 lines. That is 19% shorter — not a transformation.
- The split would require migrating 215 inbound links across 79 files, 89 of them in historical logs that should not be rewritten at all.
- It would create a third competing vocabulary authority, alongside the existing UI lexicon — exactly the collision problem P13 exists to remove.
What actually makes the page hard to trust is that five of its claims are stale, including two that contradict settled commitments (it still discusses a database engine that was rejected, and still presents a renamed concept under its old name).
A third option neither the card nor the review proposed
Section titled “A third option neither the card nor the review proposed”The owner suggested it on 2026-08-19, and it is better than both: organize one page by audience, and link the deep internals out to the pages that own them.
The split proposal assumed you must choose between “one page that serves nobody” and “two pages that serve two audiences.” That is a false choice. Audience separation is a structural problem, solvable with sections; it does not require a second URL.
| Shape | Reader finds their terms? | Link migration | Vocabulary authorities |
|---|---|---|---|
| One page, undifferentiated (today) | No — reader terms buried among internals | — | 2 (terminology + UI lexicon) |
| One page, grouped by audience | Yes — reader section first, internals below | None | 2 |
| Two pages | Yes | 215 links | 3 |
The “link internals out” half matters as much as the grouping. Today the glossary duplicates definitions that live on concept pages, which is why it is both long and prone to drift — two copies, one of which nobody updates. Replacing those with a one-line gloss plus a link to the owning page shortens the page and removes the drift source. That is the mechanism the split proposal was reaching for without naming.
What each option costs
Section titled “What each option costs”| Option | Cost | Risk | What you get |
|---|---|---|---|
| One page, grouped by audience, internals linked out | Fix five stale rows, regroup by audience, replace duplicated definitions with gloss + link | None — URL and anchors unchanged | Readers find their terms; the drift source is removed; no second authority |
| Trim in place, keep current order | Fix five stale rows | None | Correctness, but reader terms stay buried |
| Split into two pages | 215 link migrations, anchor-compatibility work, a third vocabulary authority | Two pages to keep in sync; still stale unless content is fixed anyway | A 19% shorter first page |
| Defer | Free | Stale claims keep misleading readers | Nothing |
The call
Section titled “The call”One page, grouped by audience, internals linked out to their owning concept pages. Revisit a split only if the corrected page is still unwieldy after v0.1.8, by which point the vocabulary will have stopped moving.
Summary
Section titled “Summary”| Decision | Original recommendation | After review |
|---|---|---|
| P12 primitives count | Restate on nine | Keep five; scope every count. The “exceeded” claim is false |
| P13 the word “primitive” | Full sweep, one schema bump | Full sweep now, field names included. The migration premise was wrong, but so was the caution: there are no saved recipes to break |
| P7 vocabulary 16 → 7 | Prune to seven | Selective prune: 12 keep, 1 defer, 3 cut. The seven cut next milestone’s primary artifacts |
| P14 split the glossary | Split after P7/P12/P13 | One page grouped by audience, internals linked out. Length was never the problem |
Related
Section titled “Related”- Owner review queue — the original cards
- Primitives reconciliation register — where P7–P14 came from
- Artifact roles and authority — the page defining the noun vocabulary
- Terminology — the page P14 proposes splitting