Skip to content

app: a file in the chat's folder is not called outside through a linked path - #2038

Closed
AbirAbbas wants to merge 4 commits into
app/v3from
fix/app-file-stat-alias
Closed

AbirAbbas wants to merge 4 commits into
app/v3from
fix/app-file-stat-alias

Conversation

@AbirAbbas

Copy link
Copy Markdown
Collaborator

Found while running the engine on macOS (Apple M4 Pro, macOS 15.6), where /tmp and /var are links into /private.

What was wrong (app mode)

statFile (cmd/codeaf/appconvo.go, behind attachments.add, agents.create sources and files.stat) decided "inside this workspace" by comparing spelled paths only. One folder can be spelled two ways:

  • The desktop's /folder (/workspace, /project) command sets a new chat's folder to the typed text, and the engine keeps that spelling (engineWorkspace), e.g. /tmp/demo or ~/code/app where ~/code is a link.
  • The native file picker and a Finder drag hand the desktop the resolved path, e.g. /private/tmp/demo/notes.md.

So a file in the chat's own folder got the chip "outside this workspace", and its change counts were not shown.

Reproduced over the app wire on this Mac (codeaf app, spawned as the desktop spawns it; workspace typed /tmp/e3stat-…, attachments added as the picker spells them):

path given to attachments.add app/v3 12dadd64a this branch
/private/tmp/e3stat-…/notes.md outside exists
/tmp/e3stat-…/notes.md exists exists
notes.md exists exists
/private/tmp/e3stat-…/gone.md outside missing
/etc/hosts outside outside

What changes

  • The spelled test stays first, so nothing inside by spelling is newly called outside (a link inside the workspace that points elsewhere keeps its answer). Only a path outside by spelling is resolved (appCanonical, which already handles a missing leaf), and it is inside when its resolved place is.
  • A change recorded under the workspace's own spelling, or relative to the workspace (how a tool call usually names a file), is found for either spelling.
  • The binary sniff reads through skills.ReadRegularHead (nonblocking open, descriptor checked), so a FIFO or device in the workspace — newly reachable through the resolved spelling, and already reachable before by spelling — can no longer hold the request.

agents.list's workspace filter (ListAgents) compares spellings too, but the desktop never sends a workspace to agents.list (every call in apps/desktop/src/state/engine.ts and teamStartAgents.ts passes undefined), so it is left alone.

Tests

New, in cmd/codeaf/appconvo_alias_test.go and appconvo_alias_unix_test.go (each makes its own link, so they mean the same on Linux):

  • TestAppFileStatUsesResolvedWorkspaceContainment — both spellings, files.stat and the attachment chip; missing stays missing; elsewhere stays outside.
  • TestAppFileStatFindsAChangeRecordedUnderTheWorkspaceSpelling — absolute and relative change keys.
  • TestAppFileStatKeepsALinkInsideTheWorkspaceInside
  • TestAppFileStatNeverOpensAFIFO — fails after 5 s without the regular-file opener.

Gates (on the Mac)

  • gofmt; go vet ./cmd/codeaf (darwin, linux); CGO_ENABLED=0 go build ./cmd/codeaf for darwin/arm64, linux/amd64 and windows/amd64. (GOOS=windows go vet ./cmd/codeaf fails at 12dadd64a too, on appcheckout_test.go:495's syscall.Mkfifo; the new FIFO test is tagged !windows.)
  • New tests and TestAppFilesAndAttachmentsRideOnTheMessage: pass; each new test fails at 12dadd64a.
  • Attachment, stat, files, media and submission tests in cmd/codeaf (-run 'Attach|Stat|Files|Submission|Reads|Media|Convo'): only the 4 TestAppMediaAttach* tests fail, all already failing at 12dadd64a on the Mac (TMPDIR spelling in fixtures).
  • Mac gate, full go test -count=1 -p 6 ./... compared by test name with the 12dadd64a Mac baseline: running (results will be added here).
  • CI, judged by content against app/v3 12dadd64a: PR gate 40934499950 fails exactly app/v3's 5 (commentlaw, guard, session ×2, taxonomy). Full check 40934503719: first run hit a proxy.golang.org stream error in the size job and 4 session/factory tests that fail intermittently at 12dadd64a too (reproduced locally: 1 in 30 on the base); failed jobs re-run, results will be added here.
  • The branch is on 12dadd64a; git merge-tree onto the new app/v3 (1c4ec8a25, pair: merge a computer's own chats when it joins, instead of replacing them #2031) is clean and pair: merge a computer's own chats when it joins, instead of replacing them #2031 touches none of these files.

Codex review (adversarial, read-only)

  • Round 1 (SAFE WITH FIXES): a FIFO newly reached through the resolved spelling would block the sniff → fixed; a dangling escape now reads missing instead of outside → kept (the same file in the workspace's own spelling already reads that way; the chip grants nothing); repeated workspace resolution → kept (miss path only; files.stat has no desktop caller); law-style capitals in a comment → fixed.
  • Round 2 (SAFE WITH FIXES): FIFO replacement between stat and open → fixed with the shared nonblocking opener; relative change keys → fixed.
  • Round 3 (SAFE WITH FIXES): both accepted; the only item left is pr: 0.

The change entry follows app/v3's convention (docs/changes/unreleased/0-app-<slug>.md, pr: 0, as in #2014–#2016). codeaf-changes check reports "pr is missing" for every such entry on app/v3 (249 at the base).

Manual verification

Over the app wire (the table above): codeaf app spawned exactly as the desktop spawns it, agents.create with a typed /tmp/… workspace and attachments.add with the paths the picker and Finder produce, against the shipped 12dadd64a sidecar and this branch. Not done in the real window: typing /folder and picking or dragging a file need input, and this Mac has no Accessibility or Screen Recording permission for test tooling. The desktop sends these same calls (attachments_add → attachments.add, crates/core/src/engine/mod.rs).

🤖 Generated with Claude Code

AbirAbbas and others added 4 commits October 29, 2026 19:16
statFile tested containment on the spelled paths only, so a file a picker
or a drag names /private/tmp/demo/notes.md was outside a chat whose
workspace was typed as /tmp/demo (the /folder command keeps the typed
spelling), and its attachment chip said outside this workspace. Keep the
spelled test first and, only when it fails, resolve both sides; a change
recorded under the workspace's own spelling is found too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review fix: a FIFO newly reached through the resolved spelling (and one
already inside the workspace by spelling) was opened to sniff its first
bytes, which waits for a writer and holds files.stat or attachments.add.
Only a regular file is opened now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… keys

Review fixes: the binary sniff reads through skills.ReadRegularHead, which
opens nonblocking and checks the descriptor, so a FIFO or device put in a
file's place between the stat and the open cannot hold the request. A change
recorded under the path relative to the workspace (how a tool call usually
names a file) is found for either spelling of the same file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@AbirAbbas
AbirAbbas force-pushed the fix/app-file-stat-alias branch from 643f0d7 to 60325a9 Compare October 29, 2026 23:17
@AbirAbbas

Copy link
Copy Markdown
Collaborator Author

Closing unmerged: the Mac push stopped early to save usage. What this fixes and what is left is tracked in #2054. The branch fix/app-file-stat-alias is kept.

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