Release planning

CadCraft 1.0 impact map

Every fix and feature lined up for 1.0, and what each one touches: which apps, and which layers of the stack.

ProjectCadCraft
Sheet0.10 impact
Items110
Date2026-11-09
51Fix
22Feature
0Spike
28Platform
0Site ops
8Sanity check
1Decision
≈ 319days in the written 0.10 plans
  • Planned 101 on the plan for the release it is filed under
  • Built 0 built and working in /lab (the dev builds). Whether the public has it is its reach: "in the lab", "Live" (the last gate) or "switched off"; see docs/REACH-LIVE.md
  • Partly built 1 part of it is built in /lab; the rest waits (see "Waits on")
  • Promised 0 the live site already says it is coming
  • Moved 0 moved here from an earlier release or wishlist
  • Found 0 bug found after a release shipped; fixed in a point release or the next one
  • Candidate 3 recommended in a spike or session log; owner to choose
  • Community please test 0 built and in /use; a Discord post gives the steps and the expected result and asks people to try it on their own devices, files or know-how
  • Decision Time 5 a decision, check, file, key, flag or go only you can give; folds into the list once answered

Built means built and working in the lab. Live means the public can use it, and is the last gate: 0 Built · Live, 0 Built · in the lab.

Sheet 1 · impact areas

Where 0.10 lands

How many items touch each app and each layer, by kind. One item can touch several areas, so the rows add up to more than the item count. Pick a row to filter the schedule below.

Impact areaFixFeatureSpikePlatformSite opsSanity checkDecisionAll
Apps
CadCraft 20 17 – 10 – 3 1 51
BimCraft 18 20 – 9 – 4 1 52
ModelCraft 19 20 – 10 – 3 1 53
MarkupCraft 12 16 – 9 – 3 1 41
TakeoffCraft 14 16 – 9 – 3 1 43
CivilCraft 21 18 – 10 – 4 1 54
DetailCraft 15 17 – 9 – 4 1 46
PartCraft 15 17 – 9 – 3 1 45
Studio – 1 – – – – – 1
SuperCad Studio – 6 – 6 – 1 1 14
Layers
Site and services 13 3 – 8 – 1 – 25
Desktop host – 1 – 1 – – – 2
App shell 5 15 – 8 – 2 1 31
App code 23 4 – 3 – – – 30
Our engines 8 – – 1 – 1 – 10
Vendor engines – – – – – – – 0
Drawing surface 3 – – – – – – 3
Help and words 2 1 – – – 1 – 4
Tests and tooling 2 1 – 2 – 3 – 8

Sheet 2 · schedule

Every fix and feature

Grouped by track. App chips carry each app’s layer colour; Studio is in ink. "Waits on" names what has to happen first; the small lines under it say who asked and where the item is written up.

Add an item Fork map (Rust and C++ forks) The roadmap

Issues sync every 10 minutes; last sync Nov 9, 2026 7:00 pm.

ItemWhatAppsLayersStatusDaysWaits onIssues
Performance roadmap — wave 3 60
FixPERF.21 Link to PERF.21Comment on PERF.21 Scale: ModelCraft, DetailCraft, PartCraft and CivilCraft snapshot the whole document as JSON on every undo step and autosave

