From 67f2c1e2337b9e4dbccd32f0c65cd7e68ff264ae Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Wed, 27 May 2026 12:07:26 -0300 Subject: [PATCH] fix: do not symlink .codex folders This causes Codex sandbox to fail and the apply_patch command to fail. The issue is the tracked symlink: yarn-project/.codex -> .claude The sandbox is trying to enforce /home/santiago/Projects/aztec-4/yarn-project/.codex as a read-only path, but yarn-project is also a writable root. Since .codex is a symlink inside that writable root, bubblewrap refuses to set up the sandbox: Fatal error: cannot enforce sandbox read-only path .../.codex because it crosses writable symlink .../.codex So apply_patch is not uniquely broken. I reproduced the same sandbox setup failure with simple sandboxed commands like pwd and ls. Commands that are already approved or explicitly escalated can still run because they bypass that sandbox path setup. --- barretenberg/.codex | 1 - barretenberg/.codex/agents | 1 + barretenberg/.codex/skills | 1 + barretenberg/cpp/.codex | 1 - barretenberg/cpp/.codex/agents | 1 + docs/.codex | 1 - docs/.codex/agents | 1 + docs/.codex/commands | 1 + yarn-project/.codex | 1 - yarn-project/.codex/agents | 1 + yarn-project/.codex/scripts | 1 + yarn-project/.codex/settings.json | 1 + yarn-project/.codex/skills | 1 + yarn-project/precommit.sh | 49 +++++++++++++++++++++++++++++++ 14 files changed, 58 insertions(+), 4 deletions(-) delete mode 120000 barretenberg/.codex create mode 120000 barretenberg/.codex/agents create mode 120000 barretenberg/.codex/skills delete mode 120000 barretenberg/cpp/.codex create mode 120000 barretenberg/cpp/.codex/agents delete mode 120000 docs/.codex create mode 120000 docs/.codex/agents create mode 120000 docs/.codex/commands delete mode 120000 yarn-project/.codex create mode 120000 yarn-project/.codex/agents create mode 120000 yarn-project/.codex/scripts create mode 120000 yarn-project/.codex/settings.json create mode 120000 yarn-project/.codex/skills diff --git a/barretenberg/.codex b/barretenberg/.codex deleted file mode 120000 index c8161850a43d..000000000000 --- a/barretenberg/.codex +++ /dev/null @@ -1 +0,0 @@ -.claude \ No newline at end of file diff --git a/barretenberg/.codex/agents b/barretenberg/.codex/agents new file mode 120000 index 000000000000..0efb85ec44f4 --- /dev/null +++ b/barretenberg/.codex/agents @@ -0,0 +1 @@ +../.claude/agents \ No newline at end of file diff --git a/barretenberg/.codex/skills b/barretenberg/.codex/skills new file mode 120000 index 000000000000..454b8427cd75 --- /dev/null +++ b/barretenberg/.codex/skills @@ -0,0 +1 @@ +../.claude/skills \ No newline at end of file diff --git a/barretenberg/cpp/.codex b/barretenberg/cpp/.codex deleted file mode 120000 index c8161850a43d..000000000000 --- a/barretenberg/cpp/.codex +++ /dev/null @@ -1 +0,0 @@ -.claude \ No newline at end of file diff --git a/barretenberg/cpp/.codex/agents b/barretenberg/cpp/.codex/agents new file mode 120000 index 000000000000..0efb85ec44f4 --- /dev/null +++ b/barretenberg/cpp/.codex/agents @@ -0,0 +1 @@ +../.claude/agents \ No newline at end of file diff --git a/docs/.codex b/docs/.codex deleted file mode 120000 index c8161850a43d..000000000000 --- a/docs/.codex +++ /dev/null @@ -1 +0,0 @@ -.claude \ No newline at end of file diff --git a/docs/.codex/agents b/docs/.codex/agents new file mode 120000 index 000000000000..0efb85ec44f4 --- /dev/null +++ b/docs/.codex/agents @@ -0,0 +1 @@ +../.claude/agents \ No newline at end of file diff --git a/docs/.codex/commands b/docs/.codex/commands new file mode 120000 index 000000000000..0b631dd5b721 --- /dev/null +++ b/docs/.codex/commands @@ -0,0 +1 @@ +../.claude/commands \ No newline at end of file diff --git a/yarn-project/.codex b/yarn-project/.codex deleted file mode 120000 index c8161850a43d..000000000000 --- a/yarn-project/.codex +++ /dev/null @@ -1 +0,0 @@ -.claude \ No newline at end of file diff --git a/yarn-project/.codex/agents b/yarn-project/.codex/agents new file mode 120000 index 000000000000..0efb85ec44f4 --- /dev/null +++ b/yarn-project/.codex/agents @@ -0,0 +1 @@ +../.claude/agents \ No newline at end of file diff --git a/yarn-project/.codex/scripts b/yarn-project/.codex/scripts new file mode 120000 index 000000000000..26e538548009 --- /dev/null +++ b/yarn-project/.codex/scripts @@ -0,0 +1 @@ +../.claude/scripts \ No newline at end of file diff --git a/yarn-project/.codex/settings.json b/yarn-project/.codex/settings.json new file mode 120000 index 000000000000..11a726369cef --- /dev/null +++ b/yarn-project/.codex/settings.json @@ -0,0 +1 @@ +../.claude/settings.json \ No newline at end of file diff --git a/yarn-project/.codex/skills b/yarn-project/.codex/skills new file mode 120000 index 000000000000..454b8427cd75 --- /dev/null +++ b/yarn-project/.codex/skills @@ -0,0 +1 @@ +../.claude/skills \ No newline at end of file diff --git a/yarn-project/precommit.sh b/yarn-project/precommit.sh index a4863d268296..1f5bd7c8420a 100755 --- a/yarn-project/precommit.sh +++ b/yarn-project/precommit.sh @@ -10,6 +10,50 @@ cd $(dirname $0) export FORCE_COLOR=true +# Verify every .codex directory mirrors its sibling .claude via child symlinks, so that adding a +# file or folder to a .claude config does not silently leave the sandboxed .codex path behind. +# Only immediate children are checked: a symlinked folder (e.g. .codex/skills) already covers its +# contents, and a .codex that is itself a symlink (the repo root) mirrors .claude inherently. +check_codex_symlinks() { + local repo_root claude_dirs claude_dir codex_dir path name + local -a errors=() + repo_root=$(git rev-parse --show-toplevel) + claude_dirs=$(cd "$repo_root" && git ls-files -- '.claude/*' '*/.claude/*' | sed -E 's#(.*/)?\.claude/.*#\1.claude#' | sort -u) + + for claude_dir in $claude_dirs; do + codex_dir="${claude_dir%.claude}.codex" + if [ -L "$repo_root/$codex_dir" ]; then + continue + fi + if [ ! -d "$repo_root/$codex_dir" ]; then + errors+=("missing directory $codex_dir (should mirror $claude_dir)") + continue + fi + while IFS= read -r path; do + name=$(basename "$path") + if [ ! -L "$repo_root/$codex_dir/$name" ] || [ ! "$repo_root/$codex_dir/$name" -ef "$path" ]; then + errors+=("$codex_dir/$name should be a symlink to ../.claude/$name") + fi + done < <(find "$repo_root/$claude_dir" -mindepth 1 -maxdepth 1) + while IFS= read -r path; do + name=$(basename "$path") + if [ ! -e "$repo_root/$claude_dir/$name" ] && [ ! -L "$repo_root/$claude_dir/$name" ]; then + errors+=("$codex_dir/$name is stale; no matching $claude_dir/$name") + fi + done < <(find "$repo_root/$codex_dir" -mindepth 1 -maxdepth 1) + done + + if (( ${#errors[@]} > 0 )); then + echo -e "\033[31mError:\033[0m .codex directories are out of sync with their .claude siblings:" + for e in "${errors[@]}"; do echo " - $e"; done + echo "Each entry under a .claude folder needs a sibling symlink in .codex, e.g.:" + echo " (cd /.codex && ln -s ../.claude/ && git add )" + return 1 + fi +} + +check_codex_symlinks + # Get all staged files (excluding deleted), relative to yarn-project staged_files=$(git diff-index --diff-filter=d --relative --cached --name-only HEAD) @@ -18,6 +62,11 @@ staged_files=$(git diff-index --diff-filter=d --relative --cached --name-only HE # Filter for formattable files staged_format_files=$(echo "$staged_files" | grep -E '\.(json|js|mjs|cjs|ts)$' || true) +# Drop symlinks; prettier errors when handed a symbolic link (e.g. .codex/settings.json). +if [[ -n "$staged_format_files" ]]; then + staged_format_files=$(echo "$staged_format_files" | while IFS= read -r f; do [ -L "$f" ] || printf '%s\n' "$f"; done) +fi + # Get unstaged changes for formattable files unstaged_format_files=$(git diff --relative --name-only --diff-filter=d | grep -E '\.(json|js|mjs|cjs|ts)$' || true)