Skip to content

Remember workspace mode across app launches#691

Merged
marksftw merged 3 commits into
masterfrom
codex-remember-mode-across-launches-maple
Jul 25, 2026
Merged

Remember workspace mode across app launches#691
marksftw merged 3 commits into
masterfrom
codex-remember-mode-across-launches-maple

Conversation

@marksftw

@marksftw marksftw commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Persist the selected Chat or Agent workspace mode in local storage.
  • Restore a saved Agent selection before the desktop router is created, so the first rendered route is correct.
  • Save only completed user mode-switch actions, preventing authentication, callback, settings, or other automatic navigation from overwriting the choice.
  • Add unit coverage for defaults, repeated changes, unavailable storage, deep links, and desktop restoration.

Why

Maple had no durable workspace-mode preference and always initialized from the home route, so users who selected Agent Mode returned to Chat Mode after every app relaunch.

Impact

Desktop users now resume their last explicitly selected workspace mode. Missing or invalid preferences safely default to Chat Mode, while web launches, deep links, callback routes, and failed navigation keep their existing behavior.

Validation

  • bun test — 224 passed, 0 failed
  • Focused mode tests — 9 passed, 0 failed
  • Repository pre-commit hook — Prettier, TypeScript/Vite production build, and full Bun test suite passed
  • bun run typecheck — passed
  • bun run lint — 0 errors (12 pre-existing warnings)
  • Tauri debug app-only bundle — passed
  • Desktop restart verification:
    • Chat → Agent → kill/relaunch → Agent restored
    • Agent → Chat → kill/relaunch → Chat restored

Closes #651

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 24, 2026

Copy link
Copy Markdown

Deploying maple with  Cloudflare Pages  Cloudflare Pages

Latest commit: 137ee30
Status: ✅  Deploy successful!
Preview URL: https://fd63a10d.maple-ca8.pages.dev
Branch Preview URL: https://codex-remember-mode-across-l.maple-ca8.pages.dev

View logs

@marksftw marksftw left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review report

Outcome: No blocking findings remain after the follow-up revision.

Scope and design

  • The change is appropriately narrow: one storage service, bootstrap restoration, mode-switch persistence, and focused tests.
  • Restoring the route before importing the app ensures the router's first location snapshot is correct and avoids a visible Chat-to-Agent redirect.
  • Persistence now happens only from explicit mode-switch actions. Automatic authentication, callback, settings, deep-link, and history navigation cannot accidentally rewrite the user's saved preference.
  • Missing, invalid, or unavailable local storage safely falls back to Chat Mode without disrupting navigation.

Finding validated and fixed

The original implementation observed the root route globally. An ordinary sign-in or automatic redirect to / could therefore save Chat Mode and erase a previous Agent selection. Commit f5a6ef1 removes that global observation and records the mode at the successful user switch instead. This is both safer and smaller: the follow-up added 4 lines and removed 49.

Considered without expanding scope

  • Restoring /agent uses the same eligibility behavior as navigating directly to the existing /agent route. Adding a new asynchronous feature-flag or billing route guard would change existing routing policy and substantially broaden this issue.
  • Direct URLs and browser Back/Forward do not change the saved preference. This preserves the last mode the user explicitly selected and prevents incidental navigation from clobbering it.

Verification

  • Focused persistence tests: 9 passed, 0 failed
  • Full test suite: 224 passed, 0 failed
  • Typecheck and production Vite build passed
  • Lint: 0 errors; 12 pre-existing warnings
  • Desktop restart acceptance flow passed in both directions:
    • Chat → Agent → kill/relaunch → Agent
    • Agent → Chat → kill/relaunch → Chat

The implementation satisfies issue #651 without introducing a broader routing abstraction or invasive authentication changes.

@marksftw

Copy link
Copy Markdown
Contributor Author

UX testing passed. The selected mode persists across launches.

@marksftw
marksftw marked this pull request as ready for review July 25, 2026 02:16
@marksftw
marksftw requested a review from AnthonyRonning July 25, 2026 02:17
@AnthonyRonning

Copy link
Copy Markdown
Contributor

The implementation looks good overall, especially the follow-up that persists only a completed, explicit mode switch. I agree that this should not grow into an asynchronous routing or entitlement refactor.

There is one small account-handoff edge case worth covering: workspaceMode is device/WebView-global and is restored before Maple knows which account is authenticated. Because successful logout and account deletion currently leave the key untouched, the next account using the same desktop can inherit the previous account's Agent selection.

This is not an Agent data-isolation problem—the native Agent state remains account-scoped—and I would not add a feature-flag or billing guard to /agent as part of this PR. PR #607 explicitly defined agent_mode as a rollout/sidebar-entry gate rather than an authorization boundary. I would keep this fix much narrower: reset the launch preference to Chat when the current account successfully leaves.

Concretely, I would add a tiny helper beside the existing preference functions:

export function resetWorkspaceModePreference(
  storage: WorkspaceModeStorage | null = getBrowserStorage()
): void {
  rememberWorkspaceMode("chat", storage);
}

Using the existing writer keeps the same storage-failure behavior and avoids expanding WorkspaceModeStorage with removeItem.

Then import and call it in the successful account-exit paths:

