Where should my skill write files, and which path forms work?
Your skill's file tools and its shell need DIFFERENT path forms, and getting this wrong breaks a skill that works in the CLI. Give the file-writing and file-reading tools the ABSOLUTE path of the outputs location — the model is told that path in its instructions. Older Desktop releases also accepted a bare filename there, because the file tools' working directory was the outputs location; from Desktop 2.7032.0 their working directory is a deliberately empty, write-denied directory, and a bare filename is refused. The absolute outputs path works on both, so it is the form to write. Use an ABSOLUTE session path under the outputs mount with the shell: the shell starts in a scratch directory that exists only inside the Linux environment, so a bare filename there is written somewhere the user and the file tools can never see. Handing the shell's absolute form to a file tool is rejected outright rather than translated. The uploads location is read-only; the outputs location is where deliverables belong.
Treat a folder the user connected as a separate mount with its own rules, not as part of the workspace.
Connected folders appear as their own mounts alongside the outputs location. Permissions are tracked per mount, so a capability granted for one does not extend to another. A skill that assumes one uniform writable area will be surprised the first time it touches a connected folder.
Do not assume a connected project gives you a folder mount. It does not.
Connecting a project and connecting a folder are different actions producing different results. A session with a project attached and no folder attached has no folder mount at all, and a skill that looks for one will find nothing. A connected project is also mounted read-only, while a connected folder is writable, so a project is a source to read from rather than somewhere to put output.
Give the file tools the absolute outputs path; give the shell the absolute session outputs path. No single form works for both.
The file-reading and file-writing tools run on the user's computer and need the outputs location's own absolute path, which the model is given in its instructions. Before Desktop 2.7032.0 their working directory was the outputs location, so a bare filename also landed there. From Desktop 2.7032.0 the working directory is a deliberately empty directory the tools are denied from writing to, and a relative path is refused: a bare filename given to the read, write or edit tools is refused with an error saying the file is in a directory denied by the permission settings, and a relative search is quietly re-anchored to the outputs location. Because a user may be on either release, write the absolute outputs path; it works on both. The shell does NOT share the file tools' location either way -- it starts in a scratch directory that exists only inside the Linux environment, so a bare filename written from the shell goes somewhere neither the user nor the file tools can reach, with no error. From the shell, write to the absolute outputs path instead. In the other direction, an absolute path copied out of shell output is the wrong form for the file tools and is rejected every time rather than translated. There is a third form worth knowing if your skill bundles a script: because the shell always starts in the same place and the shared area sits directly inside it, a shell command can reach the outputs location with a short relative path instead of the long absolute one, and that short form contains nothing you have to look up. But do not have the script work out its own destination by inspecting the directories around it. Surfaces differ in ways a single test cannot separate — on one of them the shell already starts inside the outputs location, so a test for a nested folder finds nothing and a naive fallback appends a second level, which nests a second outputs level and hides the file. Instead, let the component that already knows the destination pass it: the caller has the file tools, has resolved the workspace, and is the only party that can name a path the user will see. Give scripts an absolute destination as a required argument, have them fail with a clear message when it is missing rather than inventing a directory, and have them print the path they actually wrote.
Producing a file is not delivering it — and where your working directory lands differs by lane.
Where your working directory lands differs by lane, so do not assume it is either visible or invisible. On the local desktop lane the file tools' working directory was the outputs location before Desktop 2.7032.0, so a bare filename landed somewhere visible; from 2.7032.0 it is an empty directory the tools cannot write to, and a bare filename is refused. On the remote lane the working directory is a container home directory that is neither surfaced nor retained, and a file left there is lost at session end. Because a skill cannot tell which lane it is on, treat producing and delivering as two separate acts on both: write the file to a stated path, then present it with whatever tool this surface offers for sending files to the user. Note also that 'the scratch directory' is not one place — the agent is separately instructed to use a host-side temporary directory for working files, which survives the session and is never shown to the user, and the shell has its own scratch area that reaches neither the user nor your file tools. Anything outside the outputs location or a connected folder is a temporary file by definition.
Never pass an absolute session path to the file tools. It is denied, not translated.
Your shell commands print absolute paths that look like /sessions/<session-name>/mnt/outputs/report.pdf. If you hand one of those to a file-reading or file-writing tool, the call is refused — the check is a literal prefix test on /sessions/, so the same path is refused every time and retrying cannot help. Nothing rewrites it into the form those tools want. Pass the outputs location's own absolute path instead when you are using a file tool (a bare filename also worked before Desktop 2.7032.0, and is refused from 2.7032.0). Do not carry that advice back to the shell: the shell needs the absolute outputs path, because its own starting directory is invisible to the user. The error text blames the path form, which is right in this case, but the same message appears for other causes too, so read it as "this tool cannot take this path" rather than as a diagnosis.
Treat your own skill's files as read-only at runtime.
A skill's own directory is made available to the session as a mount you should not expect to write back to. Generate into the outputs location instead. A skill that rewrites its own files to keep state will not carry that state anywhere useful.
Treat the uploads location as read-only and write everything to the outputs location.
Uploaded files are mounted read-only. Attempting to write there fails. Deliverables belong in the outputs location, which is writable and is also what the delivery mechanisms operate on.
Find an uploaded file from the message that announced it, never from a fixed uploads directory.
Where an attached file lands depends on the sandbox. In the local sandbox uploads are a read-only mount beside the outputs location. In the remote sandbox a file attached mid-session landed under the home directory's hidden Claude folder, in a per-session subdirectory, with an eight-character hex prefix on its original name — and the uploads directory that sandbox's own environment text names did not exist before or after the attach. The model is told about the attachment through the conversation, so the announcing message is the one source that is right in both sandboxes. A resolver keyed on either fixed path returns nothing in the other sandbox, and the skill then proceeds as if nothing was attached — the failure with the largest blast radius for any skill that hands documents to scripts or sub-agents.
Always give search tools an explicit folder.
A search with no folder means something different on every surface. In local Cowork it searches the outputs location; in a Cowork session running in the cloud it walks the whole container home, thousands of package-cache files and the agent's own credential directory included; in Claude Code on the web it searches the repository; in a claude.ai chat there are no search tools at all. Pass the folder you mean, usually the outputs location or a connected folder, and the result is the same everywhere.
What is not established
- An earlier version of this page said a relative filename was correct for both the shell and the file tools. That was wrong -- it was copied from a product prompt string that was itself wrong, and which the product corrected in a later release. It is withdrawn.