diff --git a/plans/agent-briefings/sx-gate-loop.md b/plans/agent-briefings/sx-gate-loop.md index dd2cc4aa..f767dc6d 100644 --- a/plans/agent-briefings/sx-gate-loop.md +++ b/plans/agent-briefings/sx-gate-loop.md @@ -46,7 +46,7 @@ Pin each confirmed-and-fixed finding with a minimal repro. Add suites to - [x] K18 [W7] — `expt` overflow now float-promotes (no 63-bit wrap) - [x] K20 [W7] — `contains?` now supports dict key membership -- [ ] K09/K11/K39 [W5] — landed special-form fixes, pin each +- [x] K09/K11/K39 [W5] — longhand `unquote-splicing`, guard sentinel gensym, `do` IIFE-head - [ ] K49 [W8] — render depth/cycle guard (infinite recursive component) - [ ] crit-2 [W1] — signal-return frame key (verify the pin is non-vacuous) - [ ] C1/C1b [W3] — HTTP-mode concurrency fixes, pin @@ -77,6 +77,16 @@ Pin each confirmed-and-fixed finding with a minimal repro. Add suites to ## Progress log (newest first) +- 2026-07-03 — **K09/K11/K39 W5 special-form pins (item A.3)**. Three suites + added to `spec/tests/test-gate-pins.sx`: `gate-K09-longhand-unquote-splicing` + (R7RS longhand `(unquote-splicing X)` now splices, incl. empty-list case; + shorthand still works), `gate-K11-guard-reraise-forgeable` (a body/clause + value shaped like `(list '__guard-reraise__ X)` is returned as data, not + misread as a re-raise — sentinel is now gensym'd), `gate-K39-do-iife-head` + (`(do ((fn (x) x) 5) 99)` → 99, not a misparsed do-loop — exact core.md + repro). Gotchas hit and fixed: quasiquoted bare idents are *symbols* not + strings, and `assert=` compares with `=` (not `equal?`, which returns false + on these spliced lists). 261 passed / 0 failed under OCaml run_tests. Test-only. - 2026-07-03 — **K20 contains?-dict pin (item A.2)**. Mapped K-codes by core.md severity order (K17 append!, K18 expt, K19 harness-drift, K20 contains?-dict). Added suite `gate-K20-contains-dict` to diff --git a/spec/tests/test-gate-pins.sx b/spec/tests/test-gate-pins.sx index 75c74d47..306f44db 100644 --- a/spec/tests/test-gate-pins.sx +++ b/spec/tests/test-gate-pins.sx @@ -9,6 +9,7 @@ ;; ;; TEST-ONLY: no semantics edits. If a pin fails, the fix regressed — do NOT ;; relax the assertion; investigate the evaluator/primitive change. +;; NB: assert= uses `=` (not `equal?`); compare lists with `=`. ;; ========================================================================== ;; -------------------------------------------------------------------------- @@ -56,3 +57,65 @@ (deftest "contains? still works on string substrings" (assert (contains? "hello" "ell")))) + +;; -------------------------------------------------------------------------- +;; K09 [W5, high] R7RS longhand (unquote-splicing X) silently no-spliced — +;; only shorthand ,@/`splice-unquote` was recognized, so the longhand +;; serialized literally (zero-splice). Fixed: aliased to splice-unquote. +;; Repro (core.md). +;; -------------------------------------------------------------------------- +(defsuite + "gate-K09-longhand-unquote-splicing" + (deftest + "longhand unquote-splicing splices a list" + (assert= + (quasiquote + (1 + (unquote-splicing (list 2 3)) + 4)) + (list 1 2 3 4))) + (deftest + "longhand unquote-splicing of an empty list contributes nothing" + (assert= + (quasiquote (0 (unquote-splicing (list)) 9)) + (list 0 9))) + (deftest + "shorthand splice-unquote still works" + (assert= + (quasiquote (a (splice-unquote (list 2 3)) z)) + (list (quote a) 2 3 (quote z))))) + +;; -------------------------------------------------------------------------- +;; K11 [W5, high] guard re-raise sentinel was a plain forgeable symbol — a +;; body/clause legitimately returning (list '__guard-reraise__ X) was +;; misread as a re-raise of X. Fixed: sentinel gensym'd per execution, so a +;; user value with that head is returned as data. Repro (core.md). +;; -------------------------------------------------------------------------- +(defsuite + "gate-K11-guard-reraise-forgeable" + (deftest + "body value shaped like the sentinel is returned as data" + (assert= + (guard (e (true "caught")) (list (quote __guard-reraise__) "hi")) + (list (quote __guard-reraise__) "hi"))) + (deftest + "clause returning the forged sentinel is not re-raised" + (assert= + (guard + (e (true (list (quote __guard-reraise__) "forged"))) + (error "boom")) + (list (quote __guard-reraise__) "forged")))) + +;; -------------------------------------------------------------------------- +;; K39 [W5, med] `do` misparsed a first form whose head is a list (an IIFE) +;; as a Scheme do-loop binding spec. Repro (core.md): (do ((fn (x) x) 5) 99) +;; threw "first: expected list, got 5"; expected 99. Fixed: `do` is begin. +;; -------------------------------------------------------------------------- +(defsuite + "gate-K39-do-iife-head" + (deftest + "do with an IIFE first form returns the last form (not a do-loop)" + (assert= (do ((fn (x) x) 5) 99) 99)) + (deftest + "do with a single IIFE form returns its value" + (assert= (do ((fn () 42))) 42)))