docs/research/decisions/arthrod.md
Should Plate/Slate have a centralized AST model for document conversion, validation, and feature negotiation instead of only app-defined Slate nodes?
Yes, but not in raw Slate.
Raw Slate should stay the unopinionated editing substrate:
Value = Element[]Element = { type, children, ... }Text = { text, ... }EditorDocumentValue = { roots, state? }Plate should own the semantic document model on top.
This page is named after Arthrod, the person who raised the discussion. It is not a proposed package name, namespace, or model name.
That future Plate layer should provide:
Slate's strength is that it does not force a product document ontology. That is also why it is not enough for conversion-heavy product work. A DOCX importer, Markdown exporter, comments system, or suggestion engine needs a semantic model that says what the document means, not only how the editor currently stores editable nodes.
The current Slate v2 model already has the right lower layer:
type EditorDocumentValue = {
roots: Record<string, Descendant[]>
state?: Record<string, unknown>
}
That is a persistence envelope for editor state. It should not become the canonical DOCX/HTML/Markdown interchange AST.
Steal:
Do not steal it wholesale as Slate's value format. Portable Text is a block content format and CMS-shaped schema, not a raw editing core.
Steal:
Do not move Slate toward a ProseMirror-shaped core. The value of Slate is still JSON-like app-defined nodes plus operation/runtime layers.
Steal:
Keep this in import/export packages, not raw editor state. DOCX package graphs are format-specific.
Steal only as export syntax options.
Redoc-style Word change tracking and Markdown comment markers are useful roundtrip formats, but they are not a canonical internal AST. They are views of the semantic model.
Slate core
editing substrate: nodes, roots, paths, selections, operations, transactions
Plate semantic model
semantic document AST, schema, validators, feature policies, diagnostics
Plate plugins
ergonomic authoring APIs and renderers over the semantic model
Format adapters
DOCX, HTML, Markdown, Portable Text, ProseMirror, PDF/export
The source of truth should be a typed model registry, not hand-written Zod schemas.
The registry can generate or expose:
Sketch:
const model = definePlateDocumentModel({
nodes: {
paragraph: blockNode({
children: 'inline*',
fields: {
align: optional(enumValue(['left', 'center', 'right'])),
},
}),
commentAnchor: inlineMarker({
fields: {
threadId: stringValue(),
},
}),
},
features: {
comments: strictFeature(),
insertions: strictFeature(),
deletions: strictFeature(),
},
})
The exact API should come later. The important part is the ownership boundary: Plate owns semantic model law; Slate owns editing substrate law.
Reject. It would make Slate opinionated and drag raw Slate into product/document format policy.
Reject as source of truth. Zod is a good generated validator or integration surface, but it does not by itself define commands, normalization, conversion, feature negotiation, or docs.
Reject as canonical state. Portable Text is excellent prior art for structured portable content, but Plate needs comments, suggestions, tables, rich layout, DOCX fidelity, and editor-runtime bridges that exceed normal Portable Text.
Reject. ProseMirror schema is strong, but adopting it as the model would import ProseMirror's assumptions and weaken Slate/Plate's current direction.
Reject. This recreates the same bug class in every adapter: comments, tracked changes, tables, unknown nodes, unsupported styles, and lossy conversions all need shared policy and diagnostics.
arthrod is only the
discussion attribution.We do need that layer, but not in raw Slate. Slate should stay the unopinionated editing substrate. Plate should own a canonical semantic document model on top, with validators and converters. Then DOCX, Markdown, HTML, and other adapters can preserve what they understand, strip or keep unknown parts by policy, and emit diagnostics instead of pretending conversion is lossless.