Skip to content

fix(editor): keep an unresolvable mirrored file tab open with a truthful terminal state - #21375

Merged
nwparker merged 6 commits into
mainfrom
fix/21041-mirrored-file-tab-terminal-state
Sep 18, 2026
Merged

nwparker merged 6 commits into
mainfrom
fix/21041-mirrored-file-tab-terminal-state

Conversation

@nwparker

@nwparker nwparker commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 3 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​366 0 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​366
Prod 8 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​112 $\color{#cf222e}{\Huge{\mathbf{−}}}$​18 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​94

ELI5

When a remote-session markdown tab could not be read because the host said selector_not_found, the tab sat on that raw code forever, and the reverted #21363 went the other way and closed it — throwing away unsaved edits. Now the tab retries a few times, then says plainly that the host could not find the file's workspace, with Retry; closing stays with the tab strip's own close control. It never closes on its own.

What this PR delivers is deliberately small: a truthful, localized terminal message instead of a raw error string, and an end to pointless retries. It adds no close path and does not change how the host nominates or mirrors tabs, so the issue's three reported symptoms (tab click switching to the broken .md, a new terminal switching to it, tabs flashing open/closed) are not addressed here — see Notes.

What Changed

  • useEditorPanelFileLoadRetry: after the existing bounded backoff (250/1000/2500 ms), a selector_not_found read is replaced with a truthful terminal message (WORKTREE_HOST_UNRESOLVED_ERROR) instead of staying on the raw code. Same shape as the owner-not-ready → unreachable transition from [Bug]: Remote host attempting to view a file results in error #6648. No close path is added.
  • FileContent.loadErrorCode preserves RuntimeRpcCallError.code; the terminal transition classifies via the shared hasRuntimeRpcErrorCode matcher, so { code: 'selector_not_found', message: 'Selector not found' } reaches the same state as the bare token.
  • The terminal state's comparison key is a client sentinel (WORKTREE_HOST_UNRESOLVED_CODE on loadErrorCode); EditorFileLoadErrorView localizes the copy by that code (editor.fileLoad.hostUnresolved), so translating the message can never break the terminal check.
  • No Close action in the error view (an earlier revision added one and it was removed): it would have had to reimplement the tab strip's pin, shared-reference, and unsaved-changes semantics, had no listener under floating editor panels, and on the conflict-review overview could target an unrelated open tab sharing the inline row's absolute-path id. Closing stays with the tab strip / Cmd+W.

Why

selector_not_found is the host resolver's "could not place this workspace right now". It covers a deleted worktree and a cold or failing scan, and the codebase already documents it as UNKNOWN in two places (remote-browser-stream-errors.ts, web-runtime-session-tab-lifecycle.ts). For git worktrees, the file-read path (files.read → resolveRuntimeFileTarget → resolveWorktreeSelector) has no definitive absence code: tab_not_found / terminal_tab_not_found are only emitted by the session-tab methods, which short-circuit on the explicit id: selector and never consult the resolver — which is exactly why the host keeps publishing (and re-nominating as active) a markdown tab whose file read cannot resolve. Without positive absence evidence the safe direction is keep, never close (docs/reference/ssh-execution-boundary.md), and closeFile deletes editorDrafts with no dirty check, so an automatic close is a data-loss path.

Linked Issue

Refs #21041 — narrows the symptom (raw selector_not_found text with endless retries) but does not resolve the issue's reported tab-switching or flashing; do not auto-close.

Visual Proof

N/A — error-state copy and a secondary button inside the existing load-error card; no new layout.

Testing

  • pnpm test src/renderer/src/components/editor/useEditorPanelFileLoadRetry.test.tsx src/renderer/src/components/editor/useEditorPanelHostUnresolvedContentState.test.tsx src/renderer/src/components/editor/EditorFileLoadErrorView.test.tsx — 9 passed
  • Neighbouring editor suites (useEditorPanelContentState, useEditorPanelVisibilityContentState, EditorContent, EditorContent.markdown-classification) — 50 passed
  • pnpm tc:web — clean
  • ORCA_CODE_QUALITY_BASE=origin/main node config/scripts/check-changed-code-quality.mjs — passed (0 findings)
  • pnpm verify:localization-catalog — passed
  • Mutation A (fix removed, only the constant export kept): 3 new tests fail (classification, hook terminal message, integration terminal message).
  • Mutation B (fix(editor): evict stale mirrored file tabs #21363-style closeFile eviction re-injected on top of the fix): the integration test fails on expect(closeFile).not.toHaveBeenCalled() (called 1×) and the hook test fails on the terminal message.

AI Disclosure

Review

Self-reviewed for correctness, data safety (no automatic close and no new close path of any kind — the tab strip remains the only one), SSH/remote behavior (keep-on-unknown), and cross-platform impact (renderer only).

Agent skill upstream boundary

  • Not applicable

Notes

selector_not_found is classified through the shared hasRuntimeRpcErrorCode matcher, which is case-exact but deliberately tolerates surrounding whitespace (so transport-wrapped "…: selector_not_found\n" classifies). Do not tighten or fork it here: since nothing destructive hangs off the match any more, an over-match costs a slightly-wrong message, never a lost draft. That cheap failure direction is intentional.

On the copy "Retry, or close the tab": for a host-mirrored tab, closing it via the tab strip also closes the host's copy (closeFile → notifyHostOfMirroredEditorClose). That is the pre-existing contract of every mirrored-tab close path and this PR adds no close path, so the message does not restate it; the same message also shows for non-mirrored files read through the runtime, where it would be wrong.

Not everything that escapes files.read is UNKNOWN. Two host codes are definitive and are uncovered here rather than misclassified: folder_workspace_path_missing:<abs path> (folder workspace whose directory is gone) and worktree_execution_host_unresolved (ambiguous host routing). They do not match the selector_not_found matcher, so those tabs still sit on their raw string after the retry budget. Classifying them into their own truthful terminal states is a follow-up, not this PR. Also note selector_not_found is thrown synchronously for an unregistered folder-workspace id or a removed repo, so the copy says "may not know about it yet" rather than claiming a scan is in progress.

Pre-existing gap, out of scope, flagged for a follow-up: buildMirroredEditorTabs (web-session-tabs-sync/tab-builders.ts) spreads the existing local OpenFile and then overwrites isDirty with the host snapshot's value, and the client does not forward local dirtiness to the host. A rebuild can therefore flip a locally-dirty mirrored file to isDirty: false while editorDrafts still holds the draft, and the tab strip's dirty gate (file.isDirty) would then close it without confirmation. This PR neither introduces nor closes that path (its own guarantee is that nothing here closes a tab, which mutation B proves), but it does mean the tab strip's dirty confirmation is not a reliable draft guard for exactly the mirrored tabs this message targets; the fix belongs in the rebuild (isDirty: existingFile?.isDirty === true || tab.isDirty, or gating on editorDrafts) and should get its own test.

Follow-up, not in this PR: the host could make this state genuinely resolvable — files.read could consult the session-tab store's known workspace path (a filesystem ENOENT is then a real absence signal the client already treats as terminal), or emit a definitive worktree-absence code only when the catalog positively lacks the worktree. Until one of those exists, auto-eviction on the file-read path cannot be made safe.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • No visual proof is needed for this error-state copy change
  • Self-reviewed for correctness, security, and performance
  • Cross-platform and SSH/remote impact considered
  • Full lint, build, and platform matrix are CI coverage

…ful terminal state

A host-mirrored file whose read keeps answering `selector_not_found` used
to sit on the raw code forever (and, in the reverted #21363, was closed
outright, discarding drafts). `selector_not_found` is the host's "could
not resolve right now", not proof the workspace is gone, and the file-read
path has no definitive absence code.

Bound the retries as before, then swap in a truthful terminal message
with Retry and Close tab. The tab is never closed automatically; Close
routes through the unsaved-changes queue so a dirty draft is confirmed.

Fixes #21041

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ℹ️ No critical issues — two rough edges inline, plus one open question about whether Close sticks.

Reviewed changes

  • Bounded selector_not_found retry ends in a truthful terminal state. useEditorPanelFileLoadRetry now swaps the raw resolver code for WORKTREE_HOST_UNRESOLVED_ERROR after the existing 250/1000/2500 backoff, and shouldRetryFileLoadError marks that message terminal. No close path is added, so the tab and its draft survive.
  • EditorFileLoadErrorView gains an optional Close tab action. A ghost button beside Retry, rendered only when the caller supplies onClose, with a new catalog entry.
  • All three error-view call sites wire onClose through requestEditorFileClose. That routes into the existing unsaved-changes close queue, so a dirty tab is confirmed before it closes.
  • New coverage. Component-level tests for the two button behaviors, hook-level tests for the classification and terminal transition, and an integration test through useEditorPanelContentState asserting no closeFile and a terminal message on repeated selector_not_found.

This is the right direction: selector_not_found is UNKNOWN rather than absence (remote-browser-stream-errors.ts, web-runtime-session-tab-lifecycle.ts), the file-read path has no definitive "gone" code, and closeFile deletes editorDrafts with no dirty check — so auto-eviction was the data-loss path the #21368 revert called out. Keeping the tab and offering a dirty-checked Close is the safe shape.

ℹ️ Manual Close may not stick for the same transient state

Close tab routes through requestEditorFileClose → closeFile → notifyHostOfMirroredEditorClose → closeWebRuntimeSessionTab({ reason: 'user' }). When the host answers selector_not_found, web-runtime-session-tab-lifecycle.ts treats it as a non-definitive failure and clears the close intent — only tab_not_found / terminal_tab_not_found are made durable (#21277), and web-runtime-session-tab-activate-close.test.ts pins that selector_not_found suppression expires. If the host republishes its copy, the mirror can reappear after the close-intent TTL. Since this PR makes Close the user's only way out of the unresolved state, it's worth confirming that closing is expected to stick here, or noting it as a known follow-up.

Technical details
# Manual close durability for selector_not_found

## Affected sites
- `src/renderer/src/runtime/web-runtime-session-tab-lifecycle.ts:171-180` — only
  `tab_not_found` / `terminal_tab_not_found` become durable; every other failure
  calls `clearWebSessionCloseIntent`.
- `src/renderer/src/runtime/web-runtime-session-tab-activate-close.test.ts:318-345` —
  pins that a `selector_not_found` close intent does not survive the TTL.
- `src/renderer/src/runtime/close-mirrored-editor-tab.ts:43-58` — records the intent
  synchronously, then fires `closeWebRuntimeSessionTab`.

## Open questions for the human
- Is a manual close of a mirrored file tab expected to be durable across a
  `selector_not_found` host answer, or is that deliberately left to the
  host-resolvable follow-up in the PR notes?

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Comment thread src/renderer/src/components/editor/useEditorPanelFileLoadRetry.ts Outdated
Comment thread src/renderer/src/components/editor/EditorConflictReviewSurface.tsx Outdated
@coderabbitai

coderabbitai Bot commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: ed56e068-b06b-4299-83fc-a0b67fb60ff8

📥 Commits

Reviewing files that changed from the base of the PR and between 4943c88 and 9458eb6.

📒 Files selected for processing (10)
  • src/renderer/src/components/editor/EditorConflictReviewSurface.tsx
  • src/renderer/src/components/editor/EditorContent.tsx
  • src/renderer/src/components/editor/EditorEditFileSurface.tsx
  • src/renderer/src/components/editor/EditorFileLoadErrorView.test.tsx
  • src/renderer/src/components/editor/EditorFileLoadErrorView.tsx
  • src/renderer/src/components/editor/editor-panel-content-types.ts
  • src/renderer/src/components/editor/useEditorPanelFileLoadRetry.test.tsx
  • src/renderer/src/components/editor/useEditorPanelFileLoadRetry.ts
  • src/renderer/src/components/editor/useEditorPanelHostUnresolvedContentState.test.tsx
  • src/renderer/src/i18n/locales/en.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/renderer/src/i18n/locales/en.json

Included review availability: Your plan provides up to 10 included reviews per hour; 2 remain after this review.


📝 Walkthrough

Walkthrough

The editor now preserves runtime error codes during file loading and uses them to bound selector_not_found retries. Exhausted retries show a localized host-unresolved error while preserving dirty mirrored tabs and drafts. EditorFileLoadErrorView now supports retry and optional tab-close actions. Editor surfaces pass error codes and close handlers as applicable. Tests cover classification, bounded retries, draft preservation, localization, and close actions.

Priority: ⬆️ High

Severity of issue fixed: High

Merge Risk: ⚪ Minimal · up to 9458e

The unresolved-workspace state now displays localized messaging and provides the intended retry and close actions. No actionable merge-blocking risk remains.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.77% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 13 functions across 10 files. (1 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The description includes the required sections, linked issue, testing details, and rationale. However, it states that no close path or Close action exists, while the changes add an onClose handler, a … Update the description to match the implemented Close tab behavior and the PR objectives. Explain its unsaved-changes handling and why inline conflict-review rows omit the action. Add the required before/after visual proof, or provide a pre…
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes satisfy the coding objective in issue #21041. useEditorPanelFileLoadRetry recognizes selector_not_found, performs bounded retries, and stores WORKTREE_HOST_UNRESOLVED_CODE after exha…
Out of Scope Changes check ✅ Passed The changes remain within issue #21041. Error-code propagation, localization, close-action wiring, conflict-row omission, and regression tests directly support the reported broken Markdown-tab behavio…
Title check ✅ Passed The title clearly summarizes the primary change: keeping an unresolvable mirrored file tab open and showing a truthful terminal state.
Full details: Docstring Coverage

Explanation

Docstring coverage is 30.77% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 13 functions across 10 files. (1 skipped: 1 unsupported.)

Full details: Description check

Explanation

The description includes the required sections, linked issue, testing details, and rationale. However, it states that no close path or Close action exists, while the changes add an onClose handler, a Close tab action, related localization, and tests. It also marks visual proof as not applicable despite adding a user interaction.

Resolution

Update the description to match the implemented Close tab behavior and the PR objectives. Explain its unsaved-changes handling and why inline conflict-review rows omit the action. Add the required before/after visual proof, or provide a precise justification that covers the new interaction.

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 2e781d63-6ef2-4637-a524-bba6666003d1

📥 Commits

Reviewing files that changed from the base of the PR and between b7d694f and 48e6028.

📒 Files selected for processing (10)
  • src/renderer/src/components/editor/EditorConflictReviewSurface.tsx
  • src/renderer/src/components/editor/EditorContent.tsx
  • src/renderer/src/components/editor/EditorEditFileSurface.tsx
  • src/renderer/src/components/editor/EditorFileLoadErrorView.test.tsx
  • src/renderer/src/components/editor/EditorFileLoadErrorView.tsx
  • src/renderer/src/components/editor/editor-panel-content-types.ts
  • src/renderer/src/components/editor/useEditorPanelFileLoadRetry.test.tsx
  • src/renderer/src/components/editor/useEditorPanelFileLoadRetry.ts
  • src/renderer/src/components/editor/useEditorPanelHostUnresolvedContentState.test.tsx
  • src/renderer/src/i18n/locales/en.json

Included review availability: Your plan provides up to 10 included reviews per hour; 2 remain after this review.

Comment on lines +36 to +37
export const WORKTREE_HOST_UNRESOLVED_ERROR =
"The host couldn't find this file's workspace. It may have been removed, or the host may still be scanning. Retry, or close the tab."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Localize the terminal workspace message.

This new user-facing message always renders in English. Add an intent-named localization ID. Translate the message in the error view when loadError equals WORKTREE_HOST_UNRESOLVED_ERROR.

Based on learnings, newly added user-facing TypeScript strings must use intent-named localization IDs.

Source: Learnings

Comment thread src/renderer/src/components/editor/useEditorPanelFileLoadRetry.ts Outdated
Preserve `RuntimeRpcCallError.code` on `FileContent.loadErrorCode` and gate
the host-unresolved terminal transition with `hasRuntimeRpcErrorCode`, so a
host that sends `{ code: 'selector_not_found', message: 'Selector not found' }`
reaches the same truthful state as one that puts the bare token on the
message. Also drop the Close action on inline conflict-review rows, which
are not open tabs and would have been a dead control.
…he token matcher

Separate the terminal state's comparison key from its display text: the
retry hook stores `WORKTREE_HOST_UNRESOLVED_CODE` on `loadErrorCode`, and
the error view localizes by that code (`editor.fileLoad.hostUnresolved`),
so translating the message can never break the terminal check. Export the
selector_not_found matcher and cover near misses (case, suffix, prose,
wrong code) so only the defined token classifies.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

Reviewed the delta since the prior pullfrog review (48e6028), which lands 4943c88.

  • Classified selector_not_found by machine code, not message text. FileContent gained an optional loadErrorCode; the file-content loader preserves RuntimeRpcCallError.code, and isHostSelectorNotFoundError now matches through the shared hasRuntimeRpcErrorCode matcher over {code, message}. This closes the exact-message-compare gap where a host sending {code:'selector_not_found', message:'Selector not found'} would have stayed on raw text.
  • Gated the conflict-review Close tab on the file being a real open tab. EditorConflictReviewSurface now passes onClose only when openFiles contains contentFile.id, so synthesized inline overview rows no longer render a dead control. Verified that real conflict-review files are added to openFiles with id === absolutePath, so genuine tabs still get Close.
  • Extended coverage to both host shapes. The retry test now parameterizes the bare-code-as-message and code-with-prose-message shapes, and the integration test rejects with a real RuntimeRpcCallError and asserts both loadError and loadErrorCode.

Both prior inline threads are addressed and resolved. The manual-close durability open question remains a documented follow-up, not a blocker. Focused editor tests (10), neighbouring suites (27), and pnpm tc:web all pass locally.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ℹ️ No critical issues — one minor doc nit inline.

Reviewed changes

Reviewed the delta since the prior pullfrog review (4943c88), which lands 4208583.

  • Keyed the terminal state and its display copy on a client sentinel. Added WORKTREE_HOST_UNRESOLVED_CODE ('worktree_host_unresolved'), stored on loadErrorCode at the terminal transition; shouldRetryFileLoadError now tests that code via a new optional code parameter rather than the English message, so a localized or reworded message stays terminal.
  • Localized the host-unresolved copy in the error view. EditorFileLoadErrorView takes an optional code and renders the translated editor.fileLoad.hostUnresolved string when it matches the sentinel; all three call sites pass fileContent.loadErrorCode.
  • Pinned the token matcher. Exported isHostSelectorNotFoundError and added tests for the exact token plus near-misses (case variants, suffixes, an embedded token), and asserted the sentinel lands on loadErrorCode in both hook and integration tests.

Verified locally: focused and repro suites (17 tests) pass, pnpm tc:web is clean, and pnpm verify:localization-catalog passes.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

mimeType?: string
fileIdentity?: string
loadError?: string
/** Machine code from a runtime RPC failure; `loadError` alone may be prose (#21041). */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This field now holds both host RPC codes and the client WORKTREE_HOST_UNRESOLVED_CODE sentinel written by the retry hook, so the "from a runtime RPC failure" clause no longer covers everything a reader will find here. Worth widening the comment so a future classifier does not assume every value is a host code.

Suggested change
/** Machine code from a runtime RPC failure; `loadError` alone may be prose (#21041). */
/** Host RPC code, or the client `WORKTREE_HOST_UNRESOLVED_CODE` sentinel; `loadError` alone may be prose (#21041). */

… tab strip

The Close button routed through `requestEditorFileClose`, which skips the
pinned-tab and shared-reference checks the tab strip applies, has no
listener outside the Terminal workbench (floating editor panels), and on
the conflict-review overview could target an unrelated open tab whose id
is the same absolute path as a synthesized inline row. Rather than
reimplement the tab strip's close semantics in a second place, the error
view keeps Retry and its copy points the user at closing the tab.
The copy no longer points at a Close control that is gone ("close this
tab from the tab strip") and no longer claims a scan is in progress, since
`selector_not_found` is also thrown synchronously for unregistered folder
workspaces and removed repos. The sentinel becomes
`editor_host_workspace_unresolved` so it cannot be confused with the CLI's
`worktree_host_unresolved` client error. The doc comment narrows the
"no definitive absence code" claim to git worktrees and names the two host
codes that are definitive but not yet classified.

Refs #21041

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ℹ️ No new issues in the new commits — the delta removes the in-view Close cleanly. One prior doc nit remains open inline, so this review stays non-approving.

Reviewed changes

Reviewed the delta since the prior pullfrog review (4208583) and its test-only follow-up (9458eb6), which lands 9229b8b.

  • Removed the Close tab action from EditorFileLoadErrorView. The view is now Retry-only; closing stays with the tab strip / Cmd+W, whose path carries the pin, shared-reference, and unsaved-changes semantics the inline control would have had to reimplement.
  • Dropped the onClose wiring and imports from all three call sites (EditorContent, EditorEditFileSurface, EditorConflictReviewSurface), which also removes the conflict-surface openFiles gating that existed only to suppress a dead control.
  • Retired the now-unused Close tab catalog entry (296b59cd29) and rewrote the component test to pin Retry as the sole action.
  • Verified locally: focused editor suites (11 tests) pass, pnpm tc:web and pnpm verify:localization-catalog pass, and oxlint reports 0 findings on the five touched files. No dangling onClose / requestEditorFileClose references remain in the editor error path.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

mimeType?: string
fileIdentity?: string
loadError?: string
/** Machine code from a runtime RPC failure; `loadError` alone may be prose (#21041). */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is the still-open doc nit from the prior review: the field now holds host RPC codes and the client WORKTREE_HOST_UNRESOLVED_CODE sentinel written by the retry hook, so "from a runtime RPC failure" no longer covers every value a reader will find here. Widening it keeps a future classifier from assuming every value is a host code.

Suggested change
/** Machine code from a runtime RPC failure; `loadError` alone may be prose (#21041). */
/** Host RPC code, or the client `WORKTREE_HOST_UNRESOLVED_CODE` sentinel; `loadError` alone may be prose (#21041). */

@nwparker
nwparker merged commit 2cc34de into main Sep 18, 2026
27 checks passed
Jinwoo-H pushed a commit that referenced this pull request Sep 19, 2026
…ful terminal state (#21375)

* fix(editor): keep an unresolvable mirrored file tab open with a truthful terminal state

A host-mirrored file whose read keeps answering `selector_not_found` used
to sit on the raw code forever (and, in the reverted #21363, was closed
outright, discarding drafts). `selector_not_found` is the host's "could
not resolve right now", not proof the workspace is gone, and the file-read
path has no definitive absence code.

Bound the retries as before, then swap in a truthful terminal message
with Retry and Close tab. The tab is never closed automatically; Close
routes through the unsaved-changes queue so a dirty draft is confirmed.

Fixes #21041

* fix(editor): classify selector_not_found by RPC code, not message text

Preserve `RuntimeRpcCallError.code` on `FileContent.loadErrorCode` and gate
the host-unresolved terminal transition with `hasRuntimeRpcErrorCode`, so a
host that sends `{ code: 'selector_not_found', message: 'Selector not found' }`
reaches the same truthful state as one that puts the bare token on the
message. Also drop the Close action on inline conflict-review rows, which
are not open tabs and would have been a dead control.

* fix(editor): localize the host-unresolved copy by sentinel, and pin the token matcher

Separate the terminal state's comparison key from its display text: the
retry hook stores `WORKTREE_HOST_UNRESOLVED_CODE` on `loadErrorCode`, and
the error view localizes by that code (`editor.fileLoad.hostUnresolved`),
so translating the message can never break the terminal check. Export the
selector_not_found matcher and cover near misses (case, suffix, prose,
wrong code) so only the defined token classifies.

* test(editor): name the it.each parameter for the host answer it labels

* fix(editor): drop the load-error Close action; closing stays with the tab strip

The Close button routed through `requestEditorFileClose`, which skips the
pinned-tab and shared-reference checks the tab strip applies, has no
listener outside the Terminal workbench (floating editor panels), and on
the conflict-review overview could target an unrelated open tab whose id
is the same absolute path as a synthesized inline row. Rather than
reimplement the tab strip's close semantics in a second place, the error
view keeps Retry and its copy points the user at closing the tab.

* fix(editor): reword the host-unresolved copy and namespace its sentinel

The copy no longer points at a Close control that is gone ("close this
tab from the tab strip") and no longer claims a scan is in progress, since
`selector_not_found` is also thrown synchronously for unregistered folder
workspaces and removed repos. The sentinel becomes
`editor_host_workspace_unresolved` so it cannot be confused with the CLI's
`worktree_host_unresolved` client error. The doc comment narrows the
"no definitive absence code" claim to git worktrees and names the two host
codes that are definitive but not yet classified.

Refs #21041

(cherry picked from commit 2cc34de)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant