The Folio Tiers Share Parts, Not a Recipe
Why there is no shared folio card composer — a shared behaviour is promoted into the Stack molecule as a slot default the tier mounts and wires, never a shell the tier configures with flags
Decision
There is no shared folio card composer — no component that assembles
spine + header + facet bar + body + footer and takes a tier's configuration.
When a behaviour turns out to be shared between tier cards, it is promoted
into shared/ui/molecules/stack/v3 as a part — a slot default the caller
mounts and wires, the pattern StackDragHandle and StackDragHandleHost
set — never absorbed into a composer that the tiers configure with flags.
In plain terms: the three cards are not one precedent retyped three times; they are three executions of the same standard clauses. When two of them turn out to hand-write the same clause, that clause joins the standard set. Nobody writes a master precedent with a page of tick-boxes for the ways the three executions differ.
Compound upgrade — 2026-09-12
The agreed Stack retains this decision: shared named parts and thin entity adapters, with no kind-configured shell. It extends the parts to metadata-only header disclosure and an independent reserved proposal row across listing and canvas surfaces. The earlier uncommitted Project/Document composer experiment is not the target. The historical rationale below describes the alternatives considered in August; current disclosure semantics are governed by the Stack target.
The screens moved out — 2026-09-14
This record twice absorbed rulings that were not about the composer: screen anatomy, sidebar retention, click-to-open, tag entry, deletion cascade and a schedule of pixel values. They now live in Container Screens Share One Anatomy, which is where to read them. What stays here is the one question this record answers: whether the tier cards are assembled by a shared composer. They are not.
Nor does a record hold its own verification history. Which tier a change selected, which review it required and how many tests it passed belong to the task that did the work; kept here they read as rulings and age badly.
Why
A composer was proposed (GB-111, from the 2026-08-19 architecture review) and grilled on 2026-08-20. The premise did not survive:
- The duplication is two-way, not three-way.
block-card/v4shares almost none of the assembly — it declines the header slot as a recorded ruling (folio-slots.stories.tsx, ruling E) and overlaps the other two tiers by roughly ten lines. The genuine duplicates are two: the disclosure chevron and the sized facet box, shared bydocument-folioandmodule-card/v4only. - The cited evidence refuted the claim. The three cross-tier fixes the
proposal named as "applied three times" each landed once in the shared
shell:
bed1d23in two new shared files plusstack.tsx,ed9a5e3instack.tsx, and GB-96 closed on "stated once in the shared stack shell with no tier-local override". The parts pattern already does the work the composer promised. - The tiers diverge on purpose — where they diverge. A module's header
click expands and never collapses; a document's header click opens the
document and only the arrow toggles — each with its reason written at the
call site. Stale evidence,
amended 2026-09-12: the owner abolished the document's arrow-only collapse on
2026-08-28 — every container strip is now a door that folds its own body, and
a block never folds. The decision stands on its four other grounds, which
still hold. A composer turns
every such divergence into a flag; the three tiers force at least eight
(
hasHeader,hasDisclosure,hasGlyph,hasContainmentRail,hasPortal,headerClickExpands,facetBarInShell,bodyIsFamilySwitched). The repo already rejects a five-flag envelope by name as "a configuration object wearing an interface's clothes" (drafting-editor.tsx). - Both architecture tests fail. Deletion: removing the composer would re-create ~45 lines across two callers, half of which the two parts solve better. Locality: "why does a document header click differently from a module header" is answerable today by reading one file; under a composer the answer splits across the composer and a flag.
- The palette already rules the colour half.
stack-palette.tsis keyed by tone, not entity, and says a new tier "joins by adding a tone… never a new tree". A composer'stieraxis would be exactly the entity-keyed second tree that contract forbids.
Amendment — copies are not divergence (2026-09-14)
The ground above was read too widely, and the code showed it. WorkspaceFolio
arrived as a copy of ProjectFolio and stayed one: normalised for the kind
words, the two files were identical apart from the project's mark menu, its
identity import and its comment block — roughly two hundred lines typed twice.
Neither had a reason written at the call site, because neither had a reason.
So the ground is narrowed to what it always meant. The tiers diverge on purpose where a divergence has a reason recorded at the call site. Two tiers that assemble the same card the same way for no stated reason are not diverging; they are duplicated, and the duplication is the defect the parts rule exists to remove.
The container tiers now mount one shared FolioContainerCard and supply what
is genuinely theirs: the kind, the mark, the leading cell, the Goldie trigger.
It is a part, not a composer — it takes facts and slots and switches on
nothing, and a tier that needs different behaviour passes a different slot
rather than setting a flag. It lives in entities/folio rather than
stack/v3 because it composes the About card and the panel, which Shared may
not import; the folio entity is where the repo already files what belongs to
no single kind.
In plain terms: the rule was never "always retype it". It was "do not write one master precedent with a page of tick-boxes". Two executions that are word for word the same, with no reason given for keeping them apart, are just two copies of one precedent.
Amendment — one card, switched on size (2026-09-27)
The owner ruled (2026-09-27, GB-317) that the Document's card and the
container's card are one card printed at different sizes, and that the shared
card may switch on size, never on kind. FolioCard
(entities/folio/ui/folio-card/folio-card.tsx) takes a size — banner,
large, medium, small — and nothing else that decides its layout.
DocumentFolio and FolioContainerCard mount it and keep what is genuinely
theirs: their words, mark, palette adapter, panel and title.
Size is admitted where a kind flag is not because it is keyed by where the
card stands — heading its page, or in a list — not by what the card is, as
the palette is keyed by tone rather than entity. A Project and a Document at
the same size draw the same card; the same Document on its page and in a list
draws two sizes. Nothing in the card asks which kind it is: folioKind is
printed to data-folio-kind and never read. A kind that needs different
behaviour still supplies a different slot.
The Module and Block cards stay out. They are the ladder's small rung on the
canvas, but their disclosure, facets and drag handle diverge for reasons
written at their call sites, which is the divergence this record protects.
The small size is the bare listed row only (a Library's master Module or
Block in a list, owner ruling 2026-09-27), not those cards.
In plain terms: one standard form, printed on A4, A5 or as a letterhead depending on where it is filed. Choosing the paper size is not a tick-box for "is this a will or a trust"; the form never asks what the document is.
The rule going forward
- A behaviour shared by two or more tier cards is promoted into
stack/v3as a part with a narrow Interface — or intoentities/foliowhere the part needs the folio entity's own furniture; the tier keeps only the wiring and the facts that are genuinely its own. - A behaviour found in one tier stays in that tier until a second caller proves the seam (the two-caller bar the vocabulary authority sets for admitting anything).
- Tone and state styling extend the existing palette tables through thin
per-tier adapters (
block-card-palette.tsis the shape to copy) — never a second table and never a tier axis. folio-slots.stories.tsxremains the Live guard that a fill or a decline cannot silently reverse.- A divergence between two tiers is written down at the call site when it is made. An undocumented difference is treated as drift, and an identical assembly in two tiers is treated as a copy to be promoted — not as evidence that the tiers meant to differ.
The re-scoped GB-111 tracks the parts this decision makes due: the shared disclosure, the shared facet box, the header-paint helper, and the two missing palette adapter files.
Names Are Projections, Not Stored Copies
Why a block's or field's displayed name is computed live from label, handle, or content, why the display is the editor, and why the Control Panel label row was removed
No Separate Interview Event Log
Why the app does not keep a separate interview-event log table