Skip to content

🤖 refactor: decompose the ChatInput composer into deep hooks - #4013

Merged
ibetitsmike merged 18 commits into
mainfrom
mike/arch-composer-hooks
Aug 31, 2026
Merged

ibetitsmike merged 18 commits into
mainfrom
mike/arch-composer-hooks

Conversation

@ibetitsmike

@ibetitsmike ibetitsmike commented Aug 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Decomposes the 4,011-line ChatInput composer (ChatInputInner: 27 useState / 37 useRef / 32 useEffect conflating 7 concerns) into three deep hooks plus a pure send-preflight function, with a net -142 LOC delta. Behavior-preserving: no UX, keybind, or feature changes.

Background

Refactor #7 from the 2026-08-29 architecture review (evidence at f04e0f8a3). Every keystroke re-evaluated all 32 effects; the ~570-line send preflight and the four suggestion channels were untestable without DOM, and the four channels duplicated the same cursor/token logic.

Implementation

  • useComposerSuggestions (~500 lines): unifies the four suggestion channels (slash command, @-mention, skill, symbol) behind a single cursor/token seam. Self-contained per repo guidance: derives experiments, plugin enablement, and draft-discovery state internally; the boundary is the input seam plus identity. The token matcher is a pure exported function with DOM-free tests.
  • useComposerAttachments (~190): attachment state, processing counts, PDF/media-type validation, paste/drop/pick entry points, and draft-attachment persistence.
  • useComposerDraft (~160): storage-key scoping (workspace/creation/pending-draft), input persistence and restore across workspace switches, draft-review lifecycle.
  • prepareMessagePayload (~140): pure payload assembly extracted from handleSend; the remaining orchestrator is guard, async resolution, prepare, dispatch. Covered by table-driven unit tests, including the compaction-metadata ref gate (proven red-green).
  • index.tsx: 4,011 to 2,924 lines; effects reduced (32 to ~22). No manual memoization anywhere in the new hooks (React Compiler convention); the two effects that previously leaned on useCallback identity now key on data (editingMessage?.id) or hand out latest-ref wrappers (onReady API), and the document keydown listener uses a latest-ref handler attached only on visibility.
  • Caret state is keyed to the input it was measured against, falling back to end-of-text for external input changes (draft restores, persisted-state writes), preserving pre-extraction semantics; symbol auto-conversion propagates the converted text/cursor into the seam.

Net LOC

  • Production: +1,170 / -1,309 (-139)
  • Tests: +294 / -297 (-3)

Irreducible additions: useComposerSuggestions consolidates four previously duplicated channel implementations behind one seam (replacing ~640 deleted lines from index.tsx/CommandSuggestions.tsx); prepareMessagePayload + its tests (+211) make the send preflight unit-testable for the first time, offset by pruning mock-heavy structure-asserting tests (CommandSuggestions.test.tsx -79, symbolShortcuts.test.ts -92 net, collapsed into table-driven cases).

Validation

  • Remote dogfood UAT (regression-focused): 27 scenarios at desktop and 375 px widths, endorsed PASS covering typing, all four suggestion channels, attachments (image + PDF, button/paste/drag-drop), draft save/restore across workspace switches, edit mode + boundary confirm, destructive-command modal, Escape/Enter keybinds, and send/queue during a live stream. Both anomalies found were proven pre-existing via target-vs-base comparison.
  • Review-round hardening: CI caught two real regressions that were bisected and fixed with deterministic local repros: a stale suggestion caret when input is written externally (jest fileMentionsWithSlashCommands) and an edit-populate effect that clobbered in-progress edit text once draft helpers lost useCallback identity (e2e review.spec). Both suites now pass locally and in CI, alongside make static-check and whole-file bun runs of all touched test files.

Risks

Highest-traffic UI surface in the app. The riskiest moves are the unified suggestion token seam (channel trigger/filter/selection semantics), the send preflight extraction, and the effect re-keying described above. Mitigated by the new pure unit tests, the full UAT regression pass, the e2e/jest coverage that already caught the two regressions, and keeping ChatInputProps/ChatInputAPI unchanged.


Generated with xum • Model: anthropic:claude-fable-5 • Thinking: xhigh • Cost: $155.26

Stack

Layer 8/10 of the architecture refactor stack (net -5,101 LOC overall). This PR's diff is only this layer, against mike/arch-turn-context-assembler.

@chatgpt-codex-connector

This comment has been minimized.

@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

This comment has been minimized.

@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: 6e4d1387ad

ℹ️ 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/features/ChatInput/useComposerSuggestions.ts Outdated
Comment thread src/browser/features/ChatInput/useComposerSuggestions.ts Outdated
Comment thread src/browser/features/ChatInput/prepareMessagePayload.ts Outdated
Comment thread src/browser/features/ChatInput/useComposerSuggestions.ts
@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. What shall we delve into next?

Reviewed commit: 0029120994

ℹ️ 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".

@chatgpt-codex-connector

This comment has been minimized.

@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

This comment has been minimized.

@chatgpt-codex-connector

This comment has been minimized.

@chatgpt-codex-connector

Copy link
Copy Markdown

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

Reviewed commit: d92b0b4b6b

ℹ️ 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".

@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: d92b0b4b6b

ℹ️ 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/features/ChatInput/useComposerSuggestions.ts
Comment thread src/browser/features/ChatInput/index.tsx Outdated
@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

Reviewed commit: ce45d50a5c

ℹ️ 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".

@chatgpt-codex-connector

This comment has been minimized.

@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 it up!

