fix(core): bundle every RPC domain into frozen build + regression coverage#353
Merged
Conversation
…erage
The frozen PyInstaller core resolved intent modules dynamically via
importlib, so each domain had to be hand-listed in tuttle-rpc.spec's
hiddenimports. The 'imports' (and latently 'invoice_notes') domain was
missing, so the distributed app failed with "No module named
'tuttle.app.imports'" on document-import commit, despite working in dev.
- Replace the hand-maintained hiddenimports list with
collect_submodules("tuttle.app") so any present-or-future domain is
always bundled.
- Add unit guards (test_rpc_dispatch.py): every tuttle/app/<domain> is an
importable package, and collect_submodules covers every domain.
- Add scripts/smoke_core.py: spawns the actual frozen binary and probes
every domain; wired into CI (pre-packaging, fail-fast), pack_electron,
and a `just smoke-core` recipe.
- Rename build terminology "sidecar" -> "core" throughout.
Co-authored-by: Cursor <cursoragent@cursor.com>
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.
Summary
The distributed macOS app failed when committing from AI Document Import with:
Root cause: the frozen PyInstaller core resolves intent modules dynamically via
importlib(tuttle.app.{domain}.intent), so PyInstaller's static analyzer can't trace them. Each domain therefore had to be hand-listed intuttle-rpc.spec'shiddenimports. Theimportsdomain (and, latently,invoice_notes) was never added, so it was silently dropped from the release binary — even though it worked fine in dev. A missing__init__.py(fixed in #351) was necessary but not sufficient.Changes
hiddenimportslist withcollect_submodules("tuttle.app"), so every present and future domain is always bundled. Verified it discovers all 19 intent modules, includingimportsandinvoice_notes.tuttle_tests/test_rpc_dispatch.py):tuttle/app/<domain>is a real importable package with exactly one*Intentclass (catches missing__init__.py)collect_submodulescovers every on-disk domain (catches spec drift); skips if PyInstaller absentscripts/smoke_core.py): spawns the actual frozen binary and probes every domain, asserting none returns "No module named". Wired into CI before the expensive packaging step (fail-fast), intopack_electron.py, and as ajust smoke-corerecipe.Why the previous coverage missed this
The existing
test_rpc_dispatch.pyruns against the source tree, so it structurally cannot catch a packaging regression in the frozen binary. The new guards target the exact mechanism the release build depends on.Test plan
uv run pytest -q— 336 passedtuttle/app/imports/__init__.pymakes both unit guards fail with a precise diagnosticscripts/smoke_core.py— all 19 domains bundled & importableMade with Cursor