mirror of
https://github.com/stablyai/orca.git
synced 2026-10-09 08:02:35 +00:00
09c8597fb7cf35bfcbc7b8487d078891172fd7d6
230
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e790266546 | fix(windows): show first window before shell PATH hydration (#13799) | ||
|
|
c1e75477f3 |
Fix static analysis page stuck in loading state (#13674)
* Fix static analysis page stuck in loading state - Bound check-details requests with 30s timeout, matching remote RPC budget - Track request IDs to discard stale responses when context changes - Propagate githubRepository through store and components for proper routing - Add retry button for failed check-details loads - Improve accessibility with ARIA labels for loading and error states * Fix static analysis page stuck in loading state When an open check-details tab's repository is removed, the loading state would continue indefinitely because the fetch was still being triggered. Prevent the fetch call in this scenario to unblock the UI. Also migrates translation keys to obfuscated identifiers. * Fix static analysis page stuck in loading state Add deadline-based timeouts and request ID tracking to prevent stale responses from freezing the checks panel. Include abort signal propagation throughout the request chain and provide retry UI for failed check details loads. * fix(checks): prevent loading state from getting stuck on retry - Consolidate mount checks into a helper function - Details now clear when a new request begins - Add i18n strings for retry status |
||
|
|
9deb72f9ed |
fix(git): support Windows-linked worktrees in WSL projects (#13483)
* fix(git): support Windows-linked worktrees in WSL projects * fix(git): harden WSL linked worktree routing * fix(test): defer WSL routing filesystem access * test(git): type WSL routing probe mock * fix(git): retry transient WSL route probes safely --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
5538584c74 | perf(git): overlap status with conflict detection (#13529) | ||
|
|
73efab98f7 | perf(orchestration): keep drift Git off main thread (#13440) | ||
|
|
69ca0154b6 | fix(git): bypass WSL login shells for status reads (#13207) | ||
|
|
46b9d3b13a |
Break out test and generated lines in branch line total (#13057)
* rm comments * reduce comment |
||
|
|
debf4affe7 |
Display total lines of code change in branch header (#12771)
* Add branch line total chip to source control header Display the total lines added and removed across a branch from its fork point, measured via `git diff <mergeBase>`. Only computed when the chip is visible (request gate on merge base OID), with 500ms soft deadline to protect status latency and 15s hard timeout. Deduplicated across concurrent pollers and cached alongside line stats. Omitted on failure — always shows exact or nothing, never a partial estimate. Updates throughout the stack: native git status, relay, renderer store/API, and UI components. * Pin branch line total to app locale Format line counts using the app's configured locale instead of the system locale, ensuring consistent cross-platform display and test reliability. * test: wait for coalescer joins instead of fixed sleep Hold the diff until the second status pass actually takes the branch-total coalescer lease instead of using a fixed 400ms sleep. Fixes timing-dependent flakiness on slow machines. |
||
|
|
de64337c26 |
fix(worktree-watcher): refresh status after external pushes (#12361)
* fix(worktree-watcher): surface external push -u through the git-common watch An external-shell 'git push -u' writes only the common .git/config (plus refs/remotes/<remote>/<branch>), both invisible to the git-common event filter, so the Checks panel stayed on 'No upstream configured' until the renderer safety poll. Classify the common config and remote-tracking refs as status-tier signals, poll config alongside the other primary-checkout metadata files, and keep FETCH_HEAD/reflog/ref-lock churn ignored. * fix(worktree-watcher): refresh after subsequent pushes |
||
|
|
9deee5ad2f |
perf(worktrees): delete worktree directories after the removal returns (#12416)
* perf(worktrees): delete worktree directories after the removal returns `git worktree remove` deleted the whole checkout inline, so the remove IPC held the watcher/PTY gate for the entire recursive delete (prod traces: worktree.remove.git_remove p50 8-14s, p90 29s, max 34.7s). Local removals now rename the checkout into a hidden sibling trash root, clear Git's registration for the missing path, and delete the moved tree in the background. Renames that cannot run (WSL, other volume, Windows open handles) fall back to the previous in-place removal unchanged. * test(worktrees): keep no empty trash root when the rename cannot run * fix(worktrees): harden deferred trash cleanup * fix(worktrees): keep WSL trash on its owning host |
||
|
|
73c5009b82 |
chore(dead-code): drop ~2k lines of unreachable exports and orphan modules (#12077)
* chore(dead-code): drop 2k lines of unreachable exports and orphan modules Ran knip across every build entry (main, preload, renderer, popout, web, cli, relay, workers, forked sidecars, config scripts) and removed what no entry graph can reach. - 11 orphan modules nothing imported, plus one test that only covered them - 159 unused exports/types, with their now-dead helpers, imports and tests Each candidate was verified against dynamic references before deletion. 42 knip hits were false positives and are kept: shared modules consumed by the mobile/ workspace, the src/shared/plugins/** public API, vendored shadcn primitives, and relay wire-protocol constants held for compatibility. Adds knip.json + `pnpm audit:dead-code` so this stays measurable. Verified: pnpm typecheck, pnpm lint, and 2081 tests across the 73 affected test files all pass. * chore(dead-code): move knip config under config/ Root-level additions are blocked by the root directory guard. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
1562f12f78 |
fix(P1-D): coalesce remote-ref probes, TTL negatives, and bound unsettled keys (#12065)
* fix(P1-D): coalesce remote-ref probes, TTL negatives, and bound unsettled keys Keep forge resolution from stampeding git under worktree fan-out, let remotes added mid-session be discovered without a restart, and refuse pathological new-branch waves once the unsettled map is full. * fix(P1-D): stop abandoned probes publishing, and split capacity refusals A coalesced probe abandoned as stale kept running and still wrote its answer to the cache, so a late permanent miss could land over the successor's fresher one. Probes now publish only while they still own the in-flight key. The hosted-review capacity refusal told brand-new branches that an earlier attempt of their own never answered when the refusal was really the unsettled map or the process-wide detached cap; each cap now says what it is. Also caches stable "no such remote" SSH misses under the negative TTL instead of re-spawning the probe on every poll. Co-authored-by: Orca <help@stably.ai> * Bound SSH remote URL probe with deadline to prevent hangs The SSH branch of remote URL probes was unbounded — the relay's bounds are per-phase and reset on every frame, so a relay dribbling output would outlive them. Pass AbortSignal.timeout to the SSH provider's exec call to enforce the same 30s deadline as local probes. Treat AbortError as a transient probe error: it signals unavailable infrastructure (deadline or cancellation), not a negative answer about the remote. --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
ced4a2a959 |
fix(P1-D): bound hosted-review in-flight lookups so a wedged provider cannot pin a branch (#12030)
* fix(P1-D): bound hosted-review lookups with a detachable deadline The `inflight` map in the hosted-review branch cache was only ever cleared when the lookup settled, and nothing bounded how long that took. One wedged provider call pinned its branch for the life of the process: every later poll joined the same dead promise, so the card loaded forever with no in-session recovery. Each lookup now runs under a 120s deadline. Nothing below the funnel can be cancelled, so the deadline detaches instead: the record is released, the callers get the last known review (or a timeout error), and the branch enters the existing failure backoff. The lookup keeps running and its answer is still adopted if it lands, so a slow-but-alive host converges rather than failing forever. A token identity keeps a detached lookup from evicting the record that replaced it, and a wall-clock sweep expires records whose timer never fired — main's timers are suspended across system sleep. `inflight` is capped independently of the completed cache. The failure backoff moves to its own module: it has a different lifetime from the answer cache and is what a deadline records against. * fix(P1-D): bound `git remote get-url` on the local/WSL path `getRemoteUrlForRepo` ran the git child with no timeout, which is the one unbounded step under the hosted-review lookup funnel: `git/runner.ts` only arms its kill path when a timeout is passed, so a dead network mount or a stalled WSL interop hangs the call and everything above it. The SSH branch is already bounded by the relay mux's 30s request timeout, so it is unchanged. * rm review doc * rm review doc * test(P1-D): add probe tests and transient-failure recovery verification Add tests for coalesced-probe and remote-url-probe infrastructure. Add integration test verifying that transient Bitbucket API failures don't cache as a definitive no-review result, allowing recovery after cache TTL expiration. * fix(P1-D): track lookups from start, prevent stale scope adoption - Count unsettled lookups when they start, not after deadline expires: prevents multiple concurrent lookups for the same branch. - Add evicted generation floor: prevents adopting stale results when scope is invalidated and evicted from the map. - Consolidate duplicate repository reference cache logic into createRemoteRefProbeCache utility. - Fix deadline wrapper in git config signature lookup: bound the caller's deadline only, not the coalesced probe itself. * feat(P1-D): add remote-ref-probe-cache utility Cache successful remote URL probes per repo/runtime to avoid duplicate work. Skip caching transient errors and SSH failures so providers can retry on reconnect, preventing stale scope adoption during the session. |
||
|
|
6e2a88c091 | perf(worktrees): avoid redundant fetch during deletion (#11918) | ||
|
|
05206046f6 |
chore: condense code comments (#12008)
* chore: condense code comments * chore: shorten more code comments * clarify PTY agent session descendant cleanup behavior Refine the comment on ptyAgentSessionIds to more accurately describe when agent sessions sweep their descendant process trees and note the exception on immediate Windows shutdown. |
||
|
|
d5c4d953ec | perf: coalesce cancellable git status reads (#11691) | ||
|
|
74563b6498 |
feat(jira): link Jira issues from the workspace create dialog (#11296)
* Link Jira issues from workspace create dialog Add Jira issue linking to workspace creation, matching existing GitHub and Linear workflows. Users can paste Jira issue URLs in the smart name field to auto-populate workspace names and link the issue to the created workspace/worktree. Linked Jira issues appear on workspace cards via the new 'jira-issue' card property. Implements cancellable searches and summary reads to prevent stalled requests from blocking the shared Jira pool. Persists paired issue + source context metadata with validation of provider/site identity. Fixes git-username rate-limit handling to reject malformed JSON responses so garbage never becomes branch prefixes. * feat(jira): link issues during workspace creation - Display linked Jira issues on worktree cards - Fetch issue summaries and timestamps via Jira API - Gate Jira linking behind runtime capability check - Preserve user-typed names during async lookups * Enforce git check-ref-format rules in login validation Extend isBranchSafeHostedLogin to reject usernames that git rejects as invalid branch components: trailing dots, consecutive dots, and .lock suffix. Prevents invalid branch names from login usernames. * Enforce filesystem filename cap for branch-safe logins Loose refs store logins as single filenames, so the real constraint is the 255-byte filesystem cap, not git check-ref-format rules. This allows longer provider-agnostic logins while staying platform-safe. |
||
|
|
cbe8635f46 |
fix(worktrees): prevent deletion from blocking Orca (#11233)
* fix(worktrees): prevent deletion from blocking Orca * test(worktrees): loosen async history-delete event-loop bound for CI The main-thread safety check failed on a loaded runner when a single timer gap hit ~48ms under the prior 30ms threshold. Keep the bound well below a recursive sync-rm stall without treating CI jitter as a block. * test(worktrees): measure history-delete critical path, not timer gaps setInterval gaps during async rm of thousands of files still flake under CI scheduling. deleteWorktreeHistoryDir is sync and must only rename, so assert that critical-path wall time stays well below a recursive walk. * fix(worktrees): prevent deletion from blocking Orca Add timeout-based draining of watcher closes so SSH round-trip delays don't indefinitely block the worktree removal path. Also: order durable temp-file sweeps ahead of writes to reclaim orphans before accumulation, skip own-process temps to avoid deleting live writes, swallow persistence errors so disk failures don't cascade to query callers, and measure history-deletion progress by loop turns rather than timer gaps to detect blocking on CI runners. * fix(worktrees): prevent deletion from blocking Orca Worktree deletion can now proceed even if filesystem watchers or history cleanup operations hang, preventing Orca from freezing. Changes: - Fence install slots with tokens instead of counters so removals can abandon wedged installs without corrupting later removals - Timeout-bound watcher unsubscribe operations with a shared drain budget - Move JSON serialization of large usage caches from queue-time to write-time to avoid blocking main thread - Async tombstone + schedule history tree deletion instead of blocking recursive rmSync during GC, preventing main-thread stalls ~10s after startup * Extract usage cache writer into reusable durable snapshot class Consolidates serialized durable-write and generation-veto logic from three usage stores into UsageCacheSnapshotWriter. Eliminates duplication, centralizes multi-MB JSON serialization on the main thread via write-queue serialization, and vetoes superseded snapshots to avoid wasted rewrites. * fix(worktrees): prevent deletion from blocking Orca Worktree deletion used to recursively delete large session trees (hundreds of MB) on the critical path, stalling the event loop. Instead, rename trees into a `.pending-delete` tombstone queue and reclaim them asynchronously off the removal's critical path. Extracted host tree removal into a reusable helper (`removeHostTree`) that centralizes Windows retry logic. Added usage-cache flush on quit to prevent data loss when scans complete right before shutdown. Improved watcher removal deadline management with reserved tail slices for the final unsubscribe, and added retry logic for tombstone removals that fail once under transient Windows locking. * fix(history): retry failed session tree removals Tombstoned session trees whose removal fails transiently (e.g., EBUSY under Windows AV) are now re-queued in-process with bounded exponential backoff instead of sitting until the next HistoryManager construction. Prevents a single stuck tree from blocking the entire Orca process. |
||
|
|
6d4e335001 |
feat(worktrees): support project-level worktree.sharedDirectories in orca.yaml (#10459)
* feat(worktrees): support project-level worktree.sharedDirectories in orca.yaml Follow-up to #7549: `.worktreeinclude` copies gitignored paths into each new worktree, which is right for `.env`/`.vscode/` but wrong for large rebuildable directories. Copying `node_modules` per worktree is slow and duplicates disk, and each worktree's install then diverges. Adds `worktree.sharedDirectories` to `orca.yaml` — a versioned, in-repo list of gitignored directories that are symlinked (shared) into every new local worktree, so one install serves them all. Adds to, never replaces, the per-user Worktree Shared Paths setting. `createWorktreeSharedPaths` uses a new 'share' materialization mode that always symlinks. The existing 'link' mode APFS clone-copies on macOS, which would give each worktree an independent node_modules and defeat the point; 'link' and 'copy' behavior are unchanged. Entries must exist as gitignored directories in the primary checkout; absolute paths, `..` traversal, and `.git` are rejected. Resolution never throws, so a malformed orca.yaml cannot block worktree creation. Remote (SSH) creation skips this, as it does symlink paths and `.worktreeinclude`. Closes #10451 * fix(worktrees): keep worktrees deletable after sharing a directory A directory-only ignore rule (`node_modules/`, the common spelling) matches the primary checkout's real directory, so the shared directory resolves and gets symlinked — but it never matches the worktree's symlink, so Git reports that link as untracked. Deletion only tolerated the per-user shared paths, so every worktree in such a repo became permanently dirty: the clean preflight threw "uncommitted or untracked changes" and `git worktree remove` refused without --force. Feed the configured `orca.yaml` shared directories into the same tolerate-and-unlink machinery the per-user shared paths already use, at both deletion call sites. The names are read unfiltered, since the create-time resolver drops exactly the entry deletion needs most. * test(worktrees): register createWorktreeSharedPaths in the runtime symlink mock orca-runtime.ts imports createWorktreeSharedPaths, but the vi.mock factory for ../ipc/worktree-symlinks never listed it. Vitest resolves omitted exports lazily, so this only stays green because no runtime test configures a repo with worktree.sharedDirectories — the first one that does would fail on a mock resolution error rather than on its own assertion. * fix(source-control): don't count shared symlinks as uncommitted changes A directory-only ignore rule (`node_modules/`) matches the primary checkout's real directory but never the worktree's symlink, so Git reports the shared link as untracked for the life of the worktree. That made every affected worktree read as dirty: a phantom row in the diff view, and Create PR blocked with `blockedReason: 'dirty'` telling the user to commit an entry they cannot commit, because it is a symlink Orca created. Status and the review-creation preflight now drop untracked entries that are both declared shared (per-user shared paths or orca.yaml sharedDirectories) and actually symlinks on disk. Both conditions are required, so a regular file at a declared name, or a symlink nobody declared, still counts as user work. The decision fails closed: anything not positively identified stays dirty. The preflight moves to `--porcelain -z` so paths with spaces or non-ASCII bytes are compared raw rather than C-quoted, with a parser that consumes the origin field a rename emits instead of reading it as its own record. Symlink detection moves to a leaf module: importing it from ipc/worktree-symlinks would pull APFS cloning, and its child_process dependency, into the status graph. SSH is unaffected and left alone — remote worktree creation skips the symlink and shared-directory passes, so a remote worktree never has one. * fix(source-control): wire shared links into local status * fix(worktrees): resolve the status repo once and reject uncollapsed shared paths `git:status` resolved the registered worktree's repo twice per call — once inside `getLocalGitOptionsForRegisteredWorktree` and again for the shared-link lookup — walking every repo's worktree meta on a polling path. `apps/./web` also survived `sharedDirectories` normalization: `resolve()` collapses it when the symlink is created but Git reports the collapsed path, so every later comparison misses and the link reads as permanent untracked work. Also stop resolving shared links for SSH repos in review creation: `repo.path` names a path on the remote host. Adds the missing wiring coverage for review creation and runtime status, plus the untracked-only conjunct in both filters — all four were mutation-verified to leave the suite green before these tests. * test(worktrees): pin the resolver-to-status seam for shared directories The resolver's output and the status filter were only tested apart — status used a hardcoded `['node_modules']`. Feed the resolved directories back through `getWorktreeSharedLinkPaths` into a real `getStatus` so a resolver that ever returned a differently-spelled path can no longer leave the link showing as a phantom untracked row. * fix(worktrees): try a directory junction before a symlink on Windows A plain `fs.symlink` needs Developer Mode or admin on Windows, so an ordinary Windows user got EPERM, the per-path catch logged and continued, and the worktree came up with no shared directory and no signal. A directory junction needs no privilege, and the rest of the codebase already uses one for win32 directory links. The symlink stays as a fallback rather than being replaced: a junction cannot target a UNC path, and a WSL project's repo lives behind one, so replacing it outright would trade the local-volume bug for a WSL regression. Safe for the removal path either way — Windows reports a junction as both a symlink and a directory, so the `isSymbolicLink()` unlink that runs before `git worktree remove` still fires and still refuses to follow it. * fix(worktrees): keep NUL bytes and tolerated links out of the removal error The removal preflight switches to `git status --porcelain -z` whenever it has shared links to tolerate, then attached that raw stdout to the error. `.trim()` does not strip interior NULs, so the message reached the user as `?? node_modules<NUL>?? precious.txt<NUL>` — raw control bytes, and it named the shared link, the one entry that is not the user's work and cannot be committed away. Parse the NUL-delimited output once and use it for both the clean verdict and the error text, so the two can never disagree about what blocks removal. The `-z` switch stays: it is what keeps paths with spaces or non-ASCII names comparable against the configured entry. * chore(worktrees): drop stray reformatting and note why the SSH guard exists Committing the merge staged 792 files, so lint-staged ran the formatter across all of them and rewrapped three renderer files that were already unformatted on main. Nothing was lost — they were byte-identical to main ignoring whitespace — but they showed up in the pull request as unrelated changed files. Restored to main's exact bytes. Committed with --no-verify on purpose: the pre-commit formatter is what introduced the rewrapping, so letting it run again would simply reapply it. Every check it would have run was run by hand instead — lint, typecheck, and the IPC and source-control suites all pass, and the three restored files are expected to fail a format check because that is main's current state. Also records why the connection guard on the shared-link lookup is not dead code: the remote dirty check ignores those paths, so the guard's only effect is avoiding a stray local read and the bad cache entry it would leave behind. * refactor(source-control): drop a scan-everything guard and freeze the cached list The dirty check built a filtered array only to read its length, so it always scanned every status record; asking whether any record is untracked stops at the first one and reads the same either way. The cached shared-directory list was also handhanded out by reference, so a caller that mutated it would corrupt every read for the rest of the cache window. Marking the return readonly prevents that at compile time; copying on return would work too but would allocate on the status-polling path, and there is exactly one caller, which only spreads it. |
||
|
|
a49d68f8c2 |
perf(git): overlap getBranchCompare's head-of-chain reads (#10895)
* perf(git): overlap getBranchCompare's head-of-chain reads
Four git spawns ran strictly in series before any compare work began:
branch --show-current, the base-ref probe, rev-parse HEAD, and rev-parse <base>.
Three are independent -- compareRef is display-only metadata and HEAD's oid does
not depend on the base ref -- so they now run concurrently. The fourth was
redundant outright: the probe already runs `rev-parse --verify --quiet
<ref>^{commit}` and discarded the oid it printed, which was then re-resolved by a
second spawn. resolveWorktreeBaseCommitOid returns that oid so it can be reused;
hasWorktreeBaseCommitRef now delegates to it, leaving its other 4 callers
untouched.
3.6-3.7x on a short remote base label (192ms -> 52ms), 1.44x on an
already-qualified refs/... base, which skips the probe by design.
Reuse is keyed by ref: resolveWorktreeAddBaseRef returns at its first successful
candidate, so only that ref's oid is ever read back. Peeling is safe because only
refs/heads and refs/remotes candidates reach the probe, where ^{commit} is a
no-op.
No new git features: this removes a spawn rather than adopting an option.
Co-authored-by: Orca <help@stably.ai>
* fix(git): preserve compare semantics across providers
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
|
||
|
|
5a30c5c2ed |
perf(git): read both diff blobs concurrently (#10781)
* perf(git): read both diff blobs concurrently The diff loaders awaited their two sides in series, so the second `git show` could not start until the first had returned. The reads are independent, so that was pure added latency on every diff the review panel opens: ~47 ms sequential vs ~24 ms concurrent, a saving of ~23 ms per diff. Covers the merge-base, commit, and staged loaders, plus the unstaged path where the working-tree read is independent of the index->HEAD chain. The unstaged left chain itself stays sequential because its second step depends on the first. The staged coalescing test asserted the sequential shape (one spawn, then the next); it now pins the contract that actually matters — eight identical reads still collapse to two spawns, one per side. * test(perf): interleave the diff-blob benchmark arms Running one strategy's whole batch before the other's lets cache warming, CPU frequency drift, and background load correlate with the strategy being measured. Alternate the arms per iteration, alternate which goes first, and report medians so that drift stays common to both. Also reject malformed env settings rather than truncating them — Number.parseInt accepts "10foo" and 3.5. Interleaved result confirms the original: 1.90x-2.03x, ~24 ms saved per diff. |
||
|
|
19d082a164 |
fix(github): resolve owner/repo through SSH Host aliases (#10284) (#10361)
* fix(github): resolve owner/repo through SSH Host aliases (#10284) Expand OpenSSH Host → HostName via ssh -G before classifying github.com identity so PR merge works when origin is git@alias:owner/repo.git. Transport URLs stay unchanged so IdentityFile selection is preserved. Do not long-negative-cache indeterminate ssh -G failures. * fix(github): harden SSH alias resolution |
||
|
|
eb545aaa59 |
fix(worktree): collapse duplicate "Local Mac" run targets in the host picker (#10472)
* fix(worktree): collapse duplicate "Local Mac" run targets in the host picker A linked worktree added as its own project projects a second ready host setup on the same project+host, so the run-target picker rendered N identical "Local Mac" rows differing only by path. Only the first was reachable — resolveWorkspaceCreationTarget takes the first project+host match — so the extras pointed at paths that may no longer exist. - Dedupe ready setup options by host in the picker (display fix for profiles that already hold duplicates). - Canonicalize a stale draft's setup id to the setup the picker shows, so the displayed path is the path the workspace is created in. - Reject a linked worktree at repos:add when its main checkout is already tracked, preventing new duplicates. * fix(worktree): only dedupe a linked worktree against a git main checkout Review follow-up: the repos:add guard matched any tracked repo on the main checkout path, including a folder-kind record. A folder repo does not project onto the same project as the git worktree, so matching it would suppress a legitimate add without deduping anything. |
||
|
|
b31e9bb03d |
fix(worktree): bound the .worktreeinclude copy so a huge include can't freeze workspace creation (#10540)
* fix(worktree): bound the .worktreeinclude copy so a huge include can't freeze creation `.worktreeinclude` copying was bounded in entry count (1000) but unbounded in bytes and files, and awaited inline during worktree creation. A repo listing `node_modules` froze creation for minutes behind the create dialog on Linux and Windows, where the fallback is a full `fs.cp` (macOS gets a cheap APFS clone). Measure each copy-mode source against a cumulative budget (2 GB / 50k files) before the first byte is written, and refuse the entries that bust it. Refused entries ride the existing `CreateWorktreeResult.warning` channel so a workspace never silently comes up missing its included files. Pre-measurement rather than mid-copy abort: `fs.cp` ignores its `signal` option, so a started copy cannot be cancelled and would strand a partial tree. Refusing up front means there is no partial state to clean up. * fix(worktree): don't charge bytes for copy-on-write clones, and bound the sizing walk Two defects in the copy budget, both found by review: - The byte limit was applied on macOS, where the copy is an APFS clonefile. Measured: a 2.7 GB tree clones in 22 ms and consumes no disk. Refusing it on a 2 GB byte ceiling denied work that was already free — a regression on the one platform this bound was never meant to touch. Bytes are now charged only when a byte-for-byte copy will actually run; the volume probe that decides this is the same cached df+diskutil pair the clone runs, and writes nothing, so the "refuse before the first byte" invariant holds. The entry limit still applies everywhere: inodes are real work even on the clone path. - A refused entry consumed no budget, so a `.worktreeinclude` listing many over-budget directories paid a fresh full-limit walk for each one — up to 1000 x 50,000 lstat calls, re-creating the stall this bounds. The walk is now charged against its own ceiling whatever the verdict. Also documents that `admit()` must be awaited sequentially (CodeRabbit). * fix(worktree): give the sizing walk headroom so one huge entry can't starve the rest The walk ceiling added in the previous commit was seeded with maxEntries, the same number the entry limit uses. Sizing an entry that busts the file-count limit walks maxEntries + 1, driving the ceiling negative, so every later `.worktreeinclude` entry was refused without being measured at all. That regressed the common case: a repo listing `node_modules` plus `.env` used to get `.env`; it silently got nothing. Reproduced, and now covered by a test that fails when the headroom is removed. The walk now gets 5x the entry budget, so total sizing work stays bounded (<=250k lstat per materialization, vs the 1000 x 50k this ceiling exists to prevent) while ordinary lists never reach it. Entries refused because earlier ones exhausted the walk report a distinct 'sizing' reason, so the warning stops quoting size limits at a 4-byte file that was never measured. * fix(worktree): bill a failed clone's bytes, and blame the right ceiling Two follow-on defects from the copy-on-write fix: - A predicted APFS clone that then failed mid-copy (EPERM, ENOSPC) fell through to a real `fs.cp` whose bytes were never charged, because the entry had been admitted on the premise that cloning is free. That reopened the unbounded copy on macOS. The measured size is already known, so the fallback now bills it and refuses if it no longer fits, reporting the entry as skipped instead of silently copying gigabytes. A clone that was never viable (ApfsCloneUnavailableError) was already charged as a real copy, so that path keeps falling back as before. - The walk ceiling is also applied inside the measurement via min(remainingEntries, remainingWalk), and when the walk term bound, the refusal was still reported as 'entries' — telling the user a 3-file directory busted a 4-file limit. It now attributes to whichever ceiling actually bound. Also fixes the singular warning text, which said "entry X was not copied ... copying them would exceed ... Copy them in manually". * fix(worktree): flag a partial clone leftover, cap the warning, cover two branches - A clone that fails partway only removes an *empty* reservation, so leftovers can survive at the target. Reporting that entry as simply "not copied" sent the user to copy it in manually, straight into a half-populated directory. Those skips now carry mayBePartial and the warning says to check the path first. Cleaning up the leftovers stays the deferred follow-up it already was. - The warning enumerated every skipped path. `.worktreeinclude` allows 1000 entries and all of them can be skipped, so it now names five and counts the rest — an unbounded string is a poor look in a PR about bounds. - Two load-bearing branches had no test, both proven by surviving mutants: the `bytesAreCopied` short-circuit (reachable when a wedged df/diskutil makes the volume probe answer "no clone", so bytes are charged up front and must not be billed twice), and chargeBytes actually consuming budget for later entries. * fix(worktree): only flag directory clones as partial, and cap that list too - mayBePartial was set for every refused clone fallback, but only a *directory* clone can leave anything behind: the file path clones into a temp name and publishes with link(2), so a failure leaves nothing at the target. Sending the user to inspect a path that does not exist is its own small lie. - The partial-copy sentence sliced to five names without the "and N more" that the other sentence appends, so entries past the fifth were surfaced nowhere. Both sentences now share one nameList helper. |
||
|
|
159057c5d4 |
test(git): cover the false-positive class the header fix also removes (#10547)
The old anchored regex matched neither branch on a `[section "sub"]key = value` line, so the parser never left `[core]` and credited the next indented line to it — reporting sparse for a worktree git says is not. Fails on the pre-fix parser (returns true where git reports unset). |
||
|
|
9d02782969 |
fix(git): read core.sparseCheckout the way git does (#10537)
* fix(git): read core.sparseCheckout the way git does Sparse-checkout detection parsed git config line-by-line and only accepted a section header alone on its line, so git's legal same-line form `[core] sparseCheckout = true` matched neither branch and was silently skipped: a genuinely sparse worktree lost its badge and partial-checkout warning. It also read `config.worktree` unconditionally, although git honors that file only while extensions.worktreeConfig is on, so a stale worktree config could override the repo's real setting. Headers are now consumed left-to-right off each line (further headers and one assignment may follow), and config.worktree is read only behind the extension gate. Every new expectation was confirmed against real `git config --get`. * test(git): correct what git actually does with a trailing-junk config value Git does not reject `[core] sparseCheckout = true bogus = false` outright: it parses the line and takes the whole tail as one value (`git config --list` reports `core.sparsecheckout=true bogus = false`), then fails only the boolean coercion. The expectation is unchanged; the comment now matches the binary. |
||
|
|
879aad7dd6 |
oom(foundation): bound shared readers/limits + add BoundedMap primitive (#10299)
* oom(01): A1-shared-readers — reintroduce #10179 subset Files: 18 applied, 0 deleted (from |
||
|
|
1aaf049a4d |
fix(rate-limits): keep Codex PTY reset text for weekly-only plans (#8643)
* fix(rate-limits): keep Codex PTY reset text for weekly-only plans The PTY /status fallback parses '5h limit' and 'Weekly limit' lines by label, but the extracted reset text was only ever attached to the session window. Codex plans without a 5h session bucket (e.g. current Pro) produce a weekly-only parse, so the reset time the CLI printed was silently dropped. Fall back to the weekly window when no session window exists. * review: parse Codex PTY reset text per window into resetsAt * review: make Codex PTY status fallback work on codex >=0.145 * review: harden PTY status parse against model-scoped rows and styled output * fix(rate-limits): strip private PTY control sequences --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> |
||
|
|
b600e25fa1 |
feat(worktrees): support project-level .worktreeinclude (literal paths) for copying gitignored files into worktrees (#9791)
* feat(worktrees): copy project-level .worktreeinclude paths into new worktrees Read .worktreeinclude at the repo root (gitignore syntax) and copy matching gitignored paths from the primary checkout into each newly created local worktree, so .env and other local config carry over with zero per-user setup. - Literal patterns resolve by direct stat; globs match against ls-files --others --ignored --exclude-standard --directory (collapsed dirs keep huge repos fast); every candidate is re-verified with check-ignore so tracked or unignored files are never copied. - Copy semantics, never symlink: APFS clone-copy on macOS, real copy elsewhere, so each worktree owns its files (unlike repo.symlinkPaths, which it merges with rather than replaces). - Failures never block worktree creation. - Remote (SSH) creation skips it, same as symlinkPaths. - Split APFS clone helpers into worktree-apfs-clone.ts (max-lines). Closes #7549 * fix(worktrees): harden worktree include copying * fix(worktrees): support nested includes on Git 2.25 * fix(worktrees): bound include copy costs * fix(worktrees): close include correctness and perf gaps * fix(worktrees): preserve included copy semantics * fix(worktrees): harden include resolution * fix(worktrees): preserve bounded include resolution * fix(worktrees): bound include filesystem resolution * fix(worktrees): harden include matching * fix(worktrees): tighten include matching and scan bounds * fix(types): use concrete filesystem stat types * fix(worktrees): harden included path materialization * perf(worktrees): stop include parsing at resolver budgets * chore(skills): refresh bundled skill manifests * refactor(worktrees): reduce .worktreeinclude to focused literal-only scope The reviewed implementation grew well past the ticket (#7549), which asks for a size-M feature that reuses existing worktree machinery. Trim back to the minimal change that solves the reported problem safely: - Resolver now supports literal files and directories only. Glob/negation lines are skipped with a warning (documented follow-up), which removes the entire user-controlled-regex ReDoS surface, the CPU/byte budgets, the git enumeration scan, and the case-sensitivity engine. The filesystem + git check-ignore handle existence and case for free. - Copy layer folded back into worktree-symlinks.ts (link/copy modes share one loop); dropped worktree-path-copy.ts, worktree-target-safety.ts, the descendant-dedup/realpath/target-parent machinery, and the per-materialization APFS filesystem cache. Kept the df/diskutil probe timeout. - Reverted unrelated changes: check-ignored-paths timeout param and the git-binary-compatibility enumeration tests. Net: -1903/+172 across the include+copy code. Behavior for the ticket's cases (.env, .env.local, .vscode/, node_modules, config/secrets.json) is unchanged; gitignored-only + copy-not-symlink semantics preserved. Closes #7549 * fix(worktrees): dereference symlinked .worktreeinclude entries + cache APFS volume probe Two issues found by review + perf audit of the copy path: - Correctness (HIGH): a listed entry that is itself a gitignored symlink was copied AS a symlink (fs.cp dereference:false), and the darwin APFS branch was skipped for all symlink sources. Editing the worktree's copy then wrote through to the shared/primary target — inverting copy-mode's 'each worktree owns its files' guarantee, and escaping the worktree entirely if the link pointed outside it. Now resolve realpath for a top-level symlink in copy mode so we copy content; nested symlinks inside a copied dir stay as-is (cp -R semantics). - Perf: assertSameApfsVolume ran df+diskutil per copied path (4 subprocesses each), so an N-entry include spawned ~4N short-lived processes on the macOS create hot path, all re-probing one volume. Add a per-materialization device-keyed cache: one probe per distinct volume (4N -> ~4). Tests: symlinked-file and symlinked-dir dereference regressions (no leak to primary); APFS volume probed once regardless of copied-path count. |
||
|
|
772081577e |
Fix fork PR/MR worktree creation race via durable review-head refs (#10429)
* Fix fork PR/MR worktree creation race via durable review-head refs
When creating a fork PR/MR worktree, concurrent `git fetch origin` operations
clobber the shared FETCH_HEAD, causing the wrong commit to be checked out.
Fetch PR/MR heads into dedicated per-review refs (`refs/orca/pull/<N>`,
`refs/orca/merge-requests/<N>`) that persist and isolate each head from other
fetches. Gracefully keep the compare-base when the fetch fails but the local
ref already exists, avoiding silent fallback to the wrong branch on transient
network errors.
* Bound PR/MR head fetches with 60s timeout
Prevent PR/MR creation from hanging when a remote is stalled or
unreachable. Both GitHub and GitLab head fetches now enforce a
60-second timeout, matching the bound used in the create-path
fetch. Durable refs (refs/orca/pull/*, refs/orca/merge-requests/*)
decouple the ref from FETCH_HEAD, preserving legacy client semantics.
* test: align CI expectations with main PowerShell/sparse regressions
PR checks merge into main, which recently changed PowerShell launch args
(cwd restore after profiles) and sparse-checkout detection (require
core.sparseCheckout). Derive PowerShell spawn args from the production
resolver, mock the sparse config flag, reset shared worktree list scan
cache between tests, and stop requiring floating polls to avoid getRepos
hydration.
* Address review follow-ups on durable review-head refs
- Unify PR review-head remote selection: local and SSH GitHub paths share
resolveGitHubReviewHeadRemote, which prefers the remote mapping to the
hosting GitHub project (upstream before origin, matching work-item/API
candidate order) so contributor clones fetch refs/pull from the repo
that actually hosts the PR.
- Soft-keep durable review heads: when the PR/MR head fetch fails but
refs/orca/pull/<N> / refs/orca/merge-requests/<iid> still resolves,
keep the pinned SHA (warn) instead of failing resolve, mirroring the
compare-base fallback. Extracted shared compare-base soft-keep into
compare-base-ref-fetch.ts.
- Extract fetchGitLabMergeRequestHeadRef (local + SSH) parallel to the
GitHub helper; bound its local fetch with the shared 60s timeout.
- Share relay-style fetch validation (positive safe-integer id, remote
not starting with "-") between relay and local helpers via
review-head-tracking-ref.ts; move REVIEW_HEAD_FETCH_TIMEOUT_MS there.
- Drop the githubPullRequestHeadLocalRef re-export; resolve head SHAs via
rev-parse --verify <ref>^{commit}.
- Add GitLab anti-FETCH_HEAD regression test plus durable-head soft-keep
and remote-selection unit tests.
Co-authored-by: Orca <help@stably.ai>
* test: supply live getRepos for terminal-retirement hydrates
Main's headless tab hydrate (#9343) skips worktree keys whose repo is not
in getRepos. Retirement tests that rebuild mobile tabs from a persisted
session now advertise the fixture repo as live so PR Checks merge stays green.
* fix(editor): extract RichMarkdownEditor props to stay under max-lines
Main's SSH external-image wiring (#10323) pushed RichMarkdownEditor.tsx over
the 400-line tsx budget, failing PR Checks lint on every merge into main.
Move the props type into a sibling module so the component stays under the
limit without disabling max-lines.
* Make durable review-head refs remote-identity scoped
Embed remote name + URL hash into refs/orca/pull|merge-requests refs to prevent soft-keep from serving wrong project's PR/MR when FETCH_HEAD is clobbered by concurrent fetch. Fetch functions now return the written ref path (writer-authoritative) so callers rev-parse exactly what was fetched, not re-derive identity. Soft-keep only applies to transient errors (timeout, network); fails hard on missing refs, auth failures, and stale relay. Relay returns localRef so client avoids re-hashing (URL normalization can disagree).
---------
Co-authored-by: Orca <help@stably.ai>
|
||
|
|
e3cc08f185 |
fix(worktree): don't flag a disabled sparse checkout as sparse (#9922)
`git sparse-checkout disable` restores the full working tree and sets core.sparseCheckout=false, but deliberately leaves <gitdir>/info/sparse-checkout in place so the checkout can be re-enabled with the same patterns. detectSparseCheckout treated the mere presence of that pattern file as "sparse", so a fully-populated worktree kept showing the sparse badge and the misleading "Partial checkout. Files outside these paths are not on disk." tooltip. Gate the fast-path fs.stat behind a config read that confirms core.sparseCheckout is actually enabled (shared repo config or per-worktree config.worktree, honoring git's precedence). The config read runs only when a non-empty pattern file exists, so it does not reintroduce the per-poll subprocess fan-out PR #1290 removed, and it reads git's config files directly (no subprocess). Adds a real-git regression test (enable -> disable leaves file -> not sparse) and unit tests for the git-config boolean parser. |
||
|
|
1ace87c155 | fix(wsl): route global CLI fallbacks to user-pinned terminalWindowsWslDistro (#9734) | ||
|
|
aab112933e |
Revert "fix(memory): bound OOM-prone accumulators (#10179)" (#10255)
Co-authored-by: Orca <help@stably.ai> |
||
|
|
8f40ddf328 | fix(memory): bound OOM-prone accumulators (#10179) | ||
|
|
71bbfa022d | fix(worktrees): bound shared worktree list scans (#9786) | ||
|
|
a97c160363 | fix(github): preserve GHES non-default auth ports (#9680) | ||
|
|
971b167548 |
fix(github): load PR diffs for Enterprise remotes (#8932)
* fix(github): load PR diffs for Enterprise remotes * fix(github): encode PR content paths by segment * Fix PR review actions failing on GitHub Enterprise remotes - Threads GitHub host identity (not just owner/repo) through the client, work-item-details, issues, and RPC layers so gh commands target the correct Enterprise server instead of silently falling back to github.com - Adds a shared github-api-repository helper to resolve/host-qualify repo identity consistently across REST, GraphQL, and CLI shorthand calls - Scopes the gh rate-limit breaker and singleton rate-limit snapshot by host/runtime so a github.com block or probe can't affect GHES or WSL - Coalesces concurrent host-auth probes and paginates PR file fetching beyond 100 results - Propagates `host` through renderer PR caches, checks-panel keys, and preload IPC types so Enterprise and github.com data never collide * Route gh host qualification through runner options instead of argv sniff Move GHES/GH_HOST resolution from parsing --hostname/--repo out of gh argv to an explicit options.host passed through ghExecFileAsync, since SSH-backed repos spawn gh with no cwd and argv sniffing couldn't reliably detect the target host. The runner now injects --hostname and qualifies --repo/-R at spawn time from options.host, and rate-limit scoping/guards use the same explicit host instead of inferring it. Also adds a shared githubRepoIdentityKey helper to keep cache/store keys consistent with the new host-aware repository identity. * Fix gh CLI GHES host pinning and rate-limit scope leaks - Pin `--host` on every gh call site so a process-level GH_HOST can't silently redirect requests, and qualify `-R`/`-R=` repo shorthand alongside the existing `--repo=` handling. - Check the target scope for an active rate-limit block before each WSL/native or host fallback retry, not just on the initial attempt, so a blocked scope can't be hit again through a fallback path. - Compute idempotency once per call instead of re-deriving it after fallback reassigns args. * Fix GitHub Enterprise host identity loss across PR/work-item paths - Thread `host` through mobile PR RPC params, IPC work-item lookups, and RPC schemas so GHES identity survives the renderer/mobile/main boundary instead of silently falling back to a same-named github.com repo. - Qualify `--repo`/`-R` args for github.com too (not just GHES), since gh resolves bare shorthand against a process-level GH_HOST that can redirect pinned github.com commands. - Cache `getOriginGitHubApiRepository` to avoid a per-call uncached `git remote get-url` round trip on connection-backed repos. - Add a local-fork fallback in `getWorkItemDetails` so PRs living on a base repo (not visible via the origin slug) still resolve via cwd. - Centralize the github.com-vs-GHES host predicate in `isDefaultGitHubHost` so cache keys, quota scoping, and identity checks can't drift out of sync. * Make repository identity host-aware across all GitHub surfaces Generalize the auth-gated enterprise resolver to any remote and build a cached hosted-identity family (origin/issue/candidates/source) on top of it, then migrate every github.com-only consumer: Tasks listing/counting, branch-to-PR discovery, push targets, fork upstream, issue operations, Projects, web links, avatars, and PR-link facts. Scope the rate-limit breaker probe per runtime:host and classify WSL UNC cwds correctly. Co-authored-by: Orca <help@stably.ai> * Fix expected slug to include host field in GitHub PR link test Updates the smart-source paste-intent test fixture to match the repository slug shape that now carries a `host` field, keeping GHES host identity intact through the paste-intent parsing path. * Surface per-host gh auth state for GitHub Enterprise diagnoseGhAuth accepts the host a surface needs credentials for, scopes the account/scope diagnosis to that host, and reports whether gh has any login there; GhAuthErrorHelp renders host-qualified login/refresh commands so an unauthenticated GHES host stops masquerading as a github.com scope problem. Also fixes the mobile paste-intent expectation for host-carrying parsed links. Co-authored-by: Orca <help@stably.ai> * Bound GHES identity caches and preserve non-default ports in host identity Cap the origin-repo and host-auth caches like ownerRepoCache; keep ports from remote/link URLs so GHES on a non-default port is a distinct identity; make positional github.com slugs explicit against GH_HOST; compare work-item sources by host-aware identity key; bail cwd-less branch lookups when no repository candidate resolved; thread host through the renderer work-item slug lookup. Co-authored-by: Orca <help@stably.ai> * Thread GitHub host through issue detail requests Incorporates ghes-issue-host-support (ed6bb96ef): one hosted issue repository identity is resolved before the details fan-out so comments, timeline, participants, and mention lookups cannot drift across hosts, with SSH guards so unresolved issue/PR repositories never fall through to gh's default host. Co-authored-by: Orca <help@stably.ai> * Scope remaining GitHub rate-limit accounting * Resolve typed PR lookups across hosted repository candidates getWorkItem's PR path probes upstream-then-origin hosted candidates instead of origin alone, so fork checkouts resolve the base repo's PR with the right host; issue detail resolution reuses the up-front hosted identity and keeps the SSH unresolved-host guards. Co-authored-by: Orca <help@stably.ai> * Refactor GitHub repository execution setup * Carry host on smart-submit link intents Co-authored-by: Orca <help@stably.ai> * Carry the project host on GitHub item dialog origins Co-authored-by: Orca <help@stably.ai> * Keep GHES web ports but drop SSH transport ports in host identity Supersedes PR #9118 on this branch: http(s) remote ports identify the Enterprise web/API endpoint and are preserved, while ssh/git transport ports (including ssh.github.com:443) never leak into gh's host identity. Replaces the ssh.github.com:443 special case with the structural protocol split and ports the PR's parsing test suite. Co-authored-by: Orca <help@stably.ai> * Support GitHub Enterprise diffs and mutations with host-scoped caches Parse GitHub host identity from work-item URLs and carry it through PR/issue mutations, labels, and assignments. Bound rate-limit and scope-probe caches (1024 and 512 entries) to prevent unbounded growth when interacting with multiple GHES instances. Normalize repository identity keys to include host so github.com and GHES slugs don't collide in cache and equality checks. * Support GitHub Enterprise diffs and mutations with host-scoped caches - Carry host identity through PR mutations and reads so fork PRs on different GHES instances don't collide in cache or state tracking. - Validate host authentication before routing requests to unconfigured Enterprise servers; ambient credentials must never reach untrusted hosts. - Scope rate-limit guards and spend tracking per host so GHES quota stays independent from github.com quota. - Respect explicit --hostname arguments in gh CLI calls ahead of GH_HOST or ambient defaults, so breaker state follows the actual request target. - Detect implicit WSL runtimes from UNC paths for consistent host auth and execution-options scoping across mobile and desktop clients. * Support GitHub Enterprise work-item diffs with host-scoped execution Enterprise PRs must use their selected host consistently across diff, comments, and file-content loads. Validate repository slugs before authenticated execution to prevent path-injection via renderer overrides. Scope project browsing cache and rate-limit tracking by host to prevent cross-host pollution. Use parsed URLs as authoritative over ambient hosts for project resolution. * Support GitHub Enterprise work-item diffs with host-scoped execution Preserve host identity on PR/issue work items throughout the mutation and diff pipeline so Enterprise instances (including ported endpoints like github.acme.test:8443) can execute mutations without ambiguity. Rate-limit gh commands by the pre-qualified --repo host, cache auth state per ported host, and surface Enterprise hosts in project metadata and error messages. * fix(review): drop dead rateLimitGuard/noteRateLimitSpend re-export Both callers (project-view.ts, mutations.ts) moved to the host-scoped repositoryRateLimitGuard/noteRepositoryRateLimitSpend; the bucket-only re-export in internals.ts had zero importers left. Co-authored-by: Orca <help@stably.ai> * fix(ci): split Enterprise host work-item tests under max-lines Move GHES/SSH host-routing cases out of work-item-details.test.ts so the suite stays within the 800-line test max-lines budget. * test(github): align mocks with host-scoped repository resolution - Route origin repository resolution through getOwnerRepoForRemote, not getOwnerRepo, to match production path - Pin github.com host on origin results so host-less fixtures pass host gate in resolveGitHubApiRepository - Add generation-based invalidation to prevent stale slug-cache writes from in-flight resolutions - Fix ref-sync race in ProjectPicker: use useLayoutEffect so committed tree owns browse cache key - Defer handledCrossRepoUrlRef assignment in SmartWorkspaceNameField until resolution succeeds - Update Enterprise host routing: found work items must not silently fall back to default host when unresolved - Normalize GHES avatar URLs: accept explicit port 443 as canonical form, not a fallback trigger --------- Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Co-authored-by: Orca <help@stably.ai> |
||
|
|
e109e78ebf |
fix(source-control): keep huge change sets responsive (#9477)
* fix(source-control): keep huge change sets responsive * Fix cancellation and retry handling for capped status * Harden capped status for conflict-heavy repositories * Harden capped status recovery and cancellation * fix(source-control): preserve capped status correctness * fix(source-control): translate submodule status at render time --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> |
||
|
|
190de8223e |
refactor(comments): slim verbose comments in main integrations (git/providers/…) (#9543)
Collapse multi-line explanatory comment blocks into single-line "why" statements
per AGENTS.md ("Document the Why, Briefly"): drop restatements of the code and
mechanism narration; keep the non-obvious reason, external refs, and directives.
Comments-only — verified no code changed via a Babel/esbuild comment-strip
token-equality gate against origin/main; typecheck and oxlint clean.
Area: main — git, source-control, providers & integrations. 40 files changed, 1432 insertions(+), 4473 deletions(-).
Co-authored-by: Orca <help@stably.ai>
|
||
|
|
b320bcb374 | fix(gitlab): bound and coalesce auth probes (#9476) | ||
|
|
d67ede1594 |
Implement confirm-only PR panel composer with classified error blocking (#9428)
* Clarify PR panel guidance: classify errors and confirm-only composer Replace the ambiguous GitHub hosted-review boolean with a four-state evidence model (found/positive_unresolved/not_found/unknown) so "No PR found" never appears without an accepted lookup result. Classify GitHub refresh failures into types (rate_limited, auth, network, permission, repo_unavailable, gh_unavailable, unknown) for stable, honest copy. Confirmed-only composer: preserve drafts across transient failures; hide Create during hard errors and positive-unresolved evidence. Hard errors clear only when an eligibility request starts after the error and returns an accepted outcome. Propagate error types and unified retry schedule through the store. Sync mobile parity with shouldOpenChecksPanelCreateComposer gating. Localize all new copy. * Clarify PR panel guidance: classify errors and confirm-only composer Add reviewLookupOutcome to hosted-review eligibility and thread it through the panel so it never claims "No PR found" without accepted evidence. A failed lookup is unavailable, not a settled no-PR. Fail closed on positive unresolved evidence, hard refresh errors, and unavailable lookups. Add structured GitHub refresh-error classification with Retry-After parsing. Implement confirmed-only composer gating based on fresh, matching-context eligibility with hard-error clearing. Mobile gates on reviewLookupOutcome to prevent false Create claims. Surface throwOnFailure variants for each provider so transport failures cross the RPC boundary instead of collapsing to null. (Design success criteria 1–4; invariant 8.) * Add exec-error helpers for subprocess error classification Extracts stderr/stdout parsing and Retry-After detection into a lightweight module that can be imported without pulling in the heavier runner machinery. Supports PR-refresh error classification and proper rate-limit handling for gh commands. * test(mobile): include reviewLookupOutcome in create eligibility fixtures Create / Push & Create now fails closed unless the lookup is not_found. Update mobile test fixtures so accepted-no-PR cases can still proceed. * Add OrThrow mock variants to forge-provider test mocks forge-provider resolves branch reviews via the OrThrow variant so lookup failures surface as unavailable instead of "no PR found". |
||
|
|
d363c83ae3 |
fix(worktrees): force-remove clean worktree with initialised submodule (#9096)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: MarkXian <mark-xian@foxmail.com> |
||
|
|
a03a3dd51b |
Render png on mobile (#9087)
* Add mobile image-diff previews via shared data-URI builder - Extracts a `buildImageDataUri` helper (src/shared/image-data-uri.ts) shared by the desktop ImageViewer and mobile, so both trim whitespace-wrapped base64 and skip non-previewable mimes (e.g. application/pdf) the same way. - Adds mobile-diff-image-preview.ts to render binary git.diff results (add/modify/ delete) as images instead of falling back to "Binary preview unavailable". - Extracts resolveMobileFileTabDoc to consolidate the session file-tab loading logic (diff/image/html/text) out of the route file for testability. * Fix stale binary image fallback for empty modified diffs and relay reads - mobileDiffImageDataUri now distinguishes a true deletion (modified side absent) from a modify whose binary bytes arrived empty (relay/size-cap cases), returning null instead of the stale pre-change image - readWorkingDiffFile passes the file path to bufferToBlob so relay working-tree reads can detect previewable image extensions instead of always reporting empty binary content - add mobile-file-tab-doc.test.ts covering diff/image/binary/text resolution paths * Regenerate skill bundle manifest for 1.4.144-rc.2 Co-authored-by: Orca <help@stably.ai> * fix(review): trim comments to AGENTS.md's one/two-line why-only rule Comments in mobile-diff-image-preview.ts and mobile-file-tab-doc.ts ran 3-6 lines and narrated mechanism instead of stating only the non-obvious reason, per AGENTS.md's "Code Comments: Document the Why, Briefly" rule. Co-authored-by: Orca <help@stably.ai> * Distinguish read failures from true deletions in binary diff results - Working-tree stat/readFile errors and relay reads previously collapsed onto the same empty-content signal as a genuine deletion, letting previewers fall back to stale original bytes on a failed read. - Add modifiedDeleted/missing flags through status.ts, git-handler-ops, and git-working-file-read so only proven deletions trigger the original-bytes fallback; failed reads now return null. - Tighten buildImageDataUri to accept only image/* mimes instead of special-casing application/pdf. * fix(relay): expect missing:false on index blob maxBuffer overflow readBlobAtIndex now returns a missing flag so staged deletions are distinct from size-capped binary reads; update the overflow test. * Allow opening deleted files to show pre-delete text or image diffs Deleted files can now be opened to view their pre-delete content via git.diff (including images via modifiedDeleted). Only unresolved conflicts remain unopenable. Centralizes the canOpen rule in canOpenMobileGitStatusEntry() to keep opener guards consistent across the mobile source control UI. --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
f1d2fe5c65 |
perf(git-status): duty-cycle, cancel, and cache status polling to cut idle git load (#8922)
* feat(git-status): batch, cancel, and cache git status polling to cut idl - Add a single duty-cycled refresh scheduler (activity debounce + 60s safety timer) replacing multiple overlapping intervals, so status polling no longer runs near-continuously on large repos (#7983). - Let safety refreshes reuse cached numstat line counts instead of re-running diff --numstat every cycle, invalidated by head change, known mutations, and a bounded TTL. - Thread AbortSignal/request-token cancellation through IPC, RPC, and relay layers so a superseded or backgrounded git:status call is killed instead of finishing wastefully. - Fix automatic upstream/status apply ordering so a slow, older refresh can no longer clobber a newer result, and so an earlier refresh still applies when a later one fails. * Fix aborted git status scans being mistaken for completed empty results - An aborted scan/numstat pass now always rejects instead of silently resolving, so a cancelled request can't look like a valid empty status. - Stop clearing the line-stats cache key on abort, since an aborted pass never wrote to it — clearing was evicting a concurrent scan's healthy snapshot and forcing a redundant numstat recompute. * Fix aborted git status scans resolving as completed results Cancelled scans could still resolve with partial or stale data instead of rejecting, letting callers treat an aborted refresh as a valid status. Also stop counting aborted scan duration toward catch-up refresh pacing, which was stretching the next refresh interval by the full length of a cancelled (often slow) scan. * Add cancellable, generation-aware git status polling to cut stale scans - Route git.status through an abortable subscription per requestToken so cancelStatus can actually abort the remote scan instead of being a no-op, preventing wasted work and stale responses overwriting fresher state. - Bump the git status polling generation on push-target changes so an in-flight refresh against the old remote/branch can't apply stale upstream data to the new one. - Guard the stale-conflict poller against writes after unmount. - Retire pre-purge line-stat scans in the cache so an older in-flight scan can't repopulate a key after a token-scoped purge. |
||
|
|
6e2a4a824d |
fix(worktrees): stop surfacing prunable git worktrees as live workspaces (#8409)
* fix(worktrees): stop surfacing prunable git worktrees as live workspaces A worktree still registered in git but whose directory was deleted (git's `prunable` state) was enumerated as a normal workspace, producing repeated pty:spawn DaemonProtocolError / fs:readDir ENOENT loops and a blank pane. - Parse the `prunable` porcelain field (Git >= 2.36) in both the main and relay worktree-list parsers. - For Git < 2.36 (no `prunable` field), probe each linked worktree path for existence on the fallback line-block path, skipping locked registrations to mirror git's own prunable rules. - Omit prunable worktrees from the detected-workspace enumeration only; removal/cleanup flows keep seeing them. - Extend the real-binary compatibility contract with the 2.36 `prunable` boundary. Fixes #8389 Claude-Session: https://claude.ai/code/session_018Rg1Bpq4GGwmz613hq6RSD * fix(worktrees): pin the prunable/locked porcelain annotations to their real Git 2.31 boundary The prunable and locked annotations landed in Git 2.31, five releases before `worktree list -z` (2.36); only -z defines the capability fallback boundary. Correct the compatibility contract so a future matrix entry in the 2.31-2.35 range passes, and reword the fallback comments: on 2.31-2.35 the annotations still parse and the existence probe is a backstop; only Git <2.31 relies on it outright. * fix(worktrees): omit prunable registrations from the Space scan A prunable registration has no directory to size or reclaim, so Space rendered it as a dead "Missing" row whose checkbox stayed disabled with no prune/remove affordance (reported on macOS after a reboot cleared /private/tmp under 16 registrations). Skip prunable entries in the scan, matching the workspace enumeration; removal flows list worktrees separately and still see them. --------- Co-authored-by: kaynan <kaynan.camargo@terceiro-sky.com.br> Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> |
||
|
|
64be819790 |
fix(runtime): harden watcher and PTY teardown ownership (#8661)
* fix(runtime): retain watcher and PTY teardown ownership * fix(runtime): restore watchers after interrupted cleanup * fix(runtime): prevent stale watcher revival * test(runtime): cover watcher shutdown ownership * test(daemon): model physical PTY exit * fix(daemon): keep shutdown terminating when disposal cannot prove exit A rejecting host.dispose() (unreapable child past its exit deadline) left the shutdown RPC without its process.nextTick(shutdown) and skipped socket cleanup in shutdown(), stranding the daemon as an unreachable orphan after the stale-daemon replacement flow unlinks its socket. Log and continue: daemon exit reparents the child to init instead of blocking on it. * fix(runtime): keep local watching alive after an idle-kill deadline miss An idle child that outlived the exit deadline set shutdownRequested on the shared desktop supervisor, which has no retire-and-replace path — every later subscribe rejected supervisor_disposed and the roots were cached unwatchable, silently ending local file watching for the session. The idle path owns zero records, so there is no double-watch hazard; the zombie keeps its capacity reservation until physical exit and the next subscribe gets a fresh child. * fix(renderer): resync replayed paired-web file watches Transparent replay removed the implicit resync the old close-and-rebuild path provided: a replayed files.watch only reports changes from its own native setup, so changes during the reconnect gap were silently lost. Deliver a conservative overflow to consumers once the replayed watch is ready, matching the overflow-after-interruption contract everywhere else. * fix(runtime): address teardown review findings * fix(runtime): retry watches after teardown deadlines * Fix PTY descendant leaks on forced teardown * Fix jitter-sensitive terminal lifecycle test |
||
|
|
1a6abc87d1 |
Suppress Git Credential Manager OAuth popup loop in Orca-run git — clone, terminals/agents, setup hooks (fixes #7652) (#7986)
* Suppress Git Credential Manager OAuth popup on git clone (fixes #7652)
Orca's git runner disables the interactive credential prompt on every git
call that goes through gitExecFileAsync/gitStreamStdout, but the two raw
'git clone' spawns (desktop repos:clone and the runtime clone path) passed
no env, so they inherited process.env with no guard. On Windows a clone
that needs GitHub auth then makes Git Credential Manager pop its
'Connect to GitHub' OAuth window, and in a network-restricted intranet the
browser/device flow never completes while git's credential retry re-pops it.
Apply nonInteractiveGitEnv() to both clone spawns so the prompt is
suppressed (GCM_INTERACTIVE=never, credential.interactive=false,
GIT_TERMINAL_PROMPT=0). The credential *helper* is kept, so cached-token
clones for private repos still work; only the interactive fallback popup is
disabled and the clone fails fast with a clear error instead.
* Suppress GCM OAuth popup in agent terminals and setup hooks too (#7652)
The clone-spawn fix stopped Orca's own managed git from popping Git
Credential Manager, but git run in terminals and setup scripts inherited
process.env with no guard. That is the more likely source of the reported
loop: agents are told to run 'git pull --rebase'/'git fetch'/retry 'git
push' (preamble + conflict/push-failure prompts), and each retry re-pops
GCM's 'Connect to GitHub' window in a network-restricted intranet.
Apply the credential-prompt guard to:
- setup/archive/hook scripts (hooks.ts non-WSL exec env), which run
unattended on worktree create/archive.
- the shared PTY host env (buildPtyHostEnv), via a small
applyTerminalGitCredentialPromptGuard helper. Agent terminals are
guarded unconditionally (they cannot dismiss a GUI popup); user
terminals are guarded by default via the new
terminalSuppressGitCredentialPrompt setting so power users can opt out.
The credential helper is kept, so cached gh auth still works; only the
interactive fallback prompt is disabled. Verified end-to-end in a real
Orca terminal (GIT_TERMINAL_PROMPT=0 + GCM_INTERACTIVE=never by default;
absent when the opt-out is set).
* Scope user-terminal credential guard to Windows, add settings toggle, forward guard into WSL (#7652)
* Retrigger PR checks (Actions dropped the synchronize dispatch for
|
||
|
|
527c692b71 |
Improve git pull on remote (#8524)
* Fall back to a merge when a divergent pull has no reconciliation strateg - Git 2.27+ refuses `git pull` on divergent branches unless pull.rebase or pull.ff is configured. Retry with `--no-rebase` (Git's historical default) so pulls succeed out of the box on fresh hosts. - Skip the fallback whenever the caller already specified a reconciliation strategy (e.g. --ff-only, --rebase) so explicit policies still fail as expected on divergence. - Applied identically in the local git pull path and the relay/SSH git handler so both surfaces behave the same way. * Refactor divergent-pull merge fallback into shared helper Extracts the retry-as-merge logic (duplicated between local git and relay SSH pull paths) into `runPullWithDivergenceFallback` in git-remote-error.ts, so both callers share one implementation and test coverage. |
||
|
|
be258e23ec |
Add Grok orchestration group routing (#8058)
* docs: design Grok orchestration group * docs: plan Grok orchestration group implementation * fix: add Grok orchestration group * test(orchestration): accept Windows skill newlines * Fix @grok orchestration group matching and remove stale planning docs - Reuse the shared buildAgentNameRe matcher in groups.ts instead of a divergent local regex, so orchestration groups honor the same Windows launcher-suffix rule (grok.exe/.cmd/.bat/.ps1) as the rest of Orca's agent-title detection. - Add test coverage for real Grok OSC title shapes (spinner-collapsed, session titles) and Windows launcher-suffix titles. - Delete the now-completed design and implementation-plan docs for the Grok orchestration group work. --------- Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com> |
||
|
|
c3ccbdc306 | Fix worktree scan generation cache leak (#7675) |