Update sx-docs: add deps spec to viewer, mark Phase 1 complete

Add deps.sx to the spec navigator in sx-docs (nav-data, specs page).
Update isomorphic architecture plan to show Phase 1 as complete with
link to the canonical spec at /specs/deps.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 12:10:52 +00:00
parent 0ebf3c27fd
commit d6ca185975
3 changed files with 51 additions and 27 deletions

View File

@@ -99,7 +99,8 @@
(dict :label "Boot" :href "/specs/boot")
(dict :label "CSSX" :href "/specs/cssx")
(dict :label "Continuations" :href "/specs/continuations")
(dict :label "call/cc" :href "/specs/callcc")))
(dict :label "call/cc" :href "/specs/callcc")
(dict :label "Deps" :href "/specs/deps")))
(define plans-nav-items (list
(dict :label "Isomorphic Architecture" :href "/plans/isomorphic-architecture"
@@ -169,7 +170,12 @@
:desc "Full first-class continuations — call-with-current-continuation."
:prose "Full call/cc captures the entire remaining computation as a first-class function — not just up to a delimiter, but all the way to the top level. Invoking the continuation abandons the current computation entirely and resumes from where it was captured. Strictly more powerful than delimited continuations, but harder to implement in targets that don't support it natively. Recommended for Scheme and Haskell targets where it's natural. Python, JavaScript, and Rust targets should prefer delimited continuations (continuations.sx) unless full escape semantics are genuinely needed. Optional extension: the continuation type is shared with continuations.sx if both are loaded.")))
(define all-spec-items (concat core-spec-items (concat adapter-spec-items (concat browser-spec-items extension-spec-items))))
(define module-spec-items (list
(dict :slug "deps" :filename "deps.sx" :title "Deps"
:desc "Component dependency analysis — per-page bundling, transitive closure, CSS scoping."
:prose "The deps module analyzes component dependency graphs to enable per-page bundling. Instead of sending every component definition to every page, deps.sx walks component AST bodies to find transitive ~component references, then computes the minimal set needed. It also collects per-page CSS classes from only the used components. All functions are pure — each host bootstraps them to native code via --spec-modules deps. Platform functions (component-deps, component-set-deps!, component-css-classes, env-components, regex-find-all, scan-css-classes) are implemented natively per target.")))
(define all-spec-items (concat core-spec-items (concat adapter-spec-items (concat browser-spec-items (concat extension-spec-items module-spec-items)))))
(define find-spec
(fn (slug)

View File

@@ -621,51 +621,66 @@
(~doc-section :title "Phase 1: Component Distribution & Dependency Analysis" :id "phase-1"
(div :class "rounded border border-violet-200 bg-violet-50 p-4 mb-4"
(p :class "text-violet-900 font-medium" "What it enables")
(p :class "text-violet-800" "Per-page component bundles instead of sending every definition to every page. Smaller payloads, faster boot, better cache hit rates."))
(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"))
(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."))
(~doc-subsection :title "The Problem"
(p "client_components_tag() in jinja_bridge.py serializes ALL entries in _COMPONENT_ENV. The sx_page() template sends everything or nothing based on a single global hash. No mechanism determines which components a page actually needs."))
(~doc-subsection :title "Approach"
(~doc-subsection :title "Implementation"
(p "Phase 1 follows the spec-first architecture: the dependency analysis algorithm is defined in "
(a :href "/specs/deps" :class "text-violet-700 underline" "deps.sx")
" and bootstrapped to both Python (sx_ref.py) and JavaScript (sx-ref.js). A thin dispatcher in deps.py routes to the bootstrapped implementation when SX_USE_REF=1.")
(div :class "space-y-4"
(div
(h4 :class "font-semibold text-stone-700" "1. Transitive closure analyzer")
(p "New module shared/sx/deps.py:")
(h4 :class "font-semibold text-stone-700" "1. Transitive closure analyzer (deps.sx)")
(p "The spec defines 9 functions for dependency analysis. The core algorithm:")
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
(li "Walk Component.body AST, collect all Symbol refs starting with ~")
(li "Recursively follow into their bodies")
(li "Handle control forms (if/when/cond/case) — include ALL branches")
(li "Handle macros — expand during walk using limited eval"))
(~doc-code :code (highlight "def transitive_deps(name: str, env: dict) -> set[str]:\n \"\"\"Compute transitive component dependencies.\"\"\"\n seen = set()\n def walk(n):\n if n in seen: return\n seen.add(n)\n comp = env.get(n)\n if comp:\n for dep in _scan_ast(comp.body):\n walk(dep)\n walk(name)\n return seen - {name}" "python")))
(li "scan-refs: walk a component body AST, collect all Symbol refs starting with ~")
(li "transitive-deps: recursively follow component references through the env")
(li "compute-all-deps: batch-compute and cache deps for all components in env")
(li "Handle circular references safely via seen-set"))
(~doc-code :code (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")))
(div
(h4 :class "font-semibold text-stone-700" "2. Runtime component scanning")
(p "After _aser serializes page content, scan the SX string for (~name patterns (parallel to existing scan_classes_from_sx for CSS). Then compute transitive closure to get sub-components."))
(p "scan-components-from-source uses regex to find (~name patterns in serialized SX. components-needed combines scanning with transitive closure to get the full set of components a page requires."))
(div
(h4 :class "font-semibold text-stone-700" "3. Per-page component block")
(p "In sx_page() — replace all-or-nothing with page-specific bundle. Hash changes per page, localStorage cache keyed by route pattern."))
(h4 :class "font-semibold text-stone-700" "3. Per-page CSS classes")
(p "page-css-classes collects Tailwind classes from all components in a page bundle, enabling per-page CSS extraction."))
(div
(h4 :class "font-semibold text-stone-700" "4. SX partial responses")
(p "components_for_request() already diffs against SX-Components header. Enhance with transitive closure so only truly needed missing components are sent."))))
(h4 :class "font-semibold text-stone-700" "4. Platform interface")
(p "The spec declares 6 platform functions that each host implements natively:")
(ul :class "list-disc pl-5 text-stone-700 space-y-1 font-mono text-sm"
(li "component-deps / component-set-deps!")
(li "component-css-classes")
(li "env-components")
(li "regex-find-all / scan-css-classes")))))
(~doc-subsection :title "Files"
(ul :class "list-disc pl-5 text-stone-700 space-y-1 font-mono text-sm"
(li "New: shared/sx/deps.pydependency analysis")
(li "shared/sx/jinja_bridge.py — per-page bundle generation")
(li "shared/sx/helpers.py — modify sx_page() and sx_response()")
(li "shared/sx/types.py — add deps: set[str] to Component")
(li "shared/sx/ref/boot.sx — per-page component caching")))
(li "shared/sx/ref/deps.sxcanonical spec (9 functions)")
(li "shared/sx/deps.py — dispatcher (SX_USE_REF → bootstrapped, else fallback)")
(li "shared/sx/ref/bootstrap_py.py — spec module + platform deps")
(li "shared/sx/ref/bootstrap_js.py — spec module + platform deps")
(li "shared/sx/ref/sx_ref.py — regenerated with deps module")
(li "shared/static/scripts/sx-ref.js — regenerated with deps module")
(li "shared/sx/tests/test_deps.py — 15 tests")))
(~doc-subsection :title "Verification"
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
(li "Page using 5/50 components → data-components block contains only those 5 + transitive deps")
(li "No \"Unknown component\" errors after bundle reduction")
(li "Payload size reduction measurable"))))
(li "15 dedicated deps tests pass (scan, transitive, circular, compute-all, components-needed)")
(li "541 total Python tests pass with zero regressions")
(li "JS deps functions verified in Node.js (scanRefs, transitiveDeps, computeAllDeps, componentsNeeded)")
(li "Both bootstrapped and fallback code paths produce identical results"))))
;; -----------------------------------------------------------------------
;; Phase 2

View File

@@ -179,7 +179,10 @@ boot.sx depends on: cssx, orchestration, adapter-dom, render
;; Extensions (optional — loaded only when target requests them)
continuations.sx depends on: eval (optional)
callcc.sx depends on: eval (optional)")))
callcc.sx depends on: eval (optional)
;; Spec modules (optional — loaded via --spec-modules)
deps.sx depends on: eval (optional)")))
(div :class "space-y-3"
(h2 :class "text-2xl font-semibold text-stone-800" "Extensions")