- send_ok_raw: when SX wire format contains newlines (string literals),
fall back to (ok "...escaped...") instead of (ok-raw ...) to keep
the pipe single-line. Prevents multi-line responses from desyncing
subsequent requests.
- expand-components? flag set in kernel env (not just VM adapter globals)
so aser-list's env-has? check finds it during component expansion.
- SX_STANDALONE: restore no_oauth but generate CSRF via session cookie
so mutation handlers (DELETE etc.) still work without account service.
- Shell statics injection: only inject small values (hashes, URLs) as
kernel vars. Large blobs (CSS, component_defs) use placeholder tokens.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Compiler fixes:
- Upvalue re-lookup returns own position (uv-index), not parent slot
- Spec: cek-call uses (make-env) not (dict) — OCaml Dict≠Env
- Bootstrap post-processes transpiler Dict→Env for cek_call
VM runtime fixes:
- compile_adapter evaluates constant defines (SPECIAL_FORM_NAMES etc.)
via execute_module instead of wrapping as NativeFn closures
- Native primitives: map-indexed, some, every?
- Nil-safe HO forms: map/filter/for-each/some/every? accept nil as empty
- expand-components? set in kernel env (not just VM globals)
- unwrap_env diagnostic: reports actual type received
sx-page-full command:
- Single OCaml call: aser-slot body + render-to-html shell
- Eliminates two pipe round-trips (was: aser-slot→Python→shell render)
- Shell statics (component_defs, CSS, pages_sx) cached in Python,
injected into kernel once, referenced by symbol in per-request command
- Large blobs use placeholder tokens — Python splices post-render,
pipe transfers ~51KB instead of 2MB
Performance (warm):
- Server total: 0.55s (was ~2s)
- aser-slot VM: 0.3s, shell render: 0.01s, pipe: 0.06s
- kwargs computation: 0.000s (cached)
SX_STANDALONE mode for sx_docs dev (skips fragment fetches).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add css_extras parameter to create_base_app. Legacy apps (blog, market
etc) get the default extras (basics.css, cards.css, blog-content.css,
prism.css, FontAwesome). SX app passes css_extras=[] — it uses CSSX
for styling and custom highlighting, not Prism/FA/Ghost.
Reduces <style id="sx-css"> from ~100KB+ of irrelevant CSS to ~5KB
of Tailwind resets + only the utility rules the page actually uses.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
init-client.sx contains browser-only code (dom-listen, collect! cssx).
It was in sx/sx/ which load_sx_dir scans and evaluates server-side,
causing "Undefined symbol: dom-listen". Move to sx/init-client.sx
which is outside the component load path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Styles (indicator, jiggle animation) and nav aria-selected behavior
were inline Python strings in sx/app.py. Now they live in sx/sx/init.sx
as proper SX source — styles via collect! "cssx", nav via dom-listen.
The shell's inline_css is empty; CSSX handles style injection on boot.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Component names now reflect filesystem location using / as path separator
and : as namespace separator for shared components:
~sx-header → ~layouts/header
~layout-app-body → ~shared:layout/app-body
~blog-admin-dashboard → ~admin/dashboard
209 files, 4,941 replacements across all services.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All routes moved under /sx/ prefix:
- / redirects to /sx/
- /sx/ serves home page
- /sx/<path:expr> is the catch-all for SX expression URLs
- Bare /(...) and /~... redirect to /sx/(...) and /sx/~...
- All ~600 hrefs, sx-get attrs, defhandler paths, redirect
targets, and blueprint routes updated across 44 files
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Every URL at sx-web.org now uses bracketed SX expressions — pages AND
API endpoints. defhandler :path values, sx-get/sx-post/sx-delete attrs,
code examples, and Python route decorators all converted.
- Add SxAtomConverter to handlers.py for parameter matching inside
expression URLs (e.g. /(api.(item.<sx:item_id>)))
- Convert ~50 defhandler :path values in ref-api.sx and examples.sx
- Convert ~90 sx-get/sx-post/sx-delete URLs in reference.sx, examples.sx
- Convert ~30 code example URLs in examples-content.sx
- Convert ~30 API URLs in pages.py (Python string code examples)
- Convert ~70 page navigation URLs in pages.py
- Convert 7 Python route decorators in routes.py
- Convert ~10 reactive API URLs in marshes.sx
- Add API redirect patterns to sx_router.py (301 for old paths)
- Remove /api/ skip in app.py redirects (old API paths now redirect)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rename /reactive-islands/ → /reactive/, /reference/ → /hypermedia/reference/,
/examples/ → /hypermedia/examples/ across all .sx and .py files
- Add 404 error page (not-found.sx) working on both server refresh and
client-side SX navigation via orchestration.sx error response handling
- Add trailing slash redirect (GET only, excludes /api/, /static/, /internal/)
- Remove blue sky-500 header bar from SX docs layout (conditional on header-rows)
- Fix 405 on API endpoints from trailing slash redirect hitting POST/PUT/DELETE
- Fix client-side 404: orchestration.sx now swaps error response content
instead of silently dropping it
- Add new plan files and home page component
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Configurable page shell (~sx-page-shell kwargs + SX_SHELL app config)
so each app controls its own assets — sx docs loads only sx-browser.js
- SX-evaluated sx-on:* handlers (eval-expr instead of new Function)
with DOM primitives registered in PRIMITIVES table
- data-init boot mode for pure SX initialization scripts
- Jiggle animation on links while fetching
- Nav: 3-column grid for centered alignment, is-leaf sizing,
fix map-indexed param order (index, item), guard mod-by-zero
- Async route eval failure now falls back to server fetch
instead of silently rendering nothing
- Remove duplicate h1 title from ~doc-page
- Re-bootstrap sx-ref.js + sx-browser.js
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The sx app is stateless — no database needed. In standalone mode
(SX_STANDALONE=true), the factory now skips register_db() so the app
doesn't crash trying to connect to a non-existent PostgreSQL.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- SX_STANDALONE=true env var: no OAuth, no root header, no cross-service
fragments. Same image runs in both rose-ash cooperative and standalone.
- Factory: added no_oauth parameter to create_base_app()
- Standalone layout defcomps skip ~root-header-auto/~root-mobile-auto
- Fixed Dockerfile: was missing sx/sx/ component directory copy
- CI: deploys sx-web swarm stack on main branch when sx changes
- Stack config at ~/sx-web/ (Caddy → sx_docs, Redis)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Consolidate 86 component rendering functions into sxc/pages/__init__.py,
update 37 import sites in routes.py, remove app.py side-effect imports,
and delete sx/sxc/sx_components.py.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Defpages are now declared with absolute paths in .sx files and auto-mounted
directly on the Quart app, removing ~850 lines of blueprint mount_pages calls,
before_request hooks, and g.* wrapper boilerplate. A new page = one defpage
declaration, nothing else.
Infrastructure:
- async_eval awaits coroutine results from callable dispatch
- auto_mount_pages() mounts all registered defpages on the app
- g._defpage_ctx pattern passes helper data to layout context
Migrated: sx, account, orders, federation, cart, market, events, blog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace Python GET page handlers with declarative defpage definitions in .sx
files across all 8 apps (sx docs, orders, account, market, cart, federation,
events, blog). Each app now has sxc/pages/ with setup functions, layout
registrations, page helpers, and .sx defpage declarations.
Core infrastructure: add g I/O primitive, PageDef support for auth/layout/
data/content/filter/aside/menu slots, post_author auth level, and custom
layout registration. Remove ~1400 lines of render_*_page/render_*_oob
boilerplate. Update all endpoint references in routes, sx_components, and
templates to defpage_* naming.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- OOB nav updates: AJAX navigation now swaps both menu bar levels
(main nav highlighting + sub-nav with current page) using the same
oob_header_sx/oob_page_sx pattern as blog/market/events
- Enable OAuth for sx and test apps (removed from _NO_OAUTH, added sx
to ALLOWED_CLIENTS, added app_urls for sx/test/orders)
- Fetch real cross-service fragments (cart-mini, auth-menu, nav-tree)
instead of hardcoding empty values
- Add :selected param to ~menu-row-sx for white text current-page label
- Fix duplicate element IDs: use menu-row-sx child_id/child mechanism
instead of manual header_child_sx wrappers
- Fix home page copy: "Server-rendered DOM over the wire (no HTML)"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New public-facing service documenting the s-expression rendering engine.
Modelled on four.htmx.org with violet theme, all content rendered via sx.
Sections: docs, reference, protocols, examples (live demos), essays
(including "sx sucks"). No database — purely static documentation.
Port 8012, Redis DB 10. CI and deploy.sh updated with app_dir() mapping
for sx_docs -> sx/ directory. Caddy reverse proxy entry added separately.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>