Skip to content

fix(repos): keep trailing-space directory names that really exist - #25446

Open
ggbdpq wants to merge 3 commits into
stablyai:mainfrom
ggbdpq:fix/trailing-space-repo-path
Open

ggbdpq wants to merge 3 commits into
stablyai:mainfrom
ggbdpq:fix/trailing-space-repo-path

Conversation

@ggbdpq

@ggbdpq ggbdpq commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

ELI5

A folder whose name ends with a space (Google Drive exports cloud folders containing / that way on macOS) could not be added: Orca silently trimmed the space off the path, looked for the wrong folder, and failed with spawn git ENOENT. Now Orca first tries the path exactly as given — if that directory exists, it is used as-is; the trim only kicks in when it does not, which keeps the forgiving behavior when a stray space was just a typing slip.

What Changed

  • repos:create (repo-creation-handlers.ts) resolves the parent path raw-first: if the untrimmed spelling exists on disk it wins, otherwise the trimmed spelling is used (typed-typo tolerance, with a test locking it in).
  • The Create Project renderer flow (useCreateRepo.ts) sends the picker's path untrimmed so the main process can make that raw-first decision at all — previously it pre-trimmed and the real spelling never reached the handler.
  • The floating-workspace paths (floating-workspace-directory.ts) had the same disease in three spots: the trusted-directory list was trimmed while reading (breaking trust matching for real spaced paths), the terminal cwd resolver stat'ed the trimmed spelling (silently falling back to the default workspace when foo was real), and the setting sanitizer rejected real directories. All three now probe raw-first.
  • Tests: the two red-before cases (floating fallback to default; mkdir receiving the trimmed spelling) plus a typed-typo case pinning the fallback, and a round-trip characterization test over the worktree selector chain.

Why

Trim-on-sight conflates two different things: forgiving a stray space somebody typed, and rewriting a path the file system just handed us. A picker/drag path is already verified to exist, so rewriting it can only lose information. Raw-first is the smallest change that keeps both behaviors; the fallback direction preserves the old UX for genuine typos. Windows is naturally unaffected (NTFS forbids trailing spaces, so raw and trimmed spellings coincide there) — no platform branch needed.

Linked Issue

Fixes #25386

One honest scope note: the issue also reports selector_not_found when opening the same worktree from a second machine. We audited that whole chain — porcelain parsing, worktree id minting and splitting, selector matching, profile keys — and every point preserves trailing spaces; no defect to fix was found there, so this PR does not touch it. If the second machine registered the repo through a path that hit one of the trimmed spots fixed here, the registration fork would explain the symptom. We'd love to know how the repo was registered on machine two (picker vs. synced settings vs. typed) to chase the rest.

Visual Proof

N/A — no UI change; the before/after is in the git subprocess result (spawn git ENOENT vs. success), pinned by tests.

Testing

  • I manually tested these changes locally
  • Automated tests added/updated, or explained why not below

Commands actually run locally (macOS): new cases were red before the fix (floating fell back to the default workspace; mkdir received the space-less spelling) and green after — 6 files / 106 tests via pnpm test; adjacent worktree selector/porcelain suites (26 files / 324 tests) and renderer AddRepoCreateStep/app/repos-remote suites all green; pnpm tc and pnpm run typecheck:node clean; oxlint and oxfmt --check clean on all six touched files. Real Google Drive and a second machine were not available locally — the spaced-directory behavior is pinned at the handler level with real temp directories.

AI Disclosure

Investigation, coding and verification by GLM-5.3-Flash (ZCode agent); orchestrated, reviewed and committed by a main agent with the same model, human overseeing.

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

Pure path-handling change: no wire/compat impact, no spawn-argument changes (the paths were always passed correctly; they were resolved to the wrong spelling before), SSH remote-registration chain audited and untouched (its parent input is typed, where trim is the intended cleanup). Windows behavior is a no-op by NTFS semantics.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 60cefee7-8d45-40fb-ace5-1df145d244f7
📥 Commits

Reviewing files that changed from the base of the PR and between 1076497 and b0beec2.

📒 Files selected for processing (5)
  • src/main/ipc/floating-workspace-directory.test.ts
  • src/main/ipc/repos/remote-repo-creation.test.ts
  • src/main/ipc/repos/remote-repo-creation.ts
  • src/main/runtime/runtime-repository-registration-controller.ts
  • src/main/runtime/runtime-repository-registration-create.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/ipc/floating-workspace-directory.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

Repository creation now checks a parent path as entered before using its trimmed form. This applies to local, remote, and runtime repository creation. Floating workspace directory resolution and sanitization also check the raw path first. The renderer passes the original parent path to repository creation. Tests cover these flows and trailing-space paths in worktree parsing and matching.

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to b0bee

The reviewed path handling is mergeable after normal checks; no actionable issue remains.

Security Architecture Review

Security architecture risk: ⚪ Minimal · up to ab120

The change preserves real directory names without removing the existing path-validation or directory-trust controls. No material security risk introduced or worsened by this change was identified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The examined exposure is directory identity within existing repository-creation and floating-workspace operations. Preserving whitespace changes which local spelling is selected, but does not introduce another host route, connection identity or grant-writing authority.

Security Findings and Attack Paths

  • inferred — No introduced trust bypass was supported by the inspected flow. A renderer-supplied raw path still encounters main-process validation, and renderer settings cannot mint floating-workspace grants. Retaining the picker-approved canonical spelling restores the intended grant identity rather than transferring the grant to the trimmed directory.

