host: metamodel create-relation form (session-scoped) + keep load-rel-kinds! unrolled
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 52s

Define a relation through the UI (metamodel editor surface 1, completing it):
POST /meta/new-relation creates a relation-post (is-a relation, :rel metadata) and
registers it via a runtime concat onto host/blog-rel-kinds — safe because the serving
handler has the IO resolver installed. /meta gains a '+ Relation' form (name, label,
symmetric). Verified: define 'Blocks' (symmetric) -> Relations(5), its editor renders on
edit pages, kind-spec + symmetric correct; auth-guarded.

SESSION-SCOPED: the relation-post + edges persist durably, but the rel-kinds registry
entry is lost on restart because load-rel-kinds! must stay UNROLLED — it runs at BOOT
where it is JIT-compiled but the IO resolver is NOT yet installed, so a dynamic loader
(map/reduce over instances-of 'relation' with a durable read per item) silently returns []
(verified: dynamic -> /meta Relations(0)). The serving-JIT HO-callback-perform fix only
engages with the resolver = serve time. Flagged to sx-vm-extensions (NOTE-render-diff-for-
vm-ext.md); they ACKed + are tracking the boot-resolver fix. Reverted the dynamic loader,
kept the unroll with a comment explaining why.

VERIFICATION NOTE: the full blog suite could not complete — the box is under extreme
contention from sibling loops (load 14, multiple full conformance + erlang/vm-ext rebuilds)
and the Datalog-heavy 140-test suite times out even at a 1800s cap. Verified instead two
ways: (1) live-path HTTP (real route + auth + editor render, ephemeral SX_SERVING_JIT=1),
(2) a focused in-process eval of the create-relation core (exists/is-a/kind-spec/symmetric/
registry-len = true,true,true,true,5). Prior full run was 140/140; changes since are purely
additive (handler + form + route + 3 tests). Re-run the blog suite when the box is quiet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-30 13:52:23 +00:00
parent 536bb8b76b
commit 9effa71dde
4 changed files with 120 additions and 7 deletions

View File

@@ -164,6 +164,14 @@
;; therefore UNROLLED — no iteration over the relation list.) Metadata still lives on
;; the relation-posts; add a relation = a seed-rel! + a line in each unrolled list.
(define host/blog-rel-kinds (list))
;; UNROLLED, and it must STAY unrolled: load-rel-kinds! runs at BOOT, where it is
;; JIT-compiled but the http-listen IO resolver is NOT yet installed (that happens when
;; serving starts). The serving-JIT HO-callback-perform fix (81177d0e) only engages WITH
;; the resolver, so a dynamic loader (map/for-each/reduce over instances-of "relation"
;; with a durable read per item) silently returns [] at boot — verified 2026-06-30:
;; dynamic loader -> /meta Relations(0). So the cache loads + the list are UNROLLED (no
;; HO over a function-produced list). A new relation is a seed-rel! + a line here; or
;; appended at RUNTIME (where the resolver IS installed) — see host/blog-meta-new-relation.
(define host/blog-load-rel-kinds!
(fn ()
(begin
@@ -1187,6 +1195,11 @@
(cons (quote table)
(cons (quote (tr (th "Relation") (th "Label") (th "Kind") (th "Inverse"))) rel-rows))
(quote (p "No relations yet."))))
(form :method "post" :action "/meta/new-relation" :style "margin:0.5em 0 1.5em"
(input :name "title" :placeholder "New relation name" :style "padding:0.3em")
" " (input :name "label" :placeholder "label (optional)" :style "padding:0.3em")
" " (label (input :type "checkbox" :name "symmetric") " symmetric")
" " (button :type "submit" "+ Relation"))
(p :style "margin-top:2em;font-size:0.9em;opacity:0.8"
(a :href "/" "all posts") " · " (a :href "/tags" "tags")
" · " (unquote auth-foot))))))))))
@@ -1208,6 +1221,29 @@
(host/blog-relate! slug "type" "subtype-of"))))
(dream-redirect "/meta"))))
;; POST /meta/new-relation — DEFINE A RELATION THROUGH THE UI (metamodel editor):
;; create a relation-post (is-a relation, carrying its :rel metadata) and register it.
;; SESSION-SCOPED (2026-06-30): the relation-post + any edges it gets persist durably,
;; but the rel-kinds REGISTRY entry is added by a runtime concat (safe — the serving
;; handler has the IO resolver) and is LOST on restart, because the boot loader
;; (host/blog-load-rel-kinds!) is unrolled and can't dynamically enumerate under
;; JIT-at-boot (the kernel boot-resolver gap — flagged to the sx-vm-extensions loop in
;; plans/NOTE-render-diff-for-vm-ext.md). Re-creating the relation re-registers it.
(define host/blog-meta-new-relation
(fn (req)
(let ((title (host/field req "title"))
(label (host/field req "label"))
(symmetric (= (host/field req "symmetric") "on")))
(when (and title (not (= title "")))
(let ((slug (host/blog-slugify title)))
(begin
(host/blog--seed-rel! slug title symmetric
(if (or (nil? label) (= label "")) title label) nil)
(host/blog--cache-rel! slug)
(set! host/blog-rel-kinds
(concat host/blog-rel-kinds (list (get host/blog--rel-cache slug)))))))
(dream-redirect "/meta"))))
;; GET /<slug>/source — the raw sx_content as text/plain. Posts ARE SX source, so
;; this just hands back the stored markup (public; a published post's source is
;; not secret). 404 if the post is absent.
@@ -1467,7 +1503,8 @@
(dream-post "/:slug/edit" (host/blog--protect-html resolve host/blog-edit-submit))
(dream-post "/:slug/relate" (host/blog--protect-html resolve host/blog-relate-submit))
(dream-post "/:slug/unrelate" (host/blog--protect-html resolve host/blog-unrelate-submit))
(dream-post "/meta/new-type" (host/blog--protect-html resolve host/blog-meta-new-type)))))
(dream-post "/meta/new-type" (host/blog--protect-html resolve host/blog-meta-new-type))
(dream-post "/meta/new-relation" (host/blog--protect-html resolve host/blog-meta-new-relation)))))
;; EXPERIMENTAL: create-only, UNGUARDED — POST /new form ingest with error
;; trapping but NO auth, for validating the editor->host publish loop on the