Spines¶
A spine is a materialized synthesis tree laid over a zettelkasten corpus — a deliberate arrangement you can switch on, switch off, or swap for another, not a separate copy of the corpus. The corpus has exactly one semantic base (the notes and their authored links); a spine re-projects that same base into a shape you chose. This page explains what a spine actually is underneath, why membership lives on the spine side, and how the dashboard renders it. It also folds in the spine definition — the owner-scoped registry entry that names, stores, and governs a spine's lifecycle (once documented on its own Organizations page) — and how a spine relates to the matrix that compares it.
- Materializer:
zettelkasten/spine.py - Definition registry + proposed/spine states:
zettelkasten/organizations.py - Where spines sit in the whole picture: Zettelkasten

Selecting a spine under Organize by re-projects the corpus as a radial tree — one central apex with its dimension spines fanning out — without copying a single note.
Why it exists¶
The base zettelkasten is held together by its intrinsic shape: each note's
authored semantic links — defines, depends-on, component-of, contrasts,
and friends — plus the organic clusters those links form. That substance of the
corpus is always there. But one corpus usefully supports many arrangements: the
same papers can be organized by methodology, by claim status, by a PM-decision
rubric, by a research question. Materializing each of those as a new copy of the
notes would fork the truth; encoding each as a query would make them invisible
and unselectable.
Why a spine and not a tag scheme or a copy
A spine is a re-projection layer. The base notes are never mutated to join one, and a spine never removes a note's semantic links. Membership in a spine is derived, correctable, and disposable — the opposite of the authored, permanent links on the note itself. This is the central rule: a spine adds a structural layer on top of the semantic base without ever replacing it (north-star invariant).
The decisive design move (Spines are additive/separate/selectable layers: NOT project sources, spine-side edges, 'Organize by' picker) is that spines are additive, separate, and selectable, and crucially not project sources. A promoted spine lives in its own graph and is registered in a spine catalog (the definition registry), so the base composite graph can never accidentally cluster a spine's apex/dimension nodes in as if they were ordinary notes. The viewer picks which arrangement is active from an "Organize by" selector; selecting one loads it as a non-destructive overlay and hides the others.
How it works¶
A spine is a tree, not a grid (v2 spine model: structure tree + first-class spine-side membership relation (supersedes grid framing)). It is materialized in its
own dedicated graph: an apex node for the spine itself (tagged synthesis +
extraction-synthesis), then dimension nodes (tagged extraction-dimension
plus a schema dimension tag) and optional row hubs, each component-of its
parent, to arbitrary depth. A member note attaches to any node — interior or
leaf — through a first-class spine-member relation stored spine-side as an
outgoing cross-graph edge (dimension/hub --spine-member--> note@home_graph).
Because the edge lives on the spine, the base note stays pristine, two spines can
never bleed into each other, and teardown is just deleting the spine's graph
folder.
There are therefore two edge classes in two places. Semantic and structural links are substance — authored, permanent, and stored on the base note. Membership is arrangement — derived, rebuildable, and stored on the spine. The default spine is the degenerate case: it has no materialized graph at all, because the intrinsic semantic links already are the organic arrangement.
Every dimension node carries a durable id (source['node_id'], derived from
its schema path) so a relabel, drift, or re-promote re-titles the same node
rather than orphaning it and its spine-member edges. Row hubs are likewise keyed
by a stable row_id, not by their label. This durable-identity discipline is what
lets human corrections survive a rebuild — the lesson learned the hard way in the
membership-loss saga that preceded the current model.
How the dashboard shows a spine¶
A spine is one tree, but you meet it in a few places in the dashboard, and they are not three equal toggles of a single widget. Keeping the view model straight matters, because the corpus renders differently depending on whether a spine exists at all:
- Concepts graph — the primary view. With no materialized spine, the Concepts tab draws the semantic base as clustered halos (organic similarity, not a spine). Once a spine is materialized, the same tab can render that spine as a radial tree / radial dendrogram — the apex at the center, dimensions and row hubs fanning outward, member notes at the leaves. A matrix adds a third rendering: the graph colored by matrix cell. Halos are for semantic clustering; the radial tree is for a spine. They are different renderings of different things, not the same picture relabeled.

