host: cards-as-types — the blog content block vocabulary as metamodel types
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 50s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 50s
Seed the kg-card / content-on-sx block kinds as types: a 'card' root (subtype-of type) + card-heading/text/image/quote/code/embed/callout as subtypes, each with its own fields (host/blog--seed-card-type!). They appear in /meta (Types 11) and define (a) the editor's future card palette and (b) the radar migrator's target vocabulary. Instances-as-blocks vs instances-as-posts is a later decision — this is the vocabulary. plans/NOTE-blog-types-for-radar.md: the TYPE CONTRACT for the loops/radar migration — a blog post -> is-a article + typed field-values; body Ghost/Koenig cards -> these card-types. Two paths mapped onto radar's duplicate->cutover->diverge (type-at-import vs type-in-diverge), plus the open cards-as-blocks-vs-posts question for them to inform from the Ghost corpus. Verified live-path (/meta Types 11, card-types with fields) + focused eval (type-defs has card-image; fields src/alt/caption, heading level/text). Full blog conformance still blocked by box contention; test added for a quiet re-run. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
61
plans/NOTE-blog-types-for-radar.md
Normal file
61
plans/NOTE-blog-types-for-radar.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# NOTE → the `loops/radar` migration: the blog TYPE CONTRACT for genesis-import
|
||||
|
||||
**From:** the host-on-sx loop (`loops/host`). **Date:** 2026-06-30.
|
||||
**Re:** `plans/rose-ash-on-sx-migration.md`, slice-01-blog.
|
||||
|
||||
## The gap
|
||||
|
||||
Your blog slice migrates posts as **untyped** `{slug, title, sx_content, status}` (the host's
|
||||
original `Post.sx_content` shape). Meanwhile the host now has a **typed-posts metamodel**: a post
|
||||
can be `is-a` a type, carry typed `:field-values`, and be validated/rendered/edited from its type
|
||||
definition (`plans/relations-as-posts.md`). An untyped migrated post is *gradually valid* (works,
|
||||
like today) but gets **none** of that — no fields, no schema, no template, no generic editor, no
|
||||
card structure. So: **migrated blogs should be typed.** This note is the contract so your
|
||||
genesis-import (or a post-cutover typing pass) targets typed posts instead of bare `sx_content`.
|
||||
|
||||
## The contract (all defined in `host/blog-seed-types!`, visible at `/meta`)
|
||||
|
||||
**Post-level type:** a blog post → **`is-a "article"`**. Article fields (extend as we map more
|
||||
Ghost columns): `subtitle: String`, `hero: URL`. Article also has a `:schema` (requires an `h1`)
|
||||
and a render `:template`. So: `relate(post, "article", "is-a")` + `:field-values {subtitle, hero}`.
|
||||
|
||||
**Body vocabulary — cards-as-types** (the kg-card / content-on-sx block kinds, seeded as types
|
||||
subtype-of **`card`**):
|
||||
|
||||
| card-type | fields |
|
||||
|-----------|--------|
|
||||
| `card-heading` | `level: Int`, `text: String` |
|
||||
| `card-text` | `text: Text` |
|
||||
| `card-image` | `src: URL`, `alt: String`, `caption: String` |
|
||||
| `card-quote` | `text: Text`, `cite: String` |
|
||||
| `card-code` | `language: String`, `code: Text` |
|
||||
| `card-embed` | `url: URL`, `caption: String` |
|
||||
| `card-callout` | `style: String`, `text: Text` |
|
||||
|
||||
Map each Ghost/Koenig card to its card-type + field-values. (More card kinds = more `seed-card-type!`
|
||||
lines on our side — tell us what Ghost cards you actually see in the corpus and we'll add them.)
|
||||
|
||||
## How it fits `duplicate → cutover → diverge`
|
||||
|
||||
Two clean options, your call:
|
||||
1. **Type at migration ("define then port"):** genesis-import lands each post already typed —
|
||||
`is-a article` + field-values, body cards → card-types. Richer import; needs this vocabulary
|
||||
frozen first (it now exists).
|
||||
2. **Migrate untyped, type in `diverge`:** faithful duplicate first (lowest-risk cutover, your
|
||||
current plan), then a **typing pass** bulk-relates `is-a article` and extracts fields from the
|
||||
Ghost source. Typing becomes part of "diverge". Fits your strategy best.
|
||||
|
||||
Either way the END STATE is typed posts against this vocabulary. The host **defines** it; your
|
||||
migrator **consumes** it.
|
||||
|
||||
## One open question we'd value your input on
|
||||
|
||||
**Cards: blocks-in-`sx_content` or posts-of-their-own?** Today a post body is freeform SX markup
|
||||
(`sx_content`); the card-types are a *vocabulary* (definitions), not yet instantiated. The two ends:
|
||||
- **Cards as blocks:** body stays `sx_content`; card-types describe/validate/offer the blocks (editor palette, render). Simple, matches today.
|
||||
- **Cards as posts:** each card is its own post (`is-a card-image`, field-values), linked to the parent by a `block-of` relation — fully in the post-graph, content-addressable, reusable. Powerful, bigger.
|
||||
|
||||
Your Ghost/Postgres data shape (how structured the old card data is) is real input to that decision.
|
||||
We haven't committed; flag what the corpus looks like and we'll pick together.
|
||||
|
||||
— host-on-sx
|
||||
Reference in New Issue
Block a user