Goldie Blocks

The Folio Schema

The settled shape of the unified Folio storage — six recursive tables, twenty-nine binding constraints with their owner rulings, the printed About card grammar, the six-pillar review lens, and the two stages (proposals and Goldie; docs made wholly Current) that Backlog tasks still own.

Decision

The three-tier content schema (documents / modules / blocks, each with its own version table) is replaced by one recursive folios table, one folioVersions table of explicit save points, folioPins carrying containment order, libraries and libraryPins for the shelf, and a tags vocabulary. Six kinds share one drawer; only blocks carry material; the About card is the authored surface. This record is the durable statement of that shape and of the constraints every stage of the build was held to. The two plans that carried the work are retired; what remains unbuilt is owned by Backlog tasks named in the Gap below.

In plain terms: the filing cabinet used to have three drawers of nearly identical cards, so every rule about labels, dividers and cross-references had to be written three times. Now there is one drawer, each card says what sort it is, and the rules are written once. This page is the settled description of that cabinet and the rulings made while it was built. It is not a to-do list; the unfinished pieces are tracked as tasks.

Current — the storage shape as it stands

This is what convex/schema.ts declares today. The six legacy tables and the interview audit table are deleted; both seeded corpora compile straight into these rows.

folios — the card in the drawer. key; kind (one of six: workspace, project, document, module, content_block, data_block); parentFolioKey (absent on a root); handle and optional label; owner; derivedFromFolioKey / derivedFromVersionKey (same-kind lineage); headVersionKey (the open live draft); routeKey (a card with one opens as a page); workingRevision (root Documents only — the proposal baseline); placeholderHandle; externalSyncSource / externalSyncId; deletedAt. Indexes: by key, by parent, by parent and handle, by kind and handle, by route key, by derived-from folio, by owner and kind.

folioVersions — one save point of one folio's own content. key, folioKey, versionNumber, previousVersionKey, restoredFromVersionKey, createdBy, creationMethod, versionStatus (sealed or live draft); the envelope columns guide, tags, access; file_name (declared, unread — owner ruling 2026-09-09: download names derive from label and version); changeNote, editedAt; and payload, present only on blocks and validated by kind. Indexes: by key, by folio and version number, by folio and version status.

folioPins — the save point's schedule. parentVersionKey, childFolioKey, childVersionKey, order. A parent edition's pins say which exact child editions, in what order, that save point comprises. Position lives nowhere else.

libraries and libraryPins — the shelf and its filing slips. A library has key, label, type (public / personal / group) and an optional ownerUserId. A pin names libraryKey, folioKey, the exact folioVersionKey, optional sourceOrder / sourceSiblingMax, and retiredAt. No product writer exists yet; the internal publishToLibrary primitive is the only writer.

tags — the controlled vocabulary. key, type, value. No product writer yet.

Every stored member is typed — no v.any() in the envelope or the payload. The payload validators derive from the domain's About-card schemas, so the card's type and the column's validator are one statement. The read and write contracts, the executor catalogue and the working-revision rule are described on the Folios schema page.

Settled constraints