Measured: a 400 MB glTF (5 M faces) imports into ModelCraft in 17 s and 2.5 GB, then the FIRST edit calls model.js:2408 snap() (also :2223, :2230, :2272, :2331): 407 MB of JSON, 35.6 s, 9 GB peak, so the tab dies after a successful import. At 500k faces the snapshot is 3.1 s per edit. Same shape in DetailCraft model.js:169-199 (stringify before AND after every change), PartCraft part.js:504-517 and CivilCraft project.js:155. Change: command or diff undo (or per-definition dirty snapshots), meshes kept as typed buffers instead of base64 inside JSON, autosave payloads as Blobs of those buffers (the shell's IndexedDB store already takes Blobs). ModelCraft first. Extends PERF.7 with the 10x and 100x numbers.

ModelCraftCivilCraftDetailCraftPartCraft App code Planned 10 – docs/0.9-sessions/perf-wave3/
FixPERF.23 Link to PERF.23Comment on PERF.23 Scale: cc-ifc copies the file for the worker and keeps every mesh in memory; save is O(lines) (extends PERF.8, PERF.10)

Measured: the index scans 1.9 GB in 15.9 s and 3.3 GB in Node (linear, typed arrays, lazy per-line parse: the healthy template for the other readers). But open-async.js:33 data.slice() doubles the bytes on the page (ceiling about 1 GB in a tab), open plus geometry plus save holds 12.8 times the file (2.57 GB at 200 MB; in the browser the heap grows about 1 GB per 100 MB of IFC: 210 MB peaked at 1.99 GB, 1 GB would need 10 GB), and save() walks every line with three chunks each (model.js:232-256, 8.8 s at 115 MB). Change: transfer the caller's buffer or let the worker read from the Blob; stream meshes to GPU buffers with an LRU instead of keeping all triangles in JS; save by coalesced byte ranges into a writable stream. Pairs with the BimCraft geometry-off-the-page-thread row (PERF.8).

BimCraftDetailCraft Our engines Planned 4 – docs/0.9-sessions/perf-wave3/
FixPERF.24 Link to PERF.24Comment on PERF.24 Scale: autosave rewrites the whole document every couple of seconds of editing, in every app (extends PERF.5)

Measured and read: CadCraft autosave writes a full DWG (dwg-autosave.js:39 saveDwg; 4.8 s today, 66 s at 10x), BimCraft the full IFC bytes (bimcraft.js:585 to model.js save), MarkupCraft the whole PDF Blob (markupcraft.js:4715; TakeoffCraft stops at 64 MB, takeoffcraft.js:4313), ModelCraft the JSON (modelcraft.js:3883). On a big file autosave is the longest task the person sees while working. Change: journal autosave, keeping the opened file's bytes or handle plus the edits map (IfcFile already holds exactly that in memory), snapshots taken in a worker from transferred buffers, and a clear cap with a message above a size. Shared piece in the shell's app-autosave.js, one snapshot function per app.

CadCraftBimCraftModelCraftMarkupCraftTakeoffCraftCivilCraftDetailCraftPartCraft App shellApp code Planned 4 – docs/0.9-sessions/perf-wave3/
FixPERF.36 Link to PERF.36Comment on PERF.36 DetailCraft: every edit recomputes everything: autopilot, a quadratic clash check, a full scene of 1,925 draw calls, two JSON snapshots, linear model scans

Measured in /use 0.8.1 and in Node (tools/perf/apps3d-detailcraft.mjs, apps3d-detail-node.mjs; 2,820-member grid24x12x5 and the new 10x grid48x30x10 with 28,500 members and 100x grid60x60x40 with 285,600 members): refresh() (detailcraft.js:354) after any change runs build 151 ms, markParts 261 ms, autopilot.propose 899 ms (18 times more for 6.2 times the members), clashCheck 10.2 s (quadratic), and rebuilds the whole three.js scene with a Mesh plus an EdgesGeometry per part (1,925 draw calls for 948 parts); Model.change stringifies the state twice (model.js:165/170; 20 MB at 10x, 197 MB at 100x) and undo re-parses all of it; Tx.addHole and addConnection find by find/some (model.js:349, :376), so pasting the 10x model took 2.6 s and building the 100x file 716 s. Change: incremental build by id, patch-based undo (the whole-document snapshot row), an instanced or merged scene, id maps in the model API, clash check through a grid. Healthy: build, marks, drawings and IFC export are near linear.

DetailCraft App code Planned 6 FX.144 (the constructability hang) first, or nothing above 456 members can be measured in the browser. docs/0.9-sessions/perf-wave3/
FixPERF.51 Link to PERF.51Comment on PERF.51 Apps: 56 to 69 % of the JavaScript fetched at start never runs; dynamic import boundaries per command, workspace and file format (extends PERF.4)

Measured with V8 precise coverage on the current releases (tools/perf/addons-coverage.mjs): 6.4 MB (CadCraft) to 10.2 MB (BimCraft) of decoded JS per start, 363 to 492 modules, and after 10 s of idle plus a first command only 31 to 44 % of those bytes have executed (CadCraft 33 %, PartCraft 35 %, DetailCraft 44 %); interaction adds 1 to 2 %. Every file of the entry's static import closure is fetched (284/284 in CadCraft, 432/432 BimCraft). Per app 23 to 33 modules ran under 15 %: 0.3 to 1.1 MB each app, for example the whole DWG/DXF save path in CadCraft (dxf.js 95 KB at 8 %, dwg/write.js 58 KB at 8 %, dxf2000.js, lisp/*), ModelCraft io.js 106 KB at 11 %, PartCraft ws-illustrate 96 KB, sketchview 79 KB, ws-script 66 KB, BimCraft res-ui 107 KB, CivilCraft eg-ui 47 KB and the river/rail/water UIs. modulepreload (PERF.4) hides latency but does not cut bytes. Change: import() boundaries at the command, workspace and file-format level (the command registry already knows each command's module, input-<slug>.js); start with the entry, the view, the model and the toolbar; keep modulepreload for what stays static. Target: 1.5 to 3 MB less to download, parse and compile before the first drawing.

