Skip to content

fix(paste): keep default attachments out of Git status - #1830

Merged
devswha merged 2 commits into
mainfrom
fix/default-paste-gitignore
Oct 24, 2026
Merged

devswha merged 2 commits into
mainfrom
fix/default-paste-gitignore

Conversation

@devswha

@devswha devswha commented Oct 24, 2026

Copy link
Copy Markdown
Owner

What this does for the user

Files pasted or attached in the browser are saved in <pane cwd>/.ovis/ so the agent can read them without a permission prompt. Until now every upload showed up as an untracked change in the project's git status.

The default .ovis folder now ignores itself. Right after the server creates the folder, it writes a .gitignore containing *. The file is created exclusively (wx, mode 0600), so:

  • an ignore file already in that folder, or a symlink at that path (dangling or not), is never followed, read or replaced (only EEXIST is tolerated);
  • any other create error fails the upload with an error instead of returning a path;
  • a folder set through OVIS_PASTE_DIR (trimmed, ~ expanded, even one equal to the default path) is the user's own and gets no ignore file.

Ignored files still open by their explicit path, and the path the prompt names is unchanged.

Known trade-off, raised in review: Git-backed @ file completion lists files with git ls-files --exclude-standard, so in a Git project a pasted file no longer appears there. Looking it up by path or bare name still works. Keeping pastes out of the project's Git view is the purpose of this change, so the PR keeps it.

Why

This was a local-only branch (fix/default-paste-gitignore) that was never pushed. It is rebased onto current main and ported to the .ovis / OVIS_PASTE_DIR names from #1648. On main the paste route still only does mkdirSync, so the bug still reproduces there.

It does not overlap with #1795, which changes only Pi delivery and pending-message files and touches neither server/paste.ts nor its tests.

How it was verified

  • server/paste.test.ts and server/file-view.test.ts: all 49 pass. They cover the self-ignoring default folder, the temp-dir fallback, an existing ignore file and file or dangling symlinks kept byte for byte, overrides (trimmed, ~, equal to the default) getting no ignore file, and an unwritable folder reporting EACCES.
  • The POST /api/pane/image contract tests run on an isolated herdr session against a temporary Git project:
    • git status is empty after two concurrent uploads;
    • git check-ignore names each returned path, and the bytes read back through /api/fs/file;
    • concurrent uploads keep a custom ignore file and file or dangling symlinks;
    • overrides get no ignore file;
    • an unwritable folder answers 500 with no path.
  • bun run check fast
  • Reviews: omo-native-code-reviewer found no HIGH findings and recommended APPROVE (the MEDIUM finding is the completion trade-off above). Codex (gpt-6-astra) found nothing.

No HTTP/WS contract, auth or workflow change.

Files pasted or attached in the browser land in <pane cwd>/.ovis/, so
every upload showed up as an untracked change in the project. The
default folder now ignores itself: right after creating it, the server
writes a .gitignore of "*" with an exclusive create (wx, mode 0600). An
ignore file or link already there is never followed or replaced (only
EEXIST is tolerated; any other create error fails the upload), and a
folder named in OVIS_PASTE_DIR (after trim, ~ included, even one equal
to the default path) gets no ignore file. Returned paths stay readable:
ignored files still open by their explicit path.

Checked with unit tests in server/paste.test.ts and
server/file-view.test.ts, and contract tests for POST /api/pane/image in
a temporary Git project (git status empty, git check-ignore names each
path, bytes read back through /api/fs/file; concurrent uploads with a
custom ignore file, file and dangling symlinks, overrides, and an
unwritable folder).
@coderabbitai

coderabbitai Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 34 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration
Commits

Reviewing files that changed from the base of the PR and between 055e820 and a01d87e.

Files selected for processing: 5
  • changelog.d/default-paste-gitignore.md
  • server/api.contract.test.ts
  • server/file-view.test.ts
  • server/paste.test.ts
  • server/paste.ts
  • Autofix · 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.

@devswha