Binding on every stage; the numbering is the one the build used, so an older commit message or task comment that cites "constraint 19" still resolves.

  1. folios.kind is the sole kind discriminator. Block nature is content_block or data_block, never a second type switch.
  2. Only blocks carry payload; containers have none.
  3. A child has one stable parent folio. A parent version's pins state which exact child versions, in what order, that save point comprises.
  4. Position exists only on folioPins.order; it never prints and Goldie never authors it as a raw scalar.
  5. Derivation is same-kind and records source folio and source version.
  6. Every edit patches an open live draft. A version is created deliberately.
  7. Saving a container seals changed descendants bottom-up, reuses unchanged sealed editions, and seals the container against a fully sealed schedule.
  8. Save Version always appends a checkpoint for the explicitly saved folio; unchanged descendants reuse their latest sealed editions (GB-196).
  9. Progress derives from the fields; nothing stores a status. A field is done when it holds a value or is skipped; skipped is settable only where required is false; complete means every applicable field is answered or skipped (2026-09-02).
  10. reuseType, reuseTypeSnapshot, uiMode, the payment/generation cluster, legacy display/flow inheritance and documentLifecycle are not migrated.
  11. The About card prints identity, guide, tags, access, then content or data. Position and children never print.
  12. Stack v3 is the one live compound shell. Storage migration changes its inputs, not its anatomy. A table change does not earn a new Stack version.
  13. One executor per operation-catalogue row, for every kind, through the shared prologue; the one per-kind branch is the keyed payload union. write-document-guide keeps its own row.
  14. Access rules are kind-agnostic (owner, 2026-09-02): every access member follows one rule whatever the folio's kind. Since the per-folio lock retired on 2026-09-11, the rule governs hidden_for and what follows it.
  15. A lock never binds a Builder (owner, 2026-09-02) — moot since 2026-09-11: there is no lock.
  16. Prefer existing packages over hand-rolled code (owner, 2026-09-02): the prologue is a convex-helpers customMutation, the field contract is one zod schema, collaborative block text is @convex-dev/prosemirror-sync (GB-174, still open). The operation rule book and the folio tree stay hand-rolled: they are the product's own rules.
  17. No dual-write, ever. A unified handler never touches a legacy table.
  18. A control is offered on every kind unless a rule withholds it, and the rule is a fact rather than a kind test. FOLIO_SCHEMA_CONTAINMENT is the one table that says what sits inside what; the operation catalogue reads it rather than re-deriving it.
  19. Remove is offered where a folio is shown as a child, never where it is the subject (owner, 2026-09-04): you cannot remove the card you are standing inside. The deciding fact is the parent in the view, not the kind.
  20. Remove and Delete are different families and both apply to every kind. Remove changes the parent's schedule; Delete marks the folio itself, reversibly, and takes it out of search. Nothing is destroyed.
  21. If real user data or a production deployment is discovered, stop: replace-and-reseed is no longer authorised and the work is replanned as a widen–migrate–reconcile–narrow migration with backup and rollback.
  22. Saved versions are immutable; restore continues linear history (owner, 2026-09-06, GB-196; Sealed Versions Are Immutable). Only the working draft is editable; restore appends the selected state as the next checkpoint.
  23. Delete is a Folio fact, not a placement (owner, 2026-09-04; Delete Is a Folio Fact). A deleted Folio leaves the live canvas and every newly sealed schedule, keeps its live pin for Put back, and older editions are never altered.
  24. The About face writes identity and guide (owner, 2026-09-06; The About Face Writes Identity and Guide). A block's material is edited on its own face; Goldie keeps her own door to numbering and conditions.
  25. Duplicate copies the live draft, whole and fresh (owner, 2026-09-06; Duplicate Copies the Live Draft). Recursive; collected answers cleared; handle takes the next free suffix and the name gains " (copy)"; lineage recorded.
  26. Root Documents are created through an interim browser door (owner, 2026-09-06; Root Documents Are Created Through an Interim Browser Door). The one public write without a session; sign-in deletes the door.
  27. The Document's working revision is the proposal baseline (settled 2026-09-06). The root Document row carries a monotonic workingRevision; every material transaction advances it exactly once through the operation builder; filing records it and acceptance compares and advances it atomically, refusing as stale when it has moved. folio/documentRevision.ts owns the rule; runFolioTransaction is the seam acceptance composes on.
  28. The canvas is one screen for every kind, and depth is a fact (owner, 2026-09-07; The Folio Canvas Shows a Listing). A child with a route key is a card; one without opens in place. Home is the canvas of the session's binding.
  29. Insert from library is one operation with two doors (owner, 2026-09-07; addendum to A Library Is a Shelf). Post-MVP; constrains the source seam Stage E leaves open, nothing more.

The printed About card

