From 360acbe33c73295c1e02b242c6805571b92b9ea6 Mon Sep 17 00:00:00 2001 From: giles Date: Tue, 30 Jun 2026 11:40:53 +0000 Subject: [PATCH] =?UTF-8?q?plan:=20types=20define=20the=20UI=20=E2=80=94?= =?UTF-8?q?=20editor=20maps=20onto=20the=20metamodel=20(cards-as-types)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Capture the vision refinement: a type drives BOTH sides of the UI from one definition — fields {name, value-type, widget} drive the edit form (widget per value-type) AND the render template (parameterised SX on the type-post, instantiated with field-values). An instance is just field-values; add a field -> editor + page update, no code. kg-cards become type-posts (the content-on-sx block vocabulary is the seed set); the editor becomes a generic field-editor defined by the metamodel (the relation-editors already prove the pattern). Render template = data (meta-circular); only widgets are platform pieces, selected by value-type. Refined build order: /meta DONE -> Slice 8 typed fields (KEYSTONE) -> generic instance form -> render template -> cards-as-types + migrate; plus create-type/create-relation on /meta + clear-and-reseed. Co-Authored-By: Claude Opus 4.8 --- plans/relations-as-posts.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/plans/relations-as-posts.md b/plans/relations-as-posts.md index b259d21b..e723c725 100644 --- a/plans/relations-as-posts.md +++ b/plans/relations-as-posts.md @@ -117,6 +117,42 @@ So the complete picture: the metamodel expresses **structure + validation** of t platform's domain model uniformly; **behaviour composes from the substrate loops**; **integrations stay referenced services**. It's the convergence point of every loop in the repo. +### Types define the UI — the editor maps onto the metamodel + +The payoff of typed fields (Slice 8): **a type drives both sides of the UI from one definition.** +Beyond name + schema, a type carries **fields** `{name, value-type, widget}` and **templates**: + +- **Fields drive the edit UI** — the editor renders one input per field, the widget chosen by the + field's `value-type` (`Date`→date-picker, `URL`→link input, `String`→text, `Image`→uploader). +- **Fields drive the render** — the type's **render template** (a parameterised SX template stored + on the type-post, instantiated with the instance's field-values) references those fields by name. +- An **instance** is then just *field-values* on a post. Add a field to the type → it appears in + the editor *and* the page, **no code touched**. Same definition, both surfaces. + +**"kg-cards become types."** Each Koenig/Ghost card — image, gallery, callout, embed, bookmark, +heading — becomes a **type-post** with fields + a render template. We've already enumerated that +whole vocabulary: `[[project_content_on_sx]]` modelled heading/text/code/quote/image/embed/divider/ +list/table/callout/media as block types — **that list is the seed set of card-types.** "The old +blog posts get typed" = migrate Ghost content into typed blocks, one type-post per block kind. + +**"The editor maps onto the types."** The editor stops being hardcoded card handlers and becomes a +**generic field-editor**: given a type, emit an input per field; on save, store the values; render +through the type's template. A new card = a new type-post, **zero editor code — the editor is +defined by the metamodel.** Proof the pattern works: the edit page's relation-editors are already +*generated* from relation definitions, not hand-coded (one level up from fields). + +Honest layer: the **render template is data** (editable, meta-circular); only the irreducible +**widgets** (the date-picker, the image-uploader) are platform pieces, and `value-type` is what +*selects* the widget — the same decidable-core / fenced-frontier line as everywhere else. + +**Refined build order** (this is what `/meta` is the on-ramp to): +1. `/meta` overview — DONE (the *see*; `host/blog-type-defs` + `host/blog-meta-index`). +2. **Slice 8 — typed fields** `{name, value-type, widget}` on a type — the **keystone** (drives form + template). +3. **Generic instance form** — input per field ("the editor maps onto types"). +4. **Render template per type** — data, field-placeholders. +5. **Cards-as-types + migrate** — seed the card-type vocabulary from content-on-sx; type the old posts. +Plus the editor surfaces on `/meta`: **create-type** / **create-relation** forms, then **clear-and-reseed**. + ## Behaviour as data — lifecycles + ECA over an effect vocabulary (DESIGN — Slice 9) Structure is inert; "place an order / ship goods" is the dynamic part. The principle: