6.2 KiB
Oz File-Edit Hooks for Snapshotting Non-Git-Tracked Files — Product Spec
Linear: REMOTE-1465 Figma: none provided
Summary
When the Oz agent creates or edits a file during a cloud run, the file's absolute path is automatically added to the end-of-run snapshot declarations so the file is uploaded alongside the existing repo-diff snapshot. This closes the handoff gap for files the agent writes outside any git repo (or inside one but not visible to git diff HEAD), so the next execution of the same run can see them.
Problem
The REMOTE-1332 snapshot pipeline captures workspace state via git diff --binary HEAD plus git ls-files --others --exclude-standard for every declared .git directory under the workspace. That captures tracked changes and untracked non-gitignored files inside a declared repo, but it silently loses:
- Files the agent creates or edits outside any git repo (e.g.
/tmp,$HOME, or the workspace root beforegit init). - Files inside a repo but below a
.gitignoreentry, which the git-diff path deliberately ignores.
After a cloud → cloud handoff, the next execution never sees those files, so agent work that deposits logs, scratch scripts, or generated artifacts outside a repo is effectively thrown away.
Behavior
-
During a cloud Oz run, every time the agent successfully completes a file-edit tool call that creates or modifies a file, the file's absolute path is recorded in the snapshot declarations file as a
fileentry. The entry lands in the declarations file before the end-of-run upload reads it. -
Each entry uses the existing REMOTE-1332 schema:
{"version":1,"kind":"file","path":"<absolute path>"}. The declarations file is the same per-run file the existing pipeline reads (resolved via$OZ_SNAPSHOT_DECLARATIONS_FILE, otherwise/tmp/oz/<task-id>/snapshot-declarations.jsonl). -
All recorded paths are absolute. If a tool-call result surfaces a relative path, it is resolved against the driver's
working_dir. Paths that still cannot be made absolute are dropped with a WARN log and not recorded. -
Within a single run, the same absolute path is never written more than once to the declarations file. Multiple edits to the same file, or a create followed by an edit, still produce only one
fileentry. -
Appends happen incrementally as each tool call completes, not in a single batch at end of run. If the run crashes after some edits but before the upload step, any
fileentries that already landed on disk are still present for any recovery tooling that reads the declarations file. -
File deletions are not recorded in v1.
RequestFileEditsResult::Success.deleted_filesis observed but ignored. Deletions of tracked files inside a declared repo are still captured bygit diff HEADin the existing pipeline. -
Before upload, the snapshot pipeline drops
fileentries whose absolute path falls under any declaredrepoentry's path. This prevents double-uploading files that the repo's diff already captures. If the agent creates files and then latergit inits a directory containing them, the end-of-run script emits arepoentry for the new directory, which causes the earlierfileentries to be filtered out at gather time. -
A
fileentry whose path is not under any declared repo is uploaded in full, exactly like an operator-authoredfileentry already would be today per REMOTE-1332. -
The feature is inert when any of the following are true, matching the existing REMOTE-1332 gating:
FeatureFlag::OzHandoffis disabled.- The run has no associated task ID (purely local runs).
- The run was started with
--no-snapshot. In these cases, no declarations are written and no file-edit observations have any user-visible effect.
-
The hook only runs for the Warp Oz SDK driver. Third-party harnesses (e.g. Claude Code) do not participate in this mechanism; their file writes go through their own tools and are not observed (this can and will be fixed in follow-up PRs, using each agent's hook system to track file edits).
-
Writer failures (declarations file not writable, path normalization error, file system I/O error) are logged at WARN level and absorbed. The agent run, the current tool call, and the end-of-run snapshot upload all continue as if no file entry were recorded for that call.
-
Shell-driven writes (
cat > file,echo >>,tee,touch, etc.) are not captured by this mechanism. Those files only appear in the snapshot when they happen to sit under a declared repo and the git-diff path catches them. -
A gitignored file edited by the agent inside a declared repo is not captured by v1: it is dropped by the repo-overlap filter in (7) and git's diff path ignores it. This is a known v1 limitation; see "Open questions" below.
Non-goals
- Capturing file deletions. The existing
filedeclaration kind uploads bytes and cannot represent a tombstone. Tracked-file deletions are still covered bygit diff HEAD. - Capturing shell-driven file writes. Parsing arbitrary shell commands to infer file effects is brittle and out of scope.
- Adding start-of-run scanning. End-of-run repo scanning plus gather-time overlap filtering is enough to handle the "agent created files before
git init" case; a redundant start-of-run scan is not added. - Extending this behavior to third-party harnesses (Claude Code, etc.). Harness-specific hook support is a separate follow-up.
- Changing the existing snapshot declarations schema, the
snapshot-declarations.shscript, or the Docker image.
Open questions
- Gitignored files inside a declared repo: v1 drops any
fileentry whose path falls under a declared repo path, which means gitignored files inside that repo are silently lost. Should a follow-up tighten the overlap filter (e.g. usegit check-ignore -qto keepfileentries whose paths the repo's diff will not carry)? - Per-run size caps: REMOTE-1332's
MAX_SNAPSHOT_FILES_PER_RUN = 100already bounds total blobs. A very chatty agent could plausibly touch more than 99 distinct non-repo files in one run; any excess ends up asskippedin the manifest. Do we need a separate earlier-than-cap warning for operators when the tool-call writer approaches that ceiling?