A materialized spine as a radial tree — the hub at the center, topic apexes fanning outward through their dimensions to the member notes at the leaves.
- "Organize by" is progressive disclosure. The selector that switches the
active arrangement only appears once the owner has at least one materialized
spine. Before then there is nothing to organize by but the intrinsic semantic
projection, so the control stays hidden rather than offering an empty menu.
- Matrix and Draft live in the Workshop. They are review-scoped projections
opened under a Review in the Workshop — not peer buttons sitting next to the
Concepts graph. The Draft renders the spine tree at full depth; the Matrix
flattens it to two axes (which component-of level becomes the column cut, and
whether a cell rolls up its subtree). Conceptually all three — radial tree,
Draft, Matrix — are projections of the same spine; the invariant that ties
them together is that a projection must never show fewer, or more, nodes than
the base it projects.

The Draft projection renders the same spine tree at full depth — the identical node set the radial tree and matrix draw from, here as an indented list a narrative can be written against.
See Dashboard for the surrounding UI and Matrices for the comparison surface.
The spine definition¶
A spine definition is a named, owner-scoped matrix definition — a row axis
plus an ordered set of typed columns — held at the owner level rather than
buried inside a single review. If a spine is the materialized tree and a
matrix is the comparison surface, a spine definition is the
registry entry that names, stores, and governs the lifecycle of one such
arrangement. It is the thing a project's "Organize by" selector actually lists,
and it lives in organizations.py
(the registry file keeps its historical name).
A corpus supports many organizing principles at once — the same papers by
methodology, by claim status, by a decision rubric. Historically each of those
lived inside one review's .tables.json, which meant an arrangement of the corpus
could not be reused across reviews, listed in a switcher, or promoted into a
durable spine. Decoupling the definition from the review it was born in fixes
all three: the definition becomes a first-class, owner-addressable artifact, while
the heavy grid stays a derived cache (Organizations storage + owner-level navigation architecture (Phase 1a–1d)).
Owner scope is deliberately narrow. A spine definition is owned by exactly one
project or one graph — the same {project, graph} duality a review's
scope uses. That single owner is what makes a definition findable: a project's
catalog is precisely the set of ways you can choose to organize that project, and
nothing leaks in from a sibling.
A definition is a definition, not a copy
A spine definition never forks the corpus. In its proposed state it writes nothing back at all; even when materialized, the base notes stay pristine and only the spine graph carries the derived arrangement. This is the same north-star invariant that governs spines — additive re-projection, never replacement (Organizing principle: semantic base -> spine -> matrix (two kinds of uniting)).
The two states¶
Every spine definition occupies one of two lifecycle states, and the distinction is the heart of the model.
A proposed spine is a live, read-only projection. Its grid is derived on
demand from the row axis and columns — deterministic routing plus, optionally, the
agent-semantic tier — and nothing is written to the corpus. A proposed spine
is cheap, disposable, and always reflects the current corpus: change a note and
the next build reflects it. (This is the state the code still records as lens.)

A proposed spine projects over the live semantic base — the organic clusters the corpus already forms — without writing anything back.
A spine is a materialized arrangement. Promotion builds a real synthesis
graph — an apex, one dimension node per materializable column, one hub per row —
and the definition's spine_ref names that graph. From then on the spine is
durable: dimension nodes carry synthesized bodies, spine-member edges pin
membership spine-side, and the same tree can be projected as a Matrix, Draft, or
Graph.

