Jscad Cad
JSCAD v2 CAD renderer — evaluate CadScript main() and serialize to STL/DXF/SVG/OBJ/3MF. Includes jscad-codegen Claude Code skill.
2026.08.02.1
Real fix for the three remaining open latent bugs tracked in the LOCAL
jscad-cad-latent-bugs issue-lifecycle model (never a swamp.club Lab issue) —
B3 (MEDIUM), B4 (LOW), and B5 (LOW) — superseding the "still open" wording
recorded against them in the 2026.08.01.1 section below (which stays as
historical record). All three fixes live in the same trust-boundary Domain
Service (extensions/models/jscad/script_evaluator.ts) that B1/B2 hardened;
extensions/models/jscad_cad.ts and extensions/models/jscad/types.ts are
otherwise unchanged (types.ts's own Geometry.of([]) message is reused
verbatim, not duplicated logic).
- B3 (MEDIUM) — trailing JSON.parse corrupted by user console.log output,
fixed. The parent now extracts only the LAST non-empty line of the
subprocess's stdout and parses only that line for the
{objectCount}metadata (throwing a clear "no object-count metadata" error if no line parses); the generated eval script forcesDeno.exit(0)immediately after its own metaconsole.log, guaranteeing that line is the final one even against user-scheduled async output. A CadScript that itself callsconsole.log()ahead of returning geometry no longer corrupts the parse. - B4 (LOW) — silent objectCount 0 on an empty geometry array, fixed. The
generated eval script now guards an explicit
main() => []result withif (shapes.length === 0) { ... Deno.exit(1); }, surfacing the exact same "Geometry must contain at least one shape" message thattypes.ts's ownGeometry.of([])already throws (that guard is simply never reached on the subprocess path — it still isn't; the new guard duplicates its message, not its code). - B5 (LOW) — unbounded in-memory output, fixed. A new module constant
MAX_OUTPUT_BYTES(64 MiB default) is checked in the generated eval script BEFORE the binary/text output is allocated (binary: sum ofview.byteLength; text: sum ofp.length) — the cap rejects with a clear error, it never truncates.evaluateAndSerializegained an optional 5th positionalmaxOutputBytesparameter (mirrors the existingtimeoutMsprecedent: an internal module-constant default, not a user-facing arg); the parent adds a belt-and-suspendersDeno.statSync(outputPath).sizecheck beforereadFileSync, for defense-in-depth against a subprocess that wrote past the cap despite the guard. - N3 (accepted-residual, no code change). The
globalThis["Func"+"tion"]scanner-evasion trick that resolves the dynamic-dispatch constructor stays exactly as-is: it is the by-design execution mechanism for user CadScriptmain()(arbitrary-by-design — removing it removes the product), its blast radius is already contained by B1's scoped--allow-read/--allow-writeand B2'sAbortSignal.timeout, and the one tempting hardening (shadowingDeno/globalThisinside the user-code Function body) is rejected because it would flip the frozen B1 live-negative pin from aPermissionDeniedto aTypeErrorfor only marginal gain over the existing OS-level read scoping. Re-characterized (comment-only) injscad_cad_adversarial_test.ts's suite header and N3 pin section; the two N3 assertions and the underlying"Func" + "tion"/"${pkg}"source stay byte-identical. - New tests:
jscad_cad_adversarial_test.ts's B3/B4/B5 pins flip from characterizing-the-bug to characterizing-the-fix (B3: asserts success with the correctobjectCountinstead of aSyntaxError; B4: asserts the generated script now contains the empty-array guard instead of a silentobjectCount: 0; B5: asserts the generated script now embedsMAX_OUTPUT_BYTESinstead of asserting its absence) plus a new B5 parent-statSync-cap unit test.script_evaluator_test.ts(the SOLE live e2e suite) gains three new SYNTHETIC live cases proving all three fixes against a real subprocess, with no oversized fixtures: a B3 positive (console.log()then render succeeds), a B4 negative (main() => []is rejected with "at least one shape"), and a B5 negative (a real small render against a tinymaxOutputBytescap is rejected by the subprocess guard). The frozen argv/contract/N1/N2/B1/B2 pins across all five suites stay byte-identical/green — no argv shape change, no publicrun-method schema change. quality.yaml's ratchet stays UNSCORABLE baseline-0, honestly: the scorer's static import-specifier scanner still hard-errors on script_evaluator.ts's runtime-interpolatedimport * as serializer from "${pkg}";template literal — a separate
Notes trimmed at a line boundary to fit the registry's 4900-byte per-version cap. Full section: https://github.com/umag/swamp-workspace/blob/d850d8e8372d4c0008c9245959a090b37095de7a/jscad-cad/CHANGELOG.md
| Argument | Type | Description |
|---|---|---|
| script | string | JSCAD JavaScript defining main(params) → geometry |
Resources
Files
Release 2026.07.16.2 — align model versions with manifests
Maintenance release across the @magistr extensions. For most packages this
carries no functional change: the only edit is the model's version: field,
brought back in line with its manifest version so the published model type
version and the package version no longer drift.
Functional changes in this release are limited to:
anime-cron: normalizeTitle now strips a ": subtitle" suffix and a trailing parenthesized year before comparison, fixing dedup false-misses where the torrent title carries a subtitle or year that the AniList romaji does not.
arckit: first publish. Standalone ArcKit port — a 12-phase architecture governance state machine with 65 bundled templates, driven by a bundled skill.
Also tracks three extensions (kaiten, observability-agent, music-library) that previously existed only as untracked working-tree directories, recovered from stashes.
Added 1, removed 1 models
Merge pull request #4 from umag/extensions/magistr-grade-a-workspace
extensions: stage 15 @magistr extensions as Grade A workspace dirs + wire CI
Updated jscad-codegen skill: eval score 59%→100%. Fixed mandatory script rules, named coordinates, oversized cutters.
- Has README or module doc2/2earned
- README has a code example1/1earned
- README is substantive1/1earned
- Most symbols documented1/1earned
- No slow types (deprecated)1/1earned
- Dependencies pass trust audit2/2earned
- Has description1/1earned
- Platform support declared (or universal)2/2earned
- License declared1/1earned
- Verified public repository2/2earned