fix: bridge station file paths to supervisor artifact retrieval (#21)#22
fix: bridge station file paths to supervisor artifact retrieval (#21)#22randymorales wants to merge 2 commits into
Conversation
…a#21) The supervisor failed with "failed to load artifact: file does not exist" when asked about a file a station wrote to disk. Two parallel "artifact" systems had no link: stations save their text result into the ADK artifact service under synthetic names (station-<x>-result), but also return artifact_path pointing at a real file on disk. The role-sealed supervisor has no filesystem read tool, so it passed the disk path into load_artifacts, which has no entry for it and returns fs.ErrNotExist. Fix both sides of the gap: - read_file tool (Option 2): sandboxed file read for the supervisor, restricted to the session's project roots (workingDir + station CWDs), with symlink-safe traversal guards, a 256KB cap, and binary rejection. Handles in-tree files, read live. - Auto-register artifact files (Option 1): after a station completes with a non-empty artifact_path, snapshot the file content into the artifact service keyed by its path, so load_artifacts(path) resolves. Covers out-of-tree files (e.g. plan output in ~/.claude/plans) that read_file's project sandbox would block. - Prompt: add a <reading_files> block steering the supervisor to use read_file for paths and not pass disk paths to load_artifacts. Closes dmora#21 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Code Review
This pull request introduces a new read_file tool that allows the supervisor to read files directly from disk within allowed project roots, and adds automatic file artifact persistence. The review feedback suggests using filepath.Rel to avoid platform-specific path validation issues and adopting a more robust truncation detection method that does not rely on os.Stat's size reporting.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Address Gemini Code Assist review on dmora#22: - resolveWithinRoots: use filepath.Rel for containment instead of strings.HasPrefix(abs, root+sep), which has edge cases at filesystem roots ("/" -> "//", "C:\" -> "C:\\"). - readCappedFile: detect truncation by reading maxReadFileBytes+1 and comparing the read count, instead of trusting info.Size() (unreliable for /proc, /sys, network shares, or TOCTOU between Stat and Open). - Tests: sibling name-prefix rejection, root-resolves-to-root, and exact-cap / one-byte-over truncation boundaries. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review feedback addressed — 8de7792Both Gemini Code Assist findings on
Tests added/adjusted ( |
Summary
Fixes the supervisor error
failed to load artifact: file does not exist, raised when the user asks the supervisor about a file a station wrote to disk.Two parallel "artifact" systems had no link between them:
station-<x>-result), retrievable viaload_artifacts.artifact_path, a real path to a file on disk.The role-sealed supervisor has no filesystem read tool, so when asked about a file it passes the System B disk path into System A's
load_artifacts. No artifact exists under that path →fs.ErrNotExist→ the error.Closes #21 — full root-cause analysis and code references are in the issue.
Changes
Belt-and-suspenders fix covering both in-tree and out-of-tree files:
read_filetool (internal/agent/read_file_tool.go) — sandboxed file read for the supervisor, restricted to the session's project roots (workingDir+ each station'sresolvedCWD). Symlink-safe traversal guards (EvalSymlinkson both target and roots before prefix check), 256KB cap, binary rejection. Reads in-tree files live.harness_persist.go,agentrun.go) — after a station completes with a non-emptyartifact_path, snapshot the file's content into the artifact service keyed by its path, so the supervisor's existingload_artifacts(path)reflex resolves. Covers out-of-tree files (e.g. plan output in~/.claude/plans/) thatread_file's sandbox would block.coder.md.tpl) — new<reading_files>block steering the supervisor to useread_filefor paths and not pass disk paths toload_artifacts.read_file_tool_test.go) — sandbox traversal/symlink rejection, multi-root allow, binary/dir/oversize/missing handling.Division of responsibility
read_file(path)~/.claude/plans/)load_artifacts(path)Known trade-off: the auto-registered artifact is a snapshot taken at station completion; if a later station mutates the same file, that artifact goes stale. For in-tree paths the supervisor has both routes and the prompt steers it to
read_file(live), so the live read wins in practice.Validation
Ran the full CI matrix locally (
.github/workflows/{ci,lint}.yml):go mod verifygo vet ./...go build -race ./...go test -race ./...okgo mod tidy+ diffgofmt -l .golangci-lint rungovulncheck ./...The
govulncheckfindings (11, viago-git/stdlib throughinternal/fsext/ls.go) are pre-existing onmainand unrelated to this change — no dependencies added (go.mod/go.sum unchanged).🤖 Generated with Claude Code