;; ---------------------------------------------------------------------------
;; Isomorphic Architecture Roadmap
;; ---------------------------------------------------------------------------

(defcomp ()
  (~docs/page :title "Isomorphic Architecture Roadmap"

    (~docs/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 "/sx/(language.(spec.deps))" (~tw :tokens "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."))

    (~docs/section :title "Current State" :id "current-state"
      (ul (~tw :tokens "space-y-2 text-stone-700 list-disc pl-5")
        (li (strong "Primitive parity: ") "100%. ~80 pure primitives, same names/semantics, JS and Python.")
        (li (strong "eval/parse/render: ") "Complete both sides. sx-ref.js has eval, parse, render-to-html, render-to-dom, aser.")
        (li (strong "Engine: ") "engine.sx (morph, swaps, triggers, history), orchestration.sx (fetch, events), boot.sx (hydration) — all transpiled.")
        (li (strong "Wire format: ") "Server _aser → SX source → client parses → renders to DOM. Boundary is clean.")
        (li (strong "Component caching: ") "Hash-based localStorage for component definitions.")
        (li (strong "Boundary enforcement: ") "boundary.sx + SX_BOUNDARY_STRICT=1 validates all primitives/IO/helpers at registration.")
        (li (strong "Dependency analysis: ") "deps.sx computes per-page component bundles — only definitions a page actually uses are sent.")
        (li (strong "IO detection: ") "deps.sx classifies every component as pure or IO-dependent. Server expands IO components, serializes pure ones for client.")
        (li (strong "Client-side routing: ") "router.sx matches URL patterns. Pure pages render instantly without server roundtrips. Pages with :data fall through to server transparently.")
        (li (strong "Client IO proxy: ") "IO primitives registered on the client call back to the server via fetch. Components with IO deps can render client-side.")
        (li (strong "Streaming/suspense: ") "defpage :stream true enables chunked HTML. ~shared:pages/suspense placeholders show loading skeletons; __sxResolve() fills in content as IO completes.")))

    ;; -----------------------------------------------------------------------
    ;; Phase 1
    ;; -----------------------------------------------------------------------

    (~docs/section :title "Phase 1: Component Distribution & Dependency Analysis" :id "phase-1"

      (div (~tw :tokens "rounded border border-green-300 bg-green-50 p-4 mb-4")
        (div (~tw :tokens "flex items-center gap-2 mb-2")
          (span (~tw :tokens "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase") "Complete")
          (a :href "/sx/(language.(spec.deps))" (~tw :tokens "text-green-700 underline text-sm font-medium") "View canonical spec: deps.sx")
          (a :href "/sx/(geography.(isomorphism.bundle-analyzer))" (~tw :tokens "text-green-700 underline text-sm font-medium") "Live bundle analyzer"))
        (p (~tw :tokens "text-green-900 font-medium") "What it enables")
        (p (~tw :tokens "text-green-800") "Per-page component bundles instead of sending every definition to every page. Smaller payloads, faster boot, better cache hit rates."))

      (~docs/subsection :title "The Problem"
        (p "The page boot payload serializes every component definition in the environment. A page that uses 5 components still receives all 50+. No mechanism determines which components a page actually needs — the boundary between \"loaded\" and \"used\" is invisible."))

      (~docs/subsection :title "Implementation"

        (p "The dependency analysis algorithm is defined in "
          (a :href "/sx/(language.(spec.deps))" (~tw :tokens "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 (~tw :tokens "space-y-4")
          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "1. Transitive closure (deps.sx)")
            (p "9 functions that walk the component graph. The core:")
            (~docs/code :src (highlight "(define (transitive-deps name env)\n  (let ((key (if (starts-with? name \"~\") name\n                 (concat \"~\" name)))\n        (seen (set-create)))\n    (transitive-deps-walk key env seen)\n    (set-remove seen key)))" "lisp"))
            (p (code "scan-refs") " walks a component body AST collecting " (code "~") " symbols. "
               (code "transitive-deps") " follows references recursively through the env. "
               (code "compute-all-deps") " batch-computes and caches deps for every component. "
               "Circular references terminate safely via a seen-set."))

          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "2. Page scanning")
            (~docs/code :src (highlight "(define (components-needed page-source env)\n  (let ((direct (scan-components-from-source page-source))\n        (all-needed (set-create)))\n    (for-each (fn (name) ...\n      (set-add! all-needed name)\n      (set-union! all-needed (component-deps comp)))\n      direct)\n    all-needed))" "lisp"))
            (p (code "scan-components-from-source") " finds " (code "(~plans/content-addressed-components/name") " patterns in serialized SX via regex. " (code "components-needed") " combines scanning with the cached transitive closure to produce the minimal component set for a page."))

          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "3. Per-page CSS scoping")
            (p (code "page-css-classes") " unions the CSS classes from only the components in the page bundle. Pages that don't use a component never pay for its styles."))

          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "4. Platform interface")
            (p "The spec declares 6 functions each host implements natively — the only host-specific code:")
            (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
              (li (code "component-deps") " / " (code "component-set-deps!") " — read/write the cached deps set on a component object")
              (li (code "component-css-classes") " — read pre-scanned CSS class names from a component")
              (li (code "env-components") " — enumerate all component entries in an environment")
              (li (code "regex-find-all") " / " (code "scan-css-classes") " — host-native regex and CSS scanning")))))

      (~docs/subsection :title "Spec module"
        (p "deps.sx is loaded as a " (strong "spec module") " — an optional extension to the core spec. The bootstrapper flag " (code "--spec-modules deps") " includes it in the generated output alongside the core evaluator, parser, and renderer. Phase 2 IO detection was added to the same module — same bootstrapping mechanism, no architecture changes needed.")
        (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1 font-mono text-sm")
          (li "shared/sx/ref/deps.sx — canonical spec (14 functions, 8 platform declarations)")
          (li "Bootstrapped to all host targets via --spec-modules deps")))

      (~docs/subsection :title "Verification"
        (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
          (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 "/sx/(geography.(isomorphism.bundle-analyzer))" (~tw :tokens "text-violet-700 underline") "Live bundle analyzer") " shows real per-page savings on this app"))))

    ;; -----------------------------------------------------------------------
    ;; Phase 2
    ;; -----------------------------------------------------------------------

    (~docs/section :title "Phase 2: Smart Server/Client Boundary — IO Detection" :id "phase-2"

      (div (~tw :tokens "rounded border border-green-300 bg-green-50 p-4 mb-4")
        (div (~tw :tokens "flex items-center gap-2 mb-2")
          (span (~tw :tokens "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase") "Complete")
          (a :href "/sx/(language.(spec.deps))" (~tw :tokens "text-green-700 underline text-sm font-medium") "View canonical spec: deps.sx")
          (a :href "/sx/(geography.(isomorphism.bundle-analyzer))" (~tw :tokens "text-green-700 underline text-sm font-medium") "Live bundle analyzer with IO"))
        (p (~tw :tokens "text-green-900 font-medium") "What it enables")
        (p (~tw :tokens "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."))

      (~docs/subsection :title "IO Detection in the Spec"
        (p "Five new functions in "
          (a :href "/sx/(language.(spec.deps))" (~tw :tokens "text-violet-700 underline") "deps.sx")
          " extend the Phase 1 walker to detect IO primitive references:")

        (div (~tw :tokens "space-y-4")
          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "1. IO scanning")
            (p (code "scan-io-refs") " walks an AST node, collecting symbol names that match an IO name set. The IO set is provided by the host from boundary declarations (all three tiers: core IO, deployment IO, page helpers).")
            (~docs/code :src (highlight "(define scan-io-refs\n  (fn (node io-names)\n    (let ((refs (list)))\n      (scan-io-refs-walk node io-names refs)\n      refs)))" "lisp")))

          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "2. Transitive IO closure")
            (p (code "transitive-io-refs") " follows component deps recursively, unioning IO refs from all reachable components and macros. Cycle-safe via seen-set.")
            (~docs/code :src (highlight "(define transitive-io-refs\n  (fn (name env io-names)\n    ;; Walk deps, scan each body for IO refs,\n    ;; union all refs transitively.\n    ...))" "lisp")))

          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "3. Batch computation")
            (p (code "compute-all-io-refs") " iterates the env, computes transitive IO refs for each component, and caches the result via " (code "component-set-io-refs!") ". Called after " (code "compute-all-deps") " at component registration time."))

          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "4. Component metadata")
            (p "Each component now carries " (code "io_refs") " (transitive IO primitive names) alongside " (code "deps") " and " (code "css_classes") ". The derived " (code "is_pure") " property is true when " (code "io_refs") " is empty — the component can render anywhere without server data."))))

      (~docs/subsection :title "Selective Expansion"
        (p "The partial evaluator " (code "_aser") " now uses per-component IO metadata instead of a global toggle:")
        (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
          (li (strong "IO-dependent") " → expand server-side (IO must resolve)")
          (li (strong "Pure") " → serialize for client (let client render)")
          (li (strong "Layout slot context") " → all components still expand (backwards compat via " (code "_expand_components") " context var)"))
        (p "A component calling " (code "(highlight ...)") " or " (code "(query ...)") " is IO-dependent. A component with only HTML tags and string ops is pure."))

      (~docs/subsection :title "Platform interface additions"
        (p "Two new platform functions each host implements:")
        (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1 font-mono text-sm")
          (li "(component-io-refs c) → cached IO ref list")
          (li "(component-set-io-refs! c refs) → cache IO refs on component")))

      (~docs/subsection :title "Verification"
        (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
          (li "Components calling (query ...) or (highlight ...) classified IO-dependent")
          (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 "/sx/(geography.(isomorphism.bundle-analyzer))" (~tw :tokens "text-violet-700 underline") "Live bundle analyzer") " shows per-page IO classification"))))

    ;; -----------------------------------------------------------------------
    ;; Phase 3
    ;; -----------------------------------------------------------------------

    (~docs/section :title "Phase 3: Client-Side Routing (SPA Mode)" :id "phase-3"

      (div (~tw :tokens "rounded border border-green-300 bg-green-50 p-4 mb-4")
        (div (~tw :tokens "flex items-center gap-2 mb-2")
          (span (~tw :tokens "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase") "Complete")
          (a :href "/sx/(language.(spec.router))" (~tw :tokens "text-green-700 underline text-sm font-medium") "View canonical spec: router.sx")
          (a :href "/sx/(geography.(isomorphism.routing-analyzer))" (~tw :tokens "text-green-700 underline text-sm font-medium") "Live routing analyzer"))
        (p (~tw :tokens "text-green-900 font-medium") "What it enables")
        (p (~tw :tokens "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."))

      (~docs/subsection :title "Architecture"
        (p "Three-layer approach: spec defines pure route matching, page registry bridges server metadata to client, orchestration intercepts navigation for try-first/fallback.")

        (div (~tw :tokens "space-y-4")
          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "1. Route matching spec (router.sx)")
            (p "New spec module with pure functions for Flask-style route pattern matching:")
            (~docs/code :src (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 (~tw :tokens "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.")
            (~docs/code :src (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
            (h4 (~tw :tokens "font-semibold text-stone-700") "3. Client-side interception (orchestration.sx)")
            (p (code "bind-client-route-link") " replaces " (code "bind-boost-link") " in boost processing. On click:")
            (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
              (li "Extract pathname from href")
              (li "Call " (code "find-matching-route") " against " (code "_page-routes"))
              (li "If match found AND no :data: evaluate content expression locally with component env + URL params")
              (li "If evaluation succeeds: swap into #main-panel, pushState, log " (code "\"sx:route client /path\""))
              (li "If anything fails (no match, has data, eval error): transparent fallback to server fetch"))
            (p (code "handle-popstate") " also tries client routing before server fetch on back/forward."))))

      (~docs/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 (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
          (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 (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
          (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 "/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 "/sx/(geography.(isomorphism.data-test))" (~tw :tokens "text-violet-700 underline") "Phase 4 demo") ")")))

      (~docs/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.")
        (p "Console messages provide visibility: " (code "sx:route client /essays/why-sexps") " vs " (code "sx:route server /specs/eval") "."))

      (~docs/subsection :title "Files"
        (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1 font-mono text-sm")
          (li "shared/sx/ref/router.sx — route pattern matching spec")
          (li "shared/sx/ref/boot.sx — process page registry scripts")
          (li "shared/sx/ref/orchestration.sx — client route interception")
          (li "shared/sx/ref/bootstrap_js.py — router spec module + platform functions")
          (li "shared/sx/ref/bootstrap_py.py — router spec module (parity)")
          (li "shared/sx/helpers.py — page registry SX serialization")))

      (~docs/subsection :title "Verification"
        (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
          (li "Pure page navigation: zero server requests, console shows \"sx:route client\"")
          (li "IO/data page fallback: falls through to server fetch transparently")
          (li "Browser back/forward works with client-routed pages")
          (li "Disabling page registry → identical behavior to before")
          (li "Bootstrap parity: sx_ref.py and sx-ref.js both contain router functions"))))

    ;; -----------------------------------------------------------------------
    ;; Phase 4
    ;; -----------------------------------------------------------------------

    (~docs/section :title "Phase 4: Client Async & IO Bridge" :id "phase-4"

      (div (~tw :tokens "rounded border border-green-300 bg-green-50 p-4 mb-4")
        (div (~tw :tokens "flex items-center gap-2 mb-2")
          (span (~tw :tokens "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase") "Complete")
          (a :href "/sx/(geography.(isomorphism.data-test))" (~tw :tokens "text-green-700 underline text-sm font-medium") "Live data test page"))
        (p (~tw :tokens "text-green-900 font-medium") "What it enables")
        (p (~tw :tokens "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."))

      (~docs/subsection :title "Architecture"
        (p "Separates IO from rendering. Server evaluates :data expression (async, with DB/service access), serializes result as SX wire format. Client fetches pre-evaluated data, parses it, merges into env, renders pure :content client-side.")

        (div (~tw :tokens "space-y-4")
          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "1. Abstract resolve-page-data")
            (p "Spec-level primitive in orchestration.sx. The spec says \"I need data for this page\" — platform provides transport:")
            (~docs/code :src (highlight "(resolve-page-data page-name params\n  (fn (data)\n    ;; data is a dict — merge into env and render\n    (let ((env (merge closure params data))\n          (rendered (try-eval-content content-src env)))\n      (swap-rendered-content target rendered pathname))))" "lisp"))
            (p "Browser platform: HTTP fetch to " (code "/sx/data/<page-name>") ". Future platforms could use IPC, cache, WebSocket, etc."))

          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "2. Server data endpoint")
            (p (code "evaluate_page_data()") " evaluates the :data expression, kebab-cases dict keys (Python " (code "total_count") " → SX " (code "total-count") "), serializes as SX wire format.")
            (p "Response content type: " (code "text/sx; charset=utf-8") ". Per-page auth enforcement via " (code "_check_page_auth()") "."))

          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "3. Client data cache")
            (p "In-memory cache in orchestration.sx, keyed by " (code "page-name:param=value") ". 30-second TTL prevents redundant fetches on back/forward navigation:")
            (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
              (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 "/sx/(geography.(isomorphism.data-test))" (~tw :tokens "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)."))))

      (~docs/subsection :title "Files"
        (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1 font-mono text-sm")
          (li "shared/sx/ref/orchestration.sx — resolve-page-data spec, data cache")
          (li "shared/sx/ref/bootstrap_js.py — platform resolvePageData (HTTP fetch)")
          (li "shared/sx/pages.py — evaluate_page_data(), auto_mount_page_data()")
          (li "shared/sx/helpers.py — deps for :data pages in page registry")
          (li "sx/sx/data-test.sx — test component")
          (li "shared/sx/tests/test_page_data.py — 30 unit tests")))

      (~docs/subsection :title "Verification"
        (ul (~tw :tokens "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 "/sx/(geography.(isomorphism.data-test))" (~tw :tokens "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"))))

    ;; -----------------------------------------------------------------------
    ;; Phase 5
    ;; -----------------------------------------------------------------------

    (~docs/section :title "Phase 5: Client IO Proxy" :id "phase-5"

      (div (~tw :tokens "rounded border border-green-300 bg-green-50 p-4 mb-4")
        (div (~tw :tokens "flex items-center gap-2 mb-2")
          (span (~tw :tokens "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase") "Complete"))
        (p (~tw :tokens "text-green-900 font-medium") "What it enables")
        (p (~tw :tokens "text-green-800") "Components with IO dependencies render client-side. IO primitives are proxied to the server — the client evaluator calls them like normal functions, the proxy fetches results via HTTP, the async DOM renderer awaits the promises and continues."))

      (~docs/subsection :title "How it works"
        (p "Instead of async-aware continuations (originally planned), Phase 5 was solved by combining three mechanisms that emerged from Phases 3-4:")

        (div (~tw :tokens "space-y-4")
          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "1. IO dependency detection (from Phase 2)")
            (p "The component dep analyzer scans AST bodies for IO primitive names (highlight, asset-url, query, frag, etc.) and computes transitive IO refs. Pages include their IO dep list in the page registry."))

          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "2. IO proxy registration")
            (p (code "registerIoDeps(names)") " in orchestration.sx registers proxy functions for each IO primitive. When the client evaluator encounters " (code "(highlight code \"sx\")") ", the proxy sends an HTTP request to the server's IO endpoint and returns a Promise."))

          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "3. Async DOM renderer")
            (p (code "asyncRenderToDom") " walks the expression tree and handles Promises transparently. When a subexpression returns a Promise (from an IO proxy call), the renderer awaits it and continues building the DOM tree. No continuations needed — JavaScript's native Promise mechanism provides the suspension."))))

      (~docs/subsection :title "Why continuations weren't needed"
        (p "The original Phase 5 plan called for async-aware shift/reset or a CPS transform of the evaluator. In practice, JavaScript's Promise mechanism provided the same capability: the async DOM renderer naturally suspends when it encounters a Promise and resumes when it resolves.")
        (p "Delimited continuations remain valuable for Phase 6 (streaming/suspense on the " (em "server") " side, where Python doesn't have native Promise-based suspension in the evaluator). But for client-side IO, Promises + async render were sufficient."))

      (~docs/subsection :title "Files"
        (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1 font-mono text-sm")
          (li "shared/sx/ref/orchestration.sx — registerIoDeps, IO proxy registration")
          (li "shared/sx/ref/bootstrap_js.py — asyncRenderToDom, IO proxy HTTP transport")
          (li "shared/sx/helpers.py — io_deps in page registry entries")
          (li "shared/sx/deps.py — transitive IO ref computation")))

      (~docs/subsection :title "Verification"
        (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
          (li "Navigate to any page with IO deps (e.g. /testing/eval) — console shows IO proxy calls")
          (li "Components using " (code "highlight") " render correctly via proxy")
          (li "Pages with " (code "asset-url") " resolve script paths via proxy")
          (li "Async render completes without blocking — partial results appear as promises resolve"))))

    ;; -----------------------------------------------------------------------
    ;; Phase 6
    ;; -----------------------------------------------------------------------

    (~docs/section :title "Phase 6: Streaming & Suspense" :id "phase-6"

      (div (~tw :tokens "rounded border border-green-300 bg-green-50 p-4 mb-4")
        (div (~tw :tokens "flex items-center gap-2 mb-2")
          (span (~tw :tokens "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase") "Complete")
          (a :href "/sx/(geography.(isomorphism.streaming))" (~tw :tokens "text-green-700 underline text-sm font-medium") "Live streaming demo"))
        (p (~tw :tokens "text-green-900 font-medium") "What it enables")
        (p (~tw :tokens "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."))

      (~docs/subsection :title "What was built"
        (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
          (li (code "~shared:pages/suspense") " component — renders fallback content with a stable DOM ID, replaced when resolution arrives")
          (li (code "defpage :stream true") " — opts a page into streaming response mode")
          (li (code "defpage :fallback expr") " — custom loading skeleton for streaming pages")
          (li (code "execute_page_streaming()") " — Quart async generator response that yields HTML chunks")
          (li (code "sx_page_streaming_parts()") " — splits the HTML shell into streamable parts")
          (li (code "Sx.resolveSuspense(id, sx)") " — client-side function to replace suspense placeholders")
          (li (code "window.__sxResolve") " bootstrap — queues resolutions that arrive before sx.js loads")
          (li "Concurrent IO: data eval + header eval run in parallel via " (code "asyncio.create_task"))
          (li "Completion-order streaming: whichever IO finishes first gets sent first via " (code "asyncio.wait(FIRST_COMPLETED)"))))

      (~docs/subsection :title "Architecture"

        (div (~tw :tokens "space-y-4")
          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "1. Suspense component")
            (p "When streaming, the server renders the page with " (code "~shared:pages/suspense") " placeholders instead of awaiting IO:")
            (~docs/code :src (highlight "(~app-body\n  :header-rows (~shared:pages/suspense :id \"stream-headers\"\n    :fallback (div :class \"h-12 bg-stone-200 animate-pulse\"))\n  :content (~shared:pages/suspense :id \"stream-content\"\n    :fallback (div :class \"p-8 animate-pulse\" ...)))" "lisp")))

          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "2. Chunked transfer")
            (p "Quart async generator response yields chunks in order:")
            (ol (~tw :tokens "list-decimal pl-5 text-stone-700 space-y-1")
              (li "HTML shell + CSS + component defs + page registry + suspense page SX + scripts (immediate)")
              (li "Resolution " (code "<script>") " tags as each IO completes")
              (li "Closing " (code "</body></html>"))))

          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "3. Client resolution")
            (p "Each resolution chunk is an inline script:")
            (~docs/code :src (highlight "<script>\n  window.__sxResolve(\"stream-content\",\n    \"(~article :title \\\"Hello\\\")\")\n</script>" "html"))
            (p "The client parses the SX, renders to DOM, and replaces the suspense placeholder's children."))

          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "4. Concurrent IO")
            (p "Data evaluation and header construction run in parallel. " (code "asyncio.wait(FIRST_COMPLETED)") " yields resolution chunks in whatever order IO completes — no artificial sequencing."))))

      (~docs/subsection :title "Continuation foundation"
        (p "Delimited continuations (" (code "reset") "/" (code "shift") ") are implemented in the Python evaluator (async_eval.py lines 586-624) and available as special forms. Phase 6 uses the simpler pattern of concurrent IO + completion-order streaming, but the continuation machinery is in place for Phase 7's more sophisticated evaluation-level suspension."))

      (~docs/subsection :title "Files"
        (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1 font-mono text-sm")
          (li "shared/sx/templates/pages.sx — ~shared:pages/suspense component definition")
          (li "shared/sx/types.py — PageDef.stream, PageDef.fallback_expr fields")
          (li "shared/sx/evaluator.py — defpage :stream/:fallback parsing")
          (li "shared/sx/pages.py — execute_page_streaming(), streaming route mounting")
          (li "shared/sx/helpers.py — sx_page_streaming_parts(), sx_streaming_resolve_script()")
          (li "shared/sx/ref/boot.sx — resolve-suspense spec (canonical)")
          (li "shared/sx/ref/bootstrap_js.py — resolveSuspense on Sx object, __sxPending/Resolve init")
          (li "shared/static/scripts/sx-browser.js — bootstrapped output (DO NOT EDIT)")
          (li "shared/sx/async_eval.py — reset/shift special forms (continuation foundation)")
          (li "sx/sx/streaming-demo.sx — demo content component")
          (li "sx/sxc/pages/docs.sx — streaming-demo defpage")
          (li "sx/sxc/pages/helpers.py — streaming-demo-data page helper")))

      (~docs/subsection :title "Demonstration"
        (p "The " (a :href "/sx/(geography.(isomorphism.streaming))" (~tw :tokens "text-violet-700 underline") "streaming demo page") " exercises the full pipeline:")
        (ol (~tw :tokens "list-decimal pl-5 text-stone-700 space-y-1")
          (li "Navigate to " (a :href "/sx/(geography.(isomorphism.streaming))" (~tw :tokens "text-violet-700 underline") "/sx/(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")
          (li "The document response shows multiple chunks arriving over time: shell first, then resolution scripts")))

      (~docs/subsection :title "What to verify"
        (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
          (li (strong "Instant shell: ") "The page HTML arrives immediately — no waiting for the 1.5s data fetch")
          (li (strong "Suspense placeholders: ") "The " (code "~shared:pages/suspense") " component renders a " (code "data-suspense") " wrapper with animated fallback content")
          (li (strong "Resolution: ") "The " (code "__sxResolve()") " inline script replaces the placeholder with real rendered content")
          (li (strong "Chunked encoding: ") "Network tab shows the document as a chunked response with multiple frames")
          (li (strong "Concurrent IO: ") "Header and content resolve independently — whichever finishes first appears first")
          (li (strong "HTMX fallback: ") "SX/HTMX requests bypass streaming and receive a standard response"))))

    ;; -----------------------------------------------------------------------
    ;; Phase 7
    ;; -----------------------------------------------------------------------

    (~docs/section :title "Phase 7: Full Isomorphism" :id "phase-7"

      (div (~tw :tokens "rounded border border-green-200 bg-green-50 p-4 mb-4")
        (div (~tw :tokens "flex items-center gap-2 mb-2")
          (span (~tw :tokens "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase") "Complete"))
        (p (~tw :tokens "text-green-900 font-medium") "What it enables")
        (p (~tw :tokens "text-green-800") "Same SX code runs on either side. Runtime chooses optimal split via affinity annotations and render plans. Client data cache managed via invalidation headers and server-driven updates. Cross-host isomorphism verified by 61 automated tests."))

      (~docs/subsection :title "7a. Affinity Annotations & Render Target"

        (div (~tw :tokens "rounded border border-green-300 bg-green-50 p-3 mb-4")
          (div (~tw :tokens "flex items-center gap-2 mb-1")
            (span (~tw :tokens "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase") "Complete"))
          (p (~tw :tokens "text-green-800 text-sm") "Components declare where they prefer to render. The spec combines affinity with IO analysis to produce a per-component render target decision."))

        (p "Affinity annotations let component authors express rendering preferences:")
        (~docs/code :src (highlight "(defcomp ~plans/isomorphic/product-grid (&key products)\n  :affinity :client  ;; interactive, prefer client rendering\n  (div ...))\n\n(defcomp ~plans/isomorphic/auth-menu (&key user)\n  :affinity :server  ;; auth-sensitive, always server\n  (div ...))\n\n(defcomp ~plans/isomorphic/card (&key title)\n  ;; no annotation = :affinity :auto (default)\n  ;; runtime decides from IO analysis\n  (div ...))" "lisp"))

        (p "The " (code "render-target") " function in deps.sx combines affinity with IO analysis:")
        (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
          (li (code ":affinity :server") " → always " (code "\"server\"") " (auth-sensitive, secrets, heavy IO)")
          (li (code ":affinity :client") " → always " (code "\"client\"") " (interactive, IO proxied)")
          (li (code ":affinity :auto") " (default) → " (code "\"server\"") " if IO-dependent, " (code "\"client\"") " if pure"))

        (p "The server's partial evaluator (" (code "_aser") ") uses " (code "render_target") " instead of the previous " (code "is_pure") " check. Components with " (code ":affinity :client") " are serialized for client rendering even if they call IO primitives — the IO proxy (Phase 5) handles the calls.")

        (~docs/subsection :title "Files"
          (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1 font-mono text-sm")
            (li "shared/sx/ref/eval.sx — defcomp annotation parsing, defcomp-kwarg helper")
            (li "shared/sx/ref/deps.sx — render-target function, platform interface")
            (li "shared/sx/types.py — Component.affinity field, render_target property")
            (li "shared/sx/evaluator.py — _sf_defcomp annotation extraction")
            (li "shared/sx/async_eval.py — _aser uses render_target")
            (li "shared/sx/ref/bootstrap_js.py — Component.affinity, componentAffinity()")
            (li "shared/sx/ref/bootstrap_py.py — component_affinity(), make_component()")
            (li "shared/sx/ref/test-eval.sx — 4 new defcomp affinity tests")
            (li "shared/sx/ref/test-deps.sx — 6 new render-target tests")))

        (~docs/subsection :title "Verification"
          (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
            (li "269 spec tests pass (10 new: 4 eval + 6 deps)")
            (li "79 Python unit tests pass")
            (li "Bootstrapped to both hosts (sx_ref.py + sx-browser.js)")
            (li "Backward compatible: existing defcomp without :affinity defaults to \"auto\""))))

      (~docs/subsection :title "7b. Runtime Boundary Optimizer"

        (div (~tw :tokens "rounded border border-green-300 bg-green-50 p-3 mb-4")
          (div (~tw :tokens "flex items-center gap-2 mb-1")
            (span (~tw :tokens "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase") "Complete"))
          (p (~tw :tokens "text-green-800 text-sm") "Per-page render plans computed at registration time. Each page knows exactly which components render server-side vs client-side, cached on PageDef."))

        (p "Given component tree + IO dependency graph + affinity annotations, decide per-component: server-expand, client-render, or stream. Planning step cached at registration, recomputed on component change.")

        (p (code "page-render-plan") " in deps.sx computes per-page boundary decisions:")
        (~docs/code :src (highlight "(page-render-plan page-source env io-names)\n;; Returns:\n;; {:components {~plans/content-addressed-components/name \"server\"|\"client\" ...}\n;;  :server     (list of server-expanded names)\n;;  :client     (list of client-rendered names)\n;;  :io-deps    (IO primitives needed by server components)}" "lisp"))

        (~docs/subsection :title "Integration Points"
          (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
            (li (code "shared/sx/ref/deps.sx") " — " (code "page-render-plan") " spec function")
            (li (code "shared/sx/deps.py") " — Python wrapper, dispatches to bootstrapped code")
            (li (code "shared/sx/pages.py") " — " (code "compute_page_render_plans()") " called at mount time, caches on PageDef")
            (li (code "shared/sx/helpers.py") " — " (code "_build_pages_sx()") " includes " (code ":render-plan") " in client page registry")
            (li (code "shared/sx/types.py") " — " (code "PageDef.render_plan") " field")))

        (~docs/subsection :title "Verification"
          (ul (~tw :tokens "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 "/sx/(geography.(isomorphism.affinity))" "affinity demo page"))
            (li "Client page registry includes :render-plan for each page"))))

      (~docs/subsection :title "7c. Cache Invalidation & Optimistic Data Updates"

        (div (~tw :tokens "rounded border border-green-300 bg-green-50 p-3 mb-4")
          (div (~tw :tokens "flex items-center gap-2 mb-1")
            (span (~tw :tokens "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase") "Complete"))
          (p (~tw :tokens "text-green-800 text-sm") "Client data cache management, optimistic predicted mutations with snapshot rollback, and server-driven cache updates."))

        (p "The client-side page data cache (30-second TTL) now supports cache invalidation, server-driven updates, and optimistic mutations. The client predicts the result of a mutation, immediately re-renders with the predicted data, and confirms or reverts when the server responds.")

        (~docs/subsection :title "Cache Invalidation"
          (p "Component authors can declare cache invalidation on elements that trigger mutations:")
          (~docs/code :src (highlight ";; Clear specific page's cache after successful action\n(form :sx-post \"/cart/remove\"\n      :sx-cache-invalidate \"cart-page\"\n  ...)\n\n;; Clear ALL page caches after action\n(button :sx-post \"/admin/reset\"\n        :sx-cache-invalidate \"*\")" "lisp"))
          (p "The server can also control client cache via response headers:")
          (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
            (li (code "SX-Cache-Invalidate: page-name") " — clear cache for a page")
            (li (code "SX-Cache-Update: page-name") " — replace cache with the response body (SX-format data)")))

        (~docs/subsection :title "Optimistic Mutations"
          (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
            (li (strong "optimistic-cache-update") " — applies a mutator function to cached data, saves a snapshot for rollback")
            (li (strong "optimistic-cache-revert") " — restores the pre-mutation snapshot if the server rejects")
            (li (strong "optimistic-cache-confirm") " — discards the snapshot after server confirmation")
            (li (strong "submit-mutation") " — orchestration function: predict, submit, confirm/revert")
            (li (strong "/sx/action/<name>") " — server endpoint for processing mutations (POST, returns SX wire format)")))

        (~docs/subsection :title "Files"
          (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1 font-mono text-sm")
            (li "shared/sx/ref/orchestration.sx — cache management + optimistic cache functions + submit-mutation spec")
            (li "shared/sx/ref/engine.sx — SX-Cache-Invalidate, SX-Cache-Update response headers")
            (li "shared/sx/pages.py — mount_action_endpoint for /sx/action/<name>")
            (li "sx/sx/optimistic-demo.sx — live demo component")))

        (~docs/subsection :title "Verification"
          (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
            (li "Live demo at " (a :href "/sx/(geography.(isomorphism.optimistic))" (~tw :tokens "text-violet-600 hover:underline") "/sx/(geography.(isomorphism.optimistic))"))
            (li "Console log: " (code "sx:optimistic confirmed") " / " (code "sx:optimistic reverted")))))

      (~docs/subsection :title "7d. Offline Data Layer"

        (div (~tw :tokens "rounded border border-green-300 bg-green-50 p-3 mb-4")
          (div (~tw :tokens "flex items-center gap-2 mb-1")
            (span (~tw :tokens "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase") "Complete"))
          (p (~tw :tokens "text-green-800 text-sm") "Service Worker with IndexedDB caching, connectivity tracking, and offline mutation queue with replay on reconnect."))

        (p "A Service Worker registered at " (code "/sx-sw.js") " provides three-tier caching, plus an offline mutation queue that builds on Phase 7c's optimistic updates:")
        (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
          (li (strong "/sx/data/* ") "— network-first with IndexedDB fallback. Page data cached on fetch, served from IndexedDB when offline.")
          (li (strong "/sx/io/* ") "— network-first with IndexedDB fallback. IO proxy responses cached the same way.")
          (li (strong "/static/* ") "— stale-while-revalidate via Cache API. Serves cached assets immediately, updates in background.")
          (li (strong "Offline mutations") " — " (code "offline-aware-mutation") " routes to " (code "submit-mutation") " when online, " (code "offline-queue-mutation") " when offline. " (code "offline-sync") " replays the queue on reconnect."))

        (~docs/subsection :title "How It Works"
          (ol (~tw :tokens "list-decimal list-inside text-stone-700 space-y-2")
            (li "On boot, " (code "sx-browser.js") " registers the SW at " (code "/sx-sw.js") " (root scope)")
            (li "SW intercepts fetch events and routes by URL pattern")
            (li "For data/IO: try network first, on failure serve from IndexedDB")
            (li "For static assets: serve from Cache API, revalidate in background")
            (li "Cache invalidation propagates: element attr / response header → in-memory cache → SW message → IndexedDB")
            (li "Offline mutations queue locally, replay on reconnect via " (code "offline-sync"))))

        (~docs/subsection :title "Files"
          (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1 font-mono text-sm")
            (li "shared/static/scripts/sx-sw.js — Service Worker (network-first + stale-while-revalidate)")
            (li "shared/sx/ref/orchestration.sx — offline queue, sync, connectivity tracking, sw-post-message")
            (li "shared/sx/pages.py — mount_service_worker() serves SW at /sx-sw.js")
            (li "sx/sx/offline-demo.sx — live demo component")))

        (~docs/subsection :title "Verification"
          (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
            (li "Live demo at " (a :href "/sx/(geography.(isomorphism.offline))" (~tw :tokens "text-violet-600 hover:underline") "/sx/(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")))))

      (~docs/subsection :title "7e. Isomorphic Testing"

        (div (~tw :tokens "rounded border border-green-300 bg-green-50 p-3 mb-4")
          (div (~tw :tokens "flex items-center gap-2 mb-1")
            (span (~tw :tokens "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase") "Complete"))
          (p (~tw :tokens "text-green-800 text-sm") "Cross-host test suite: same SX expressions evaluated on Python (sx_ref.py) and JS (sx-browser.js via Node.js), HTML output compared."))

        (p "61 isomorphic tests verify that Python and JS produce identical results:")
        (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
          (li "37 eval tests: arithmetic, comparison, strings, collections, logic, let/lambda, higher-order, dict, keywords, cond/case")
          (li "24 render tests: elements, attributes, nesting, void elements, boolean attrs, conditionals, map, components, affinity, HTML escaping"))

        (~docs/subsection :title "Files"
          (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1 font-mono text-sm")
            (li "shared/sx/tests/test_isomorphic.py — cross-host test suite")
            (li "Run: " (code "python3 -m pytest shared/sx/tests/test_isomorphic.py -q")))))

      (~docs/subsection :title "7f. Universal Page Descriptor"

        (div (~tw :tokens "rounded border border-green-300 bg-green-50 p-3 mb-4")
          (div (~tw :tokens "flex items-center gap-2 mb-1")
            (span (~tw :tokens "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase") "Complete"))
          (p (~tw :tokens "text-green-800 text-sm") "defpage is portable: same descriptor executes on server (execute_page) and client (tryClientRoute)."))

        (p "The defpage descriptor is universal — the same definition works on both hosts:")
        (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
          (li (strong "Server: ") (code "execute_page()") " evaluates :data and :content slots, expands server components via " (code "_aser") ", returns SX wire format")
          (li (strong "Client: ") (code "try-client-route") " matches route, evaluates content SX, renders to DOM. Data pages fetch via " (code "/sx/data/") ", IO proxied via " (code "/sx/io/"))
          (li (strong "Render plan: ") "each page's " (code ":render-plan") " is included in the client page registry, showing which components render where")
          (li (strong "Console visibility: ") "client logs " (code "sx:route plan pagename — N server, M client") " on each navigation")))

      (div (~tw :tokens "rounded border border-amber-200 bg-amber-50 p-3 mt-2")
        (p (~tw :tokens "text-amber-800 text-sm") (strong "Depends on: ") "All previous phases.")))

    ;; -----------------------------------------------------------------------
    ;; Cross-Cutting Concerns
    ;; -----------------------------------------------------------------------

    (~docs/section :title "Cross-Cutting Concerns" :id "cross-cutting"

      (~docs/subsection :title "Error Reporting"
        (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
          (li "Phase 1: \"Unknown component\" includes which page expected it and what bundle was sent")
          (li "Phase 2: Server logs which components expanded server-side vs sent to client")
          (li "Phase 3: Client route failures include unmatched path and available routes")
          (li "Phase 4: Client data errors include page name, params, server response status")
          (li "Source location tracking in parser → propagate through eval → include in error messages")))

      (~docs/subsection :title "Backward Compatibility"
        (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
          (li "Pages without annotations behave as today")
          (li "SX-Request / SX-Components / SX-Css header protocol continues")
          (li "Existing .sx files require no changes")
          (li "_expand_components continues as override")
          (li "Each phase is opt-in: disable → identical to previous behavior")))

      (~docs/subsection :title "Spec Integrity"
        (p "All new behavior specified in .sx files under shared/sx/ref/ before implementation. Bootstrappers transpile from spec. This ensures JS and Python stay in sync.")))

    ;; -----------------------------------------------------------------------
    ;; Critical Files
    ;; -----------------------------------------------------------------------

    (~docs/section :title "Critical Files" :id "critical-files"
      (div (~tw :tokens "overflow-x-auto rounded border border-stone-200")
        (table (~tw :tokens "w-full text-left text-sm")
          (thead (tr (~tw :tokens "border-b border-stone-200 bg-stone-100")
            (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "File")
            (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Role")
            (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Phases")))
          (tbody
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/async_eval.py")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Core evaluator, _aser, server/client boundary")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "2, 5"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/helpers.py")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "sx_page(), sx_response(), output pipeline")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "1, 3"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/jinja_bridge.py")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "_COMPONENT_ENV, component registry")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "1, 2"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/pages.py")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "defpage, execute_page(), page lifecycle")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "2, 3"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/ref/boot.sx")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Client boot, component caching")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "1, 3, 4"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/ref/orchestration.sx")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Client fetch/swap/morph")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "3, 4"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/ref/eval.sx")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Evaluator spec")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "4"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/ref/engine.sx")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Morph, swaps, triggers")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "3"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/deps.py")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Dependency analysis (new)")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "1, 2"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/ref/router.sx")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Client-side routing (new)")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "3"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/ref/io-bridge.sx")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Client IO primitives (new)")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "4"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/ref/suspense.sx")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Streaming/suspension (new)")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "5"))))))))

;; ---------------------------------------------------------------------------
;; SX CI Pipeline
;; ---------------------------------------------------------------------------
