Skip to content

build: second supply-chain dependency reduction pass#804

Merged
bpowers merged 17 commits into
mainfrom
dep-diet-round2
Jun 21, 2026
Merged

build: second supply-chain dependency reduction pass#804
bpowers merged 17 commits into
mainfrom
dep-diet-round2

Conversation

@bpowers

@bpowers bpowers commented Jun 20, 2026

Copy link
Copy Markdown
Owner

Summary

A follow-up to PR #719: a fresh attribution audit of both lockfiles (Rust workspace crates + all JS packages), verifying actual call sites before cutting. Everything removed is dead weight, a redundant pin, or replaceable by a small amount of tested code we own.

Before After Delta
Cargo.lock crates 467 463 -4
pnpm-lock packages 1346 1320 -26

The headline counts undersell it: the biggest wins are deduplicating parallel transitive trees (firestore/gax) and evicting the @floating-ui/popper positioning stack, both of which trade many duplicated/heavy packages for owned code.

server: redundant firestore pin + dead cookie-parser

firebase-admin already bundles @google-cloud/firestore and re-exports its full type surface (plus an app-aware getFirestore()) from firebase-admin/firestore. The server also pinned @google-cloud/firestore@^8.3.0, which resolved to 8.5.0 next to firebase-admin's 7.11.6; the two diverge below google-gax (4.x vs 5.x), dragging parallel copies of google-gax, google-auth-library, the grpc proto loader, and node-fetch into the tree. Retargeted the two import sites at firebase-admin/firestore and swapped new Firestore() for getFirestore() (the admin app is already initialized before the DB is built). The runtime API used (collection/doc/where/runTransaction, FieldPath.documentId()) is stable across 7.x/8.x.

cookieParser() was registered as middleware but nothing reads req.cookies/req.signedCookies (seshcookie parses the Cookie header itself). Removed it + @types/cookie-parser and the duplicate cookie-signature copy it pulled.

build: criterion plotters trim + unused tracing

