test(win): Pro-on-Windows integration + capture/settings fixes - #77
test(win): Pro-on-Windows integration + capture/settings fixes#77Anurag-Wednesday wants to merge 9 commits into
Conversation
Reflect is pure aggregation over observations the capture pipeline already writes - it adds no capture of its own. Replay's port put those observations on Windows, so Reflect needs no Windows implementation, only the gate flip. Audit found zero platform coupling on the whole path: crm/reflect.ts imports only core getDB, ./schema and ./utils; its IPC (crm:day-reflection, crm:week-reflection) is platform-free; ReflectScreen carries no native code. The only native dep on the path is better-sqlite3, already proven on Windows by core. Catalog copy and the screen have no Mac/Cmd/Option strings, so no copy neutralization was needed. The single catalog edit is sufficient for the same reason it was for Replay: nav lock is entitlement-only (locked: !isPro) and the screen gate routes through proFeatureComingSoon, with no core file special-casing the route - so nav, gating and copy all light up from the one source of truth. Tests: WIN_PORTED is parameterized, so adding reflect derives four assertions (live on win32; win32-only and not linux by implication; the "exactly the ported features are win32-supported" invariant; and that proFeatureComingSoon does not gate it). Reflect's own logic keeps its existing 13-case real-DB integration coverage. Verified: npm test 3161 passed; node, web and pro typechecks clean; eslint clean on both touched files. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015U3TnnNULxfCb4TsjAGjiC
LLMService read its persisted state (active model + user settings) from the
constructor. `llm` is a module-level singleton, so it is constructed while
index.ts's IMPORTS are still evaluating - which under ESM completes before
index.ts's own body runs unifyUserDataPath() -> app.setPath('userData', ...).
Every path resolved at construction therefore pointed at the PRE-override
profile.
Two real consequences:
1. Production: at construction the canonical-dir migration ("My Memories" /
"my-memories" -> "Off Grid AI Desktop") has not run yet, so a user's saved
settings and active model could be silently missed and replaced by
defaults.
2. Harness: an OFFGRID_USER_DATA temp profile was ignored outright. A probe
confirmed the constructor resolving the REAL profile while
OFFGRID_USER_DATA pointed at the temp dir. This is what made
e2e/settings-sections.spec.ts "resource mode survives a relaunch" fail -
the setting persisted correctly but was never read back.
Writes never had the bug: persist() goes through the settingsFile getter,
which resolves late. This was read-side-only asymmetry. It is also the exact
hazard the activeModelFile / settingsFile getters were introduced to avoid
(see the comment at llm.ts:98) - calling resolveModel() and reading the
settings file from the constructor defeated them.
Fix: drop the constructor and load once, lazily, via ensureLoaded(), wired
into the ten public entry points that depend on persisted state or model
paths. hasVision/modelsExist/activeModelInfo keep their deliberate
resolveModel() call so a newly activated model is still picked up.
Tests: 5 cases in llm-lazy-settings-load.test.ts, built on the configureRuntime
seam so they reproduce the production shape (construct first, choose the
profile second). Includes a two-profile case that pins the defect directly -
configure A, construct, switch to B, and assert B is read. 4 of the 5 fail
without this change and all 5 pass with it. e2e/settings-sections.spec.ts is
3/3 (was 2/3).
Verified: npm test 3163 passed; node + web typechecks clean; eslint 0 errors.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015U3TnnNULxfCb4TsjAGjiC
Two features in one PR because Notifications has no content without Day:
proactive.ts builds its notifications from getDayPlan / getEventPrep (both
from ahead.ts) plus listUpcomingEvents, so porting Notifications alone would
have lit up an empty surface on Windows.
Neither needs a pro-side change. A sweep for
osascript|process.platform|darwin|win32|pgrep|pkill|execFile|spawn returns
zero hits across day.ts, day-layout.ts, ahead.ts, ahead-heuristics.ts,
calendar.ts, DayView.tsx, TodoCard.tsx, notify.ts, proactive.ts,
proactive-window.ts, NotificationList.tsx, notification-target.ts and
notification-routing.ts. No Mac/macOS/Cmd/Option copy in any of those surfaces
or in either catalog entry, so no neutralization was needed.
Day's two data sources both work on Windows now: the calendar comes from
connectors (HTTP), and the activity half reads observations, which Replay's
port put there. Notification delivery is Electron's Notification guarded by
isSupported(), and core already calls setAppUserModelId (index.ts:301) - which
Windows REQUIRES for a toast to appear at all.
Also fixes the landing-screen landmine, which Day forced. App.tsx opened on
`isPro && isMac() ? 'day' : 'models'` - a platform decision living OUTSIDE the
capability seam. It was right only by accident: it agreed with the catalog
while Day was macOS-only, and would have stranded a ported Day on Windows,
with nav and gating lighting Day up from `platforms` while the landing screen
still asked isMac(). The decision moves into proCatalog as a pure
landingView(platform, isPro), so the landing screen can never disagree with
nav and gating again. App.tsx no longer imports isMac.
Tests: WIN_PORTED gains day + notifications, deriving eight assertions from
the parameterized suite. Plus a new five-case landingView describe, including
the stranded-Day regression guard (landingView('win32', true) must be 'day' -
the old isMac rule returns 'models') and a DRY case asserting landingView
agrees with the day feature's own platforms list on every platform. Verified
by reverting to the old rule: two tests fail.
Verified: npm test 3169 passed; node + web + pro typechecks clean; eslint 0 new
errors (App.tsx's 9 are pre-existing, identical count on main). e2e 73 passed /
1 failed, the one failure being settings-sections "resource mode survives a
relaunch" - a pre-existing core bug on main, fixed separately in #73 and not
reachable from this change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015U3TnnNULxfCb4TsjAGjiC
…into test/win-pro-integration-0806
… test/win-pro-integration-0806
…win-pro-integration-0806 # Conflicts: # src/renderer/src/lib/__tests__/proCatalog.lookup.test.ts
The Windows Pro port migrated the feature nav to the per-feature capability seam but left the Settings screen on the old blanket !isMac gate, so on Windows the Capture health panel (frame/observation counts) and the Proactive-delivery toggle it hosts were hidden behind a 'Pro on macOS' placeholder - even though the capture engine runs and Notifications is nav-enabled. Render the registered Capture section on every platform where Pro is active; the placeholder is now only the free-build upsell. Drop the stale macOnly flags on the capture/proactive settings slots. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xr51GbAPDvPYL5yhXj4gva
A model context window near the 2048 clamp floor overflows the observation distill prompt, silently halting frame->observation processing so Day and Reflect never populate - with no user-facing reason. Add MIN_OBSERVATION_CTX (4096) as the shared floor the pipeline needs, a pure isContextOverflowError classifier so the distill can treat an overflow as terminal (not retry forever), and a Settings context-window hint that warns when the effective window is below that floor. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xr51GbAPDvPYL5yhXj4gva
Update the D31 registry-seam test that asserted the old Mac-only gate (capture withheld on win32) to the new behavior: capture is ported to Windows, so its registered section renders on win32 and the free-build placeholder does not. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xr51GbAPDvPYL5yhXj4gva
|
ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing |
📝 WalkthroughWalkthroughThe PR adds lazy persisted-settings loading, context-window overflow detection and warnings, Windows support for selected Pro features, platform-aware landing navigation, and cross-platform capture settings rendering. ChangesLLM context and initialization
Windows Pro catalog and settings
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| // Where to open: derived from the per-feature capability seam (see landingView), so | ||
| // the landing screen can never disagree with nav and gating about whether Day is | ||
| // available on this platform. | ||
| const [viewMode, setViewMode] = useState<ViewMode>(landingView(currentPlatform(), isPro)) |
There was a problem hiding this comment.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/main/__tests__/llm-lazy-settings-load.test.ts`:
- Around line 47-57: Add a regression test alongside the existing lazy settings
case that seeds a model file and active-model.json, constructs LLMService before
configureRuntime(), then configures the profile and verifies activeModelInfo()
resolves the model from that profile on first use. Ensure the test specifically
covers deferred active-model resolution through ensureLoaded().
In `@src/renderer/src/App.tsx`:
- Around line 229-232: Update the root-path routing effect in App.tsx to use
landingView(currentPlatform(), isPro) instead of unconditionally selecting day,
preserving the selector’s capability-based result for free, Windows Pro, and
unsupported-platform users. Add App-level root-path integration coverage in
src/renderer/src/lib/__tests__/proCatalog.lookup.test.ts lines 173-212 for those
three user scenarios; both sites require changes.
In `@src/renderer/src/lib/ctx-options.ts`:
- Around line 42-48: The effective-context check in ctx-options.ts#L42-L48 must
run before the model-cap hint so it takes priority when the engine’s effective
context is below MIN_OBSERVATION_CTX; preserve the existing warning text and
other hint behavior. Add a regression assertion in
src/renderer/src/lib/__tests__/ctx-options.test.ts#L62-L73 using ctxSize 65536,
effectiveCtxSize 2048, and modelMaxCtx 32768, verifying the observation warning
is returned instead of the model-cap hint.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 6155446d-2795-42ad-876a-e0a5e484adb8
📒 Files selected for processing (13)
src/main/__tests__/llama-error.test.tssrc/main/__tests__/llm-lazy-settings-load.test.tssrc/main/llama-error.tssrc/main/llm.tssrc/renderer/src/App.tsxsrc/renderer/src/components/Settings.tsxsrc/renderer/src/components/__tests__/Settings.pro-sections.test.tsxsrc/renderer/src/components/pro/proCatalog.tssrc/renderer/src/components/pro/proSettingsCatalog.tssrc/renderer/src/lib/__tests__/ctx-options.test.tssrc/renderer/src/lib/__tests__/proCatalog.lookup.test.tssrc/renderer/src/lib/ctx-options.tssrc/shared/llm-defaults.ts
| it('picks up a data dir configured AFTER the instance was constructed', () => { | ||
| // Construct FIRST — mirrors the module-level singleton being built during imports. | ||
| const svc = new LLMService() | ||
| // ...then point the runtime at the profile, as index.ts's body does later. | ||
| seedSettings(tmp, { performanceMode: 'extreme', temperature: 0.42 }) | ||
| configureRuntime({ dataDir: tmp }) | ||
|
|
||
| const s = svc.getSettings() | ||
| expect(s.performanceMode).toBe('extreme') | ||
| expect(s.temperature).toBe(0.42) | ||
| }) |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Add a lazy active-model regression case.
These tests cover llm-settings.json only. ensureLoaded() also defers active-model.json resolution.
Seed a model file and active-model.json, construct LLMService before configureRuntime(), then assert that activeModelInfo() reads the model from the profile configured at first use.
As per coding guidelines, "Add regression tests in the same change for every behavior change, including bug cases, branches, conditions, error paths, and copy or contract changes."
Proposed regression test
+const seedActiveModel = (dataDir: string, primary: string): void => {
+ const modelsDir = path.join(dataDir, 'models')
+ fs.mkdirSync(modelsDir, { recursive: true })
+ fs.writeFileSync(path.join(modelsDir, primary), '')
+ fs.writeFileSync(
+ path.join(modelsDir, 'active-model.json'),
+ JSON.stringify({ id: 'late-model', primary })
+ )
+}
+
+it('reads the active model from the profile configured at first use', () => {
+ const svc = new LLMService()
+ seedActiveModel(tmp, 'late-model.gguf')
+ configureRuntime({ dataDir: tmp })
+
+ expect(svc.activeModelInfo()).toEqual({ id: 'late-model', vision: false })
+})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| it('picks up a data dir configured AFTER the instance was constructed', () => { | |
| // Construct FIRST — mirrors the module-level singleton being built during imports. | |
| const svc = new LLMService() | |
| // ...then point the runtime at the profile, as index.ts's body does later. | |
| seedSettings(tmp, { performanceMode: 'extreme', temperature: 0.42 }) | |
| configureRuntime({ dataDir: tmp }) | |
| const s = svc.getSettings() | |
| expect(s.performanceMode).toBe('extreme') | |
| expect(s.temperature).toBe(0.42) | |
| }) | |
| const seedActiveModel = (dataDir: string, primary: string): void => { | |
| const modelsDir = path.join(dataDir, 'models') | |
| fs.mkdirSync(modelsDir, { recursive: true }) | |
| fs.writeFileSync(path.join(modelsDir, primary), '') | |
| fs.writeFileSync( | |
| path.join(modelsDir, 'active-model.json'), | |
| JSON.stringify({ id: 'late-model', primary }) | |
| ) | |
| } | |
| it('reads the active model from the profile configured at first use', () => { | |
| const svc = new LLMService() | |
| seedActiveModel(tmp, 'late-model.gguf') | |
| configureRuntime({ dataDir: tmp }) | |
| expect(svc.activeModelInfo()).toEqual({ id: 'late-model', vision: false }) | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/__tests__/llm-lazy-settings-load.test.ts` around lines 47 - 57, Add
a regression test alongside the existing lazy settings case that seeds a model
file and active-model.json, constructs LLMService before configureRuntime(),
then configures the profile and verifies activeModelInfo() resolves the model
from that profile on first use. Ensure the test specifically covers deferred
active-model resolution through ensureLoaded().
Source: Coding guidelines
| // Where to open: derived from the per-feature capability seam (see landingView), so | ||
| // the landing screen can never disagree with nav and gating about whether Day is | ||
| // available on this platform. | ||
| const [viewMode, setViewMode] = useState<ViewMode>(landingView(currentPlatform(), isPro)) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Keep root-path routing consistent with landingView.
landingView returns 'models' for free users and unsupported platforms. The mount-only URL effect still maps '/' to 'day' at Line 295. That effect runs after Line 232 and overwrites the selector result.
src/renderer/src/App.tsx#L229-L232: route/throughlandingView(currentPlatform(), isPro)instead of the unconditionaldaymapping.src/renderer/src/lib/__tests__/proCatalog.lookup.test.ts#L173-L212: add an App-level root-path test for free, Windows Pro, and unsupported-platform users. The current selector-only tests cannot detect this overwrite.
As per coding guidelines, "Define mappings, routing rules, and capability checks once and reuse the single source of truth" and "Add user-behavior integration tests through real product boundaries."
📍 Affects 2 files
src/renderer/src/App.tsx#L229-L232(this comment)src/renderer/src/lib/__tests__/proCatalog.lookup.test.ts#L173-L212
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/renderer/src/App.tsx` around lines 229 - 232, Update the root-path
routing effect in App.tsx to use landingView(currentPlatform(), isPro) instead
of unconditionally selecting day, preserving the selector’s capability-based
result for free, Windows Pro, and unsupported-platform users. Add App-level
root-path integration coverage in
src/renderer/src/lib/__tests__/proCatalog.lookup.test.ts lines 173-212 for those
three user scenarios; both sites require changes.
Source: Coding guidelines
| // The EFFECTIVE window (after the RAM clamp) is what the engine actually runs with, so a value | ||
| // the model can't fit its distill prompt into silently stops screen-capture observations. Warn | ||
| // before that happens - this is the most consequential hint, so it wins over the ones below. | ||
| const effective = effectiveCtxSize && effectiveCtxSize > 0 ? effectiveCtxSize : ctxSize | ||
| if (effective && effective > 0 && effective < MIN_OBSERVATION_CTX) { | ||
| return `At ${asK(effective)} the context is small - on-device observations (Day, Reflect) may stop processing. Raise it to at least ${asK(MIN_OBSERVATION_CTX)}.` | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Prioritize the effective-context warning before the model-cap hint.
When ctxSize exceeds modelMaxCtx and effectiveCtxSize is below 4K, Line 39 returns before this branch. The UI then hides the warning for the context size that the engine actually uses.
Move the effective-context check before the model-cap return. Add a regression case with ctxSize: 65536, effectiveCtxSize: 2048, and modelMaxCtx: 32768.
src/renderer/src/lib/ctx-options.ts#L42-L48: evaluate the below-floor effective context before existing hint branches.src/renderer/src/lib/__tests__/ctx-options.test.ts#L62-L73: assert that the observation warning wins when the model-cap condition is also true.
📍 Affects 2 files
src/renderer/src/lib/ctx-options.ts#L42-L48(this comment)src/renderer/src/lib/__tests__/ctx-options.test.ts#L62-L73
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/renderer/src/lib/ctx-options.ts` around lines 42 - 48, The
effective-context check in ctx-options.ts#L42-L48 must run before the model-cap
hint so it takes priority when the engine’s effective context is below
MIN_OBSERVATION_CTX; preserve the existing warning text and other hint behavior.
Add a regression assertion in
src/renderer/src/lib/__tests__/ctx-options.test.ts#L62-L73 using ctxSize 65536,
effectiveCtxSize 2048, and modelMaxCtx 32768, verifying the observation warning
is returned instead of the model-cap hint.
Source: Coding guidelines
|



Windows Pro integration test branch — combines the open Pro-on-Windows PRs (#74 Day+Notifications, #73 LLM lazy settings, #72 Reflect) and adds fixes found while testing the build. Pairs with desktop-pro
test/win-pro-integration-0806(PR #40).Fixes in this branch
!isMacgate after the feature nav was migrated to the per-feature seam, so on Windows the Capture health panel (frame/observation counts) and the Proactive-delivery toggle were hidden behind a "Pro on macOS" placeholder even though the engine runs. Now renders the registered section on every Pro platform.MIN_OBSERVATION_CTXfloor, a pureisContextOverflowErrorclassifier (used pro-side to mark the failure terminal), and a Settings context-window hint that warns when the effective window is too small for observations. Fixes the silent empty-Day/Reflect trap.Tests
llama-error+ctx-optionsextended;Settings.pro-sectionsD31 seam updated to assert Windows Pro renders Capture. Coretsc(node+web) + protscclean; pre-push coverage gate green.🤖 Generated with Claude Code
https://claude.ai/code/session_01Xr51GbAPDvPYL5yhXj4gva
Summary by CodeRabbit
New Features
Bug Fixes
Greptile Summary
This PR enables additional Pro surfaces on Windows, lazily loads persisted LLM state after runtime path setup, and adds context-window diagnostics.
Confidence Score: 4/5
The PR appears safe to merge, with a non-blocking startup flicker that can misleadingly show Windows Pro users the Day purchase screen before Pro registration finishes.
The newly selected Windows Day landing route renders before the asynchronous Pro route registration, causing the existing null-view fallback to appear temporarily; no blocking runtime or data-integrity failure was established.
Files Needing Attention: src/renderer/src/App.tsx
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart LR A[Windows Pro launch] --> B[landingView selects Day] B --> C{Pro renderer registered?} C -- No --> D[UpgradeScreen fallback] E[Async Pro activation] --> F[Register Pro renderer] F --> C C -- Yes --> G[Render Day]Reviews (1): Last reviewed commit: "test(win): assert Windows Pro renders th..." | Re-trigger Greptile