mirror of
https://github.com/stablyai/orca.git
synced 2026-10-19 16:03:26 +00:00
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:
@@ -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`
|
||||
@@ -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.
|
||||
@@ -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`
|
||||
@@ -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
|
||||
```
|
||||
@@ -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`
|
||||
@@ -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).
|
||||
@@ -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`)
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
@@ -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.
|
||||
@@ -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.)
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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`
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
```
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
@@ -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`.
|
||||
@@ -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`
|
||||
@@ -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
|
||||
```
|
||||
@@ -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)
|
||||
@@ -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
|
||||
```
|
||||
@@ -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`)
|
||||
@@ -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`
|
||||
@@ -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.
|
||||
@@ -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`
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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)
|
||||
@@ -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).
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
@@ -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`
|
||||
@@ -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 (`<br/>`) 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 <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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
```
|
||||
@@ -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`
|
||||
@@ -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.
|
||||
@@ -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)
|
||||
@@ -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`
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
@@ -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`
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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(/<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')
|
||||
}
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user