Pages whose components aren't loaded client-side now fall through
to server fetch instead of silently failing in the async callback.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Client router uses first-match, so /isomorphism/data-test was matching
the /isomorphism/<slug> wildcard instead of the specific data-test route.
Moved bundle-analyzer, routing-analyzer, data-test before the wildcard.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
handle-popstate falls back to #main-panel when no [sx-boost] element
is found, fixing back button for apps using explicit sx-target attrs.
bindClientRouteClick also checks sx-target on the link itself.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
bindClientRouteClick was calling tryClientRoute(pathname) without the
target-sel argument. This caused resolve-route-target to return nil,
so client routing ALWAYS fell back to server fetch on link clicks.
Now finds the sx-boost ancestor and passes its target selector.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Phase 4 section: green "Complete" badge with live data test link
- Documents architecture: resolve-page-data, server endpoint, data cache
- Lists files, 30 unit tests, verification steps
- Renumber: Phase 5 = async continuations, Phase 6 = streaming, Phase 7 = full iso
- Update Phase 3 to note :data pages now also client-routable
- Add data-test to "pages that fall through" list
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The registrations were in the platform eval block which emits before
var PRIMITIVES = {}. Moved to core.list and core.dict primitive sections.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously these mutating operations were internal helpers in the JS
bootstrapper but not declared in primitives.sx or registered in the
Python evaluator. Now properly specced and available in both hosts.
Removes mock injections from cache tests — they use real primitives.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 10 new tests: cache key generation, set/get, TTL expiry, overwrite,
key independence, complex nested data
- Update data-test.sx with cache verification instructions:
navigate away+back within 30s → client+cache, after 30s → new fetch
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
boot.sx uses parse-route-pattern from router.sx, but router was only
included as an opt-in spec module. Now auto-included when boot is in
the adapter set.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests cover: SX wire format roundtrip for data dicts (11 tests),
kebab-case key conversion (4 tests), component dep computation for
:data pages (2 tests), and full pipeline simulation — serialize on
server, parse on client, merge into env, eval content (3 tests).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Spec layer (orchestration.sx):
- try-client-route now handles :data pages instead of falling back to server
- New abstract primitive resolve-page-data(name, params, callback) — platform
decides transport (HTTP, IPC, cache, etc)
- Extracted swap-rendered-content and resolve-route-target helpers
Platform layer (bootstrap_js.py):
- resolvePageData() browser implementation: fetches /sx/data/<name>, parses
SX response, calls callback. Other hosts provide their own transport.
Server layer (pages.py):
- evaluate_page_data() evaluates :data expr, serializes result as SX
- auto_mount_page_data() mounts /sx/data/ endpoint with per-page auth
- _build_pages_sx now computes component deps for all pages (not just pure)
Test page at /isomorphism/data-test exercises the full pipeline.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three levels of ../ overshot from /app/sxc/pages/ to /. Use same
two-level pattern with /app/shared fallback as _read_spec_file.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4-phase design: server endpoint for on-demand component defs,
SX-specced client prefetch logic (hover/viewport triggers),
boundary declarations, and bootstrap integration.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
render-dom-unknown-component now calls (error ...) instead of
creating a styled div. This lets tryEvalContent catch the error
and fall back to server fetch, instead of rendering "Unknown
component: ~name" into the page.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove strict deps check — for case expressions like essay pages,
deps includes ALL branches but only one is taken. Instead, just
try to eval the content. If a component is missing, tryEvalContent
catches the error and we transparently fall back to server fetch.
deps field remains in registry for future prefetching use.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each page entry now includes a deps list of component names needed.
Client checks all deps are loaded before attempting eval — if any
are missing, falls through to server fetch with a clear log message.
No bundle bloat: server sends components for the current page only.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
NIL is a frozen sentinel object ({_nil:true}) which is truthy in JS.
(not expr) compiled to !expr, so (not nil) returned false instead of
true. Fixed to compile as !isSxTruthy(expr) which correctly handles
NIL. This was preventing client-side routing from activating.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SES lockdown may suppress console.error. Use logInfo for error
reporting since we know it works ([sx-ref] prefix visible).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Log "sx:route server fetch <url>" when falling back to network
- Use console.error for eval errors (not console.warn)
- Restructure bind-event to separate client route check from &&-chain
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
parseRoutePattern was undefined because the router module
wasn't included in the build. Now passing --spec-modules router.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The data-mount="body" script replaces the entire body content,
destroying the <script type="text/sx-pages"> tag. Moving
processPageScripts before processSxScripts ensures the page
registry is read before the body is replaced.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Server-side: log page count, output size, and first 200 chars in _build_pages_sx.
Client-side: log script tag count, text length, parsed entry count in processPageScripts.
Helps diagnose why pages: 0 routes loaded.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
str.format() doesn't re-process braces inside substituted values,
so the escaping was producing literal doubled braces {{:name...}}
in the output, which the SX parser couldn't parse.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This was silently masking the str.format() braces bug. If page
registry building fails, it should crash visibly, not serve a
broken page with 0 routes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
pages_sx contains SX dict literals with {} (empty closures) which
Python's str.format() interprets as positional placeholders, causing
a KeyError that was silently caught. Escape braces before formatting.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shows "pages: N routes loaded" at startup and
"sx:route no match (N routes) /path" when no route matches,
so we can see if routes loaded and why matching fails.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
boot-init prints SX_VERSION (build timestamp) to console on startup.
tryClientRoute logs why it falls through: has-data, no content, eval
failed, #main-panel not found. tryEvalContent logs the actual error.
Added logWarn platform function.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
tryClientRoute now logs why it falls through: has-data, no content,
eval failed, or #main-panel not found. tryEvalContent logs the actual
error on catch. Added logWarn platform function (console.warn).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
bind-event now checks tryClientRoute before executeRequest for GET
clicks on links. Previously only boost links (inside [sx-boost]
containers) attempted client routing — explicit sx-get links like
~nav-link always hit the network. Now essay/doc nav links render
client-side when possible.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All nav links use sx-select="#main-panel" to extract content from
responses. The index partial must include this wrapper so the select
finds it, matching the pattern used by test-detail-section.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Filter cards: target #test-results (the actual response container)
instead of sx-select #main-panel (not present in partial response).
Back link: use innerHTML swap into #main-panel (no sx-select needed).
Results route: use sx_response() for correct content-type.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The /results endpoint returns SX wire format but was sending it with
content-type text/html. The SX engine couldn't process it, so raw
s-expressions appeared as text and the browser tried to resolve
quoted strings like "a.jpg" as URLs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>