await os.signOut();
resetWorkspaceModePreference();

The normal logout call sites are:

  • frontend/src/components/settings/SettingsLayout.tsx
  • frontend/src/components/GuestPaymentWarningDialog.tsx
  • frontend/src/components/VerificationModal.tsx

For frontend/src/components/settings/DeleteAccountSettings.tsx, I would reset it once remote account deletion has been confirmed, before the final reload. That also handles the case where deletion succeeded but a later local sign-out step reports an error:

if (!deletionConfirmed) {
  await os.confirmAccountDeletion(confirmationCode, secret);
  deletionConfirmed = true;
  setIsAccountDeleted(true);
  cleanupBlockRef.current.retainUntilNextSession();
}

resetWorkspaceModePreference();

One focused test should be enough:

test("resets the launch preference to Chat when an account leaves", () => {
  const storage = new MemoryStorage();
  rememberWorkspaceMode("agent", storage);

  resetWorkspaceModePreference(storage);

  expect(getStoredWorkspaceMode(storage)).toBe("chat");
});

That keeps the current bootstrap approach, avoids cross-account carryover, and adds only a helper plus four small call-site changes. If Maple later decides the feature flag must act as a true revocation or operational kill switch, that should be handled separately with an authoritative /agent capability boundary rather than expanding this local-preference PR.

@marksftw

Copy link
Copy Markdown
Contributor Author

responding to the feedback

@marksftw

Copy link
Copy Markdown
Contributor Author

Implemented the account-handoff reset in 05891e6.

What changed:

  • Added resetWorkspaceModePreference() using the existing failure-tolerant preference writer.
  • Reset the saved launch mode only after successful sign-out in the Settings, guest-payment, and verification flows.
  • Reset it immediately after confirmed remote account deletion, before best-effort local cleanup/sign-out.
  • Added a focused Agent → Chat reset test.

Validation:

  • Focused preference tests: 7 passed
  • Full frontend suite: 225 passed, 0 failed (674 assertions)
  • Typecheck, Prettier, lint, and production build passed; lint remains at 0 errors with the same 12 existing warnings
  • Built and launched the desktop app against the dev OpenSecret, billing, and flags endpoints
  • Desktop UI acceptance test: Chat → Agent → quit/relaunch restored Agent; Agent → logout → quit/relaunch opened the unauthenticated home /, not /agent
  • Smoke-tested the login chooser and email/password form after logout

This keeps the change limited to explicit successful account exits and does not alter entitlement or routing logic.

@marksftw

Copy link
Copy Markdown
Contributor Author

resolving conflicts and testing

@marksftw
marksftw force-pushed the codex-remember-mode-across-launches-maple branch from 05891e6 to 137ee30 Compare July 25, 2026 03:37
@marksftw

Copy link
Copy Markdown
Contributor Author

Final validation report

PR #691 is rebased onto master at 8f5f174; the current head is 137ee30. The four rebase conflicts were import-only overlaps in the account-exit components. They were resolved by preserving master’s newer native-auth lifecycle work and reapplying the workspace-mode reset.

Automated validation

  • Full frontend suite: 248 passed, 0 failed (754 assertions)
  • Typecheck and targeted Prettier checks passed
  • Lint passed with 0 errors and the same 12 existing warnings
  • Production frontend build passed
  • Tauri debug app bundle passed
  • Packaged wiring was verified to use the dev OpenSecret, billing, and flags endpoints, with no local OpenSecret or billing URL embedded

Agent-driven UI validation

  • Chat → Agent → quit/relaunch restored Agent
  • Agent → logout → quit/relaunch opened the unauthenticated home /, not /agent
  • The freshly rebased app quit and relaunched successfully and retained workspaceMode=chat after logout
  • The login chooser and email/password form rendered correctly
  • A deliberately invalid, non-sensitive login received the expected dev API error, confirming the packaged app was connected to the dev environment

Human UI validation

The requester independently tested the final dev-backed build and reports that the human UI testing succeeded as well.

No blocking findings remain from the rebase, automated checks, agent-driven UI testing, or human UI testing.

@marksftw

Copy link
Copy Markdown
Contributor Author

ready for review

@AnthonyRonning AnthonyRonning left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved. We reviewed the final implementation and smoke-tested this exact head end to end in the packaged macOS app against the managed OpenSecret, billing, and feature-flag stack. Chat and Agent persistence, cold restarts, functional model paths, billing state, and the logout reset all passed. The code is focused, failure-tolerant, and appropriately avoids broader routing or authentication complexity. No remaining concerns.

@AnthonyRonning

Copy link
Copy Markdown
Contributor

Everything looks good from our side. The current head passed code review and full macOS GUI smoke testing against the managed OpenSecret, billing, and feature-flag stack, including Chat and Agent persistence, cold restarts, functional model paths, billing state, and the logout reset. The implementation is clean, narrowly scoped, and makes the right tradeoff without extra complexity. You can merge whenever you are ready.

@marksftw
marksftw merged commit e36e5d0 into master Jul 25, 2026
16 checks passed
@marksftw
marksftw deleted the codex-remember-mode-across-launches-maple branch July 25, 2026 13:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remember mode across app launches

2 participants