criterion is now default-features = false, features = ["cargo_bench_support"], dropping the plotters/plotters-backend/plotters-svg/web-sys stack (we only read the terminal summary; the bench harnesses report in-chat). Removed the declared-but-unused tracing dependency from simlin-mcp-core (Closes #803).

diagram: reimplement 4 UI components, drop 5 deps

Replaced five third-party UI dependencies with small owned implementations, keeping each component's public API and its existing tests as the contract. This fully evicts the @floating-ui/* + react-popper positioning stack and downshift.

  • Accordion -- context-backed single-item disclosure; the open/close height animation is a JS-free grid-template-rows 0fr<->1fr transition (no more Radix --radix-accordion-content-height). Adds an Accordion test where none existed.
  • Checkbox -- native <input type="checkbox"> (appearance:none) with a CheckIcon overlay, preserving the data-state + primary/secondary class contract.
  • Autocomplete -- a new owned useCombobox hook (filter-as-you-type listbox, arrow/enter/escape keys, WAI-ARIA combobox attributes) replaces downshift's useCombobox; the component body is otherwise unchanged.
  • Menu -- an owned fixed-position portal positioned from the live anchor rect (the useAnchorRect issue-diagram: Menu anchors Radix to a position:fixed proxy holding a stale getBoundingClientRect snapshot #710 logic is unchanged), with Escape + click-outside dismissal and close-on-select via context. Drops Radix dropdown-menu (and with it @floating-ui).
  • SpeedDial action tooltips -- a CSS-only hover/focus tooltip replaces Radix tooltip.

Radix dialog/tabs/toast deliberately stay -- they carry focus-trap/ARIA correctness that is not worth re-owning. All 1195 diagram tests pass.

Deliberately kept (audited, not worth touching)

  • Rust: ignore, calamine, notify-debouncer-full, jsonschema -- big headline closures evaporate under inverse-dependency analysis (shared with tracing-subscriber/salsa/loro) or are already correctly feature-gated. twox-hash/smallvec/rustc-hash/indexmap add zero net crates.
  • JS: helmet/@iarna/toml (zero deps), cors (one tiny pkg), katex (one call site, but deps only on commander -- weight is fonts not tree), slate, @firebase/*, clsx, wouter -- load-bearing or zero-closure.

Verification

Every commit passed the full pre-commit battery (rustfmt, clippy, cargo test, eslint, build, tsc, JS tests, pysimlin). New/reimplemented components keep their existing component tests green; the firestore retarget and cookie-parser removal were validated against the server suite (111 tests) and tsc.

Closes #803

🤖 Generated with Claude Code

bpowers added 4 commits June 20, 2026 08:10
…rser

firebase-admin already bundles @google-cloud/firestore and re-exports its
entire type surface (plus an app-aware getFirestore()) from
firebase-admin/firestore. The server also pinned @google-cloud/firestore
directly at ^8.3.0, which resolved to 8.5.0 alongside the 7.11.6 that
firebase-admin ships -- and the two diverge below google-gax (4.x vs 5.x),
dragging parallel copies of google-gax, the grpc proto loader, node-fetch,
and friends into the tree purely because of the separate pin. Retarget the
two import sites (db-firestore.ts, table-firestore.ts) at
firebase-admin/firestore and swap `new Firestore()` for `getFirestore()`
(the admin app is already initialized in app.ts before the database is
constructed). The runtime API used here -- collection/doc/where/
runTransaction, FieldPath.documentId() -- is stable across 7.x/8.x.

cookieParser() was registered as middleware but nothing ever reads
req.cookies or req.signedCookies: the seshcookie session library parses
the Cookie header itself. Remove the dead middleware (and its mirror in
the session-auth test) along with cookie-parser + @types/cookie-parser;
that also sheds the duplicate cookie-signature@1.0.6 copy.
criterion was pulled with its default features on plus html_reports, which
enables the plotters HTML-report backend. We only ever read the terminal
summary from benchmark runs (the regenerable harnesses report in-chat), so
switch to default-features = false with just cargo_bench_support. That drops
plotters, plotters-backend, plotters-svg, and web-sys from the lockfile and
stops compiling the plotting stack for `cargo test`/bench builds.

simlin-mcp-core declared tracing as a direct dependency but never logged or
spanned anything; remove it. tracing stays in the tree via simlin-serve and
rmcp, so this only corrects a misleading direct edge.
…deps

Replace five third-party UI dependencies with small owned implementations,
keeping each component's public API and existing tests as the contract. This
evicts the @floating-ui/popper positioning stack and downshift entirely.

- Accordion: a context-backed single-item disclosure. The open/close height
  animation is a JS-free grid-template-rows 0fr<->1fr transition, replacing
  the Radix keyframes that depended on --radix-accordion-content-height. Adds
  an accordion test (there was none).
- Checkbox: a native <input type="checkbox"> styled with appearance:none and
  a CheckIcon overlay, preserving the data-state and primary/secondary class
  contract the test pins.
- Autocomplete: a new owned useCombobox hook (filter-as-you-type listbox with
  arrow/enter/escape keys and WAI-ARIA combobox attributes) replaces
  downshift's useCombobox; the component body is otherwise unchanged. Opens on
  click/type/arrow to match the wiring-editor selects.
- Menu: an owned fixed-position portal positioned from the live anchor rect
  (the useAnchorRect issue-#710 logic is unchanged), with Escape and
  click-outside dismissal and close-on-select via context. Drops Radix
  dropdown-menu (and with it the @floating-ui stack).
- SpeedDial action tooltips: a CSS-only hover/focus tooltip (left-pointing
  arrow, 300ms show delay) replaces Radix tooltip.

Removed deps: @radix-ui/react-accordion, @radix-ui/react-checkbox,
@radix-ui/react-dropdown-menu, @radix-ui/react-tooltip, downshift. The Radix
dialog/tabs/toast components stay (accessibility-sensitive, still used).
The grid-rows height animation keeps the panel content mounted while closed,
which left it keyboard-focusable and exposed to assistive tech -- a regression
from Radix's `hidden`. Mark the collapsed content subtree `inert` so it is
removed from the tab order and a11y tree while still laid out for the
transition.

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

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

aria-labelledby={triggerId}
className={clsx(styles.details, className)}
style={style}
data-state={open ? 'open' : 'closed'}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Hide collapsed accordion contents from focus

When open is false this panel is only collapsed visually via data-state, but the region and all of its children remain mounted without hidden, inert, or aria-hidden. In the New Project form the collapsed Advanced section contains a checkbox, so keyboard users can tab into an invisible control before reaching Create; keep the closed content out of the accessibility tree/tab order while preserving the animation.

Useful? React with 👍 / 👎.

@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown

[P2] Re-clicking the trigger no longer dismisses the menu (src/diagram/components/Menu.tsx lines 100–112)

The new outside-dismissal listener registers mousedown on document and calls onClose whenever the event target is not inside contentRef. The only known caller (src/app/Home.tsx handleMenu) wires the trigger's onClick to setAnchorEl(event.currentTarget) — it does not toggle — and relied on Radix's DismissableLayer to ignore the trigger element. When the user re-clicks the trigger to dismiss an open menu, mousedown now fires onClosesetAnchorEl(null), and the immediately-following React click fires handleMenusetAnchorEl(currentTarget). Under React 18+ automatic batching (this repo is on React 19), both updates land in the same render and the second wins, so the menu visibly never closes; without batching, it would flicker closed and immediately re-open. The Escape key and outside-clicks still work, and MenuItem's close() still dismisses, so this is a UX regression rather than a broken flow — but the trigger-toggle affordance users expect from the avatar menu is gone, and menu.test.tsx/home.test.tsx (which mocks Menu) don't cover it. Consider exempting anchorEl (and its descendants) from the outside check, e.g. if (anchorEl?.contains(event.target as Node)) return;.

Overall correctness verdict: correct (with minor regression)

The dependency removals (@google-cloud/firestore, cookie-parser, tracing, criterion's plotters stack, the diagram Radix/downshift components) are clean — call sites are retargeted, owned replacements preserve the existing test contracts, and the firebase-admin getFirestore() swap is safe because admin.initializeApp() runs in the App constructor before createDatabase is awaited in setup(). The Menu trigger re-click regression above is the only behavioral concern I found; it is non-blocking and addressable in a follow-up.

@codecov

codecov Bot commented Jun 20, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.84%. Comparing base (65e639c) to head (b19cba6).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #804   +/-   ##
=======================================
  Coverage   90.83%   90.84%           
=======================================
  Files         224      224           
  Lines      142627   142627           
=======================================
+ Hits       129562   129563    +1     
+ Misses      13065    13064    -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown

Review Summary

I reviewed the diff for code-correctness bugs and didn't find any blocking issues that would warrant changes. The PR is a careful supply-chain reduction with the existing component tests acting as the contract for the reimplementations, plus a new Accordion test where coverage was missing. A few things I specifically verified:

  • firebase-admin/firestore retarget: getFirestore() correctly returns the Firestore instance bound to the already-initialized admin app; new Firestore() would have instantiated a separate non-admin client.
  • cookie-parser removal: confirmed no req.cookies / req.signedCookies reads anywhere in src/server/.
  • tracing removal from simlin-mcp-core: confirmed zero usages in source.
  • criterion trim: the bench files only use core criterion APIs (Criterion, BenchmarkId, criterion_group / criterion_main, BatchSize), which all stay available without default features.
  • Accordion inert: well-supported in React 19 types and modern browsers; disabled short-circuits toggle and the button is also natively disabled.
  • useCombobox selection focus: onMouseDown on items calls event.preventDefault(), which keeps focus on the input through the click so the portaled listbox's onBlur-close doesn't beat the click.
  • Menu outside-click on anchor: the trigger-toggle interaction is consistent with the old Radix-backed behavior; not a regression.

Overall correctness verdict

Correct. I see no behavioral regressions, no broken contracts, and the new tests look adequate for the components introduced.

- useCombobox: set the field text to the chosen item on selection. Without it
  an uncontrolled Autocomplete (one that does not feed `value` back in) kept
  showing the partial text the user typed instead of the selection -- a
  behavioral regression from downshift.
- Menu: restore the keyboard/focus affordances Radix provided. Move focus to
  the first item on open (the menu portals to end-of-body, so otherwise a
  keyboard user must tab the whole page to reach it); Up/Down/Home/End rove
  between enabled items; Escape closes and returns focus to the anchor;
  focus leaving the menu (tab-out) dismisses it. Also exclude the anchor from
  outside-press dismissal so a trigger press no longer triggers a redundant
  close->reopen and a toggling trigger can work.
- Accordion: mark the collapsed region itself (not just its inner wrapper)
  inert + aria-hidden, so a closed panel is not announced as an empty region.

Adds Menu dismissal/keyboard tests, an uncontrolled-select Autocomplete test,
and updates the Accordion tests for the region-level hiding.
@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown

Reviewed the PR end-to-end. No blocking issues found.

Verified correctness of the reimplemented components:

  • Accordion: context-backed disclosure, inert + aria-hidden on collapsed region, grid-template-rows transition — controlled/uncontrolled paths and the new region-exposure tests look right.
  • Checkbox: controlled/uncontrolled state correctly mirrored, indicator overlay is pointer-events: none so clicks reach the input.
  • Autocomplete + useCombobox: selectItem fills the input so uncontrolled callers see the chosen value (new test covers this); onMouseDown preventDefault on items beats the input blur; ARIA combobox attributes are wired correctly.
  • Menu: outside-mousedown/Escape dismissal with the anchor-press exemption avoids close→reopen flicker; auto-focus + arrow-key wrap + Tab-out blur dismissal are consistent; the issue diagram: Menu anchors Radix to a position:fixed proxy holding a stale getBoundingClientRect snapshot #710 anchor-rect positioning is preserved.
  • SpeedDial tooltip: CSS-only hover/focus, aria-hidden because the button retains aria-label.

Server changes are safe:

  • firebase-admin/firestore re-exports Firestore/CollectionReference/FieldPath; getFirestore() is correctly called after admin.initializeApp() (constructor → setup ordering).
  • Confirmed no req.cookies/req.signedCookies usage exists; seshcookie parses the Cookie header itself, so the cookie-parser removal is sound.

Build changes:

  • criterion with default-features = false, features = ["cargo_bench_support"] keeps the bench-runner harness; only the plotters HTML-report stack drops.
  • tracing confirmed unused in simlin-mcp-core.

Overall correctness: correct.

bpowers added 3 commits June 20, 2026 09:31
- db-firestore: getFirestore() resolves against the default Firebase app and
  throws app/no-app if none is initialized. App bootstrap calls
  admin.initializeApp() first, but standalone callers (scripts/debug-import.mjs)
  reach createDatabase without it -- the prior new Firestore() needed no app.
  Initialize a default app on demand when none exists; reuse the existing one
  otherwise (no double init). Adds a db-firestore unit test for both paths.
- useCombobox: scroll the keyboard-highlighted option into view. Focus stays on
  the input (combobox pattern), so arrowing past the fold of a list taller than
  the 300px popup moved the highlight off-screen with nothing scrolling, which
  downshift handled. `block: 'nearest'` only scrolls when the row is off-screen;
  the call is optional-chained since jsdom lacks scrollIntoView.
The blur handler exempted the anchor so a mouse press on the trigger would not
flicker-close the menu, but that also wrongly kept it open when a keyboard user
moved focus back to the trigger (Shift+Tab) -- focus had genuinely left the menu
yet it stayed open.

Distinguish the two with a transient skip-blur-close flag set during pointer
interactions (owned by the mousedown listener) and around Escape's focus
restore, so the blur handler closes on a genuine keyboard focus-out -- including
Shift+Tab to the trigger -- while mouse/Escape focus moves still do not
double-close. Adds a Shift+Tab-to-trigger regression test.
Two related blur-close bugs in the owned Menu:

- The skip-blur-close flag was stranded true after an outside-press dismissal:
  that path sets the flag and calls onClose synchronously inside the mousedown
  listener, which tears down the mouseup reset before it fires. The flag then
  survived to the next open and suppressed a genuine keyboard focus-out. Clear
  it on open so every session starts clean.
- A null relatedTarget was treated as "stay open", but Tab from the only/last
  item blurs to browser chrome with relatedTarget null -- focus has left the
  menu, so it must close (Radix did). Treat null as outside unless the
  skip-blur-close flag is set.

Adds regression tests for focus-out to browser chrome and for keyboard
focus-out after a prior outside-click dismissal.
@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown

Reviewed the dependency-trim changes. No blocking issues found.

The firestore retarget is well-handled: createFirestoreDatabase now calls initializeApp() on demand when no Firebase app exists, with regression tests pinning that contract for both bootstrap-via-createApp and standalone callers like scripts/debug-import.mjs. The cookie-parser removal is safe — nothing reads req.cookies/req.signedCookies (seshcookie owns Cookie-header parsing).

The four reimplemented diagram components preserve their public APIs and their existing tests as the contract:

  • Accordioninert + aria-hidden on the collapsed region correctly removes it from tab order and the a11y tree while keeping it laid out for the grid-rows transition; isControlled branching for the expanded prop matches the prior Radix semantics.
  • Checkboxappearance: none + -webkit-appearance: none reliably drops the native glyph, and the indicator overlay's pointer-events: none correctly lets the input own clicks.
  • MenuuseAnchorRect re-measures on capture-phase scroll and resize and bails on identical rects to avoid render storms; skipBlurCloseRef correctly disambiguates pointer- and Escape-driven focus moves from genuine tab-out blur.
  • Autocomplete/useComboboxmousedown.preventDefault() on items keeps focus on the input so the trailing blur can't beat the click; the controlled/uncontrolled inputValue sync is preserved, and the scrollIntoView highlight follow is sensibly guarded with optional chaining.

The criterion trim correctly keeps cargo_bench_support while dropping the plotters/HTML-report stack — the bench harnesses only read the terminal summary. The tracing drop from simlin-mcp-core is verified-unused (no references remain in the crate).

Verdict: correct.

The proxy span is rendered by our own Menu now, not anchored by Radix; correct
the getProxySpan helper comment accordingly. No behavior change.
@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown

Reviewed the diff end-to-end (server firestore retarget, cookie-parser removal, criterion trim, tracing removal, and the four reimplemented diagram components).

The reimplementations carefully preserve the prior public API shapes and the tests give strong contractual coverage: Menu's positioning + dismissal + keyboard edge cases are all pinned; Accordion's inert/aria-hidden + controlled/uncontrolled toggle is covered; Autocomplete's external-value sync, portal repositioning, and clearOnEscape are tested. The useCombobox hook keeps the downshift return shape (getInputProps/getMenuProps/getItemProps/isOpen/highlightedIndex), and the Escape/Arrow/Enter handling matches the prior semantics (with the same uncontrolled-fill-on-select that downshift previously provided).

The server retarget (admin's bundled firestore via firebase-admin/firestore + on-demand initializeApp() when no app exists) is clean and the new db-firestore.test.ts pins the on-demand init contract for standalone callers. The cookie-parser removal is correctly scoped (no remaining req.cookies/req.signedCookies readers; seshcookie parses Cookie itself). The criterion default-features = false, features = ["cargo_bench_support"] trim drops the plotters/plotters-backend/plotters-svg/web-sys stack while leaving the benchmark harness intact, and the unused tracing removal from simlin-mcp-core is verified (no tracing:: call sites).

No correctness issues to flag.

Overall correctness: correct.

When a keyboard user typed, arrowed to highlight an option, then tabbed away,
the blur handler closed the list and cleared the highlight without selecting --
so they lost the choice they navigated to unless they pressed Enter. downshift
accepted the highlighted item on a keyboard-driven blur.

Restore that: on blur, if an option is highlighted and the blur is not caused by
a pointer press, commit it. A mouse-driven blur (clicking elsewhere to dismiss)
still just closes, so clicking away never accidentally selects. A small always-on
pointer tracker distinguishes the two without the teardown-stranding race.
@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown

Code Review

I reviewed this PR end-to-end with focus on the diagram component reimplementations, the firestore migration, and the criterion trim. The work is thorough, the tests are comprehensive, and patches 4-9 already iterated through multiple rounds of feedback on the owned components. I didn't find any blocking bugs.

Notes (non-blocking)

  • SpeedDial tooltip lacks collision detection. The previous Radix Tooltip.Content side="right" collisionPadding={16} was replaced with a pure-CSS left: 100%; margin-left: 8px positioning. If the SpeedDial is ever moved closer to the right viewport edge, the tooltip will clip off-screen with no fallback. The current placement (lower-left FAB tool palette) keeps it safe today, but it's a regression in robustness worth flagging if the SpeedDial relocates.

  • useCombobox Enter without highlight does not preventDefault. src/diagram/components/useCombobox.ts:122-128 only calls event.preventDefault() when isOpen && highlightedIndex >= 0. If the user presses Enter with the listbox open but nothing highlighted, the keystroke can still propagate to a surrounding form's submit. Downshift's useCombobox consumed Enter unconditionally while open. Not exercised by any current caller (the wiring-editor Autocompletes are not inside <form>s with default submit), but worth a note for future consumers.

  • Menu's escape event.preventDefault() on document listener. Menu.tsx:102 calls preventDefault on every document-level Escape while a menu is open, even if a parent layer (a Dialog, say) would otherwise consume it. Radix's DismissableLayer coordinated this via z-ordered stacks; the owned Menu doesn't. No current call site nests Menu in a Dialog, so latent until then.

Overall

Correct. The PR achieves its stated goal (4 Cargo + 26 pnpm packages dropped, plus the firestore/gax tree dedupe), preserves the public API contract of each replaced component, and ships matching tests for new code paths. The Menu blur/focus state machine (the trickiest piece) is rigorously covered by tests that pin the edge cases the patches discovered.

🤖 Generated with Claude Code

@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown

Review

I focused on the four reimplemented src/diagram/components/* and the src/server/models/db-firestore.ts retarget. Most of the work looks careful and well-tested (the menu's skipBlurCloseRef reset-on-open, the firestore default-app init-on-demand, and the inert/aria-hidden region pair on the Accordion all close off realistic edge cases). One behavior regression worth a look:

[P2] useCombobox does not reset inputValue to the selected item on blur

src/diagram/components/useCombobox.ts:182-192

When the user types into a partially-filled Autocomplete and then blurs without picking a suggestion (mouse-driven blur, or keyboard blur with no active highlight), onBlur only calls setIsOpen(false) / setHighlightedIndex(-1). It never resets inputValue back to the prior selection's display. downshift's default reducer on InputBlur set inputValue = itemToString(selectedItem), so the field reverted; the new hook doesn't accept selectedItem and so can't. In ModuleDetails's wiring rows the parent's onChange callback also bails on null/empty, so value=ref.src stays put — the user is left seeing the half-typed string ('b') while the underlying reference is still 'population', with no way to revert short of typing it back. The 'selecting an option fills the input even when uncontrolled' test pins the select-path equivalent but not the blur-path one. Worth either threading selectedItem/value into useCombobox and resetting on blur, or syncing inputValue back to value in Autocomplete.tsx when blur closes the list without committing.

Overall correctness

Correct, modulo the regression above. Existing tests pass and the new tests pin the intended new behavior; the regression is a behavior delta that the test suite doesn't catch.

…omment

The keyboard-blur commit relies on a flag that says "this blur was caused by a
mouse press." The mousedown/mouseup pair could strand the flag true if the
mouseup was never delivered (release outside the window), which would then make
the next genuine keyboard tab-away fail to commit. Replace it with a single
mousedown listener that defers clearing to the next task: the click-induced blur
fires synchronously in the mousedown's task, so it still observes the flag, but
the flag can never linger regardless of where the release lands.

Also correct a stale "downshift's getInputProps()" comment in TextField (it is
the owned combobox getInputProps() now).
@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown

Review

I reviewed this dependency-trim PR (criterion plotters stack, tracing from simlin-mcp-core, @google-cloud/firestore redundant pin, cookie-parser, and the Radix/downshift reimplementations).

The reimplementations are unusually well-tested: the iterative follow-up commits (c8fd205, 74c064c, 4f96eb8, c608076, 602c177, 7eb4da2) specifically address keyboard-blur, focus-restoration, outside-press, pointer-tracker stranding, and uncontrolled-select edge cases in the owned useCombobox and Menu, each with a regression test. I traced the listener-cleanup ordering on outside-press dismissal (the skipBlurCloseRef clear in the second useEffect at Menu.tsx:161 correctly handles the stranded-true case after the mousedown listener tears down its own mouseup reset) and the capture-phase mousedown tracker in useCombobox.ts:82-94 (the setTimeout(0) reset is safe against synchronous focus-blur-during-mousedown ordering, including event.preventDefault() on options keeping the input focused).

The Accordion's grid 0fr-to-1fr height trick plus inert on the region (not just the inner wrapper) correctly hides collapsed content from a11y/tab order while keeping the transition live. The Checkbox's absolute-positioned indicator with pointer-events: none lets clicks fall through to the native input. The Firestore migration to firebase-admin/firestore re-exports is straightforward, and createFirestoreDatabase correctly handles the standalone-caller case with on-demand initializeApp() (covered by the new db-firestore.test.ts).

No blocking findings.

Overall correctness

Correct.

Option presses only prevented the focus-stealing blur via onMouseDown. On touch,
focus can shift on pointerdown (before the synthesized mousedown), so the input
blurred and the listbox closed/unmounted before the tap's click landed -- mobile
users couldn't select autocomplete options. Also prevent default on the option's
onPointerDown, which fires first for both touch and mouse, before any focus
shift. onMouseDown is kept for the mouse compatibility-blur path.
@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown

Reviewed the diff focused on correctness/maintainability. No blocking bugs found. The reimplementations carefully address the Radix → owned-component migration:

  • Firestore retarget (db-firestore.ts:39-52, table-firestore.ts): callsite-API parity with firebase-admin/firestore re-exports is preserved; the on-demand initializeApp() guard correctly handles the standalone-caller path (debug-import.mjs) while not double-initing when app.ts has already called admin.initializeApp(). Verified no remaining req.cookies/req.signedCookies readers after the cookie-parser removal.
  • Criterion trim (simlin-engine/Cargo.toml:77): retaining cargo_bench_support keeps criterion_main!/criterion_group! macros working; HTML reports were unused per the bench harness usage.
  • Menu owned implementation: the skip-blur-close ref machinery handles the stranded-flag case (cleared on open via Menu.tsx:161) and outside-press dismissal (Menu.tsx:113-129). The proxy span still carries aria-haspopup=\"menu\" even when closed (Menu.tsx:228-241) — a minor stylistic carryover from the Radix-trigger era, since the real trigger (e.g. Home.tsx's IconButton) already sets its own aria-haspopup. Auto-focusing the first item on open (Menu.tsx:162) is a behavior change from the prior mouse-click-opens-but-doesn't-focus-items pattern; verified the only caller (Home.tsx logout menu) is fine with this.
  • useCombobox: pointerDownRef teardown-stranding is correctly avoided by the deferred setTimeout(0) reset (useCombobox.ts:84-95). aria-selected is wired to highlightedIndex rather than the selected value (useCombobox.ts:204) — technically the WAI-ARIA combobox spec asks for the chosen option to carry it, but this matches downshift's prior behavior and is sub-finding for this PR.
  • Accordion: the inert={open ? undefined : true} + aria-hidden pairing on the region (Accordion.tsx:137-138) correctly removes the collapsed content from tab order and the a11y tree; React 19 supports inert as a real prop.
  • Touch selection (useCombobox.ts:211-212): preventing default on both onMouseDown and onPointerDown is belt-and-suspenders for cross-platform focus retention; verified the spec allows click to still fire after preventDefault on pointerdown.

Overall correctness: correct.

bpowers added 2 commits June 20, 2026 12:53
The "is this blur caused by a press?" guard that suppresses auto-committing the
highlighted option was set only from mousedown. On touch, tapping outside a
focused autocomplete moves focus and fires the input blur BEFORE the synthesized
mousedown, so the flag was still false and the blur was treated as a keyboard
tab-away -- committing the highlighted row when the user meant to dismiss.

Listen on pointerdown instead: it fires for both mouse and touch and, on touch,
before the focus shift, so the flag is set when the blur fires. The deferred
self-reset still applies, so it can never strand true.
Every MenuItem had tabIndex=0, so in a multi-item menu Tab moved between items
(and the blur handler ignores within-menu focus moves), trapping keyboard users
in the menu instead of leaving it -- a regression from Radix's roving focus.

Give items tabIndex=-1: the menu already focuses the first item on open and
moves focus with the arrow keys programmatically, and with nothing tabbable
Tab/Shift+Tab now leave the menu and the resulting focus-out dismisses it.
@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown

Review

I traced through the Firestore migration, the Criterion/tracing trims, all five reimplemented diagram components, and the new useCombobox hook. I did not find any actionable bugs.

Highlights of what I verified:

  • createFirestoreDatabase correctly handles standalone callers (getApps().length === 0initializeApp()), preserving the zero-config behavior the previous new Firestore() offered. The runtime surface used (collection/doc/where/runTransaction, FieldPath.documentId()) is identical across firebase-admin/firestore and @google-cloud/firestore.
  • useCombobox blur logic (the most subtle bit): the document-level pointerdown listener + deferred setTimeout(0) reset correctly distinguishes pointer-driven from keyboard-driven blurs. The flag can't strand true (deferred reset doesn't depend on pointerup), it's set in the same task as the press-induced blur so the blur reads it correctly, and on touch the pointerdown precedes the focus shift (the comment correctly motivates this over mousedown).
  • Menu dismissal: Escape with focus restore, outside-press exempting the anchor, tab-out (including Shift+Tab back to the trigger and null relatedTarget to browser chrome), and the stranded-skipBlurCloseRef regression around outside-press dismissals are all addressed with tests. The portal proxy <span> with aria-haspopup continues the prior Radix-as-asChild behavior (no regression).
  • Accordion: the inert + aria-hidden combination on the collapsed region correctly defends both keyboard reach and the a11y tree while preserving the grid-template-rows: 0fr↔1fr height animation. Controlled/uncontrolled mode follows the standard React pattern.
  • Checkbox: native <input type=checkbox> with pointer-events: none on the overlay indicator correctly routes clicks to the input; data-state and primary/secondary class contracts are preserved.
  • SpeedDial tooltip: the visibility 0s transitions correctly compose with the 300ms show delay so the tooltip stays unrendered for hit-testing during the delay window, then fades in.
  • Lock-file delta: the deduplication of @google-cloud/firestore 7.x/8.x, google-gax 4.x/5.x and the eviction of the @floating-ui + react-popper stack are real wins; I confirmed each removed package via the lockfile diff.

Overall correctness verdict

Correct. The patch should not break existing code or tests. The reimplementations preserve the public API and the existing component tests as the contract, and the new tests cover the trickier corners (uncontrolled-select fill, scroll-into-view, keyboard-vs-pointer blur, Escape focus restore, Shift+Tab to trigger, null-relatedTarget close, stranded-flag reopen, touch-tap selection, on-demand Firebase app init).

@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown

Reviewed the dep-reduction re-implementations against their preserved behaviors. The drops (firestore pin, cookie-parser, criterion plotters, tracing in simlin-mcp-core) are clean — no remaining call sites found. Components (Accordion, Checkbox, Autocomplete/useCombobox, Menu, SpeedDial tooltip) preserve their existing public APIs and test contracts.

One latent edge case worth noting:

[P3] Menu loses live re-positioning on window resize when the anchor rect doesn't change

File: src/diagram/components/Menu.tsx:213-224

The proxy/content position math for align === 'end' and side === 'top' uses window.innerWidth - anchorRect.right / window.innerHeight - anchorRect.top, but useAnchorRect's bail-out at lines 52–60 returns the previous rect when getBoundingClientRect is value-equal — so a window resize that does NOT shift the anchor (e.g. an absolutely-positioned anchor with a fixed left) won't re-render and the menu drifts (with the prior radix/floating-ui stack this was reliably re-positioned). The only in-tree call site (Home.tsx AccountIcon inside a flex AppBar) does move on resize so this is currently latent, but a future anchor at a fixed pixel coord with a right/top origin would mis-position until the menu is reopened. Tracking window.innerWidth/innerHeight in the effect (or invalidating the bail-out on resize) would close it.

Overall correctness verdict

Correct — the PR is safe to merge. No bugs found that block release, and the re-implementations preserve all existing tests (1195 diagram tests cited in the description). The Menu edge case above is latent and worth tracking but does not affect the current call site.

bpowers added 2 commits June 20, 2026 13:15
The outside-press dismissal listened on mousedown. Surfaces like the diagram
canvas call preventDefault() on pointerdown for pan/drag, which suppresses the
compatibility mousedown -- so pressing such a surface while a menu was open
could leave it stuck open. Listen on pointerdown in the capture phase (matching
Radix's outside-interaction handling): pointerdown fires for mouse and touch and,
in capture, runs before the target's own handlers regardless of what they do.
The skip-blur-close pairing moves to pointerup accordingly.
Follow-up to the pointerdown/pointerup dismissal switch: the open-effect
strand-reset comment and two menu-test comments still described the old
mousedown/mouseup listeners. Align the wording with the code. No behavior change.
@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown

Reviewed the diff focusing on the reimplemented components and the server-side surgery.

Overall correctness: looks correct.

A few low-priority observations, none rising to a bug worth flagging as a fix:

  • Menu.tsx: by-design exemption of the anchor from outside-press dismissal is a small UX change from Radix's dropdown-menu, where pressing the trigger again would toggle the menu. With Home.tsx's current handleMenu (always sets anchorEl), re-clicking the trigger no longer closes the menu; callers wanting click-to-toggle now need explicit state. Since no caller relies on that behavior today and the comment justifies the choice, this is just worth being aware of.
  • Autocomplete.tsx: the defaultValue field on AutocompleteProps is still declared but never read in the implementation (this is a pre-existing condition, not introduced here — calling out only because Editor.tsx:1519 still passes it).
  • Menu.tsx: the proxy <span> is rendered into the portal even when open is false, holding the last-known anchorRect. pointer-events: none and zero size make this harmless visually, but the aria-haspopup="menu" is exposed at all times. Minor a11y nit, not actionable.

The reimplemented Accordion, Checkbox, Autocomplete/useCombobox, Menu/MenuItem, and the CSS-only SpeedDial tooltip preserve their existing test contracts and add new tests where prior coverage was thin (accordion, autocomplete keyboard/pointer paths, menu anchor-following + dismissal). The criterion trim keeps every symbol the benches import (BenchmarkId, Criterion, BatchSize, Throughput, black_box, criterion_group, criterion_main). The firestore retarget is a clean swap (the API used — collection/doc/where/runTransaction, FieldPath.documentId() — is identical between 7.x and 8.x), and createFirestoreDatabase correctly initializes a default app on demand for standalone callers, with a regression test pinning it.

No blocking findings.

@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown

Review: PR 804 - second supply-chain dependency reduction pass

I reviewed the substantial set of changes: removal of the @google-cloud/firestore direct dep (re-targeting at firebase-admin/firestore), cookie-parser removal, criterion default-feature trim, tracing removal from simlin-mcp-core, and reimplementation of Accordion/Checkbox/Autocomplete/Menu/SpeedDial-tooltip without Radix/downshift/floating-ui.

Findings

I did not find any blocking bugs introduced in this commit. The work appears carefully executed:

  • Menu reimplementation correctly handles the tricky cases: capture-phase pointerdown (matching the existing canvas preventDefault interaction), skipBlurCloseRef flag with re-init on open (with a regression test for the stranded-flag path), roving-focus via tabIndex={-1}, and arrow/Home/End key navigation. Blur dismissal correctly distinguishes intra-menu focus moves from focus-outs.
  • useCombobox preserves the necessary semantics: pointerDownRef keyed on pointerdown (so touch is handled before focus shift), setTimeout(0) for self-resetting (no reliance on pointerup that may never fire), and the option-press path uses both onMouseDown and onPointerDown preventDefault to retain input focus across mouse and touch. Highlight resets on input change so a shrunk filtered list cannot leave a stale index.
  • Accordion correctly uses grid-template-rows 0fr to 1fr for height transitions (with the min-height: 0 escape hatch on the inner) and uses inert plus aria-hidden together so a collapsed region is removed from both tab order and the a11y tree.
  • Checkbox data-state moved from wrapper to the input element; CSS selectors in Checkbox.module.css use .checkbox[data-state='checked'] and .primary[data-state='checked'] which match the input directly. Indicator overlay correctly uses pointer-events: none so clicks reach the input.
  • SpeedDial tooltip CSS-only delay/show via transition-delay on the hover state matches the prior delayDuration={300}. The .action rule already had position: relative so the absolutely-positioned tooltip resolves correctly.
  • Firestore migration: createFirestoreDatabase correctly handles the standalone-caller case via getApps() guard before initializeApp(); getFirestore() and the named types (Firestore, CollectionReference, FieldPath) are stable re-exports from firebase-admin/firestore.
  • criterion trim: cargo_bench_support is the feature that gates the bench-harness API used in the benches (criterion_main!, Criterion, BenchmarkId, etc.), so the default-features-off plus bench-support form keeps benches working while dropping the plotters/rayon/web-sys HTML-report stack.
  • tracing removal from simlin-mcp-core verified - no source references in src/simlin-mcp-core/src/.

Overall correctness verdict

Correct. The changes preserve existing public API contracts, are accompanied by new tests where behavior was reimplemented (Accordion, additions to Autocomplete and Menu, db-firestore on-demand init), and the dependency removals are appropriately justified. No actionable bugs to flag.

The Autocomplete list only closed via input blur. A press on a surface that
prevents the focus change -- e.g. the diagram canvas, which preventDefault()s
pointerdown for pan/drag -- never blurs the input, so the portaled listbox could
stay open over the canvas. Add a capture-phase outside-pointerdown handler that
closes the list when the press lands outside the field and the listbox (matched
by id, since the listbox is portaled), independent of blur -- the dismissal
behavior downshift provided.
@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown

Code review for PR #804

I reviewed the supply-chain reduction changes across the server (firestore retarget, cookie-parser removal), the diagram component reimplementations (Accordion, Autocomplete + useCombobox, Checkbox, Menu, SpeedDial tooltip), and the Rust trims (criterion features, unused tracing in mcp-core).

The retargeting work is careful and well-scoped:

  • firebase-admin/firestore re-exports the same Firestore/FieldPath/CollectionReference surface, and the on-demand initializeApp() in db-firestore.ts is correctly gated by getApps().length === 0 (with a test pinning both branches).
  • cookieParser() removal is correct — req.cookies/req.signedCookies are unreferenced in the server and seshcookie parses the Cookie header itself.
  • The reimplemented components preserve their previous public API and pass the existing tests; new tests added for Menu dismissal, Accordion a11y/inert, and Autocomplete keyboard/pointer behavior look sensible.
  • The Menu's skipBlurCloseRef interaction with the outside-press path (where onClose synchronously tears down the pointerup reset) is correctly handled by resetting the flag in the open-effect.
  • The Rust changes are clean: no benchmarks reference plotters symbols, and no simlin-mcp-core Rust source uses tracing.

One minor behavior change worth noting (not a bug, but a subtle UX divergence): in the new useCombobox, pressing Escape without clearOnEscape simply closes the dropdown without restoring inputValue to the previously selected item. Downshift's default behavior reset the input on Escape; the new implementation leaves the user-typed text in the field. If any consumer relied on the old reset semantics, they'd see stale text after Escape. The existing tests don't exercise this case, so it's worth confirming it matches intent.

Overall correctness: correct

The patch should not break existing tests, and I did not identify any blocking bugs. The behavior change above is the only thing worth a second look.

@bpowers

bpowers commented Jun 20, 2026

Copy link
Copy Markdown
Owner Author

Review cycle complete

Both automated reviewers (codex and the Claude /review agent) report no actionable issues on the final commit, in the same iteration. This took 14 review rounds; nearly every codex round surfaced one more genuine behavioral edge that the dropped libraries (downshift, Radix dropdown/tooltip) had handled, and each was fixed at the root with a regression test. The diagram suite grew from 1195 to 1213 tests, all green, and every commit cleared the full pre-commit battery.

The bulk of the work hardened the owned reimplementations to true downshift/Radix parity. The Menu gained real focus management — focus moves into the menu on open, arrow/Home/End roving with tabIndex=-1 items (so Tab leaves the menu rather than cycling), Escape returns focus to the trigger, and dismissal handles outside presses, focus-out (including to browser chrome and back to the trigger), and the anchor-press case without flicker. Dismissal moved from mousedown to capture-phase pointerdown so a press on a surface that preventDefault()s pointerdown (e.g. the diagram canvas) still closes it. The Autocomplete/useCombobox gained the same outside-pointerdown dismissal, keyboard-blur commit of the highlighted option, scroll-into-view for off-screen highlights, and correct touch behavior (option taps keep selection; tap-to-dismiss never auto-commits) — all distinguishing genuine keyboard focus-out from pointer-driven blurs via a strand-proof pointer tracker. The Accordion collapsed region is now inert + aria-hidden (not just visually hidden), and the server getFirestore() retarget initializes a default Firebase app on demand so standalone callers (e.g. scripts/debug-import.mjs) keep working.

@bpowers

bpowers commented Jun 20, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

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

Reviewed commit: b19cba6706

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

@bpowers bpowers merged commit 5dc2e40 into main Jun 21, 2026
15 checks passed
@bpowers bpowers deleted the dep-diet-round2 branch June 21, 2026 04:55
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.

simlin-mcp-core: remove declared-but-unused tracing dependency

1 participant