CadCraftBimCraftModelCraftMarkupCraftTakeoffCraftCivilCraftDetailCraftPartCraft App code Planned 10 – docs/0.9-sessions/perf-wave3/
FixPERF.52 Link to PERF.52Comment on PERF.52 Apps: the add-on loader is serial and imports every add-on's help at activation: 13 add-ons on = 235 requests, 3.0 MB, 2.5 s of steps

addons.js (the shell copy synced into every build) awaits each bundled addon.json in turn (:390-396; 13 in CadCraft, default-off ones too), activates one after another (:381) and imports each add-on's help module at activation (:572-589; Electrical's 58 KB every start). Measured with 13 add-ons on (tools/perf/addons-load.mjs): 235 requests, 3.0 MB, 2,552 ms of serial steps, longest step 382 ms; in parallel the same work is about 0.4 s. Baseline CadCraft with nothing on: 13 serial addon.json round trips, about 0.9 s on a real line. Change: put every bundled manifest into addons/index.json at release time (one request) or Promise.all the discovery, import all entries in parallel and activate() in index order, import help lazily from api.help on F1.

CadCraftBimCraftModelCraftMarkupCraftTakeoffCraftCivilCraftDetailCraftPartCraft App shell Planned 3 – docs/0.9-sessions/perf-wave3/
FixPERF.55 Link to PERF.55Comment on PERF.55 Apps: 84 to 95 % of the language keys shipped on an app page are never read; split the catalog by namespace (extends PERF.2)

Instrumented t() on the current releases: CadCraft ships 8,818 keys / 529 KB in #cc-lang, 442 are read by the end of idle and 1,024 after a first command; 70 namespaces (198 KB) are never touched in the first minute (frame 32 KB, m3d 29 KB …); BimCraft 117 namespaces / 300 KB untouched; PartCraft's script namespace 1,140 keys / 73 KB with 13 read; the shell's 1,515 keys / 82 KB with about 300 read in every app. Parsing is cheap (5 to 11 ms); the bytes are the cost, 400 to 500 KB of every page view. Change: split the catalog by namespace at build time (tools/lang), inline shell plus the entry's namespaces, load a namespace on the first t() miss or together with the module that owns it (the dynamic-import row gives the boundary); Lang::merged() key prefix is the split key.

CadCraftBimCraftModelCraftMarkupCraftTakeoffCraftCivilCraftDetailCraftPartCraft Help and wordsApp shell Planned 3 – docs/0.9-sessions/perf-wave3/
FixPERF.56 Link to PERF.56Comment on PERF.56 Apps: the shell costs 154 to 257 ms of main thread per start, and three document-wide MutationObservers fire 100+ times a second while drawing

Measured per app: app-phone.js 17 to 56 ms running the phone layout on a desktop viewport; app-boot.js 10 to 73 ms with a body-subtree MutationObserver whose say() rewrites the DOM on every mutation (app-boot.js:82-83) and never settles while the pointer moves (141 to 221 boot-step rewrites in 3 s of drawing); three observers cascade (app-shell.js:477-482, app-phone.js:421 and :483; CadCraft 360 observer callbacks in 3 s of drawing); app-settings.js 15 to 65 ms; app-devices-ui.js 26 ms. Shell bytes: 1.35 MB fetched, about 780 KB never run (app-addons-account.js 59 KB when signed out, calc.js, ai-*.js, app-devices-ui.js, app-propgrid.js). Change: app-phone returns after the matchMedia check on wide pointer-fine screens and attaches its observers only when cc-phone is on; app-boot ignores mutations inside output-like elements and disconnects on settle; app-shell observes dialog additions only (or an attributeFilter without childList on the whole tree); dynamic-import addons-account, calc, ai-*, devices-ui on first use.

CadCraftBimCraftModelCraftMarkupCraftTakeoffCraftCivilCraftDetailCraftPartCraft App shell Planned 3 – docs/0.9-sessions/perf-wave3/
FixPERF.60 Link to PERF.60Comment on PERF.60 Apps: the help index is one 1.9 MB module imported on F1 (52 locale copies = most of the 103 MB offline help; extends PERF.15)