Promotion materializes that projection into a durable synthesis graph — an apex, dimension nodes, and spine-member edges — shown here as a radial tree.
What a definition holds¶
The stored definition is byte-compatible with what the matrix engine consumes, because it reuses the engine's own normalizers. The load-bearing fields:
columns— the ordered, typed column definitions (the dimensions).row_axis— how rows are enumerated (source, note, tag, note type, or agroupstrategy). The selectable row axis is what generalized the matrix from "one row per source" to any pivot (Row axes: drop persona, add greyed-out 'spine' axis; materialize-spine flow surfaced in Preview).overlay— the author's corrections (member adds/removes and cell overrides), stored as id-references and re-applied on each fresh proposed build so human edits survive a rebuild.matrix_view— how the spine tree flattens into the 2-axis grid (whichcomponent-oflevel is the column cut, whether a cell rolls up its subtree). It only takes effect once the definition is materialized as a spine — a proposed spine has no tree to flatten.spine_ref— the apex graph a promoted definition writes into (empty while proposed).lens_definition— the pre-promotion row axis + columns, stashed on promote so demote/delete can restore the proposed form after the live columns have been rewritten to link-form.table_id(andreview) — the review-local id that keys the grid cache and cell-note anchors.
Storage¶
Each spine definition is a single JSON file, keyed by owner:
<base>/_organizations/<owner_type>/<owner_name>/<id>.json # one definition per file
<base>/_organizations/<owner_type>/<owner_name>/index.json # derived listing
The per-file layout gives every definition an independent, git-trackable
artifact — clean diffs, no whole-store write contention — while index.json is a
derived read-accelerator, rebuilt by scanning the directory on every write so it
can never drift from the source-of-truth files. All writes go through the same
substrate reviews use (review_write_lock → atomic_write_text →
_schedule_zettel_commit), keyed by a per-owner lock, so concurrent edits to two
definitions never corrupt each other. A lazy, idempotent migration imports legacy
review tables as proposed-state definitions the first time an owner is listed, and
records which reviews it has absorbed so a deleted definition is never silently
resurrected.
The lifecycle operations¶
Authoring a spine definition (creating a proposed one, GET/POST on an
owner's definitions) is managed through the dashboard's REST surface. The
lifecycle transitions below are exposed both there and as the spine(action=…)
MCP tool — a single collapsed dispatcher (promote / demote / resync /
verify / delete, plus list) so an agent can drive a spine end-to-end. Both
call the same functions in organizations.py, which serialize on a per-owner
file lock, so the MCP and dashboard paths are safe to interleave:
- promote (
proposed → spine) builds the spine skeleton, bulk-attaches the current cell members onto their dimension and hub nodes, stashes the pre-promotion form inlens_definition, and rewrites the live columns to link-form so future builds route deterministically through the materialized edges. The synthesis graph is not registered as a project source — spines are additive overlays, not corpus members. This same path is what remine reuses when it applies an approved grid. - demote (
spine → proposed) stops treating the spine as materialized but keeps the graph, its nodes, and the attach edges, and reverts the live columns to the stashed proposed definition. It is non-destructive: a later re-promote or resync still finds the nodes. (Discovery honors this — a demoted definition's apex no longer reports as a live spine, and orphaned non-enforcing keyed meta for demoted spines is pruned; Prune orphaned non-enforcing keyed meta for unused/demoted spines.) - resync incrementally re-routes new or unrouted in-scope notes into an existing spine, adding only the missing attach edges (idempotent). Corrections are honored, not undone — a removed member is never re-attached, an added one is attached like any other.
- verify is a read-only synthesis audit: it checks that every synthesized cell summary stays within the notes routed into that cell (ungrounded summaries, empty-with-members, synthesis over ungrounded members), optionally escalating to a semantic overreach check. It never builds or writes.
- delete unlinks the definition file, rebuilds the index, and — because a
project's
default_spinenames a definition by id — clears any danglingdefault_spinepointer so it can never reference a deleted spine.
The default spine¶
A project's default_spine field names the definition that is its primary
"Organize by" principle. Unset means the implicit intrinsic projection — the
organic semantic cluster graph, which has no definition record because there is
nothing to point at. The pointer is kept honest by the registry: it auto-clears
when its definition is deleted and follows one through an id change, so it can
never dangle.
Spine vs spine definition vs matrix¶
These three words name three points on one lifecycle, and they are easy to conflate. Concretely:
| Spine definition | Spine | Matrix | |
|---|---|---|---|
| What it is | A named, owner-scoped definition of how to arrange the corpus | The materialized form of a definition: a real synthesis tree in its own graph | A comparison grid formed when spines share a spine-schema |
| State / where it lives | proposed (live projection) or spine (materialized) — _organizations/… |
A dedicated synthesis graph named by spine_ref |
Emergent — no separate storage; computed from sibling spines |
| Writes to the corpus? | Proposed: no. Spine: materializes a spine graph (never mutates base notes) | Adds spine-member edges spine-side; base notes untouched |
No — a re-projection of existing spines |
| Shape | Whatever the definition describes | Tree: apex → dimensions → row hubs → member notes | Rows = topics (spine apexes), columns = shared dimensions |
| Read more | this page | this page | Matrices |
The short version: a spine definition is the intention, a spine is that intention made real and attachable, and a matrix is what several spines become when they agree on their columns.
What's available¶
- The shared materializer —
build_spine_skeletonstamps the apex, optional spec, and the dimension tree idempotently, behind one tiny ops protocol. Two backends implement it:ServerSpineGraphOpswrites through the MCP server primitives (used by grounded-extraction prep), andSpineGraphOpswrites directly throughZettelGraph(used by promotion, the dashboard, and tests with no server process). Both share one tag/relation vocabulary so writers and the reader can never silently drift. - The spine-definition catalog — a project's spine definitions are its
catalog of candidate arrangements. A definition in the proposed state is a
live, read-only projection (derived on demand, nothing written back); a
definition in the spine state is a materialized, spine-schema-backed spine
whose
spine_refnames its apex graph. Seeorganizations.py. - The default spine — a project's
default_spinefield points at the definition that is its primary "Organize by" principle. Unset means the implicit intrinsic projection: the organic semantic cluster graph, which has no definition record because there is nothing to point at. The pointer auto-clears when its definition goes away, so it can never dangle. - The embedded extraction schema — a materialized spine carries the full expanded extraction schema inline (generated on promotion from the proposed-state columns plus the synthesis block), so a scribe can re-source extraction from the spine itself rather than a separately-named schema file (Schemas can declare materialized structure (the synthesis spine); added a schema-generation tool). This is the grounded-extraction schema — the extraction rubric — a different layer from the spine-schema fingerprint that groups sibling spines into a matrix; see the caution below.
- Discovery, not examples — spines are discovered from the live graph universe
rather than shipped as domain samples (Don't ship domain spine examples; discover live spines via schema(action="spines")).
schema(action="spines")reports each live spine and itsincompletelist (skeleton nodes stillscaffold, not yet authored tomaterialized), so "which spines exist and which are unfinished?" is one query.
Two things named 'schema' — keep them apart
A spine touches two distinct schema layers, and conflating them is the
classic mistake. The spine-schema is the shared rubric a spine-group is
built from — concretely a spine's column_key_fingerprint, its sorted set of
column keys — and it is what decides whether sibling spines fold into one
matrix. The grounded-extraction schema is the extraction
rubric the extraction pipeline reads a source
against (create_extraction_graph, the entries in extraction-schemas.yaml).
A materialized spine embeds the latter inline so a scribe can re-source from
it; the former is derived from that schema's dimension tags but names a
different job — grouping, not extraction. The extraction layer keeps the plain
word schema; the grouping layer is always spine-schema.
Commands¶
Natural-language phrasings for working with spines — say the thing, the agent
runs it. The lifecycle transitions are exposed as the spine(action=…) MCP tool
(one collapsed dispatcher), so the agent can drive a spine end-to-end; the same
transitions are also available from the dashboard UI. Authoring a new
proposed definition (below) is still dashboard-only. See the full
command cheatsheet.
| Say this | What runs |
|---|---|
| "what spines exist", "list the spines", "show unfinished spines" | spine(action="list") / schema(action="spines") |
| "suggest a spine", "propose an arrangement", "what dimensions could organize this" | Spine suggestion from a frame result (dashboard) |
| "make a spine", "define an arrangement by methodology" | Create a proposed spine definition (dashboard) |
| "organize by X", "set the default spine" | project(action="set_default_spine") |
| "promote this spine", "materialize it", "build the spine graph" | spine(action="promote") (proposed → spine) |
| "demote the spine", "unmaterialize", "back to a lens" | spine(action="demote") (spine → proposed) |
| "resync the spine", "route the new notes in", "update membership" | spine(action="resync") |
| "verify the spine", "check the synthesis", "audit for overreach" | spine(action="verify") (read-only; semantic=true for the over-reach tier) |
| "delete this spine", "drop the arrangement" | spine(action="delete") |
| "draft against this spine", "walk the spine", "build the outline for this spine" | syllabus/outline build with a spine partition |
| "attach this claim to the spine", "route this into the right dimension" | note(action="attach") |
| "write this dimension's portrait", "mark this node materialized" | note(action="update", replace_body=…) + synthesis_status → materialized |
Design notes¶
The decisions behind this, drawn as a slice of the memory tree.
- D v2 spine model: structure tree + first-class spine-side membership relation (supersedes grid framing)active
Design refinement raised during the Graph C spine-promotion review. - D Spines are additive/separate/selectable layers: NOT project sources, spine-side edges, 'Organize by' pickeractive
User requirement: building a new spine must be ADDITIVE to the cluster graph but SEPARATE, and the viewer must be able to pick which spine / organizational principle is active. - D Schemas can declare materialized structure (the synthesis spine); added a schema-generation toolactive
Extended extraction schemas so a schema can declare its OWN materialized structure instead of producing a hub-and-spokes dandelion. - D Don't ship domain spine examples; discover live spines via schema(action="spines")active
Two follow-ups to the materialized-spine work. - D Organizations storage + owner-level navigation architecture (Phase 1a–1d)active
An Organization is a first-class, owner-scoped (project OR graph) matrix definition — a row axis + ordered typed columns — decoupled from any single review. - D Organizing principle: semantic base -> spine -> matrix (two kinds of uniting)active
The canonical conceptual model for the whole zettelkasten organization layer. - D Row axes: drop persona, add greyed-out 'spine' axis; materialize-spine flow surfaced in Previewactive
Design decisions from user feedback while testing the matrix wizard: - D Prune orphaned non-enforcing keyed meta for unused/demoted spinesactive
Post-1.3.1 follow-up: closed the documented DEFERRED LIMITATION where materialize_structure writes a structure-only (non-enforcing) keyed block onto each source OPTIMISTICALLY, before reconcile decides membership.
- R Angelo
- P zettelkastenactive
- P Phase 2: Literature Review Systemactive
- P Workshop tab: Reading Syllabus (learn) + claim-centric Review/Outline (write)active
- P Organizations: project-level lenses + spines (matrix builder redesign)active
- D Organizations storage + owner-level navigation architecture (Phase 1a–1d)active
- C Checkpoint: Organizations Phase 1a–1d complete + verified
- D Row axes: drop persona, add greyed-out 'spine' axis; materialize-spine flow surfaced in Previewactive
- P Spine promotion: materialize a matrix lens into a graph (cols+rows, bulk-attach)active
- P Spines as the primary organization mechanism (spine directory + default lens + embedded schema)active
- P Phase: Graph D-phase3 — spine-sourced extraction (scribe reads embedded schema + spine_ref structure; spine scope; spine-member attach)active
- P Deferred spine affordances: multi-spine extraction + tag_stamp route + per-doc spine routingactive
- D Prune orphaned non-enforcing keyed meta for unused/demoted spinesactive
- P Deferred spine affordances: multi-spine extraction + tag_stamp route + per-doc spine routingactive
- P Phase: Graph D-phase3 — spine-sourced extraction (scribe reads embedded schema + spine_ref structure; spine scope; spine-member attach)active
- P Schema matrix views (ephemeral hub) + user-facing Spines renameactive
- D Organizing principle: semantic base -> spine -> matrix (two kinds of uniting)active
- D Organizations storage + owner-level navigation architecture (Phase 1a–1d)active
- P Organizations: project-level lenses + spines (matrix builder redesign)active
- P Workshop tab: Reading Syllabus (learn) + claim-centric Review/Outline (write)active
- P Phase: Grounded Extraction Pipeline + Reusable Schemas (coordinator capability)active
- D Schemas can declare materialized structure (the synthesis spine); added a schema-generation toolactive
- D Don't ship domain spine examples; discover live spines via schema(action="spines")active
- A Schema-authoring docs now require an explicit synthesis policy, not just an extraction policy
- D Schemas can declare materialized structure (the synthesis spine); added a schema-generation toolactive
- P Phase 2: Literature Review Systemactive
- P zettelkastenactive
Related¶
Semantically related entries from the memory graph.