fix(tui): resolve terminal theme mode from every signal instead of guessing dark - #1152
fix(tui): resolve terminal theme mode from every signal instead of guessing dark#1152sahrizvi wants to merge 4 commits into
Conversation
…ssing dark Third attempt at the same defect (#617 → #704 → #736): code rendered in near-white on a light terminal, readable in VS Code but not in the terminal itself. The first two fixes adjusted colour values, which is why neither held. The actual defect is in `app.tsx`, where the mode-resolution chain ended in a hardcoded fallback: const envMode = detectModeFromCOLORFGBG(process.env.COLORFGBG) const mode = envMode === "light" ? "light" : ((await renderer.waitForThemeMode(1000)) ?? "dark") Apple Terminal — the client named in #736's metadata, alongside "macOS Appearance: Light" — sets no `COLORFGBG` and does not reliably answer the OSC 11 background query. Both signals are therefore absent, the chain returns "dark", and a light-background user gets the dark palette no matter how its colours are tuned. That is not a palette bug, so palette fixes could not close it. Changes: - `resolveInitialMode()` encodes the whole chain as one pure function, ordered by how well each signal describes *this terminal window*: COLORFGBG, then the OSC 11 reply, then OS appearance, then dark as a genuine last resort. A dark-profile terminal under a light system theme stays dark. - `detectSystemAppearance()` adds the signal that was missing. macOS sets `AppleInterfaceStyle` to "Dark" in dark mode and leaves it *unset* in light mode, so `defaults` exiting non-zero is the light answer rather than a failure; only ENOENT or a timeout is treated as "unknown". Every report of this bug came from darwin. - The call site now honours a dark `COLORFGBG` too. It previously kept only "light", so a terminal that had already reported a dark background still paid the full one-second OSC timeout before agreeing with it. `detectModeFromCOLORFGBG` carried a comment saying it was "extracted from app.tsx for direct test coverage (#704)" but had no tests at all. It does now. Verified by mutation rather than by the suite going green: restoring the old hardcoded fallback fails the test named for #736, discarding a dark COLORFGBG fails the precedence test, and letting OS appearance outrank the terminal's own background fails two more. Scope note: this closes the colour-mode family. #404 (garbled ASCII logo), #609 (malformed layout) and #737 (unexpected CJK glyphs) were grouped with it during triage, but they are glyph-width and encoding problems rather than colour, and need separate work. #809 (dark text on a dark box) is plausibly the same misdetection, but the report carries no terminal details, so it is referenced rather than closed. Tests: 13 new, 280 tui pass (1 pre-existing failure unrelated, identical on main). Closes #736 Refs #809 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… fallback assuming dark Reworked after a codex audit of the whole colour-legibility class, which found the first attempt was aimed at the less important of two layers and that several of its tests proved less than their names claimed. ## Direct-run renderer (the layer the audit ranked higher) `resolveRunTheme` returned a hardcoded dark `RUN_THEME_FALLBACK` on both failure exits, so a light terminal whose palette query failed got dark panels. Worse, that fallback's `text` prefers the terminal's *own* default foreground, which on a light terminal resolves to black — a black foreground over a hardcoded `#0f172a` panel is literally dark text in a dark box, the symptom reported in #809. The fallback is now built per mode and memoized, and both exits resolve a mode first. The dark instance is still the same object, so callers comparing it by identity are unaffected. ## Startup detection - Precedence corrected. OSC 11 describes *this* window right now; `COLORFGBG` is inherited and survives ssh, tmux, sudo and profile changes. The previous ordering let a stale env var override a live answer. COLORFGBG now only shortens the OSC wait (250ms instead of 1s), which keeps #704's startup win without trading away correctness. - The appearance probe no longer reports "light" for every failure. macOS leaves `AppleInterfaceStyle` unset in light mode and `defaults` says so explicitly; that diagnostic is the light answer, while EACCES, EMFILE, ENOENT, a signal or a timeout mean unknown. Guessing light on those produces the inverse of the bug being fixed. - It invokes `/usr/bin/defaults`, so a different `defaults` earlier on PATH cannot answer a question about macOS appearance. - It does not run over ssh, where the appearance belongs to the remote host rather than the terminal the user is looking at, nor in CI. ## Tests The audit named six tests that overclaimed. `execFile` is now injectable and the probe tests use a spy, so "does not spawn" is asserted rather than assumed, and every failure branch is driven directly. The regression test is renamed for the shape it actually covers instead of implying end-to-end coverage it does not have. Seven mutants were confirmed to fail: old precedence, missing ssh guard, missing CI guard, relative `defaults`, any-failure-means-light, always-dark direct-run fallback, and a light fallback whose panel is still dark. ## Scope Claims only what the code supports. The audit found #617 was missing Markdown `fg` and code-block background (fixed separately in 5ae5b79), #704 bundled three changes with no way to attribute the fix, and #404/#609/#737/#116 are glyph-width, encoding or layout problems rather than colour. #736 is the best-supported colour-mode case but remains conditional, so it is referenced, not closed. Tests: 17 detection, 9 direct-run theme, 284 tui, 187 cli/run. Typecheck clean. Refs #736 Refs #809 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.
Tip: disable this comment in your organization's Code Review settings.
|
Thanks for your contribution! This PR doesn't have a linked issue. All PRs must reference an existing issue. Please:
See CONTRIBUTING.md for details. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
Included review availability: Your plan provides up to 4 included reviews per hour; 3 remain after this review. 📝 WalkthroughWalkthroughThe TUI now detects terminal mode from OSC responses, ChangesTerminal theme detection
Estimated code review effort: 3 (Moderate) | ~25 minutes Merge Risk: 🟡 Moderate · up to Direct-run rendering can still select a dark fallback on light macOS terminals when terminal signals are unavailable, leaving text difficult to read and preserving the bug this PR is intended to fix. The fallback path needs correction or explicit owner acceptance before merge. Sequence Diagram(s)sequenceDiagram
participant TUI
participant Terminal
participant SystemAppearance
participant ThemeResolver
TUI->>Terminal: Query OSC 11 and read COLORFGBG
TUI->>SystemAppearance: Query appearance if terminal signals are absent
SystemAppearance-->>TUI: Return light, dark, or unknown
TUI->>ThemeResolver: Resolve initial mode
ThemeResolver-->>TUI: Return mode-specific fallback theme
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Linked Issues checkExplanation The changes address the linked issue [ Full details: Out of Scope Changes checkExplanation The theme-detection and direct-run fallback changes are within the scope of [ Full details: Description checkExplanation The description follows the repository template. It identifies the issue, marks the change as a bug fix, explains the implementation, documents verification results, addresses screenshots, and completes the checklist.
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
| * This is the signal that was missing: every report of this bug came from | ||
| * darwin, on a terminal that answers neither of the cheaper probes. | ||
| */ | ||
| /** Minimal shape of `child_process.execFile`, injectable so tests can drive every branch. */ |
There was a problem hiding this comment.
SUGGESTION: The detectSystemAppearance doc comment (lines 61-68) is now orphaned — inserting ExecFileLike here detaches it from the function it documents.
The macOS-specific explanation (AppleInterfaceStyle set to "Dark" in dark mode and absent in light mode, non-zero exit = light) is meant to describe detectSystemAppearance, but it now sits directly above the ExecFileLike type. Move that doc comment down to immediately precede detectSystemAppearance (or fold it into that function's JSDoc), so the two descriptions stop pointing at the wrong declarations.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| // silently reported as light. | ||
| const err = error as NodeJS.ErrnoException & { killed?: boolean; status?: number | null; stderr?: string } | ||
| const notFound = /does not exist/i.test(String(err.stderr ?? err.message ?? "")) | ||
| const clean = err.code === undefined && err.killed !== true |
There was a problem hiding this comment.
SUGGESTION: The clean clause is dead code that contradicts the contract stated in the comment above it.
For a real execFile error, code is always populated: the errno string on spawn failure (ENOENT/EACCES/EMFILE/...), the numeric exit code on a non-zero exit, or null when killed is true. So err.code === undefined never holds, and this clause never fires. If it ever did fire (e.g. a future error shape without code), it would silently report "light" for an unknown failure — exactly what the comment says must not happen. If the intent is to treat a clean non-zero exit (missing key) as light, match the exit status (typeof err.code === "number" / err.status === 1) instead; otherwise remove the clause.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| * the direct-run and scrollback renderer. | ||
| */ | ||
| function fallbackMode(renderer: CliRenderer): "dark" | "light" { | ||
| return resolveInitialMode({ |
There was a problem hiding this comment.
SUGGESTION: fallbackMode omits the OS-appearance signal, so the direct-run renderer does not actually "agree" with the TUI startup path it shares resolveInitialMode with.
The startup path feeds appearance from detectSystemAppearance() into resolveInitialMode, but this fallback only passes COLORFGBG and themeMode. On a light Apple Terminal (no COLORFGBG, no OSC 11 reply) a failed palette query still resolves to "dark" and reproduces the dark-on-dark symptom (#809) this PR targets. Consider threading appearance through here too (which would require making this path async), or note the limitation explicitly.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
Code Review SummaryStatus: 3 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)SUGGESTION
Files Reviewed (6 files)
Fix these issues in Kilo Cloud Previous Review Summary (commit 0306bf0)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 0306bf0)Status: 3 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)SUGGESTION
Files Reviewed (6 files)
Reviewed by deepseek-v4-pro · Input: 51.5K · Output: 12.7K · Cached: 439K Review guidance: REVIEW.md from base branch |
There was a problem hiding this comment.
5 issues found across 6 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/tui/src/app.tsx">
<violation number="1" location="packages/tui/src/app.tsx:278">
P2: When a valid but stale `COLORFGBG` is present and OSC 11 takes longer than 250 ms, the live terminal answer is discarded and the stale value determines the theme. Keep the OSC probe alive through the full response deadline before falling back to `COLORFGBG`.</violation>
</file>
<file name="packages/opencode/src/cli/cmd/run/theme.ts">
<violation number="1" location="packages/opencode/src/cli/cmd/run/theme.ts:696">
P1: When direct-run palette detection fails on a light macOS terminal with no OSC or `COLORFGBG`, `fallbackMode` still returns dark because it omits the macOS appearance probe. Run `detectSystemAppearance()` when the terminal signals are unavailable before resolving the fallback mode.</violation>
<violation number="2" location="packages/opencode/src/cli/cmd/run/theme.ts:696">
P2: fallbackMode never supplies the `appearance` signal to resolveInitialMode, so on the exact reported scenario — macOS Apple Terminal with no COLORFGBG and no OSC 11 reply (renderer.themeMode stays null) — the direct-run/scrollback fallback still resolves to `"dark"` and repaints a light panel dark, which is the #809 symptom this PR sets out to fix. resolveInitialMode explicitly supports `appearance` (and detectSystemAppearance exists for it); only the dark last-resort stays. Note detectSystemAppearance is async (Promise), so wiring it in requires awaiting it in the fallback path rather than calling fallbackMode synchronously.</violation>
<violation number="3" location="packages/opencode/src/cli/cmd/run/theme.ts:709">
P2: When a runtime palette refresh fails in light mode, this returns a distinct light fallback that `footer.ts` does not recognize as a fallback. The footer then replaces the last known-good theme; preserve the existing theme for either per-mode fallback.</violation>
</file>
<file name="packages/tui/src/terminal-detection.ts">
<violation number="1" location="packages/tui/src/terminal-detection.ts:112">
P3: The `clean` branch resolves any error without a `code` and without `killed` to `"light"`, which contradicts the comment directly above it ("must not be silently reported as light"). Only the missing-AppleInterfaceStyle diagnostic should be treated as light; dropping the `clean` fallback keeps unknown errors as `null` and avoids ever guessing light on an unknown dark terminal (the inverse of the bug this PR fixes).</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| * the direct-run and scrollback renderer. | ||
| */ | ||
| function fallbackMode(renderer: CliRenderer): "dark" | "light" { | ||
| return resolveInitialMode({ |
There was a problem hiding this comment.
P1: When direct-run palette detection fails on a light macOS terminal with no OSC or COLORFGBG, fallbackMode still returns dark because it omits the macOS appearance probe. Run detectSystemAppearance() when the terminal signals are unavailable before resolving the fallback mode.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/cli/cmd/run/theme.ts, line 696:
<comment>When direct-run palette detection fails on a light macOS terminal with no OSC or `COLORFGBG`, `fallbackMode` still returns dark because it omits the macOS appearance probe. Run `detectSystemAppearance()` when the terminal signals are unavailable before resolving the fallback mode.</comment>
<file context>
@@ -651,6 +676,27 @@ export const RUN_THEME_FALLBACK: RunTheme = {
+ * the direct-run and scrollback renderer.
+ */
+function fallbackMode(renderer: CliRenderer): "dark" | "light" {
+ return resolveInitialMode({
+ colorfgbg: process.env["COLORFGBG"],
+ osc: renderer.themeMode ?? null,
</file context>
| // now. COLORFGBG only buys a shorter wait: with a usable hint in hand we | ||
| // can stop waiting sooner, which keeps #704's startup win without | ||
| // letting a stale env var override a live answer. | ||
| const oscMode = (await renderer.waitForThemeMode(envMode ? 250 : 1000)) ?? null |
There was a problem hiding this comment.
P2: When a valid but stale COLORFGBG is present and OSC 11 takes longer than 250 ms, the live terminal answer is discarded and the stale value determines the theme. Keep the OSC probe alive through the full response deadline before falling back to COLORFGBG.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/tui/src/app.tsx, line 278:
<comment>When a valid but stale `COLORFGBG` is present and OSC 11 takes longer than 250 ms, the live terminal answer is discarded and the stale value determines the theme. Keep the OSC probe alive through the full response deadline before falling back to `COLORFGBG`.</comment>
<file context>
@@ -265,9 +265,19 @@ export const run = Effect.fn("Tui.run")(function* (input: TuiInput) {
+ // now. COLORFGBG only buys a shorter wait: with a usable hint in hand we
+ // can stop waiting sooner, which keeps #704's startup win without
+ // letting a stale env var override a live answer.
+ const oscMode = (await renderer.waitForThemeMode(envMode ? 250 : 1000)) ?? null
+ const appearance = oscMode || envMode ? null : await detectSystemAppearance()
+ const mode = resolveInitialMode({ colorfgbg: process.env.COLORFGBG, osc: oscMode, appearance })
</file context>
| const oscMode = (await renderer.waitForThemeMode(envMode ? 250 : 1000)) ?? null | |
| const oscMode = (await renderer.waitForThemeMode(1000)) ?? null |
| const bg = colors.defaultBackground ?? colors.palette[0] | ||
| if (!bg) { | ||
| return RUN_THEME_FALLBACK | ||
| return runThemeFallback(fallbackMode(renderer)) |
There was a problem hiding this comment.
P2: When a runtime palette refresh fails in light mode, this returns a distinct light fallback that footer.ts does not recognize as a fallback. The footer then replaces the last known-good theme; preserve the existing theme for either per-mode fallback.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/cli/cmd/run/theme.ts, line 709:
<comment>When a runtime palette refresh fails in light mode, this returns a distinct light fallback that `footer.ts` does not recognize as a fallback. The footer then replaces the last known-good theme; preserve the existing theme for either per-mode fallback.</comment>
<file context>
@@ -660,7 +706,7 @@ export async function resolveRunTheme(renderer: CliRenderer): Promise<RunTheme>
const bg = colors.defaultBackground ?? colors.palette[0]
if (!bg) {
- return RUN_THEME_FALLBACK
+ return runThemeFallback(fallbackMode(renderer))
}
</file context>
| * the direct-run and scrollback renderer. | ||
| */ | ||
| function fallbackMode(renderer: CliRenderer): "dark" | "light" { | ||
| return resolveInitialMode({ |
There was a problem hiding this comment.
P2: fallbackMode never supplies the appearance signal to resolveInitialMode, so on the exact reported scenario — macOS Apple Terminal with no COLORFGBG and no OSC 11 reply (renderer.themeMode stays null) — the direct-run/scrollback fallback still resolves to "dark" and repaints a light panel dark, which is the #809 symptom this PR sets out to fix. resolveInitialMode explicitly supports appearance (and detectSystemAppearance exists for it); only the dark last-resort stays. Note detectSystemAppearance is async (Promise), so wiring it in requires awaiting it in the fallback path rather than calling fallbackMode synchronously.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/cli/cmd/run/theme.ts, line 696:
<comment>fallbackMode never supplies the `appearance` signal to resolveInitialMode, so on the exact reported scenario — macOS Apple Terminal with no COLORFGBG and no OSC 11 reply (renderer.themeMode stays null) — the direct-run/scrollback fallback still resolves to `"dark"` and repaints a light panel dark, which is the #809 symptom this PR sets out to fix. resolveInitialMode explicitly supports `appearance` (and detectSystemAppearance exists for it); only the dark last-resort stays. Note detectSystemAppearance is async (Promise), so wiring it in requires awaiting it in the fallback path rather than calling fallbackMode synchronously.</comment>
<file context>
@@ -651,6 +676,27 @@ export const RUN_THEME_FALLBACK: RunTheme = {
+ * the direct-run and scrollback renderer.
+ */
+function fallbackMode(renderer: CliRenderer): "dark" | "light" {
+ return resolveInitialMode({
+ colorfgbg: process.env["COLORFGBG"],
+ osc: renderer.themeMode ?? null,
</file context>
| const clean = err.code === undefined && err.killed !== true | ||
| resolve(notFound || clean ? "light" : null) | ||
| }, |
There was a problem hiding this comment.
P3: The clean branch resolves any error without a code and without killed to "light", which contradicts the comment directly above it ("must not be silently reported as light"). Only the missing-AppleInterfaceStyle diagnostic should be treated as light; dropping the clean fallback keeps unknown errors as null and avoids ever guessing light on an unknown dark terminal (the inverse of the bug this PR fixes).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/tui/src/terminal-detection.ts, line 112:
<comment>The `clean` branch resolves any error without a `code` and without `killed` to `"light"`, which contradicts the comment directly above it ("must not be silently reported as light"). Only the missing-AppleInterfaceStyle diagnostic should be treated as light; dropping the `clean` fallback keeps unknown errors as `null` and avoids ever guessing light on an unknown dark terminal (the inverse of the bug this PR fixes).</comment>
<file context>
@@ -20,4 +22,99 @@ export function detectModeFromCOLORFGBG(value: string | undefined): "dark" | "li
+ // silently reported as light.
+ const err = error as NodeJS.ErrnoException & { killed?: boolean; status?: number | null; stderr?: string }
+ const notFound = /does not exist/i.test(String(err.stderr ?? err.message ?? ""))
+ const clean = err.code === undefined && err.killed !== true
+ resolve(notFound || clean ? "light" : null)
+ },
</file context>
| const clean = err.code === undefined && err.killed !== true | |
| resolve(notFound || clean ? "light" : null) | |
| }, | |
| resolve(notFound ? "light" : null) |
Marker Guard failed on #1152: theme.ts is an upstream-shared file, so custom code there must be fenced to survive an upstream merge overwriting it. The mode-aware fallback added in this branch was unmarked. Six regions are now fenced: the shared-resolver import, the per-mode seed, the memoized per-mode fallback theme, the mode probe, and both failure exits in resolveRunTheme. Verified with the same command CI runs: bun run script/upstream/analyze.ts --markers --base origin/main --strict Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
2 similar comments
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
Issue for this PR
Closes #736
Refs #809
Closesfor #736,Refsfor #809 — the difference is deliberate and explained in the scope note. Everything else this description mentions is named to put it out of scope, not to claim it.Type of change
What does this PR do?
The bug. Code output renders in near-white on a light terminal — readable when the session is opened in VS Code, not in the terminal itself. This is the third report of it: #617 → #704 → #736.
Why the previous two fixes did not hold. Both adjusted colour values. The defect is not a palette; it is that the mode-resolution chain in
app.tsxended in a hardcoded guess:Apple Terminal — named in #736's metadata next to "macOS Appearance: Light" — sets no
COLORFGBGand does not reliably answer the OSC 11 background query. With both signals absent the chain returns"dark", so a light-background user gets the dark palette no matter how well that palette is tuned. No colour change could close it.Two layers are fixed.
Startup detection.
resolveInitialMode()now encodes the whole chain as one pure function, ordered by how well each signal describes this terminal window: the OSC 11 reply, thenCOLORFGBG, then OS appearance, then dark as a genuine last resort. OSC outranksCOLORFGBGbecause the env var is inherited and survives ssh, tmux, sudo and profile changes;COLORFGBGnow only shortens the OSC wait to 250ms, which keeps the startup win from #704 without letting a stale value beat a live answer.detectSystemAppearance()supplies the signal that was missing — macOS leavesAppleInterfaceStyleunset in light mode, so adefaultsexit saying the key does not exist is the light answer, whileEACCES,ENOENT, a signal or a timeout mean unknown. It runs/usr/bin/defaultsso a stray binary onPATHcannot answer, and skips the probe entirely over ssh (where the appearance belongs to the remote host) and in CI.Direct-run renderer.
resolveRunThemereturned a hardcoded dark fallback on both failure exits, so a light terminal whose palette query failed got dark panels. That fallback'stextprefers the terminal's own default foreground, which on a light terminal resolves to black — black text over a hardcoded#0f172apanel is literally dark text in a dark box. The fallback is now built per mode and memoized; the dark instance is unchanged by identity, so callers comparing it withtoBeare unaffected.How did you verify your code works?
Mutation, not a green suite. Seven mutants were each confirmed to fail a test:
COLORFGBGoutranks OSC againdefaultsdefaultsfailure means lightexecFileis injected and the probe tests use a spy, so "does not spawn" is asserted rather than assumed.Suites: 288 tui, 187 cli/run, 9 direct-run theme, typecheck clean. The one failing tui test (
formats session continuation summary) fails identically onmain.Screenshots / recordings
Not a UI-layout change; the observable difference is text colour on a light terminal.
Checklist
Scope note. An audit of the whole colour-legibility area found the original grouping of these reports was wrong, so this claims only what the code supports:
fgand a missing code-block background, fixed separately in5ae5b79fcb. This change does nothing for it./usr/bin/defaults, no overriding user lock, and a theme that depends on terminal mode. The reporter's environment satisfies all four (darwin, Apple Terminal, macOS Appearance Light), and the mechanism is reproduced, so this closes it. If it recurs in an environment those conditions do not cover, that is a new report rather than this one reopening.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Tests