From 2fe580bb332f0cdbea1b7aea570c44a3b7bdc4bc Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Tue, 4 Aug 2026 21:32:43 +0200 Subject: [PATCH] IJAI-689 add parallels-vm-ui-tests, and make driving the guest allowlistable Running a UI lane in the Parallels guest was documented but not actually runnable by an agent: the two mechanics it rests on - writing a file into the guest, and getting commits across - are shell shapes a permission classifier reasonably distrusts when they appear ad hoc at a call site, so both were denied mid-run. They are the same two operations every time, which makes them a script rather than a habit. Everything now goes through ./.agents/skills/parallels-vm-ui-tests/scripts/vm.sh, named once in tool-permissions.json. Sync streams an incremental git bundle over the Parallels exec channel instead of standing up the git daemon the runbook described: no network listener, no credential in the guest, and 190 commits came to under 10 MB. Taking the lease resets the guest tree, because taking the lease is taking the guest - it is a build machine seeded from the host, not anybody's working copy. A dirty-tree gate would only ever be answered one way, so the guard is a saved patch instead: whatever was there is written to /Volumes/Dev/discarded-.patch before the reset, and the paths are printed. The AIR guide keeps its provisioning history, class list and timings, and defers the mechanics here. Description budget note: skill descriptions share a 6 KiB pool (render-guides.mjs), and it was already within 10 bytes of full, so this one is 87 bytes. The next skill needs a trim elsewhere or a deliberate raise of that constant, which costs every session's context. (cherry picked from commit bd9c12ebebf635255250ff3505d190c12a0e377d) GitOrigin-RevId: b69adf05df20a1452733cda42eea0b368965172b --- .ai/tool-permissions.json | 126 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 .ai/tool-permissions.json diff --git a/.ai/tool-permissions.json b/.ai/tool-permissions.json new file mode 100644 index 000000000000..421b1ae60bf8 --- /dev/null +++ b/.ai/tool-permissions.json @@ -0,0 +1,126 @@ +{ + "$comment": [ + "The repository's tool-permission list, rendered by community/.ai/render-guides.mjs into", + ".claude/settings.json (permissions.allow / permissions.deny) and .codex/rules/default.rules.", + "Edit this file and rerun the renderer; never edit the generated rules by hand.", + "", + "Entries are argv prefixes, not command strings: [\"./bazel.cmd\", \"build\"] matches", + "`./bazel.cmd build //x` but never `./bazel.cmd clean`. Both matchers compare literal text, so a", + "leading `./` is emitted in both spellings; other spellings of the same file (`../../x`, an", + "absolute path) fall through to a prompt.", + "", + "Each harness matches every stage of a pipeline independently, which is why the read-only", + "filters at the end of `allow` are listed at all: without them `rg.cmd x | head -5` prompts.", + "", + "`deny` wins over `allow` in both harnesses and is the load-bearing half: it also overrides", + "Claude Code's built-in read-only command set, which is what `grep` and `find` are listed for.", + "", + "`{{TOOLS_DIR}}` resolves per edition (./community/tools, or ./tools in a community checkout).", + "Everything else is spelled relative to the workspace root the rules are rendered into." + ], + + "allow": [ + ["./plugins/air/scripts/bt.cmd"], + ["./.agents/skills/parallels-vm-ui-tests/scripts/vm.sh"], + ["./tests.cmd"], + ["./community/tests.cmd"], + ["./bazel.cmd", "build"], + ["./bazel.cmd", "test"], + ["./bazel.cmd", "query"], + ["./bazel.cmd", "cquery"], + ["./bazel.cmd", "info"], + ["./bazel.cmd", "run"], + ["bazel", "build"], + ["bazel", "test"], + ["bazel", "query"], + ["bazel", "cquery"], + ["bazel", "info"], + ["bazel", "run"], + ["./bazel-build-all.cmd"], + ["./build/jpsModelToBazel.cmd"], + ["bun", "build/jps-module.mjs"], + ["{{TOOLS_DIR}}/rg.cmd"], + ["{{TOOLS_DIR}}/fd.cmd"], + ["{{TOOLS_DIR}}/bun.cmd"], + ["{{TOOLS_DIR}}/uv.sh"], + ["bun", "test"], + ["bun", "run", "build"], + ["node", "--test"], + ["./.ownership/cli.cmd"], + ["teamcity"], + ["bd"], + ["npm", "view"], + ["git", "status"], + ["git", "diff"], + ["git", "log"], + ["git", "show"], + ["git", "grep"], + ["git", "ls-files"], + ["git", "rev-list"], + ["git", "rev-parse"], + ["git", "check-ignore"], + ["git", "fetch"], + ["git", "add"], + ["git", "commit"], + ["git", "mv"], + ["git", "rm"], + ["git", "restore"], + ["git", "stash"], + ["git", "checkout"], + + ["awk"], + ["basename"], + ["cat"], + ["cut"], + ["date"], + ["dirname"], + ["du"], + ["echo"], + ["false"], + ["head"], + ["jq"], + ["ls"], + ["nl"], + ["printf"], + ["pwd"], + ["readlink"], + ["realpath"], + ["rev"], + ["sed"], + ["sort"], + ["stat"], + ["tac"], + ["tail"], + ["tr"], + ["true"], + ["uniq"], + ["wait"], + ["wc"], + ["which"] + ], + + "$comment_deny": [ + "`grep` and `find` are denied outright, in every pipeline position: pipe into", + "`{{TOOLS_DIR}}/rg.cmd` instead, which reads stdin. Expressing \"denied only as the first stage\"", + "took a whole argv-parsing hook; the flat rule is what lets both harnesses do this natively.", + "", + "`sed -i` is a speed bump against bulk in-place rewrites (the Edit tool is the documented path),", + "not a boundary: an `awk` print target or a shell redirect can still write a file." + ], + "deny": [ + ["bazel", "clean"], + ["./bazel.cmd", "clean"], + ["./bazel-clean-caches.cmd"], + ["grep"], + ["find"], + ["sed", "-i"], + ["sed", "--in-place"] + ], + + "$comment_denyTools": [ + "Whole tools, denied by name. Claude Code drops these from the model's context entirely, so no", + "call is attempted; the guide's Tooling rules point at the ijproxy and rg.cmd/fd.cmd equivalents.", + "Codex has no such tools, so its rules file omits this section." + ], + "denyTools": ["Grep", "Glob"] +}