host: typed relations — Phase 2, type resolution with subsumption

The spine: types ARE posts, and typing is transitive the right way. is-a
(instance-of) does NOT chain on its own, but subsumption does — an instance of a
subtype is an instance of the supertype.

- registry gains "subtype-of" (directed, transitive). host/blog-types-of(slug) =
  declared is-a targets PLUS every subtype-of-ancestor of each (composed host-side
  over relations/descendants — no new Datalog rules). host/blog-is-a?(slug,type)
  is transitive through subtype-of.
- host/blog-seed-types! seeds the root type-posts "type" and "tag" (real posts
  that document themselves) with tag subtype-of type, so anything is-a tag is
  transitively a type. Idempotent; wired into serve.sh.
- gradual-validation seam: host/blog-type-schemas (empty) + host/blog-schema-of +
  host/blog-type-valid? (vacuously true with no schemas) wired into edit-submit
  alongside the parse check — enforcement is a one-line add later, not a retrofit.

6 tests: types-of = declared + all subtype-of supertypes; is-a? transitive
through subtype-of; is-a alone does NOT chain; instance of tag is transitively a
type; type-valid vacuous with no schemas. 255/255.

Verified live: /type/ + /tag/ render as posts, tag subtype-of type survived a
recreate (durable), ocaml is-a tag.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-28 16:49:00 +00:00
parent b0b0a0592b
commit cb2fc788d7
3 changed files with 100 additions and 1 deletions

View File

@@ -137,6 +137,10 @@ EPOCH=1
echo "(epoch $EPOCH)"
echo "(eval \"(host/blog-seed! \\\"welcome\\\" \\\"Welcome to the SX host\\\" \\\"(article (h1 \\\\\\\"Welcome to the SX host\\\\\\\") (p \\\\\\\"Rendered by lib/host via render-to-html, from the durable SX store.\\\\\\\"))\\\" \\\"published\\\")\")"
EPOCH=$((EPOCH+1))
# Seed the root type-posts (type, tag) — types ARE posts. Idempotent.
echo "(epoch $EPOCH)"
echo "(eval \"(host/blog-seed-types!)\")"
EPOCH=$((EPOCH+1))
echo "(epoch $EPOCH)"
# Anonymous reads (feed timeline + relations container reads + blog post detail)
# plus the GUARDED blog write routes: POST /new (editor form ingest), POST/PUT/