subagent transcript

session 17f09f89 · workflow wf_4e894f41
delegated task
You are working on the FreeCAD -> WebAssembly port. Ground EVERYTHING in the real code; do not guess. - Source tree: /home/magik6k/lcad-wasm/freecad-p
▸ Magik
You are working on the FreeCAD -> WebAssembly port. Ground EVERYTHING in the real code; do not guess. - Source tree: /home/magik6k/lcad-wasm/freecad-port/FreeCAD (git branch wasm-port). CMake build dir (out-of-source, already configured): /opt/toolchains/src/freecad-gui-build — read /opt/toolchains/src/freecad-gui-build/CMakeCache.txt for BUILD_* flags and dep paths. Build binary: bin/FreeCAD.wasm (~180 MB). - Toolchain: emscripten, ASYNCIFY + JS exceptions (-fexceptions), SINGLE-THREADED (no pthreads). Static CPython 3.14 + static Qt 6.11 (wasm) + static OCCT + static Coin3D. Everything is statically linked; there is NO dlopen — Python C-extension modules are registered statically via PyImport_AppendInittab in the generated file src/Main/WasmInittab.cpp / WasmInittabGui.cpp (see also freecadWasmRegisterInittab in src/Main/MainGui.cpp). - CRITICAL wasm constraints you MUST check for and design around: (1) THREADING HANGS: any std::thread ctor / std::async(default or async policy) / QThreadPool / QtConcurrent / QThread worker HANGS or throws (no pthreads). Fix pattern: force synchronous/deferred on __EMSCRIPTEN__ (e.g. std::launch::deferred), or #ifndef FC_OS_WASM guard the worker and run inline. Example already in-tree: Mod/Sketcher/App/planegcs/GCS.cpp forced std::launch::deferred; App/Application.cpp guards _recomputeThread with #ifndef FC_OS_WASM; Gui/ProgressBar.cpp skips qApp->processEvents() on __EMSCRIPTEN__. (2) MODAL DIALOGS / NESTED EVENT LOOPS: QDialog::exec()/QMessageBox::exec()/QEventLoop::exec() may deadlock under asyncify when driven from outside the main loop. Task-panel (docked, non-modal) dialogs are preferred. (3) Reentrant ccalls from JS during asyncify suspend corrupt state — a QTimer command-pump (/fc-cmd.py) exists for JS->Python. QtWebEngine has NO wasm port (Web workbench / in-app help / real Start page can't use it). (4) Wasm guards used in-tree: __EMSCRIPTEN__ (compiler-provided) and FC_OS_WASM (FreeCAD-defined) — both are defined on wasm. - Built modules today: Part, Sketcher, Material, Start (+ kernel). Everything else is OFF (see BUILD_* in CMakeCache). - PySide/shiboken: FREECAD_USE_PYSIDE=OFF, FREECAD_USE_SHIBOKEN=OFF, PySide6_DIR/Shiboken6_DIR NOTFOUND. Python 'PySide'/'shiboken6' import fails at runtime ("Cannot load shiboken6"). Many workbenches (Draft, BIM, most task dialogs) are Python+PySide and cannot function without it. DELIVERABLES — do BOTH: 1) Write a thorough, concrete, ready-to-apply plan to /home/magik6k/lcad-wasm/porting/<SLUG>/plan.md — include: exact CMake flags/edits, exact source edits (file + before/after or unified-diff snippets precise enough to apply), any NEW files (full contents), external-dependency build recipes (how to build each dep for this emscripten/asyncify toolchain), a threading-hazard list (grep the real files, give file:line + the exact construct + the exact fix), a modal-dialog-hazard list, the PyImport_AppendInittab lines to add, an ordered step list, and an effort/risk estimate. Put any patch files or new-file contents as separate files in that dir too. 2) Return the structured summary (the schema). Keep summary fields concise; the depth goes in plan.md. Your returned text IS the structured object. Be exhaustive and correct — token cost is not a concern. Verify claims against the actual files (use grep/read). If a dependency has no known wasm port, say so plainly and estimate the porting effort. === COMPLETENESS CRITIC === Reports produced: pyside-recipe, pyside-integrate, partdesign, draft, measure, spreadsheet-surface, import-io, mesh, fem, techdraw, small-python-mods, threading-audit, modal-audit, webengine-stub, missing-cmds-sketcher, pyside-verify. Read /home/magik6k/lcad-wasm/porting/ROADMAP.md and the plan.md files. What is MISSING from this parity analysis? Consider: build-system/size budget (180MB already), font/text rendering, undo-redo, preferences dialogs, the Python stdlib coverage in the packaged zip, unit system, clipboard, printing, translation/i18n, drag-drop, 3Dconnexion, any module or cross-cutting concern no agent covered, and whether any "config-only" verdict is over-optimistic. Write the gaps to /home/magik6k/lcad-wasm/porting/GAPS.md and return a concise bulleted list of the most important missing items.
Fable
thinking
I'm taking on the role of a completeness critic to review the ROADMAP.md and plan.md files in the porting directory, identify what gaps exist in the parity analysis, document those gaps in GAPS.md, and provide a concise summary of what's missing.
Fable
⚙ Bash {"command": "find /home/magik6k/lcad-wasm/porting -maxdepth 2 -name \"*.md\" | sort; echo \"---DIRS---\"; ls -la /home/magik6k/lcad-wasm/porting/", "description": "List porting reports and dirs"}
tool result
/home/magik6k/lcad-wasm/porting/ROADMAP.md
/home/magik6k/lcad-wasm/porting/draft/plan.md
/home/magik6k/lcad-wasm/porting/fem/plan.md
/home/magik6k/lcad-wasm/porting/import-io/plan.md
/home/magik6k/lcad-wasm/porting/measure/plan.md
/home/magik6k/lcad-wasm/porting/mesh/plan.md
/home/magik6k/lcad-wasm/porting/missing-cmds-sketcher/plan.md
/home/magik6k/lcad-wasm/porting/modal-audit/plan.md
/home/magik6k/lcad-wasm/porting/partdesign/plan.md
/home/magik6k/lcad-wasm/porting/pyside-integrate/plan.md
/h  …[truncated]
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/porting/ROADMAP.md"}
tool result
1	# FreeCAD → WebAssembly — Master Porting Roadmap
2	
3	Tree: `/home/magik6k/lcad-wasm/freecad-port/FreeCAD` (branch `wasm-port`)
4	Build dir (configured, out-of-source, Ninja + emscripten): `/opt/toolchains/src/freecad-gui-build`
5	Toolchain: emscripten 4.0.12, ASYNCIFY + JS-EH (`-fexceptions`), **single-threaded (no pthreads)**,
6	static CPython 3.14.4, static Qt 6.11.1 (`wasm_singlethread`, `-no-feature-thread`), static OCCT, static Coin3D.
7	No dlopen — Python C-extensions registered via `Py  …[truncated]
Fable
thinking
I'm going to review the plan.md files to identify what's already documented and spot any gaps in coverage.
Fable
The ROADMAP is thorough. Let me read the individual plans to understand coverage depth and find what's NOT covered.
Fable
⚙ Bash {"command": "for f in modal-audit threading-audit webengine-stub small-python-mods pyside-integrate; do echo \"===== $f =====\"; wc -l /home/magik6k/lcad-wasm/porting/$f/plan.md; done", "description": "Word counts of key plans"}
tool result
===== modal-audit =====
378 /home/magik6k/lcad-wasm/porting/modal-audit/plan.md
===== threading-audit =====
250 /home/magik6k/lcad-wasm/porting/threading-audit/plan.md
===== webengine-stub =====
299 /home/magik6k/lcad-wasm/porting/webengine-stub/plan.md
===== small-python-mods =====
380 /home/magik6k/lcad-wasm/porting/small-python-mods/plan.md
===== pyside-integrate =====
528 /home/magik6k/lcad-wasm/porting/pyside-integrate/plan.md
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/porting/modal-audit/plan.md"}
tool result
1	# Modal-dialog / nested-event-loop audit — FreeCAD wasm port
2	
3	Slug: `modal-audit`
4	Scope: global sweep of `src/` (esp. `Gui/` and candidate Mods Part, Sketcher, Material,
5	PartDesign, Measure, Import) for `QDialog::exec()`, `QMessageBox::*`, `QEventLoop`,
6	`QProgressDialog`, `QInputDialog::get*`, `QFileDialog::get*`, and FreeCAD's own
7	`Gui::FileDialog::get*`. Every claim below is grounded in the tree at
8	`/home/magik6k/lcad-wasm/freecad-port/FreeCAD` (branch `wasm-port`) and the conf  …[truncated]
Fable
thinking
I'm scanning through the other implementation plans to map out coverage and identify any gaps, then I'll search the source code for those cross-cutting concerns like font rendering, undo-redo, preferences, Python stdlib handling, units, clipboard, printing, i18n, drag-drop, and 3Dconnexion support.
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/porting/small-python-mods/plan.md"}
tool result
1	# WASM Port Plan — Small Python Modules: Show, Plot, OpenSCAD, Assembly, Inspection
2	
3	Scope slug: `small-python-mods`. Target tree: `/home/magik6k/lcad-wasm/freecad-port/FreeCAD`
4	(branch `wasm-port`). Build dir: `/opt/toolchains/src/freecad-gui-build`. Install prefix:
5	`/opt/toolchains/freecad-dist`. Toolchain: emscripten + ASYNCIFY + JS-EH (`-fexceptions`),
6	**single-threaded (no pthreads)**, static CPython 3.14 / Qt 6.11 / OCCT / Coin3D.
7	
8	All claims below are grounded in the actua  …[truncated]
Fable
thinking
I'm going to scan through the remaining plans quickly to get a sense of what's covered, then search the source code for those cross-cutting concerns the task mentioned—font and text rendering, undo-redo, preferences, Python stdlib zip handling, unit systems, clipboard, printing, translation and internationalization, drag-and-drop, and 3Dconnexion support. I'll read a couple more plans while doing targeted searches to map out where these features appear across the codebase.
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/porting/webengine-stub/plan.md"}
tool result
1	# Wasm port plan — QtWebEngine-dependent surfaces (slug: `webengine-stub`)
2	
3	**Verdict:** QtWebEngine has **no wasm port**, but FreeCAD's C++ GUI has **zero compile/link
4	dependency** on it. The single QtWebEngine consumer in the whole tree is the pure-Python
5	`Mod/Help/Help.py`, which is already `BUILD_HELP=OFF`, not in the wasm inittab, and needs PySide
6	(`FREECAD_USE_PYSIDE=OFF`). The Start workbench — which *is* built — renders with native Qt Widgets,
7	not WebEngine, which is exactl  …[truncated]
Fable
thinking
Now I'm looking for what the plans are missing—the cross-cutting concerns that might not have been covered. There's a specific list to check: build system and size budgets, font rendering, undo-redo functionality, preferences dialogs, Python standard library coverage in the packaged zip, unit systems, clipboard support, printing, internationalization, drag-and-drop, 3Dconnexion support, and any other modules or concerns the agents might have overlooked. I should also verify whether any "config-only" verdicts are actually realistic. Let me search through the source code to validate these gaps.
Fable
The plans are extremely thorough on their assigned scopes. Now let me ground the cross-cutting gaps by grepping the actual source.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD\necho \"===== CLIPBOARD =====\"; grep -rlnI \"QClipboard\\|QApplication::clipboard\\|qApp->clipboard\" src/Gui src/Mod/Part src/Mod/Sketcher 2>/dev/null | head -20\necho \"===== Q…
tool result
===== CLIPBOARD =====
src/Gui/CommandDoc.cpp
src/Gui/EditorView.cpp
src/Gui/ImageView.cpp
src/Gui/MainWindow.cpp
src/Gui/Placement.cpp
src/Gui/PythonConsole.cpp
src/Gui/QtAll.h
src/Gui/StatusBarLabel.cpp
src/Gui/SplashScreen.cpp
src/Gui/TextDocumentEditorView.cpp
src/Gui/VectorListEditor.cpp
src/Gui/Dialogs/DlgAbout.cpp
src/Gui/Dialogs/DlgUnitsCalculatorImp.cpp
src/Gui/propertyeditor/PropertyEditor.cpp
src/Mod/Sketcher/Gui/CommandSketcherTools.cpp
===== QDrag / drag-drop =====
src/Gui/ImageView.  …[truncated]
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD\necho \"===== Spaceball init in GuiApplicationNativeEventAware =====\"; grep -niI \"importSettings\\|spnav\\|__EMSCRIPTEN\\|FC_OS_WASM\\|ifdef\\|QMainWindow\\|Q_WS\\|Display\\|ope…
tool result
===== Spaceball init in GuiApplicationNativeEventAware =====
28:#include <QMainWindow>
36:#if defined(_USE_3DCONNEXION_SDK) || defined(SPNAV_FOUND)
38:#  if defined(SPNAV_USE_X11)
54:#if defined(_USE_3DCONNEXION_SDK) || defined(SPNAV_FOUND)
72:void Gui::GUIApplicationNativeEventAware::initSpaceball(QMainWindow* window)
74:#if defined(_USE_3DCONNEXION_SDK) || defined(SPNAV_FOUND)
134:    importSettings(motionDataArray);
170:void Gui::GUIApplicationNativeEventAware::importSettings(std::vector<int>  …[truncated]
Fable
thinking
Spaceball initialization is guarded by platform-specific defines that won't be set in WebAssembly, so it's effectively disabled there—no issue. For i18n, translations are compiled into the binary as .qm resources through the .qrc file, which should work fine, though the 180MB size is worth considering alongside the unit system, preferences, and Python stdlib components.

Now I need to check the packaged Python zip, standard library, font rendering, and how the unit system handles locale settings.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD\necho \"===== deploy dir contents =====\"; ls -la /home/magik6k/lcad-wasm/freecad-port/deploy* 2>/dev/null | head -40\necho \"===== python zip / stdlib in build =====\"; find /opt…
tool result
===== deploy dir contents =====
/home/magik6k/lcad-wasm/freecad-port/deploy:
total 275080
drwxr-xr-x 1 agent agent       312 Jul  5 01:29 .
drwxr-xr-x 1 agent agent       636 Jul  4 20:50 ..
-rw-r--r-- 1 agent agent    531487 Jul  5 01:26 FreeCAD.js
-rw-r--r-- 1 agent agent     98929 Jul  5 01:27 FreeCAD.js.gz
-rwxr-xr-x 1 agent agent 188407835 Jul  5 01:26 FreeCAD.wasm
-rwxr-xr-x 1 agent agent  68143106 Jul  5 01:27 FreeCAD.wasm.gz
-rw-r--r-- 1 agent agent      1613 Jul  4 20:38 README.md
-rw-r  …[truncated]
Fable
thinking
I need to dig into the freecad.data.js file to see what's actually preloaded—particularly whether the full Python standard library is included and what else gets bundled. Since it's only 15MB, that's surprisingly compact, so I should examine the boot.py and the contents of the Python zip to understand what's really available.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port\necho \"===== preload entries in freecad.data.js =====\"; grep -oE '\"[^\"]*@[^\"]*\"|preload|package_uuid|/freecad/[^\"]*' deploy/freecad.data.js 2>/dev/null | grep -oE '/freecad/[A-Za-z…
tool result
===== preload entries in freecad.data.js =====
/freecad/Ext
/freecad/Ext/PySide/QtCore.py
/freecad/Ext/PySide/QtGui.py
/freecad/Ext/PySide/QtNetwork.py
/freecad/Ext/PySide/QtSvg.py
/freecad/Ext/PySide/QtSvgWidgets.py
/freecad/Ext/PySide/QtUiTools.py
/freecad/Ext/PySide/QtWebEngineWidgets.py
/freecad/Ext/PySide/QtWidgets.py
/freecad/Ext/PySide/__init__.py
/freecad/Ext/freecad
/freecad/Ext/freecad/UiTools.py
/freecad/Ext/freecad/__init__.py
/freecad/Ext/freecad/__pycache__/__init__.cpython-314.pyc  …[truncated]
Fable
thinking
The PySide Ext shims are already bundled as passthrough modules, but they're pointing to PySide6 which isn't actually present in the environment. I need to check what's in the Python standard library zip to see if there are missing modules FreeCAD depends on — with 492 files totaling 8.8 MB, the real question is whether the full stdlib is actually there.

I'm concerned about commonly-trimmed modules like `xml`, `sqlite3`, `ssl`, `lzma`, and `bz2` that often lose their native extensions in minimal distributions. FreeCAD relies on things like JSON and XML parsing, so I should verify which stdlib modules are actually present and check for gaps in unit systems, locale support, and font rendering  …
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD\necho \"===== all Mod dirs =====\"; ls src/Mod/\necho \"===== BUILD flags in cache =====\"; grep -E \"^BUILD_[A-Z_]*:BOOL\" /opt/toolchains/src/freecad-gui-build/CMakeCache.txt | …
tool result
===== all Mod dirs =====
AddonManager
Assembly
BIM
CAM
CMakeLists.txt
Draft
Fem
Help
Import
Inspection
JtReader
Material
Measure
Mesh
MeshPart
OpenSCAD
Part
PartDesign
Plot
Points
ReverseEngineering
Robot
Show
Sketcher
Spreadsheet
Start
Surface
TechDraw
TemplatePyMod
Test
Tux
Web
__init__.py
mod.dox
===== BUILD flags in cache =====
BUILD_ADDONMGR:BOOL=OFF
BUILD_ASSEMBLY:BOOL=OFF
BUILD_BIM:BOOL=OFF
BUILD_CAM:BOOL=OFF
BUILD_DESIGNER_PLUGIN:BOOL=OFF
BUILD_DRAFT:BOOL=OFF
BUILD_DYNAMIC_LINK_PYTHON:BO  …[truncated]
Fable
thinking
Looking at which modules haven't been covered by any agent reports—CAM, BIM, Robot, ReverseEngineering, JtReader, Tux, Test, and AddonManager are all either not included in the audit or explicitly excluded due to build flags and dependencies.

Beyond module coverage, I need to examine some cross-cutting concerns that could affect the wasm port: how the unit system and locale handling work through Base::Quantity, font rendering with fontconfig and freetype for Qt, the undo/redo transaction system, the preferences dialog, and clipboard functionality on wasm. The size budget is also a real constraint—we're already at 180MB uncompressed (68MB gzipped), and adding modules like PartDesign, Mesh, T  …
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD\necho \"===== fontconfig / freetype / QFontDatabase =====\"; grep -rlnI \"fontconfig\\|FcConfig\\|QFontDatabase::addApplicationFont\\|loadFont\" src/Gui src/Mod/TechDraw src/Mod/D…
tool result
===== fontconfig / freetype / QFontDatabase =====
src/Mod/TechDraw/Gui/AppTechDrawGui.cpp
===== unit system / locale QLocale =====
src/App/Document.cpp
src/App/Expression.h
src/App/Expression.cpp
src/App/ExpressionParser.h
src/App/ObjectIdentifier.h
src/App/PropertyGeo.cpp
src/App/PropertyUnits.h
src/App/PropertyUnits.cpp
src/App/Application.cpp
src/Base/CMakeLists.txt
===== preferences dialog DlgPreferences =====
src/Gui/Dialogs/DlgPreferencePackManagement.ui
src/Gui/Dialogs/DlgPreferencePackMa  …[truncated]
Fable
thinking
The Preferences dialog itself should work under asyncify since it's launched modally from the main loop, but there are complications: the restart-required flows won't function on WebAssembly, and many preference pages are dynamically loaded from workbenches via PySide, which won't be available in that environment.

The dialog is also quite large and hasn't been audited. Beyond that, TechDraw relies on application fonts for rendering dimensions and text — I need to verify whether those fonts are actually bundled and accessible, since that's a potential gap in the WebAssembly plan.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD\necho \"===== TechDraw font load =====\"; grep -niI \"addApplicationFont\\|font\\|\\.ttf\\|QFontDatabase\" src/Mod/TechDraw/Gui/AppTechDrawGui.cpp | head\necho \"===== TechDraw he…
tool result
===== TechDraw font load =====
23:#include <QFontDatabase>
84:    // add fonts
85:    std::string fontDir = App::Application::getResourceDir() + "Mod/TechDraw/Resources/fonts/";
87:    std::vector<std::string> fontsAll(
88:        {"osifont-lgpl3fe.ttf", "osifont-italic.ttf", "Y14.5-2018.ttf", "Y14.5-FreeCAD.ttf"});
90:    for (auto& font : fontsAll) {
91:        QString fontFile = QString::fromStdString(fontDir + font);
92:        int rc = QFontDatabase::addApplicationFont(fontFile);
95  …[truncated]
Fable
thinking
TechDraw needs font files bundled in the filesystem for runtime loading, which is a packaging concern that may not be covered by the current plan. Qt-for-wasm has its own font handling through bundled defaults or browser fallbacks, but text rendering in both the 3D view and Qt widgets requires proper font support — this is a broader architectural issue that needs addressing.

I should also verify a few other integration points: the Material module's network access through QNetworkAccessManager, clipboard functionality on Qt wasm (which uses async browser APIs and mostly works but has async paste limitations), and whether there's coverage for template-based document creation and recent files   …
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD\necho \"===== IDBFS / persistent config =====\"; grep -rniI \"IDBFS\\|idbfs\\|syncfs\\|user.cfg\\|getUserConfigPath\\|GetUserConfigDir\" src/App/Application.cpp src/Main 2>/dev/nu…
tool result
===== IDBFS / persistent config =====
src/Main/CMakeLists.txt:96:        "-lidbfs.js"
src/App/Application.cpp:1459:std::string Application::getUserConfigPath()
src/App/Application.cpp:1461:    return Base::FileInfo::pathToString(_appDirs->getUserConfigPath()) + PATHSEP;
src/App/Application.cpp:2482:    ("user-cfg,u", boost::program_options::value<std::string>(),"User config file to load/save user settings")
src/App/Application.cpp:2703:    if (vm.contains("user-cfg")) {
src/App/Application.cpp:2  …[truncated]
Fable
thinking
IDBFS is linked in the build, but I need to verify whether the actual mount and sync operations are wired up to persist user.cfg and saved documents across page reloads—that's a key gap to check.

Now I'm ready to consolidate everything into a comprehensive GAPS.md. I'll quickly scan the roadmap summaries for the missing commands and modules like techdraw and pyside-recipe to see if they mention anything about fonts or sizing that I should flag.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/porting\necho \"===== mention of fonts anywhere =====\"; grep -rliI \"font\" */plan.md\necho \"===== mention of size / MB / budget =====\"; grep -rniI \"size budget\\|180\\|MB\\|download\\|gzip\" */pl…
tool result
===== mention of fonts anywhere =====
small-python-mods/plan.md
modal-audit/plan.md
===== mention of size / MB / budget =====
draft/plan.md:62:`src/Base/PyObjectBase.h:67`) to `PyMODINIT_FUNC PyInit_DraftUtils(void)`, the symbol the inittab
draft/plan.md:107:   generated `Draft_rc.py`), ~2-3 MB uncompressed.
draft/plan.md:141:every module that references `QtCore` symbols from them) — an invasive, fragile fork that still
draft/plan.md:191:  and none embedded in `deploy/FreeCAD.wasm`.
draft/plan.m  …[truncated]
Fable
thinking
The font references are only about the dialog component, not actual font rendering concerns. The persist and reload mentions are just translation-related incidentals. I need to verify that none of these actually address the config persistence or size budget as a real constraint—the size mentions are just about object sizes, not download limits. Let me check what that translation reference is about.

Actually, the grep patterns are too loose and matching incidentally on words like "print" in printf or "undo" nowhere relevant. I should stop over-verifying and move forward with what I've confirmed.

I have enough grounding now. The key finding is that CAM, Robot, BIM, JtReader, ReverseEngineeri  …
Fable
I have enough grounding. Let me verify two more specifics before writing.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD\necho \"===== IDBFS mount wiring in code (not just link flag) =====\"; grep -rniI \"IDBFS\\|FS.mount\\|syncfs\\|mkdir.*persist\" src/Main deploy/../deploy/boot.py /home/magik6k/lc…
tool result
===== IDBFS mount wiring in code (not just link flag) =====
src/Main/CMakeLists.txt:96:        "-lidbfs.js"
===== Qt printsupport in Qt wasm build =====
libQt6PrintSupport.a
libQt6PrintSupport.prl
===== TechDraw plan: does it mention bundling the .ttf fonts? =====
292:TechDraw reads data files at runtime from `${CMAKE_INSTALL_DATADIR}/Mod/TechDraw`: `Templates/*.svg` (page templates), `PAT/*.pat` + `Patterns/` (hatch patterns), `Symbols/`, `LineGroup/*.csv`, `CSVdata/*.csv` (installed by `src/Mo  …[truncated]
Fable
thinking
The TechDraw plan does cover bundling the Mod/TechDraw directory with its fonts, though it doesn't explicitly mention that fonts are stored in `Gui/Resources/fonts` and loaded through QFontDatabase from the install path. So TechDraw's font handling is reasonably addressed, but the base application's font rendering in the wasm environment still needs consideration.

For IDBFS, the linker flag exists but there's no actual wiring of FS.mount or syncfs in the codebase, meaning user preferences and saved state aren't persisting across reloads unless the JS file bridge is handling that entirely. That's a genuine gap. Qt PrintSupport is built and linked, with modal guards in place for the print dia  …
Fable
I have thorough grounding. Writing the gaps file.
Fable
⚙ Write {"file_path": "/home/magik6k/lcad-wasm/porting/GAPS.md", "content": "# FreeCAD → WebAssembly — Parity Analysis GAPS (completeness critic)\n\nReviews consolidated in `ROADMAP.md` cover the 16 assigned surfaces well. This file lists what the\…
tool result
File created successfully at: /home/magik6k/lcad-wasm/porting/GAPS.md (file state is current in your context — no need to Read it back)
Fable
thinking
I need to pull together the key gaps that need addressing.
Fable
Wrote `/home/magik6k/lcad-wasm/porting/GAPS.md`. Most important missing items from the parity analysis: - **State persistence across page reload (HIGH, un-analyzed):** `-lidbfs.js` is linked (`Main/CMakeLists.txt:96`) but there is NO `FS.mount(IDBFS)`/`syncfs` wiring anywhere in `src/Main`, `boot.py`, or `index.html`. `user.cfg` (`App/Application.cpp:3303`) lives on ephemeral MEMFS → preferences, recent files, layout, unit settings all lost every reload. Needs a dedicated IDBFS plan (PR0). - **Binary size / download budget (HIGH, un-analyzed):** already 188 MB (68 MB gz); the whole roadmap is additive and every "config-only" flip relinks the full binary. No per-module size measurement, no `-Oz`/`--gc-sections`/module-split strategy, no browser-ceiling analysis. PySide alone is "+180 MB". Needs a size-budget spike gating bulk enablement. - **Base-app fonts / text rendering (MED/HIGH, un-analyzed):** TechDraw's own `.ttf`s are handled, but nobody verified the base Qt-wasm UI font or **Coin3D 3D-viewport text** (dimension/constraint/Measure labels, nav cube) has any glyph source with no fontconfig. Affects already-shipped Sketcher/Measure visuals. - **Seven modules given no verdict at all (MED):** CAM/Path, Robot, BIM, ReverseEngineering, JtReader, Tux, AddonManager — dropped silently. Some (Robot/KDL, ReverseEngineering, libarea) have portable C++ cores worth a real triage. - **Preferences dialog un-audited (MED):** works under asyncify but has a meaningless "restart required" modal (`DlgPreferencesImp.cpp:974`), PySide-backed per-workbench pages will fail, and it writes the non-persisted `user.cfg`. - **Clipboard copy/paste un-audited (MED):** Qt-wasm `QClipboard` is async/gesture-gated, but FreeCAD uses it synchronously in Std_Copy/Paste and Sketcher geometry copy — likely silent no-op. - **i18n `.qm` bundling + size (MED/LOW):** `Translator.cpp` loads locale `.qm` at runtime; must be preloaded or only English works, and shipping all locales feeds the size problem. - **Networked surfaces beyond WebEngine (LOW/MED):** `QNetworkAccessManager` (DownloadManager, Material-external, Start news, AddonManager) maps to CORS-restricted `fetch` — un-analyzed as a class. - **Cleared but worth stating:** 3Dconnexion/Spaceball is a confirmed no-op (`GuiApplicationNativeEventAware.cpp:72` guarded by SDK/SPNAV defines, neither set on wasm) — so it isn't re-investigated. Over-optimism flags: (1) "config-only, hours" ignores the full 188 MB relink+redeploy per flip AND the per-module resource-bundle step (only spelled out for TechDraw); (2) all Wave-A wins hard-depend on the medium-risk PR3 file bridge to be usable; (3) PartDesign needs a full command-registration PySide sweep, not just the 2 documented imports; (4) TechDraw's 1–2 day vs 1–2 week threading estimate is still unresolved (biggest single-module schedule risk); (5) the bundled `Ext/PySide/*.py` shims falsely suggest PySide is half-present — they passthrough to absent PySide6.