Skip to content

🤖 feat: add project switcher to the scratch creation page - #3817

Merged
ibetitsmike merged 8 commits into
mainfrom
mike/mobile-chat-ax2b
Aug 7, 2026
Merged

🤖 feat: add project switcher to the scratch creation page#3817
ibetitsmike merged 8 commits into
mainfrom
mike/mobile-chat-ax2b

Conversation

@ibetitsmike

@ibetitsmike ibetitsmike commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a project switcher to the scratch chat creation page so mobile users can see the scope they are creating in and jump to a project's creation page without reopening the sidebar. Also fixes a pre-existing unit-test-harness isolation bug that this PR's new files exposed on CI.

Background

On narrow viewports the sidebar auto-collapses after navigation, so the most prominent mobile entry point (the + next to "Chats") lands on a bare scratch composer with no visible scope and no way to reach a project. The project creation page (ProjectPage) already had a working inline project selector; the scratch page had none because ChatInput skipped CreationControls entirely for kind="scratch".

Implementation

  • Extracted the project select from CreationControls into a shared CreationProjectSelect component (styling, tooltip, aria-label, and data-testid="project-selector" preserved).
  • CreationControls now renders the shared component; behavior unchanged.
  • Scratch creation composers render a header row with a "Scratch" picker listing Scratch plus all user projects; picking a project calls beginWorkspaceCreation(path) and lands on that project's creation page.
  • Added ScratchPage.stories.tsx with laptop + phone Pixel viewports and a pinned mobile1 local viewport; the play function opens a new scratch chat and asserts the switcher shows "Scratch".

Test-isolation fixes (tests/ui/)

Adding files to the repo shifted bun's test-file discovery order on CI, which exposed a pre-existing isolation bug: some test files tear down with globalThis.document = undefined, and installDom() snapshots taken after such a teardown restore that poisoned state, propagating an undefined document to later file boundaries. Modules that probe the environment at module-eval time then break at those boundaries: @react-dnd/asap crashes on document.createTextNode (poisoning react-dnd's exports into TDZ and cascading ~35 failures across ProjectSidebar/AgentListItem/GitStatusIndicatorView tests), and menu components mis-bind similarly. Fix at the harness chokepoint:

  • installDom()'s uninstaller now self-heals: if document/window would end up undefined, it reinstalls a baseline DOM so file boundaries stay healthy.
  • tests/ui/dom.ts eagerly evaluates react-dnd right after the baseline bootstrap, pinning asap's scheduler binding to a healthy DOM.
  • New tests/ui/domIsolation.test.ts guards both invariants (red-green verified against each half of the fix independently); wired into make test-unit.

A second isolation class surfaced on the next CI orders: bun's mock.module registrations are process-global and survive suite completion, so file-scope stubs (most commonly a closed-Dialog-renders-null stub) leaked into later files and emptied unrelated suites (GitStatusIndicatorView, TaskGroupListItem, AgentListItem). Fixes:

  • New tests/ui/moduleMocks.ts helper restoreModulesAfterSuite re-registers captured real exports in afterAll; applied to every file-scope Dialog mocker that lacked restoration (SshPromptDialog, PlanFileDialog, WorkflowRunToolCall, SavedQuerySqlDialog, ProjectDeleteConfirmationModal, MemoryTab, ProjectPage.autofocus).
  • TaskGroupListItem menu-shortcut test queries with findByRole(..., { hidden: true }): PositionedMenu keeps content visibility-hidden until a rAF placement pass, and shortcut handling must not depend on that timing.
  • The bun-only domIsolation.test.ts is excluded from Jest's integration run (bun:test cannot resolve under Jest).

Validation

  • Manual UAT at 390x844 in a dev-server sandbox: picker present on scratch page, Scratch to project navigation works, ProjectPage picker unaffected, scratch send still works, desktop width also shows the picker, no right-edge overflow.
  • Storybook test-runner: ScratchPage + ProjectPage suites pass (13/13).
  • Full bun test src locally: 10417 pass, 1 fail that is pre-existing on main (WorkspaceService bash monitor wakes, environment-dependent, passes on CI; verified by running the same file on a clean main worktree).
  • Harness fix red-green: removing the self-heal fails the boundary-invariant test; removing the react-dnd pin fails the poisoned-boundary eval test.

Risks

Low for the feature: CreationControls is a mechanical extraction with unchanged props and markup; the new picker only renders for scratch creation composers, which previously had no controls at all.

Low-medium for the harness fix: the self-heal changes what installDom() teardown leaves behind (a baseline DOM instead of undefined). Tests that intentionally assert a DOM-free environment after uninstalling would be affected; the full unit suite shows none exist today.

Pains

The CI Test / Unit failure did not reproduce locally (bun's file discovery order differs per filesystem and bun test reorders CLI file arguments), so the mechanism had to be reconstructed from CI logs plus targeted probe files measuring document/MutationObserver state at file boundaries.


Generated with mux • Model: anthropic:claude-fable-5 • Thinking: xhigh

On mobile the sidebar closes after navigating to the scratch new chat
page, leaving no visible scope and no way to reach a project's creation
page. Extract the CreationControls project select into a shared
CreationProjectSelect and render it on the scratch composer with a
Scratch entry plus all user projects; picking a project navigates to
that project's creation page.
@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ae43f6dc0e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/browser/components/ScratchPage/ScratchPage.stories.tsx Outdated
Comment thread src/browser/components/ScratchPage/ScratchPage.stories.tsx
@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: b35bed781b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: 819fc09003

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

Reviewed commit: d4b5f167d6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: 39a073f9e7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

Reviewed commit: 6ff5420ef8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The CI shard intermittently never mounts Radix portal content in happy-dom
(documented fragility), timing out the menu query. Mock PositionedMenu with
an inline conditional render like ProjectSidebar.test.tsx does, restoring
the real module after the suite.
…olves correctly

mock.module resolves relative specifiers against the calling file, so the
restore helper registered a bogus virtual module and left the real
PositionedMenu mocked, which poisoned later suites' own mock.module
registrations (useBrowserBridgeConnection's API mock stopped applying).
The helper now rejects relative paths.
@ibetitsmike
ibetitsmike added this pull request to the merge queue Aug 7, 2026
Merged via the queue into main with commit 5da66a7 Aug 7, 2026
21 of 22 checks passed
@ibetitsmike
ibetitsmike deleted the mike/mobile-chat-ax2b branch August 7, 2026 22:17
@mux-bot mux-bot Bot mentioned this pull request Aug 8, 2026
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.

1 participant