Trust Boundaries and Controls

  • observed — With requireTrusted enabled, an untrusted resolved floating directory still falls back to the app-owned workspace. Without that flag, accessible ad hoc directories remain permitted by the existing resolver policy. The directory picker records canonical grants, while settings IPC removes renderer-provided grant-list updates.

Resilience and Maintainability Implications

  • observed — Creation retains exact-target deduplication checks, nonempty-directory rejection and ownership-sensitive Git-failure cleanup. Repository persistence and worktree preparation retain their existing ordering. Renderer reset, host-token and unmount guards discard stale results; they do not cancel the main-process operation. These mechanisms are unchanged, not proof of atomic or race-free execution.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 14 functions across 10 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: preserving existing directory names that end with trailing spaces during repository path handling.
Description check ✅ Passed The description is complete and relevant. It covers the user impact, implementation, rationale, linked issue, scope limits, testing, AI disclosure, platform considerations, and checklist. The Review s…
Linked Issues check ✅ Passed Issue #25386 requires exact preservation of existing trailing-space paths or a clear error. The reviewed code now probes raw parent paths before trimmed fallbacks in local, runtime, remote, floating-w…
Out of Scope Changes check ✅ Passed The production changes and tests address issue #25386. They preserve trailing-space paths during repository creation, runtime and remote registration, trusted-directory handling, terminal cwd resoluti…
  • Fix all pre-merge checks with AI
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 887aea73-99ac-42c6-acd2-0ac1b15f00c3
📥 Commits

Reviewing files that changed from the base of the PR and between a154562 and ab12085.

📒 Files selected for processing (6)
  • src/main/ipc/floating-workspace-directory.test.ts
  • src/main/ipc/floating-workspace-directory.ts
  • src/main/ipc/repos-create.test.ts
  • src/main/ipc/repos/repo-creation-handlers.ts
  • src/main/runtime/worktree-selector-trailing-space-path.test.ts
  • src/renderer/src/components/sidebar/useCreateRepo.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Comment thread src/main/ipc/repos/repo-creation-handlers.ts
ggbdpq added 2 commits October 7, 2026 22:23
A trailing space is a legal POSIX directory name (Google Drive exports
"foo/" from the cloud as "foo " locally), but the create-project and
floating-workspace paths trimmed it off before validating: the picker's
verified path was resolved as its trimmed spelling, so Orca created or
looked for the wrong directory and `spawn git` failed with ENOENT.

Resolve those paths raw-first: if the requested spelling exists, use it
as-is; the trim only applies when it does not, which keeps the
forgiving behavior for a typed stray space. The renderer now sends the
picker's path untrimmed for the same reason. Windows is naturally
unaffected (NTFS forbids trailing spaces, so the raw probe and the
trimmed spelling coincide there).

The cross-machine selector_not_found half of the issue is not addressed
here: the worktree selector serialization chain was audited end to end
(porcelain parsing, id minting/splitting, selector matching, profile
keys) and preserves trailing spaces at every point, so no defect to fix
was found on that path.

Fixes stablyai#25386

Generated-by: GLM-5.3-Flash (ZCode)
… raw parent

The bare catch in resolveCreateParentPath treated every access failure
as "the raw spelling does not exist", so an EACCES/EPERM/ELOOP silently
fell back to the trimmed path and the project could be created in a
directory the user did not pick. Non-absence errors now propagate and
the handler surfaces them as its existing { error } shape; the fallback
only applies to genuine absence. Message-based detection keeps the
existing code-less ENOENT test mocks working.

Generated-by: GLM-5.3-Flash (ZCode)
@ggbdpq

ggbdpq commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current main (40d35fb): main's #24863 Phase 3 added a host-resolution guard to handleCreate (options.hostId === null early return) — it composes orthogonally with the raw-parent change (empty check still trims, the picker path still travels untrimmed to the main-process probe), and the trailing-space cases all pass on the new base (51 + 3 tests).

@ggbdpq
ggbdpq force-pushed the fix/trailing-space-repo-path branch from aeffb92 to 1076497 Compare October 7, 2026 14:24

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 5b7e272b-e633-453f-b89d-d0dd77433c35
📥 Commits

Reviewing files that changed from the base of the PR and between aeffb92 and 1076497.

📒 Files selected for processing (3)
  • src/main/ipc/floating-workspace-directory.test.ts
  • src/main/ipc/floating-workspace-directory.ts
  • src/renderer/src/components/sidebar/useCreateRepo.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Comment thread src/renderer/src/components/sidebar/useCreateRepo.ts
…eation

useCreateRepo now sends the parent as typed, but the runtime registration
controller and createRemoteRepo still trimmed it, so a valid parent whose
name ends with a space resolved to a different directory. Both creation
paths now probe the raw spelling first and fall back to the trim only when
the raw path is genuinely absent (ENOENT); other probe failures surface
instead of silently creating the project under the trimmed spelling. The
SSH probe reuses isENOENT, which already covers relay errors that cross
as canonical ENOENT messages, and re-expands ~ for both spellings.

Also adds the SAFETY: rationale the changed-casting gate requires to an
existing as-never in floating-workspace-directory.test.ts flagged by the
same scan.

Generated-by: GLM-5.3-Flash (ZCode)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Repo path with a trailing space → spawn git ENOENT on add / selector_not_found from another machine

1 participant