Repository navigation
UI Consistency: 1 issue found
apps/web/src/components/settings/ProviderInstanceCard.tsx(L161-171): the new environment resync effect compares successiveprops.environmentvalues, so it also fires for updates the section itself published; any commit on one row rebuildsrowsfrom props and discards unpublished empty rows added via Add. Suggested fix: gate the reset on the last locally published value instead of the previous props.
Previously flagged issues appear resolved in this revision: the Models/Configuration tab strip now sits outside the read-only inert subtrees, the greyed-out treatment is scoped to the inert regions rather than the navigable provider list, and providerSettingsTabClassName now uses an inset focus ring so it is not clipped by the device strip's ScrollArea.
Details
Note
Your check run agent prompt is: .macroscope/check-run-agents/ui-consistency.md
More information about how Check Run Agents work can be found in our Help Center.
Reviewed commits since the last run: 652a7ac (sync provider environment drafts) and 123fb56 (keep read-only provider navigation active), plus a re-check of the full merge-base diff for the in-scope files (ProviderInstanceCard.tsx, ProviderSettingsPanel.tsx, providerSettingsTabs.ts, providerStatus.ts).
Checks performed:
- Verified
ring-insetis a valid Tailwind v4 utility in this repo (used bySidebar.logic.ts,PreviewRecentUrlCard.tsx,MessagesTimeline.tsx), so the new inset focus ring on the shared tab helper resolves the earlier clipping finding. - Traced the read-only path: list rows stay interactive with
Switch disabled={readOnly}; the editor header and body areinert+ dimmed while the tab strip between them stays live, matching the navigation-vs-write split. - Traced
ProviderEnvironmentSectionstate flow againstDraftInput/useCommitOnBlur(commits on blur and Enter-then-blur) andmakeEnvironmentDraftRow(ids areindex:name, so unchanged rows are not re-keyed). Focus loss is therefore not a concern; the loss of unpublished draft rows is. - Confirmed
providerEnvironmentsEqualcovers all four fields of theProviderInstanceEnvironmentVariablecontract (packages/contracts/src/providerInstance.ts), so the equality helper itself is complete. - Confirmed
SettingsPageContainersupports thewidth="expanded"andclassNameprops used by the panel, and that the Advanced disclosure and device strip now use the sharedpx-3 sm:px-4inset.
π Agent activity β 16 tool calls
π I want to see what's changed on the PR since commit d36f66b, since the head is now at 123fb56, so I'll diff between those two commits.
1. git_tools β d36f66b..123fb56
2. git_tools β a6797b3..123fb56
π Looking at the commit history, 4b2e49a was already reviewed, so the two new commits I need to check are 652a7ac (syncing provider environment drafts) and 123fb56 (keeping read-only provider navigation active). I'll pull up those diffs now.
3. git_tools β 652a7ac
4. git_tools β 123fb56
π I'm tracing through the latest diff to confirm the structure change: the editor header and body are now wrapped with
inert={readOnly}, and I need to determine whether the tab strip sitting between them is inside or outside that inert wrapper, since that affects whether it stays interactive in read-only mode. Good, the tab strip stays outside the inert wrapper, so tabs remain clickable in read-only mode as intended for navigation, which resolves the earlier concern. The delete button in the header is inert-blocked in read-only regardless of howonDeleteis conditionally passed, so that's fine, and the visual inconsistency of the tab strip staying full opacity while header/body dim to 50% seems like a reasonable, likely intentional choice to keep navigation visually active. Now I'm tracing the environment sync effect in ProviderEnvironmentSection, where rows are reset from props when the incoming environment differs from the previous value via deep-equal comparison. I'm worried this creates a bug: since ProviderInstanceCard remounts per selected row via key, switching selection is fine, but when a user edits andonChangefires, updating props, the effect could re-run and regenerate row ids from props, potentially wiping local draft state like an in-progress empty-named row ifpublishRowsfiltered it out before the round-trip.
5. browse_code β apps/web/src/components/settings/ProviderInstanceCard.tsx
π I'm tracing through the sync logic: an empty draft row that was only added via setRows, without any name, value, or redacted value set, gets filtered out by the early-continue check when publishing, so it correctly never makes it into the published list. But then when the user commits a valid variable elsewhere, publishRows fires, props.environment changes, and the effect's deep-equal check sees old vs new props differ, triggering setRows(props.environment.map(...)) which regenerates all row ids. Since rows are keyed by id, this remounts every row and could steal focus or drop uncommitted input from other draft rows β a real regression the sync effect introduces, since the comparison is only against the previous props reference, not against local unsynced state.
6. git_tools β export function DraftInput
7. browse_code β apps/web/src/components/ui/draft-input.tsx
π On Enter, the input retains focus after commit, but if the row remounts with a new key, that focus gets lost mid-editβa moderate issue. There's also a bigger concern: an empty draft row added via "Add" could get silently discarded when another row commits, since publish skips it and props reset the state. This still feels within scope though, since prior checks flagged similar behavioral regressions like read-only navigation and inert states. I'm also double-checking whether the array reference passed as props is actually stable across renders, since the deep-equal guard should prevent unnecessary resets when the environment array's identity changes but not its content β this seems to fix the earlier bug where an empty array was recreated fresh each render. Now I'm working through whether this draft-row-loss issue is a genuine regression versus the merge base, since at merge base there was no reset effect at all β state was initialized once and rows persisted. This PR's new effect appears to introduce the wipe, but I need to understand what problem it was meant to solve, possibly related to preserving drafts when settings sync externally, so I'm trying to trace back to the commit that introduced this behavior to understand its original intent.
8. git_tools β d36f66b
π I'm realizing that using
hiddenkeeps both panels mounted, so state should persist naturally across tab switches without needing a sync effectβwhich makes me question why commit 652a7ac added that sync logic in the first place, unless it was meant to handle external prop changes rather than tab switching itself. Rather than dwell on that, I want to shift to reviewing the full current state of the PRβlooking at the complete ProviderInstanceCard editor section and ProviderSettingsPanel to catch any remaining UI-consistency issues across the whole diff, while being careful not to repeat points I've already flagged.
9. browse_code β apps/web/src/components/settings/ProviderInstanceCard.tsx
π Checking the list row select button's focus ring placement β it looks fine given the padding. I'm also verifying that the provider settings tab's inset ring fix actually works correctly in this version of Tailwind. Checking whether the header being inert makes sense since it holds the delete/update controls, while the tab strip stays reachable outside the inert wrapper β that resolves the earlier tab-reachability concern. I should also verify the read-only Switch disables correctly and check the list header row's spacing. Let me search the repo for other uses of
ring-insetto see if this styling choice matches existing conventions elsewhere.
10. git_tools β ring-inset|inset-ring
11. git_tools β tailwindcss
π I'm checking whether the focus ring on the list-row select button might get clipped by the row's overflow-hidden container, given the row's padding and minimum height. Turning to the two recent commits, I want to verify the panel state and container width settings still match prior review comments, then focus specifically on new issues introduced by these changes. One concern from the newer commit: making the editor header inert during read-only mode could block access to the update-available popover, which contains a harmless copy command a viewer might still want to use even without update permissions. Checking further, though, the entire editor column was already inert before this commit, so that's not a new regression β the header just inherits existing behavior. I'm also noting the tab strip stays at full opacity between two dimmed panels, which is consistent since navigation should remain interactive even in read-only mode, similar to how the list column behaves. A more substantive issue I want to flag is the environment sync effect: it resets rows whenever a self-originated commit happens, which can wipe out pending empty draft rows and re-key inputs unexpectedly. This looks like a genuine interaction regression introduced by these commits that hasn't been raised before, and it's worth checking whether preserving interaction semantics falls within scope for this review. I'm tracing through the exact sequence: a user adding a new empty row, typing a name, then blurring β the environment prop update triggers the effect, which rebuilds rows with fresh IDs even when the underlying content matches. This id churn on an otherwise identical row is the concerning part I want to verify carefully. If the row's key changes, the DraftInput remounts, which means committing via Enter would kill focus right when the user's cursor is still in the field β a real regression where subsequent keystrokes go nowhere. There's also a second scenario: adding a second empty row, then editing and blurring the first row causes a prop update that resets rows from props and wipes out the still-empty second row the user hadn't filled in yet, which is a genuine data loss case worth flagging alongside the redaction-state question for sensitive values. I think this fits the review scope since it's an interaction/state-preservation issue, similar to prior focus-ring and accessibility flags. The fix would be to track the last published row values in a ref and skip resetting from props when the incoming props match what was just published, rather than always diffing against the previous props. I'm working out that the effect should only re-check equality against the last published value stored in the ref, and confirming that the environment-cleaning logic already excludes empty-name entries so the comparison stays consistent. Since the fix spans multiple hunks in the card component, I'm leaning toward describing the change with a small diff rather than an inline suggestion. Now I want to make sure I haven't missed anything else in the panel, so I'm double-checking whether SettingsPageContainer already supports the
width="expanded"andclassNameprops, and whether ScrollArea already exposes hideScrollbars/scrollFade.
12. git_tools β width|SettingsPageContainer|hideScrollbars|scrollFade
π I'm checking whether the Advanced trigger and device strip padding fixes actually made it into the current code β confirming both now use the responsive px-3/px-4 spacing as intended. Now I want to scan further down in the panel to catch any remaining issues.
13. browse_code β apps/web/src/components/settings/ProviderSettingsPanel.tsx
π I'm noting that in read-only mode the Advanced section is forced open even though it's inert, meaning the trigger can't be collapsedβthis seems intentional since the health interval stays visible, so it's harmless rather than a bug. Checking other panels: the tab visibility uses the
hiddenattribute, and Tailwind's preflight appliesdisplay: none !importantto hidden elements, so that works correctly. I'm also confirming that a<code>element nested inside a button and span is fine structurally, and moving on to look at how the list row's select button determines itsaria-pressedstate relative to the default selected row fallback. Looking at read-only mode, theonSelecthandler is still passed through, so that's fine. I also verify that the row button's focus ring won't get clipped since the padding and overflow settings give enough room β this matches the earlier fix usingring-insetfor the tab strip, and I confirm that inset ring still renders visibly within the scrollable tab area. The main open question left is whether the environment draft-reset behavior is truly a meaningful regression worth flagging. I also want to check whether "focus loss on Enter" is a real issue by looking at howuseCommitOnBlurbehaves β if pressing Enter also triggers blur, then the remount concern becomes irrelevant.
14. browse_code β apps/web/src/hooks/useCommitOnBlur.ts
π Since Enter always blurs first, the commit-after-blur ordering removes the focus-loss concern. But there's still a real bug: if a user adds a new empty draft row and then edits and blurs an existing row, the resulting prop update replaces the whole rows array and wipes out that pending empty row before they can fill it in. I check the invalid-name case too β publishRows aborts entirely if any row has an incomplete pattern, so no props change happens there, meaning no reset risk in that scenario. So the real loss case is narrower than I thought: it's specifically about newly-added empty rows getting discarded whenever a sibling row commits, which feels like a minor but worth-flagging interaction bug. I'll trace the fix: track the last published value in a ref so the sync effect can skip resets that match its own recent update. The effect in question sits around lines 161-171, so I'll comment on the setRows line or that range in the diff. I also want to double check the read-only editor visuals β the tab strip is active now while the editor content is dimmed, contrasting with the list rows which stay full opacity. The prior review comment about scoping the greyed-out treatment to the two inert subtrees and keeping the tab strip outside inert seems to have been addressed correctly. The disabled Switch in read-only list rows also looks fine as-is. One thing worth verifying:
providerEnvironmentsEqualonly compares name, value, sensitive, and valueRedacted β I should check the schema to confirm there isn't another field on ProviderInstanceEnvironmentVariable that could be missed by this equality check.
15. browse_code β Show the definition/schema of ProviderInstanceEnvironmentVariable (all its fields) and the makeEnvironmentDraftRow / EnvironmentDraftRow definitions used in apps/web/src/components/settings/ProviderInstanceCard.tsx.
π Since draft row ids are deterministic based on index and name, unchanged rows won't remount on reset β that concern is resolved. The real issue remains that a pending empty row, with its own temporary id not present in props, vanishes after reset. I also note that renaming a row changes its id and triggers a remount, clearing any unfocused draft state. The core finding: adding an empty row via the Add button is purely local state, so any commit on another row triggers a resync that drops that unpublished empty row β reachable simply by adding two empty rows, filling and blurring one, which publishes and wipes the second. A fix would be tracking the last published array to skip resets caused by the component's own publish, or otherwise preserving unpublished draft rows during resync. I want to post an inline review comment on the effect around the line building rows from props.environment, verifying it aligns with the diff hunk covering the new useEffect. My comment will explain that the resync fires even for updates the component itself published, so committing one row can silently drop other newly-added but unpublished rows, and I'll suggest gating on the last value this component actually published rather than comparing directly to previous props. I'll keep any illustrative fix as brief prose rather than a diff since this spans multiple hunks. Since this is a genuine regression risk, I'll mark the check state as failure and submit the review.
16. modify_pr β One finding on the new environment-draft resync in ProviderInstanceCard.tsx. Previously flagged issues (tab strip reachable in read-only, greying scoped to the inert subtrees, inset focus ring in providerSettingsTabClassName) look resolved.
Agent Credits: 165 credits