Aser serialization: aser-call/fragment now return SxExpr instead of String.
serialize/inspect passes SxExpr through unquoted, preventing the double-
escaping (\" → \\\" ) that broke client-side parsing when aser wire format
was output via raw! into <script> tags. Added make-sx-expr + sx-expr-source
primitives to OCaml and JS hosts.
Binary blob protocol: eval, aser, aser-slot, and sx-page-full now send SX
source as length-prefixed blobs instead of escaped strings. Eliminates pipe
desync from concurrent requests and removes all string-escape round-trips
between Python and OCaml.
Bridge safety: re-entrancy guard (_in_io_handler) raises immediately if an
IO handler tries to call the bridge, preventing silent deadlocks.
Fetch error logging: orchestration.sx error callback now logs method + URL
via log-warn. Platform catches (fetchAndRestore, fetchPreload, bindBoostForm)
also log errors instead of silently swallowing them.
Transpiler fixes: makeEnv, scopePeek, scopeEmit, makeSxExpr added as
platform function definitions + transpiler mappings — were referenced in
transpiled code but never defined as JS functions.
Playwright test infrastructure:
- nav() captures JS errors and fails fast with the actual error message
- Checks for [object Object] rendering artifacts
- New tests: delete-row interaction, full page refresh, back button,
direct load with fresh context, code block content verification
- Default base URL changed to localhost:8013 (standalone dev server)
- docker-compose.dev-sx.yml: port 8013 exposed for local testing
- test-sx-build.sh: build + unit tests + Playwright smoke tests
Geography content: index page component written (sx/sx/geography/index.sx)
describing OCaml evaluator, wire formats, rendering pipeline, and topic
links. Wiring blocked by aser-expand-component children passing issue.
Tests: 1080/1080 JS, 952/952 OCaml, 66/66 Playwright
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
jinja_bridge, helpers, handlers, query_executor now conditionally
import from ref/sx_ref and ref/async_eval_ref when SX_USE_REF=1.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The inter-service data layer (fetch_data/call_action) was the least
structured part of the codebase — Python _handlers dicts with ad-hoc
param extraction scattered across 16 route files. This replaces them
with declarative .sx query/action definitions that make the entire
inter-service protocol self-describing and greppable.
Infrastructure:
- defquery/defaction special forms in the sx evaluator
- Query/action registry with load, lookup, and schema introspection
- Query executor using async_eval with I/O primitives
- Blueprint factories (create_data_blueprint/create_action_blueprint)
with sx-first dispatch and Python fallback
- /internal/schema endpoint on every service
- parse-datetime and split-ids primitives for type coercion
Service extractions:
- LikesService (toggle, is_liked, liked_slugs, liked_ids)
- PageConfigService (ensure, get_by_container, get_by_id, get_batch, update)
- RelationsService (wraps module-level functions)
- AccountDataService (user_by_email, newsletters)
- CartItemsService, MarketDataService (raw SQLAlchemy lookups)
50 of 54 handlers converted to sx, 4 Python fallbacks remain
(ghost-sync/push-member, clear-cart-for-order, create-order).
Net: -1,383 lines Python, +251 lines modified.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>