devswha commented Oct 24, 2026

Copy link
Copy Markdown
Owner Author

Merge pass review-work for b1c4194: REVIEW FAILED on one confinement case.

Blocker: when .ovis is a symlink, the * ignore file lands in the link's target. server/paste.ts:102-109 creates .gitignore with wx. That flag protects only the last path component, not the parent directory. If <cwd>/.ovis is a symlink to another folder, for example another repository's root, the * file is created there, and it hides every untracked file in that repository. The gate reproduced this: the other repository's git status went from ?? user-link to empty. Pastes already follow that link on main. The difference is that this PR adds a Git-policy file that affects files it didn't write.

Suggested fix: only write the ignore file into a .ovis that is a real directory. Either lstat it and skip when it's a symlink, or write the ignore file only when this server just created the folder. Add a regression test showing an outside repository stays unchanged. This is a behaviour change in server/, so it's worth a CodeRabbit re-review once pushed.

Checked and passing:

  • 59 tests pass, and the typecheck passes.
  • The isolated POST /api/pane/image contract tests pass, 19/19 (git status empty, override, error cases, token gate).
  • Code review (programming + remove-ai-slops) PASS. Six mutants are each caught: no ignore write, w instead of wx, errors swallowed, overrides getting the ignore file, no 0600, and no trim. No race between concurrent uploads, and EACCES surfaces as a 500.

Non-blocking test notes:

  • The unwritable-folder contract test can't fail on this change; the unit test at paste.test.ts:242 covers it.
  • The "concurrent" contract test can't really race, and the contract tests mostly repeat the unit tests.

@devswha

devswha commented Oct 24, 2026

Copy link
Copy Markdown
Owner Author

Fixed the blocker in a01d87e.

  • Change: preparePasteDirectory (server/paste.ts) writes the default .gitignore only when <cwd>/.ovis is a real directory. After mkdir it lstats the folder, and when .ovis is a symlink it writes nothing, so no * file lands in the link's target. Pastes still follow the link as on main; only the Git-policy file is withheld.
  • Regression test: server/paste.test.ts, "writes no ignore file through a .ovis that is a link to another folder". .ovis links to an outside folder that holds a user file, and the test asserts that folder's contents are unchanged. It fails without the lstat check (15 pass, 1 fail) and passes with it (16 pass).
  • bun run check fast passed on a01d87e before the push.

This is a behaviour change in server/, so the push triggers CodeRabbit's re-review, as you suggested.

@devswha

devswha commented Oct 24, 2026

Copy link
Copy Markdown
Owner Author

Merge pass review-work report. Overall: PASSED.

Lane Verdict Confidence
Code review of b1c4194 (omo-native-code-reviewer) PASS -
Gate review of b1c4194 (another model; programming + remove-ai-slops applied directly) REJECT: a .ovis symlink sent the * ignore file into another repository and hid its files HIGH
Merge pass re-check of a01d87e (the fix) PASS -

The fix in a01d87e

  • preparePasteDirectory writes the default .gitignore only when <cwd>/.ovis is not a symlink (lstatSync(directory).isSymbolicLink()). The link's target, such as another repository, is left exactly as it was.
  • The ignore file is still created with wx, so an existing ignore file or link is never followed or replaced.
  • A directory named in OVIS_PASTE_DIR still gets no ignore file.
  • The new test links .ovis to an outside folder and asserts that the folder is unchanged. With the link check removed, that test fails (mutant run).
  • paste and conventions tests pass (26), and so does the typecheck.
  • git merge-tree is clean against main and against fix(files): serve a rendered page's files and media from the descriptor that was checked #1892, which shares server/file-view.test.ts.

Scope note: the gate suggested failing the upload outright. The fix skips only the ignore write. Attachments still go wherever a user-made .ovis link points, which is unchanged from before this PR, and only the new Git-policy write is held back.

All required checks are green and no review thread is open.

CodeRabbit did not review this PR (rate-limited or skipped). The reviews above stand in for it.

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.

1 participant