Restructure SX docs nav into 4 top-level sections with nested routing
New hierarchy: Geography (Reactive Islands, Hypermedia Lakes, Marshes, Isomorphism), Language (Docs, Specs, Bootstrappers, Testing), Applications (CSSX, Protocols), Etc (Essays, Philosophy, Plans). All routes updated to match: /reactive/* → /geography/reactive/*, /docs/* → /language/docs/*, /essays/* → /etc/essays/*, etc. Updates nav-data.sx, all defpage routes, API endpoints, internal links across 43 files. Enhanced find-nav-match for nested group resolution. Also includes: page-helpers-demo sf-total fix (reduce instead of set!), rebootstrapped sx-browser.js and sx_ref.py, defensive slice/rest guards. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -279,8 +279,8 @@
|
||||
|
||||
(~doc-subsection :title "CID References in Page Registry"
|
||||
(p "The page registry (shipped to the client as " (code "<script type=\"text/sx-pages\">") ") currently lists deps by name. Extend to include CIDs:")
|
||||
(~doc-code :code (highlight "{:name \"docs-page\" :path \"/docs/<slug>\"\n :auth \"public\" :has-data false\n :deps ({:name \"~essay-foo\" :cid \"bafy...essay\"}\n {:name \"~doc-code\" :cid \"bafy...doccode\"})\n :content \"(case slug ...)\" :closure {}}" "lisp"))
|
||||
(p "The " (a :href "/plans/predictive-prefetch" :class "text-violet-700 underline" "predictive prefetch system") " uses these CIDs to fetch components from the resolution cascade rather than only from the origin server's " (code "/sx/components") " endpoint."))
|
||||
(~doc-code :code (highlight "{:name \"docs-page\" :path \"/language/docs/<slug>\"\n :auth \"public\" :has-data false\n :deps ({:name \"~essay-foo\" :cid \"bafy...essay\"}\n {:name \"~doc-code\" :cid \"bafy...doccode\"})\n :content \"(case slug ...)\" :closure {}}" "lisp"))
|
||||
(p "The " (a :href "/etc/plans/predictive-prefetch" :class "text-violet-700 underline" "predictive prefetch system") " uses these CIDs to fetch components from the resolution cascade rather than only from the origin server's " (code "/sx/components") " endpoint."))
|
||||
|
||||
(~doc-subsection :title "SX Response Component Headers"
|
||||
(p "Currently, " (code "SX-Components") " header lists loaded component names. Extend to support CIDs:")
|
||||
@@ -407,9 +407,9 @@
|
||||
(~doc-section :title "Relationships" :id "relationships"
|
||||
(p "This plan is the foundation for several other plans and roadmaps:")
|
||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
||||
(li (a :href "/plans/sx-activity" :class "text-violet-700 underline" "SX-Activity") " Phase 2 (content-addressed components on IPFS) is a summary of this plan. This plan supersedes that section with full detail.")
|
||||
(li (a :href "/plans/predictive-prefetch" :class "text-violet-700 underline" "Predictive prefetching") " gains CID-based resolution — the " (code "/sx/components") " endpoint and IPFS gateway become alternative resolution paths in the prefetch cascade.")
|
||||
(li (a :href "/plans/isomorphic-architecture" :class "text-violet-700 underline" "Isomorphic architecture") " Phase 1 (component distribution) is enhanced — CIDs make per-page bundles verifiable and cross-server shareable.")
|
||||
(li (a :href "/etc/plans/sx-activity" :class "text-violet-700 underline" "SX-Activity") " Phase 2 (content-addressed components on IPFS) is a summary of this plan. This plan supersedes that section with full detail.")
|
||||
(li (a :href "/etc/plans/predictive-prefetch" :class "text-violet-700 underline" "Predictive prefetching") " gains CID-based resolution — the " (code "/sx/components") " endpoint and IPFS gateway become alternative resolution paths in the prefetch cascade.")
|
||||
(li (a :href "/etc/plans/isomorphic-architecture" :class "text-violet-700 underline" "Isomorphic architecture") " Phase 1 (component distribution) is enhanced — CIDs make per-page bundles verifiable and cross-server shareable.")
|
||||
(li "The SX-Activity vision of " (strong "serverless applications on IPFS") " depends entirely on this plan. Without content-addressed components, applications can't be pinned to IPFS as self-contained artifacts."))
|
||||
(div :class "rounded border border-amber-200 bg-amber-50 p-3 mt-2"
|
||||
(p :class "text-amber-800 text-sm" (strong "Depends on: ") "deps.sx (complete), boundary enforcement (complete), IPFS infrastructure (exists in artdag, needs wiring to web platform)."))))))
|
||||
|
||||
@@ -278,11 +278,11 @@
|
||||
(tr :class "border-b border-stone-100"
|
||||
(td :class "px-3 py-2 text-stone-700" "Canonical serialization")
|
||||
(td :class "px-3 py-2 text-stone-700" (span :class "text-red-700 font-medium" "Not started"))
|
||||
(td :class "px-3 py-2" (a :href "/plans/content-addressed-components" :class "text-violet-700 underline" "Content-Addressed Components") " Phase 1"))
|
||||
(td :class "px-3 py-2" (a :href "/etc/plans/content-addressed-components" :class "text-violet-700 underline" "Content-Addressed Components") " Phase 1"))
|
||||
(tr :class "border-b border-stone-100"
|
||||
(td :class "px-3 py-2 text-stone-700" "Component CIDs")
|
||||
(td :class "px-3 py-2 text-stone-700" (span :class "text-red-700 font-medium" "Not started"))
|
||||
(td :class "px-3 py-2" (a :href "/plans/content-addressed-components" :class "text-violet-700 underline" "Content-Addressed Components") " Phase 2"))
|
||||
(td :class "px-3 py-2" (a :href "/etc/plans/content-addressed-components" :class "text-violet-700 underline" "Content-Addressed Components") " Phase 2"))
|
||||
(tr :class "border-b border-stone-100"
|
||||
(td :class "px-3 py-2 text-stone-700" "Purity verification")
|
||||
(td :class "px-3 py-2 text-stone-700" (span :class "text-green-700 font-medium" "Complete"))
|
||||
@@ -301,4 +301,4 @@
|
||||
(td :class "px-3 py-2 text-stone-600" "artdag L1/L2, IPFSPin model")))))
|
||||
|
||||
(div :class "rounded border border-amber-200 bg-amber-50 p-3 mt-2"
|
||||
(p :class "text-amber-800 text-sm" (strong "Builds on: ") (a :href "/plans/content-addressed-components" :class "underline" "Content-Addressed Components") " (canonical serialization + CIDs), " (a :href "/plans/self-hosting-bootstrapper" :class "underline" "self-hosting bootstrappers") " (spec-first architecture). " (strong "Enables: ") (a :href "/plans/sx-activity" :class "underline" "SX-Activity") " (serverless applications on IPFS).")))))
|
||||
(p :class "text-amber-800 text-sm" (strong "Builds on: ") (a :href "/etc/plans/content-addressed-components" :class "underline" "Content-Addressed Components") " (canonical serialization + CIDs), " (a :href "/etc/plans/self-hosting-bootstrapper" :class "underline" "self-hosting bootstrappers") " (spec-first architecture). " (strong "Enables: ") (a :href "/etc/plans/sx-activity" :class "underline" "SX-Activity") " (serverless applications on IPFS).")))))
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
(~doc-section :title "Context" :id "context"
|
||||
(p "SX has a working server-client pipeline: server evaluates pages with IO (DB, fragments), serializes as SX wire format, client parses and renders to DOM. The language and primitives are already isomorphic " (em "— same spec, same semantics, both sides.") " What's missing is the " (strong "plumbing") " that makes the boundary between server and client a sliding window rather than a fixed wall.")
|
||||
(p "The key insight: " (strong "s-expressions can partially unfold on the server after IO, then finish unfolding on the client.") " The system knows which components have data fetches (via IO detection in " (a :href "/specs/deps" :class "text-violet-700 underline" "deps.sx") "), resolves those server-side, and sends the rest as pure SX for client rendering. The boundary slides automatically based on what each component actually needs."))
|
||||
(p "The key insight: " (strong "s-expressions can partially unfold on the server after IO, then finish unfolding on the client.") " The system knows which components have data fetches (via IO detection in " (a :href "/language/specs/deps" :class "text-violet-700 underline" "deps.sx") "), resolves those server-side, and sends the rest as pure SX for client rendering. The boundary slides automatically based on what each component actually needs."))
|
||||
|
||||
(~doc-section :title "Current State" :id "current-state"
|
||||
(ul :class "space-y-2 text-stone-700 list-disc pl-5"
|
||||
@@ -32,8 +32,8 @@
|
||||
(div :class "rounded border border-green-300 bg-green-50 p-4 mb-4"
|
||||
(div :class "flex items-center gap-2 mb-2"
|
||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||
(a :href "/specs/deps" :class "text-green-700 underline text-sm font-medium" "View canonical spec: deps.sx")
|
||||
(a :href "/isomorphism/bundle-analyzer" :class "text-green-700 underline text-sm font-medium" "Live bundle analyzer"))
|
||||
(a :href "/language/specs/deps" :class "text-green-700 underline text-sm font-medium" "View canonical spec: deps.sx")
|
||||
(a :href "/geography/isomorphism/bundle-analyzer" :class "text-green-700 underline text-sm font-medium" "Live bundle analyzer"))
|
||||
(p :class "text-green-900 font-medium" "What it enables")
|
||||
(p :class "text-green-800" "Per-page component bundles instead of sending every definition to every page. Smaller payloads, faster boot, better cache hit rates."))
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
(~doc-subsection :title "Implementation"
|
||||
|
||||
(p "The dependency analysis algorithm is defined in "
|
||||
(a :href "/specs/deps" :class "text-violet-700 underline" "deps.sx")
|
||||
(a :href "/language/specs/deps" :class "text-violet-700 underline" "deps.sx")
|
||||
" — a spec module bootstrapped to every host. Each host loads it via " (code "--spec-modules deps") " and provides 6 platform functions. The spec is the single source of truth; hosts are interchangeable.")
|
||||
|
||||
(div :class "space-y-4"
|
||||
@@ -85,7 +85,7 @@
|
||||
(li "15 dedicated tests: scan, transitive closure, circular deps, compute-all, components-needed")
|
||||
(li "Bootstrapped output verified on both host targets")
|
||||
(li "Full test suite passes with zero regressions")
|
||||
(li (a :href "/isomorphism/bundle-analyzer" :class "text-violet-700 underline" "Live bundle analyzer") " shows real per-page savings on this app"))))
|
||||
(li (a :href "/geography/isomorphism/bundle-analyzer" :class "text-violet-700 underline" "Live bundle analyzer") " shows real per-page savings on this app"))))
|
||||
|
||||
;; -----------------------------------------------------------------------
|
||||
;; Phase 2
|
||||
@@ -96,14 +96,14 @@
|
||||
(div :class "rounded border border-green-300 bg-green-50 p-4 mb-4"
|
||||
(div :class "flex items-center gap-2 mb-2"
|
||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||
(a :href "/specs/deps" :class "text-green-700 underline text-sm font-medium" "View canonical spec: deps.sx")
|
||||
(a :href "/isomorphism/bundle-analyzer" :class "text-green-700 underline text-sm font-medium" "Live bundle analyzer with IO"))
|
||||
(a :href "/language/specs/deps" :class "text-green-700 underline text-sm font-medium" "View canonical spec: deps.sx")
|
||||
(a :href "/geography/isomorphism/bundle-analyzer" :class "text-green-700 underline text-sm font-medium" "Live bundle analyzer with IO"))
|
||||
(p :class "text-green-900 font-medium" "What it enables")
|
||||
(p :class "text-green-800" "Automatic IO detection and selective expansion. Server expands IO-dependent components, serializes pure ones for client. Per-component intelligence replaces global toggle."))
|
||||
|
||||
(~doc-subsection :title "IO Detection in the Spec"
|
||||
(p "Five new functions in "
|
||||
(a :href "/specs/deps" :class "text-violet-700 underline" "deps.sx")
|
||||
(a :href "/language/specs/deps" :class "text-violet-700 underline" "deps.sx")
|
||||
" extend the Phase 1 walker to detect IO primitive references:")
|
||||
|
||||
(div :class "space-y-4"
|
||||
@@ -145,7 +145,7 @@
|
||||
(li "Pure components (HTML-only) classified pure with empty io_refs")
|
||||
(li "Transitive IO detection: component calling ~other where ~other calls (current-user) → IO-dependent")
|
||||
(li "Bootstrapped to both hosts (sx_ref.py + sx-ref.js)")
|
||||
(li (a :href "/isomorphism/bundle-analyzer" :class "text-violet-700 underline" "Live bundle analyzer") " shows per-page IO classification"))))
|
||||
(li (a :href "/geography/isomorphism/bundle-analyzer" :class "text-violet-700 underline" "Live bundle analyzer") " shows per-page IO classification"))))
|
||||
|
||||
;; -----------------------------------------------------------------------
|
||||
;; Phase 3
|
||||
@@ -156,8 +156,8 @@
|
||||
(div :class "rounded border border-green-300 bg-green-50 p-4 mb-4"
|
||||
(div :class "flex items-center gap-2 mb-2"
|
||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||
(a :href "/specs/router" :class "text-green-700 underline text-sm font-medium" "View canonical spec: router.sx")
|
||||
(a :href "/isomorphism/routing-analyzer" :class "text-green-700 underline text-sm font-medium" "Live routing analyzer"))
|
||||
(a :href "/language/specs/router" :class "text-green-700 underline text-sm font-medium" "View canonical spec: router.sx")
|
||||
(a :href "/geography/isomorphism/routing-analyzer" :class "text-green-700 underline text-sm font-medium" "Live routing analyzer"))
|
||||
(p :class "text-green-900 font-medium" "What it enables")
|
||||
(p :class "text-green-800" "After initial page load, pure pages render instantly without server roundtrips. Client matches routes locally, evaluates content expressions with cached components, and only falls back to server for pages with :data dependencies."))
|
||||
|
||||
@@ -168,13 +168,13 @@
|
||||
(div
|
||||
(h4 :class "font-semibold text-stone-700" "1. Route matching spec (router.sx)")
|
||||
(p "New spec module with pure functions for Flask-style route pattern matching:")
|
||||
(~doc-code :code (highlight "(define split-path-segments ;; \"/docs/hello\" → (\"docs\" \"hello\")\n(define parse-route-pattern ;; \"/docs/<slug>\" → segment descriptors\n(define match-route-segments ;; segments + pattern → params dict or nil\n(define find-matching-route ;; path + route table → first match" "lisp"))
|
||||
(~doc-code :code (highlight "(define split-path-segments ;; \"/language/docs/hello\" → (\"docs\" \"hello\")\n(define parse-route-pattern ;; \"/language/docs/<slug>\" → segment descriptors\n(define match-route-segments ;; segments + pattern → params dict or nil\n(define find-matching-route ;; path + route table → first match" "lisp"))
|
||||
(p "No platform interface needed — uses only pure string and list primitives. Bootstrapped to both hosts via " (code "--spec-modules deps,router") "."))
|
||||
|
||||
(div
|
||||
(h4 :class "font-semibold text-stone-700" "2. Page registry")
|
||||
(p "Server serializes defpage metadata as SX dict literals inside " (code "<script type=\"text/sx-pages\">") ". Each entry carries name, path pattern, auth level, has-data flag, serialized content expression, and closure values.")
|
||||
(~doc-code :code (highlight "{:name \"docs-page\" :path \"/docs/<slug>\"\n :auth \"public\" :has-data false\n :content \"(case slug ...)\" :closure {}}" "lisp"))
|
||||
(~doc-code :code (highlight "{:name \"docs-page\" :path \"/language/docs/<slug>\"\n :auth \"public\" :has-data false\n :content \"(case slug ...)\" :closure {}}" "lisp"))
|
||||
(p "boot.sx processes these at startup using the SX parser — the same " (code "parse") " function from parser.sx — building route entries with parsed patterns into the " (code "_page-routes") " table. No JSON dependency."))
|
||||
|
||||
(div
|
||||
@@ -191,16 +191,16 @@
|
||||
(~doc-subsection :title "What becomes client-routable"
|
||||
(p "All pages with content expressions — most of this docs app. Pure pages render instantly; :data pages fetch data then render client-side (Phase 4):")
|
||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
||||
(li (code "/") ", " (code "/docs/") ", " (code "/docs/<slug>") " (most slugs), " (code "/protocols/") ", " (code "/protocols/<slug>"))
|
||||
(li (code "/examples/") ", " (code "/examples/<slug>") ", " (code "/essays/") ", " (code "/essays/<slug>"))
|
||||
(li (code "/plans/") ", " (code "/plans/<slug>") ", " (code "/isomorphism/") ", " (code "/bootstrappers/")))
|
||||
(li (code "/") ", " (code "/language/docs/") ", " (code "/language/docs/<slug>") " (most slugs), " (code "/applications/protocols/") ", " (code "/applications/protocols/<slug>"))
|
||||
(li (code "/examples/") ", " (code "/examples/<slug>") ", " (code "/etc/essays/") ", " (code "/etc/essays/<slug>"))
|
||||
(li (code "/etc/plans/") ", " (code "/etc/plans/<slug>") ", " (code "/geography/isomorphism/") ", " (code "/language/bootstrappers/")))
|
||||
(p "Pages that fall through to server:")
|
||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
||||
(li (code "/docs/primitives") " and " (code "/docs/special-forms") " (call " (code "primitives-data") " / " (code "special-forms-data") " helpers)")
|
||||
(li (code "/language/docs/primitives") " and " (code "/language/docs/special-forms") " (call " (code "primitives-data") " / " (code "special-forms-data") " helpers)")
|
||||
(li (code "/reference/<slug>") " (has " (code ":data (reference-data slug)") ")")
|
||||
(li (code "/bootstrappers/<slug>") " (has " (code ":data (bootstrapper-data slug)") ")")
|
||||
(li (code "/isomorphism/bundle-analyzer") " (has " (code ":data (bundle-analyzer-data)") ")")
|
||||
(li (code "/isomorphism/data-test") " (has " (code ":data (data-test-data)") " — " (a :href "/isomorphism/data-test" :class "text-violet-700 underline" "Phase 4 demo") ")")))
|
||||
(li (code "/language/bootstrappers/<slug>") " (has " (code ":data (bootstrapper-data slug)") ")")
|
||||
(li (code "/geography/isomorphism/bundle-analyzer") " (has " (code ":data (bundle-analyzer-data)") ")")
|
||||
(li (code "/geography/isomorphism/data-test") " (has " (code ":data (data-test-data)") " — " (a :href "/geography/isomorphism/data-test" :class "text-violet-700 underline" "Phase 4 demo") ")")))
|
||||
|
||||
(~doc-subsection :title "Try-first/fallback design"
|
||||
(p "Client routing uses a try-first approach: attempt local evaluation in a try/catch, fall back to server fetch on any failure. This avoids needing perfect static analysis of content expressions — if a content expression calls a page helper the client doesn't have, the eval throws, and the server handles it transparently.")
|
||||
@@ -232,7 +232,7 @@
|
||||
(div :class "rounded border border-green-300 bg-green-50 p-4 mb-4"
|
||||
(div :class "flex items-center gap-2 mb-2"
|
||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||
(a :href "/isomorphism/data-test" :class "text-green-700 underline text-sm font-medium" "Live data test page"))
|
||||
(a :href "/geography/isomorphism/data-test" :class "text-green-700 underline text-sm font-medium" "Live data test page"))
|
||||
(p :class "text-green-900 font-medium" "What it enables")
|
||||
(p :class "text-green-800" "Client fetches server-evaluated data and renders :data pages locally. Data cached with TTL to avoid redundant fetches on back/forward navigation. All IO stays server-side — no continuations needed."))
|
||||
|
||||
@@ -258,7 +258,7 @@
|
||||
(li "Cache miss: " (code "sx:route client+data /path") " — fetches from server, caches, renders")
|
||||
(li "Cache hit: " (code "sx:route client+cache /path") " — instant render from cached data")
|
||||
(li "After TTL: stale entry evicted, fresh fetch on next visit"))
|
||||
(p "Try it: navigate to the " (a :href "/isomorphism/data-test" :class "text-violet-700 underline" "data test page") ", go back, return within 30s — the server-time stays the same (cached). Wait 30s+ and return — new time (fresh fetch)."))))
|
||||
(p "Try it: navigate to the " (a :href "/geography/isomorphism/data-test" :class "text-violet-700 underline" "data test page") ", go back, return within 30s — the server-time stays the same (cached). Wait 30s+ and return — new time (fresh fetch)."))))
|
||||
|
||||
(~doc-subsection :title "Files"
|
||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1 font-mono text-sm"
|
||||
@@ -273,7 +273,7 @@
|
||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
||||
(li "30 unit tests: serialize roundtrip, kebab-case, deps, full pipeline simulation, cache TTL")
|
||||
(li "Console: " (code "sx:route client+data") " on first visit, " (code "sx:route client+cache") " on return within 30s")
|
||||
(li (a :href "/isomorphism/data-test" :class "text-violet-700 underline" "Live data test page") " exercises the full pipeline with server time + pipeline steps")
|
||||
(li (a :href "/geography/isomorphism/data-test" :class "text-violet-700 underline" "Live data test page") " exercises the full pipeline with server time + pipeline steps")
|
||||
(li "append! and dict-set! registered as proper primitives in spec + both hosts"))))
|
||||
|
||||
;; -----------------------------------------------------------------------
|
||||
@@ -331,7 +331,7 @@
|
||||
(div :class "rounded border border-green-300 bg-green-50 p-4 mb-4"
|
||||
(div :class "flex items-center gap-2 mb-2"
|
||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||
(a :href "/isomorphism/streaming" :class "text-green-700 underline text-sm font-medium" "Live streaming demo"))
|
||||
(a :href "/geography/isomorphism/streaming" :class "text-green-700 underline text-sm font-medium" "Live streaming demo"))
|
||||
(p :class "text-green-900 font-medium" "What it enables")
|
||||
(p :class "text-green-800" "Server streams partially-evaluated SX as IO resolves. Client renders available subtrees immediately with loading skeletons, fills in suspended parts as data arrives."))
|
||||
|
||||
@@ -392,9 +392,9 @@
|
||||
(li "sx/sxc/pages/helpers.py — streaming-demo-data page helper")))
|
||||
|
||||
(~doc-subsection :title "Demonstration"
|
||||
(p "The " (a :href "/isomorphism/streaming" :class "text-violet-700 underline" "streaming demo page") " exercises the full pipeline:")
|
||||
(p "The " (a :href "/geography/isomorphism/streaming" :class "text-violet-700 underline" "streaming demo page") " exercises the full pipeline:")
|
||||
(ol :class "list-decimal pl-5 text-stone-700 space-y-1"
|
||||
(li "Navigate to " (a :href "/isomorphism/streaming" :class "text-violet-700 underline" "/isomorphism/streaming"))
|
||||
(li "Navigate to " (a :href "/geography/isomorphism/streaming" :class "text-violet-700 underline" "/geography/isomorphism/streaming"))
|
||||
(li "The page skeleton appears " (strong "instantly") " — animated loading skeletons fill the content area")
|
||||
(li "After ~1.5 seconds, the real content replaces the skeletons (streamed from server)")
|
||||
(li "Open the Network tab — observe " (code "Transfer-Encoding: chunked") " on the document response")
|
||||
@@ -481,7 +481,7 @@
|
||||
(~doc-subsection :title "Verification"
|
||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
||||
(li "5 new spec tests (page-render-plan suite)")
|
||||
(li "Render plans visible on " (a :href "/isomorphism/affinity" "affinity demo page"))
|
||||
(li "Render plans visible on " (a :href "/geography/isomorphism/affinity" "affinity demo page"))
|
||||
(li "Client page registry includes :render-plan for each page"))))
|
||||
|
||||
(~doc-subsection :title "7c. Cache Invalidation & Optimistic Data Updates"
|
||||
@@ -518,7 +518,7 @@
|
||||
|
||||
(~doc-subsection :title "Verification"
|
||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
||||
(li "Live demo at " (a :href "/isomorphism/optimistic" :class "text-violet-600 hover:underline" "/isomorphism/optimistic"))
|
||||
(li "Live demo at " (a :href "/geography/isomorphism/optimistic" :class "text-violet-600 hover:underline" "/geography/isomorphism/optimistic"))
|
||||
(li "Console log: " (code "sx:optimistic confirmed") " / " (code "sx:optimistic reverted")))))
|
||||
|
||||
(~doc-subsection :title "7d. Offline Data Layer"
|
||||
@@ -553,7 +553,7 @@
|
||||
|
||||
(~doc-subsection :title "Verification"
|
||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
||||
(li "Live demo at " (a :href "/isomorphism/offline" :class "text-violet-600 hover:underline" "/isomorphism/offline"))
|
||||
(li "Live demo at " (a :href "/geography/isomorphism/offline" :class "text-violet-600 hover:underline" "/geography/isomorphism/offline"))
|
||||
(li "Test with DevTools Network → Offline mode")
|
||||
(li "Console log: " (code "sx:offline queued") ", " (code "sx:offline syncing") ", " (code "sx:offline synced")))))
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@ python test_js_compile.py # renders both, diffs DOM" "bash")))
|
||||
(code "createElement") ", " (code "appendChild") ", " (code "textContent")
|
||||
". This gives SX a compilation target competitive with Svelte's "
|
||||
"approach: components compile away, the framework disappears.")
|
||||
(p "Combined with the " (a :href "/plans/content-addressed-components" "content-addressed components")
|
||||
(p "Combined with the " (a :href "/etc/plans/content-addressed-components" "content-addressed components")
|
||||
" plan, a page's compiled JS could be stored on IPFS by its content hash. "
|
||||
"The server returns a CID. The browser fetches and executes pre-compiled JavaScript. "
|
||||
"No parser, no evaluator, no network round-trip for component definitions."))
|
||||
|
||||
@@ -78,11 +78,11 @@
|
||||
(~doc-section :title "Data Model" :id "data"
|
||||
(p "The current nav data is flat — each section has its own " (code "define") ". The new model is a single tree:")
|
||||
|
||||
(~doc-code :code (highlight "(define sx-nav-tree\n {:label \"sx\"\n :href \"/\"\n :children (list\n {:label \"Docs\"\n :href \"/docs/introduction\"\n :children docs-nav-items}\n {:label \"CSSX\"\n :href \"/cssx/\"\n :children cssx-nav-items}\n {:label \"Reference\"\n :href \"/reference/\"\n :children reference-nav-items}\n {:label \"Protocols\"\n :href \"/protocols/wire-format\"\n :children protocols-nav-items}\n {:label \"Examples\"\n :href \"/examples/click-to-load\"\n :children examples-nav-items}\n {:label \"Essays\"\n :href \"/essays/\"\n :children essays-nav-items}\n {:label \"Philosophy\"\n :href \"/philosophy/sx-manifesto\"\n :children philosophy-nav-items}\n {:label \"Specs\"\n :href \"/specs/\"\n :children specs-nav-items}\n {:label \"Bootstrappers\"\n :href \"/bootstrappers/\"\n :children bootstrappers-nav-items}\n {:label \"Testing\"\n :href \"/testing/\"\n :children testing-nav-items}\n {:label \"Isomorphism\"\n :href \"/isomorphism/\"\n :children isomorphism-nav-items}\n {:label \"Plans\"\n :href \"/plans/\"\n :children plans-nav-items}\n {:label \"Reactive Islands\"\n :href \"/reactive-islands/\"\n :children reactive-islands-nav-items})})" "lisp"))
|
||||
(~doc-code :code (highlight "(define sx-nav-tree\n {:label \"sx\"\n :href \"/\"\n :children (list\n {:label \"Docs\"\n :href \"/language/docs/introduction\"\n :children docs-nav-items}\n {:label \"CSSX\"\n :href \"/applications/cssx/\"\n :children cssx-nav-items}\n {:label \"Reference\"\n :href \"/reference/\"\n :children reference-nav-items}\n {:label \"Protocols\"\n :href \"/applications/protocols/wire-format\"\n :children protocols-nav-items}\n {:label \"Examples\"\n :href \"/examples/click-to-load\"\n :children examples-nav-items}\n {:label \"Essays\"\n :href \"/etc/essays/\"\n :children essays-nav-items}\n {:label \"Philosophy\"\n :href \"/etc/philosophy/sx-manifesto\"\n :children philosophy-nav-items}\n {:label \"Specs\"\n :href \"/language/specs/\"\n :children specs-nav-items}\n {:label \"Bootstrappers\"\n :href \"/language/bootstrappers/\"\n :children bootstrappers-nav-items}\n {:label \"Testing\"\n :href \"/language/testing/\"\n :children testing-nav-items}\n {:label \"Isomorphism\"\n :href \"/geography/isomorphism/\"\n :children isomorphism-nav-items}\n {:label \"Plans\"\n :href \"/etc/plans/\"\n :children plans-nav-items}\n {:label \"Reactive Islands\"\n :href \"/reactive-islands/\"\n :children reactive-islands-nav-items})})" "lisp"))
|
||||
|
||||
(p "The existing per-section lists (" (code "docs-nav-items") ", " (code "plans-nav-items") ", etc.) remain unchanged — they just become the " (code ":children") " of tree nodes. Sub-sections that have their own sub-items can nest further:")
|
||||
|
||||
(~doc-code :code (highlight ";; Future: deeper nesting\n{:label \"Plans\"\n :href \"/plans/\"\n :children (list\n {:label \"Status\" :href \"/plans/status\"}\n {:label \"Bootstrappers\" :href \"/plans/self-hosting-bootstrapper\"\n :children (list\n {:label \"py.sx\" :href \"/plans/self-hosting-bootstrapper\"}\n {:label \"js.sx\" :href \"/plans/js-bootstrapper\"})}\n ;; ...\n )}" "lisp"))
|
||||
(~doc-code :code (highlight ";; Future: deeper nesting\n{:label \"Plans\"\n :href \"/etc/plans/\"\n :children (list\n {:label \"Status\" :href \"/etc/plans/status\"}\n {:label \"Bootstrappers\" :href \"/etc/plans/self-hosting-bootstrapper\"\n :children (list\n {:label \"py.sx\" :href \"/etc/plans/self-hosting-bootstrapper\"}\n {:label \"js.sx\" :href \"/etc/plans/js-bootstrapper\"})}\n ;; ...\n )}" "lisp"))
|
||||
|
||||
(p "The tree depth is unlimited. The nav component recurses."))
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
(~doc-section :title "Path Resolution" :id "resolution"
|
||||
(p "Given a URL path, compute the breadcrumb trail and children. This is a tree walk:")
|
||||
|
||||
(~doc-code :code (highlight "(define resolve-nav-path\n (fn (tree current-href)\n ;; Walk sx-nav-tree, find the node matching current-href,\n ;; return the trail of ancestors + current children.\n ;;\n ;; Returns: {:trail (list of {:node N :siblings S})\n ;; :children (list) or nil\n ;; :depth number}\n ;;\n ;; Example: current-href = \"/plans/typed-sx\"\n ;; → trail: [{:node Plans :siblings [Docs, CSSX, ...]}\n ;; {:node Typed-SX :siblings [Status, Reader-Macros, ...]}]\n ;; → children: nil (leaf node)\n ;; → depth: 2\n (let ((result (walk-nav-tree tree current-href (list))))\n result)))" "lisp"))
|
||||
(~doc-code :code (highlight "(define resolve-nav-path\n (fn (tree current-href)\n ;; Walk sx-nav-tree, find the node matching current-href,\n ;; return the trail of ancestors + current children.\n ;;\n ;; Returns: {:trail (list of {:node N :siblings S})\n ;; :children (list) or nil\n ;; :depth number}\n ;;\n ;; Example: current-href = \"/etc/plans/typed-sx\"\n ;; → trail: [{:node Plans :siblings [Docs, CSSX, ...]}\n ;; {:node Typed-SX :siblings [Status, Reader-Macros, ...]}]\n ;; → children: nil (leaf node)\n ;; → depth: 2\n (let ((result (walk-nav-tree tree current-href (list))))\n result)))" "lisp"))
|
||||
|
||||
(p "This runs server-side (it's a pure function, no IO). The layout component calls it with the current URL and passes the result to " (code "~sx-nav") ". Same pattern as the current " (code "find-current") " but produces a richer result.")
|
||||
|
||||
@@ -181,7 +181,7 @@
|
||||
(~doc-section :title "Layout Simplification" :id "layout"
|
||||
(p "The defpage layout declarations currently specify section, sub-label, sub-href, sub-nav, selected — five params to configure two menu bars. The new layout takes one param: the nav trail.")
|
||||
|
||||
(~doc-code :code (highlight ";; Current (verbose, configures two bars)\n(defpage plan-page\n :path \"/plans/<slug>\"\n :layout (:sx-section\n :section \"Plans\"\n :sub-label \"Plans\"\n :sub-href \"/plans/\"\n :sub-nav (~section-nav :items plans-nav-items\n :current (find-current plans-nav-items slug))\n :selected (or (find-current plans-nav-items slug) \"\"))\n :content (...))\n\n;; New (one param, nav computed from URL)\n(defpage plan-page\n :path \"/plans/<slug>\"\n :layout (:sx-docs :path (str \"/plans/\" slug))\n :content (...))" "lisp"))
|
||||
(~doc-code :code (highlight ";; Current (verbose, configures two bars)\n(defpage plan-page\n :path \"/etc/plans/<slug>\"\n :layout (:sx-section\n :section \"Plans\"\n :sub-label \"Plans\"\n :sub-href \"/etc/plans/\"\n :sub-nav (~section-nav :items plans-nav-items\n :current (find-current plans-nav-items slug))\n :selected (or (find-current plans-nav-items slug) \"\"))\n :content (...))\n\n;; New (one param, nav computed from URL)\n(defpage plan-page\n :path \"/etc/plans/<slug>\"\n :layout (:sx-docs :path (str \"/etc/plans/\" slug))\n :content (...))" "lisp"))
|
||||
|
||||
(p "The layout component computes the nav trail internally from the path and the nav tree. No more passing section names, sub-labels, or pre-built nav components through layout params.")
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
|
||||
(~doc-subsection :title "Eager Bundle"
|
||||
(p "The server already computes per-page component bundles. For key navigation paths — the main nav bar, section nav — the server can include " (em "linked routes' components") " in the initial bundle, not just the current page's.")
|
||||
(~doc-code :code (highlight ";; defpage metadata declares eager prefetch targets\n(defpage docs-page\n :path \"/docs/<slug>\"\n :auth :public\n :prefetch :eager ;; bundle deps for all linked pure routes\n :content (case slug ...))" "lisp"))
|
||||
(~doc-code :code (highlight ";; defpage metadata declares eager prefetch targets\n(defpage docs-page\n :path \"/language/docs/<slug>\"\n :auth :public\n :prefetch :eager ;; bundle deps for all linked pure routes\n :content (case slug ...))" "lisp"))
|
||||
(p "Implementation: " (code "components_for_page()") " already scans the page SX for component refs. Extend it to also scan for " (code "href") " attributes, match them against the page registry, and include those pages' deps in the bundle. The cost is a larger initial payload; the benefit is zero-latency navigation within a section."))
|
||||
|
||||
(~doc-subsection :title "Idle Timer"
|
||||
@@ -123,7 +123,7 @@
|
||||
|
||||
(~doc-subsection :title "Declarative Configuration"
|
||||
(p "All strategies configured via " (code "defpage") " metadata and " (code "sx-prefetch") " attributes on links/containers:")
|
||||
(~doc-code :code (highlight ";; Page-level: what to prefetch for routes linking TO this page\n(defpage docs-page\n :path \"/docs/<slug>\"\n :prefetch :eager) ;; bundle with linking page\n\n(defpage reference-page\n :path \"/reference/<slug>\"\n :prefetch :components) ;; prefetch components, data on click\n\n;; Link-level: override per-link\n(a :href \"/docs/components\"\n :sx-prefetch \"idle\") ;; prefetch after page idle\n\n;; Container-level: approach prediction for nav areas\n(nav :sx-prefetch \"approach\"\n (a :href \"/docs/\") (a :href \"/reference/\") ...)" "lisp"))
|
||||
(~doc-code :code (highlight ";; Page-level: what to prefetch for routes linking TO this page\n(defpage docs-page\n :path \"/language/docs/<slug>\"\n :prefetch :eager) ;; bundle with linking page\n\n(defpage reference-page\n :path \"/reference/<slug>\"\n :prefetch :components) ;; prefetch components, data on click\n\n;; Link-level: override per-link\n(a :href \"/language/docs/components\"\n :sx-prefetch \"idle\") ;; prefetch after page idle\n\n;; Container-level: approach prediction for nav areas\n(nav :sx-prefetch \"approach\"\n (a :href \"/language/docs/\") (a :href \"/reference/\") ...)" "lisp"))
|
||||
(p "Priority cascade: explicit " (code "sx-prefetch") " on link > " (code ":prefetch") " on target defpage > default (hover). The system never prefetches the same components twice — " (code "_prefetch-pending") " and " (code "loaded-component-names") " handle dedup.")))
|
||||
|
||||
;; -----------------------------------------------------------------------
|
||||
@@ -189,7 +189,7 @@
|
||||
|
||||
(~doc-section :title "Request Flow" :id "request-flow"
|
||||
(p "End-to-end example: user hovers a link, components prefetch, click goes client-side.")
|
||||
(~doc-code :code (highlight "User hovers link \"/docs/sx-manifesto\"\n |\n +-- bind-prefetch-on-hover fires (150ms debounce)\n |\n +-- compute-missing-deps(\"/docs/sx-manifesto\")\n | +-- find-matching-route -> page with deps:\n | | [\"~essay-sx-manifesto\", \"~doc-code\"]\n | +-- loaded-component-names -> [\"~nav\", \"~footer\", \"~doc-code\"]\n | +-- missing: [\"~essay-sx-manifesto\"]\n |\n +-- prefetch-components([\"~essay-sx-manifesto\"])\n | +-- GET /sx/components?names=~essay-sx-manifesto\n | | Headers: SX-Components: ~nav,~footer,~doc-code\n | +-- Server resolves transitive deps\n | | (also needs ~rich-text, subtracts already-loaded)\n | +-- Response:\n | (defcomp ~essay-sx-manifesto ...) \n | (defcomp ~rich-text ...)\n |\n +-- sx-process-component-text registers defcomps in env\n |\n +-- User clicks link\n +-- try-client-route(\"/docs/sx-manifesto\")\n +-- has-all-deps? -> true (prefetched!)\n +-- eval content -> DOM\n +-- Client-side render, no server roundtrip" "text")))
|
||||
(~doc-code :code (highlight "User hovers link \"/language/docs/sx-manifesto\"\n |\n +-- bind-prefetch-on-hover fires (150ms debounce)\n |\n +-- compute-missing-deps(\"/language/docs/sx-manifesto\")\n | +-- find-matching-route -> page with deps:\n | | [\"~essay-sx-manifesto\", \"~doc-code\"]\n | +-- loaded-component-names -> [\"~nav\", \"~footer\", \"~doc-code\"]\n | +-- missing: [\"~essay-sx-manifesto\"]\n |\n +-- prefetch-components([\"~essay-sx-manifesto\"])\n | +-- GET /sx/components?names=~essay-sx-manifesto\n | | Headers: SX-Components: ~nav,~footer,~doc-code\n | +-- Server resolves transitive deps\n | | (also needs ~rich-text, subtracts already-loaded)\n | +-- Response:\n | (defcomp ~essay-sx-manifesto ...) \n | (defcomp ~rich-text ...)\n |\n +-- sx-process-component-text registers defcomps in env\n |\n +-- User clicks link\n +-- try-client-route(\"/language/docs/sx-manifesto\")\n +-- has-all-deps? -> true (prefetched!)\n +-- eval content -> DOM\n +-- Client-side render, no server roundtrip" "text")))
|
||||
|
||||
;; -----------------------------------------------------------------------
|
||||
;; File changes
|
||||
@@ -248,7 +248,7 @@
|
||||
|
||||
(~doc-section :title "Relationship to Isomorphic Roadmap" :id "relationship"
|
||||
(p "This plan sits between Phase 3 (client-side routing) and Phase 4 (client async & IO bridge) of the "
|
||||
(a :href "/plans/isomorphic-architecture" :class "text-violet-700 underline" "isomorphic architecture roadmap")
|
||||
(a :href "/etc/plans/isomorphic-architecture" :class "text-violet-700 underline" "isomorphic architecture roadmap")
|
||||
". It extends Phase 3 by making more navigations go client-side without needing any IO bridge — purely by ensuring component definitions are available before they're needed.")
|
||||
(div :class "rounded border border-amber-200 bg-amber-50 p-3 mt-2"
|
||||
(p :class "text-amber-800 text-sm" (strong "Depends on: ") "Phase 3 (client-side routing with deps checking). No dependency on Phase 4.")))))
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
(~doc-code :code (highlight "#'my-function → (quote my-function)" "lisp")))
|
||||
|
||||
(~doc-subsection :title "Extensible dispatch: #name"
|
||||
(p "User-defined reader macros via " (code "#name expr") ". The parser reads an identifier after " (code "#") ", looks up a handler in the reader macro registry, and calls it with the next parsed expression. See the " (a :href "/plans/reader-macro-demo" :class "text-violet-600 hover:underline" "#z3 demo") " for a working example that translates SX spec declarations to SMT-LIB.")))
|
||||
(p "User-defined reader macros via " (code "#name expr") ". The parser reads an identifier after " (code "#") ", looks up a handler in the reader macro registry, and calls it with the next parsed expression. See the " (a :href "/etc/plans/reader-macro-demo" :class "text-violet-600 hover:underline" "#z3 demo") " for a working example that translates SX spec declarations to SMT-LIB.")))
|
||||
|
||||
|
||||
;; -----------------------------------------------------------------------
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
;; -----------------------------------------------------------------------
|
||||
|
||||
(~doc-section :title "Tiers" :id "tiers"
|
||||
(p "Four tiers, matching the " (a :href "/reactive/plan" :class "text-violet-700 underline" "reactive islands") " levels:")
|
||||
(p "Four tiers, matching the " (a :href "/geography/reactive/plan" :class "text-violet-700 underline" "reactive islands") " levels:")
|
||||
|
||||
(div :class "overflow-x-auto rounded border border-stone-200 mb-4"
|
||||
(table :class "w-full text-left text-sm"
|
||||
@@ -82,7 +82,7 @@
|
||||
;; -----------------------------------------------------------------------
|
||||
|
||||
(~doc-section :title "The Slicer is SX" :id "slicer-is-sx"
|
||||
(p "Per the " (a :href "/plans/self-hosting-bootstrapper" :class "text-violet-700 underline" "self-hosting principle") ", the slicer is not a build tool — it's a spec module. " (code "slice.sx") " analyzes the spec's own dependency graph and determines which defines belong to which tier.")
|
||||
(p "Per the " (a :href "/etc/plans/self-hosting-bootstrapper" :class "text-violet-700 underline" "self-hosting principle") ", the slicer is not a build tool — it's a spec module. " (code "slice.sx") " analyzes the spec's own dependency graph and determines which defines belong to which tier.")
|
||||
(p (code "js.sx") " (the self-hosting SX-to-JavaScript translator) already compiles the full spec. Slicing is a filter: " (code "js.sx") " translates only the defines that " (code "slice.sx") " selects for a given tier.")
|
||||
|
||||
(~doc-code :code (highlight ";; slice.sx — determine which defines each tier needs\n;;\n;; Input: the full list of defines from all spec files\n;; Output: a filtered list for the requested tier\n\n(define tier-deps\n ;; Which spec modules each tier requires\n {:L0 (list \"engine\" \"boot-partial\")\n :L1 (list \"engine\" \"boot-partial\" \"dom-partial\")\n :L2 (list \"engine\" \"boot-partial\" \"dom-partial\"\n \"signals\" \"dom-island\")\n :L3 (list \"eval\" \"render\" \"parser\"\n \"engine\" \"orchestration\" \"boot\"\n \"dom\" \"signals\" \"router\")})\n\n(define slice-defines\n (fn (tier all-defines)\n ;; 1. Get the module list for this tier\n ;; 2. Walk each define's dependency references\n ;; 3. Include a define only if ALL its deps are\n ;; satisfiable within the tier's module set\n ;; 4. Return the filtered define list\n (let ((modules (get tier-deps tier)))\n (filter\n (fn (d) (tier-satisfies? modules (define-deps d)))\n all-defines))))" "lisp"))
|
||||
@@ -161,7 +161,7 @@
|
||||
|
||||
(~doc-subsection :title "Cache Behavior"
|
||||
(p "Each tier file is content-hashed (like the current " (code "sx_js_hash") " mechanism). Cache-forever semantics. A user who visits any L0 page caches the L0 runtime permanently. If they later visit an L2 page, only the ~10KB delta downloads.")
|
||||
(p "Combined with " (a :href "/plans/environment-images" :class "text-violet-700 underline" "environment images") ": the image CID includes the tier. An L0 image is smaller than an L3 image — it contains fewer primitives, no parser state, no evaluator. The standalone HTML bundle for an L0 page is tiny.")))
|
||||
(p "Combined with " (a :href "/etc/plans/environment-images" :class "text-violet-700 underline" "environment images") ": the image CID includes the tier. An L0 image is smaller than an L3 image — it contains fewer primitives, no parser state, no evaluator. The standalone HTML bundle for an L0 page is tiny.")))
|
||||
|
||||
;; -----------------------------------------------------------------------
|
||||
;; Automatic tier detection
|
||||
@@ -273,10 +273,10 @@
|
||||
|
||||
(~doc-section :title "Relationships" :id "relationships"
|
||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
||||
(li (a :href "/plans/environment-images" :class "text-violet-700 underline" "Environment Images") " — tiered images are smaller. An L0 image omits the parser, evaluator, and most primitives.")
|
||||
(li (a :href "/plans/content-addressed-components" :class "text-violet-700 underline" "Content-Addressed Components") " — component CID resolution is L3-only. L0 pages don't resolve components client-side.")
|
||||
(li (a :href "/reactive/plan" :class "text-violet-700 underline" "Reactive Islands") " — L2 tier is defined by island presence. The signal runtime is the L1→L2 delta.")
|
||||
(li (a :href "/plans/isomorphic-architecture" :class "text-violet-700 underline" "Isomorphic Architecture") " — client-side page rendering is L3. Most pages don't need it."))
|
||||
(li (a :href "/etc/plans/environment-images" :class "text-violet-700 underline" "Environment Images") " — tiered images are smaller. An L0 image omits the parser, evaluator, and most primitives.")
|
||||
(li (a :href "/etc/plans/content-addressed-components" :class "text-violet-700 underline" "Content-Addressed Components") " — component CID resolution is L3-only. L0 pages don't resolve components client-side.")
|
||||
(li (a :href "/geography/reactive/plan" :class "text-violet-700 underline" "Reactive Islands") " — L2 tier is defined by island presence. The signal runtime is the L1→L2 delta.")
|
||||
(li (a :href "/etc/plans/isomorphic-architecture" :class "text-violet-700 underline" "Isomorphic Architecture") " — client-side page rendering is L3. Most pages don't need it."))
|
||||
|
||||
(div :class "rounded border border-amber-200 bg-amber-50 p-3 mt-2"
|
||||
(p :class "text-amber-800 text-sm" (strong "Depends on: ") (code "js.sx") " (complete), " (code "deps.sx") " (complete), " (code "bootstrap_js.py") " adapter selection (exists). " (strong "New: ") (code "slice.sx") " spec module.")))))
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
(p :class "text-green-700 text-sm"
|
||||
(code "py.sx") " is implemented and verified. G0 == G1: 128/128 defines match, "
|
||||
"1490 lines, 88,955 bytes — byte-for-byte identical. "
|
||||
(a :href "/bootstrappers/self-hosting" :class "underline text-green-600 font-medium"
|
||||
(a :href "/language/bootstrappers/self-hosting" :class "underline text-green-600 font-medium"
|
||||
"See live verification."))))
|
||||
|
||||
;; -----------------------------------------------------------------------
|
||||
|
||||
@@ -37,37 +37,37 @@
|
||||
(div :class "rounded border border-green-200 bg-green-50 p-4"
|
||||
(div :class "flex items-center gap-2 mb-1"
|
||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||
(a :href "/isomorphism/" :class "font-semibold text-green-800 underline" "Isomorphic Phase 1: Dependency Analysis"))
|
||||
(a :href "/geography/isomorphism/" :class "font-semibold text-green-800 underline" "Isomorphic Phase 1: Dependency Analysis"))
|
||||
(p :class "text-sm text-stone-600" "Per-page component bundles via deps.sx. Transitive closure, scan-refs, components-needed, page-css-classes. 15 tests, bootstrapped to both hosts."))
|
||||
|
||||
(div :class "rounded border border-green-200 bg-green-50 p-4"
|
||||
(div :class "flex items-center gap-2 mb-1"
|
||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||
(a :href "/isomorphism/" :class "font-semibold text-green-800 underline" "Isomorphic Phase 2: IO Detection"))
|
||||
(a :href "/geography/isomorphism/" :class "font-semibold text-green-800 underline" "Isomorphic Phase 2: IO Detection"))
|
||||
(p :class "text-sm text-stone-600" "Automatic IO classification. scan-io-refs, transitive-io-refs, compute-all-io-refs. Server expands IO components, serializes pure ones for client."))
|
||||
|
||||
(div :class "rounded border border-green-200 bg-green-50 p-4"
|
||||
(div :class "flex items-center gap-2 mb-1"
|
||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||
(a :href "/isomorphism/" :class "font-semibold text-green-800 underline" "Isomorphic Phase 3: Client-Side Routing"))
|
||||
(a :href "/geography/isomorphism/" :class "font-semibold text-green-800 underline" "Isomorphic Phase 3: Client-Side Routing"))
|
||||
(p :class "text-sm text-stone-600" "router.sx spec, page registry via <script type=\"text/sx-pages\">, client route matching, try-first/fallback to server. Pure pages render without server roundtrips."))
|
||||
|
||||
(div :class "rounded border border-green-200 bg-green-50 p-4"
|
||||
(div :class "flex items-center gap-2 mb-1"
|
||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||
(a :href "/isomorphism/" :class "font-semibold text-green-800 underline" "Isomorphic Phase 4: Client Async & IO Bridge"))
|
||||
(a :href "/geography/isomorphism/" :class "font-semibold text-green-800 underline" "Isomorphic Phase 4: Client Async & IO Bridge"))
|
||||
(p :class "text-sm text-stone-600" "Server evaluates :data expressions, serializes as SX wire format. Client fetches pre-evaluated data, caches with 30s TTL, renders :content locally. 30 unit tests."))
|
||||
|
||||
(div :class "rounded border border-green-200 bg-green-50 p-4"
|
||||
(div :class "flex items-center gap-2 mb-1"
|
||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||
(a :href "/isomorphism/" :class "font-semibold text-green-800 underline" "Isomorphic Phase 5: Client IO Proxy"))
|
||||
(a :href "/geography/isomorphism/" :class "font-semibold text-green-800 underline" "Isomorphic Phase 5: Client IO Proxy"))
|
||||
(p :class "text-sm text-stone-600" "IO primitives (highlight, asset-url, etc.) proxied to server via registerIoDeps(). Async DOM renderer handles promises through the render tree. Components with IO deps render client-side via server round-trips — no continuations needed."))
|
||||
|
||||
(div :class "rounded border border-green-200 bg-green-50 p-4"
|
||||
(div :class "flex items-center gap-2 mb-1"
|
||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||
(a :href "/testing/" :class "font-semibold text-green-800 underline" "Modular Test Architecture"))
|
||||
(a :href "/language/testing/" :class "font-semibold text-green-800 underline" "Modular Test Architecture"))
|
||||
(p :class "text-sm text-stone-600" "Per-module test specs (eval, parser, router, render) with 161 tests. Three runners: Python, Node.js, browser. 5 platform functions, everything else pure SX."))))
|
||||
|
||||
;; -----------------------------------------------------------------------
|
||||
@@ -81,7 +81,7 @@
|
||||
(div :class "rounded border border-amber-200 bg-amber-50 p-4"
|
||||
(div :class "flex items-center gap-2 mb-1"
|
||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-amber-600 text-white uppercase" "Partial")
|
||||
(a :href "/plans/fragment-protocol" :class "font-semibold text-amber-900 underline" "Fragment Protocol"))
|
||||
(a :href "/etc/plans/fragment-protocol" :class "font-semibold text-amber-900 underline" "Fragment Protocol"))
|
||||
(p :class "text-sm text-stone-600" "Fragment GET infrastructure works. The planned POST/sexp structured protocol for transferring component definitions between services is not yet implemented. Fragment endpoints still use legacy GET + X-Fragment-Request headers."))))
|
||||
|
||||
;; -----------------------------------------------------------------------
|
||||
@@ -95,42 +95,42 @@
|
||||
(div :class "rounded border border-stone-200 bg-stone-50 p-4"
|
||||
(div :class "flex items-center gap-2 mb-1"
|
||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-700 text-white uppercase" "Done")
|
||||
(a :href "/plans/reader-macros" :class "font-semibold text-stone-800 underline" "Reader Macros"))
|
||||
(a :href "/etc/plans/reader-macros" :class "font-semibold text-stone-800 underline" "Reader Macros"))
|
||||
(p :class "text-sm text-stone-600" "# dispatch in parser.sx spec, Python parser.py, hand-written sx.js. Three built-ins (#;, #|...|, #') plus extensible #name dispatch. #z3 demo translates define-primitive to SMT-LIB.")
|
||||
(p :class "text-sm text-stone-500 mt-1" "48 parser tests (SX + Python), all passing. Rebootstrapped to JS and Python."))
|
||||
|
||||
(div :class "rounded border border-stone-200 bg-stone-50 p-4"
|
||||
(div :class "flex items-center gap-2 mb-1"
|
||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-stone-500 text-white uppercase" "Not Started")
|
||||
(a :href "/plans/sx-activity" :class "font-semibold text-stone-800 underline" "SX-Activity"))
|
||||
(a :href "/etc/plans/sx-activity" :class "font-semibold text-stone-800 underline" "SX-Activity"))
|
||||
(p :class "text-sm text-stone-600" "Federated SX over ActivityPub — 6 phases from SX wire format for activities to the evaluable web on IPFS. Existing AP infrastructure provides the foundation but no SX-specific federation code exists.")
|
||||
(p :class "text-sm text-stone-500 mt-1" "Remaining: shared/sx/activity.py (SX<->JSON-LD), shared/sx/ipfs.py, shared/sx/ref/ipfs-resolve.sx, shared/sx/registry.py, shared/sx/anchor.py."))
|
||||
|
||||
(div :class "rounded border border-stone-200 bg-stone-50 p-4"
|
||||
(div :class "flex items-center gap-2 mb-1"
|
||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-stone-500 text-white uppercase" "Not Started")
|
||||
(a :href "/plans/glue-decoupling" :class "font-semibold text-stone-800 underline" "Cross-App Decoupling via Glue"))
|
||||
(a :href "/etc/plans/glue-decoupling" :class "font-semibold text-stone-800 underline" "Cross-App Decoupling via Glue"))
|
||||
(p :class "text-sm text-stone-600" "Eliminate all cross-app model imports by routing through a glue service layer. No glue/ directory exists. Apps are currently decoupled via HTTP interfaces and DTOs instead.")
|
||||
(p :class "text-sm text-stone-500 mt-1" "Remaining: glue/services/ for pages, page_config, calendars, marketplaces, cart_items, products, post_associations. 25+ cross-app imports to eliminate."))
|
||||
|
||||
(div :class "rounded border border-stone-200 bg-stone-50 p-4"
|
||||
(div :class "flex items-center gap-2 mb-1"
|
||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-stone-500 text-white uppercase" "Not Started")
|
||||
(a :href "/plans/social-sharing" :class "font-semibold text-stone-800 underline" "Social Network Sharing"))
|
||||
(a :href "/etc/plans/social-sharing" :class "font-semibold text-stone-800 underline" "Social Network Sharing"))
|
||||
(p :class "text-sm text-stone-600" "OAuth-based sharing to Facebook, Instagram, Threads, Twitter/X, LinkedIn, and Mastodon via the account service. No models, blueprints, or platform clients created.")
|
||||
(p :class "text-sm text-stone-500 mt-1" "Remaining: SocialConnection model, social_crypto.py, platform OAuth clients (6), account/bp/social/ blueprint, share button fragment."))
|
||||
|
||||
(div :class "rounded border border-green-200 bg-green-50 p-4"
|
||||
(div :class "flex items-center gap-2 mb-1"
|
||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||
(a :href "/isomorphism/" :class "font-semibold text-stone-800 underline" "Isomorphic Phase 6: Streaming & Suspense"))
|
||||
(a :href "/geography/isomorphism/" :class "font-semibold text-stone-800 underline" "Isomorphic Phase 6: Streaming & Suspense"))
|
||||
(p :class "text-sm text-stone-600" "Server streams partially-evaluated SX as IO resolves. ~suspense component renders fallbacks, inline resolution scripts fill in content. Concurrent IO via asyncio, chunked transfer encoding.")
|
||||
(p :class "text-sm text-stone-500 mt-1" "Demo: " (a :href "/isomorphism/streaming" "/isomorphism/streaming")))
|
||||
(p :class "text-sm text-stone-500 mt-1" "Demo: " (a :href "/geography/isomorphism/streaming" "/geography/isomorphism/streaming")))
|
||||
|
||||
(div :class "rounded border border-green-300 bg-green-50 p-4"
|
||||
(div :class "flex items-center gap-2 mb-1"
|
||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||
(a :href "/isomorphism/" :class "font-semibold text-stone-800 underline" "Isomorphic Phase 7: Full Isomorphism"))
|
||||
(a :href "/geography/isomorphism/" :class "font-semibold text-stone-800 underline" "Isomorphic Phase 7: Full Isomorphism"))
|
||||
(p :class "text-sm text-stone-600" "Affinity annotations, render plans, optimistic data updates, offline mutation queue, isomorphic testing harness, universal page descriptor.")
|
||||
(p :class "text-sm text-stone-500 mt-1" "All 6 sub-phases (7a–7f) complete."))))))
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
(~doc-section :title "The Opportunity" :id "opportunity"
|
||||
(p "SX already has types. Every primitive in " (code "primitives.sx") " declares " (code ":returns \"number\"") " or " (code ":returns \"boolean\"") ". Every IO primitive in " (code "boundary.sx") " declares " (code ":returns \"dict?\"") " or " (code ":returns \"any\"") ". Component params are named. The information exists — nobody checks it.")
|
||||
(p "A gradual type system makes this information useful. Annotations are optional. Unannotated code works exactly as before. Annotated code gets checked at registration time — zero runtime cost, errors before any request is served. The checker is a spec module (" (code "types.sx") "), bootstrapped to every host.")
|
||||
(p "This is not Haskell. SX doesn't need a type system to be correct — " (a :href "/plans/theorem-prover" :class "text-violet-700 underline" "prove.sx") " already verifies primitive properties by exhaustive search. Types serve a different purpose: they catch " (strong "composition errors") " — wrong argument passed to a component, mismatched return type piped into another function, missing keyword arg. The kind of bug you find by reading the stack trace and slapping your forehead."))
|
||||
(p "This is not Haskell. SX doesn't need a type system to be correct — " (a :href "/etc/plans/theorem-prover" :class "text-violet-700 underline" "prove.sx") " already verifies primitive properties by exhaustive search. Types serve a different purpose: they catch " (strong "composition errors") " — wrong argument passed to a component, mismatched return type piped into another function, missing keyword arg. The kind of bug you find by reading the stack trace and slapping your forehead."))
|
||||
|
||||
;; -----------------------------------------------------------------------
|
||||
;; What already exists
|
||||
@@ -111,7 +111,7 @@
|
||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
||||
(li (strong "Runtime values.") " " (code "(if condition 42 \"hello\")") " — the type is " (code "(or number string)") ". The checker doesn't know which branch executes.")
|
||||
(li (strong "Dict key presence.") " " (code "(get user \"name\")") " — the checker knows " (code "get") " returns " (code "any") " but doesn't track which keys a dict has. (Future: typed dicts/records.)")
|
||||
(li (strong "Termination.") " That's " (a :href "/plans/theorem-prover" :class "text-violet-700 underline" "prove.sx") "'s domain.")
|
||||
(li (strong "Termination.") " That's " (a :href "/etc/plans/theorem-prover" :class "text-violet-700 underline" "prove.sx") "'s domain.")
|
||||
(li (strong "Effects.") " Purity is already enforced by " (code "deps.sx") " + boundary. Types don't duplicate it.")))
|
||||
|
||||
(~doc-subsection :title "Inference"
|
||||
@@ -225,7 +225,7 @@
|
||||
;; -----------------------------------------------------------------------
|
||||
|
||||
(~doc-section :title "Types vs Proofs" :id "types-vs-proofs"
|
||||
(p (a :href "/plans/theorem-prover" :class "text-violet-700 underline" "prove.sx") " and types.sx are complementary, not competing:")
|
||||
(p (a :href "/etc/plans/theorem-prover" :class "text-violet-700 underline" "prove.sx") " and types.sx are complementary, not competing:")
|
||||
|
||||
(div :class "overflow-x-auto rounded border border-stone-200 mb-4"
|
||||
(table :class "w-full text-left text-sm"
|
||||
@@ -351,10 +351,10 @@
|
||||
|
||||
(~doc-section :title "Relationships" :id "relationships"
|
||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
||||
(li (a :href "/plans/theorem-prover" :class "text-violet-700 underline" "Theorem Prover") " — prove.sx verifies primitive properties; types.sx verifies composition. Complementary.")
|
||||
(li (a :href "/plans/content-addressed-components" :class "text-violet-700 underline" "Content-Addressed Components") " — component manifests gain type signatures. A consumer knows param types before fetching the source.")
|
||||
(li (a :href "/plans/environment-images" :class "text-violet-700 underline" "Environment Images") " — the type registry serializes into the image. Type checking happens once at image build time, not on every startup.")
|
||||
(li (a :href "/plans/runtime-slicing" :class "text-violet-700 underline" "Runtime Slicing") " — types.sx is a registration-time module, not a runtime module. It doesn't ship to the client. Zero impact on bundle size."))
|
||||
(li (a :href "/etc/plans/theorem-prover" :class "text-violet-700 underline" "Theorem Prover") " — prove.sx verifies primitive properties; types.sx verifies composition. Complementary.")
|
||||
(li (a :href "/etc/plans/content-addressed-components" :class "text-violet-700 underline" "Content-Addressed Components") " — component manifests gain type signatures. A consumer knows param types before fetching the source.")
|
||||
(li (a :href "/etc/plans/environment-images" :class "text-violet-700 underline" "Environment Images") " — the type registry serializes into the image. Type checking happens once at image build time, not on every startup.")
|
||||
(li (a :href "/etc/plans/runtime-slicing" :class "text-violet-700 underline" "Runtime Slicing") " — types.sx is a registration-time module, not a runtime module. It doesn't ship to the client. Zero impact on bundle size."))
|
||||
|
||||
(div :class "rounded border border-amber-200 bg-amber-50 p-3 mt-2"
|
||||
(p :class "text-amber-800 text-sm" (strong "Depends on: ") "primitives.sx (return types exist), boundary.sx (IO return types exist), eval.sx (defcomp parsing). " (strong "New: ") (code "types.sx") " spec module, type annotations in " (code "parse-comp-params") "."))
|
||||
|
||||
Reference in New Issue
Block a user