fix(desktop): keep Settings stable during Runtime Host refresh - #3450
Conversation
jackwener
left a comment
There was a problem hiding this comment.
Automated review of exact head 54651d6b0aeca844f527f75cfe00f4a8d6b6c9fc against current main@d62857a8357e9160926726a2a13096bc2dc2b91d.
The defect remains on main: every Settings mount starts the Runtime Host catalog/settings/connections resources cold, so the mixed-ownership General page briefly renders defaults/loading feedback before Host data arrives. The patch keeps only masked read snapshots in renderer memory, keys Host data by profileId:hostId, prunes an old epoch when the catalog changes, and keeps cached Host controls inert until the current mount verifies the target. The separate read/write tickets also prevent a late old-Host result from populating the newly selected Host. The reopen E2E observes the transient DOM, so it would catch the original flash rather than only asserting the settled page. I found no actionable P0-P2 defect; git diff --check is clean.
Required conclusions:
- Optimal for the actual problem: yes. The implementation is larger than the symptom, but stable cross-unmount rendering requires a cache, and the masking, epoch key, verification bit, and interaction fence are necessary to avoid turning stale UI into write authority.
- Production code to delete: the PR replaces the older parallel resource unions/ticket refs; none further identified.
- Tests to delete/replace: none identified. The helper tests and transient-DOM E2E cover distinct contracts.
- Deeper refactor: no; keeping the cache outside AppShell avoids lifting Settings-only state into a broader owner.
- Ready to merge: not yet. No hosted
testcheck is reported on this exact head, and automated review is not approval. - Residual risks/gaps: this changes user-visible loading/error behavior and the security-sensitive Host write boundary across General, Projects, and Data. The supplied tests cover cache epochs and the default General reopen, but independent human review should still exercise non-default Host selection and failure/retry behavior.
This affects user-visible behavior and Runtime Host authority fencing, so CONTRIBUTING.md requires independent human judgment on the exact head.
54651d6 to
43e53f7
Compare
jackwener
left a comment
There was a problem hiding this comment.
Automated re-review of exact head 43e53f751ba9b604b994d75bf88c8a2781972e08 against main@f1f4e71a9540a4da23159052c924fee72202e989.
No new actionable implementation defect was found relative to the previously reviewed revision. git range-diff shows the same one-commit settings stabilization adapted onto the newer base; the keyed snapshot/cache/request-authority structure still keeps one Host's data from being exposed through another, retains last-ready content during revalidation, and fences mutations until the selected Host is freshly verified.
The head is not mergeable, however. Current main and this PR both modify apps/desktop/e2e/settings.spec.ts, settings-surface.tsx, and nav-sidebar.css; the E2E conflict specifically places main's “wide settings gutters scroll the whole main pane” test against this PR's reopen-stability test. Rebase and preserve both behavioral contracts rather than choosing one side. Re-run the exact-head check after resolution.
Required conclusions:
- Optimal for the actual problem: yes; the cache remains renderer-memory, keyed by bridge/Host identity, and mutations stay behind fresh authority.
- Production code to delete: none identified.
- Tests to delete/replace: none; preserve both the main scroll-ownership E2E and this PR's reopen-stability E2E.
- Deeper refactor: no.
- Ready to merge: no while conflicting; there is also no hosted
testresult or independent human approval on this head. - Residual risks: conflict resolution is the primary regression risk, particularly losing main's newly merged settings scroll ownership or this PR's revalidation coverage.
This changes user-visible Settings behavior. Independent human review is required; this automated review is not approval.
Astro-Han
left a comment
There was a problem hiding this comment.
Thanks — the cache ownership and Runtime Host fencing are thoughtfully structured, and I did not find a cross-Host authority or stale-write correctness issue on exact head 43e53f751ba9b604b994d75bf88c8a2781972e08. I found two non-blocking user-facing gaps below.
Since this head currently conflicts with main in the same Settings surface, please preserve these final-state requirements while rebasing. After the semantic rebase, these findings remain P2 and do not by themselves block approval.
AI-assisted review disclosure: Codex delegated independent review passes; I verified the reported paths and conclusions against this exact head.
43e53f7 to
fa01d91
Compare
|
@Astro-Han Thanks for the careful review. I rebased onto the current
The semantic rebase also preserves both #3453 scroll ownership and this PR’s reopen-stability contract. Exact-head verification: desktop tests 1076/1076, Storybook smoke 169/169, Settings E2E 4/4, and full Electron E2E 41 passed / 1 skipped. Could you please take another look when convenient? |
Astro-Han
left a comment
There was a problem hiding this comment.
Thanks for the substantial cleanup of the settings cache and intent flow. The exact-head test is green, and the earlier findings are improved, but the current page-level boundary still mixes Desktop-owned and Host-owned availability, and the refresh generation does not yet revoke write authority. I left the two final-state requirements inline.\n\nAI-assisted review disclosure: Codex delegated independent state and test reviews; I reconciled the conflicting conclusions against the exact-head control flow and live PR state before posting.
fa01d91 to
1ac77bf
Compare
|
@Astro-Han Thanks for the follow-up review. I rebased the branch onto The follow-up binds Runtime Host reads and writes to lifecycle epoch as well as Exact-head local verification is green: lint, format, build, typecheck, both knip workspaces, Astryx inventory (191 files), Desktop tests (1118/1118), Storybook build/render smoke (172/172), and Electron E2E (41 passed, 1 repository-defined skip). The new hosted CI run is awaiting maintainer approval before its jobs can start. Could you please re-review the new exact head when convenient? — Codex, posting on behalf of @chihumyum |
29153de to
280d833
Compare
Astro-Han
left a comment
There was a problem hiding this comment.
Re-reviewed at the new head 280d833a after the rebase. We re-verified each of our earlier findings against this head rather than carrying them forward, and one of them does not survive.
Withdrawing our P1 about the ownership seam — you were right and we were wrong. Our claim was that the outer RuntimeHostInteractionBoundary renders the whole SettingsPageBody inert and so blocks desktop-side controls on pages that mix client and Host concerns. That premise does not hold: requiresRuntimeHost is sectionScope === 'runtime-host', and SETTINGS_SECTION_SCOPES marks general, projects and data as 'mixed', so isInteractive={!requiresRuntimeHost || runtimeHostContentVerified} evaluates to true on exactly the pages we were worried about. The inert branch is unreachable there. We should have traced the scope table before filing it.
Two P2 from the earlier rounds are confirmed fixed. The write-authority/epoch one: settings-request-authority.ts now binds each ticket to (key, targetEpoch, targetRevision, generation) and selectTarget bumps every generation when the key or epoch changes, so a superseded mutation response is rejected before commit. The catalog-intent one: the parent now waits for the child's onInitialProviderCatalogConsumed callback before clearing providerCatalogRequested (settings-surface.tsx:998-1000).
One P2 from the earlier round is still open — the boundary is invisible. RuntimeHostInteractionBoundary still applies only inert plus aria-busy. Assistive technology is informed; a sighted user is not. During a slow reconnect the page looks fully usable and silently swallows clicks. A muted/loading treatment on the wrapper would close it.
One new P2, inline — and it is worth noting that this is where the risk our withdrawn P1 was gesturing at actually lives. The mechanism we described was wrong, but there is a real gap on the mixed pages; it is just an inconsistency between them rather than an outer-boundary problem.
Everything else on the rebase increment checks out — the epoch fix survived intact.
This review was AI-assisted. Findings were verified against the exact head listed above; any mistakes are ours to correct — please push back where we got it wrong.
Astro-Han
left a comment
There was a problem hiding this comment.
Thanks — and I owe you a correction before anything else.
Re-reviewed at exact head 7a332f583df031cde86ef5e799291340d978c830. test is green.
My earlier fence-visibility P2 was wrong, and it was my mistake. I claimed the boundary left unverified content looking fully actionable. It doesn't: .settingsRuntimeHostInteractionBoundary[inert] > * sets opacity: var(--opacity-disabled) in nav-sidebar.css, and the component already pairs inert with aria-busy. I reviewed the component and not the stylesheet that goes with it, so I saw two thirds of the mechanism and reported the missing third as absent. It was there the whole time. Sorry for the detour.
The other three all check out on this head:
- Ownership seam —
requiresRuntimeHostkeys offsectionScope === 'runtime-host', and the mixed pages resolveisInteractiveto true, so the seam sits where you said it does. - Write authority — tickets bound to
(key, epoch, revision, generation). - Catalog intent — the parent waits on
onInitialProviderCatalogConsumed.
I also went to check your comment that the project read does not start while unverified, half-expecting to find it only gated on host. It isn't — reload returns early on !host || !props.runtimeHostTargetVerified (projects-settings-page.tsx:99), and the same guard repeats at 109, 158 and 193. Your description was accurate and I was wrong to doubt it.
On proportion: 2230 lines is a lot for "keep Settings stable", so I split it — 973 lines are tests and stories, leaving 1257 for the state machine, snapshot cache and boundary. That reads as matched to the problem rather than a refactor riding along.
No findings. Approving.
This review was AI-assisted. Findings were verified against the exact head listed above; any mistakes are mine to correct — please push back where I got it wrong.
|
needs rebase~ |
7a332f5 to
41dbbe7
Compare
|
41dbbe7 to
34c52eb
Compare
|
@Astro-Han I rebased this branch onto the current Local exact-head verification is green: lint, format, build, typecheck, Astryx inventory (202 files), both Knip workspaces, and Desktop tests (1221/1221). The hosted CI run is waiting for maintainer approval before jobs can start. Could you please approve the run when convenient? — Codex, posting on behalf of @chihumyum |
Astro-Han
left a comment
There was a problem hiding this comment.
Approving 34c52eb25950e95f6182ae6b27f260b14211834a. Required test is completed / success bound to that exact SHA. No P0–P3.
This is a re-review at the current head. The prior approval was given on 7a332f58, and the branch has moved since, so the conclusion was re-derived rather than carried forward.
Range-diff against the previously reviewed state: of the three semantic commits, 448298d8f (fence Settings across Host generations) and 34c52eb25 (clarify Projects revalidation authority) are byte-identical to what was reviewed. 6b6c4fc82 (keep Settings stable during Runtime Host refresh) is byte-identical apart from the regenerated inventory lines. Everything since is the rebase onto main@1d06330a1.
The previously failing check was reproduced independently rather than assumed fixed: check-astryx-surface-inventory.mjs at this exact head returns ok (202 files).
One thing recorded rather than escalated: the hosted run skipped Run Runtime Host tests via the affected-surface planner. This PR sits on the Settings/runtime-host boundary, but its own diff changes no file inside the runtime-host package, and the earlier rounds covered that surface locally. Flagging it because a planner skip and a genuine pass look the same in the summary view, and the distinction should be visible to whoever merges rather than buried.
The muted/inert boundary styling is unchanged from the reviewed state, so there is no new visual surface here.
Disclosure, because it changes what this approval is worth: this is an AI review. Under CONTRIBUTING.md §Review it does not count as the required independent human review. It means the code has been checked, not that the gate is open — merge still needs a committer other than the author to give LGTM and to decide.
Astro-Han
left a comment
There was a problem hiding this comment.
A maintainer challenged this PR's proportionality directly: if this is just a flicker / lost-state bug, is this the right shape of fix? Isn't it over-complex — do you need this many modules? That is a fair question for +2230 lines and five new modules, so it was audited as a first-principles question rather than as a findings pass, at exact head 34c52eb25950e95f6182ae6b27f260b14211834a.
The conclusion did not go the way the challenge pointed: the core complexity is root-cause-driven and should not be reduced to "add a skeleton". No new P0–P3. What follows is the reasoning, including the part that would justify a smaller shape.
First, a framing correction. About 44% of the added lines are unit tests, E2E and Storybook (356 + 66 + 551). That does not by itself prove proportionality, but "+2230" should not be read as 2230 lines of production logic.
The problem is two independent failures stacked, not one cosmetic one.
SettingsModalis gated bysettingsOpen && (...)inapp-shell-overlays.tsx:134— closing Settings unmounts it entirely. On reopen the old implementation restarted from defaults and empty resources while the Host's catalog, settings and connections were still in flight, which is what produced the loading banner and the collapsing Host rows.- The Host lifecycle has an
epoch, but the renderer's authority was effectivelyprofileId:hostId. When the same profile/host reconnects or is replaced, a stale read or write still looks like it belongs to the current key. Swappingloadingfor a skeleton does not change that: the stale response still lands in the new Host generation.
So the minimal closing concept is not "a skeleton". It is: a generation authority (profileId + hostId + epoch, invalidating outstanding tickets on lifecycle events); snapshot state that separates hasSnapshot from isVerified, so a retained value is never mistaken for current Host authority; an interaction fence, so a stale snapshot is visible but Host-backed controls stay inert until re-verified; and — because the modal genuinely unmounts — a cache that outlives the unmount.
That last one is worth naming, because it is the module that looks most decorative and is not: the new E2E in settings.spec.ts makes "reopening Settings shows no loading banner and does not interrupt Host controls" an explicit acceptance condition. Under that contract a renderer-memory cache keyed on bridge identity is part of the behaviour, not polish.
Where the challenge does land: the module boundaries are consolidatable even though the concepts are not. settings-request-authority.ts, settings-resource-state.ts and the cache could reasonably collapse into a single surface-owned useSettingsAuthority object without changing behaviour; settings-skeleton.tsx's top-level skeleton is UX rather than race repair; and reconcileRuntimeHostProfileSelection is a selection policy sitting in a resource-state module with exactly one production caller (settings-surface.tsx:642), so it belongs closer to the surface. None of that is a correctness finding — file count is not a defect — and trading a module away for the old race back would not be a simplification.
Two things are product decisions, not code cleanup, and are flagged rather than decided here: whether a stale-but-inert Host value is acceptable at all (the alternative is an explicit "verifying" state), and whether SettingsRowSkeleton must reserve layout for not-yet-known Host rows — the current E2E requires that General never shows a window with no Host controls, so it cannot be removed on aesthetic grounds alone.
Required test is completed / success bound to this exact SHA. This audit does not block merge.
AI-assisted review. The unmount gate, the epoch-fencing rationale, and the single-caller claim above were each verified against the source at this exact head. Under CONTRIBUTING.md §Review this does not count as the required independent human review.
34c52eb to
80f236d
Compare
Astro-Han
left a comment
There was a problem hiding this comment.
Reviewed at exact head 80f236dea0f7ca99c03bed1a2750896a387ed2cb. Re-reviewed from scratch: the earlier approval on this PR is bound to an older head, so nobody had looked at the current one.
Coverage: where Settings state comes from across a Runtime Host refresh, which user-visible state is rebuilt or discarded, and whether "stays stable" is achieved by caching old values or by deferring re-render. Not covered: visual and copy.
One [P1].
[P1] The preserved Models subtree keeps OAuth state from the retired generation
Same-key Host epoch replacement revokes and revalidates the catalog, Settings, and Connections. The Models subtree is intentionally kept mounted so the replacement does not destroy drafts, route, scroll, and focus — that part is a good design choice.
But the OAuth card/account snapshots and the active login controllers inside that subtree only refresh on mount. Because the subtree is deliberately never remounted, once the outer fence lifts they carry state belonging to the retired generation:
- stale signed-in status and account email displayed against the current Host;
- Login/Logout actions that are still live and act on the current Host while originating from the retired generation;
- a pending browser authorization that is not generation-cancelled, so an authorization started under the old Host can land after replacement.
This is the failure mode the outer fence exists to prevent — it just does not reach into this subtree, and the exemption that protects the user's drafts also protects the stale credentials view.
The fix should propagate an epoch/revision token into the OAuth sub-authority so that sub-tree revalidates on generation change. Remounting the whole Settings tree would fix the staleness and reintroduce exactly the draft/route/scroll/focus loss this PR set out to eliminate, so that is the wrong lever.
Verification and limits, stated plainly
- Clean root build passed; 62/62 targeted state/OAuth/Host tests passed.
- The Settings Electron end-to-end suite could not be run — the reviewing host has no Electron GUI libraries. So the finding above rests on state-ownership tracing plus the targeted suites, not on an end-to-end reproduction.
- There is no hosted green for this head: the hosted
testrun was cancelled during dependency install. Not cancelled-and-therefore-fine — simply no terminal result, so the CI gate is unmet independently of the finding.
80f236d to
2f2d5e3
Compare
|
@Astro-Han Thanks for the exact-head review. I rebased onto current main@4852d4922 and addressed the OAuth generation P1 in 2f2d5e3. The selected Runtime Host lifecycle epoch now flows into a Settings generation context. OAuth card snapshots clear and re-read on generation change, with ticket fencing preventing a retired read from landing later. Only the OAuth login and relogin controllers sit behind the keyed generation boundary, so their existing unmount cleanup cancels any pending browser authorization. The parent Settings and Models subtree stays mounted, preserving route, catalog filter, drafts, scroll, and focus. Coverage now includes a focused lifecycle test plus Storybook journeys for both stale account replacement and an in-flight authorization cancelled exactly once while the provider setup route survives. Exact-head local verification is green: lint, format, ASF headers, Astryx inventory, full typecheck/build, Desktop tests, both Knip workspaces, Storybook build and 184/184 render smoke, and Settings Electron E2E 5/5. Hosted CI has just been retriggered and does not have a result yet. Could you please re-review exact head 2f2d5e3 when convenient? — Codex, posting on behalf of @chihumyum |
2f2d5e3 to
e97d137
Compare
|
@Astro-Han Rebased again onto current main@cd0163a1e. The only semantic conflict was General Settings: I preserved upstream WorkHub as a Desktop-owned, always-available control while retaining this PRs Host-owned workspace-instructions snapshot and placeholder behavior. The other conflict was resolved by regenerating the Astryx inventory. New exact head: e97d137. Range-diff shows the generation-fence and Projects commits unchanged; the stabilization commit differs only by the upstream WorkHub row, and the OAuth commit only by the regenerated inventory total. Exact-head local verification is green: lint, format, ASF headers, Astryx inventory (205 files), full typecheck/build, Desktop tests 1336/1336, both Knip workspaces, Storybook build and 184/184 render smoke, and Settings Electron E2E 5/5. The PR is mergeable again. Hosted CI has been retriggered and has no result yet. Please review exact head e97d137 when convenient. — Codex, posting on behalf of @chihumyum |
Astro-Han
left a comment
There was a problem hiding this comment.
PR #3450 re-review — e97d1378df68307beb9cb7dd05bb86f9f0cbcaa3
Outcome
NO-GO: one P1 remains.
Finding
[P1] Retire the GitHub Copilot connection-detail controller on Host generation changes
GitHubCopilotReloginNotice remains mounted across a same-key Runtime Host generation change. It captures the Host and starts connectExistingLogin, then accepts the late result and invokes onRelogin whenever its component-level mounted ref remains true (provider-connection-detail.tsx:942-977). Unlike OAuthReloginNotice immediately below it (provider-connection-detail.tsx:997-1006) and the catalog login panel, it is not wrapped in RuntimeHostSettingsGenerationBoundary.
Reproduction: open a GitHub Copilot connection detail, click Import/Reimport, and replace the selected Host with the same profileId:hostId before the promise settles. The Settings/connection-detail tree deliberately stays mounted, so this controller's mounted ref and action guard survive: the old generation can report an error or call the stale onRelogin callback into the new page, and the new generation's action remains blocked until the old request finishes. This is the same class of Host-owned OAuth subauthority the new generation boundary is intended to retire. Wrap this controller (or move it onto the already generation-keyed shared direct OAuth controller) and add the same-generation replacement regression.
Verified behavior
- OAuth card snapshots clear and re-read on
generationKey; the monotonically increasing refresh ticket rejects old-generation and same-generation superseded reads. - Browser OAuth login/relogin controllers remount under the narrow generation-keyed boundary. Unmount cancels a known pending authorization ID and the mounted fence suppresses late callbacks; an initiation still awaiting
getAuthUrlcancels its ID when it eventually returns. - The boundary is narrow: parent Settings, Models route, provider catalog filter, DOM scroll/focus, and drafts stay mounted. Existing stories exercise catalog route retention, account replacement, and browser authorization cancellation.
- Mutation check: removing the
Fragmentgeneration key maderuntime-host-settings-generation.testfail specifically because the OAuth controller did not unmount/remount; stable Settings state remained outside the keyed boundary. Source was restored and rebuilt afterward.
Checks run
- Exact-head hosted
test: SUCCESS. - Desktop workspace dependency builds: green.
- Desktop TypeScript checks (preload, main, renderer, Storybook): green.
- Focused OAuth/Host generation suites: 35/35 green.
- Worktree clean after mutation restoration.
e97d137 to
cbace2e
Compare
|
@Astro-Han Thanks for the exact-head review. The remaining Copilot generation P1 is fixed, and the branch is rebased onto current
A new Story starts a pending Copilot reimport, replaces the same During the rebase, current main's newly merged Goal adapter test also exposed three fixture fields that are not part of Exact-head verification: lint, format, ASF headers, Astryx inventory (207 files), full typecheck/build, Desktop tests, both Knip workspaces, Storybook build and 186/186 render smoke, and Settings Electron E2E 5/5. The PR is mergeable; the newly triggered hosted check has not reported a result yet. Could you please re-review |
jackwener
left a comment
There was a problem hiding this comment.
Independent review of cbace2eb9336f32cd10a3622691855629674c92a.
GO for the stated problem; 1×P2 so I am not approving. Hosted test is terminal success. PR is CONFLICTING — no merge from me.
What this solves
Settings was flashing the warning banner and collapsing Runtime Host rows on every reopen/revalidation. The cut is right: keep last-ready masked snapshots visible, bind write authority to Host key + lifecycle epoch, mute Host-owned controls until that generation is verified, and leave Desktop-owned prefs usable on mixed pages.
Findings
[P2] independently derived — models lifts the write fence before the new generation's connections are verified
See inline on settings-surface.tsx. After a same-key Host replacement the catalog can come back isVerified while connections are still the previous generation; ProvidersPanel also keeps its old list clickable because reload() does not enter a loading state.
Prior Copilot P1 — independently re-judged closed on this head
Both GitHubCopilotReloginNotice and OAuthReloginNotice remount under RuntimeHostSettingsGenerationBoundary, and connectExistingLogin now returns before onRelogin when the old controller has unmounted. I am not re-filing it.
No P0/P1 on this head.
Generated-by: Codex
Generated-by: Codex
Generated-by: Codex
Generated-by: Codex
Generated-by: Codex
Require the new Runtime Host generation's connection snapshot before Models becomes interactive, while preserving the last-ready rows during revalidation. Cover the catalog-ready/connections-pending race in Storybook. Generated-by: Codex
cbace2e to
26af71b
Compare
|
@jackwener The Models/connections generation finding is fixed on exact head 26af71b and the inline thread now has the reproduction/validation evidence. The PR is rebased and GitHub reports it mergeable. The fork CI is waiting for maintainer approval: https://github.com/apache/maka/actions/runs/32684251884. Could you approve that workflow when convenient? — Codex, posting on behalf of @chihumyum |
There was a problem hiding this comment.
Incremental re-review at exact head 26af71b73a6316cfbd21de0adb405cf73d3043f1 (previous approval on 34c52eb2; this pass covers only the three new commits: f1dd6f1d OAuth state, ce8ffe85 Copilot reimport, 26af71b7 Models fencing).
The P1 is closed, and here is the lifecycle answer. GitHubCopilotReloginNotice now mounts inside RuntimeHostSettingsGenerationBoundary (provider-connection-detail.tsx), which is a Fragment keyed by the generation key (runtime-host-settings-target.tsx). A same-key Host replacement changes the key, so the controller remounts: useActionGuard (connectGuard) and useMountedRef (mountedRef) are per-instance hooks and are therefore rebuilt per generation — they no longer survive across generations. The late-callback half is covered too: after connectExistingLogin resolves, if (!mountedRef.current) return; drops a completion belonging to the retired generation. The new story ModelsCopilotReimportHostGenerationRevalidation exercises exactly this: import in flight → same-key replacement → guard re-armed, old resolve produces no credential refresh, new import works. The sibling OAuthReloginNotice and the OAuthLoginPanel received the same boundary wrap.
No dual authorities in the three new retirement paths. OAuth card snapshots reset and refetch on generation change (useOAuthCards effect keyed on useRuntimeHostSettingsGenerationKey()); the pending browser authorization is torn down via teardownPendingAuthorization (the new unit test runtime-host-settings-generation.test.ts proves only Host-owned controllers unmount while route/scroll/focus state survives); Models joins the connection-verified fence (settings-surface.tsx adds models to sectionNeedsConnections, and the settingsRuntimeHostInteractionBoundary goes inert until the new generation verifies — pinned by ModelsConnectionsHostGenerationRevalidation). One authority per state, each retired with its generation.
Executed evidence (real Windows x64, exact head): desktop main rebuild clean; runtime-host-settings-generation unit test passes (retires only Host-owned controllers, cancels the pending authorization id).
Gate: test completed/success on the exact head (this diff touches no manifest/sandbox paths, so nothing else is owed); head re-polled at review time, unchanged, MERGEABLE.
No new findings in the incremental surface.
简体中文
增量复审:P1 已闭合——GitHubCopilotReloginNotice 现在挂在 generation boundary 里,换代即重挂,connectGuard/mountedRef 随实例重建;迟到的成功回调由 mountedRef 闸丢弃;新增 story 把这条路径完整演了一遍。三条新退场路径均无双权威(OAuth 快照按代重置重取、进行中的授权按代取消、Models 进连接验证围栏)。本机真 Windows 重建 + 新增单测通过。增量面无新 finding。
|
LGTM — merging. Thanks for the long back-and-forth on this one; the Settings surface staying put across a Runtime Host refresh is a real improvement. 中文看过了,合并。感谢反复打磨——Runtime Host 刷新时设置界面不再跳,这个改动值。 |
The Host now owns Copilot enrollment, so the connection detail offers the device sign-in and the catalog panel keeps the local import. The generation story still clicked a `重新导入` button that this branch removed, and upstream apache#3450 could not see the collision: the story and the panel changed on different sides of the merge. Drive the surface that exists. The property under test is unchanged — a same-key Host replacement retires the action guard without discarding the detail route, and the previous generation's late success must not report into the new one — so the story holds the first `completeAuthorization` unsettled across the replacement instead of the first import. Generated-by: Claude Code
Summary
ProvidersPanelconsumes it, and visibly mute cached Host pages while their authority fence is active.Behaviour
Before fixing, the yellow banner flashes each time the settings page is opened.
before.mp4
After fixing, no yellow banner was ever shown.
after.mp4
Verification
npm run lintnpm run format:checknpm run buildnpm run typechecknpm --workspace @maka/desktop test— 1118/1118 passednpm --workspace @maka/desktop run build-storybooknpm --workspace @maka/desktop run smoke:storybook— 172/172 passednpx knip --workspace apps/desktopnpx knip --workspace packages/uinpm run astryx:surface-inventory— 191 files, 1 documented exclusionnpm --workspace @maka/desktop run e2e— 41 passed, 1 skippedReview focus
AI use
Select exactly one:
Tool(s) and scope: Codex — implementation, race analysis, unit/Storybook/Electron
E2E coverage, regression verification, and pull request preparation. The human
contributor reviewed the contribution and accepts responsibility for it.
Checklist
Does this PR entail a change in behavior?