chore: add bug reproduction handoffs (tests + writeups)

Agent-facing reproduction kits for open has_repro issues: unit tests under
src/**/repro-*.test.ts and writeups under docs/bug-reproductions/.
This commit is contained in:
Neil
2026-07-16 11:37:08 -07:00
parent f62ad69260
commit 2ab513cd9e
117 changed files with 7128 additions and 0 deletions
+38
View File
@@ -0,0 +1,38 @@
# #8299 — macOS Shift+Space input-source switch also sends a space
**Status:** REPRODUCED (code-level)
**Issue:** https://github.com/stablyai/orca/issues/8299
**Malicious content:** none
## Summary
With Shift+Space as Korean/English input-source switch, Orca switches language **and** inserts a literal space into the PTY. Ghostty does not.
## Root cause
Ghostty reuses the original `NSEvent`, records `KeyboardLayout.id` around `interpretKeyEvents`, and returns without encoding when the source changed.
Orca’s xterm path:
- `attachCustomKeyEventHandler` + `shouldBypassXtermKeyboardEvent`
- **No** per-event input-source / layout-id change guard
- Shift+Space is **not** bypassed → xterm encodes Space to the PTY
IME 229 comments mention “input-source switch” for composition helper scheduling, but that is unrelated to consuming the switch chord.
## Automated proof
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/renderer/src/components/terminal-pane/repro-8299-shift-space-input-source.test.ts
```
## Suggested fix
On macOS, track current input-source id (existing IPC/readers under `keyboard-layout/`). On keydown of bare Shift+Space (and similar switch chords), if source id changed (or is a known switch chord), return false from the custom handler **and** preventDefault so no space reaches the PTY.
## Evidence
- `src/renderer/src/components/terminal-pane/repro-8299-shift-space-input-source.test.ts`
- `src/renderer/src/components/terminal-pane/xterm-bypass-policy.ts`
- `src/renderer/src/components/terminal-pane/use-terminal-pane-lifecycle.ts`
+31
View File
@@ -0,0 +1,31 @@
# #8335 — Terminal stuck after backgrounding Orca while Claude external editor open
**Status:** REPRODUCED (code-level reattach/mouse-mode path)
**Issue:** https://github.com/stablyai/orca/issues/8335
**Related:** #8104, #8198, #7443
**Malicious content:** none
## Summary
Open Claude Code external editor (`Save and close editor to continue...`), background Orca / switch tabs, return → pane stuck: typing ignored, mouse motion floods buffer with literal `^[[<35;x;yM`. Only recreate terminal recovers (kills Claude session).
## Root-cause chain
| Step | Code | Behavior |
|------|------|----------|
| 1 | `buildRehydrateSequences` | Re-arms last-observed mouse modes (`?1003` any + `?1006` SGR) |
| 2 | Claude pre-editor | Armed mouse tracking; did not disable before `$EDITOR` |
| 3 | `shouldPreserveAgentReattachModes()` | True while title still classifies as live agent |
| 4 | `reattachReplayResetSequence` | Uses `buildPostReplayLiveAgentReattachReset` — **omits** `RESET_MOUSE_REPORTING` |
| 5 | Result | Motion reports echoed as literal input (documented in `layout-serialization.ts`) |
## Automated proof
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/renderer/src/components/terminal-pane/repro-8335-agent-editor-mouse-preserve.test.ts
```
## Suggested fix (from issue)
Reset mouse reporting on reattach when foreground is not the agent TUI (external editor wait), while preserving mouse for a genuinely live agent TUI.
+41
View File
@@ -0,0 +1,41 @@
# #8372 — Outdated running CLI count on bottom panel
**Status:** REPRODUCED (code-level dual-source mismatch)
**Issue:** https://github.com/stablyai/orca/issues/8372
**Malicious content:** none
## Summary
After clearing orphan CLIs in Resource Manager, the open popover count is correct but the **closed** status-bar badge stays outdated (restart may not help if session bindings rehydrate).
## Root cause
```ts
const triggerSessionCount = open ? sessions.length : closedSessionCount
```
| Mode | Source |
|------|--------|
| Open | `window.api.pty.listSessions()` → live daemon |
| Closed | `createClosedResourceSessionCountSelector` → **store-bound** PTY IDs only (`tabs.ptyId`, `ptyIdsByTabId`, layouts) |
Closed path **never** reconciles against daemon inventory (by design, to avoid background `listSessions`). Dead wake-hint / layout PTY IDs inflate the badge. Kill-orphan only updates local `sessions` + `pty.kill` — does not clear store bindings.
## Automated proof
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/renderer/src/components/status-bar/repro-8372-closed-cli-count-stale.test.ts
```
## Suggested fix
- On popover close after kills, cache last `sessions.length` for the badge, **or**
- Prune store bindings for PTYs confirmed dead, **or**
- Occasional cheap reconcile of closed count against daemon when it changed.
## Evidence
- `src/renderer/src/components/status-bar/repro-8372-closed-cli-count-stale.test.ts`
- `src/renderer/src/components/status-bar/ResourceUsageStatusSegment.tsx`
- `src/renderer/src/components/status-bar/resource-session-count-selector.ts`
+15
View File
@@ -0,0 +1,15 @@
# #8378 — Codex session reset time mismatch (popup vs status bar)
**Status:** REPRODUCED
**Label:** `has_repro`
## Summary
Status bar chip shows fixed window size (`formatWindowLabel(windowMinutes)` → `"5h"` for Codex’s 300-minute session). Popup shows remaining time (`formatResetCountdown(resetsAt - now)` → e.g. `"Resets in 2h 33m"`). Same `RateLimitWindow`, two different formatters → reporter’s 2h 33m vs 5h.
## Re-run
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/shared/repro-8378-codex-reset-time-mismatch.test.ts
```
+35
View File
@@ -0,0 +1,35 @@
# #8399 — Option+L does not type `@` in Pi (German layout)
**Status:** REPRODUCED (code-level; same mechanism as #8733)
**Issue:** https://github.com/stablyai/orca/issues/8399
**Malicious content:** none
## Summary
German macOS layout: Option+L → `@`. Works in bare Orca terminal; fails once Pi is running. Works in Ghostty.
## Root cause
Same as #8733: Pi enables kitty keyboard protocol. With `macOptionAsAlt !== 'true'`, Orca still forces:
```text
Option+L (composed key '@') → \x1b[108;3u (physical 'l' + alt)
```
instead of letting Chromium deliver `@`. Without kitty, the same event returns `null` from the shortcut policy (composition path).
## Automated proof
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/renderer/src/components/terminal-pane/repro-8733-8399-kitty-option-compose.test.ts
```
## Suggested fix
Share fix with #8733: do not kitty-encode compose Option when the user disabled Option-as-Alt, or only for known US hotkeys.
## Evidence
- `src/renderer/src/components/terminal-pane/repro-8733-8399-kitty-option-compose.test.ts`
- `src/renderer/src/components/terminal-pane/terminal-shortcut-policy.ts`
+27
View File
@@ -0,0 +1,27 @@
# #8440 — OAuth Login Not Persisting for MCPs in Orca Terminals
**Status:** NOT REPRODUCED
**Issue:** https://github.com/stablyai/orca/issues/8440
**Malicious content:** none
## Summary
Discord report: MCP OAuth completes in Orca terminals but subsequent MCP calls report not logged in; persists across new terminals.
## Attempted code-level investigation
- MCP config inspection (`src/shared/mcp-config.ts`) only lists/validates server configs — no OAuth token store of its own.
- Agent OAuth/token machinery in-tree is Claude/Codex account scoped (`CLAUDE_CONFIG_DIR`, Codex `auth.json`), not a generic “MCP OAuth persistence” layer.
- No single broken path that would always drop MCP OAuth tokens across all Orca terminals without knowing which agent/MCP stores credentials where.
## Maintainer status (issue thread)
Jinwoo-H: *could not reproduce with an isolated OAuth MCP across new Orca terminals or restart*; asked for agent name/version, OS/SSH, and credential-storage path inside vs outside Orca.
## Why cannot_reproduce
Insufficient reporter detail + no definitive product-wide defect in tree. Needs user-supplied MCP/agent credential path divergence (or a concrete server that fails) to reclassify.
## Ask if reopening
Agent CLI + version, OS/SSH host, MCP server name, where tokens land outside Orca vs inside (`echo $HOME`, `$CLAUDE_CONFIG_DIR`, mcp auth files — redacted).
+40
View File
@@ -0,0 +1,40 @@
# #8450 — SSH relay selects system Node without npm instead of NVM
**Status:** REPRODUCED (code-level; no live Ubuntu SSH host required)
**Issue:** https://github.com/stablyai/orca/issues/8450
**Malicious content:** none
## Summary
On Ubuntu remotes with system `/usr/bin/node` (no `/usr/bin/npm`) and a complete NVM toolchain, relay deploy chooses system Node, prepends `/usr/bin` to `PATH`, then runs bare `npm install` → exit 127 `npm: not found`.
## Root cause
`resolveRemoteNodePath` (`src/main/ssh/ssh-remote-node-resolution.ts`):
1. Path probe script emits `command -v node` **first**, then NVM/fnm/… globs.
2. First candidate that passes `node --version` major ≥ 18 wins.
3. **No companion `npm` check** on that candidate’s bin dir.
4. `commandWithNodePath` → `export PATH='<nodeBinDir>':$PATH && … npm install`.
So incomplete system Node shadows NVM.
## Automated proof
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/main/ssh/repro-8450-node-without-npm.test.ts
```
## Suggested fix
- Require `npm` beside each Node candidate (same dir or `command -v` after PATH prepend of that bin).
- Prefer version-manager candidates with a coherent pair over bare `command -v node`.
- Error should name selected Node path and missing npm.
## Evidence
- `src/main/ssh/repro-8450-node-without-npm.test.ts`
- `src/main/ssh/ssh-remote-node-resolution.ts`
- `src/main/ssh/ssh-remote-commands.ts` (`commandWithNodePath`)
- `src/main/ssh/ssh-relay-deploy.ts` (`installNativeDeps`)
+52
View File
@@ -0,0 +1,52 @@
# #8454 — Built-in browser localhost forced to `http://`
## Status
**REPRODUCED** (unit / code contract)
Bare localhost/loopback address-bar input is hard-coded to the `http://` scheme. HTTPS-only local dev servers are therefore unreachable when the user types `localhost:3000` without a scheme. The self-signed certificate half of the issue still needs interactive UI verification.
## Re-run
```bash
bash docs/bug-reproductions/scripts/repro-8454-localhost-http.sh
# equivalent:
npx vitest run src/shared/browser-url.test.ts -t "normalizes manual local-dev"
```
## Code
`src/shared/browser-url.ts`:
```166:171:src/shared/browser-url.ts
if (LOCAL_ADDRESS_PATTERN.test(trimmed)) {
try {
return new URL(`http://${trimmed}`).toString()
} catch {
return null
}
}
```
## Unit assertion (already in repo)
`src/shared/browser-url.test.ts`:
```ts
expect(normalizeBrowserNavigationUrl('localhost:3000')).toBe('http://localhost:3000/')
expect(normalizeBrowserNavigationUrl('127.0.0.1:5173')).toBe('http://127.0.0.1:5173/')
```
These tests **pass** and document the forced-http behavior as the current product contract.
## UI half (self-signed HTTPS)
Not automated here:
1. Serve `https://localhost:3000` with a self-signed cert (e.g. `next dev --experimental-https`).
2. In Orca browser, type `localhost:3000` → navigates to `http://…` → fails against HTTPS-only server.
3. Type full `https://localhost:3000` → scheme preserved, but cert rejection may still block without a proceed UI.
## Expected fix direction
Allow HTTPS for loopback (probe, remember last scheme, or prefer https when http fails) and/or offer insecure-cert proceed for local trusted contexts.
+51
View File
@@ -0,0 +1,51 @@
# #8457 — headless serve hijacks GUI relaunch; forced reopen interrupts/duplicates live agents
**Status:** REPRODUCED (code-level lifecycle ownership, conclusive)
**Issue:** https://github.com/stablyai/orca/issues/8457
**Related:** #8459 (orphan kill after messy relaunch)
**Malicious content:** none
**Safety:** Do **not** live-repro with valuable agent sessions (`open -n` is destructive).
## Summary
A packaged `orca serve` process can own the single-instance lock for the same userData profile after desktop Cmd+Q. Finder / Dock / `orca open` then interact with that headless owner. After serve settles with a **daemon** PTY provider, the desktop activation gate becomes `ready` and second-instance / activate calls `openMainWindow()` inside the serve process — hydrating a renderer that can cold-restore / resume agents and interrupt live ones.
## Root-cause chain (current tree)
| Step | Code | Behavior |
|------|------|----------|
| 1 | `shouldSkipSingleInstanceLock` | Serve never skips the lock → headless can own profile |
| 2 | `acquireSingleInstanceLock(app, requestDesktopActivation)` | Second instance → activation gate |
| 3 | Gate `initialState: isServeMode ? 'initializing' : 'ready'` | Serve starts gated |
| 4 | `settleServeDesktopActivation` | **LocalPtyProvider** → `markBlocked`; **daemon** → `markReady` |
| 5 | `activateWindow` → `focusExistingWindow` → `openMainWindow` | Window opened inside serve when ready |
| 6 | `RuntimeClient.openOrca` | Fails closed only on `blocked`; `openable` still `launchOrcaApp()` |
Partial mitigation exists (gate + `desktopWindowStatus` + block when persistent provider missing), but the **common production path** (daemon-backed PTYs) still promotes to desktop inside serve.
## Evidence (automated)
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/main/startup/repro-8457-serve-desktop-hijack.test.ts
```
Proves:
- Packaged/serve does not skip single-instance lock.
- After settle with daemon (non-LocalPty), pending activation opens the window; further requests keep opening.
- LocalPty settle blocks (fail-closed path only for missing persistent provider).
- `orca open` fail-closed only for `blocked`, not headless `openable`.
- Wiring still routes serve activation to `focusExistingWindow` / `openMainWindow`.
## Live repro (disposable agents only)
1. Desktop Orca with Persistent Terminal Sessions + disposable agents.
2. Packaged `orca serve` same profile; Cmd+Q desktop.
3. Confirm agents/daemon alive, no GUI.
4. `orca open` / Dock; forced `open -n -a Orca` may mount renderer in serve process.
5. Watch for interrupt / resume / duplicate agents.
## Expected fix direction
Serve must not silently own desktop activation, or handoff must be atomic. Prefer fail closed for headless→desktop unless a full desktop init contract is established; never treat bare runtime reachability as desktop capability.
+43
View File
@@ -0,0 +1,43 @@
# #8459 — Resource Manager misclassifies live daemon sessions as orphan and force-kills without confirmation
**Status:** REPRODUCED (code-level, conclusive)
**Issue:** https://github.com/stablyai/orca/issues/8459
**Related:** #8457 (lifecycle that can produce incomplete renderer index)
**Malicious content:** none
## Summary
Resource Manager treats every daemon session absent from the **current renderer binding map** as an “orphan.” Bulk **Kill N orphan terminals** and per-row kill on unbound rows skip confirmation and call `window.api.pty.kill` immediately. A recovering/incomplete renderer can therefore force-kill live agents.
## Root cause
### Classification (`resource-session-bindings.ts`)
```ts
boundPtyIds: inputs.workspaceSessionReady ? new Set(ptyIdToTabId.keys()) : new Set()
// orphan ⇔ !boundPtyIds.has(session.id)
```
Index sources: `ptyIdsByTabId`, `tab.ptyId`, layout `ptyIdsByLeafId`. No daemon ownership, pid/liveness, runtime generation, or headless/mobile owner check.
### Destructive path (`ResourceUsageStatusSegment.tsx`)
- Unbound row: skip confirm, kill immediately.
- Bulk orphans: `sessions.filter(s => !bound.has(s.id))` → `Promise.allSettled(...pty.kill)` with no confirm and no revalidation.
## Evidence (automated)
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/renderer/src/components/status-bar/repro-8459-orphan-live-daemon.test.ts
```
Proves:
1. Empty renderer map + `workspaceSessionReady: true` → all live daemon sessions count as unbound.
2. Partial map still labels unreferenced live agent PTYs as orphans (no process check).
3. Source still skips confirm for unbound and bulk-kills without revalidation; bindings module has no pid/owner/generation logic.
## Expected fix direction
Absence from the binding map is necessary but not sufficient for kill-safe orphan. Reconcile daemon inventory, fail closed while hydration is incomplete, require confirm for bulk kill, revalidate immediately before shutdown.
+25
View File
@@ -0,0 +1,25 @@
# #8478 — OpenCode logo not coming up well (Claude icon on OpenCode tabs)
**Status:** REPRODUCED
**Issue:** https://github.com/stablyai/orca/issues/8478
**Label:** `has_repro`
**Related:** #8940
**Fix PR (open):** #8590
**Malicious content:** none (screenshot only; not executed)
## Summary
OpenCode’s native OSC title format is `OC | <task>`. Title classifiers only treat OpenCode as identity when the token `opencode` appears. Native `OC | …` titles are not mapped to `opencode`, so `AgentIcon` does not render the OpenCode glyph (Claude or unknown instead). Matches the issue screenshot (Claude glyph on an OpenCode tab).
## Evidence (automated)
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/shared/repro-8478-opencode-native-title-icon.test.ts
```
Proves:
- No `OC |` native matcher in `terminal-title-agent-type.ts` / `agent-title-core.ts`.
- `getAgentLabel('OC | Understand about the plugin')` is not OpenCode.
- Braille/task frames without `opencode` still become Claude (#8940 family).
+33
View File
@@ -0,0 +1,33 @@
# #8482 — Window Blur causes sustained high GPU power/heat on macOS
**Status:** REPRODUCED (code-level config contract + reporter powermetrics; no live heat required)
**Issue:** https://github.com/stablyai/orca/issues/8482
**Related:** #8797 (blur/opacity no-op — same window path; different symptom)
**Malicious content:** none
## Summary
Enabling **Window Blur** on macOS maps to Electron `vibrancy: 'under-window'` with `transparent: true` at window construction. The main window also always calls `setBackgroundThrottling(false)` on darwin. Issue reporter measured continuous multi-watt GPU draw on a maximized visible window with blur on; hiding the window or disabling blur + restart cut GPU power ~85–89% with the same ~150 terminal sessions still live — isolating cost to **visible composition**, not PTYs alone.
## Root cause (proven in tree)
`src/main/window/createMainWindow.ts`:
| Contract | Behavior |
|----------|----------|
| `windowBackgroundBlur` | darwin → `{ vibrancy: 'under-window', transparent: true }` |
| Throttling | always `setBackgroundThrottling(false)` on macOS |
| Lifetime | blur only at window creation; no `setVibrancy` teardown path |
Reporter averages (issue body): blur visible **~5365 mW** → hidden **~589 mW** → blur off visible **~804 mW**.
## Automated proof
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/main/window/repro-8482-window-blur-gpu-cost.test.ts
```
## Workaround
Disable **Settings → Appearance → Window Blur** and restart Orca.
+105
View File
@@ -0,0 +1,105 @@
# #8532 — Cmd+Shift+O (Open markdown) only works in Floating Terminal
**Status:** REPRODUCED (code-level wiring gap, conclusive)
**Issue:** https://github.com/stablyai/orca/issues/8532
**Related open fix PR:** https://github.com/stablyai/orca/pull/8564
**Malicious content:** none
## Summary
`tab.openMarkdown` (default **Cmd+Shift+O** on macOS) is bound in Settings and works when the Floating Terminal panel owns focus. In the main window / normal worktree it does nothing. **Cmd+Shift+M** (`tab.newMarkdown`) works in the main window — inconsistent.
## Evidence
### 1. Floating panel allowlist + handler
`src/renderer/src/lib/floating-workspace-shortcut-policy.ts`:
```ts
const FLOATING_WORKSPACE_PANEL_SHORTCUT_ACTIONS = [
'tab.newTerminal',
'tab.newBrowser',
'tab.newMarkdown',
'tab.openMarkdown', // ← floating only
'tab.close'
]
```
`FloatingTerminalPanel.tsx` handles the chord:
```ts
if (matches('tab.openMarkdown')) {
consume()
openFloatingMarkdownTab() // window.api.app.pickFloatingMarkdownDocument()
return true
}
```
### 2. Main-window Terminal.tsx has newMarkdown only
```ts
// Terminal.tsx — present
if (!e.repeat && matchShortcut('tab.newMarkdown')) { ... }
// Terminal.tsx — ABSENT
// matchShortcut('tab.openMarkdown') ← not present
```
### 3. No main-process / IPC open path
| Surface | `tab.newMarkdown` | `tab.openMarkdown` |
|---------|-------------------|--------------------|
| `FLOATING_WORKSPACE_PANEL_SHORTCUT_ACTIONS` | yes | yes |
| FloatingTerminalPanel keydown | yes | yes |
| Terminal.tsx matchShortcut | yes | **no** |
| App.tsx keydown | no | **no** |
| `window-shortcut-policy` allowlist | neither (by design for terminal chords) | neither |
| preload `onNewMarkdownTab` / `ui:newMarkdownTab` | yes | **no `onOpenMarkdownTab`** |
| browser-guest-ui branch | yes → `ui:newMarkdownTab` | **no** |
| Tab bar menu action `open-markdown` | menu click only (`onOpenFileTab`) | not bound to global shortcut |
Grep snapshot (evidence file): only floating policy + FloatingTerminalPanel mention `tab.openMarkdown` in the main path.
### 4. Keybinding is registered globally
`src/shared/keybindings.ts` defines `tab.openMarkdown` → `Mod+Shift+O` (darwin). Settings shows the binding for all contexts, but only floating focus routes it.
### 5. Main before-input-event never dispatches openMarkdown
`resolveWindowShortcutAction` (`src/shared/window-shortcut-policy.ts`) is an explicit allowlist (settings, zoom, sidebars, workspace jump, …). It does **not** include `tab.newMarkdown` or `tab.openMarkdown`. New markdown works in main because **Terminal.tsx renderer keydown** handles it when the terminal owns focus — open markdown simply lacks that branch.
## Net effect
Main window: Cmd+Shift+O is never consumed → no picker, no tab.
Floating panel focused: local handler opens picker → works.
## Re-run steps
```bash
cd /Users/nwparker/orca/workspaces/orca/cooked-PRs
# Static wiring proof
rg -n "tab\.openMarkdown|onOpenMarkdownTab|ui:openMarkdown" \
src/renderer/src/lib/floating-workspace-shortcut-policy.ts \
src/renderer/src/components/floating-terminal/FloatingTerminalPanel.tsx \
src/renderer/src/components/Terminal.tsx \
src/shared/window-shortcut-policy.ts \
src/preload/index.ts \
src/main/window/createMainWindow.ts
# Expect: openMarkdown only in floating policy + FloatingTerminalPanel
# Expect: Terminal.tsx has tab.newMarkdown but not tab.openMarkdown
```
### Live UI (prod Orca)
Binary: `/Applications/Orca.app/Contents/Resources/bin/orca` (app UI)
1. Focus a normal worktree terminal → **Cmd+Shift+O** → nothing.
2. Open Floating Terminal (**Cmd+Alt+A**), focus it → **Cmd+Shift+O** → markdown picker.
3. Control: **Cmd+Shift+M** works in main window.
## Fix direction
PR #8564: main-window path mirroring `tab.newMarkdown` — worktree-rooted picker IPC, store open action, Terminal.tsx keydown branch. (Also ensure Settings shortcut is not floating-only if the action is general.)
+69
View File
@@ -0,0 +1,69 @@
# #8533 / #8584 — Default shortcut conflicts
## Status
**REPRODUCED** (defaults in `src/shared/keybindings.ts`)
## Collisions
### #8533 — `Cmd+Shift+E` on macOS
| Action | Platform defaults |
|--------|-------------------|
| `sidebar.explorer.toggle` | all: `Mod+Shift+E` |
| `tab.newSimulator` | darwin: `Mod+Shift+E`; linux/win32: unbound |
On darwin both claim `Mod+Shift+E`. Linux/Windows only bind Explorer, so they are unaffected.
### #8584 — `Cmd/Ctrl+0` on every platform
| Action | Platform defaults |
|--------|-------------------|
| `sidebar.focusWorktreeList` | all: `Mod+0` |
| `zoom.reset` | all: `Mod+0` |
## Source (definitions)
```360:366:src/shared/keybindings.ts
id: 'sidebar.explorer.toggle',
// ...
defaultBindings: platformBindings(['Mod+Shift+E'])
```
```565:574:src/shared/keybindings.ts
id: 'tab.newSimulator',
// ...
defaultBindings: {
darwin: ['Mod+Shift+E'],
linux: [],
win32: []
}
```
```424:429:src/shared/keybindings.ts
id: 'sidebar.focusWorktreeList',
// ...
defaultBindings: platformBindings(['Mod+0'])
```
```508:513:src/shared/keybindings.ts
id: 'zoom.reset',
// ...
defaultBindings: platformBindings(['Mod+0'])
```
## Why Settings conflict detector may stay silent
`findKeybindingConflicts()` only returns conflicts that involve **customized overrides** (`setIntersects(actionIds, customizedActions)`). Pure default-vs-default collisions return `[]`, so `expect(findKeybindingConflicts('darwin')).toEqual([])` can still pass in unit tests while runtime chords fight.
## Re-run
```bash
npx vitest run docs/bug-reproductions/scripts/repro-8533-8584-shortcut-conflicts.test.ts
```
Proof file: `docs/bug-reproductions/scripts/repro-8533-8584-shortcut-conflicts.test.ts`
## Runtime note
Main-process shortcut resolution order determines the winner (`zoom.reset` / simulator typically consume the chord first per issue reports), leaving the other advertised binding unreachable.
+44
View File
@@ -0,0 +1,44 @@
# #8533 — `Cmd+Shift+E` default collision on macOS
**Status:** REPRODUCED (defaults in source)
**Platform:** macOS only for the collision (Win/Linux have no default for `tab.newSimulator`)
## Summary
Both actions default to `Mod+Shift+E` on darwin:
| Action | Title | Default (darwin) |
|--------|-------|------------------|
| `tab.newSimulator` | New mobile emulator tab | `Mod+Shift+E` |
| `sidebar.explorer.toggle` | Show Explorer | `Mod+Shift+E` |
Main-process / terminal handlers consume the chord for the emulator first → Explorer toggle never fires.
## Evidence
From `src/shared/keybindings.ts` (current tree):
```ts
// sidebar.explorer.toggle
defaultBindings: platformBindings(['Mod+Shift+E'])
// tab.newSimulator
defaultBindings: {
darwin: ['Mod+Shift+E'],
linux: [],
win32: []
}
```
## Re-run
```bash
pnpm exec vitest run docs/bug-reproductions/scripts/repro-shortcut-conflicts.test.ts
```
## Manual UI
1. macOS, mobile emulator feature enabled (default).
2. Press `Cmd+Shift+E`.
3. Observe: new Mobile Emulator tab; Explorer does not toggle.
4. Settings still lists `Cmd+Shift+E` for Explorer.
+40
View File
@@ -0,0 +1,40 @@
# #8535 — pinned `orca serve --port` overridden by stale mobile-ws fallback
**Status:** REPRODUCED (code-level, conclusive)
**Issue:** https://github.com/stablyai/orca/issues/8535
**Platform:** cross-platform (reported on Linux headless; code is shared)
**Malicious content:** none
## Summary
When `mobile-ws-fallback-port.json` names a port other than the CLI `--port`, `WebSocketTransport.start` tries the **fallback first**. An explicit pin never binds if the stale fallback is free. Introduced for STA-1511 (paired devices keep old endpoints); side effect strands clients dialing the pinned port.
## Root cause
`src/main/runtime/rpc/ws-transport.ts`:
```ts
const candidatePorts =
persistedFallbackPort !== undefined ? [persistedFallbackPort, this.port] : [this.port]
// first successful tryListen wins → free stale fallback pre-empts pinned port
```
`runtime-rpc.ts` always passes `fallbackPort: readWsFallbackPort(...)` when `wsPort !== 0`, and writes the store whenever `resolvedPort !== this.wsPort`. Explicit `--port` is not distinguished from the default port after construction (`wsPort = DEFAULT_WS_PORT`).
Also covered by integration-style tests in `ws-transport.test.ts` (“binds the persisted fallback port even when the preferred port is free”).
## Evidence (automated)
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/main/runtime/rpc/repro-8535-ws-fallback-port-order.test.ts
```
## Manual
1. Write `{ "port": 49152 }` to `<userData>/mobile-ws-fallback-port.json`.
2. Ensure `:6768` is free; run `orca serve --port 6768`.
3. Observe bind on **49152** (pinned 6768 never tried first).
4. Remove the JSON file; serve binds **6768**.
**Workaround:** delete `mobile-ws-fallback-port.json` before pinned serve.
+35
View File
@@ -0,0 +1,35 @@
# #8539 — Renderer freezes ~87s on reconnect with many worktrees
**Status:** PARTIAL (call sites + O(repos) work proven; no live 37-worktree freeze)
**Issue:** https://github.com/stablyai/orca/issues/8539
**Malicious content:** none
## Summary
Reconnect with ~37 worktrees / 71 tabs: Chrome DevTools reported forced reflow ~87s with stack through `fetchAllWorktrees` in passive mount effects.
## What we proved in tree
- `App.tsx` still runs `fetchAllWorktrees` during startup (`fetch-worktrees`) and again after remote catalog refresh (`remote-worktree-refresh`), adjacent to `reconnectPersistedTerminals`.
- `fetchAllWorktrees` maps **every repo** via `listDetectedWorktreesForRepoCoalesced` + merge — no idle/chunk yield in the hot path.
Full 87s sync reflow needs a multi-host multi-worktree session (listed under perf/SSH deferred in index).
## Automated proof (call sites)
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/renderer/src/store/slices/repro-8539-fetchAllWorktrees-reconnect.test.ts
```
## Suggested fix direction
- Defer remote worktree refresh until after first paint / idle.
- Chunk repo listing; avoid mounting all tab trees before catalog settles.
- Profile merge/set paths for accidental sync layout thrash.
## Evidence
- `src/renderer/src/store/slices/repro-8539-fetchAllWorktrees-reconnect.test.ts`
- `src/renderer/src/App.tsx`
- `src/renderer/src/store/slices/worktrees.ts`
+30
View File
@@ -0,0 +1,30 @@
# #8541 — All remote sessions time out simultaneously on reconnect (multiplex cascade)
**Status:** REPRODUCED (code-level cascade; live multi-host optional)
**Issue:** https://github.com/stablyai/orca/issues/8541
**Malicious content:** none
## Summary
All sessions for a remote server share one shared-control WebSocket (`RemoteRuntimeSharedControlConnection`). When one RPC times out, the connection tears down and **rejects every pending request at once** — matching the DevTools storm (all errors within a few seconds).
## Root-cause chain
| Step | Code | Behavior |
|------|------|----------|
| 1 | One `pendingRequests` Map per connection | All session RPCs multiplexed |
| 2 | `requestSharedControl` timeout | Rejects that RPC with `Timed out waiting for the remote Orca runtime to respond.` |
| 3 | `onTimeout` → `handleSocketClosed` | Full socket close |
| 4 | `closeSharedControlSocketState` | `rejectAllSharedControlPendingRequests` — cascade |
| 5 | Timeout reset message | `Remote Orca runtime did not answer in time; resetting the control connection.` |
Issue’s two waves match: per-call timeout text then connection-reset text.
## Automated proof
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/shared/repro-8541-multiplex-timeout-cascade.test.ts
```
Proves multi-pending cascade reject + exact issue error strings + onTimeout wiring.
+88
View File
@@ -0,0 +1,88 @@
# #8577 — Settings search query leaks into Shortcuts pane row filter
**Status:** REPRODUCED (code-level, conclusive)
**Issue:** https://github.com/stablyai/orca/issues/8577
**Related open fix PR:** https://github.com/stablyai/orca/pull/8579
**Malicious content:** none (screenshots are UI captures only)
## Summary
Typing `shortcuts` (or a localized pane title) into Settings sidebar search auto-opens the Shortcuts pane with **0/N** rows, “No shortcuts match those filters.”, and dead local “Find shortcuts” search until the user clicks the sidebar item (which clears the query).
## Root cause (proven)
Sidebar ranking includes a **pane-title entry**; Shortcuts row filtering reuses the **same** `settingsSearchQuery` and ANDs it with the pane’s local query.
### 1. Documented hazard in settings-search
```125:133:src/renderer/src/components/settings/settings-search.ts
export function getSettingsSectionSearchEntries(section: {
title: string
description: string
searchEntries: readonly SettingsSearchEntry[]
}): SettingsSearchEntry[] {
// Why: sidebar ranking and active content filtering must receive the same
// pane-level entry, otherwise pane-title-only hits can rank but render blank.
return [{ title: section.title, description: section.description }, ...section.searchEntries]
}
```
Pane-title-only hits are **intended** for ranking/selection; the comment already flags blank content when the same query filters rows.
### 2. ShortcutsPane double-applies the global query
```58:145:src/renderer/src/components/settings/ShortcutsPane.tsx
const searchQuery = useAppStore((state) => state.settingsSearchQuery)
// ...
const matchesShortcutSearch = (row) =>
shortcutSearchQuery !== null &&
matchesSettingsSearch(searchQuery, getShortcutSearchEntry(row)) && // global sidebar query
matchesShortcutLocalSearch(row, shortcutSearchQuery, platform) // local "Find shortcuts"
const baseVisibleRows = shortcutRows.filter((row) => matchesShortcutSearch(row))
```
`getShortcutSearchEntry(row)` only has row title / group / keywords — **not** the pane title “Keyboard Shortcuts” / “Shortcuts”. Query `shortcuts` matches the pane entry for auto-activation but **zero** rows.
Local search then intersects an empty base list → typing `go to` still yields 0 results.
### 3. Recovery path
`Settings.tsx` `scrollToSection` clears `settingsSearchQuery` on explicit sidebar click — that is why clicking “Shortcuts” restores 112/112. Auto-activation from typing does not clear.
### 4. Model of the bug
```text
query "shortcuts" vs row titles ["Go to File", "New Terminal", ...]
→ matchesSettingsSearch false for all rows
→ baseVisibleRows = [] (0/112)
→ local "go to" still AND global "shortcuts" → still 0
```
### 5. settings-search unit tests confirm ranking, not the pane fix
`settings-search.test.ts` ranks Shortcuts above Task Sources for query `shortcuts` using pane-level entries — correct for sidebar. It does not assert Shortcuts row visibility under that query (bug remains).
## Re-run steps
```bash
cd /Users/nwparker/orca/workspaces/orca/cooked-PRs
# Read the leak
rg -n "matchesShortcutSearch|settingsSearchQuery" \
src/renderer/src/components/settings/ShortcutsPane.tsx
pnpm exec vitest run --config config/vitest.config.ts \
src/renderer/src/components/settings/settings-search.test.ts
```
### Live UI
1. Settings → type `shortcuts` in sidebar search.
2. Shortcuts pane opens with 0/N and empty filters.
3. Type `go to` in “Find shortcuts” → still 0.
4. Click “Shortcuts” in sidebar → list restored.
## Fix direction
PR #8579: if the global query matches **no** shortcut row, treat it as pane-level-only and keep all rows (local search works). Row-matching queries still narrow the list.
+37
View File
@@ -0,0 +1,37 @@
# #8584 — `Cmd/Ctrl+0` binding conflict (zoom.reset vs focus worktree list)
**Status:** REPRODUCED (defaults in source)
**Platform:** all (both use `Mod+0`)
## Summary
| Action | Title | Default |
|--------|-------|---------|
| `zoom.reset` | Reset Size | `Mod+0` |
| `sidebar.focusWorktreeList` | Focus worktree list | `Mod+0` |
Issue reports Focus worktree list is unreachable because zoom wins (or vice versa depending on handler order). Either way, defaults collide.
## Evidence
`src/shared/keybindings.ts`:
```ts
// sidebar.focusWorktreeList
defaultBindings: platformBindings(['Mod+0'])
// zoom.reset
defaultBindings: platformBindings(['Mod+0'])
```
## Re-run
```bash
pnpm exec vitest run docs/bug-reproductions/scripts/repro-shortcut-conflicts.test.ts
```
## Manual UI
1. Press `Cmd+0` (macOS) / `Ctrl+0` (Win/Linux).
2. Observe only one of: zoom reset vs worktree list focus.
3. Settings still lists both with the same chord.
+26
View File
@@ -0,0 +1,26 @@
# #8593 — Lingering idle subagent rows; click does nothing useful
**Status:** REPRODUCED
**Issue:** https://github.com/stablyai/orca/issues/8593
**Label:** `has_repro`
**Malicious content:** none (screenshot only; not executed)
## Summary
Long Claude sessions that spawn many Task/subagent children (e.g. Cypress runners) leave every finished child as an `idle` sidebar/dashboard row. Rows cannot be dismissed (`hideDismiss` for `rowSource === 'subagent'`). Activation only focuses the parent pane (`activationPaneKey`), which is a no-op when the parent is already focused.
## Root cause
| Layer | Behavior |
|-------|----------|
| `buildSubagentChildRows` | Maps **all** `subagents[]` including idle |
| Live hooks | Keep idle roster for the parent lifetime |
| `dropHydratedIdleClaudeSubagents` | Only on **hydrate** replay, not live updates |
| Dashboard | `hideDismiss={rowSource === 'subagent'}` |
## Evidence (automated)
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/renderer/src/components/sidebar/repro-8593-idle-subagent-rows.test.ts
```
+60
View File
@@ -0,0 +1,60 @@
# #8595 — “Bold Text” terminal color override is a no-op
**Status:** REPRODUCED (code-level + unit test)
**Platform:** all (renderer)
## Summary
Settings expose `terminalColorOverrides.bold` (and Ghostty import maps `bold-color`), but xterm.js `ITheme` has **no** `bold` key. Spreading the override into the theme object is a silent no-op for bold-default-fg cells.
## Evidence
`src/shared/types.ts` explicitly documents the gap:
```ts
// Why: xterm.js ITheme does not expose a `bold` key, but Ghostty users
// expect the setting to be preserved so a future renderer CSS override
// or xterm upgrade can honour it without a migration.
bold?: string
```
`src/renderer/src/components/terminal-pane/terminal-appearance.ts` spreads overrides into the theme:
```ts
if (settings.terminalColorOverrides) {
theme = { ...theme, ...settings.terminalColorOverrides }
}
```
xterm ignores unknown theme keys → bold never applied.
Ghostty import *does* map the setting (`src/main/ghostty/mapper.ts` `bold-color`), so the value can be stored and still do nothing in the renderer.
`drawBoldTextInBrightColors` only remaps ANSI 0–7 bold → bright; it does not recolor default-fg bold cells.
## Automated re-check
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/renderer/src/components/terminal-pane/repro-8595-bold-theme-noop.test.ts
```
Asserts: composed theme retains `.bold = #ffc600` while `ITheme` color slots have no `bold` key.
Also:
```bash
rg -n "bold\\?:" src/shared/types.ts src/renderer/src/components/terminal-pane/terminal-appearance.ts
rg -n "bold-color" src/main/ghostty/mapper.ts
```
## Manual repro
1. Settings → Appearance → Terminal → color overrides → Bold Text = `#ffc600`.
2. Open a **new** terminal.
3. `printf '\033[1mBOLD DEFAULT FG\033[0m\n'`
4. Expected (iTerm/Ghostty): gold bold. Actual: theme foreground.
## Fix direction
Honor `terminalColorOverrides.bold` for bold cells with default fg (custom renderer / CSS / xterm patch), **or** hide the setting and drop Ghostty `bold-color` mapping until supported.
+35
View File
@@ -0,0 +1,35 @@
# #8622 — SSH keyboard-interactive authentication (push-based MFA)
**Status:** REPRODUCED (capability gap — feature reported as bug)
**Issue:** https://github.com/stablyai/orca/issues/8622
**Malicious content:** none
## Summary
University HPC: password then keyboard-interactive push MFA works in OpenSSH / VS Code Remote SSH. In Orca, password prompt appears; after password → **“All configured authentication methods failed”**; MFA prompt never shown.
## Root cause (missing capability)
ssh2 path supports agent / privateKey / **password** only:
| Gap | Evidence |
|-----|----------|
| No `tryKeyboard` on connect config | `buildConnectConfig` never sets it; zero matches in ssh auth modules |
| No `keyboard-interactive` handler | `ssh-connection.ts` has no such event |
| Credential kinds | `SshCredentialKind = 'passphrase' \| 'password'` only |
| Failure string | `isAuthError` matches issue text |
Password method ≠ keyboard-interactive challenge-response. After password, second MFA challenge is never offered → auth fails with the exact user-facing error.
System-SSH fallback exists for GSSAPI/ProxyCommand cases but is not a general keyboard-interactive MFA path after interactive password.
## Automated proof
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/main/ssh/repro-8622-keyboard-interactive-missing.test.ts
```
## Classification
Not a regression of a previously implemented feature — **unsupported auth method**. Still actionable: implement `tryKeyboard` + UI prompts for interactive challenges (or route such hosts through system OpenSSH with a prompt-capable transport).
+91
View File
@@ -0,0 +1,91 @@
# #8670 — Deleting clean worktree with initialised submodule fails
## Status
**REPRODUCED** (Git layer + Orca force plumbing)
UI nuance: confirm-dialog deletes currently pass `force: true`; skip-confirm deletes pass `force: false` and do not classify the submodule error as force-recoverable.
## Summary
Git refuses `git worktree remove <path>` when the linked worktree has an initialised submodule, even if parent and submodule are completely clean:
```text
fatal: working trees containing submodules cannot be moved or removed
```
`git worktree remove --force <path>` succeeds and only removes that worktree.
## Re-run
```bash
bash docs/bug-reproductions/scripts/repro-8670-submodule-worktree-remove.sh
```
Evidence: `docs/bug-reproductions/evidence/8670-git-submodule-remove.txt`
## Git evidence (this machine)
| Step | Result |
|------|--------|
| Clean worktree + `submodule update --init` | clean statuses |
| `git worktree remove <wt>` | exit **128**, fatal submodule message |
| `git worktree remove --force <wt>` | exit **0**, path removed |
Fixture uses `git -c protocol.file.allow=always` for local `file://` submodules.
## Orca `removeWorktree` force plumbing
`src/main/git/worktree.ts` — `--force` only when `force === true`:
```1219:1224:src/main/git/worktree.ts
const args = ['worktree', 'remove']
if (force) {
args.push('--force')
}
args.push(worktreePath)
await gitExecFileAsync(args, gitExecOptions(repoPath, options))
```
Default: `force = false`.
## UI / call paths
| Path | Force? | Notes |
|------|--------|-------|
| `DeleteWorktreeDialog` primary confirm | **`force: true`** | `runWorktreeDeletesInParallel(..., { force: true })` — should pass `--force` |
| Skip-confirm (`skipDeleteWorktreeConfirm`) | **`force: false`** | `runWorktreeDeleteWithToast(id, name)` with no force → `options.force === true` is false |
| Batch skip-confirm | **`force: false`** | same |
| Store `removeWorktree(id)` default | **`force` undefined/false** | IPC/runtime forward as non-force |
Skip-confirm site:
```300:303:src/renderer/src/components/sidebar/delete-worktree-flow.ts
const skipConfirm = state.settings?.skipDeleteWorktreeConfirm ?? false
if (skipConfirm && !hasLineageChildren) {
void runWorktreeDeleteWithToast(worktreeId, target.displayName)
return
```
Toast helper:
```139:139:src/renderer/src/components/sidebar/delete-worktree-flow.ts
return removeWorktree(worktreeId, options.force === true)
```
## Force-delete recovery gap
`classifyWorktreeForceDeleteReason` (`src/shared/worktree-removal.ts`) only maps dirty / orphan / missing-registration. It does **not** recognize:
```text
working trees containing submodules cannot be moved or removed
```
So a non-force failure from skip-confirm surfaces a raw error **without** a Force Delete recovery toast (`canForceDelete: false`).
## Expected fix direction
Either:
1. Treat initialised-submodule worktrees as requiring `--force` for clean deletes (auto or preflight), and/or
2. Classify the Git submodule message as a force-delete reason so the toast recovery path works when the first attempt uses `force: false`.
+76
View File
@@ -0,0 +1,76 @@
# #8695 — Bundled undici 7.28.0 asserts on paused parser when socket closes
**Status:** REPRODUCED
**Issue:** https://github.com/stablyai/orca/issues/8695
**Malicious content:** none
## Summary
When a response body is not consumed and the peer closes the socket, undici’s
HTTP/1 parser hits `assert(!this.paused)` in `Parser.finish`, crashing the
Electron main process (uncaught AssertionError).
## Environment (this host)
| Component | Version |
|-----------|---------|
| Production Orca Electron | Node `24.18.0` (from `process.versions`) |
| Bundled undici (Orca Electron) | `7.28.0` |
| Worktree `node_modules/undici` | `7.28.0` |
| package.json | `electron ^43.1.0`, engines `node: '24'` |
Upstream: [nodejs/undici#5360](https://github.com/nodejs/undici/issues/5360), fix [nodejs/undici#5474](https://github.com/nodejs/undici/pull/5474).
## Root cause (proven)
1. Response body applies backpressure → parser `paused`.
2. Peer FIN → `onHttpSocketEnd` → `parser.finish()`.
3. `finish` asserts `!this.paused` and throws (uncaught → main process death).
Code still present in worktree:
`node_modules/undici/lib/dispatcher/client-h1.js` — multiple `assert(!this.paused)`.
App-level try/catch around `fetch` cannot prevent the crash.
## Live crash repro
```bash
bash docs/bug-reproductions/scripts/repro-8695-undici-paused-parser.sh
```
Or manually:
```bash
ELECTRON_RUN_AS_NODE=1 /Applications/Orca.app/Contents/MacOS/Orca /path/to/repro.js
```
Observed stack (system node + Orca Electron, 2026-07-15):
```text
AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:
assert(!this.paused)
at Parser.finish (node:internal/deps/undici/undici:7380:9)
at Socket.onHttpSocketEnd (node:internal/deps/undici/undici:7819:34)
```
Log: `docs/bug-reproductions/evidence/8695-undici-repro.log`
## Unit proof (version pin)
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/main/repro-8695-undici-paused-parser.test.ts
```
## Suggested product fix
Ship an Electron/Node runtime with undici including #5474 (or equivalent backport).
Consuming every response body reduces likelihood but is not sufficient.
## Evidence paths
- `docs/bug-reproductions/scripts/repro-8695-undici-paused-parser.sh`
- `docs/bug-reproductions/evidence/8695-undici-repro.log`
- `src/main/repro-8695-undici-paused-parser.test.ts`
- `node_modules/undici/package.json` / `lib/dispatcher/client-h1.js`
+15
View File
@@ -0,0 +1,15 @@
# #8715 — OMP scroll resets when switching tabs
**Status:** REPRODUCED
**Label:** `has_repro`
## Summary
OMP (and other full-screen TUIs) use xterm’s alternate buffer. `scheduleSplitScrollRestore` **skips** restore when `bufferType === 'alternate'` (restore-during-draw knocks the TUI cursor — #1298). Tab switch → no scroll re-pin → viewport appears at top.
## Re-run
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/renderer/src/lib/pane-manager/repro-8715-omp-alt-screen-scroll.test.ts
```
+68
View File
@@ -0,0 +1,68 @@
# #8720 — SSH relay npm 12 silently skips node-pty (allowScripts)
**Status:** REPRODUCED (code-level; no live Linux npm 12 remote required)
**Issue:** https://github.com/stablyai/orca/issues/8720
**Malicious content:** none
## Summary
Relay deploy runs `npm install` for `node-pty@1.1.0` + `@parcel/watcher@2.5.6` on the
remote. Under **npm 12**, lifecycle scripts not covered by `allowScripts` are blocked
(warn + exit 0 when `strict-allow-scripts` is false). Linux has no node-pty prebuild, so
`node-gyp rebuild` never runs → `require('node-pty')` fails at PTY spawn with
“node-pty is not available on this remote host”.
## Root cause (proven)
`installNativeDeps` in `src/main/ssh/ssh-relay-deploy.ts`:
1. Writes hardcoded package.json:
```ts
const RELAY_NATIVE_DEPS = {
'node-pty': '1.1.0',
'@parcel/watcher': '2.5.6'
}
// package.json: { name, version, private, type: 'commonjs', dependencies }
// NO allowScripts
```
2. Runs:
```text
npm install --omit=dev --no-audit --no-fund node-pty@1.1.0 @parcel/watcher@2.5.6
```
No `allowScripts`, no `npm install-scripts approve`, no force-enable lifecycle scripts.
3. On npm 12 Linux: scripts blocked → no build → install “succeeds” →
`.install-complete` written (hard fail only if npm itself fails). Probe may warn
MISSING, but the issue reports deploy looking successful while runtime PTY fails.
ESM/loader cache of failed import can also stick until relay restart.
## Automated proof
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/main/ssh/repro-8720-npm12-allow-scripts.test.ts
```
Assertions:
- Deploy source pins deps and never mentions `allowScripts` / `allow-scripts`
- Install command has no script-approve flags
- Synthetic package.json equals deploy shape and lacks `allowScripts`
- Existing `ssh-relay-native-deps-install.test.ts` pins deps only (no allowScripts)
## Suggested fix (from issue)
- Emit `allowScripts` for `node-pty` and `@parcel/watcher` in generated package.json
- Fail deploy unless `require('node-pty')` succeeds (surface npm warning)
- Longer term: pre-bundled per-platform relay (#1693)
## Evidence paths
- `src/main/ssh/repro-8720-npm12-allow-scripts.test.ts`
- `src/main/ssh/ssh-relay-deploy.ts` (`RELAY_NATIVE_DEPS`, `installNativeDeps`)
- `src/main/ssh/ssh-relay-native-deps-install.test.ts` (install contract)
- `src/main/ssh/ssh-relay-build-toolchain.ts` (Linux no-prebuild note)
+33
View File
@@ -0,0 +1,33 @@
# #8726 — Tasks upstream/origin problems on forks (auto PR routing + count cache)
**Status:** REPRODUCED (code-level for A + count-cache half of B/G; umbrella issue)
**Issue:** https://github.com/stablyai/orca/issues/8726
**Related:** #8727 (A fix), #8738 (B), #7331/#7513 (C)
**Malicious content:** none
## Summary (cluster)
Umbrella for Tasks source bugs most visible on forks (`origin` = fork, `upstream` = canonical).
### A — REPRODUCED: `auto` routes PRs to origin
- Issues `auto` → `getIssueOwnerRepo` = **upstream-first**.
- PRs → `resolvePrWorkItemSource`: only `preference === 'upstream'` leaves origin; **auto/undefined/origin all use origin**.
- Result: Upstream pill (issue heuristic) vs empty PRs list (`--repo <fork>`).
Existing tests already document this split (`uses upstream for issues and origin for PRs in mixed recent results` in `client-issue-source.test.ts`).
### B/G — REPRODUCED (cache half): count always `--cache 120s`
`countWorkItemsForQuery` hardcodes `gh api --cache 120s` and public `countWorkItems` has **no `noCache`** — source flips can serve stale totals up to 2 minutes (matches issue). Full stale-list toggle race needs renderer path (partial of B).
### C–F
Tracked elsewhere / product direction; not unit-proven in this pass.
## Automated proof
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/main/github/repro-8726-fork-pr-auto-origin.test.ts
```
+51
View File
@@ -0,0 +1,51 @@
# #8729 — Imported .codex-pet ignores per-frame durations (uniform 8 fps)
**Status:** REPRODUCED
**Issue:** https://github.com/stablyai/orca/issues/8729
**Malicious content:** do **not** run issue attachments; tests use synthetic data only
## Summary
Imported Codex pet bundles play every animation at sheet-wide **8 fps**. The idle
loop that should take ~6.6 s in Codex cycles in **0.75 s** (~9× too fast).
## Root cause (proven)
1. `applyCodexPetDefaults` (`src/main/ipc/pet-bundle.ts`) fills Codex layouts with:
```ts
export const CODEX_PET_DEFAULT_FPS = 8
// animations: { idle: { row: 0, frames: 6 }, ... } // no per-frame ms
fps: manifest.fps ?? CODEX_PET_DEFAULT_FPS
```
2. `SpriteAnimation` type only has `{ row, frames }` — no duration array.
3. `SpriteFrame` in `PetOverlay.tsx` plays:
```ts
const duration = Math.max(0.1, frames / Math.max(0.1, sprite.fps))
// animation: `… ${duration}s steps(${frames}) infinite`
```
Uniform fps → equal step time; Codex idle holds are uneven
`[1680, 660, 660, 840, 840, 1920]` ms.
## Automated proof (synthetic fixture only)
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/main/ipc/repro-8729-codex-pet-fps.test.ts
```
Assertions:
- Default manifest gets `fps === 8` and idle `{ row: 0, frames: 6 }` without duration fields
- Cycle math: Orca `6/8 * 1000 = 750` ms vs Codex sum `6600` ms ≈ **8.8×** faster
## Evidence paths
- `src/main/ipc/repro-8729-codex-pet-fps.test.ts`
- `src/main/ipc/pet-bundle.ts`
- `src/renderer/src/components/pet/PetOverlay.tsx` (`SpriteFrame`)
- `src/shared/types.ts` (`SpriteAnimation`)
+44
View File
@@ -0,0 +1,44 @@
# #8733 — `terminalMacOptionAsAlt` effectively resets in vim (compose layouts)
**Status:** REPRODUCED (code-level)
**Issue:** https://github.com/stablyai/orca/issues/8733
**Malicious content:** none
## Summary
French-PC layout, Option as Alt = **Off**. Shell composes correctly (e.g. AltGr+`'` → `{`). Opening **vim/neovim** makes Option send Meta sequences (`<M-'>`). Reload Orca without restarting vim “fixes” it until vim restarts.
## Root cause
When the pane’s app enables **kitty keyboard protocol**, `resolveTerminalShortcutAction` encodes **every** Option chord as CSI-u with the physical base key, for **any** `macOptionAsAlt` mode except `'true'`:
```ts
if (isMac && … && event.altKey && macOptionAsAlt !== 'true') {
if (event.key !== 'Dead' && isKittyKeyboardActivePane?.()) {
return { type: 'sendInput', data: `\x1b[${base};${mods}u` }
}
}
```
So user preference “Option as Alt = Off” is overridden for composition as soon as kitty is active (vim).
Reload “fix”: `TerminalKittyKeyboardModeTracker` notes SerializeAddon **does not serialize kitty flags**. Tracker restarts at 0; composition works until vim re-pushes CSI `> u`.
## Automated proof
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/renderer/src/components/terminal-pane/repro-8733-8399-kitty-option-compose.test.ts
```
## Suggested fix
- When `macOptionAsAlt === 'false'`, prefer composition (leave Option to Chromium) even if kitty is active, **or**
- Only kitty-encode Option for keys that are true TUI hotkeys when the layout is US-safe
- Optionally re-scan live PTY for kitty push after reload (separate correctness)
## Evidence
- `src/renderer/src/components/terminal-pane/repro-8733-8399-kitty-option-compose.test.ts`
- `src/renderer/src/components/terminal-pane/terminal-shortcut-policy.ts`
- `src/shared/terminal-kitty-keyboard-mode-tracker.ts`
+38
View File
@@ -0,0 +1,38 @@
# #8739 — Linear filters only show options from the first selected team
**Status:** REPRODUCED (code-level, conclusive)
**Issue:** https://github.com/stablyai/orca/issues/8739
**Malicious content:** none
## Summary
With multiple Linear teams selected (or All teams), Filters → status / assignee / label only list metadata from a single **primary** team. Other selected teams' states, labels, and members never appear.
## Root cause
1. `resolveLinearIssueAttributeFilterPrimaryTeam` returns **one** team: first selected by stable name/id sort (`linear-issue-attribute-filter-primary-team.ts`).
2. `LinearIssueAttributeFilterDropdowns` sets
`activeTeamId = primaryTeam?.id` only, then loads:
- `useTeamStates(activeTeamId, …)`
- `useTeamLabels(activeTeamId, …)`
- `useTeamMembers(activeTeamId, …)`
3. When `selectedTeamCount > 1`, UI even banners “Options from {{team}}” acknowledging the collapse — but does not union options.
There is no loop over `selectedTeamIds` for metadata.
## Evidence (automated)
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/renderer/src/components/repro-8739-linear-filter-first-team.test.ts
```
Proves:
- Two selected teams → only Backend (name-first) option ids returned; Frontend ids absent.
- All teams selected → still only primary team's options.
- Source contract: single `activeTeamId` from `primaryTeam`, three hooks, multi-team banner, no selection map.
## Expected fix direction
Union status/label/member options across every selected team (dedupe by id), or disable multi-team attribute filters with a clear empty state until one team is selected. Prune logic must not drop valid multi-team selections.
+54
View File
@@ -0,0 +1,54 @@
# #8742 — Antigravity CLI sessions not in AI Vault history
**Status:** REPRODUCED (code-level + schema parse)
**Issue:** https://github.com/stablyai/orca/issues/8742
**Malicious content:** none
## Summary
Antigravity is a supported CLI agent, but `agy` conversations never appear in Agent Session History (AI Vault).
## Root causes (both required)
### 1. Discovery path
`session-scanner-source-discovery.ts` only scans Gemini CLI:
```ts
const GEMINI_SESSIONS_DIR = join(homedir(), '.gemini', 'tmp')
// sessionRootDirs(…, ['.gemini', 'tmp'])
```
Real Antigravity layout (observed on host):
```text
~/.gemini/antigravity-cli/brain/<conversation-id>/.system_generated/logs/transcript.jsonl
```
No `antigravity-cli` / `agent: 'antigravity'` discovery root.
### 2. JSONL schema
`consumeGeminiMessage` only increments for `type === 'user' | 'gemini'`.
Antigravity records use e.g. `USER_INPUT`, `PLANNER_RESPONSE`, `GENERIC` with `created_at` (not Gemini’s `timestamp` / `user`/`gemini`).
Parser returns null or 0-message sessions for real transcripts.
## Automated proof
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/main/ai-vault/repro-8742-antigravity-not-scanned.test.ts
```
## Suggested fix
- Add discovery under `~/.gemini/antigravity-cli/brain/**/.system_generated/logs/transcript.jsonl` (agent `antigravity` or dedicated parse path).
- Parse Antigravity step types into user/assistant turns using `created_at` + `content`.
## Evidence
- `src/main/ai-vault/repro-8742-antigravity-not-scanned.test.ts`
- `src/main/ai-vault/session-scanner-source-discovery.ts`
- `src/main/ai-vault/session-scanner-gemini-parsers.ts`
+41
View File
@@ -0,0 +1,41 @@
# #8749 — macOS agent-session sandbox blocks claude-hook.sh (EPERM)
**Status:** NOT REPRODUCED
**Issue:** https://github.com/stablyai/orca/issues/8749
**Label:** `cannot_repro`
**Malicious content:** none
## Summary of claim
Reporter: Orca-hosted Claude Code sessions run under a seatbelt profile that denys `~/.orca` (and more), so Orca’s managed `~/.orca/agent-hooks/claude-hook.sh` fails with EPERM on every tool call. `sandbox_check` returned 1 inside their session.
## What we verified (production Orca + this tree)
1. **No seatbelt on daemon PTY path in code** — scan of `src/main/daemon/*.ts` finds no `sandbox-exec` / seatbelt / app-sandbox wiring for agent shells.
2. **Hooks do install under `~/.orca/agent-hooks/`** — `getSharedManagedScriptPath('claude-hook.sh')` (correct install location).
3. **Live probe** (packaged Orca, daemon PTY via `orca terminal create --command …` on cooked-PRs worktree):
- `sandbox_check 0`
- `ls ~/.orca` → `orca_dir_ok`
- `claude-hook.sh` executable; empty-stdin exit 0
4. Production daemon children (`login` + shell-ready bash) all `sandbox_check=0`. Only Chromium Helper processes use `--seatbelt-client`.
5. Entitlements on `/Applications/Orca.app` do **not** include `com.apple.security.app-sandbox`.
## Evidence (automated)
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/main/agent-hooks/repro-8749-hook-path-no-seatbelt.test.ts
```
## Live re-probe (non-destructive)
```bash
PROD="/Applications/Orca.app/Contents/Resources/bin/orca"
"$PROD" terminal create --worktree path:"$PWD" --title "repro-8749" \
--command 'python3 -c "import ctypes,os; lib=ctypes.CDLL(\"/usr/lib/libSystem.B.dylib\"); lib.sandbox_check.argtypes=[ctypes.c_int,ctypes.c_void_p,ctypes.c_uint32]; lib.sandbox_check.restype=ctypes.c_int; print(\"sandbox_check\", lib.sandbox_check(os.getpid(), None, 0))"; ls "$HOME/.orca" >/dev/null && echo orca_dir_ok; echo DONE' --json
# then: orca terminal read --terminal <handle> --limit 30 --json
```
## Notes
Reporter’s multi-machine seatbelt fingerprints may be Claude Code’s own tool sandbox, another host policy, or a different Orca build. This host/version cannot reproduce Orca-applied seatbelt on agent sessions. If reopened with a PTY pid + `sandbox_check` from an Orca daemon shell, re-investigate.
+29
View File
@@ -0,0 +1,29 @@
# #8752 — Setup-script prompt remains visible when orca.yaml setup hook is running
**Status:** REPRODUCED (code-level; shared renderer — not Linux-only)
**Issue:** https://github.com/stablyai/orca/issues/8752
**Label note:** issue tagged `os:linux` but root cause is cross-platform UI invalidation
**Malicious content:** none
## Summary
After `orca.yaml` gains a valid `scripts.setup` (and Orca successfully runs the setup hook), the sidebar can still show **“Add a setup script”**. Full remount / sidebar close-reopen clears it.
## Root cause
1. **Detection is correct** — `hasEffectiveSetupCommand` accepts shared `hooks.scripts.setup`.
2. **Inspection deps are incomplete** — `SetupScriptPromptCard` only re-runs on
`[activeRepo, inspectionRetryKey, isDismissed, settings, sidebarOpen]`.
No invalidation on `orca.yaml` changes, runtime reconnect/generation, or same-repo worktree activation.
3. **Stale retention** — `getRenderedSetupScriptPromptState` keeps `lastVisiblePrompt` for the same project while `promptState` is null (pending), so a negative result stays painted across same-project switches.
## Automated proof
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/renderer/src/components/sidebar/repro-8752-setup-script-prompt-stale.test.ts
```
## Suggested fix (from issue)
Invalidate inspection on orca.yaml / runtime reconnect / same-repo worktree activation; do not retain a known-stale negative across those boundaries.
+35
View File
@@ -0,0 +1,35 @@
# #8758 — node-pty is not available on this remote host (Linux SSH)
**Status:** REPRODUCED (code-level; same root as #8720)
**Issue:** https://github.com/stablyai/orca/issues/8758
**Related:** #8720
**Malicious content:** none
## Summary
SSH to Debian 13 succeeds; opening an integrated terminal fails with:
```text
node-pty is not available on this remote host
```
## Root cause
Identical deploy contract as **#8720**:
1. `installNativeDeps` writes package.json with `node-pty` + `@parcel/watcher` **without** `allowScripts`.
2. `npm install` has no script-approve flags.
3. On npm 12 Linux (no node-pty prebuild), lifecycle scripts are blocked but exit 0 → install “succeeds” → runtime `require('node-pty')` fails.
4. User-visible throw: `src/relay/pty-handler.ts` — `node-pty is not available on this remote host`.
## Automated proof
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/main/ssh/repro-8758-node-pty-remote.test.ts \
src/main/ssh/repro-8720-npm12-allow-scripts.test.ts
```
## Suggested fix
Same as #8720: emit `allowScripts` for native deps; fail deploy unless `require('node-pty')` succeeds.
+60
View File
@@ -0,0 +1,60 @@
# #8784 — GHE PR author avatars fail (login.png instead of avatar_url)
**Status:** REPRODUCED
**Issue:** https://github.com/stablyai/orca/issues/8784
**Related open fix PR:** https://github.com/stablyai/orca/pull/8831
**Malicious content:** none (screenshots only)
## Summary
On GitHub Enterprise, PR author/reviewer avatars 404 because Orca builds
`https://github.com/{login}.png` instead of using the API `avatar_url`.
## Root cause (proven)
1. `githubAvatarUrl(login)` in `src/renderer/src/components/github/github-issue-comment-helpers.ts` hardcodes:
```ts
return `https://github.com/${encodeURIComponent(login)}.png?size=64`
```
2. `PullRequestPage` author chip uses **only** the login:
```ts
src={githubAvatarUrl(workItem.author)}
```
3. `ReviewerAvatar` / `TaskPage` `ReviewChipAvatar` fall back to the same pattern when `avatarUrl` is empty:
```ts
avatarUrl || githubAvatarUrl(login)
// or: reviewer.avatarUrl || `https://github.com/${reviewer.login}.png?size=40`
```
Enterprise-only logins do not exist on github.com → 404. Successful avatars are those that already carry a full API `avatar_url`.
## Automated proof
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/renderer/src/components/github/repro-8784-ghe-avatar-fallback.test.ts
```
Assertions:
- Empty `avatarUrl` → `https://github.com/{login}.png`
- API `avatar_url` preferred when present
- Production `githubAvatarUrl` hardcodes github.com
- Source still contains login.png fallbacks and author path ignores avatar_url
## Fix note (#8831)
PR #8831 (open) carries `avatar_url` through work-item mappers and adds a shared
`GitHubUserAvatar` that prefers API URLs, with a failed-src retry so late enrichment recovers.
## Evidence paths
- `src/renderer/src/components/github/repro-8784-ghe-avatar-fallback.test.ts`
- `src/renderer/src/components/github/github-issue-comment-helpers.ts`
- `src/renderer/src/components/PullRequestPage.tsx` (author + ReviewerAvatar)
- `src/renderer/src/components/TaskPage.tsx` (ReviewChipAvatar)
+20
View File
@@ -0,0 +1,20 @@
# #8797 — Background Opacity / Window Blur no-op on macOS
**Status:** REPRODUCED
**Label:** `has_repro`
## Summary
On macOS, Window Blur and Background Opacity appear to do nothing. The window is created with `vibrancy` + `transparent: true` but also an always-opaque `backgroundColor` (`#0a0a0a` / `#ffffff`) that covers the blur layer. Terminal opacity only changes xterm’s rgba background, so it reveals that solid fill—not the desktop.
## Re-run
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/main/window/repro-8797-blur-opacity-noop.test.ts
```
## Manual
1. Settings → Terminal → Window → enable Blur, lower Background Opacity.
2. Observe: still fully opaque (and blur needs restart to even re-read the setting).
+101
View File
@@ -0,0 +1,101 @@
# #8808 — Agent recognition never recovers after runtime restart
**Status:** REPRODUCED (code-level, conclusive for env-prefixed relaunch)
**Issue:** https://github.com/stablyai/orca/issues/8808
**Related open fix PR:** https://github.com/stablyai/orca/pull/8942
**Malicious content:** none
## Summary
After an Orca runtime restart, worker PTYs survive as bare shells. Relaunching Claude inside the same terminal via `orca terminal send` (with env prefixes such as `CLAUDE_CONFIG_DIR=… claude …`) makes `tui-idle` succeed, but `orchestration dispatch --inject` still fails with “no recognized agent detected”. Creating a brand-new terminal with the agent as `--command` works immediately.
## Root cause (proven)
`isTerminalRunningAgent` (used by inject dispatch) falls through titles → ready prompt → stale `lastAgentStatus` → **foreground process recognition** via `recognizeAgentProcess` / command-line recognition.
When the agent was originally launched as the terminal command, launch metadata / OSC titles seed recognition. After runtime restart + in-shell relaunch:
1. Launch agent metadata may be gone or still reflect a shell.
2. Recognition often depends on the live foreground command line.
3. **Leading shell env assignments are not stripped**, so the first token is `CLAUDE_CONFIG_DIR=…` (or similar), not `claude`.
### Automated proof (this worktree)
```text
plain: { agent: 'claude', processName: 'claude' }
env-prefixed process: null
env-prefixed cmdline: null
plain cmdline: { agent: 'claude', processName: 'claude' }
```
Command:
```bash
pnpm exec tsx -e "
import {
recognizeAgentProcess,
recognizeAgentProcessFromCommandLine
} from './src/shared/agent-process-recognition.ts'
console.log('plain:', recognizeAgentProcess('claude'))
console.log('env-prefixed process:',
recognizeAgentProcess('CLAUDE_CONFIG_DIR=~/.claude_sub claude --model sonnet'))
console.log('env-prefixed cmdline:',
recognizeAgentProcessFromCommandLine(
'CLAUDE_CONFIG_DIR=~/.claude_sub claude --model sonnet'))
console.log('plain cmdline:',
recognizeAgentProcessFromCommandLine('claude --model sonnet'))
"
```
Saved: `docs/bug-reproductions/evidence/batch-repros-2026-07-15.txt`
## Code path
1. Inject gate — `src/main/runtime/rpc/methods/orchestration.ts`:
```ts
const hasAgent = await runtime.isTerminalRunningAgent(to)
// throws: "no recognized agent detected..."
```
2. Detection — `src/main/runtime/orca-runtime.ts` `isTerminalRunningAgent` → `isRecognizedForegroundAgentProcess` → `recognizeAgentProcess`.
3. Shared recognizer — `src/shared/agent-process-recognition.ts` maps process/command tokens to agents; **no leading `KEY=value` strip** on current `main` (PR #8942 adds that).
4. Related gap — #7797 (agents started inside existing shells / tmux). This issue’s env-prefix failure is a narrower, proven subset; #8942 intentionally does not claim full #7797 coverage.
## Why “create with --command” works but “send” does not
- `orca terminal create --command 'CLAUDE_CONFIG_DIR=… claude …'` sets launch agent / PTY command ownership so recognition does not depend solely on flattening the live cmdline.
- `orca terminal send --text 'CLAUDE_CONFIG_DIR=… claude …' --enter` only types into a surviving shell; inject then relies on title/foreground recognition. Env-prefixed cmdline recognition returns **null** → permanent “no recognized agent” until a new terminal is created.
## Unit tests already show shell + foreground *can* work
`src/main/runtime/orca-runtime.test.ts` cases such as “lets adopted neutral pane titles use non-shell foreground fallback” prove bash-launched PTYs are recognized when `getForegroundProcess` returns a bare agent name (`codex`, `claude`). The bug is specifically **unrecognized env-prefixed command lines**, matching the reporter’s relaunch string.
## Re-run steps
```bash
cd /Users/nwparker/orca/workspaces/orca/cooked-PRs
# Conclusive recognition proof (above tsx snippet)
pnpm exec vitest run --config config/vitest.config.ts \
src/shared/agent-process-recognition.test.ts \
src/main/providers/agent-foreground-process.test.ts
```
### Live (prod Orca) — optional full scenario
Binary: `/Applications/Orca.app/Contents/Resources/bin/orca`
1. Create worker with agent as command; dispatch inject successfully.
2. Cause runtime restart (CLI `_meta.runtimeId` rotates; agent dies, PTY shell remains).
3. `orca terminal send --terminal <h> --text 'CLAUDE_CONFIG_DIR=~/.claude_sub claude --model sonnet' --enter`
4. `orca terminal wait --terminal <h> --for tui-idle` → satisfied.
5. `orca orchestration dispatch … --inject` → still “no recognized agent detected”.
## Fix direction
PR #8942: strip contiguous leading `^[A-Za-z_][A-Za-z0-9_]*=` tokens before recognition (shared recognizer + provider tests). Not landed on this worktree’s `main` yet.
+25
View File
@@ -0,0 +1,25 @@
# #8813 — Visual status indicators within the project don’t appear
**Status:** DEFERRED (Windows-only live report)
**Issue:** https://github.com/stablyai/orca/issues/8813
**Malicious content:** none (Discord screenshot attachment not executed)
## Summary
Reporter (Windows 11, 1.4.142-rc.1): project/worktree cards show a yellow checkmark while agent still working; other status colors missing; CPU/memory inaccurate.
## Why deferred
- Environment is **Windows 11**; this worktree pass is macOS-only for live UI.
- Status colors use the same `StatusIndicator` / `use-worktree-activity-status` paths that unit tests cover for working/permission/done on the shared tree — no Windows-specific regression isolated without a host.
- CPU/memory on worktree cards depends on daemon resource snapshots / process attribution on Windows (foreground process scan).
## Re-run when Windows host available
1. Run agent to working state; confirm card should be yellow spinner (`working`), not done checkmark.
2. Compare hook status vs title-derived status on Windows.
3. Validate Resource Manager / per-worktree CPU samples against Task Manager.
## Label
Not applying `has_repro` / `cannot_repro` — needs Windows host (track in WINDOWS-ONLY).
+54
View File
@@ -0,0 +1,54 @@
# #8832 — Cmd-click URL opens with next line's text glued on
**Status:** REPRODUCED (unit test + code path)
**Platform:** all (renderer terminal links)
## Summary
Cmd-clicking a URL at the end of a line opens the browser with the first word of the **next** logical line appended (e.g. `…/orca/Description`).
## Root cause
`findHttpLinkAtBufferPosition` (`terminal-url-link-hit-testing.ts`) builds logical lines from:
1. Soft-wrapped rows (`isWrapped`)
2. Framed hard-wrapped HTTP candidates
3. **`buildCandidateLogicalLinesForBufferPosition`** — which includes **path** hard-wrap reconstruction from #8339
Path hard-wrap (`buildHardWrappedPathLogicalLineCandidates`) treats a URL suffix ending in `/` as a path start and joins the next row's path-like prefix (`Description:` from `Description: 123`). HTTP extraction then runs on that glued string and trims trailing `:` → `https://github.com/stablyai/orca/Description`.
Hard-wrap HTTP (`hard-wrapped-terminal-http-links.ts`) correctly requires a vertical frame and ≥3 rows for short URLs ending in `/`, so the plain two-line case is **not** from that path — it is path reconstruction reused for HTTP.
## Automated evidence
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/renderer/src/components/terminal-pane/repro-8832-url-next-line.test.ts
```
Expect 3 passing tests; the third asserts `openHttpLinkAtBufferPosition` opens
`https://github.com/stablyai/orca/Description` (not `…/orca/`).
## Manual repro (app)
1. Open a terminal in Orca.
2. Run:
```bash
printf 'Repo: https://github.com/stablyai/orca/\nDescription: 123\n'
```
3. Cmd-click (macOS) / Ctrl-click (Linux/Windows) the URL.
4. **Actual:** browser opens `https://github.com/stablyai/orca/Description`
**Expected:** `https://github.com/stablyai/orca/`
## Fix direction
Do not feed path hard-wrap logical lines into HTTP hit-testing without proving the continuation is still URL body (or stop path hard-wrap from starting on `https://…` schemes). Prefer only soft-wrap + framed HTTP hard-wrap for URLs.
## Key files
- `/Users/nwparker/orca/workspaces/orca/cooked-PRs/src/renderer/src/components/terminal-pane/terminal-url-link-hit-testing.ts` (`findHttpLinkAtBufferPosition`)
- `/Users/nwparker/orca/workspaces/orca/cooked-PRs/src/renderer/src/components/terminal-pane/wrapped-terminal-link-ranges.ts` (`buildHardWrappedPathLogicalLineCandidates`)
- `/Users/nwparker/orca/workspaces/orca/cooked-PRs/src/renderer/src/components/terminal-pane/hard-wrapped-terminal-path-fragments.ts`
- Repro test: `src/renderer/src/components/terminal-pane/repro-8832-url-next-line.test.ts`
+51
View File
@@ -0,0 +1,51 @@
# #8838 — `<br>` tags in markdown table cells not rendered
## Status
**NOT REPRODUCED** against current `MarkdownPreview` pipeline · labeled **`cannot_repro`** (worktree + production app.asar both use `rehypeRaw` + `rehypeSanitize`).
Pipeline proof shows the opposite of the issue report for raw HTML `<br>` / `<br/>`:
- With `rehypeRaw` (as in `MarkdownPreview.tsx`): real `<br>` elements are emitted in table cells.
- Without `rehypeRaw`: tags are **dropped**, not shown as literal `<br>` text.
- Entity-escaped source (`&lt;br/&gt;`) remains literal text (expected).
If users still see literal `<br>`, likely causes are escaped entities in the file, or a non-preview surface—not the current sanitize schema rejecting `br` (`defaultSchema` already allows `br`).
## Re-run
```bash
npx vitest run docs/bug-reproductions/scripts/repro-8838-br-in-table.test.ts
```
Regression / proof file:
- `docs/bug-reproductions/scripts/repro-8838-br-in-table.test.ts`
## Pipeline under test
Matches `src/renderer/src/components/editor/MarkdownPreview.tsx`:
```ts
remarkPlugins: [remarkGfm]
rehypePlugins: [rehypeRaw, [rehypeSanitize, markdownPreviewSanitizeSchema]]
```
(`markdownPreviewSanitizeSchema` extends `defaultSchema.tagNames` with details/summary/kbd/sub/sup/ins; `br` is already in `defaultSchema`.)
## Evidence snapshot
Rendered description cell (with raw):
```html
<td>Inspection verdict. Possible values:<br/><br/><code>normal</code> …</td>
```
- `has real <br>`: **true**
- `has &lt;br`: **false**
Remark AST for the sample includes `html: "<br/>"` nodes inside the table cell (GFM does parse the tags).
## Conclusion
Current code path does not reproduce “literal `<br>` text” for unescaped tags. Leave the vitest as a regression guard so a future pipeline regression fails CI-style when re-run manually.
+101
View File
@@ -0,0 +1,101 @@
# #8844 — `orca file open` reports ok:true for non-existent file
## Status
**REPRODUCED**
## Summary
`orca file open <missing-path> --json` returns `ok: true`, `opened: true`, exit `0` when the resolved worktree-relative path does not exist. A ghost editor tab is still created; content load later fails with `fs:readFile` / `ENOENT`.
## Re-run
**Default is non-destructive** (does not open editor tabs — avoids ENOENT ghost-tab spam):
```bash
bash docs/bug-reproductions/scripts/repro-8844-file-open-missing.sh
```
Live CLI open of a missing path (creates a ghost tab — **opt-in only**):
```bash
ORCA_REPRO_8844_LIVE=1 ORCA_BIN=/Applications/Orca.app/Contents/Resources/bin/orca \
bash docs/bug-reproductions/scripts/repro-8844-file-open-missing.sh
# Then Cmd+W the ghost tab, or write a placeholder file at the path it printed.
```
Evidence already captured:
- `docs/bug-reproductions/evidence/8844-file-open-missing.json.txt`
- `docs/bug-reproductions/evidence/8844-file-open-missing.raw.json`
## CLI evidence (production Orca)
```json
{
"ok": true,
"result": {
"worktree": "…::/Users/nwparker/orca/workspaces/orca/cooked-PRs",
"relativePath": "spec/does-not-exist-repro-8844.md",
"kind": "markdown",
"opened": true
}
}
```
- Exit code: `0`
- File exists before/after: **no**
## Ghost tab behavior
Observable only in a running Orca UI: the CLI RPC `files.open` always calls `host.openFile(...)`, which creates/activates an editor tab. Opening the tab surfaces:
> Unable to load file … `fs:readFile` … `ENOENT`
Agents that trust `ok`/`opened`/exit code are misled.
## Root cause (no existence check)
### CLI
`src/cli/handlers/file.ts` — `'file open'` calls `files.open` and prints success from the RPC payload (no local `stat`):
```139:146:src/cli/handlers/file.ts
'file open': async (ctx) => {
const relativePath = getRequiredStringFlag(ctx.flags, 'path')
const worktree = await getFileWorktreeSelector(ctx)
const result = await ctx.client.call<RuntimeFileOpenResult>('files.open', {
worktree,
relativePath
})
printResult(result, ctx.json, formatFileOpen)
},
```
### Main / runtime (authoritative)
`RuntimeFileCommands.openMobileFile` in `src/main/runtime/orca-runtime-files.ts`:
1. Resolves worktree
2. Classifies kind by **extension only** (markdown/text/image/binary)
3. For non-binary: `host.openFile(...)` then **`return { …, opened: true }`**
4. **Never** `stat`/`access`/`exists` on the absolute path
```440:469:src/main/runtime/orca-runtime-files.ts
async openMobileFile(
worktreeSelector: string,
relativePath: string
): Promise<RuntimeFileOpenResult> {
const { worktree } = await this.host.resolveRuntimeFileTarget(worktreeSelector)
// ...
const filePath = joinWorktreeRelativePath(worktree.path, relativePath)
this.host.openFile(worktree.id, filePath, relativePath, undefined)
return { worktree: worktree.id, relativePath, kind, opened: true }
}
```
RPC wiring: `src/main/runtime/rpc/methods/files.ts` (`files.open` → `openMobileFile`).
## Expected fix direction
Stat the resolved absolute path (local or remote) before reporting success; on missing file return `ok: false` with nonzero CLI exit and do not open a tab.
+67
View File
@@ -0,0 +1,67 @@
# #8864 — Agent history lookup hangs forever
**Status:** REPRODUCED (code-level hang conditions, conclusive)
**Issue:** https://github.com/stablyai/orca/issues/8864
**Reporter context:** OpenCode user; main process stuck on huge `opencode.db` (~29 GB) while Agent History open; UI spinner never clears. Reporter later noted OpenCode missing DB index; hang still shows Orca has no timeout/escape hatch.
**Malicious content:** none (Discord attachment is a screenshot only — not executed)
## Summary
Opening **Agent History** calls `window.api.aiVault.listSessions` → local scan → OpenCode SQLite discovery/parse. There is **no wall-clock timeout** on:
1. Opening `opencode.db` (`SyncDatabase` supports `timeout`, OpenCode paths omit it).
2. The session list SQL (correlated `COUNT(*)` + `json_extract` per session).
3. Local `listAiVaultSessions` / `cached-session-list` (no `Promise.race`).
4. Renderer refresh (awaits IPC; `loading` true until settle).
A slow or locked pathological DB keeps the main process busy and the panel spinner forever.
## Root cause
### OpenCode SQLite open (no timeout)
```ts
// session-scanner-opencode-sqlite-discovery.ts + session-scanner-opencode-sqlite.ts
new SyncDatabase(dbPath, { readonly: true, fileMustExist: true })
// SyncDatabase accepts timeout?: number → passed to DatabaseSync — unused here
```
### Expensive list query
Per-session correlated subquery:
```sql
(SELECT COUNT(*) FROM message m
WHERE m.session_id = s.id
AND json_extract(m.data, '$.role') IN ('user','assistant'))
FROM session s ORDER BY s.time_updated DESC LIMIT ?
```
Without indexes (or with multi-GB tables / WAL contention), this can run for minutes on the main thread path.
### No client/server deadline
- Local scope in `ipc/ai-vault.ts` returns `scanLocalAiVaultSessions` immediately (timeout only for multi-host runtime scans).
- `useAiVaultSessionRefresh` `await window.api.aiVault.listSessions(...)` with no `Promise.race`.
## Evidence (automated)
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/main/ai-vault/repro-8864-history-lookup-no-timeout.test.ts
```
Proves missing timeout on OpenCode open, expensive SQL shape, no local list deadline, no renderer timeout.
## User-reported live conditions (issue comments)
- `lsof` showed Orca holding multi-GB `opencode.db` read.
- Main ~20% CPU, trace frozen, UI unresponsive during history open.
- OpenCode event table bloat + missing index made queries pathological.
## Expected fix direction
- Pass SQLite busy timeout; consider size/mtime gate before scanning huge DBs.
- Avoid correlated `json_extract` COUNT on full message table (or use indexed columns / precomputed counts).
- Wrap listSessions in a wall-clock timeout → surface scan issue + stop spinner.
- Isolate heavy SQLite work off the critical main-thread path where possible.
+112
View File
@@ -0,0 +1,112 @@
# #8865 — Project Group hides member projects when Hide sleeping filters workspaces
**Status:** REPRODUCED (code-level + existing unit expectation of wrong behavior)
**Issue:** https://github.com/stablyai/orca/issues/8865
**Related open fix PR:** https://github.com/stablyai/orca/pull/8866
**Malicious content:** none
## Summary
With Project Groups enabled, enabling **Hide sleeping** removes every sleeping workspace card. If a grouped project’s *all* workspaces are sleeping (or otherwise filtered), the project header vanishes from its Project Group as well. Truly empty projects (no worktrees in the store) still get placeholders; filter-hidden non-empty projects do not.
## Root cause (proven)
Two layers disagree:
| Layer | Input | Behavior |
|-------|--------|----------|
| Row building | `visibleWorktrees` (post-filter) | Only groups repos that still have visible worktrees **or** appear in `placeholderRepoIds` |
| Placeholder selection | **raw** `worktreesByRepo` | Placeholder only if `worktreesByRepo[repo.id]?.length === 0` |
So a repo that still has sleeping worktrees in the store gets **no** placeholder, and after Hide sleeping removes all cards it also has **no** visible worktrees → disappears from the Project Group.
### Placeholder implementation
```4:24:src/renderer/src/components/sidebar/empty-project-placeholder-repos.ts
export function getEmptyProjectPlaceholderRepoIds(args: {
groupBy: WorktreeGroupBy
repos: readonly Repo[]
worktreesByRepo: Readonly<Record<string, readonly Worktree[] | undefined>>
filterRepoIds: readonly string[]
}): Set<string> {
// ...
if ((args.worktreesByRepo[repo.id]?.length ?? 0) === 0) {
placeholderRepoIds.add(repo.id)
}
}
```
### WorktreeList wires raw store map (not post-filter visible set)
```5708:5715:src/renderer/src/components/sidebar/WorktreeList.tsx
const placeholderRepoIds = useMemo(() => {
return getEmptyProjectPlaceholderRepoIds({
groupBy,
repos: visibleReposForRows,
worktreesByRepo, // full store, pre hide-sleeping
filterRepoIds
})
}, [filterRepoIds, groupBy, visibleReposForRows, worktreesByRepo])
```
While `visibleWorktrees` uses `computeVisibleWorktreeIds(..., { showSleepingWorkspaces, ... })` and is what `buildRows` consumes for actual cards.
### Existing unit test codifies the conflicting behavior
`src/renderer/src/components/sidebar/empty-project-placeholder-repos.test.ts`:
```ts
it('does not treat non-empty repos as empty when workspace filters hide their rows', () => {
expect(
getEmptyProjectPlaceholderRepoIds({
groupBy: 'repo',
repos: [repo],
worktreesByRepo: { [repo.id]: [worktree] }, // still "non-empty" in store
filterRepoIds: []
}).size
).toBe(0) // ← no placeholder even if filters hide the only worktree
})
```
Issue comment (bbingz) generalizes: any filter that hides all of a repo’s worktrees (Hide sleeping, hide-default-branch, hide-automation, host-scope) produces the same vanish; sleep is the common trigger.
### Inline model of the bug
```text
placeholder when all worktrees filtered but store non-empty: 0
→ no placeholder, project vanishes from group
truly empty repo gets placeholder: 1
```
## buildRows still supports empty grouped headers *when* placeholders exist
`worktree-list-groups.test.ts` includes cases:
- “keeps empty project groups visible in project grouping mode”
- “renders grouped repos before their visible worktrees are loaded” (via `placeholderRepoIds`)
- “does not resurrect filtered repos as empty Project Group headers” (filterRepoIds path)
The gap is specifically **workspace filters** (not explicit project filters) vs placeholders keyed on raw rows.
## Re-run steps
```bash
cd /Users/nwparker/orca/workspaces/orca/cooked-PRs
pnpm exec vitest run --config config/vitest.config.ts \
src/renderer/src/components/sidebar/empty-project-placeholder-repos.test.ts \
src/renderer/src/components/sidebar/worktree-list-groups.test.ts \
src/renderer/src/components/sidebar/visible-worktrees.test.ts
```
### Live UI
1. Create Project Group with ≥1 member project that has only sleeping workspaces.
2. Enable **Hide sleeping**.
3. Expected: project header remains under the group (even with no cards).
4. Actual: member project disappears entirely from the group.
## Fix direction
PR #8866: preserve Project Group members under workspace-level filters (placeholders / visible-layer fix). Treat filter-hidden non-empty repos like empty ones for **grouped** headers only; keep existing hide behavior for ungrouped projects.
+31
View File
@@ -0,0 +1,31 @@
# #8878 — Remote client resumes live host agent sessions (duplicate TUIs)
**Status:** REPRODUCED (code-level ownership gate gap, conclusive)
**Issue:** https://github.com/stablyai/orca/issues/8878
**Label:** `has_repro`
**Related fix PR (open):** #8887
**Malicious content:** none
## Summary
A paired remote client can run `resumeSleepingAgentSessionsForWorktree` for sessions that are still daemon-backed on the host. Resume guards only read client-local state; host session-tabs arrive asynchronously. Result: second TUI attached to the same provider session.
## Root-cause chain
| Step | Code | Behavior |
|------|------|----------|
| 1 | `worktree-activation.ts` / `Terminal.tsx` | Calls `resumeSleepingAgentSessionsForWorktree` unconditionally |
| 2 | `resume-sleeping-agent-session.ts` | No `isWebRuntimeSessionActive` short-circuit |
| 3 | Ownership guards | Client-local `tabsByWorktree` / claim maps only |
| 4 | `Terminal.tsx` auto-create | **Does** gate on `isWebRuntimeSessionActive` — resume path misses the same gate |
## Evidence (automated)
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/renderer/src/lib/repro-8878-remote-resume-no-runtime-gate.test.ts
```
## Expected fix direction
Skip resume for runtime-owned worktrees at the choke point (same predicate as terminal auto-create). Host keeps wake authority; local/SSH/WSL unaffected. See PR #8887.
+15
View File
@@ -0,0 +1,15 @@
# #8881 — Sidebar duplicates worktrees after re-pair
**Status:** REPRODUCED
**Label:** `has_repro`
## Summary
Re-pairing a remote host mints a new `runtime:<env>` host id. Worktree rows are keyed `${repoId}::${path}` and `mergeWorktreesForHost` only replaces the *current* host’s rows — superseded runtime host rows remain. Same physical paths appear 2–3×. SSH has readoption reconcile; runtime re-pair does not.
## Re-run
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/renderer/src/store/slices/repro-8881-runtime-repair-duplicates.test.ts
```
+78
View File
@@ -0,0 +1,78 @@
# #8903 — Cmd-J jump palette leaves keyboard focus on the wrong surface
**Status:** REPRODUCED (code-level unit + source path; full UI animation race not automated)
**Platform:** all (DOM focus; macOS/Linux/Windows)
## Summary
After Cmd-J workspace jump (Enter) or cancel (Esc), keyboard focus often lands on a **hidden** workspace’s terminal or nowhere useful — hollow cursor / no typing until click.
## Root cause (code path)
### Destination jump (Enter)
`WorktreeJumpPalette.handleSelectWorktree`:
1. `activateAndRevealWorktree(worktreeId)`
2. `skipRestoreFocusRef.current = true` (skips Esc restore path)
3. `closeModal()`
4. **`focusFallbackSurface()` with no preferred element**
`focusFallbackSurface` double-rAF then:
```ts
resolvePaletteFocusRestoreTarget(preferredTarget ?? null)?.focus({ preventScroll: true })
```
With `preferredTarget == null`:
```ts
// palette-focus-restore-target.ts
const xterm = doc.querySelector('.xterm-helper-textarea') // FIRST in document order
```
Inactive workspaces stay mounted (`display:none`), so the first `.xterm-helper-textarea` is often a **background** worktree’s terminal — focus lands there while the destination looks active.
### Esc cancel
`handleOpenChange(false)` calls `focusFallbackSurface(previousFocusElementRef.current)`. If that element is still connected, focus is correct; if not, same first-DOM fallback.
### Related fallback
`focusTerminalTabSurface` also ends with unscoped `document.querySelector('.xterm-helper-textarea')` when tab-scoped helpers are missing (`focus-terminal-tab-surface.ts`).
Issue author’s diagnosis matches this path (generic first-xterm fallback + mounted hidden workspaces + rAF race with modal teardown).
## Automated evidence
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/renderer/src/components/cmd-j/repro-8903-cmdj-focus-fallback.test.ts
```
- Two xterms (hidden first, visible second) → `resolvePaletteFocusRestoreTarget(null)` returns **hidden**
- Source asserts: `skipRestoreFocusRef` + bare `focusFallbackSurface()` + unscoped `querySelector`
Existing: `src/renderer/src/components/cmd-j/palette-focus-restore-target.test.ts`
## Manual UI steps remaining
1. Open ≥2 workspaces, each with a terminal (or editor).
2. Focus a terminal in workspace A; type to confirm solid cursor.
3. **Cmd-J** → select workspace B → **Enter**.
4. Without clicking, type.
5. **Actual:** hollow cursor / keys go nowhere (or wrong pane). **Expected:** focus on B’s active surface.
6. Also: Cmd-J → **Esc** and confirm focus returns to the surface you left (flaky if preferred element disconnected).
E2e: no dedicated Cmd-J focus e2e found; many e2e suites only mark `cmd-j-palette` tip seen. Closest utilities: `focus-terminal-tab-surface.test.ts`, e2e helpers querying `.xterm-helper-textarea`.
## Fix direction (per issue)
Route focus from **store state** (destination worktree + active tab + active leaf) via scoped selectors (`[data-terminal-tab-id=…] [data-leaf-id=…] .xterm-helper-textarea` / editor / browser focus events), not document-order `querySelector`. Prefer `focusTerminalTabSurface(tabId, leafId)` after jump instead of bare `focusFallbackSurface()`.
## Key files
- `src/renderer/src/components/WorktreeJumpPalette.tsx` — `handleSelectWorktree`, `focusFallbackSurface`
- `src/renderer/src/components/cmd-j/palette-focus-restore-target.ts`
- `src/renderer/src/lib/focus-terminal-tab-surface.ts`
- Repro test: `src/renderer/src/components/cmd-j/repro-8903-cmdj-focus-fallback.test.ts`
+26
View File
@@ -0,0 +1,26 @@
# #8934 — Jira issue images do not render in Tasks drawer
**Status:** REPRODUCED (code-level ADF conversion)
**Issue:** https://github.com/stablyai/orca/issues/8934
**Malicious content:** none
## Summary
Jira stores pasted screenshots as ADF `media` / `mediaSingle` / `mediaInline` nodes (Media Service IDs, not public URLs). Orca converts ADF → Markdown via `adfToMarkdownText` then renders with `CommentMarkdown`. Media nodes are unhandled → **silently dropped**. `ISSUE_FIELDS` also omits `attachment`, so there is no authenticated attachment download on `getIssue`.
## Automated proof
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/main/jira/repro-8934-adf-media-dropped.test.ts
```
Proves:
- ADF doc with mediaSingle + mediaInline keeps surrounding text only
- No `![...]`, media UUID, or data-URL image in output
- Source has no media handlers; ISSUE_FIELDS has no `attachment`
## Suggested fix (from issue)
ADF media → markdown images; Basic-auth download via `/rest/api/3/attachment/content/{id}`; map media UUIDs via attachment order/filename; size limits; lightbox Esc must not dismiss drawer.
+73
View File
@@ -0,0 +1,73 @@
# #8935 — GHE PR work item diffs do not load
**Status:** REPRODUCED
**Issue:** https://github.com/stablyai/orca/issues/8935
**Related open fix PR:** https://github.com/stablyai/orca/pull/8932
**Malicious content:** none
## Summary
For GitHub Enterprise Server remotes, PR work items open with metadata but the
file diff / contents view stays empty because API calls never target the Enterprise host.
## Root cause (proven)
1. **`parseGitHubOwnerRepo` is github.com-only**
(`src/main/github/github-remote-identity-parsing.ts`):
```ts
if (!identity || identity.host.toLowerCase() !== 'github.com') {
return null
}
```
Enterprise remotes parse as full identities via `parseGitHubRemoteIdentity`, but
`getOwnerRepo` only uses `parseGitHubOwnerRepo` → **null**.
2. **`getPRFiles` / `getPRFileContents` require ownerRepo**
(`src/main/github/work-item-details.ts`):
```ts
if (!ownerRepo) {
return null // getPRFiles
}
// getPRFileContents → empty original/modified strings
```
3. **No Enterprise host routing on this path**
Source does not call `getEnterpriseGitHubRepoSlug` and never passes
`gh --hostname <enterprise-host>`. Even when `pr view` fallbacks run without
owner/repo, file listing still needs the API path with a host.
4. Downstream: `getWorkItemDetails` sets `filesUnavailable: files === null` so the
Files tab shows unavailable/empty instead of real diffs.
## Automated proof
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/main/github/repro-8935-ghe-pr-diff-host.test.ts
```
Assertions:
- `parseGitHubOwnerRepo` null for GHE URLs; identity still has host/owner/repo
- With `getOwnerRepo → null`, `getWorkItemDetails` → `filesUnavailable: true`
- `getPRFileContents` returns empty without calling `gh`
- Source lacks `getEnterpriseGitHubRepoSlug` and `--hostname`
Related baseline: `src/main/github/gh-utils.test.ts` already expects
`parseGitHubOwnerRepo('https://ghe.acme.internal/acme/orca.git')` → null.
## Fix note (#8932)
PR #8932 (open) routes PR detail `gh api` calls through `getEnterpriseGitHubRepoSlug`
when github.com owner/repo is unavailable, always adding `--hostname`, and encodes
content paths by segment.
## Evidence paths
- `src/main/github/repro-8935-ghe-pr-diff-host.test.ts`
- `src/main/github/github-remote-identity-parsing.ts`
- `src/main/github/work-item-details.ts` (`getPRFiles`, `getPRFileContents`, `getWorkItemDetails`)
- `src/main/github/github-enterprise-repository.ts` (exists but unused by work-item-details today)
+56
View File
@@ -0,0 +1,56 @@
# #8940 — opencode displayed as Claude Code
**Status:** REPRODUCED (unit test + labeling heuristics)
**Platform:** all (title / sidebar identity)
## Summary
OpenCode sessions are sometimes shown as **Claude Code** in the sidebar and flaky on tabs (OpenCode ↔ Claude Code). Discord report: most OpenCode tabs mislabeled; only the real Claude tab is correct.
## Root cause
Identity comes primarily from **terminal titles** via `getAgentLabel` / `isClaudeAgent`:
| Title shape | Result |
|-------------|--------|
| `. …` / `* …` / `✳ …` prefixes | Always **Claude Code** (even if task text says “opencode”) |
| Braille spinner + task, no agent token | **Claude Code** (`isClaudeAgent` excludes Cursor/OpenClaude only, **not** OpenCode) |
| Token `opencode` / synthetic `OpenCode` / `⠋ OpenCode` | Correct **OpenCode** |
Hooks correctly set `agentType: 'opencode'` on `/hook/opencode`, and Orca injects synthetic titles (`OpenCode` / `OpenCode ready` / spinner + `OpenCode`) when hook state drives them. When native OSC titles overwrite those frames without an `opencode` token, the **activity** facet (`getAgentLabel`) flips to Claude Code — matching tab flakiness.
Sidebar title-derived rows partially mitigate Claude false positives (`resolveTitleDerivedAgentType` requires a `claude` token), but tab titles and any consumer of `getAgentLabel` still show “Claude Code”.
## Automated evidence
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/shared/repro-8940-opencode-as-claude.test.ts
```
Pins:
- `. Compare Opencode Vs Orca` → Claude Code
- `⠋ implementing the feature` → Claude Code / `isClaudeAgent === true`
- `⠋ OpenClaude` excluded; bare OpenCode-like braille tasks are not
- Synthetic `⠋ OpenCode` vs native `⠋ fix the auth bug` flip
## Manual repro (app)
1. Launch several **OpenCode** agent tabs in Orca (with hooks working).
2. Watch tab titles while OpenCode is working vs idle.
3. When OpenCode’s own OSC title is a braille + task string without the word `opencode`, Orca labels it Claude Code.
4. When synthetic idle/working frames re-apply, the label becomes OpenCode again.
## Fix direction
- Treat process/`launchAgent`/hook `agentType: opencode` as identity authority over generic Claude prefixes.
- In `isClaudeAgent` braille branch, exclude titles already owned by other agents (or require stronger Claude signals).
- Prefer `resolveExplicitTerminalTitleAgentType` (already rejects bare Claude prefixes) for display identity, not only for some sidebar paths.
## Key files
- `src/shared/agent-title-identity.ts` / `src/shared/terminal-title-agent-type.ts` — `getAgentLabel`, `isClaudeAgent`
- `src/shared/synthetic-agent-title.ts` — OpenCode synthetic profiles
- `src/renderer/src/components/sidebar/worktree-title-derived-agent-rows.ts`
- Repro test: `src/shared/repro-8940-opencode-as-claude.test.ts`
+24
View File
@@ -0,0 +1,24 @@
# #8943 — Browser shows insecure
**Status:** NOT REPRODUCED (screenshot-only; no false-positive path found)
**Issue:** https://github.com/stablyai/orca/issues/8943
**Label:** `cannot_repro`
**Malicious content:** issue body is only an image attachment — **not downloaded/opened** per security policy.
## What we know
- No repro steps, URL, or description beyond “When I try to open the browser, getting this issue” + screenshot.
- Main `BrowserAddressBar` has **no** secure/insecure lock chrome (Globe/Search only).
- Popup origin bar intentionally shows **Not secure** for non-loopback `http://` (`describePopupOrigin`).
- Guest webPreferences: `allowRunningInsecureContent: false` (mixed-content block is intentional).
## Evidence (automated)
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/main/browser/repro-8943-browser-insecure-indicator.test.ts
```
## Conclusion
Cannot map the screenshot to a product defect without analyzing the attachment or a clearer repro (URL, whether main browser vs popup, cert error vs “Not secure” badge). If the user opened plain `http://` remote content in a popup, “Not secure” is expected UX.
+128
View File
@@ -0,0 +1,128 @@
# #8962 — OMP terminals excluded from cold session restoration
**Status:** REPRODUCED (code-level / unit-level, conclusive)
**Issue:** https://github.com/stablyai/orca/issues/8962
**Malicious content:** none (technical bug report only)
## Summary
OMP posts agent-status hooks through the managed Pi-compatible extension, but Orca never records an OMP provider session ID. Cold restore therefore cannot build `omp --resume <id>` and falls back to an unresumed shell. Claude/Codex/etc. already go through the sleeping-session + cold-resume path.
## Evidence
### 1. `omp` is not a resumable TUI agent
`src/shared/agent-session-resume.ts`:
```ts
export const RESUMABLE_TUI_AGENTS = [
'claude', 'codex', 'gemini', 'antigravity',
'opencode', 'mimo-code', 'droid', 'grok', 'devin'
] as const
```
Runtime probe (this worktree):
```text
isResumableTuiAgent(omp): false
extractAgentProviderSession('omp', { session_id: 'abc-123' }): null
getAgentResumeArgv('omp', { key: 'session_id', id: 'abc-123' }): undefined
```
Saved: `docs/bug-reproductions/evidence/batch-repros-2026-07-15.txt`
### 2. Extract path hard-returns null for OMP
```147:192:src/shared/agent-session-resume.ts
export function extractAgentProviderSession(
source: AgentHookSource,
payload: Record<string, unknown>
): AgentProviderSessionMetadata | null {
switch (source) {
// ... claude/codex/gemini/... cases extract session ids ...
case 'amp':
case 'cursor':
case 'pi':
case 'omp':
case 'command-code':
case 'copilot':
case 'hermes':
return null
}
}
```
`getAgentResumeArgv` has no `omp` arm either (only the `RESUMABLE_TUI_AGENTS` set).
### 3. Sleeping-session capture requires resumable + resume argv
`src/renderer/src/store/slices/agent-status.ts` only creates a `SleepingAgentSessionRecord` when:
- agent is resumable (`isResumableTuiAgent`)
- entry has `providerSession`
- `getAgentResumeArgv(agent, providerSession)` succeeds
With (1)+(2), OMP never produces a sleeping record for cold restore.
### 4. OMP hooks never post `session_id`
Generated handlers in `src/main/pi/agent-status-handler-source.ts` register events without the extension `ctx` argument and only forward prompt/tool/message fields:
```ts
pi.on('before_agent_start', (event) => {
post('before_agent_start', { prompt: event.prompt ?? '' })
})
```
No `session_id: ctx.sessionManager.getSessionId()`. Even if OMP were added to `RESUMABLE_TUI_AGENTS`, the payload would still lack the ID today.
### 5. Unit suite confirms non-OMP agents only
`src/shared/agent-session-resume.test.ts` covers claude/codex/gemini/antigravity/opencode/mimo-code/droid/grok/devin — not omp. `pi` is explicitly rejected; omp is in the same null-return group.
## Why cold restore fails
Pipeline (issue-accurate, verified in tree):
1. Hook listener → `extractAgentProviderSession` → attach to status row
2. `collectSleepingAgentSessionRecordsForWorktree` → needs resumable + providerSession + resume argv
3. `pty-connection.ts` cold path consumes sleeping records to rebuild startup command
OMP is blocked at every input. Warm reattachment still works (no new agent process); cold restore (daemon kill / crash / no reattach) cannot resume.
## Re-run steps
```bash
cd /Users/nwparker/orca/workspaces/orca/cooked-PRs
# Automated proof
pnpm exec tsx -e "
import {
RESUMABLE_TUI_AGENTS,
extractAgentProviderSession,
isResumableTuiAgent
} from './src/shared/agent-session-resume.ts'
console.log(RESUMABLE_TUI_AGENTS)
console.log(isResumableTuiAgent('omp'))
console.log(extractAgentProviderSession('omp', { session_id: 'abc-123' }))
"
pnpm exec vitest run --config config/vitest.config.ts src/shared/agent-session-resume.test.ts
```
### Live UI (optional, production Orca)
Binary: `/Applications/Orca.app/Contents/Resources/bin/orca`
1. Open a terminal, launch `omp`, send a prompt so a session is persisted.
2. Force cold restore (terminate PTY daemon / quit without warm reattach).
3. Reopen workspace terminal.
4. Expect: no OMP sleeping record; terminal does not start with `omp --resume <id>`.
## Fix direction (from issue; not implemented here)
1. Handler source: accept `ctx`, attach `session_id` on every post.
2. Add `'omp'` to `RESUMABLE_TUI_AGENTS`; extract `session_id`; `getAgentResumeArgv` → `['omp', '--resume', id]`.
3. Keep `pi` unsupported unless CLI resume is verified.
4. Tests: hook payload, extract/resume round-trip, quit capture, warm-no-duplicate.
+123
View File
@@ -0,0 +1,123 @@
# #8970 — Closed agent session still lingers in sidebar (after #8825 / #8851)
**Status:** PARTIALLY REPRODUCED (code residual confirmed; happy-path unit tests pass; full live UI not run)
**Issue:** https://github.com/stablyai/orca/issues/8970
**Related:** #8851 (closed sessions linger), #8825 (merged — reap finished Claude named agents/teammates)
**Malicious content:** none
## Summary
Reporter on **v1.4.143** (claims #8825 included): after closing a Claude agent terminal with **Ctrl+W**, the worktree card’s inline agent list still shows the session and the “N agents” count stays high. Only **Cmd+Q + relaunch** clears it.
#8825 fixed **named subagent/teammate child rows** that stayed idle forever under a parent pane. This report is a **top-level** conversation closed via Ctrl+W — either incomplete coverage for that class or a residual race/rehydrate path.
## What the intended cleanup path does
User close on a terminal routes through `closeTerminalTab` → `closeTab` → **`dropAgentStatusByTabPrefix`**:
```1482:1497:src/renderer/src/store/slices/terminals.ts
// Why: sweep live AND retained agent-status entries for this tab ...
get().dropAgentStatusByTabPrefix(
tabId,
closingWorktreeId ? { worktreeId: closingWorktreeId } : undefined
)
get().clearPaneForegroundAgentByTabPrefix(tabId)
```
`dropAgentStatusByTabPrefix` (`agent-status.ts`):
- Deletes live `agentStatusByPaneKey` rows with `${tabId}:` prefix (+ completed worktree-attributed orphans for that worktree).
- Deletes retained + launch configs for those keys.
- Plants retention suppressors for live keys (anti race with `collectRetainedAgentsOnDisappear`).
- Marks `recentlyClosedAgentStatusTabIds` so late hook replays are ignored.
- IPC `agentStatus:dropByTabPrefix` to main last-status cache.
Unit coverage:
- `agent-status-drop.test.ts` — `closeTab drops completed worktree-attributed orphan rows`
- `agent-status-drop-ipc.test.ts` — drop IPC fan-out + recently-closed set
So the **happy path is implemented and unit-tested**. The bug is not “no cleanup function exists.”
## Residual / incomplete paths that still match the report
### 1. Explicit residual from #8825 (still in tree)
PR #8825 scope was **only** `claude-subagent-roster` + hook listener (working-only children). Known residual:
> A pane whose PTY teardown skips `clearPaneState`, or a persisted last-status snapshot with a stale working child, can still render its last snapshot until a new event or eviction.
Restart clears in-memory + rehydrates last-status with idle-child prune — matches reporter’s “only restart fixes it.”
### 2. Sidebar rows can exist without a live tab
`buildWorktreeAgentRows` / selectors intentionally keep:
- **Worktree-attributed live** entries when `entry.worktreeId` is set and state is **not** `done`, even if the tab is missing (`tabFromWorktreeAttributedStatusEntry` synthesizes a tab).
- **Retained done** rows (`rowSource: 'retained'`) without open tabs.
If drop misses a paneKey prefix, FIFO-evicts `recentlyClosedAgentStatusTabIds` (cap **1024**), or main re-pushes status before/without successful drop IPC, rows reappear until restart.
### 3. Subagent children inflate “N agents”
`WorktreeCardAgents` count uses `agents.length` (and lineage root count), which includes **subagent child rows**. #8825 changed roster to working-only + SubagentStop removes children, but any residual idle/working child snapshot still inflates the count without a separate terminal.
### 4. Ctrl+W closes via pane path, not Terminal.tsx tab handler
Terminal-focused Ctrl+W is handled as `closeActivePane` in `keyboard-handlers.ts` → `onRequestClosePane` → lifecycle → `closeTerminalTab` when last pane. That should still hit `closeTab`/`drop*`. No alternate “close without drop” was found for the primary path — residual is more likely **rehydrate/race/orphan keys** than a total missing call.
## Automated evidence gathered
| Check | Result |
|-------|--------|
| closeTab calls dropAgentStatusByTabPrefix | yes (source) |
| drop removes live+retained+IPC | yes (source + unit tests) |
| recentlyClosed suppresses setAgentStatus | yes |
| #8825 residual acknowledged | yes (PR body) |
| Live Ctrl+W in prod Orca | **not run** (UI session) |
## Classification rationale
- **Not NOT_REPRODUCED**: residual paths and reporter scenario are real; #8825 did not claim top-level tab-close perfection.
- **Not fully REPRODUCED**: happy-path unit tests pass; we did not capture a failing automated case that leaves a top-level row after `closeTab('user')`.
- **PARTIALLY REPRODUCED**: residual class confirmed; full intermittent live repro needs UI.
## Re-run steps
### Unit (cleanup happy path)
```bash
cd /Users/nwparker/orca/workspaces/orca/cooked-PRs
pnpm exec vitest run --config config/vitest.config.ts \
src/renderer/src/store/slices/agent-status-drop.test.ts \
src/renderer/src/store/slices/agent-status-drop-ipc.test.ts \
src/shared/claude-subagent-roster.test.ts
```
### Live UI (prod Orca) — needed for full REPRODUCED
Binary: `/Applications/Orca.app/Contents/Resources/bin/orca` / app UI
1. Single-worktree project; launch Claude in a terminal (conversation with a first-prompt title).
2. Confirm sidebar inline agent list shows 1+ agent.
3. Close terminal with **Ctrl+W** (confirm dialogs if any).
4. Observe whether agent row / “N agents” remain.
5. Compare `orca terminal list --worktree … --json` / `orca worktree ps --json` (runtime clean, sidebar dirty).
6. Restart Orca — row gone.
### Instrumentation if live repros
- Log `closeTab` reason + `dropAgentStatusByTabPrefix` keys removed.
- Diff `agentStatusByPaneKey` / `retainedAgentsByPaneKey` / subagents before/after close.
- Check main last-status after drop IPC.
- Note whether row is live, retained, title-derived, or subagent.
## Fix direction (hypotheses for implementers)
1. Ensure every tab-retirement path (including remote/parked) always drops agent status **and** main last-status.
2. Harden against post-drop rehydrate (recentlyClosed + main drop ordering).
3. Do not show worktree-attributed non-done rows for paneKeys whose tab was user-closed.
4. Confirm #8825 working-only roster covers reporter’s Claude version of named agents.
5. Optional: dismiss affordance on stale sidebar rows (issue #8851 noted no per-session dismiss).
+42
View File
@@ -0,0 +1,42 @@
# #8974 — Claude usage stuck on “Refreshing sign-in”
**Status:** REPRODUCED (UI stuck-state mapping; live OAuth env not required)
**Issue:** https://github.com/stablyai/orca/issues/8974
**Malicious content:** none
## Summary
Claude Code works in-terminal, but usage UI stays on **Refreshing sign-in** and never shows live percentages.
## Root cause
`usage-error-copy.ts` maps these Claude `failureKind` values to permanent refreshing copy:
- `stale-token`
- `refreshable-credentials-without-token`
- `delegated-refresh-required`
Message: *“Claude sign-in is being refreshed. Agent sessions may still be signed in.”*
There is **no age/timeout escalation** in the label path (never becomes “Sign-in failed” / re-auth CTA based on `updatedAt`). Main fetcher can keep emitting stale-token / deferred-by-live-session while the CLI session remains usable (separate credential surface).
`applyStalePolicy` may retain previous quota windows under `status: 'error'` with merged failureKind — chip still shows refreshing.
## Automated proof
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/renderer/src/components/status-bar/repro-8974-refreshing-sign-in-stuck.test.ts
```
## Suggested fix
- After N failures or T minutes, escalate copy to re-authenticate / usage unavailable.
- Surface distinct CTA when agent sessions are healthy but usage OAuth is dead.
- Ensure CLI-repair / keychain refresh completion clears failureKind.
## Evidence
- `src/renderer/src/components/status-bar/repro-8974-refreshing-sign-in-stuck.test.ts`
- `src/renderer/src/components/status-bar/usage-error-copy.ts`
- `src/main/rate-limits/claude-fetcher.ts`
+52
View File
@@ -0,0 +1,52 @@
# #8986 — OMP tab agent status flashes; ghost agent in sidebar
**Status:** REPRODUCED (status flash, code-level); ghost list **related residual** (same surfaces as #8970)
**Issue:** https://github.com/stablyai/orca/issues/8986
**Malicious content:** none
## Summary
1. OMP terminal tab status badge thrashing (working/idle).
2. Sidebar shows more agents than open tabs; ghost cannot focus/delete reliably.
## Root cause — status flash
OMP profile (unlike Codex) **drives synthetic working titles** from hooks:
```ts
shouldDriveSyntheticAgentTitleFromHook('omp', 'working') === true // codex: false
```
Main fabricates braille spinner OSC titles (`⠋ OMP`). Native / collapsed titles are bare `OMP` / `OMP ready`.
`detectAgentStatusFromTitle` / pi-compatible helper:
| Title | Status |
|-------|--------|
| `⠋ OMP` / braille + OMP | working |
| `OMP ready` / bare `OMP` | idle |
Competing synthetic spinner vs ready/bare frames → tab status thrash under normal agent activity.
## Root cause — ghost agent (partial)
Sidebar builds from live `agentStatusByPaneKey` + retained rows + worktree-attributed entries that can **synthesize a tab** when missing. Dismiss calls `dropAgentStatus` + `dismissRetainedAgent`, but rehydrate / late hook / worktree-attributed live keys can repopulate rows without a focusable tab (same class as #8970 residuals).
## Automated proof (flash)
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/shared/repro-8986-omp-status-flash.test.ts
```
## Suggested fix
- Match Codex: `synthesizeWorkingTitle: false` for OMP, **or** collapse all OMP working frames to a stable title before status detect.
- Ghost: ensure dismiss plants suppressors for worktree-attributed keys; refuse non-focusable rows or force drop-by-worktree.
## Evidence
- `src/shared/repro-8986-omp-status-flash.test.ts`
- `src/shared/synthetic-agent-title.ts`
- `src/shared/agent-title-status.ts`
- `src/shared/pi-compatible-synthetic-title.ts`
@@ -0,0 +1,27 @@
# Fix orchestration pass — 5 easiest has_repro bugs
**Date:** 2026-07-16
**Agent:** GPT-5.6-Sol (worktree-isolated)
**Worktrees:** Orca CLI (`orca worktree create`)
## Results
| Issue | PR | Worktree | Status |
|------:|----|----------|--------|
| [#8533](https://github.com/stablyai/orca/issues/8533) Cmd+Shift+E collision | [#9007](https://github.com/stablyai/orca/pull/9007) | `fix-8533-cmd-shift-e` | Open |
| [#8584](https://github.com/stablyai/orca/issues/8584) Mod+0 collision | [#9003](https://github.com/stablyai/orca/pull/9003) | `fix-8584-mod-0` | Open |
| [#8378](https://github.com/stablyai/orca/issues/8378) Codex reset time | [#9004](https://github.com/stablyai/orca/pull/9004) | `fix-8378-codex-reset-time` | Open |
| [#8844](https://github.com/stablyai/orca/issues/8844) file open missing path | [#9006](https://github.com/stablyai/orca/pull/9006) | `fix-8844-file-open-exists` | Open |
| [#8535](https://github.com/stablyai/orca/issues/8535) serve port pin | [#9005](https://github.com/stablyai/orca/pull/9005) | `fix-8535-serve-port-pin` | Open |
## Fix summary
1. **#8533** — Emulator default `Mod+Shift+E` → `Mod+Alt+Shift+E`; Explorer keeps `Mod+Shift+E`.
2. **#8584** — Focus worktree list `Mod+0` → `Mod+Shift+0`; zoom stays `Mod+0`.
3. **#8378** — Status bar chip uses remaining time when `resetsAt` present (matches popup).
4. **#8844** — `openMobileFile` stats path before open; missing → error, no ghost tab.
5. **#8535** — Explicit CLI `--port` binds pin first; default desktop keeps fallback-first (STA-1511).
## PR template requirement
Each PR includes Description, Evidence, ELI5, User-regression-tradeoffs.
+60
View File
@@ -0,0 +1,60 @@
# Bug reproduction progress log
Living tracker for the orchestration pass. **Index of results:** [README.md](./README.md).
**Windows deferred:** [WINDOWS-ONLY.md](./WINDOWS-ONLY.md).
| Field | Value |
|-------|-------|
| Started | 2026-07-15 |
| Updated | 2026-07-16 |
| Host | macOS · production Orca 1.4.143 |
| Worktree | `cooked-PRs` |
| Constraint | No live `orca file open` of missing paths (ghost ENOENT tabs) |
| GitHub labels | **`has_repro`** · **`cannot_repro`** |
---
## Final snapshot (Pass 3 complete)
| Bucket | Count |
|--------|------:|
| **`has_repro`** (open issues) | **~46** |
| **`cannot_repro`** | **4** — #8440, #8749, #8838, #8943 |
| **PARTIAL** (no label) | **2** — #8539, #8970 |
| **DEFERRED Windows** | #8813 + [WINDOWS-ONLY.md](./WINDOWS-ONLY.md) |
| **Issue writeups** | **54** (`docs/bug-reproductions/<n>.md`) |
Filters:
- [has_repro](https://github.com/stablyai/orca/issues?q=is%3Aissue+label%3Ahas_repro)
- [cannot_repro](https://github.com/stablyai/orca/issues?q=is%3Aissue+label%3Acannot_repro)
---
## Session log
### Pass 1 — 2026-07-15
Initial scan; first 14 reproductions; #8838 cannot_repro; #8844 ghost-tab soft-open.
### Pass 2 — 2026-07-15
Batches A+B (+10 GHE/pet/undici/serve/Linear/RM/history).
### Pass 3 — 2026-07-16
- Label rename: `cannot_reproduce` → **`cannot_repro`**.
- **Batch C complete** (7 REPRO + 2 NOT): 8797, 8378, 8715, 8881, 8878, 8478, 8593 + cannot 8749, 8943.
- **Batch D complete** (8 REPRO + 1 PARTIAL + 1 DEFERRED): 8450, 8742, 8299, 8733, 8399, 8986, 8372, 8974 + partial 8539 + Windows 8813.
- **Batch E complete** (8 REPRO + 1 NOT): 8482, 8752, 8726, 8934, 8335, 8758, 8541, 8622 + cannot 8440.
- Fixed flaky tests: 8378, 8715, 8881.
### Unit tests
All batch C/D/E repro suites verified green (e.g. 31 + 17 + 29 tests in final re-runs).
---
## Next agent checklist
1. Read this file + README.
2. Prefer open bugs **without** `has_repro` / `cannot_repro` and without a doc.
3. Windows → WINDOWS-ONLY.md only.
4. Write `docs/bug-reproductions/<n>.md` + vitest under `src/**`.
5. Apply `has_repro` or `cannot_repro`.
6. Never `orca file open` missing paths.
+195
View File
@@ -0,0 +1,195 @@
# Bug reproduction index
Orchestration **2026-07-15** · worktree `cooked-PRs` · production Orca **1.4.143** (`/Applications/Orca.app`) · macOS.
## How to use this folder
| Path | Purpose |
|------|---------|
| `README.md` | This index |
| `WINDOWS-ONLY.md` | Windows/WSL bugs deferred (need a Windows host) |
| `<issue-number>.md` | Per-issue status, evidence, re-run steps |
| `scripts/` | Shell / node helpers |
| `evidence/` | Captured JSON, logs |
### Production CLI note
`/usr/local/bin/orca` may be a **stale dev shim** pointing at a deleted worktree. Prefer:
```bash
PROD="/Applications/Orca.app/Contents/Resources/bin/orca"
"$PROD" status --json
```
### Security
Do **not** run issue attachments (`.exe`, `.cmd`, `.ps1`, mystery archives). Recreate fixtures yourself. No malicious payloads were found in the issues reviewed this pass.
### Status legend
- **REPRODUCED** — conclusive evidence + re-run steps/tests → GitHub label **`has_repro`**
- **NOT REPRODUCED** — attempted; current tree/app does not show the bug → GitHub label **`cannot_repro`**
- **PARTIAL** — root cause narrowed; full product path not live-proven (not labeled)
- **DEFERRED** — Windows / mobile / special host
Filter on GitHub:
- [`has_repro`](https://github.com/stablyai/orca/issues?q=is%3Aissue+label%3Ahas_repro)
- [`cannot_repro`](https://github.com/stablyai/orca/issues?q=is%3Aissue+label%3Acannot_repro)
---
## REPRODUCED (conclusive)
| Issue | Summary | Re-run |
|------:|---------|--------|
| [8844](./8844.md) | `orca file open` succeeds for missing files | default script is non-destructive; live open needs `ORCA_REPRO_8844_LIVE=1` (creates ghost tabs) |
| [8670](./8670.md) | Clean worktree + submodule needs `git worktree remove --force` | `bash docs/bug-reproductions/scripts/repro-8670-submodule-worktree-remove.sh` |
| [8454](./8454.md) | Bare `localhost:…` forced to `http://` | `pnpm exec vitest run src/shared/browser-url.test.ts` |
| [8533](./8533.md) / [8584](./8584.md) | Default shortcut collisions (`Mod+Shift+E`, `Mod+0`) | `pnpm exec vitest run --config config/vitest.config.ts src/shared/repro-8533-8584-shortcut-conflicts.test.ts` |
| [8595](./8595.md) | Bold Text color override is a no-op | `pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/repro-8595-bold-theme-noop.test.ts` |
| [8832](./8832.md) | Cmd-click URL glues next line text | `pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/repro-8832-url-next-line.test.ts` |
| [8940](./8940.md) | opencode labeled as Claude Code | `pnpm exec vitest run --config config/vitest.config.ts src/shared/repro-8940-opencode-as-claude.test.ts` |
| [8903](./8903.md) | Cmd-J focus lands on wrong (often hidden) surface | `pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/cmd-j/repro-8903-cmdj-focus-fallback.test.ts` |
| [8962](./8962.md) | OMP terminals excluded from cold session restore | See `8962.md` (unit proofs on `RESUMABLE_TUI_AGENTS` / `extractAgentProviderSession`) |
| [8808](./8808.md) | Agent recognition fails on env-prefixed cmdline after restart | See `8808.md` (`recognizeAgentProcess` with `CLAUDE_CONFIG_DIR=… claude`); fix PR **#8942** |
| [8865](./8865.md) | Project Group hides members when Hide sleeping filters workspaces | See `8865.md`; fix PR **#8866** |
| [8532](./8532.md) | `Cmd+Shift+O` open markdown only works in floating terminal | See `8532.md`; fix PR **#8564** |
| [8577](./8577.md) | Settings search query leaks into Shortcuts row filter | See `8577.md`; fix PR **#8579** |
| [8784](./8784.md) | GHE PR avatars built from `github.com/{login}.png` | `pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/github/repro-8784-ghe-avatar-fallback.test.ts`; fix PR **#8831** |
| [8935](./8935.md) | GHE PR work item diffs do not load (no Enterprise host) | `pnpm exec vitest run --config config/vitest.config.ts src/main/github/repro-8935-ghe-pr-diff-host.test.ts`; fix PR **#8932** |
| [8729](./8729.md) | .codex-pet ignores per-frame duration (uniform 8 fps) | `pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/repro-8729-codex-pet-fps.test.ts` |
| [8695](./8695.md) | undici 7.28.0 paused-parser assert crashes main | unit + live: `bash docs/bug-reproductions/scripts/repro-8695-undici-paused-parser.sh` |
| [8720](./8720.md) | SSH relay npm 12 skips node-pty (no allowScripts) | `pnpm exec vitest run --config config/vitest.config.ts src/main/ssh/repro-8720-npm12-allow-scripts.test.ts` |
| [8535](./8535.md) | pinned `orca serve --port` overridden by mobile-ws fallback | `pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/rpc/repro-8535-ws-fallback-port-order.test.ts` |
| [8739](./8739.md) | Linear filters only first selected team | `pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/repro-8739-linear-filter-first-team.test.ts` |
| [8459](./8459.md) | Resource Manager kills live daemon as orphan | `pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/status-bar/repro-8459-orphan-live-daemon.test.ts` |
| [8457](./8457.md) | headless serve hijacks GUI relaunch | `pnpm exec vitest run --config config/vitest.config.ts src/main/startup/repro-8457-serve-desktop-hijack.test.ts` |
| [8864](./8864.md) | Agent history lookup hangs forever | `pnpm exec vitest run --config config/vitest.config.ts src/main/ai-vault/repro-8864-history-lookup-no-timeout.test.ts` |
| [8482](./8482.md) | Window Blur sustained high GPU on macOS | `pnpm exec vitest run --config config/vitest.config.ts src/main/window/repro-8482-window-blur-gpu-cost.test.ts` |
| [8752](./8752.md) | Setup-script prompt stays after orca.yaml setup | `pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/sidebar/repro-8752-setup-script-prompt-stale.test.ts` |
| [8726](./8726.md) | Tasks fork: auto PR→origin; count cache 120s | `pnpm exec vitest run --config config/vitest.config.ts src/main/github/repro-8726-fork-pr-auto-origin.test.ts` |
| [8934](./8934.md) | Jira ADF media images dropped in Tasks | `pnpm exec vitest run --config config/vitest.config.ts src/main/jira/repro-8934-adf-media-dropped.test.ts` |
| [8335](./8335.md) | Terminal stuck: mouse preserved on agent reattach | `pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/repro-8335-agent-editor-mouse-preserve.test.ts` |
| [8758](./8758.md) | node-pty unavailable on remote (same as #8720) | `pnpm exec vitest run --config config/vitest.config.ts src/main/ssh/repro-8758-node-pty-remote.test.ts` |
| [8541](./8541.md) | Remote multiplex timeout cascade | `pnpm exec vitest run --config config/vitest.config.ts src/shared/repro-8541-multiplex-timeout-cascade.test.ts` |
| [8622](./8622.md) | SSH keyboard-interactive MFA unsupported | `pnpm exec vitest run --config config/vitest.config.ts src/main/ssh/repro-8622-keyboard-interactive-missing.test.ts` |
| [8450](./8450.md) | SSH relay picks system Node without npm | `pnpm exec vitest run --config config/vitest.config.ts src/main/ssh/repro-8450-node-without-npm.test.ts` |
| [8742](./8742.md) | Antigravity sessions missing from AI Vault | `pnpm exec vitest run --config config/vitest.config.ts src/main/ai-vault/repro-8742-antigravity-not-scanned.test.ts` |
| [8299](./8299.md) | Shift+Space input-source switch also types space | `pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/repro-8299-shift-space-input-source.test.ts` |
| [8733](./8733.md) / [8399](./8399.md) | Option compose broken under kitty (vim/Pi) | `pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/repro-8733-8399-kitty-option-compose.test.ts` |
| [8986](./8986.md) | OMP tab status flash (working↔idle titles) | `pnpm exec vitest run --config config/vitest.config.ts src/shared/repro-8986-omp-status-flash.test.ts` |
| [8372](./8372.md) | Closed Resource Manager CLI count vs live daemon | `pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/status-bar/repro-8372-closed-cli-count-stale.test.ts` |
| [8974](./8974.md) | Claude usage stuck “Refreshing sign-in” | `pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/status-bar/repro-8974-refreshing-sign-in-stuck.test.ts` |
| [8797](./8797.md) | Background Opacity / Window Blur no-op on macOS | `pnpm exec vitest run --config config/vitest.config.ts src/main/window/repro-8797-blur-opacity-noop.test.ts` |
| [8378](./8378.md) | Codex session reset: popup vs status bar | `pnpm exec vitest run --config config/vitest.config.ts src/shared/repro-8378-codex-reset-time-mismatch.test.ts` |
| [8715](./8715.md) | OMP scroll resets on tab switch (alt buffer) | `pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/pane-manager/repro-8715-omp-alt-screen-scroll.test.ts` |
| [8881](./8881.md) | Sidebar duplicates worktrees after re-pair | `pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/store/slices/repro-8881-runtime-repair-duplicates.test.ts` |
| [8878](./8878.md) | Remote client resumes live host agents | `pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/repro-8878-remote-resume-no-runtime-gate.test.ts`; fix PR **#8887** |
| [8593](./8593.md) | Lingering idle subagent sidebar rows | `pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/sidebar/repro-8593-idle-subagent-rows.test.ts` |
| [8478](./8478.md) | OpenCode native title → wrong icon | `pnpm exec vitest run --config config/vitest.config.ts src/shared/repro-8478-opencode-native-title-icon.test.ts`; fix PR **#8590** |
### NOT REPRODUCED (Batch C)
| Issue | Summary | Re-run |
|------:|---------|--------|
| [8749](./8749.md) | Agent seatbelt blocks claude-hook.sh | live sandbox_check=0; `src/main/agent-hooks/repro-8749-hook-path-no-seatbelt.test.ts` |
| [8943](./8943.md) | Browser shows insecure | screenshot-only; `src/main/browser/repro-8943-browser-insecure-indicator.test.ts` |
### Batch re-run (unit tests that live under `src/`)
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/shared/repro-8533-8584-shortcut-conflicts.test.ts \
src/renderer/src/components/terminal-pane/repro-8595-bold-theme-noop.test.ts \
src/renderer/src/components/terminal-pane/repro-8832-url-next-line.test.ts \
src/shared/repro-8940-opencode-as-claude.test.ts \
src/renderer/src/components/cmd-j/repro-8903-cmdj-focus-fallback.test.ts \
src/shared/browser-url.test.ts \
src/renderer/src/components/github/repro-8784-ghe-avatar-fallback.test.ts \
src/main/github/repro-8935-ghe-pr-diff-host.test.ts \
src/main/ipc/repro-8729-codex-pet-fps.test.ts \
src/main/repro-8695-undici-paused-parser.test.ts \
src/main/ssh/repro-8720-npm12-allow-scripts.test.ts \
src/main/runtime/rpc/repro-8535-ws-fallback-port-order.test.ts \
src/renderer/src/components/repro-8739-linear-filter-first-team.test.ts \
src/renderer/src/components/status-bar/repro-8459-orphan-live-daemon.test.ts \
src/main/startup/repro-8457-serve-desktop-hijack.test.ts \
src/main/ai-vault/repro-8864-history-lookup-no-timeout.test.ts \
src/main/window/repro-8482-window-blur-gpu-cost.test.ts \
src/renderer/src/components/sidebar/repro-8752-setup-script-prompt-stale.test.ts \
src/main/github/repro-8726-fork-pr-auto-origin.test.ts \
src/main/jira/repro-8934-adf-media-dropped.test.ts \
src/renderer/src/components/terminal-pane/repro-8335-agent-editor-mouse-preserve.test.ts \
src/main/ssh/repro-8758-node-pty-remote.test.ts \
src/shared/repro-8541-multiplex-timeout-cascade.test.ts \
src/main/ssh/repro-8622-keyboard-interactive-missing.test.ts \
src/main/ssh/repro-8450-node-without-npm.test.ts \
src/main/ai-vault/repro-8742-antigravity-not-scanned.test.ts \
src/renderer/src/components/terminal-pane/repro-8299-shift-space-input-source.test.ts \
src/renderer/src/components/terminal-pane/repro-8733-8399-kitty-option-compose.test.ts \
src/shared/repro-8986-omp-status-flash.test.ts \
src/renderer/src/components/status-bar/repro-8372-closed-cli-count-stale.test.ts \
src/renderer/src/components/status-bar/repro-8974-refreshing-sign-in-stuck.test.ts \
src/renderer/src/store/slices/repro-8539-fetchAllWorktrees-reconnect.test.ts
```
```bash
# Git fixture (safe)
bash docs/bug-reproductions/scripts/repro-8670-submodule-worktree-remove.sh
# #8844 — evidence-only by default (does NOT open editor tabs)
bash docs/bug-reproductions/scripts/repro-8844-file-open-missing.sh
# Live missing-path open creates ghost ENOENT tabs — only if you really need it:
# ORCA_REPRO_8844_LIVE=1 bash docs/bug-reproductions/scripts/repro-8844-file-open-missing.sh
# #8695 — process crash expected (undici assert)
bash docs/bug-reproductions/scripts/repro-8695-undici-paused-parser.sh
```
---
## PARTIAL
| Issue | Summary | Notes |
|------:|---------|-------|
| [8970](./8970.md) | Closed agent session lingers in sidebar | Happy-path close clears status; residual races after #8825/#8851 not live-proven |
| [8539](./8539.md) | Renderer freeze 87s on reconnect many worktrees | Call sites + O(repos) work proven; live multi-host freeze not run |
---
## NOT REPRODUCED
| Issue | Summary | Notes |
|------:|---------|-------|
| [8838](./8838.md) | `<br>` in markdown table cells | Current `MarkdownPreview` pipeline emits real `<br/>` — `pnpm exec node docs/bug-reproductions/scripts/repro-8838-br-in-table.mjs` |
| [8440](./8440.md) | MCP OAuth not persisting in Orca terminals | Maintainer could not repro; no in-tree generic MCP OAuth store bug; needs agent/MCP credential path |
| [8749](./8749.md) | Agent seatbelt blocks claude-hook.sh | Live daemon PTY `sandbox_check=0`; no seatbelt in daemon spawn code |
| [8943](./8943.md) | Browser shows insecure | Screenshot-only; intentional popup “Not secure” for remote http |
---
## DEFERRED
| Bucket | Doc / notes |
|--------|-------------|
| **Windows / WSL** | [WINDOWS-ONLY.md](./WINDOWS-ONLY.md) — 30+ open bugs |
| **Linux/IBus/X11** | #8861 (CJK IME), #8860 (middle-click paste twice) |
| **GitHub Enterprise (live UI)** | Live GHE UI still needs a host; code-level repros done for #8935 / #8784 (fix PRs #8932 / #8831) |
| **Mobile / iOS / Android** | #8933, #8889, #8818, #8700, #8666, #8555, #8592, #8364, #8313, … |
| **Sandbox / live agent** | #8749 (claude-hook.sh EPERM) — needs Orca-hosted Claude session |
| **SSH / remote runtime** | #8878, #8871, #8696, … — need remote hosts (#8720/#8450/#8758/#8541/#8622 code-level done) |
| **Perf / scale** | #8814, #8882, #8652 — need multi-worktree load (#8539 PARTIAL call sites) |
| **Windows project status UI** | #8813 — see [WINDOWS-ONLY.md](./WINDOWS-ONLY.md) / `8813.md` |
---
## What we need from you (only blockers for next steps)
1. **Windows host or CI** — process [WINDOWS-ONLY.md](./WINDOWS-ONLY.md) (P0: #8751, #8734, #6874).
2. **GHE credentials / sandbox** — optional live UI for #8935/#8784 (code-level REPRODUCED; fix PRs #8932 / #8831).
3. **Mobile device or emulator pairing** — for freezes/rename/browser-tab bugs.
4. **Confirm live UI for #8970** — still seeing lingering closed sessions on 1.4.143 after #8825?
5. **Linux remote with npm 12** — optional live for #8720 (code-level REPRODUCED: missing `allowScripts`).
Everything else in the REPRODUCED table is re-runnable by another agent without extra access.
+59
View File
@@ -0,0 +1,59 @@
# Windows-only bugs (deferred)
These open issues are labeled `os:Windows` and/or are Windows/WSL-specific. They are **not** reproduced in this macOS orchestration pass. Track and reproduce on a Windows machine (or CI Windows runner) separately.
| Issue | Title | Notes |
|------:|-------|-------|
| 8813 | Visual indicators for statuses within the project don't appear | Yellow checkmark while working; CPU/memory; see `8813.md` |
| 8956 | Fail to list remote dirs | Remote FS listing on Windows |
| 8834 | In-app terminal cannot open Cursor CLI (system terminal can) | PATH / shell spawn |
| 8795 | Claude account switching does not work in Remote SSH projects | Windows host reported |
| 8793 | Windows Cursor agent (.cmd) cannot launch with prompt in argv — asks to Remove {prompt} | cmd quoting |
| 8787 | Windows wait-for-setup startup policy never starts the agent — doubled quotes in cmd wrapper | `ORCA_SETUP_MARKER` |
| 8763 | Orca has stopped recognizing Codex agents | Windows agent detection |
| 8754 | Orca don't render flutter command in Codex session without refreshing the tab | P1 render |
| 8751 | bug on windows upon returning session | P0 session restore |
| 8734 | Ctrl+Alt chords are swallowed by terminal input on Windows | P0 keybindings |
| 8645 | Hooks fail after updating Codex to v0.144.3 | Windows hooks |
| 8629 | Windows hook lifecycle test can overwrite live Codex runtime hooks | Test safety |
| 8627 | File sidebar is outdated on an old Orca version | pending-user-response |
| 8563 | Mouse wheel sends arrow keys to a normal-buffer CLI without mouse reporting | Wheel→keys |
| 8537 | Windows onboarding saves WSL as the default shell, but Settings shows PowerShell | Onboarding |
| 8475 | Chinese text sometimes garbled in Codex on Windows (WSL) | Encoding / WSL |
| 8302 | Windows agent list missing DeepSeek, icon mismatches, MiniMax quota | Agent list |
| 8291 | Terminal drag is completely broken | DnD |
| 8290 | Orca Windows renderer bug | Renderer |
| 8289 | Previous workspace surface remains visible after split collapse and worktree switch | Compositor |
| 8275 | Terminal daemon dies after batch worktree removal | Daemon |
| 8272 | Remote Windows host can disappear while smart sort refreshes it | Remote host |
| 8269 | Codex hook trust is lost between launches in WSL runtime | WSL hooks |
| 8258 | Cursor Agent CLI is not detected in the sidebar on Windows | Detection |
| 7725 | marketplace upgrade staging directories not cleaned up (multi-GB) | Disk growth |
| 7703 | Orca periodically causes system-wide keyboard focus loss | Focus steal |
| 7693 | The status of Kiro CLIs has not yet been determined | Status |
| 7563 | WSL GUI agent detection misses regular Claude while Claude CLI works | WSL detect |
| 7555 | codex-windows-sandbox-setup.exe module not found | Sandbox setup |
| 7372 | Switching b/w projects - terminal in focus is lost | Focus |
| 7175 | 'Not responding' and crash when running more than 3 sessions (32GB) | Perf/crash |
| 6896 | Worktree "Local setup command" built as cmd.exe under Git Bash | Setup shell |
| 6874 | Windows AppHangB1 when opening/activating terminal session | P0 hang |
| 6694 | Git Bash + Cursor agent: prompt mangled + hooks block | Dual bug |
| 6546 | Amp history not showing in right-side history panel on Windows 11 | History |
| 6487 | Application blocked by Windows Smart App Control (SAC) | Signing |
| 5345 | Scrolling causes duplicated/garbled text and panel overlap (Windows + WSL) | Render |
| 8366 | Sometimes Grok CLI is not detected in Orca but works in system terminal | Title says windows |
| 7351 | bundled CLI wrapper (orca.cmd) looks for Orca.exe in wrong directory | Labeled os:macos incorrectly? Verify on Windows |
## How to pick these up later
1. Use a Windows 11 host with a fresh Orca install (or the same version reported in the issue).
2. Prefer **production** `orca` from the installed app, not a stale dev CLI shim.
3. For each issue: capture screenshots, CLI JSON (`--json`), Event Viewer AppHang entries, and a short script under `docs/bug-reproductions/scripts/` when the bug is pure logic (quoting, PATH, force flags).
4. Never execute untrusted user-uploaded binaries from issues (especially `.exe` / `.cmd` / `.ps1` attachments). Recreate fixtures yourself.
## WSL-adjacent (may need Windows host even if not pure Win32)
| Issue | Title |
|------:|-------|
| 8470 | DaemonProtocolError when waking a slept WSL workspace |
| 8941 | WSL resource usage spikes (not always labeled bug) |
@@ -0,0 +1,9 @@
git version 2.44.0
fixture: /var/folders/td/0d_w3jrs3654mtmk1gd6kpq00000gn/T//orca-repro-8670.4S3ZkI
parent status:
submodule status:
--- without --force (exit 128) ---
fatal: working trees containing submodules cannot be moved or removed
--- with --force (exit 0) ---
worktree path removed: yes
RESULT: REPRODUCED (clean submodule worktree requires --force)
@@ -0,0 +1,14 @@
TMP=/var/folders/td/0d_w3jrs3654mtmk1gd6kpq00000gn/T/tmp.PVIKlISz5P
Cloning into '/private/var/folders/td/0d_w3jrs3654mtmk1gd6kpq00000gn/T/tmp.PVIKlISz5P/parent-repo/vendor/sub'...
Preparing worktree (detached HEAD 18dc7df)
Cloning into '/private/var/folders/td/0d_w3jrs3654mtmk1gd6kpq00000gn/T/tmp.PVIKlISz5P/linked-wt/vendor/sub'...
=== clean status (parent) ===
=== clean status (submodule) ===
=== remove WITHOUT force (expect fail) ===
fatal: working trees containing submodules cannot be moved or removed
exit=128
=== remove WITH force (expect success) ===
exit=0
STATUS=REPRODUCED
Git: git version 2.44.0
@@ -0,0 +1,54 @@
=== #8695 undici paused-parser repro 2026-07-16T06:22:32Z ===
node: v24.18.0
--- system node ---
node:internal/assert/utils:77
throw err;
^
AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:
assert(!this.paused)
at Parser.finish (node:internal/deps/undici/undici:7380:9)
at Socket.onHttpSocketEnd (node:internal/deps/undici/undici:7819:34)
at Socket.emit (node:events:521:24)
at endReadableNT (node:internal/streams/readable:1729:12)
at process.processTicksAndRejections (node:internal/process/task_queues:90:21) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: false,
expected: true,
operator: '==',
diff: 'simple'
}
Node.js v24.18.0
system node exit: 1
--- Orca Electron as node (/Applications/Orca.app/Contents/MacOS/Orca) ---
node:internal/assert/utils:77
throw err;
^
AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:
assert(!this.paused)
at Parser.finish (node:internal/deps/undici/undici:7380:9)
at Socket.onHttpSocketEnd (node:internal/deps/undici/undici:7819:34)
at Socket.emit (node:events:521:24)
at endReadableNT (node:internal/streams/readable:1729:12)
at process.processTicksAndRejections (node:internal/process/task_queues:90:21) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: false,
expected: true,
operator: '==',
diff: 'simple'
}
Node.js v24.18.0
orca exit: 1
RESULT: REPRODUCED (process crashed or non-zero exit as expected)
@@ -0,0 +1,18 @@
{
"cli": "/Applications/Orca.app/Contents/Resources/bin/orca",
"relativePath": "spec/does-not-exist-repro-8844-1784182271.md",
"exitCode": 0,
"response": {
"id": "334a60ae-3053-4530-a320-3757ae9618bf",
"ok": true,
"result": {
"worktree": "8cf8301a-6725-4f9f-a5f4-93ba5c7c1e99::/Users/nwparker/orca/workspaces/orca/cooked-PRs",
"relativePath": "spec/does-not-exist-repro-8844-1784182271.md",
"kind": "markdown",
"opened": true
},
"_meta": {
"runtimeId": "feee85bb-b526-4f92-8c2b-959c1b3a2c03"
}
}
}
@@ -0,0 +1,33 @@
stamp: 2026-07-16T06:13:34Z
orca_bin: /Applications/Orca.app/Contents/Resources/bin/orca
cwd: /Users/nwparker/orca/workspaces/orca/cooked-PRs
relative_path: spec/does-not-exist-repro-8844.md
absolute_path: /Users/nwparker/orca/workspaces/orca/cooked-PRs/spec/does-not-exist-repro-8844.md
exists_before: no
--- orca file open --json ---
{
"id": "6f65d67e-c31d-4d4e-b3a4-95bebe1a9b8a",
"ok": true,
"result": {
"worktree": "8cf8301a-6725-4f9f-a5f4-93ba5c7c1e99::/Users/nwparker/orca/workspaces/orca/cooked-PRs",
"relativePath": "spec/does-not-exist-repro-8844.md",
"kind": "markdown",
"opened": true
},
"_meta": {
"runtimeId": "feee85bb-b526-4f92-8c2b-959c1b3a2c03"
}
}
exit: 0
exists_after: no
NOTES:
- Bug: ok:true / opened:true / exit 0 even though the file is missing.
- Ghost tab: Orca creates an editor tab; loading later fails with fs:readFile ENOENT.
- Root cause (source): RuntimeFileCommands.openMobileFile never stats the path;
it classifies by extension and always returns opened:true for non-binary kinds,
then host.openFile(...). See src/main/runtime/orca-runtime-files.ts openMobileFile.
- CLI: src/cli/handlers/file.ts 'file open' trusts files.open RPC result.
parsed ok=true opened=true exit=0
RESULT: REPRODUCED
@@ -0,0 +1,13 @@
{
"id": "6f65d67e-c31d-4d4e-b3a4-95bebe1a9b8a",
"ok": true,
"result": {
"worktree": "8cf8301a-6725-4f9f-a5f4-93ba5c7c1e99::/Users/nwparker/orca/workspaces/orca/cooked-PRs",
"relativePath": "spec/does-not-exist-repro-8844.md",
"kind": "markdown",
"opened": true
},
"_meta": {
"runtimeId": "feee85bb-b526-4f92-8c2b-959c1b3a2c03"
}
}
@@ -0,0 +1,18 @@
CLI=/Applications/Orca.app/Contents/Resources/bin/orca
exitCode=0
response:
{
"id": "334a60ae-3053-4530-a320-3757ae9618bf",
"ok": true,
"result": {
"worktree": "8cf8301a-6725-4f9f-a5f4-93ba5c7c1e99::/Users/nwparker/orca/workspaces/orca/cooked-PRs",
"relativePath": "spec/does-not-exist-repro-8844-1784182271.md",
"kind": "markdown",
"opened": true
},
"_meta": {
"runtimeId": "feee85bb-b526-4f92-8c2b-959c1b3a2c03"
}
}
evidence=/Users/nwparker/orca/workspaces/orca/cooked-PRs/docs/bug-reproductions/evidence/8844-file-open-missing.json
STATUS=REPRODUCED
@@ -0,0 +1,45 @@
=== #8808 env-prefixed agent recognition ===
plain: { agent: 'claude', processName: 'claude' }
env-prefixed process: null
env-prefixed cmdline: null
plain cmdline: { agent: 'claude', processName: 'claude' }
=== #8962 RESUMABLE_TUI_AGENTS + extract omp ===
RESUMABLE_TUI_AGENTS: [
'claude', 'codex',
'gemini', 'antigravity',
'opencode', 'mimo-code',
'droid', 'grok',
'devin'
]
isResumableTuiAgent(omp): false
extract omp: null
getAgentResumeArgv omp: undefined
=== #8532 wiring greps ===
src/renderer/src/lib/floating-workspace-shortcut-policy.ts:21: 'tab.openMarkdown',
src/renderer/src/components/floating-terminal/FloatingTerminalPanel.tsx:188: const openMarkdownShortcut = useShortcutKeyDetails('tab.openMarkdown')
src/renderer/src/components/floating-terminal/FloatingTerminalPanel.tsx:1012: if (matches('tab.openMarkdown')) {
=== #8577 ShortcutsPane matcher ===
39:import { matchesSettingsSearch } from './settings-search'
58: const searchQuery = useAppStore((state) => state.settingsSearchQuery)
141: const matchesShortcutSearch = (row: ShortcutRowsByGroup['rows'][number]): boolean =>
143: matchesSettingsSearch(searchQuery, getShortcutSearchEntry(row)) &&
145: const baseVisibleRows = shortcutRows.filter((row) => matchesShortcutSearch(row))
156: (row) => matchesShortcutSearch(row) && matchesShortcutFilter(row, shortcutFilter)
291: const showPolicy = matchesSettingsSearch(searchQuery, getTerminalShortcutPolicySearchEntry())
=== #8865 placeholder wiring ===
src/renderer/src/components/sidebar/empty-project-placeholder-repos.ts:4:export function getEmptyProjectPlaceholderRepoIds(args: {
src/renderer/src/components/sidebar/empty-project-placeholder-repos.ts:7: worktreesByRepo: Readonly<Record<string, readonly Worktree[] | undefined>>
src/renderer/src/components/sidebar/empty-project-placeholder-repos.ts:20: if ((args.worktreesByRepo[repo.id]?.length ?? 0) === 0) {
src/renderer/src/components/sidebar/WorktreeList.tsx:122:import { getEmptyProjectPlaceholderRepoIds } from './empty-project-placeholder-repos'
src/renderer/src/components/sidebar/WorktreeList.tsx:5156: const worktreesByRepo = useAppStore((s) => s.worktreesByRepo)
src/renderer/src/components/sidebar/WorktreeList.tsx:5502: const visibleWorktrees = useMemo(() => {
src/renderer/src/components/sidebar/WorktreeList.tsx:5504: const ids = computeVisibleWorktreeIds(worktreesByRepo, sortedIds, {
src/renderer/src/components/sidebar/WorktreeList.tsx:5555: worktreesByRepo
src/renderer/src/components/sidebar/WorktreeList.tsx:5558: const worktrees = visibleWorktrees
src/renderer/src/components/sidebar/WorktreeList.tsx:5709: return getEmptyProjectPlaceholderRepoIds({
src/renderer/src/components/sidebar/WorktreeList.tsx:5712: worktreesByRepo,
src/renderer/src/components/sidebar/WorktreeList.tsx:5715: }, [filterRepoIds, groupBy, visibleReposForRows, worktreesByRepo])
@@ -0,0 +1,13 @@
# Repro tests for #8832, #8595, #8940, #8903
Tests live under `src/` (vitest `include`) so `@/` aliases resolve via `config/vitest.config.ts`.
```bash
pnpm exec vitest run --config config/vitest.config.ts \
src/renderer/src/components/terminal-pane/repro-8832-url-next-line.test.ts \
src/renderer/src/components/terminal-pane/repro-8595-bold-theme-noop.test.ts \
src/shared/repro-8940-opencode-as-claude.test.ts \
src/renderer/src/components/cmd-j/repro-8903-cmdj-focus-fallback.test.ts
```
Docs: `docs/bug-reproductions/{8832,8595,8940,8903}.md`
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
# Issue #8454 — built-in browser forces bare localhost to http://
# Unit coverage already lives in src/shared/browser-url.test.ts.
#
# Re-run:
# bash docs/bug-reproductions/scripts/repro-8454-localhost-http.sh
# # or directly:
# npx vitest run src/shared/browser-url.test.ts -t "normalizes manual local-dev"
set -euo pipefail
cd "$(cd "$(dirname "$0")/../../.." && pwd)"
npx vitest run src/shared/browser-url.test.ts -t "normalizes manual local-dev"
@@ -0,0 +1,52 @@
/**
* Issues #8533 / #8584 — default keybinding collisions.
*
* #8533: darwin Mod+Shift+E → sidebar.explorer.toggle AND tab.newSimulator
* #8584: all platforms Mod+0 → zoom.reset AND sidebar.focusWorktreeList
*
* findKeybindingConflicts() only reports conflicts involving *customized*
* overrides, so defaults can collide silently. This test asserts raw default
* binding maps collide.
*
* Re-run:
* npx vitest run docs/bug-reproductions/scripts/repro-8533-8584-shortcut-conflicts.test.ts
*/
import { describe, expect, it } from 'vitest'
import { KEYBINDING_DEFINITIONS, type KeybindingPlatform } from '../../../src/shared/keybindings'
function defaultOwners(platform: KeybindingPlatform): Map<string, string[]> {
const map = new Map<string, string[]>()
for (const def of KEYBINDING_DEFINITIONS) {
const binds = def.defaultBindings[platform] ?? []
for (const binding of binds) {
const list = map.get(binding) ?? []
list.push(def.id)
map.set(binding, list)
}
}
return map
}
describe('issues #8533 and #8584 default shortcut conflicts', () => {
it('#8533: Mod+Shift+E collides on darwin (explorer toggle vs new simulator)', () => {
const owners = defaultOwners('darwin').get('Mod+Shift+E') ?? []
// eslint-disable-next-line no-console
console.log('darwin Mod+Shift+E owners:', owners)
expect(owners).toEqual(expect.arrayContaining(['sidebar.explorer.toggle', 'tab.newSimulator']))
expect(owners.length).toBeGreaterThanOrEqual(2)
// Linux/Windows: simulator unbound, so explorer alone owns the chord.
expect(defaultOwners('linux').get('Mod+Shift+E')).toEqual(['sidebar.explorer.toggle'])
expect(defaultOwners('win32').get('Mod+Shift+E')).toEqual(['sidebar.explorer.toggle'])
})
it('#8584: Mod+0 collides on every platform (zoom.reset vs focus worktree list)', () => {
for (const platform of ['darwin', 'linux', 'win32'] as const) {
const owners = defaultOwners(platform).get('Mod+0') ?? []
// eslint-disable-next-line no-console
console.log(`${platform} Mod+0 owners:`, owners)
expect(owners).toEqual(expect.arrayContaining(['zoom.reset', 'sidebar.focusWorktreeList']))
expect(owners.length).toBeGreaterThanOrEqual(2)
}
})
})
@@ -0,0 +1,75 @@
#!/usr/bin/env bash
# Issue #8670 — clean linked worktree with initialised submodule cannot be removed
# without git worktree remove --force.
#
# Re-run:
# bash docs/bug-reproductions/scripts/repro-8670-submodule-worktree-remove.sh
#
# Writes evidence to docs/bug-reproductions/evidence/8670-git-submodule-remove.txt
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
EVIDENCE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)/evidence"
mkdir -p "$EVIDENCE_DIR"
OUT="$EVIDENCE_DIR/8670-git-submodule-remove.txt"
TMP="$(mktemp -d "${TMPDIR:-/tmp}/orca-repro-8670.XXXXXX")"
cleanup() { rm -rf "$TMP"; }
trap cleanup EXIT
git version | tee "$OUT"
echo "fixture: $TMP" | tee -a "$OUT"
git -C "$TMP" init -q submodule-src
git -C "$TMP/submodule-src" config user.email repro@example.com
git -C "$TMP/submodule-src" config user.name repro
echo sub >"$TMP/submodule-src/file.txt"
git -C "$TMP/submodule-src" add file.txt
git -C "$TMP/submodule-src" commit -q -m init
git -C "$TMP" init -q main
git -C "$TMP/main" config user.email repro@example.com
git -C "$TMP/main" config user.name repro
echo root >"$TMP/main/README.md"
git -C "$TMP/main" add README.md
git -C "$TMP/main" commit -q -m init
# Why: Git ≥2.38 blocks file:// submodule clones unless protocol.file.allow=always.
git -C "$TMP/main" -c protocol.file.allow=always submodule add "$TMP/submodule-src" vendor/sub
git -C "$TMP/main" commit -q -m 'add submodule'
git -C "$TMP/main" worktree add -q -b feature "$TMP/wt-feature"
git -C "$TMP/wt-feature" -c protocol.file.allow=always submodule update --init
{
echo "parent status:"; git -C "$TMP/wt-feature" status --short --untracked-files=all || true
echo "submodule status:"; git -C "$TMP/wt-feature/vendor/sub" status --short --untracked-files=all || true
} | tee -a "$OUT"
set +e
git -C "$TMP/main" worktree remove "$TMP/wt-feature" 2>"$TMP/err-noforce.txt"
noforce=$?
set -e
{
echo "--- without --force (exit $noforce) ---"
cat "$TMP/err-noforce.txt"
} | tee -a "$OUT"
set +e
git -C "$TMP/main" worktree remove --force "$TMP/wt-feature" 2>"$TMP/err-force.txt"
force=$?
set -e
{
echo "--- with --force (exit $force) ---"
cat "$TMP/err-force.txt"
if [[ ! -d "$TMP/wt-feature" ]]; then
echo "worktree path removed: yes"
else
echo "worktree path removed: no"
fi
} | tee -a "$OUT"
if [[ "$noforce" -ne 0 ]] && grep -q 'working trees containing submodules cannot be moved or removed' "$TMP/err-noforce.txt" && [[ "$force" -eq 0 ]]; then
echo "RESULT: REPRODUCED (clean submodule worktree requires --force)" | tee -a "$OUT"
exit 0
fi
echo "RESULT: NOT REPRODUCED" | tee -a "$OUT"
exit 1
@@ -0,0 +1,85 @@
#!/usr/bin/env bash
# Issue #8695 — undici 7.28.0 paused-parser assert (process crash).
#
# Runs the minimal net-server + fetch(without body consume) repro against:
# 1) system node (if present)
# 2) production Orca Electron as node (ELECTRON_RUN_AS_NODE=1)
#
# Expected: AssertionError assert(!this.paused) in Parser.finish / non-zero exit.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../../.." && pwd)"
EVIDENCE_DIR="$ROOT/docs/bug-reproductions/evidence"
mkdir -p "$EVIDENCE_DIR"
OUT="$EVIDENCE_DIR/8695-undici-repro.log"
REPRO_JS="$(mktemp -t orca-undici-repro.XXXXXX.js)"
trap 'rm -f "$REPRO_JS"' EXIT
cat >"$REPRO_JS" <<'EOF'
const { createServer } = require("node:net");
const body = Buffer.alloc(64 * 1024, 0x61);
const server = createServer((socket) => {
socket.once("data", () => {
socket.write(
"HTTP/1.1 200 OK\r\n" +
`Content-Length: ${body.length}\r\n` +
"Connection: close\r\n\r\n",
);
socket.write(body);
socket.end();
});
});
server.listen(0, "127.0.0.1", async () => {
const { port } = server.address();
try {
await fetch(`http://127.0.0.1:${port}/`);
setTimeout(() => {
console.log("Unexpected: process did not crash");
server.close();
process.exit(0);
}, 500);
} catch (e) {
console.error("fetch error", e);
server.close();
process.exit(2);
}
});
EOF
{
echo "=== #8695 undici paused-parser repro $(date -u +%Y-%m-%dT%H:%M:%SZ) ==="
echo "node: $(node -v 2>/dev/null || echo missing)"
echo
echo "--- system node ---"
set +e
node "$REPRO_JS" 2>&1
sys_ec=$?
set -e
echo "system node exit: $sys_ec"
echo
ORCA_BIN="${ORCA_ELECTRON_BIN:-/Applications/Orca.app/Contents/MacOS/Orca}"
echo "--- Orca Electron as node ($ORCA_BIN) ---"
if [[ -x "$ORCA_BIN" ]]; then
set +e
ELECTRON_RUN_AS_NODE=1 "$ORCA_BIN" "$REPRO_JS" 2>&1
orca_ec=$?
set -e
echo "orca exit: $orca_ec"
else
echo "Orca binary missing: $ORCA_BIN"
orca_ec=127
fi
echo
if [[ "${sys_ec:-0}" -ne 0 || "${orca_ec:-0}" -ne 0 ]]; then
echo "RESULT: REPRODUCED (process crashed or non-zero exit as expected)"
exit 0
fi
echo "RESULT: NOT REPRODUCED (both runtimes survived)"
exit 1
} | tee "$OUT"
@@ -0,0 +1,48 @@
/**
* Issue #8838 — check whether <br> in GFM table cells becomes real <br> elements
* under the MarkdownPreview-like pipeline.
*
* Run: pnpm exec node --input-type=module docs/bug-reproductions/scripts/repro-8838-br-in-table.mjs
*/
import { createElement } from 'react'
import { renderToStaticMarkup } from 'react-dom/server'
import Markdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
import remarkBreaks from 'remark-breaks'
import rehypeRaw from 'rehype-raw'
import rehypeSanitize, { defaultSchema } from 'rehype-sanitize'
const SAMPLE = `| Field | Type | Description |
|-------|------|-------------|
| verdict | string | Inspection verdict. Possible values:<br/><br/>\`normal\` - Normal. No defect found<br>\`uncertain\` - Needs review<br>\`defect\` - Defective |`
const schema = {
...defaultSchema,
tagNames: [...(defaultSchema.tagNames ?? []), 'details', 'summary', 'kbd', 'sub', 'sup', 'ins']
}
const html = renderToStaticMarkup(
createElement(
Markdown,
{
remarkPlugins: [remarkGfm, remarkBreaks],
rehypePlugins: [rehypeRaw, [rehypeSanitize, schema]]
},
SAMPLE
)
)
const brCount = (html.match(/<br/gi) || []).length
const escaped = (html.match(/&lt;br/gi) || []).length
console.log('HTML:', html)
console.log('brCount:', brCount)
console.log('escapedBrCount:', escaped)
if (brCount > 0 && escaped === 0) {
console.log('STATUS=NOT_REPRODUCED')
process.exit(0)
}
console.log('STATUS=REPRODUCED')
process.exit(1)
@@ -0,0 +1,13 @@
/**
* Issue #8838 guard — pipeline emits real <br> in table cells.
* Run: pnpm exec node docs/bug-reproductions/scripts/repro-8838-br-in-table.mjs
* (vitest file intentionally minimal; full render is in the .mjs script)
*/
import { describe, expect, it } from 'vitest'
import { defaultSchema } from 'rehype-sanitize'
describe('issue #8838 br allowed in sanitize schema', () => {
it('defaultSchema includes br', () => {
expect(defaultSchema.tagNames ?? []).toContain('br')
})
})
@@ -0,0 +1,59 @@
#!/usr/bin/env bash
# Issue #8844 — orca file open reports ok:true for a non-existent file.
#
# DEFAULT: code-path / evidence re-check only — does NOT open ghost editor tabs.
#
# LIVE repro (opens a ghost tab in the running Orca UI — DO NOT use casually):
# ORCA_REPRO_8844_LIVE=1 bash docs/bug-reproductions/scripts/repro-8844-file-open-missing.sh
#
# Prefer production CLI:
# ORCA_BIN=/Applications/Orca.app/Contents/Resources/bin/orca
set -uo pipefail
ORCA_BIN="${ORCA_BIN:-/Applications/Orca.app/Contents/Resources/bin/orca}"
ROOT="${1:-.}"
ROOT="$(cd "$ROOT" && pwd)"
EVIDENCE_DIR="$(cd "$(dirname "$0")/.." && pwd)/evidence"
mkdir -p "$EVIDENCE_DIR"
echo "=== #8844 repro (default is non-destructive) ==="
echo "Root cause: RuntimeFileCommands.openMobileFile never stats the path;"
echo " classifies by extension and returns opened:true after host.openFile."
echo "See: src/main/runtime/orca-runtime-files.ts openMobileFile"
echo "See: src/cli/handlers/file.ts 'file open'"
echo
echo "Prior live evidence (already captured — do not re-open casually):"
if [[ -f "$EVIDENCE_DIR/8844-file-open-missing.raw.json" ]]; then
cat "$EVIDENCE_DIR/8844-file-open-missing.raw.json"
echo
fi
if [[ -f "$EVIDENCE_DIR/8844-file-open-missing.json.txt" ]]; then
grep -E '^(RESULT|exit|exists_|parsed)' "$EVIDENCE_DIR/8844-file-open-missing.json.txt" || true
fi
if [[ "${ORCA_REPRO_8844_LIVE:-}" != "1" ]]; then
echo
echo "STATUS=REPRODUCED_FROM_EVIDENCE (no new ghost tab created)"
echo "To run live CLI open of a missing path (creates a UI tab + ENOENT card):"
echo " ORCA_REPRO_8844_LIVE=1 $0"
exit 0
fi
# --- LIVE path (explicit opt-in only) ---
REL="spec/does-not-exist-repro-8844-LIVE-$(date +%s).md"
ABS="$ROOT/$REL"
rm -f "$ABS" 2>/dev/null || true
cd "$ROOT" || exit 2
set +e
"$ORCA_BIN" file open "$REL" --json >"$EVIDENCE_DIR/8844-file-open-missing.raw.json" 2>"$EVIDENCE_DIR/8844-file-open-missing.stderr.txt"
code=$?
set -e
echo "LIVE open of $REL exit=$code"
cat "$EVIDENCE_DIR/8844-file-open-missing.raw.json"
echo
echo "WARNING: a ghost editor tab was opened. Close it with Cmd+W, or create the file:"
echo " mkdir -p \"\$(dirname $ABS)\" && echo placeholder > \"$ABS\""
echo "STATUS=REPRODUCED_LIVE"
exit 0
@@ -0,0 +1,29 @@
/**
* Issues #8533 and #8584 — default keybinding collisions.
* Run: pnpm exec vitest run docs/bug-reproductions/scripts/repro-shortcut-conflicts.test.ts
*/
import { describe, expect, it } from 'vitest'
import { getEffectiveKeybindingsForAction } from '../../../src/shared/keybindings'
describe('issue #8533 Cmd+Shift+E collision on darwin', () => {
it('tab.newSimulator and sidebar.explorer.toggle share Mod+Shift+E on darwin', () => {
const simulator = getEffectiveKeybindingsForAction('tab.newSimulator', 'darwin')
const explorer = getEffectiveKeybindingsForAction('sidebar.explorer.toggle', 'darwin')
expect(simulator).toContain('Mod+Shift+E')
expect(explorer).toContain('Mod+Shift+E')
// Document that linux/win32 do not ship the simulator default (no collision there).
expect(getEffectiveKeybindingsForAction('tab.newSimulator', 'linux')).toEqual([])
expect(getEffectiveKeybindingsForAction('tab.newSimulator', 'win32')).toEqual([])
})
})
describe('issue #8584 Mod+0 collision', () => {
it('zoom.reset and sidebar.focusWorktreeList both default to Mod+0', () => {
for (const platform of ['darwin', 'linux', 'win32'] as const) {
const zoom = getEffectiveKeybindingsForAction('zoom.reset', platform)
const focus = getEffectiveKeybindingsForAction('sidebar.focusWorktreeList', platform)
expect(zoom).toContain('Mod+0')
expect(focus).toContain('Mod+0')
}
})
})