fix(check): purity check now descends into js/export wrappers#4
Merged
Conversation
check-purity! only descended into list-shaped wrapper forms, but js/export and js/export-default parse to jst-export(-default) STRUCTS, not lists. So every EXPORTED defn — the public API, exactly what you most want the purity guarantee on — silently escaped the !-effect check. Descend into jst-export / jst-export-default / with-meta wrappers. Active suite 1377/1377. Surfaces real leaks in downstream corpora (gjoa: 27 exported factory fns that mutate without a ! marker) — those are the check working as intended.
tompassarelli
added a commit
that referenced
this pull request
Jun 17, 2026
The concrete crown jewel, tying moves 1-3: a scope-correct rename performed as a CLAIM EDIT on the canonical Fram store, exported byte-stable, recompiled. - bin/test/code-as-claims/rename-in-store.clj: ports chartroom's Turtle #4 (self-contained in beagle's repair toolchain). Loads files' lossless claims into ONE Fram store; renames a symbol in the target module(s) by SUPERSEDING its `v` claims (claim-native, recoverable); the same-named symbol in other modules is untouched by construction (scope is structural, not lexical). Collision invariant: refuses a rename onto an existing binding (exit 3, no claims mutated). - bin/test/code-as-claims/rename.sh (gate): rename mod_a's `helper` -> safe-add; proves mod_a renamed (def + caller), mod_b's identical `helper` UNTOUCHED, renamed tree RECOMPILES clean, collision refused. Verified the killer property directly: `helper` inside a STRING is left intact (a text sed corrupts it; the graph edit, operating on symbol leaves, does not). Wired into CI. Honest scope: module-local rename (def + callers in the module); cross-module collision is correct; intra-module local shadowing + cross-file rename-CASCADE are documented follow-ups. Uses the move-3 fixes (number? node-ref test in the loader).
tompassarelli
added a commit
that referenced
this pull request
Jun 18, 2026
…ariants) rename.sh §10: an unquote nested inside a quote inside a template still escapes+renames; a nullary (bare-symbol) defunion variant is renameable. delete.sh §6-7: deleting a defunion with a live variant-ctor ref is refused (orphan scan keyed on the deleted subtree); an unused parameterized union head is deletable.
tompassarelli
added a commit
that referenced
this pull request
Jun 21, 2026
…HAMT Closes the last soundness hole-class (#4 runtime dedup) from beagle-2's boundary map. The runtime collection builders no longer emit native ARRAYS for sets (a pre-existing set-builder bug) and route to value-dedup HAMT when the element type is compound: coll-kind helper: 'set | 'vec | 'map | 'unknown, seeing through conj/into/disj to the underlying collection (so a native Set/conj-result isn't mistaken for an array). conj : value-set -> hamtSetAdd fold ; native set -> new Set([...c, x]) (a Set, not [...c,x]) ; vec -> [...c,x]. into : value-set -> xs.reduce(hamtSetAdd, target) ; native set -> new Set([... target, ...xs]) ; vec -> spread. count : native branch now dispatches on coll-kind -> set:.size / map:Object.keys ().length / else:.length, so (count (conj #{..} x)) is .size not .length (was undefined). frequencies : compound elements -> value-keyed hamtMap (was native object -> "[object Object]" collisions). group-by left native (its key type comes from f's return, not reliably inferable — documented residual). Verified vs Clojure shape: conj/into on scalar+compound sets (3/1/4/2), frequencies compound/scalar (2/2). rep-soundness +4 = 24/24; active 1484/1484, gated JS (emit-js 147, exec-oracle 7, ...) + behavioral 78 green. ALL 6 hole-classes now closed: #1 records, #2 unions, #3 escape-across-fn, #4 runtime dedup, #5 heterogeneous, #6 cross-rep equality.
tompassarelli
added a commit
that referenced
this pull request
Jun 21, 2026
beagle-2's corpus caught the residual: my first #4 routed conj/into on the TARGET set's rep, so an EMPTY `#{}`, a native scalar set, or a compound set LITERAL target (all classify native) left `(into #{} compound)` / `(conj #{} compound)` as a native `new Set` -> ref-dedup -> NO value-dedup. `(set X)` worked but `(into #{} X)` — the idiomatic dedup-to-set — did not. Fix: route conj/into on the ELEMENT type (conj-hset? / into-hset?), coercing the target to a hamtSet (emit-set-as-hamt) when an element is not provably-scalar: - conj-hset?: target is a set AND (target already hset OR some conjed elem not provably-scalar) -> hamtSetAdd fold over hamtSet(target). - into-hset?: target is a set AND (target hset OR xs's element type not provably- scalar) -> xs.reduce(hamtSetAdd, hamtSet(target)). - into a VECTOR stays a native array (not over-promoted); scalar set -> native Set. classify-rep + count now see conj/into results as hset. Verified vs Clojure: (into #{} [[1 2][1 2][3 4]])=2, (conj (conj #{}{:a 1}){:a 1}) =1, (into #{[1 2]} [[1 2][3 4]])=2, into-vector stays 3, scalar-set into=4. rep-soundness +4 element-driven gates (empty/literal-target + vec-not-promoted) = 28/28; active 1488/1488, gated JS + behavioral green. #4 now fully closed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
check-purity! skipped
jst-export/jst-export-defaultstructs (only list-shaped wrappers were descended), so exported functions — the public API — escaped the!-effect check entirely. Now descends into export + with-meta wrappers.Active suite 1377/1377. Surfaces genuine leaks downstream (gjoa: 27 exported
make-*factory fns that mutate without a!marker) — the check doing its job.