Problem
The framework's canonical settings template
(.claude/settings.json) declares sandbox.filesystem.allowRead: ["."]
with the intent that any adopter project — placed anywhere on disk,
named anything — is readable under the sandbox by virtue of being the
session's CWD.
In practice this is not happening. In a session started in
~/code/airflow-s/ with the template installed at user-scope, the
resolved sandbox state shows:
write.allowOnly keeps "." and "\$TMPDIR" as literals → resolved
at access-time → write to CWD works.
read.allowWithinDeny pre-resolves everything to absolute paths →
"." silently disappears from the resolved set → reads under CWD
fall through to the broad denyRead: ["~/"] and fail with
Operation not permitted.
Symptom seen on a real session: git rev-parse --git-dir in the project
root fails ("Unable to read current working directory: Operation not
permitted"); Read-tool reads of .apache-steward.lock,
.apache-steward/, etc. also fail. The only way to unblock is per-command
dangerouslyDisableSandbox: true or hand-editing ~/.claude/settings.json
to add an explicit absolute path for the project.
This breaks the promise that adoption alone is sufficient to make the
secure setup work on a freshly-cloned project, regardless of its path
or name.
Where to fix
Two parts:
-
Upstream (out of framework scope, but worth surfacing):
the harness should resolve "." consistently across allowRead and
allowWrite — either keep it literal in both (resolved at access-time)
or expand it to the abs path of CWD-at-session-start in both.
-
Framework-side defensive fix (this issue):
setup-isolated-setup-install should resolve the project root at
install time (git rev-parse --show-toplevel) and add the resulting
absolute path to allowRead (and allowWrite if not already
covered) in the merged ~/.claude/settings.json — in addition to
the "." entry, not replacing it.
Belt and braces:
- If the harness ever stops resolving
".", the explicit absolute
path still covers this project.
- If
"." works correctly, the explicit entry is redundant but
harmless.
- Idempotent: re-installs check for an existing entry before adding.
`setup-steward/adopt` is not the right place — sandbox config is
`setup-isolated-setup-install`'s responsibility. `adopt` should at
most verify the project root is covered and refer out to
`setup-isolated-setup-install` if not (verify-and-remediate, not
own-and-write).
Verify-side follow-up
`setup-isolated-setup-verify` should add a check: attempt a sandboxed
read of the project root and a sandboxed write of a temp file inside it.
If either fails, surface the gap and propose re-running
`setup-isolated-setup-install`.
Repro
- Fresh clone of any adopter project (e.g. `git clone ~/code/foo/`).
- Run the secure-setup install with no project-specific customisation.
- Start a Claude Code session in `~/code/foo/`.
- Invoke any framework skill that reads project files — e.g.
`/setup-steward verify`. Reads under `~/code/foo/` will fail.
Problem
The framework's canonical settings template
(
.claude/settings.json) declaressandbox.filesystem.allowRead: ["."]with the intent that any adopter project — placed anywhere on disk,
named anything — is readable under the sandbox by virtue of being the
session's CWD.
In practice this is not happening. In a session started in
~/code/airflow-s/with the template installed at user-scope, theresolved sandbox state shows:
write.allowOnlykeeps"."and"\$TMPDIR"as literals → resolvedat access-time → write to CWD works.
read.allowWithinDenypre-resolves everything to absolute paths →"."silently disappears from the resolved set → reads under CWDfall through to the broad
denyRead: ["~/"]and fail withOperation not permitted.Symptom seen on a real session:
git rev-parse --git-dirin the projectroot fails ("Unable to read current working directory: Operation not
permitted");
Read-tool reads of.apache-steward.lock,.apache-steward/, etc. also fail. The only way to unblock is per-commanddangerouslyDisableSandbox: trueor hand-editing~/.claude/settings.jsonto add an explicit absolute path for the project.
This breaks the promise that adoption alone is sufficient to make the
secure setup work on a freshly-cloned project, regardless of its path
or name.
Where to fix
Two parts:
Upstream (out of framework scope, but worth surfacing):
the harness should resolve
"."consistently acrossallowReadandallowWrite— either keep it literal in both (resolved at access-time)or expand it to the abs path of CWD-at-session-start in both.
Framework-side defensive fix (this issue):
setup-isolated-setup-installshould resolve the project root atinstall time (
git rev-parse --show-toplevel) and add the resultingabsolute path to
allowRead(andallowWriteif not alreadycovered) in the merged
~/.claude/settings.json— in addition tothe
"."entry, not replacing it.Belt and braces:
".", the explicit absolutepath still covers this project.
"."works correctly, the explicit entry is redundant butharmless.
`setup-steward/adopt` is not the right place — sandbox config is
`setup-isolated-setup-install`'s responsibility. `adopt` should at
most verify the project root is covered and refer out to
`setup-isolated-setup-install` if not (verify-and-remediate, not
own-and-write).
Verify-side follow-up
`setup-isolated-setup-verify` should add a check: attempt a sandboxed
read of the project root and a sandboxed write of a temp file inside it.
If either fails, surface the gap and propose re-running
`setup-isolated-setup-install`.
Repro
`/setup-steward verify`. Reads under `~/code/foo/` will fail.