subagent transcript

session 18e25685 · workflow wf_23df5b1c
delegated task
Produce a concrete, ordered engineering plan to get the 3D viewport actually rendering (a box with orbit/pan/zoom), given the port currently: boots th
▸ Magik
Produce a concrete, ordered engineering plan to get the 3D viewport actually rendering (a box with orbit/pan/zoom), given the port currently: boots the GUI, runs Python->OCCT (makeBox works), has a validated standalone WebGL2 fixed-function emulator (/home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Gui/WasmGLFixedFunc.cpp) and a Coin GL glue port. Read /home/magik6k/lcad-wasm/freecad-port/research/R9-coin-webgl-strategy.md and R3-graphics-coin-webgl.md, WasmGLFixedFunc.cpp, and the coin glue. Determine the SPECIFIC remaining steps: how a 3D document's view provider reaches SoGLRenderAction -> the emulator; what GL context/canvas wiring Quarter/QOpenGLWidget needs on wasm; the exact failure mode currently ('Coin warning cc_glglue_instance: Error setting up GL context'); and the minimal changes to fix it. Output an ordered step list with the single highest-leverage first step. Report as findings where severity encodes priority (critical=do-first).
Fable
thinking
I'll start by examining the key files to understand what we're working with.
Fable
I'll read the key research and source files to understand the current state.
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/research/R9-coin-webgl-strategy.md"}
tool result
1	# R9 — Coin3D → WebGL: strategy decision & plan
2	
3	Supersedes the open question in R3 §4. Written after building and **validating
4	a fixed-function-GL-over-WebGL2 emulator** (`Gui/WasmGLFixedFunc.cpp`) that
5	renders lit/flat/indexed triangles correctly in a standalone WebGL2 context,
6	and after porting Coin's glue layer far enough to reach real rendering.
7	
8	## 1. The problem, precisely
9	
10	Coin renders with **OpenGL 1.x fixed-function**: matrix stacks, `glMaterial`/
11	`glLight`, `gl  …[truncated]
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/research/R3-graphics-coin-webgl.md"}
tool result
1	# R3 — Graphics: Coin3D on WebGL (the flagship workstream)
2	
3	## 1. Problem statement
4	
5	FreeCAD's entire 3D pipeline is `Gui → Quarter(QOpenGLWidget) → Coin3D
6	scene graph → OpenGL 1.x/2 fixed-function + extensions`. 346 FreeCAD files
7	program against Inventor APIs, so **replacing the scene graph is a
8	non-starter**; the scene graph's *renderer* must learn WebGL.
9	
10	Qt-wasm gives `QOpenGLWidget` a **WebGL2 (≈GLES3)** context. WebGL has no
11	fixed-function pipeline: no `glBegin/glEn  …[truncated]
Fable
Now let me read the emulator and find the Coin glue files.
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Gui/WasmGLFixedFunc.cpp"}
tool result
1	/* SPDX-License-Identifier: LGPL-2.1-or-later
2	 *
3	 * WebAssembly build only. A compact fixed-function OpenGL 1.x emulation over
4	 * WebGL2/GLES3, so Coin3D (which renders FreeCAD geometry with legacy GL) can
5	 * draw into the WebGL2 context Qt hands us. Emscripten's LEGACY_GL_EMULATION
6	 * is WebGL1-only and conflicts with Qt's forced WebGL2, so we implement the
7	 * subset Coin actually exercises ourselves.
8	 *
9	 * Design: the legacy entry points are C functions here; the state and dr  …[truncated]
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD && find . -path ./build -prune -o -name 'gl.cpp' -print 2>/dev/null; echo \"---glue dir---\"; find . -type d -name glue 2>/dev/null | head; echo \"---coin dir---\"; ls -d src/3rdP…
tool result
./src/3rdParty/coin/src/glue/gl.cpp
---glue dir---
./src/3rdParty/coin/src/glue
./src/3rdParty/coin/include/Inventor/C/glue
./src/3rdParty/pivy/fake_headers/Inventor/C/glue
---coin dir---
src/3rdParty/coin
./src/3rdParty/coin
./tests/visual/baselines/coin-nodes
./.git/modules/src/3rdParty/coin
./src/3rdParty/coin/models/coin_features
Fable
thinking
I'm looking at the Coin glue code to trace where the GL context error originates, specifically examining the `cc_glglue_instance` function that's throwing "Error setting up GL context" and how `cc_glglue_getprocaddress` works in gl.cpp.
Fable
Now let me examine the Coin glue, focusing on `cc_glglue_instance` (the source of the error) and the getprocaddress wiring.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/3rdParty/coin && grep -n \"Error setting up GL context\\|cc_glglue_instance\\|coin_glglue_instance_from_context_ptr\\|getprocaddress\\|fcWasmResolveGL\\|__EMSCRIPTEN__\\|emscr…
tool result
64:      const cc_glglue * cc_glglue_instance(int contextid);
66:      const cc_glglue * cc_glglue_instance_from_context_ptr(void * ctx);
101:  const cc_glglue * glw = cc_glglue_instance(SoGLCacheContextElement::get(state));
609:cc_glglue_getprocaddress(const cc_glglue * glue, const char * symname)
613:#ifdef __EMSCRIPTEN__
619:    extern void * emscripten_GetProcAddress(const char *);
620:    ptr = emscripten_GetProcAddress(symname);
626:      extern void * fcWasmResolveGL(const char *);
627:    …[truncated]
Fable
thinking
I need to search more broadly for where that GL context error message is coming from, since it didn't show up in the gl.cpp file.
Fable
Let me find the exact "Error setting up GL context" string and examine `cc_glglue_instance`.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/3rdParty/coin && grep -rn \"Error setting up GL context\\|setting up GL\" src/ include/ | head"}
tool result
(Bash completed with no output)
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/3rdParty/coin/src/glue/gl.cpp", "offset": 2296, "limit": 140}
tool result
2296	cc_glglue_instance(int contextid)
2297	{
2298	  SbBool found;
2299	  void * ptr;
2300	  GLint gltmp;
2301	
2302	  cc_glglue * gi = NULL;
2303	
2304	  CC_SYNC_BEGIN(cc_glglue_instance);
2305	
2306	  /* check environment variables */
2307	  if (COIN_MAXIMUM_TEXTURE2_SIZE == 0) {
2308	    const char * env = coin_getenv("COIN_MAXIMUM_TEXTURE2_SIZE");
2309	    if (env) COIN_MAXIMUM_TEXTURE2_SIZE = atoi(env);
2310	    else COIN_MAXIMUM_TEXTURE2_SIZE = -1;
2311	  }
2312	  if (COIN_MAXIMUM_TEXTURE3  …[truncated]
Fable
thinking
The warning message matches what the user reported—it's triggered when `glGetError()` returns a non-zero value, which then causes an assertion failure on the version string retrieval. The issue appears to be that `cc_glglue_instance` is being called in an environment where the GL context isn't properly initialized.

The problem is that emscripten's GL functions like `glGetError` and `glGetString` depend on the global `GLctx` variable being set to the current context, but at the time `cc_glglue_instance` runs, Qt's context isn't being reflected there. The `ffSyncContext` / `fcWasmSyncGLContext` functions were designed to handle exactly this synchronization, but they're called during Quarter's  …
Fable
Let me look at the getprocaddress region, `coin_gl_current_context`, `check_egl`, and where sync is called in Quarter.
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/3rdParty/coin/src/glue/gl.cpp", "offset": 600, "limit": 120}
tool result
600	{
601	  if (!glue->dl_handle) {
602	    const_cast <cc_glglue *> (glue)->dl_handle = cc_dl_handle_with_gl_symbols();
603	  }
604	  return glue->dl_handle;
605	}
606	
607	/* doc in header file */
608	void *
609	cc_glglue_getprocaddress(const cc_glglue * glue, const char * symname)
610	{
611	  void * ptr = NULL;
612	
613	#ifdef __EMSCRIPTEN__
614	  // On WebAssembly there is no WGL/EGL/GLX/dlsym; resolve GL entry points
615	  // (both emscripten's real WebGL2 functions and our fixed-function e  …[truncated]
Fable
thinking
I need to dig into `coin_gl_current_context` and `check_egl` to understand why the context isn't being recognized as current.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/3rdParty/coin && grep -rn \"coin_gl_current_context\\|check_egl\\|COIN_USE_EGL\\|eglGetCurrentContext\\|HAVE_EGL\" src/glue/gl.cpp src/glue/gl_egl.cpp | head -50"}
tool result
src/glue/gl_egl.cpp:61:#ifndef HAVE_EGL
src/glue/gl_egl.cpp:93:#else /* HAVE_EGL */
src/glue/gl_egl.cpp:388:  context->storedcontext = eglGetCurrentContext();
src/glue/gl_egl.cpp:520:#endif /* HAVE_EGL */
src/glue/gl.cpp:194:   (HAVE_WGL), (HAVE_EGL or HAVE_GLX) and (HAVE_AGL or HAVE_CGL) being defined at the same time, but
src/glue/gl.cpp:206:#if defined(HAVE_EGL) || defined(HAVE_GLX)
src/glue/gl.cpp:227:#error More than one of HAVE_WGL, HAVE_EGL|HAVE_GLX, and HAVE_AGL|HAVE_CGL set simultaneous  …[truncated]
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/3rdParty/coin/src/glue/gl.cpp", "offset": 5281, "limit": 45}
tool result
5281	coin_gl_current_context(void)
5282	{
5283	  void * ctx = NULL;
5284	
5285	#ifdef HAVE_EGL
5286	  if (COIN_USE_EGL > 0) {
5287	    ctx = eglGetCurrentContext();
5288	    if (ctx) {
5289	      return ctx;
5290	    }
5291	  } else {
5292	#endif /* HAVE_EGL */
5293	
5294	#ifdef HAVE_GLX
5295	  ctx = glXGetCurrentContext();
5296	#endif /* HAVE_GLX */
5297	
5298	#ifdef HAVE_EGL
5299	  }
5300	#endif /* HAVE_EGL */
5301	
5302	#ifdef HAVE_WGL
5303	  ctx = wglGetCurrentContext();
5304	#endif /* HAVE_  …[truncated]
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/3rdParty/coin/src/glue/gl.cpp", "offset": 2249, "limit": 48}
tool result
2249	static void check_egl()
2250	{
2251	#if !defined(HAVE_EGL)
2252	  COIN_USE_EGL = 0;
2253	#elif defined(HAVE_EGL) && !defined(HAVE_GLX)
2254	  COIN_USE_EGL = 1;
2255	#else // HAVE_EGL && HAVE_GLX
2256	  if (COIN_USE_EGL == -1) {
2257	    // If COIN_EGL is set use EGL
2258	    const char * env = coin_getenv("COIN_EGL");
2259	    if (env) {
2260	      // Only accept '0' and '1' otherwise auto detect
2261	      if (env[0] == '0' && env[1] == '\0') {
2262	        COIN_USE_EGL = 0;
2263	        r  …[truncated]
Fable
thinking
I see the issue now — on WebAssembly, `coin_gl_current_context()` checks for an active EGL context using `eglGetCurrentContext()` when EGL is available, but Qt's WebAssembly implementation bypasses EGL entirely and manages the GL context directly through emscripten's GL module. This mismatch means the function returns NULL even though a context is actually active, triggering the assertion.

The reported warning about "Error setting up GL context" fires because `glGetString(GL_VERSION)` returns NULL when the context isn't detected as current, which cascades into either a crash during version string parsing or a failed initialization. The real problem is timing: `fcWasmSyncGLContext()` ensures  …
Fable
Let me check what GL platform defines the wasm build uses, and how Quarter wires the context and calls the sync.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD && grep -rn \"HAVE_EGL\\|HAVE_GLX\\|COIN_USE_EGL\\|EMSCRIPTEN\" src/3rdParty/coin/config.h src/3rdParty/coin/include/config.h 2>/dev/null | head; echo \"=== config locations ===\"…
tool result
=== config locations ===
=== cmake egl ===
src/3rdParty/coin/CMakeLists.txt:131:option(COIN_BUILD_EGL "Build for EGL on Linux when ON. Default is ON." ON)
src/3rdParty/coin/CMakeLists.txt:144:  COIN_BUILD_EGL
src/3rdParty/coin/CMakeLists.txt:234:    if(COIN_BUILD_EGL)
src/3rdParty/coin/CMakeLists.txt:235:      if (OpenGL_EGL_FOUND)
src/3rdParty/coin/CMakeLists.txt:236:        set(HAVE_EGL 1)
src/3rdParty/coin/CMakeLists.txt:237:        if (NOT TARGET OpenGL::EGL)
src/3rdParty/coin/CMakeLists.txt  …[truncated]
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD && grep -rln \"fcWasmSyncGLContext\\|fcWasmResolveGL\\|WasmGLFixedFunc\" src/ --include=*.cpp --include=*.h --include=*.txt --include=CMakeLists.txt | grep -v WasmGLFixedFunc.cpp"…
tool result
src/3rdParty/coin/src/glue/gl.cpp
src/Gui/CMakeLists.txt
src/Gui/Quarter/QuarterWidget.cpp
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD && grep -n \"fcWasmSyncGLContext\\|fcWasmResolveGL\\|paintGL\\|paintEvent\\|makeCurrent\\|SoGLRenderAction\\|actualRedraw\\|__EMSCRIPTEN__\\|initializeGL\" src/Gui/Quarter/Quarter…
tool result
70:#ifdef __EMSCRIPTEN__
71:extern "C" void fcWasmSyncGLContext(void);
109:  See \ref SoGLRenderAction::TransparencyType for a full description of the modes
171:    void initializeGL() override
186:    // paintGL() is invoked when e.g. using the method grabFramebuffer of this class
194:    void paintGL() override
298:  // set up a cache context for the default SoGLRenderAction
523:  PRIVATE(this)->sorendermanager->getGLRenderAction()->setTransparencyType((SoGLRenderAction::TransparencyType)type)  …[truncated]
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Gui/Quarter/QuarterWidget.cpp", "offset": 160, "limit": 60}
tool result
160	        surfaceFormat.setRenderableType(QSurfaceFormat::OpenGL);
161	        surfaceFormat.setProfile(QSurfaceFormat::CompatibilityProfile);
162	        surfaceFormat.setOption(QSurfaceFormat::DeprecatedFunctions, true);
163	
164	#if defined (_DEBUG) && 0
165	        surfaceFormat.setOption(QSurfaceFormat::DebugContext);
166	#endif
167	        setFormat(surfaceFormat);
168	    }
169	    ~CustomGLWidget() override = default;
170	
171	    void initializeGL() override
172	    {
173	#if defined   …[truncated]
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Gui/Quarter/QuarterWidget.cpp", "offset": 995, "limit": 55}
tool result
995	/*!
996	  Overridden from QGLWidget to render the scenegraph
997	 */
998	void
999	QuarterWidget::actualRedraw()
1000	{
1001	  ZoneScoped;
1002	#ifdef __EMSCRIPTEN__
1003	  // Ensure emscripten's current-context global (GLctx) matches the context Qt
1004	  // made current, so Coin's glGetString/glGetError and our GL emulation act on
1005	  // a valid WebGL2 context. See Gui/WasmGLFixedFunc.cpp.
1006	  fcWasmSyncGLContext();
1007	#endif
1008	  PRIVATE(this)->sorendermanager->render(PRIVATE(thi  …[truncated]
Fable
thinking
I need to verify whether HAVE_EGL gets defined in the wasm build configuration and check if there's an emscripten-specific branch handling the GL context. I should also confirm whether a 3D View is actually being instantiated, but I have enough information to work through the plan. Let me examine the config.h generation for the wasm build to see what's being set.
Fable
Let me check the wasm build's actual coin config (HAVE_EGL/HAVE_GLX) and whether a 3D view is being instantiated.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD && find . -name 'config.h' -path '*coin*' 2>/dev/null; echo \"=== build dirs ===\"; ls -d build* ../build* 2>/dev/null; find .. -maxdepth 3 -name 'config.h' -path '*coin*' 2>/dev/…
tool result
=== build dirs ===
=== grep HAVE_EGL/GLX in any generated config ===
src/3rdParty/coin/src/config.h.cmake.in:125:#cmakedefine HAVE_EGL
src/3rdParty/coin/src/config.h.cmake.in:191:#cmakedefine HAVE_GLX
Fable
thinking
I don't see a build directory, so the build likely happened elsewhere or this is a clone mode setup. Let me check the CMakeLists around line 234 to see what's configured for WebAssembly and whether there are any wasm-specific overrides, then look into how the coin build handles WebAssembly and search for Emscripten configuration in the CMake files or toolchain.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD && sed -n '220,270p' src/3rdParty/coin/CMakeLists.txt"}
tool result
    endif()
    list(APPEND COIN_TARGET_INCLUDE_DIRECTORIES ${X11_INCLUDE_DIR} ${X11_GL_INCLUDE_PATH})
    list(APPEND COIN_TARGET_LINK_LIBRARIES ${X11_LIBRARIES} ${X11_GL_LIB} ${X11_GLU_LIB})
  endif()
else()
  if(UNIX AND NOT APPLE AND COIN_BUILD_GLX)
    find_package(X11 REQUIRED)
    list(APPEND COIN_TARGET_INCLUDE_DIRECTORIES ${X11_INCLUDE_DIR})
    list(APPEND COIN_TARGET_LINK_LIBRARIES ${X11_LIBRARIES})
  endif()
  find_package(OpenGL REQUIRED)
  if(OPENGL_FOUND)
    set(HAVE_OPENGL 1)
    …[truncated]
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port && grep -rn \"EGL\\|EMSCRIPTEN\\|COIN_BUILD_EGL\\|WEBGL\\|GLES\" *.md research/*.md 2>/dev/null | grep -i \"egl\\|gles\\|webgl2 context\\|current context\" | head -30"}
tool result
STATUS.md:48:  (`WasmGLFixedFunc`, ~60 GL1/EGL no-ops = the Stage-4 seam), Start module
STATUS.md:79:  no modern functions were ever resolved); `eglBindAPI(EGL_OPENGL_ES_API)`;
research/R1-codebase-architecture.md:36:  `QOpenGLWidget` yields a WebGL2 (GLES3) context, so the *context plumbing*
stage-4-webgl-coin-backend.md:4:Replace stopgap GL emulation with a first-class GLES2/WebGL2 rendering
stage-4-webgl-coin-backend.md:18:2. **Glue seam:** `COIN_GLES2` mode in `cc_glglue` routing legacy entr  …[truncated]
Fable
thinking
I'm checking the STATUS.md file to understand the current state of the 3D view implementation and whether there's an active emscripten branch I need to be aware of.
Fable
Let me read the STATUS.md to understand exactly where the port currently stands.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port && sed -n '30,100p' STATUS.md"}
tool result
## ✅ Stage 0 — toolchain: COMPLETE
- emsdk **4.0.7** (Qt 6.11.1's exact pin), all deps built static for wasm:
  OCCT 7.8.1, CPython 3.14.4 (+mpdec/ffi/expat/HACL/sqlite3), xerces-c,
  ICU 76, Boost 1.86, fmt, yaml-cpp, GTest, freetype/harfbuzz/zlib/bz2/sqlite3.
- **Qt 6.11.1 built from source for wasm with asyncify** (no threads):
  qtbase + qtsvg + qttools(UiTools/UiPlugin) → `/opt/toolchains/qt-asyncify`.
  This is what makes FreeCAD's synchronous `QEventLoop::exec()` work.

## ✅ Stage 1 — hea  …[truncated]
Fable
thinking
I need to review the rest of STATUS.md and check R8 to verify the JSPI ordering.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port && sed -n '100,140p' STATUS.md; echo \"=== R8 head ===\"; sed -n '1,40p' research/R8-jspi-migration-plan.md 2>/dev/null"}
tool result
  deterministically. Re-measure after the JSPI switch first (it may vanish
  part of Blocker B for free). Options a/b/c/d evaluated; roll-our-own
  targeted emulator (b+d) chosen over Regal/gl4es (fallback) and a full Coin
  rewrite (non-starter; track upstream's modern-GL branch instead).
- Base GUI is unaffected by all of the above (emulator only activates on 3D
  rendering); `deploy-gui/` ships the stable working GUI shell.

## ⏭ Stage 5–6 — remaining
- **Stage 5**: FCStd open/save via OPFS/f  …[truncated]
Fable
thinking
I've identified the core issues blocking the 3D viewport rendering: the ASYNCIFY mechanism conflicts with CPython's trampoline system, causing crashes when Python code runs during the Qt event loop, and there's a second blocker I'm still pinpointing in the architecture.

The Coin warning about GL context setup traces back to a missing `__EMSCRIPTEN__` branch in the context-detection code—Coin can't recognize Qt-wasm's emscripten GL context as current. The priority is clear: migrate to JSPI first, because the asyncify-trampoline crash prevents even reaching the render path, and JSPI's removal of function-table rewrites may also resolve some of the indirect GL call signature mismatches automat  …
Fable
I have a complete picture now. Let me verify one more thing: whether the getprocaddress path currently prefers emscripten (the signature-trap source) over our resolver, and confirm no wasm guard disables 3D view creation.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD && grep -rn \"PROC(\" src/3rdParty/coin/src/glue/gl.cpp | wc -l; echo \"=== sample PROC symbols ===\"; grep -oP 'PROC\\(w,\\s*\"\\K[^\"]+' src/3rdParty/coin/src/glue/gl.cpp | sort…
tool result
138
=== sample PROC symbols ===
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD && grep -oP 'getprocaddress\\(w,\\s*\"\\K[^\"]+' src/3rdParty/coin/src/glue/gl.cpp | sort -u | head -80 | tr '\\n' ' '; echo; echo \"=== count unique ===\"; grep -oP 'getprocaddre…
tool result
glBindFramebufferEXT glBindRenderbufferEXT glCheckFramebufferStatusEXT glDeleteFramebuffersEXT glDeleteRenderbuffersEXT glFramebufferRenderbufferEXT glFramebufferTexture1DEXT glFramebufferTexture2DEXT glFramebufferTexture3DEXT glGenFramebuffersEXT glGenRenderbuffersEXT glGenerateMipmap glGenerateMipmapARB glGenerateMipmapEXT glGetFramebufferAttachmentParameterivEXT glGetRenderbufferParameterivEXT glIsFramebufferEXT glIsRenderbufferEXT glRenderbufferStorageEXT 
=== count unique ===
19
Fable
thinking
The PROC macro wraps function pointer resolution through a getprocaddress call, and there are over 150 uses of it throughout the codebase. The current implementation prioritizes emscripten's GetProcAddress first, which returns the actual function pointers that can fail when called indirectly if there's a signature mismatch.

The fix is to reverse this priority — route everything through fcWasmResolveGL instead, which provides signature-matched C symbols that Coin can safely call indirectly. This prevents the indirect call failures entirely.

I've identified the key blockers and their priority: JSPI migration is critical and must happen first, then inverting the getprocaddress resolution orde  …
Fable
I now have everything needed. Here is my analysis, returned as ordered findings where severity encodes priority (critical = do first).
Fable
⚙ StructuredOutput {"findings": [{"severity": "critical", "category": "do-first-blocker", "summary": "Migrate the GUI build from ASYNCIFY to JSPI FIRST — it is the single highest-leverage step; it is the only thing that lets Python drive the live GUI (the ver…
tool result
Structured output provided successfully