Reviews, plans and replies are written where the guardrail allows: g1t's answer files are inside it
The outside_workspace guardrail let file tools reach only the project, /tmp and the toolchain caches, but the runner asks the agent to write /work/review.json, /work/plan.json and /work/answer.md. On 2026-10-06 a review on automation-lab#12 was refused that write and its verdict came back as a plain comment. Those three exact files are now allowed; anything else beside the project still is not.
4 files+24−50/4 viewed
| 417 | 417 | format!("/{}", parts.join("/")) | |
| 418 | 418 | } | |
| 419 | 419 | ||
| 420 | − | /// Where file tools may go: the project, scratch space, and the caches | |
| 421 | − | /// where dependencies' sources are. | |
| 420 | + | /// The files the runner asks the agent to write its answer to, outside | |
| 421 | + | /// the project so they are never committed: a review, a plan, a reply. | |
| 422 | + | const ANSWER_FILES: [&str; 3] = [crate::review::REVIEW_FILE, crate::plan::PLAN_FILE, crate::reply::ANSWER_FILE]; | |
| 423 | + | ||
| 424 | + | /// Where file tools may go: the project, scratch space, the caches where | |
| 425 | + | /// dependencies' sources are, and the answer files. | |
| 422 | 426 | fn inside_allowed(path: &str, place: &Place) -> bool { | |
| 427 | + | if ANSWER_FILES.contains(&path) { | |
| 428 | + | return true; | |
| 429 | + | } | |
| 423 | 430 | let roots = [ | |
| 424 | 431 | crate::WORKDIR.to_owned(), | |
| 425 | 432 | "/tmp".to_owned(), | |
| 814 | 821 | } | |
| 815 | 822 | ||
| 816 | 823 | #[test] | |
| 824 | + | fn the_answer_files_may_be_written_and_nothing_else_beside_the_project() { | |
| 825 | + | let policy = all_on(); | |
| 826 | + | let write = |path: &str| decide(&policy, "Write", &json!({ "file_path": path, "content": "{}" }), &place()); | |
| 827 | + | for path in ["/work/review.json", "/work/plan.json", "/work/answer.md", "/work/repo/src/lib.rs", "/tmp/x"] { | |
| 828 | + | assert_eq!(write(path), None, "{path}"); | |
| 829 | + | } | |
| 830 | + | for path in ["/work/notes.json", "/work/review.json.bak", "/etc/hosts"] { | |
| 831 | + | assert!(write(path).is_some(), "{path}"); | |
| 832 | + | } | |
| 833 | + | } | |
| 834 | + | ||
| 835 | + | #[test] | |
| 817 | 836 | fn force_pushes_are_refused_however_written() { | |
| 818 | 837 | let policy = all_on(); | |
| 819 | 838 | for command in [ |
| 23 | 23 | use crate::report::Reporter; | |
| 24 | 24 | use crate::{WORKDIR, auth_option, env, harness}; | |
| 25 | 25 | ||
| 26 | − | const PLAN_FILE: &str = "/work/plan.json"; | |
| 26 | + | pub(crate) const PLAN_FILE: &str = "/work/plan.json"; | |
| 27 | 27 | ||
| 28 | 28 | const INSTRUCTIONS: &str = "You are planning work for a team of coding agents. The repository is checked out in the current directory. \ | |
| 29 | 29 | Read enough of it to understand how it is built and tested. Do not modify it. |
| 21 | 21 | use crate::report::Reporter; | |
| 22 | 22 | use crate::{WORKDIR, auth_option, env, git, harness}; | |
| 23 | 23 | ||
| 24 | − | const ANSWER_FILE: &str = "/work/answer.md"; | |
| 24 | + | pub(crate) const ANSWER_FILE: &str = "/work/answer.md"; | |
| 25 | 25 | const MAX_ANSWER_CHARS: usize = 20_000; | |
| 26 | 26 | ||
| 27 | 27 | const INSTRUCTIONS: &str = "Write your answer to /work/answer.md as Markdown, addressed to whoever asked: \ |
| 23 | 23 | use crate::{WORKDIR, auth_option, env, git, harness}; | |
| 24 | 24 | ||
| 25 | 25 | const DIFF_FILE: &str = "/work/change.diff"; | |
| 26 | − | const REVIEW_FILE: &str = "/work/review.json"; | |
| 26 | + | pub(crate) const REVIEW_FILE: &str = "/work/review.json"; | |
| 27 | 27 | ||
| 28 | 28 | const INSTRUCTIONS: &str = "You are reviewing a pull request. The repository is checked out in the current directory at the pull request's head. \ | |
| 29 | 29 | The change under review is in /work/change.diff; read it first, then read the surrounding code as needed. You may run the project's tests. Do not modify the repository. |