fix(desktop): make fresh installs always populate lessons (v0.2.8)#33
Merged
GigaChadGRC merged 1 commit intomainfrom Apr 18, 2026
Merged
fix(desktop): make fresh installs always populate lessons (v0.2.8)#33GigaChadGRC merged 1 commit intomainfrom
GigaChadGRC merged 1 commit intomainfrom
Conversation
The headline goal: a fresh download of the macOS / Windows desktop build must reliably show all 184 lesson modules on first launch, and content updates on later releases must not wipe user progress. Root causes addressed: - macOS Gatekeeper / hardened runtime silently killed the bundled Node sidecar because nested executables were not signed with the JIT and library-validation entitlements V8 + Prisma's native query engine require. Adds src-tauri/Entitlements.plist and rewrites the unsigned fallback codesign step in .github/workflows/desktop-build.yml to sign inner executables and dylibs first, then deep-sign the .app with the same entitlements. The Rust launcher also strips the com.apple.quarantine xattr from its own .app on startup. - Sidecar was blind on stdout (only stderr captured) so silent failures were invisible. Now writes both next-stderr.log and next-stdout.log alongside sidecar.log; /api/diagnostics surfaces them with platform- aware Prisma-engine and app-data-dir resolution (no more darwin-arm64 hardcoding). - ensure_writable_db's size-only update logic either dropped new content or wiped user progress on launch. Replaced with: copy bundled seed only when no user DB exists, and reconcile content via a Next.js instrumentation hook that compares the bundled data/release-library/session-content.json generatedAt against a new UserStats.seedGeneratedAt column. Existing UserStats, TopicProgress, SessionCompletion, ReadingPosition, CapstoneRubricState, and QuestionAnalytics rows are preserved across releases. Seed logic shared with scripts/seed-release-library.ts via a new src/lib/seed-release-library.ts module. - Stale duplicate dev.db files that Next standalone trace pulled in could win find_bundled_db's lookup. prepare-tauri-sidecar.mjs now prunes them, enforces a >1MB sanity guard on the canonical seed, and the Rust find_bundled_db only consults a single canonical path. Other fixes uncovered by the audit: - Reapply do-while strip and safe entity decode order in src/app/api/news/route.ts (CodeQL: incomplete multi-character sanitization + double unescaping had regressed). - kill_stale_port_holder now identifies the holder of port 1430 via ps/tasklist and refuses to kill -9 anything that isn't node. - firstTryCorrect counter in /api/session/complete actually increments for repeat questions (was guarded by `&& !existing` inside the update-only branch, so it never fired). - toKeyTerms keeps short acronyms (AI, GRC, ISO, SOC) by changing the filter from length > 2 to length >= 2 plus a stopword set; curriculum:validate now asserts AI Governance keeps "AI". Verified locally: - npm run lint, library:verify, qa Playwright (0/10 pages with issues) - /api/diagnostics returns sessionContentCount=184, prismaStatus= connected, seedGeneratedAt set on cold AND warm boot - Direct exec of the bundled Tauri sidecar (resources/next-standalone/ server.js under resources/node-runtime/bin/node) boots, applies the release snapshot, preserves a hand-inserted TopicProgress row across a NULL seedGeneratedAt restart - cargo check --release on src-tauri compiles cleanly
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
Fixes the long-standing "fresh install shows 'Loading...' with no lessons" problem on the macOS desktop build, plus eight other bugs uncovered by a deep audit. Bumps to
v0.2.8.The core insight from the audit was that fresh installs were failing for multiple compounding reasons, not one — and existing users were being silently wiped on upgrade. This PR addresses every root cause and adds runtime self-healing so users never see an empty library again.
Tier 1 — fixes for "lessons not populating"
src-tauri/Entitlements.plist+ correct codesign order indesktop-build.ymlnodebecause nested executables lacked JIT (V8) and library-validation (Prisma engine) entitlements. Sign inner execs/dylibs first, then deep-sign the.appwith the same entitlements.strip_quarantine_from_self()insrc-tauri/src/lib.rsnext-stdout.log)ensure_writable_dbsize-comparison with copy-only-if-missing + content-versioned reseed viasrc/instrumentation.tsUserStats/TopicProgress/SessionCompletion/ReadingPosition/CapstoneRubricState/QuestionAnalyticswhen it did copy. New approach: bundle ships seeded DB; runtime reconcilesSessionContentagainstdata/release-library/session-content.jsonkeyed by newUserStats.seedGeneratedAt. Preserves all user progress across upgrades.dev.dbfiles inprepare-tauri-sidecar.mjs, add >1MB sanity guard, single canonicalfind_bundled_dbpathprisma/dev.dbcopies that could win the lookup.Tier 2 — other audit findings
stripHtmlin/api/newsregressed; reapplied do-while strip + safe entity decode order (CodeQL: incomplete multi-character sanitization + double unescaping).kill_stale_port_holdernow identifies the holder of port 1430 viaps/tasklistand refuses tokill -9anything that is notnode— surfaces a clear error instead of nuking unrelated dev servers.firstTryCorrectanalytics counter was guarded by&& !existinginside the update-only branch, so it never incremented. Fixed.toKeyTermsfilter dropped 3-letter acronyms (AI, GRC, ISO, SOC). Changed tolength >= 2+ small stopword set;npm run curriculum:validatenow asserts "AI Governance" keeps "AI"./api/diagnosticsis now platform-aware: derives Prisma engine filename fromprocess.platform/process.arch, surfaces app-data dir, both log files, andseedGeneratedAt.Schema
Adds nullable
UserStats.seedGeneratedAt String?. Backwards-compatible — populated by the instrumentation hook on next launch viaprisma db pushsemantics (no destructive migration).Test plan
Verified locally on macOS arm64:
npm run lint— cleannpm run library:verify— 184 modules, all citation rates 100%BASE_URL=http://127.0.0.1:3199 npm run qa— 0/10 pages with issues/api/diagnostics→sessionContentCount: 184,prismaStatus: connected,seedGeneratedAtset, both stdout+stderr logs surfaced/api/news→ 25 articles, all 5 sources OKseedGeneratedAt, deleted oneSessionContentrow, restarted server → instrumentation upserted 184 rows, restored the missing one, preserved a hand-insertedTopicProgressrowrelease snapshot ... already applied; skipping reseednpm run tauri:prepareproduces canonical 9MBdev.dband bundleddata/release-library/session-content.jsonresources/next-standalone/server.jsunderresources/node-runtime/bin/node) boots, applies snapshot, serves 184 modulescargo check --releasecleanDocs
README.md— replaced "lessons missing" troubleshooting with a self-healing note + log paths +xattrworkaround.CHANGELOG.md— new top-level changelog file with full v0.2.8 entry.desktop-build.ymlrelease-notes body — mentions hardened-runtime entitlements + self-healing.