CadCraft help/help.js is 1.9 MB (BimCraft 1.7 MB), parsed whole on the first F1; the 52 compiled locale copies at 2.1 to 2.2 MB each are most of the 103 MB help folder that "Save for offline" downloads. Change: compile help as an index (titles, search terms) plus one chunk per topic group, load a topic on open; offline saves the index for every language and topics for the chosen one. bin/build-help.php, help-panel.js.

CadCraftBimCraftModelCraftMarkupCraftTakeoffCraftCivilCraftDetailCraftPartCraft Help and words Planned 3 – docs/0.9-sessions/perf-wave3/
FixPERF.71 Link to PERF.71Comment on PERF.71 Code: eight app mains are one closure of 2,500 to 5,800 lines each; split by topic

The AST pass over 5,500 modules (204,968 functions) found each app's main a single init closure: bimcraft init 5,759 lines with 1,557 nested functions, markupcraft 4,755, takeoffcraft 4,509, modelcraft 3,858, partcraft 3,770, cadcraft 2,898, civilcraft 2,848, detailcraft start 2,478. Everything inside shares one scope, so nothing can be dynamically imported (the start-up row), tested alone or reasoned about; this is the single biggest piece of sloppy structure the owner asked about. The report (docs/0.9-sessions/perf-wave3/code-quality.md Q1) outlines each by its inner functions and proposes modules per topic (BimCraft: IFC open/save, fd, QTO, find, clash, props, nine context modules; the same per app). L per app, M for CadCraft; one app per lab pass, behind the existing browser tests (tools/<app>-test, bin/test-<app>*.mjs).

CadCraftBimCraftModelCraftMarkupCraftTakeoffCraftCivilCraftDetailCraftPartCraft App code Planned 20 – docs/0.9-sessions/perf-wave3/
FixPERF.72 Link to PERF.72Comment on PERF.72 Code: 4,300 one-line functions that only pass arguments on (forwarder adapters rebuilt per sub-UI, help: () => t() closures)

tools/perf/quality-forwarders.mjs: 2,299 forwarders plus 2,007 thin wrappers, 278 getters, 401 constant-return functions, 2,683 one-use helpers. Worst: civilcraft.js 182 forwarders (units: () => units(), say: (m) => say(m), row: (...x) => row(...x) rebuilt for four sub-UIs, lines 157-165), bimcraft.js 143, partcraft.js 117, landscape-ui.js 52 (the same {edit, LS, U} trio six times), commands.js 147 help: () => t('…') closures plus seven ask wrappers. This is read and parse cost and noise, not runtime (inlined). Change: pass the declarations by name, one adapter object per app shared by its sub-UIs, help: 'key' resolved at display time. S per file.

CadCraftBimCraftCivilCraftDetailCraftPartCraft App code Planned 3 – docs/0.9-sessions/perf-wave3/
FixPERF.73 Link to PERF.73Comment on PERF.73 Code: copies that drifted and hand copies with no sync tool (six inflate implementations, xlsx.js in four variants, input-mouse.js in eight apps)

The byte-copy system works where a tool owns the copy (1,011 of 1,043 copy groups). Outside it: inflateRaw plus deflateZlib byte-identical in lib/3dm and lib/chip, six inflate implementations in lib and crc32 in 316 files (one lib/zip); xlsx.js six times in four hand-edited variants (one lib/xlsx); pdf-snap.js and takeoffcraft snap.js diverged; lib/walls ahead of bimcraft wall-square.js (two solvers for one wall); 31 drifted file pairs in all; whole-file hand copies with no tool: input-mouse.js in eight apps, wheel-pad.js in five, panes.js in three, params.js, framing.js, bcf.js, render-tools.js, carbon-factors.js, keynotes.js; CadCraft's m3d copy stale because its tool refuses to run (FX.143). Change: one owner per copy (lib/ or the shell) with a sync tool, and bin/test-sync-copies.mjs that runs every sync tool with --check (130 tools; some write without the flag: give them one first). Also the STEP string helpers S/E/L/B typed into five IFC writers (civilcraft three, detailcraft, partcraft) belong in lib/ifc/step.js.

