diff --git a/sx/sx/plans.sx b/sx/sx/plans.sx index b5ac84f..7e2a34b 100644 --- a/sx/sx/plans.sx +++ b/sx/sx/plans.sx @@ -630,58 +630,54 @@ (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.")) + (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.")) (~doc-subsection :title "Implementation" - (p "Phase 1 follows the spec-first architecture: the dependency analysis algorithm is defined in " + (p "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.") + " — a spec module bootstrapped to every host. Each host loads it via " (code "--spec-modules deps") " and provides 6 platform functions. The spec is the single source of truth; hosts are interchangeable.") (div :class "space-y-4" (div - (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 "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"))) + (h4 :class "font-semibold text-stone-700" "1. Transitive closure (deps.sx)") + (p "9 functions that walk the component graph. The core:") + (~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")) + (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 :class "font-semibold text-stone-700" "2. Runtime component scanning") - (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.")) + (h4 :class "font-semibold text-stone-700" "2. Page scanning") + (~doc-code :code (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 "(~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 :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.")) + (h4 :class "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 :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"))))) + (p "The spec declares 6 functions each host implements natively — the only host-specific code:") + (ul :class "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"))))) - (~doc-subsection :title "Files" + (~doc-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. The same mechanism can carry future modules (e.g., io-detection for Phase 2) without changing the bootstrapper architecture.") (ul :class "list-disc pl-5 text-stone-700 space-y-1 font-mono text-sm" - (li "shared/sx/ref/deps.sx — canonical 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"))) + (li "shared/sx/ref/deps.sx — canonical spec (9 functions, 6 platform declarations)") + (li "Bootstrapped to all host targets via --spec-modules deps"))) (~doc-subsection :title "Verification" (ul :class "list-disc pl-5 text-stone-700 space-y-1" - (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")))) + (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 "/plans/bundle-analyzer" :class "text-violet-700 underline" "Live bundle analyzer") " shows real per-page savings on this app")))) ;; ----------------------------------------------------------------------- ;; Phase 2