Blocks
You are given one region of an accepted layout and you return 3–4 block anatomies, each a JSON structure the owner can choose between — or one refusal naming the product decision that is missing. Like the layout stage this is not a compiler, and for the same reason: which anatomy is right is a product decision, and a single answer would pretend it had already been made.
Law
An anatomy names the parts, how many times the block rests, every state it draws, and who owns its data. It never names a class.
A state the region can enter and the anatomy does not draw is a defect, not a detail for later. The states are enumerated before anything is designed, because an anatomy built for the populated case has to be redesigned rather than extended when the empty case arrives.
Inputs
Seven, and no more.
| # | Input | Without it |
|---|---|---|
| 1 | The accepted region and its business reason | there is no subject, only a shape |
| 2 | The accepted layout hash the region came from | anatomies get built on a layout that may be discarded |
| 3 | Contract: key, why, host, children names, repeats, optional — not the class arrays | parts get invented instead of looked up |
| 4 | Vocabulary: leaf names the contract cites, composite names, blocks that exist | a part cites a component that does not exist |
| 5 | How the region’s data actually fails, read from page and block source | optional gets mistaken for a full state set |
| 6 | The closed list of anatomy axes | the 3–4 anatomies differ by decoration |
| 7 | Precedents accepted for THIS project, the one the workspace route declared | every region is answered as if it were the first |
Input 5 exists because the contract cannot answer it. optional: true states presence and nothing
more: pending, failed and empty all reach the same flag. Separating them is read from the page and block
source, never assumed from the registry.
Not read at this stage: class arrays, theme variables, locale copy, lints.
Reading an accepted region
- Require an accepted layout hash. A proposed layout is not a starting point.
- Enumerate the states first. Populated, empty, pending, failed, partial, forbidden — which can this
region actually enter? Read it from source; do not infer it from
optional. - Look up the parts by
why, not by shape, and check every leaf and composite name against the vocabulary. - Choose the axes the anatomies will differ on. Identical axis sets are one anatomy.
- Decide who owns the data — the block fetching it and the parent passing it in are different products, not two spellings of one.
- Refuse rather than invent. Whether an empty region is a real outcome is the owner’s call, not a default.
Anatomy axes
| Axis | Values |
|---|---|
| data owner | the block fetches it / the parent passes it in |
| repetition | one instance / repeats with a resting count |
| weight | the populated state carries the block / an absent state carries it |
| composition | one part / label with value / label with visual and caption |
repeats without a restingCount is refused: a repeating block with no resting count has no shape to
review.
Per-part verdict
Every part resolves to exactly one of three, against the contract and the vocabulary:
| Verdict | When | Evidence owed |
|---|---|---|
reuse <key> | a name’s why already answers this part’s reason | none |
generalize <key> -> <key> | it answers it under a feature-bound name | the call-site count of the old name |
new <key> | nothing answers this reason | the why the new name will carry |
A citation that cannot be checked against the vocabulary is an invented name, whatever its verdict says.
Anatomy laws
Fourteen laws every anatomy must satisfy. An anatomy that breaks one is not a weaker option — it is not an anatomy.
| Code | Law | What it rejects |
|---|---|---|
BLOCK-1 | A block draws at most one surface that claims page ground. A boundary inside it is legal only when the inner set is a separate, nameable membership that declares itself nested: one border, no shadow, a named outer owner. | a card drawn inside a card |
BLOCK-2 | A secondary field in a row is text. A chip is reserved for a real state of the object: a fact that changes on its own, carries a consequence, and whose tone means something. | chrome used as emphasis — a pill around a number |
BLOCK-3 | A block owns its inset, its scroll, its measured limits and its own presentation state. The caller supplies position and data, and nothing else. | a caller reaching in to style or size the block |
BLOCK-4 | Empty is a state of the block, not the absence of the block. Zero results is an answer the product gives, in the block’s own shell under its own name. | removing the block when the data is empty |
BLOCK-5 | A block draws only facts a producer serves. No field means no field — not a constant, not a placeholder, not a plausible number. | an invented field, which is worse than a missing one because it is not visibly absent |
BLOCK-6 | Two places that show the same thing share one real owner: the shared visual row is merged, the differing interaction host stays apart. | merging on resemblance alone, or quoting half the law |
BLOCK-7 | In a repeated block, columns line up across every row, and content that opens sits flush with the trigger that opened it. | alignment decided per row |
BLOCK-8 | Name the group first, choose the spacing second. Spacing cannot substitute for structure. | a layout tuned by adjusting distances until it looks right |
BLOCK-9 | The name of a list surface belongs to the list branch. Hiding the name is legal only when an enclosing owner already renders that exact resolved name. | a heading drawn outside the list with the name hidden inside it |
BLOCK-10 | List every state before drawing any of them. A state is a situation that picks a different tree; anything drawing the same tree with different words is a prop. | designing the populated case and discovering the rest later |
BLOCK-11 | Every action owns its own pending flag, and one block is one settling unit. | one shared loading flag across several actions — a spinner on the wrong button |
BLOCK-12 | A failure has a visible owner in every layout. A failure is a settled answer, not a wait. | an error rendered as a spinner that never resolves |
BLOCK-13 | A block accepts closed data. It never accepts arbitrary content and never lets the caller decide what appears inside it. | arbitrary content, which makes the block a branch |
BLOCK-14 | Proposals are per block. A surface with N blocks yields 3–4 candidates for each of them. | turning several blocks into 3–4 page-wide combinations |
Rules
- An anatomy carries no class, no token, no colour.
- The state set is enumerated before the anatomy is designed, and the anatomy draws all of it.
repeatscarries arestingCount.- Every part cites a name that exists, or declares a new one with its
why. - No two anatomies in a batch share the whole axis set.
- 3–4 anatomies when the region admits more than one; fewer with the reason stated, never padded.
- A missing product decision is returned to the owner.
- The JSON is canonical, and its hash is what approval attaches to.
- Feedback opens a new round; an accepted anatomy is never edited in place.
Refusal
Refusal is an output. It is used when:
- the request does not say whether an empty region is a real outcome;
- who owns the data cannot be determined from the region or the source;
- the resting count is unstated and the region gives no basis for one;
- a part requires a component that does not exist, which makes it a contract or component change rather than an anatomy choice.
refusal: returned-to-owner
missing: <the decision nobody has made>
blocked: <which parts cannot be resolved without it>Output
The output is JSON, and its authority is schema.json beside this record. envelope
holds what varies between runs — including the accepted layoutHash this region came from — and the hash
covers an anatomy only.
{
"schema": 1,
"envelope": {
"session": "coding-drill-result/2026-08-18",
"round": 1,
"project": "starci-academy",
"region": "criteria",
"layoutHash": "f5534ef5e7fbe30c385108fb95702a64ac66d905414e0f7105873d67822be54c"
},
"anatomies": [
{
"id": "a",
"axes": {"dataOwner": "parent", "repetition": "repeats", "weight": "populated", "composition": "label-value"},
"citesPrecedent": "none",
"states": ["populated", "empty", "pending"],
"restingCount": 4,
"parts": [
{
"name": "criterion-row",
"cites": {"kind": "entry", "verdict": "generalize", "from": "flashcard-result-fact-row", "to": "fact-row", "callSites": 1},
"whyMatch": "a name read against one stored value, repeated as rows on a shared baseline"
}
],
"reason": "why this anatomy is worth the owner's attention"
}
]
}Validate before writing and before hashing:
node <trust>/scripts/validate-artifact.mjs --schema <trust>/brainstorms/blocks/schema.json --data <batch.json> --hashBeyond the shape, the validator refuses a class token anywhere in the batch, two anatomies sharing an axis
set, a repeats anatomy with no restingCount, and a batch where no anatomy cites none.
Worked example
Region. “The criteria list on the coding drill result page: each criterion with its score.”
The states are read from source first: the list is populated when the drill scored criteria, empty when it scored none, and pending while the result loads. Three states, so every anatomy draws three.
Anatomy a reuses the accepted shape with the entry generalised — one call site, so the rename is one
edit. Anatomy b keeps the same shape but treats the empty state as the one carrying the region, because a
drill that scored nothing is a real outcome and a silent list reads as a load that never finished. Anatomy
c is the departure: no repetition at all, the criteria as one grouped visual with a caption, citing
profile-breakdown.
Because c cites none, the batch has its departure. Without it, a and b differ on one axis and the
owner is shown one anatomy twice.
Scope
This stage decides what a block is made of and which states it owes. It does not decide where the region
sits — that was the layout — and it does not decide a class, which is the law’s business. The fourteen laws
an anatomy must satisfy are stated above as BLOCK-n codes, so an anatomy is checked against a citable
code rather than against a reader’s memory of the legacy tree.