fix(supervisor): attachments land in session working dir, not %TEMP% - #403
Open
finedesignz wants to merge 2 commits into
Open
fix(supervisor): attachments land in session working dir, not %TEMP%#403finedesignz wants to merge 2 commits into
finedesignz wants to merge 2 commits into
Conversation
term.attach_file was writing uploads to tmpdir()/remo-attachments/<sessionId>, outside the session's project_dir, so the CLI's own file tools couldn't resolve a typed attachment path relative to the repo. - Write to <repoPath>/.remo/attachments/<sessionId>/<nonce>-<safeName> instead, with a nonce prefix so same-name re-uploads never collide. - Auto-append .remo/ to the repo's .gitignore (idempotent), skipped when .git is absent (rootless/orchestrator dirs). - Best-effort cleanup of the per-session attachments dir in SessionBridge.stop(). - Verified the existing `pty.write(abs + ' ')` needs no shell-style quoting: pty_host.rs's session_input writes raw bytes straight to the PTY master (no shell involved), so a space in the path is just a literal character in the CLI's prompt text, not an argv boundary. Left unquoted; documented why inline. Extracted the file-write logic into writeAttachmentFile()/ attachmentsDirFor() so it's unit-testable without spawning a real PTY. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Owner
Author
AI Review GateGate verdict: FAILURE — blocking finding from Codex Claude Code (QC): pass
Codex: BLOCK
Policy: both reviewers are blocking — a genuine blocking finding from either fails the gate. An infrastructure failure (quota exhausted, timeout, auth failure, no parseable output) is ADVISORY and never blocks: it means the reviewer never saw the code, which is not a verdict about the code. |
… call QC finding on PR #403: the "preserves existing content" test read .gitignore into `contents` BEFORE the second ensureAttachmentsGitignored(repo) call and asserted occurrence-count against that stale variable, so it would pass even if a duplicate line were appended. Now re-reads the file after the second call. Verified via mutation test: temporarily disabling the idempotency check in ensureAttachmentsGitignored made this test fail as expected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
term.attach_filewas writing uploaded files totmpdir()/remo-attachments/<sessionId>— outside the session's actual working directory — so a typed attachment path wasn't resolvable relative to the repo the CLI is operating on.Locked design (panel decision):
<repoPath>/.remo/attachments/<sessionId>/<ts-nonce>-<safeName>instead of a host temp dir.sanitizeAttachmentName()is unchanged; a short nonce prefix (Date.now()-<rand>) makes same-name re-uploads within one session non-colliding.mkdirSync(..., { recursive: true, mode: 0o700 })..gitignoreguard: when<repoPath>/.gitexists, ensure a literal.remo/line is present (append if missing, create the file if absent). Skipped entirely when.gitis absent — rootless/ambient/orchestrator dirs (process-manager.tsalready skips the.gitrequirement forspec.orchestrator).SessionBridge.stop()nowrmSyncs the session's attachments dir, fail-open (never throws out ofstop()).pty.write(abs + ' ')types the path as raw PTY input bytes. Tracedpty_host.rs'ssession_input→ it writes straight to the PTY master (writer.write_all(bytes)), with no shell in between. The receiving end is the CLI's own interactive prompt textbox, which reads the whole typed line as literal prompt text — there's no argv/shell tokenization to defeat, so a space in the path is just a character, not a boundary. Quoting it would instead inject literal quote characters into the prompt. Left unquoted; documented inline insession-bridge.tsso this isn't re-litigated later.Extracted the write logic into two small exported helpers —
attachmentsDirFor()andwriteAttachmentFile()— so the path/collision/gitignore logic is unit-testable without spawning a real PTY runner (attach_file's PTY spawn path isn't mockable today; this was the smallest seam that gives real coverage).Files changed
supervisor/src/runners/session-bridge.tssupervisor/test/attachment-project-dir.test.ts(new)CLAUDE.md(documented the new attachment destination + gitignore/cleanup behavior in the PTY terminal surface docs-map row)Test plan
.gitabsent,SessionBridge.stop()cleanup (including when nothing was ever written).bun test supervisor/test/attachment-project-dir.test.ts supervisor/test/attachment-filename-sanitize.test.ts— 12 pass, 0 fail.bun run check-baseline— OK, within tolerance (baseline pass=2010/skip=247/fail=0 → actual pass=2063/skip=248/fail=0; this worktree needed a freshbun install, no drift beyond that).Could not verify
supervisor/testsuite has 5 pre-existing failures unrelated to this change:bridge-permission-returnpath.test.ts(question_response assertion) reproduces identically onmainbefore my changes;pty-byte-relay.test.ts/pty-orphan-teardown.test.tsfail in this sandbox becausenode-ptyisn't installed / no real PTY host is available in the CI-less dev container. None touch attachment code.claude/codexTUI reading the file) — no running supervisor/hub in this sandbox; covered instead by the unit tests above plus the code trace for the quoting question.🤖 Generated with Claude Code