CadCraftBimCraftModelCraftMarkupCraftTakeoffCraftCivilCraftDetailCraftPartCraft App codeTests and tooling Planned 4 – docs/0.9-sessions/perf-wave3/
FixPERF.74 Link to PERF.74Comment on PERF.74 Code: shared utilities written hundreds of times: vector math 2,088 implementations, csv cell 431, point-in-polygon 243, esc 198, uid 188, download 163

tools/perf/quality-utils.mjs counted 6,943 implementations of 24 utility names (21,260 lines): vector math 2,088 (516 distinct bodies in 1,094 files), area 299, point-in-polygon 243 (identical clusters of 17, 12, 9, 8 and 6), bbox 190, lerp 69, clamp 68; text and bytes: esc 198, uid 188, download/saveBlob 163, csv cell 431, base64 59. The shell exports none of these; lib/units is the one shared table that is used everywhere. Change: lib/geom2d (vec, bbox, area, point-in-polygon, lerp, clamp) and a shell util.js (esc, uid, download, csv, base64) synced into the builds like host.js; mechanical replacement guided by the cluster list, with the quality scripts kept as the check so the count only goes down. Also shell-mount.js (a 48-line block identical in seven apps) and ui-util.js (eight variants of one 45 to 76 line file) become synced shell files, and the Studio Live stateCore plus diff and invert hand-copied in three apps and joinFromPage in six become a shell live-state.js.

CadCraftBimCraftModelCraftMarkupCraftTakeoffCraftCivilCraftDetailCraftPartCraft App codeApp shell Planned 6 – docs/0.9-sessions/perf-wave3/
FixPERF.75 Link to PERF.75Comment on PERF.75 Code: deep clones by JSON round trip (205 sites, some inside merge loops) and 24 timers polling faster than once a second

Perf-relevant sloppiness confirmed by reading the top sites. Clones: 205 JSON.parse(JSON.stringify()) plus 112 clone helpers; takeoffcraft/a Discord member/learn.js clones inside merge loops (11 sites), clipboard.js and dwg-keep.js clone document-sized objects: structuredClone or a cloneDoc that copies only what changes. Timers: cadcraft live.js:763 and markupcraft live-ui.js:71 setInterval(push, 33) build presence and JSON.stringify it 30 times a second while idle; hazard-ui and people-ui animate with setInterval 33; countall-ui waits on a worker with a 20 ms poll; plc-ui 50 ms; 250 to 500 ms pollers elsewhere. Change: event-driven pushes with a dirty flag, requestAnimationFrame for animation, promise-based worker replies; a check that lists any setInterval under 1 s. Also: a console.log in bimcraft fem/frame-push.js:155 inside the solver loop, 12 new RegExp inside loops (takeoffcraft xlsx-read.js:74 on file open).

CadCraftBimCraftModelCraftMarkupCraftTakeoffCraftCivilCraftDetailCraftPartCraft App code Planned 2 – docs/0.9-sessions/perf-wave3/
FixPERF.78 Link to PERF.78Comment on PERF.78 Code: dead modules and exports, god functions and cyclomatic hotspots beyond the mains

Confirmed dead: cadcraft addons/windows/lib/cutlist/cutlist.js (190 lines, a stale copy nothing imports), addons/trusscraft/lib/layout.js in four apps (tests only), modelcraft anim/skin-gpu.js, compute/parity.js twice, partcraft eng-data and eng-units stubs; 32 dead exports (about 54 KB: partcraft worker solvers, the openfoam runner) plus 26 orphan modules (about 141 KB). Name-level dead code across the 5,020 namespace and dynamic imports needs a bundler dry run (one tree-shake pass per app as a report, nothing shipped bundled). Hotspots: 120 functions over 300 lines and 618 with cyclomatic complexity 40 or more: readFamily 234, writeDwg 769 lines at 214 (plus convert 195, buildModelParts 182), vla-host get 207, validateItem 194, buildSite3D 190, dxf entityFrom0 187 and writeDxf 159, pdf-write writeMarkedUp 182, writeCivilIfc 702 lines, model-ui mountModel 1,804 lines, res-ui 1,407, shell app-panels 1,742 and app-settings 1,521. Change: table-driven dispatch for the entity switches, one module per panel section, delete the dead files; each behind the existing tests.

CadCraftBimCraftModelCraftCivilCraftDetailCraftPartCraft App code Planned 4 – docs/0.9-sessions/perf-wave3/
Suggest an item

Anyone can send one; no account and no name needed. The team reads each idea, and the ones we take on appear on this page. Please leave out web addresses.