;; sx-web: Federated Component Web
;; Browser-native peer network with content-addressed SX components.
;; Builds on sx-pub (phases 1-4 complete: publishing, federation, anchoring, IPFS).

(defcomp ()
  (~docs/page :title "sx-web: Federated Component Web"

    (p (~tw :tokens "text-stone-500 text-sm italic mb-8")
      "A two-tier peer network where server nodes pin to IPFS and browser tabs "
      "participate via WebTransport. Components, tests, and content are all "
      "content-addressed SX expressions verified by CID regardless of source. "
      "Builds on sx-pub phases 1–4 (publishing, federation, anchoring, IPFS).")

    ;; =====================================================================
    ;; Architecture
    ;; =====================================================================

    (~docs/section :title "Architecture" :id "architecture"

      (p "Two tiers, one logical network. Trust anchored on CIDs, not sources.")

      (table (~tw :tokens "w-full mb-6 text-sm")
        (thead
          (tr (th (~tw :tokens "text-left p-2") "") (th (~tw :tokens "text-left p-2") "Server Node") (th (~tw :tokens "text-left p-2") "Browser Node")))
        (tbody
          (tr (td (~tw :tokens "p-2 font-medium") "Runtime") (td (~tw :tokens "p-2") "OCaml + Python") (td (~tw :tokens "p-2") "JavaScript (sx-browser.js)"))
          (tr (td (~tw :tokens "p-2 font-medium") "Storage") (td (~tw :tokens "p-2") "IPFS daemon + PostgreSQL") (td (~tw :tokens "p-2") "IndexedDB + Cache API"))
          (tr (td (~tw :tokens "p-2 font-medium") "IPFS") (td (~tw :tokens "p-2") "Full DHT peer, pins content") (td (~tw :tokens "p-2") "Verifies CIDs, caches locally"))
          (tr (td (~tw :tokens "p-2 font-medium") "Transport") (td (~tw :tokens "p-2") "WebSocket + HTTP federation") (td (~tw :tokens "p-2") "WebSocket to home + WebRTC to peers"))
          (tr (td (~tw :tokens "p-2 font-medium") "Availability") (td (~tw :tokens "p-2") "Long-lived, always on") (td (~tw :tokens "p-2") "Ephemeral, online when open"))
          (tr (td (~tw :tokens "p-2 font-medium") "Trust") (td (~tw :tokens "p-2") "CID verification + blockchain anchor") (td (~tw :tokens "p-2") "CID verification (same guarantee)"))))

      (p "A component fetched from IPFS, from a server, from another browser tab, "
         "or from local IndexedDB cache is verified the same way: hash it, check the CID. "
         "The transport is irrelevant to the trust model."))

    ;; =====================================================================
    ;; Phase 1: Browser Node Foundation
    ;; =====================================================================

    (~docs/section :title "Phase 1: Browser Node Foundation" :id "phase-1"

      (p (~tw :tokens "text-stone-600 italic mb-4")
        "Make the browser a first-class participant, not just a viewer.")

      (~docs/subsection :title "1a. Content Store (IndexedDB)"
        (p "Browser-local content-addressed store. Same interface as server-side "
           (code "content-put") "/" (code "content-get") " but backed by IndexedDB.")

        (~docs/code :src (highlight
          ";; Browser content store — pure SX, platform provides IndexedDB ops\n(define browser-store (make-content-store\n  {:backend :indexeddb\n   :db-name \"sx-web\"\n   :verify true}))     ;; always verify CID on read\n\n;; Same API as server store\n(content-put browser-store sx-source)   ;; → CID\n(content-get browser-store cid)         ;; → SX source | nil\n(content-has? browser-store cid)        ;; → bool\n(content-pin browser-store cid)         ;; mark as persistent\n(content-unpin browser-store cid)       ;; allow eviction"
          "lisp"))

        (p "Two IndexedDB object stores:")
        (ul (~tw :tokens "list-disc pl-6 mb-4 space-y-1")
          (li (strong "blobs") " — CID → SX source bytes (content-addressed, immutable)")
          (li (strong "pins") " — CID → {pinned, last-accessed, size, source-node} (eviction metadata)"))

        (p "Unpinned content evicted LRU when storage exceeds quota. "
           "Pinned content survives until explicitly unpinned."))

      (~docs/subsection :title "1b. Service Worker"
        (p "Intercepts fetch requests to resolve CIDs locally before hitting the network.")

        (~docs/code :src (highlight
          ";; Service Worker routes\n;;\n;; /sx-web/cid/<cid>  → check IndexedDB first, then fetch from home node\n;; /sx-web/resolve/<path> → resolve path→CID via local index, then fetch\n;; /sx-web/eval/<cid>  → fetch + evaluate, return rendered result\n;;\n;; Cache strategy:\n;;   CID routes are immutable — cache forever once verified\n;;   Path routes check freshness against home node"
          "text"))

        (p "The Service Worker makes the browser work offline for all pinned components. "
           "Online, it deduplicates fetches across tabs — one IndexedDB, shared cache."))

      (~docs/subsection :title "1c. CID Verification in JavaScript"
        (p "SHA3-256 hashing in the browser for CID verification. "
           "Uses SubtleCrypto API (hardware-accelerated on modern browsers).")

        (~docs/code :src (highlight
          ";; New platform primitive for browser host\n(define content-hash\n  (fn (source)\n    (host-call (host-get (host-global \"crypto\") \"subtle\")\n      \"digest\" \"SHA-256\" (encode-utf8 source))))\n\n;; Verification: hash content, compare to claimed CID\n(define verify-cid\n  (fn (cid source)\n    (= cid (content-hash source))))"
          "lisp"))

        (p "One new platform primitive: " (code "content-hash") ". "
           "Everything else (verification, pinning, eviction) is pure SX."))

      (~docs/subsection :title "1d. Deliverables"
        (ul (~tw :tokens "list-disc pl-6 mb-4 space-y-1")
          (li (code "web/lib/content-store.sx") " — browser content store (~120 LOC)")
          (li (code "web/lib/sw-routes.sx") " — Service Worker CID routing (~80 LOC)")
          (li "Platform primitive: " (code "content-hash") " in JS host (~15 LOC)")
          (li "IndexedDB schema: blobs + pins stores"))))

    ;; =====================================================================
    ;; Phase 2: WebTransport Peer Layer
    ;; =====================================================================

    (~docs/section :title "Phase 2: Peer Network" :id "phase-2"

      (p (~tw :tokens "text-stone-600 italic mb-4")
        "Three transports, one SX protocol. Browser↔server via WebSocket, "
        "browser↔browser via WebRTC data channels, server↔server via federation. "
        "The protocol layer doesn't know or care which pipe carries it.")

      (~docs/subsection :title "2a. SX Protocol Definition"
        (p "The protocol is an SX component. Nodes fetch, evaluate, and speak it.")

        (~docs/code :src (highlight
          "(defprotocol sx-sync \"0.1\"\n  :messages {\n    ;; Content exchange\n    :have     (fn (cids) ...)        ;; I have these CIDs\n    :want     (fn (cids) ...)        ;; I need these CIDs\n    :provide  (fn (cid source) ...)  ;; Here's the content\n    :reject   (fn (cid reason) ...)  ;; Can't provide\n\n    ;; Discovery\n    :announce (fn (cid metadata) ...)  ;; New content published\n    :query   (fn (pattern) ...)       ;; Search for components\n    :results (fn (matches) ...)       ;; Search results\n\n    ;; Identity\n    :hello    (fn (node-id pubkey) ...)   ;; Handshake\n    :verify   (fn (challenge sig) ...)    ;; Prove identity\n  }\n\n  :flow {\n    ;; Content resolution\n    :want → :provide | :reject\n    ;; Publishing\n    :announce → :want (from interested peers)\n    ;; Connection\n    :hello → :verify → ready\n  })"
          "lisp"))

        (p "The protocol definition is itself content-addressed. Nodes pin the protocol "
           "version they speak. Protocol upgrades are published to sx-pub like any component."))

      (~docs/subsection :title "2b. Three Transports"

        (table (~tw :tokens "w-full mb-6 text-sm")
          (thead
            (tr (th (~tw :tokens "text-left p-2") "Link") (th (~tw :tokens "text-left p-2") "Transport") (th (~tw :tokens "text-left p-2") "Why")))
          (tbody
            (tr (td (~tw :tokens "p-2 font-medium") "Browser ↔ Home node") (td (~tw :tokens "p-2") "WebSocket") (td (~tw :tokens "p-2") "Reliable, always-on, works today, no infrastructure changes"))
            (tr (td (~tw :tokens "p-2 font-medium") "Browser ↔ Browser") (td (~tw :tokens "p-2") "WebRTC data channel") (td (~tw :tokens "p-2") "True peer-to-peer, NAT traversal via ICE/STUN, UDP + TCP modes"))
            (tr (td (~tw :tokens "p-2 font-medium") "Server ↔ Server") (td (~tw :tokens "p-2") "HTTP federation") (td (~tw :tokens "p-2") "Existing sx-pub protocol, signed activities"))))

        (p "The SX protocol layer is transport-agnostic. " (code "peer-send") " and " (code "peer-listen") " "
           "work identically whether the peer is connected via WebSocket, WebRTC, or HTTP. "
           "Upgrade to WebTransport when HTTP/3 infrastructure matures — zero protocol changes."))

      (~docs/subsection :title "2c. Browser ↔ Home Node (WebSocket)"
        (p "Browser connects to home node on page load. Receives push updates, "
           "sends publish requests, queries the federation.")

        (~docs/code :src (highlight
          ";; Connect to home node — WebSocket, works today\n(define home (ws-connect (str \"wss://\" home-node \"/sx-web/ws\")))\n\n;; Same protocol regardless of transport\n(peer-send home (sx-hello (node-id) (node-pubkey)))\n(peer-listen home (fn (msg) (dispatch-protocol msg)))\n\n;; Push notifications from server:\n;;   (announce \"Qm..xyz\" {:author \"...\" :type :component})\n;; Content exchange:\n;;   (want (list \"Qm..abc\")) → (provide \"Qm..abc\" \"(defcomp ...)\")\n;; Peer introduction:\n;;   (peers (list {:id \"...\" :offer sdp-offer}))"
          "lisp")))

      (~docs/subsection :title "2d. Browser ↔ Browser (WebRTC)"
        (p "Direct peer-to-peer connections between browsers. Home nodes relay "
           "the signaling (SDP offer/answer exchange), then browsers talk directly. "
           "Essential for low-latency game state, collaborative editing, and peer content exchange.")

        (~docs/code :src (highlight
          ";; Signaling: home nodes relay SDP offers\n;;\n;; Browser A → Home A (WebSocket): (rtc-offer peer-b-id sdp)\n;; Home A → Home B (federation):   (rtc-relay peer-a-id sdp)\n;; Home B → Browser B (WebSocket):  (rtc-offer peer-a-id sdp)\n;; Browser B → Home B:              (rtc-answer peer-a-id sdp)\n;; ... relay back ...\n;; WebRTC connection established — browsers talk directly\n\n;; After signaling, same API as any peer\n(define peer-b (rtc-connect offer))\n(peer-send peer-b (signal-update \"player-x\" 42))\n(peer-listen peer-b (fn (msg) (dispatch-protocol msg)))"
          "lisp"))

        (~docs/code :src (highlight
          ";; WebRTC data channel modes — chosen per room\n;;\n;; Reliable ordered (TCP-like):\n;;   Chat messages, component exchange, CID resolution\n;;   Every message arrives, in order\n;;\n;; Unreliable unordered (UDP-like):\n;;   Game state (player positions), cursor positions, audio levels\n;;   Latest value matters, old values can drop\n;;\n;; A room can use both — reliable for commands, unreliable for state\n(define game-room (join-room home \"game-1\"\n  {:reliable {:chat (signal (list)) :inventory (signal (dict))}\n   :unreliable {:player-x (signal 0) :player-y (signal 0)\n                :aim-angle (signal 0)}}))"
          "lisp"))

        (p "Peer-to-peer means:")
        (ul (~tw :tokens "list-disc pl-6 mb-4 space-y-1")
          (li "Game state at LAN-like latency — no server round-trip")
          (li "Content exchange between nearby browsers without hitting IPFS")
          (li "Collaborative editing with sub-frame update propagation")
          (li "Works even if both home nodes are on modest hardware")
          (li "Server load scales with signaling, not with traffic")))

      (~docs/subsection :title "2d. Shared Signals Over WebTransport"
        (p "Signals synchronized across peers. The same reactive primitive "
           "that drives local UI drives multiplayer state. A " (code "shared-signal") " "
           "is a signal whose writes propagate over WebTransport to all subscribed peers.")

        (~docs/code :src (highlight
          ";; Shared signal — local writes propagate to peers, remote writes update locally\n(define player-x (shared-signal room \"player-x\" 0))\n(define player-y (shared-signal room \"player-y\" 0))\n\n;; Local code uses normal signal ops — no awareness of networking\n(reset! player-x 42)          ;; updates local + sends to peers\n(deref player-x)              ;; reactive, triggers re-render\n(swap! player-y inc)           ;; same — local update + peer sync\n\n;; Under the hood:\n;;   (reset! shared-sig val)\n;;   → (reset! local-signal val)\n;;   → (peer-send room (signal-update \"player-x\" val))\n;;\n;;   incoming (signal-update \"player-x\" val) from peer\n;;   → (reset! local-signal val)\n;;   → reactive DOM updates fire as normal"
          "lisp"))

        (p "A " (code "room") " is a WebTransport channel with a set of shared signals. "
           "Peers join a room, receive current state, and get incremental updates. "
           "The signal names are the contract — any SX component that reads "
           (code "(deref player-x)") " works whether the signal is local or shared.")

        (~docs/code :src (highlight
          ";; Room: a named set of shared signals over WebTransport\n(define room (join-room home-peer \"game-lobby\"\n  {:signals {:player-x 0 :player-y 0 :chat-messages (list)}\n   :on-join  (fn (peer-id) (announce peer-id))\n   :on-leave (fn (peer-id) (remove-player peer-id))}))\n\n;; Game loop reads shared signals — identical to local signals\n(effect (fn ()\n  (draw-player (deref player-x) (deref player-y))))\n\n;; Chat is shared too — append a message, all peers see it\n(reset! chat-messages (append (deref chat-messages)\n  (dict :from (node-id) :text \"hello\")))"
          "lisp"))

        (p "Protocol messages for shared state:")
        (~docs/code :src (highlight
          ";; Added to sx-sync protocol\n:join-room    (fn (room-id signal-names) ...)   ;; subscribe to room\n:room-state   (fn (room-id signals) ...)        ;; full state snapshot\n:signal-update (fn (room-id name value) ...)    ;; incremental update\n:leave-room   (fn (room-id) ...)                ;; unsubscribe\n\n;; Conflict resolution: last-writer-wins by default\n;; Opt-in CRDT merge for collaborative data (lists, sets, maps)\n:signal-merge  (fn (room-id name op args) ...)  ;; CRDT operation"
          "text"))

        (p "Games, collaborative editors, chat, multiplayer tools — all the same primitive. "
           "A " (code "shared-signal") " is a signal. Existing components work unchanged. "
           "The networking is invisible to the reactive layer."))

      (~docs/subsection :title "2f. Deliverables"
        (ul (~tw :tokens "list-disc pl-6 mb-4 space-y-1")
          (li (code "web/lib/sx-sync.sx") " — protocol definition incl. shared signals (~300 LOC)")
          (li (code "web/lib/peer.sx") " — transport-agnostic peer abstraction (~200 LOC)")
          (li (code "web/lib/shared-signal.sx") " — shared signal primitive (~120 LOC)")
          (li "Platform primitives: " (code "ws-connect, rtc-connect, peer-send, peer-listen") " (~80 LOC JS)")
          (li "Server: WebSocket endpoint + signaling relay + room state (~350 LOC Python)")
          (li "Conflict resolution: LWW default + CRDT opt-in (~150 LOC SX)"))))

    ;; =====================================================================
    ;; Phase 3: In-Browser Authoring
    ;; =====================================================================

    (~docs/section :title "Phase 3: In-Browser Authoring" :id "phase-3"

      (p (~tw :tokens "text-stone-600 italic mb-4")
        "Edit, preview, test, and publish from a browser tab. No local dev environment.")

      (~docs/subsection :title "3a. Live Editor"
        (p "SX editor component with real-time preview. "
           "Edit source on the left, rendered output on the right. "
           "The editor is itself an SX island.")

        (~docs/code :src (highlight
          "(defisland ~sx-web/editor (&key initial-source)\n  (let ((source (signal (or initial-source \"\")))\n        (parsed (computed (fn () (try-parse (deref source)))))\n        (preview (computed (fn () (try-render (deref parsed))))))\n    (div :class \"flex h-full\"\n      ;; Source editor (textarea or CodeMirror via foreign FFI)\n      (div :class \"flex-1\"\n        (textarea :bind source :class \"font-mono ...\"))\n      ;; Live preview\n      (div :class \"flex-1 border-l\"\n        (if (get (deref preview) \"error\")\n          (div :class \"text-red-600\" (get (deref preview) \"error\"))\n          (div (get (deref preview) \"result\")))))))"
          "lisp"))

        (p "The evaluator runs client-side. No server round-trip for preview. "
           "Syntax errors, type errors, and render errors show inline as you type."))

      (~docs/subsection :title "3b. In-Browser Test Runner"
        (p "Tests are SX expressions. The browser evaluator runs them directly.")

        (~docs/code :src (highlight
          ";; Tests travel with components\n(deftest ~my-component/tests\n  (test \"renders title\"\n    (let ((html (render-to-html (~my-component :title \"Hello\"))))\n      (assert-contains html \"Hello\")))\n\n  (test \"handles empty props\"\n    (let ((html (render-to-html (~my-component))))\n      (assert-contains html \"Untitled\"))))\n\n;; Run in browser — same evaluator, same results\n(run-tests ~my-component/tests)\n;; => {:passed 2 :failed 0 :errors ()}"
          "lisp"))

        (p "Component + tests + docs are published as a single unit to sx-pub. "
           "Any node can re-run the tests to verify the component works."))

      (~docs/subsection :title "3c. Publish Flow"
        (p "From browser, one action: edit → test → publish → federate.")

        (~docs/code :src (highlight
          ";; Browser publish flow\n(define publish!\n  (fn (source)\n    (let ((cid (content-hash source))\n          (tests (extract-tests source))\n          (results (run-tests tests)))\n      (when (all-passed? results)\n        ;; Pin locally\n        (content-pin browser-store cid)\n        ;; Push to home node via WebTransport\n        (peer-send home-peer\n          (sx-publish cid source\n            {:tests results\n             :requires (extract-requires source)}))\n        ;; Home node pins to IPFS + anchors + federates\n        cid))))"
          "lisp"))

        (p "The browser handles editing, testing, hashing, and local pinning. "
           "The home server handles IPFS pinning, blockchain anchoring, and federation delivery. "
           "Clean split: browsers create, servers distribute."))

      (~docs/subsection :title "3d. Deliverables"
        (ul (~tw :tokens "list-disc pl-6 mb-4 space-y-1")
          (li (code "web/lib/editor.sx") " — live editor island (~300 LOC)")
          (li (code "web/lib/test-runner.sx") " — browser test runner (~150 LOC)")
          (li (code "web/lib/publish.sx") " — publish flow (~100 LOC)")
          (li "Test extraction from SX source (find deftest forms)"))))

    ;; =====================================================================
    ;; Phase 4: Component Discovery & Resolution
    ;; =====================================================================

    (~docs/section :title "Phase 4: Component Discovery & Resolution" :id "phase-4"

      (p (~tw :tokens "text-stone-600 italic mb-4")
        "Find, resolve, and depend on components across the federation.")

      (~docs/subsection :title "4a. Dependency Resolution"
        (p "Components declare dependencies via " (code ":requires") ". "
           "Resolution checks local cache first, then home node, then federation.")

        (~docs/code :src (highlight
          ";; Component with declared dependencies\n(defcomp ~charts/bar-chart (&key data labels)\n  :requires ((~cssx/tw \"Qm..css\")           ;; pinned by CID\n             (~charts/axis \"Qm..axis\")        ;; specific version\n             (~charts/tooltip :latest))        ;; resolve latest from publisher\n  ...)\n\n;; Resolution order:\n;;   1. Local IndexedDB (by CID)\n;;   2. Service Worker cache\n;;   3. Home node (WebTransport :want)\n;;   4. Federation query (WebTransport :query)\n;;   5. IPFS gateway (HTTP fallback)"
          "lisp"))

        (p "CID dependencies are immutable — resolve once, cache forever. "
           (code ":latest") " dependencies check the publisher's feed for the current CID."))

      (~docs/subsection :title "4b. Component Browser"
        (p "Browse, search, and preview components from the federated network. "
           "Each component renders its own preview because it IS executable SX.")

        (~docs/code :src (highlight
          ";; Search the federation\n(peer-send home-peer\n  (sx-query {:type :component\n             :tags [\"chart\" \"visualization\"]\n             :has-tests true}))\n\n;; Results include CID, metadata, AND the component itself\n;; Click to preview — evaluates client-side\n;; Click to pin — stores in IndexedDB\n;; Click to fork — opens in editor with source"
          "lisp"))

        (p "The component browser is itself an SX component published to sx-pub."))

      (~docs/subsection :title "4c. Deliverables"
        (ul (~tw :tokens "list-disc pl-6 mb-4 space-y-1")
          (li (code "web/lib/resolver.sx") " — dependency resolution chain (~200 LOC)")
          (li (code "sx/sx/tools/component-browser.sx") " — federated browser (~400 LOC)")
          (li (code "sx/sx/tools/search.sx") " — search interface (~150 LOC)")
          (li "Server: search index over pinned components"))))

    ;; =====================================================================
    ;; Phase 5: Node Bootstrap
    ;; =====================================================================

    (~docs/section :title "Phase 5: Node Bootstrap" :id "phase-5"

      (p (~tw :tokens "text-stone-600 italic mb-4")
        "Standing up a new node: one command, minutes to first render.")

      (~docs/subsection :title "5a. Server Node Bootstrap"
        (~docs/code :src (highlight
          "# One command. Pulls Docker image, starts all services.\ndocker run -d --name sx-node \\\n  -e SX_SEED_PEER=pub.sx-web.org \\\n  -e SX_DOMAIN=my-node.example.com \\\n  -p 443:443 \\\n  ghcr.io/rose-ash/sx-node:latest\n\n# What happens:\n#   1. IPFS daemon starts, connects to network\n#   2. Fetches spec CIDs from seed peer\n#   3. Evaluates specs — now has a working SX evaluator\n#   4. Follows seed peer — receives component announcements\n#   5. Caddy auto-provisions TLS via Let's Encrypt\n#   6. WebTransport endpoint live\n#   7. Ready to serve, publish, and federate"
          "bash"))

        (p "The bootstrap IS the proof: if your node can fetch specs from the network, "
           "evaluate them, and render components — the system works. "
           "No manual configuration beyond domain name and seed peer."))

      (~docs/subsection :title "5b. Browser Node Bootstrap"
        (~docs/code :src (highlight
          ";; Visit any sx-web node in a browser\n;; → sx-browser.js loads (evaluator + signals + DOM adapter)\n;; → Service Worker installs (CID caching)\n;; → WebTransport connects to host node\n;; → Spec CIDs pinned to IndexedDB\n;; → You're a node.\n;;\n;; No install. No signup. No CLI.\n;; Close the tab → ephemeral node disappears\n;; Reopen → reconnects, IndexedDB still has your pins"
          "text"))

        (p "A browser becomes a node by visiting a URL. "
           "Leaving keeps your pins. Returning restores your state. "
           "The barrier to entry is typing an address."))

      (~docs/subsection :title "5c. Deliverables"
        (ul (~tw :tokens "list-disc pl-6 mb-4 space-y-1")
          (li "Docker image: " (code "sx-node") " with OCaml host, IPFS, Caddy, PostgreSQL")
          (li "Bootstrap script: fetch specs, verify CIDs, initialize DB")
          (li "Seed peer protocol: advertise available specs + popular components")
          (li "Browser auto-bootstrap: Service Worker install + spec pinning on first visit"))))

    ;; =====================================================================
    ;; Phase 6: AI Composition Layer
    ;; =====================================================================

    (~docs/section :title "Phase 6: AI Composition Layer" :id "phase-6"

      (p (~tw :tokens "text-stone-600 italic mb-4")
        "The federated graph is simultaneously training data, retrieval context, and composition target.")

      (~docs/subsection :title "6a. Component Retrieval"
        (p "AI resolves components by CID, not by guessing. "
           "The federation is a typed, tested, searchable context window.")

        (~docs/code :src (highlight
          ";; AI composition query\n(ai-compose\n  {:intent \"dashboard with sales chart and user table\"\n   :constraints {:has-tests true\n                 :min-pins 10         ;; popularity signal\n                 :verified true}       ;; blockchain-anchored\n   :context (deref current-page)})\n\n;; AI doesn't generate from scratch — it assembles:\n;; 1. Search federation for matching components\n;; 2. Check tests pass for each candidate\n;; 3. Resolve dependencies\n;; 4. Compose into page, respecting :requires\n;; 5. Output is auditable: list of CIDs + composition logic"
          "lisp"))

        (p "The output is a composition of existing, tested, content-addressed components. "
           "Not generated code — assembled code. Deterministic, verifiable, traceable."))

      (~docs/subsection :title "6b. Self-Improving Tooling"
        (p "AI tools are SX components published to sx-pub. "
           "They improve as the network grows — more components to compose from, "
           "more tests to validate against, more usage patterns to learn from.")

        (p "The network effect works for both humans and AI:")
        (ul (~tw :tokens "list-disc pl-6 mb-4 space-y-1")
          (li "Every component published is more material for AI composition")
          (li "Every test attached is more validation for AI output")
          (li "Every fork is a preference signal for AI ranking")
          (li "Every pin is a popularity signal for AI retrieval")))

      (~docs/subsection :title "6c. Deliverables"
        (ul (~tw :tokens "list-disc pl-6 mb-4 space-y-1")
          (li (code "web/lib/ai-compose.sx") " — composition interface (~200 LOC)")
          (li "Embedding index over component metadata + signatures")
          (li "Retrieval API: search by intent, filter by quality signals")
          (li "Composition validator: check output resolves, tests pass"))))

    ;; =====================================================================
    ;; Implementation Order
    ;; =====================================================================

    (~docs/section :title "Implementation Order" :id "order"

      (~docs/code :src (highlight
        "Phase 1  Browser Node Foundation    ~400 LOC SX + ~100 LOC JS\n         IndexedDB store, Service Worker, CID verification\n         Prerequisite: none (extends existing sx-browser.js)\n\nPhase 2  Peer Network                  ~770 LOC SX + ~430 LOC Python/JS\n         WebSocket + WebRTC + protocol + shared signals + rooms\n         Prerequisite: Phase 1 (needs content store)\n\nPhase 3  In-Browser Authoring          ~550 LOC SX\n         Editor, test runner, publish flow\n         Prerequisite: Phase 1 + 2 (needs store + peer connection)\n\nPhase 4  Component Discovery           ~750 LOC SX\n         Dependency resolution, component browser, search\n         Prerequisite: Phase 2 (needs federation queries)\n\nPhase 5  Node Bootstrap                ~300 LOC config + scripts\n         Docker image, auto-bootstrap, seed protocol\n         Prerequisite: Phase 1–4 (packages everything)\n\nPhase 6  AI Composition                ~400 LOC SX + embedding index\n         Retrieval, composition, validation\n         Prerequisite: Phase 4 (needs searchable component graph)\n\n         Total new SX: ~2650 LOC\n         Total new platform: ~350 LOC (JS + Python)\n         Timeline: Phases are independent workstreams after Phase 1"
        "text"))

      (p "Phase 1 is the foundation — everything else depends on the browser being able to "
         "store, verify, and cache content-addressed SX. After that, phases 2–4 can proceed "
         "in parallel. Phase 5 packages it all. Phase 6 builds on the populated network."))

    ;; =====================================================================
    ;; What Already Exists
    ;; =====================================================================

    (~docs/section :title "What Already Exists (sx-pub Phases 1–4)" :id "existing"

      (table (~tw :tokens "w-full mb-6 text-sm")
        (thead
          (tr (th (~tw :tokens "text-left p-2") "Capability") (th (~tw :tokens "text-left p-2") "Status") (th (~tw :tokens "text-left p-2") "Location")))
        (tbody
          (tr (td (~tw :tokens "p-2") "IPFS async client") (td (~tw :tokens "p-2 text-green-700") "Done") (td (~tw :tokens "p-2 font-mono text-xs") "shared/utils/ipfs_client.py"))
          (tr (td (~tw :tokens "p-2") "Merkle tree + proofs") (td (~tw :tokens "p-2 text-green-700") "Done") (td (~tw :tokens "p-2 font-mono text-xs") "shared/utils/anchoring.py"))
          (tr (td (~tw :tokens "p-2") "OpenTimestamps anchoring") (td (~tw :tokens "p-2 text-green-700") "Done") (td (~tw :tokens "p-2 font-mono text-xs") "shared/utils/anchoring.py"))
          (tr (td (~tw :tokens "p-2") "ActivityPub federation") (td (~tw :tokens "p-2 text-green-700") "Done") (td (~tw :tokens "p-2 font-mono text-xs") "shared/infrastructure/activitypub.py"))
          (tr (td (~tw :tokens "p-2") "sx-pub HTTP handlers") (td (~tw :tokens "p-2 text-green-700") "Done") (td (~tw :tokens "p-2 font-mono text-xs") "sx/sx/handlers/pub-api.sx"))
          (tr (td (~tw :tokens "p-2") "Publishing + CID resolution") (td (~tw :tokens "p-2 text-green-700") "Done") (td (~tw :tokens "p-2 font-mono text-xs") "sx/sxc/pages/pub_helpers.py"))
          (tr (td (~tw :tokens "p-2") "SX wire format (aser)") (td (~tw :tokens "p-2 text-green-700") "Done") (td (~tw :tokens "p-2 font-mono text-xs") "web/adapter-sx.sx"))
          (tr (td (~tw :tokens "p-2") "Content store (in-memory)") (td (~tw :tokens "p-2 text-green-700") "Done") (td (~tw :tokens "p-2 font-mono text-xs") "lib/content.sx"))
          (tr (td (~tw :tokens "p-2") "Component localStorage cache") (td (~tw :tokens "p-2 text-green-700") "Done") (td (~tw :tokens "p-2 font-mono text-xs") "web/boot.sx"))
          (tr (td (~tw :tokens "p-2") "Docker dev environment") (td (~tw :tokens "p-2 text-green-700") "Done") (td (~tw :tokens "p-2 font-mono text-xs") "docker-compose.dev-pub.yml"))))

      (p "The server-side infrastructure is complete. The plan above extends it to browsers."))))