The About YAML is the user's authored card and Goldie's read/write surface for facts about the folio. The grammar:

  • Sparse. Optional and empty members are omitted — absence means "not set", never a manufactured default. Absent groups are not printed empty.
  • Booleans print as yes/no; collections print as lists; negative entries are never manufactured.
  • Children never appear — no modules: or blocks: arrays.
  • Neither position nor order prints; the word payload never prints — a block's material prints as its content: or data: wrapper, last.
  • guide: prints directly under identity: on every kind.
  • Stored kind prints as folio_kind.
  • libraries: lists the Libraries holding the folio, by label, printed from its active Library entries (owner, 2026-09-29). It is read-only: an edit to it is ignored, not refused, because membership changes only by a deliberate save from Publish to Libraries › (libraries.mdx). A folio on no shelf prints no libraries: line.
  • The owner prints as the last member of tags:, owner: with their name as the listed cards show it (owner, 2026-09-29), not under access: by account key. Read-only, like libraries:: an edit is ignored. An owner with no stored name prints no owner: line. The on-screen About face shows the same owner avatar after the last tag. Why: the owner is metadata that helps a person judge whether to use the folio, as the tags do, so it reads as one of them, and the YAML matches the card, where the owner closes the tag row in the bottom-right corner and the tags are never empty.
  • Author, MVP (owner, 2026-09-29): the author is taken from the owner at the folio's first save to a Library. Owner and author differ where a folio is held by someone other than its drafter (a publisher's precedent, a firm library), so the author will show as its own avatar beside the owner; where the two are the same person, one avatar names both. A person picks their avatar from a selection and may change it at any time, initials until they do; the card shows the avatar alone, and hovering it names the person and their firm (owner, 2026-09-29). The author is stored as an account id and never edited; the name, firm and avatar shown are read live from that account, which the person edits in their Account details. Not yet built (GB-323).

Canonical top-level order:

identity:
guide:
tags:         # ends with owner: <name>, read-only
libraries:    # read-only, printed from Library entries
access:
logic:        # post-MVP
content:      # content_block — text, numbering, repeats, join_for (post-MVP)
data:         # data_block — fields

The minimum authored card is identity.folio_kind and label, plus the material wrapper on a block. handle prefills from the label and decouples only when edited directly. owner is set by the act of creation. Every absent member has a stated meaning: no guide is no standing instruction; no access.hidden_for is visible to every authorised role; no content.numbering is the app default scheme; no field required is not required; no field value is unanswered. The objects a kind holds are capabilities, not kinds: containers hold identity, access, tags, logic and guide; a content block adds content with numbering, repeats and join_for; a data block adds data. One keyed table drives the validator, the About print and the nesting refusal, and no kind-switch exists outside it.

What the pillars cover

The owner's six pillars are a review lens for the schema, not a storage structure. Each object on the card answers one of them: identity answers what this folio is; position (the pin) answers where it sits; access answers who owns it, may see it and may act on it; logic (post-MVP) answers when it participates; numbering, repeats and join_for answer how a block prints; guide and tags answer why — what it is for and what helps a person or Goldie find and choose it. Three things have no pillar and are deliberately outside the lens: substance (the block's text or fields), provenance (derived_from, created_by) and bindings (route, session, working revision). The lens changes no storage; it is the checklist a reviewer walks to ask whether a proposed member has a home.

Gap — what Backlog tasks own

Stage E — proposals and Goldie address Folios. Proposals landed 2026-09-15 (GB-248): the proposals table and convex/folio/proposalSchedule.ts address folio keys, filing records the Document's working revision as baseline, and acceptance runs inside runFolioTransaction with that revision as expectedRevision, refuses as stale when it has moved, otherwise applies the whole schedule atomically and advances the revision once, and rejection leaves no lineage trace (Proposals Schema). Goldie herself — one agent per folio, the Builder brief, the per-turn briefing and read tools — is GB-277 (One Goldie Per Folio); the scope guard's remaining half and the insertion visual language are GB-145 and GB-278.

Stage F — durable docs become wholly Current. Not started. The Folios schema page still carries a Current / Target / Gap split; the documents, modules and blocks schema pages describe deleted tables; numbering, access, permissions, flow and DSL pages have doc-sync debt (block-level numbering restart, the citation-grammar pointer, the field-type pointer into the DSL spec); and roughly ninety source comments cite the retired plans by path. A Backlog task owns rewriting or archiving those pages, removing the plan citations once the docs own their rules, and passing bun run docs:trace.

On this page