review quick-wins: JIT gate, crash guards, crit-2 signal-return, regen repair

Server (sx_server.ml):
- HTTP mode: JIT hook now opt-in via SX_SERVING_JIT, matching epoch mode
  (was unconditional — live serving-JIT miscompiles J1/J2/J3 de-risked)
- command channel: malformed/non-ASCII line returns an error response
  instead of killing the shared process (C1/C1b)
- response cache: soft error pages no longer cached (S4);
  http_render_page returns (html, is_error)

Kernel spec + regen:
- crit-2: signal-return frame stored the saved kont under :f but the reader
  looked up "saved-kont" — handler value became the whole program's result
  and the covering test passed vacuously. Fixed; raise-continuable now also
  resumes at the raise site (rest-k, not unwound-k), mirroring signal-condition
- quasiquote: R7RS longhand unquote-splicing aliased to splice-unquote
  (used to serialize literally — silent zero-splice)
- guard: re-raise sentinel gensym'd per execution (was forgeable by any
  (list '__guard-reraise__ x) value)
- do: IIFE-head form no longer misparses as a Scheme do-loop
- render: area/base/embed/param/track added to HTML_TAGS (were void-only
  and rendered as Undefined symbol)
- REGEN REPAIR: checked-in sx_ref.ml carried hand-written additions that
  every regeneration silently lost (let-values/define-values/delay/
  delay-force registrations, AdtValue define-type) plus 5 regen blockers
  (arrow-name mangling, 3-arg get, &rest defines, HO-position helper refs,
  transpiler prim-table gaps). Moved into bootstrap.py FIXUPS/skips and the
  transpiler prim table — regen is now reproducible, compiles, and tests
  at baseline (CI Dockerfile.test steps 3-4 could not previously have
  produced a compiling kernel)

Primitives:
- contains?: dict key-check arm per its spec doc
- expt: promotes to float on int63 overflow ((expt 2 100) returned 0)
- mcp_tree parity with sx_primitives: get (Integer indices + 3-arg default),
  split (literal substring, was char-class — the historical gotcha lived
  here), empty? on ""/{}, contains?, equal?, keyword-name, char-code
  (Integer), parse-number (Integer-aware)

Python/docs:
- shared/sx/boundary.py: dead validation now logs a one-time WARNING instead
  of silently no-oping (full revival gated: tier-1 declarations deleted and
  SX_BOUNDARY_STRICT=1 is live in production compose)
- CLAUDE.md: canonical reference now points at spec/*.sx; island authoring
  rules corrected (let IS sequential, bodies ARE implicit begin)

Verification: full suite 5762 passed / 274 failed — fail set byte-identical
to the pre-change baseline (273 in-progress hs-* + pre-existing r7rs radix
shadow). All repros verified fixed on both the native binary and the rebuilt
WASM browser kernel. Review findings: /tmp/sx-review/*.md

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-03 13:49:43 +00:00
parent 071c2f9a8a
commit dc7aa709bd
14 changed files with 3445 additions and 3213 deletions

View File

@@ -1312,7 +1312,12 @@
(= (type-of item) "list")
(= (len item) 2)
(= (type-of (first item)) "symbol")
(= (symbol-name (first item)) "splice-unquote"))
;; ,@ parses to splice-unquote; accept the R7RS longhand
;; unquote-splicing too (it used to serialize literally —
;; a silent zero-splice)
(or
(= (symbol-name (first item)) "splice-unquote")
(= (symbol-name (first item)) "unquote-splicing")))
(let
((spliced (trampoline (eval-expr (nth item 1) env))))
(if
@@ -1395,7 +1400,9 @@
(let
((result (apply producer (list))))
(if
(and (dict? result) (get result :_values false))
;; 2-arg get: absent key → nil (falsy) — the explicit false default
;; was redundant AND the OCaml transpiler only emits 2-arg get
(and (dict? result) (get result :_values))
(apply consumer (get result :_list))
(apply consumer (list result))))))
@@ -1415,7 +1422,7 @@
(let
((result (trampoline (eval-expr val-expr local))))
(let
((vs (if (and (dict? result) (get result :_values false)) (get result :_list) (list result))))
((vs (if (and (dict? result) (get result :_values)) (get result :_list) (list result))))
(for-each-indexed
(fn
(idx name)
@@ -1442,7 +1449,7 @@
(let
((result (trampoline (eval-expr val-expr env))))
(let
((vs (if (and (dict? result) (get result :_values false)) (get result :_list) (list result))))
((vs (if (and (dict? result) (get result :_values)) (get result :_list) (list result))))
(for-each-indexed
(fn (idx name) (env-bind! env (symbol-name name) (nth vs idx)))
names)
@@ -1699,7 +1706,9 @@
(body (rest args))
(var (first var-clauses))
(clauses (rest var-clauses))
(sentinel (make-symbol "__guard-reraise__")))
;; gensym: fresh tag per guard execution — a user value shaped
;; (list '__guard-reraise__ x) must not be mistaken for a re-raise
(sentinel (gensym "__guard-reraise__")))
(step-eval-list
(list
(quote let)
@@ -1841,12 +1850,18 @@
("io" (step-sf-io args env kont))
("begin" (step-sf-begin args env kont))
("do"
;; do-loop only when it really looks like Scheme do:
;; every element of the first form is a (var init [step])
;; list AND a test-clause list follows. A begin-style
;; (do ((fn (x) x) 5) ...) — IIFE head — used to misparse
;; as a binding list.
(if
(and
(not (empty? args))
(>= (len args) 2)
(list? (first args))
(not (empty? (first args)))
(list? (first (first args))))
(every? (fn (b) (list? b)) (first args))
(list? (nth args 1)))
(let
((bindings (first args))
(test-clause (nth args 1))
@@ -2056,7 +2071,9 @@
(args env)
(let ((thunk (make-lambda (list) (first args) env))) {:_iterative true :forced false :value nil :thunk thunk :_promise true})))
(define promise? (fn (v) (and (dict? v) (get v :_promise false))))
;; 2-arg get throughout: absent key → nil (falsy) — explicit false/nil
;; defaults were redundant AND the OCaml transpiler only emits 2-arg get
(define promise? (fn (v) (and (dict? v) (get v :_promise))))
;; ═══════════════════════════════════════════════════════════════
;; Part 11: Entry Points
@@ -2075,12 +2092,12 @@
(not (promise? p))
p
(if
(get p :forced false)
(get p :value nil)
(get p :forced)
(get p :value)
(let
((result (apply (get p :thunk nil) (list))))
((result (apply (get p :thunk) (list))))
(let
((final (if (and (get p :_iterative false) (promise? result)) (force result) result)))
((final (if (and (get p :_iterative) (promise? result)) (force result) result)))
(dict-set! p :forced true)
(dict-set! p :value final)
final))))))
@@ -2971,12 +2988,15 @@
((registered
(get (env-get env "*adt-registry*") type-name)))
(when
(and registered (not (some match-clause-is-else? clauses)))
;; eta-expanded: a bare helper in HO position transpiles to
;; cek_call on an OCaml function (type error); a lambda body
;; calling it directly emits a plain application
(and registered (not (some (fn (c) (match-clause-is-else? c)) clauses)))
(let
((clause-ctors
(filter
(fn (n) (not (nil? n)))
(map match-clause-ctor-name clauses))))
(map (fn (c) (match-clause-ctor-name c)) clauses))))
(match-warn-non-exhaustive
env type-name registered clause-ctors)))))))))
@@ -4507,8 +4527,12 @@
rest-k)))))
("restart" (make-cek-value value env rest-k))
("signal-return"
;; the frame constructor stores the caller's kont under :f
;; (natively cf_f) — reading "saved-kont" returned nil and
;; silently discarded the whole continuation outside the
;; signal site (handler value became the program's result)
(let
((saved-kont (get frame "saved-kont")))
((saved-kont (get frame "f")))
(make-cek-value value (get frame "env") saved-kont)))
("comp-trace" (make-cek-value value env rest-k))
("cond-arrow"
@@ -4565,8 +4589,12 @@
(list condition)
(if
continuable?
;; R7RS: the handler's value returns to the RAISE SITE,
;; so save rest-k (the kont at raise time) — unwound-k
;; has already dropped the frames between the raise
;; site and the handler (mirrors step-sf-signal)
(kont-push
(make-signal-return-frame fenv unwound-k)
(make-signal-return-frame fenv rest-k)
unwound-k)
(kont-push
(make-raise-guard-frame fenv unwound-k)

View File

@@ -160,7 +160,15 @@
"template"
"slot"
"dialog"
"menu"))
"menu"
;; void elements must ALSO be here — render routes a head symbol to
;; the element renderer only when it's in HTML_TAGS; these five were
;; only in VOID_ELEMENTS and fell through to "Undefined symbol"
"area"
"base"
"embed"
"param"
"track"))
;; Self-closing tags (br, img, hr, etc.)
(define