Reviewed commit: 74d4e1380b

ℹ️ 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".

@chatgpt-codex-connector

This comment has been minimized.

@ibetitsmike
ibetitsmike force-pushed the mike/arch-composer-hooks branch from 74d4e13 to 6ea1b80 Compare August 30, 2026 16:39
@ibetitsmike
ibetitsmike changed the base branch from main to mike/arch-turn-context-assembler August 30, 2026 16:39
@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

This comment has been minimized.

@ibetitsmike
ibetitsmike force-pushed the mike/arch-composer-hooks branch from 6ea1b80 to 33414ea Compare August 30, 2026 17:13
@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

This comment has been minimized.

@chatgpt-codex-connector

Copy link
Copy Markdown

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

Reviewed commit: 33414ea75e

ℹ️ 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
ibetitsmike force-pushed the mike/arch-composer-hooks branch from 33414ea to 1cd8bd4 Compare August 30, 2026 21:10
@ibetitsmike
ibetitsmike force-pushed the mike/arch-composer-hooks branch from 1cd8bd4 to 2ce16e8 Compare August 30, 2026 21:33
Base automatically changed from mike/arch-turn-context-assembler to main August 31, 2026 03:17
_Generated with `xum` • Model: `openai:gpt-5.6-sol` • Thinking: `high`_

<!-- mux-attribution: model=openai:gpt-5.6-sol thinking=high -->
Remove redundant comments and stop exporting extraction-only helper types.
Remove dead hook fields, duplicate metadata work, redundant suggestion branches, and repeated test structure.
@ibetitsmike
ibetitsmike force-pushed the mike/arch-composer-hooks branch from 85bfc67 to 830926c Compare August 31, 2026 03:20
@ibetitsmike
ibetitsmike added this pull request to the merge queue Aug 31, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 31, 2026
@ibetitsmike
ibetitsmike added this pull request to the merge queue Aug 31, 2026
Merged via the queue into main with commit 85bf85c Aug 31, 2026
36 of 38 checks passed
@ibetitsmike
ibetitsmike deleted the mike/arch-composer-hooks branch August 31, 2026 04:14
yermakoffivan pushed a commit to yermakoffivan/mux that referenced this pull request Sep 25, 2026
…orts (coder#4472)

## Summary

Deletes browser production code that only tests still called, and moves
the tests that guarded live behavior onto the live code path. Part of
the test-audit follow-up campaign (frontend area, WS7).

## What was removed

| Removed | Why it was dead | What happened to its tests |
| --- | --- | --- |
| `StatsTab` component + its test-only `_snapshot`/`_clearStats` props
and `useStatsData` overrides | No production caller since coder#2729 moved
the Stats tab to `StatsContainer` (`TimingPanel` +
`ModelBreakdownPanel`) | `StatsTab.clear.test.tsx` replaced by
`TimingPanel.clear.test.tsx`, which drives the live path: real
`WorkspaceStore` stats subscription + `APIProvider` client whose
`workspace.stats.clear` rejects |
| `compareRecords`, `compareArrays` (`useStableReference.ts`) | Only
`compareMaps` is used (`App.tsx`) | 12 comparator tests deleted; stale
"tested manually via useUnreadTracking…" comments fixed |
| `closeSplit` (`rightSidebarLayout.ts`) | Never called in production
since coder#1340 | Test deleted |
| `isActionableTaskExecutionStatus` | Last caller removed in coder#4341 | No
tests |
| `shouldRefreshInlineSkillSuggestions` | coder#4013 made inline suggestions
synchronously derived, so there is no refresh gate | Tests deleted |
| `getAnthropicThinkingDisableReason` + `hasAnthropicThinkingSignature`
| coder#1450 stopped disabling Anthropic thinking | Tests deleted; the one
transform test that used it as an oracle now asserts the preserved
signature directly |
| `useProjectGitStatuses` (`GitStatusStore.ts`) | No production caller |
None in CI |
| `buildReviewDiffPathFilter`, `normalizeReviewPanelAssistedHunks`
(test-only wrappers in `ReviewPanel.tsx`) | Production calls
`buildReviewDiffPathFilterSpecs` / `getReviewPanelPathContext` +
`normalizeAssistedReviewHunks` directly | Tests retargeted to those
production functions (`getReviewPanelPathContext` is now exported) |
| `computeTaskAwaitPollGroupInfos` (test-only wrapper) | ChatPane calls
`computeOperationalBundleInfos({ taskAwaitPollsOnly })` | Tests use a
local fixture helper around the production function |
| 2 `/fork` cases in `parser.test.ts` | Same contract and name as
`fork.test.ts` | Deleted |

Retained on purpose (test reset seams, not dead code): `__resetForTests`
(highlight worker), `resetAiSelectionIntentForTests`,
`overlayWorkspaceStoreRaw` (test-support module).

## Validation

- Sweep: every `export` under `src/browser` (excluding stories) checked
for non-test references across `src/`, `tests/`, `vscode/`, `scripts/`,
`.storybook/`; orphan-module sweep found only the two HTML entry points.
- `TimingPanel.clear.test.tsx` fails when `handleClearStats` stops
setting the error (mutation check).
- `bun test` on RightSidebar, hooks/useStableReference,
utils/{messages,agentSkills,ui,rightSidebarLayout},
stores/GitStatusStore: 1141 pass.
- `make static-check` green.

LOC: production +11 / −585; tests +134 / −355.

---

_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking:
`high` • Cost: `$4.21`_

<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high
costs=4.21 -->
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