Repository navigation
feat(sidecar,shell): project write policy, git in the sandbox (v0.5.1 HGW-W2) - #80
SaulBuilds wants to merge 7 commits into
Conversation
Owner decision 2026-10-15 (HGW (2)). Before this change any shell_run
output, even an approved `node -v`, tainted the session, and every later
command and sidecar-hosted write was declined for the rest of the
conversation ("which this tool's host cannot ask for"), with no way to
clear it.
Now, in a session opened with hicAware: true:
- a tainted effectful sidecar call (fs_*, file_write, sheet_write,
toolchain tools, capsules) waits on a new per-session action card
(GET /sessions/:id/actions/pending, POST .../actions/decide
{id, allow, subject}); the card shows the tool, the exact canonical
arguments, a summary, the hicReason and every taint source;
- shell_run keeps its own card and still asks after taint; the card
now carries hicReason and taintSources;
- an approval releases that one call; the session stays tainted, so
the next action asks again (never global, never automatic);
- decisions are recorded (kind sidecar.tool_call, write-ahead).
Without hicAware (headless, scheduled) nobody can be asked: the call is
declined with citrate_agent_loop::cannot_ask_reason, which names the
taint source and says why.
Tests: agent-loop taint_tests (+1), action_approvals unit (+3),
action_approvals_tests session/route tests (+6), shell_run_tests (+2,
one rewritten), files_tests taint case rewritten to the card flow.
Workspace: 2241 -> 2253 passed, 0 failed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe
…t-retry (HGW-1) Aligns the HGW-1 interface with the HGW-W1 issue (#74): - routes are GET /sessions/:id/approvals/pending and POST /sessions/:id/approvals/:aid/decide {allow, subject}; - POST /sessions/:id/taint/clear {note}: the member clears the taint from an explicit card (TaintState::clear_by_member). Recorded write-ahead (kind taint.clear); with no records log, or when the record cannot be written, nothing is cleared (503). Scheduled (unattended) runs are never cleared (409); empty note 400; not tainted 409. Approving one action still never clears the taint; - a declined card tells the model to ask the member instead of retrying (shell_run and approval cards). Test support opens one undo store per manager (a store is locked while open). Tests: action_approvals_tests (+2). Workspace: 2253 -> 2255 passed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe
…(HGW-2) Owner decision 2026-10-15 (HGW). Hermes could not set up a project: the file tools refused package.json, foundry.toml, remappings.txt and the rest of the build configuration everywhere, and the macOS sandbox denied every write to .git/config and .git/hooks, so `git init` failed even offline. Project roots (agent-sidecar): - POST /sessions takes `projectRoots` (absolute, existing folders under live read+write subtree folder grants; needs `grants`). SessionGrants keeps them; project_create (next PR) adds the projects it makes. - Inside a project root the file tools (fs_*, file_write, sheet_write) may create and change build configuration. Outside, refused as before (the message now points at projects). A marker file alone never makes a folder a project. - Env files stay the member's everywhere. The file tools never write inside a .git folder (git does, through the sandboxed shell_run). - The toolchain's own config check (ffi, fs_permissions, ...) is unchanged: forge still refuses risky config whoever wrote it. Sandbox (agent-shell): - Seatbelt: .git/ is writable inside write roots (git init/config/commit work); a hook file other than git's *.sample files is still denied, as are removing or moving .git/hooks and symlinking a new one in. - bwrap: .git/config is no longer masked (parity); existing .git/hooks stays read-only. - RunReport.git_warnings: risky git state a run leaves behind that would run a program outside the sandbox later (hooks, core.hooksPath, core.fsmonitor, `!` aliases, filters, includes, ...), new since before the run. shell_run puts it in the run facts and the summary. - Linux bwrap run tests (skip unless bwrap works; CITRATE_REQUIRE_BWRAP=1 makes them fail instead, for the Linux CI lane). Tests: project_policy_tests (+6), agent-shell sandbox (+3 macOS real Seatbelt incl. git init/commit and hooks closed; +3 Linux), git_state unit (+2), shell_run git warning (+1). Workspace: 2253 -> 2265 passed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe
…x (HGW-2, #75) Per the HGW-W2 issue (#75): the Seatbelt `.env*` deny and the bwrap `.env*` masks exempt the templates' `.env.example` and `.env.sample` (not secrets; agent-guard exempts them the same way). `.env` and `.env.local` stay neither readable nor writable. Tests: sandbox env_files unit extended; real-Seatbelt test (+1). Workspace: 2267 -> 2268 passed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe
BerryManifold
left a comment
There was a problem hiding this comment.
Holding this permission expansion pending completion and verification of the git-state safeguards in the follow-up stack. Review found a blocking security boundary issue that remains present at #84. Details and local reproduction are retained privately under the organization security policy. Six project-policy tests passed locally; those tests do not cover the blocking scenario. Please address the follow-up before merging the affected stack.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe
…dependency updates, #85) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe
BerryManifold
left a comment
There was a problem hiding this comment.
Approval submitted at the owner’s explicit direction. This does not retract or resolve the previously reported git-state cleanup finding; no new verification of a fix is claimed.
Matr0xshka
left a comment
There was a problem hiding this comment.
Approval submitted at the owner’s explicit direction. This does not retract or resolve the previously reported git-state cleanup finding; no new verification of a fix is claimed.
The squash of #79 on main is identical to hgw/w1-taint-hic@00264e5, which this branch already contains, so the resolution keeps this branch's tree. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe
The merge-base changed after approval.
main is the squash of #79, identical to hgw/w1-taint-hic@00264e5, which this branch already contains; the resolution keeps this branch's tree. Lands #80-#84 as one squash (owner decision 2026-10-15). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe
… project_create, 24-step turns, auto-approve, git safety) (#84) * feat(sidecar): untrusted-content taint escalates to HIC approval (HGW-1) Owner decision 2026-10-15 (HGW (2)). Before this change any shell_run output, even an approved `node -v`, tainted the session, and every later command and sidecar-hosted write was declined for the rest of the conversation ("which this tool's host cannot ask for"), with no way to clear it. Now, in a session opened with hicAware: true: - a tainted effectful sidecar call (fs_*, file_write, sheet_write, toolchain tools, capsules) waits on a new per-session action card (GET /sessions/:id/actions/pending, POST .../actions/decide {id, allow, subject}); the card shows the tool, the exact canonical arguments, a summary, the hicReason and every taint source; - shell_run keeps its own card and still asks after taint; the card now carries hicReason and taintSources; - an approval releases that one call; the session stays tainted, so the next action asks again (never global, never automatic); - decisions are recorded (kind sidecar.tool_call, write-ahead). Without hicAware (headless, scheduled) nobody can be asked: the call is declined with citrate_agent_loop::cannot_ask_reason, which names the taint source and says why. Tests: agent-loop taint_tests (+1), action_approvals unit (+3), action_approvals_tests session/route tests (+6), shell_run_tests (+2, one rewritten), files_tests taint case rewritten to the card flow. Workspace: 2241 -> 2253 passed, 0 failed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe * feat(sidecar): approvals routes per #74, explicit taint clear, ask-not-retry (HGW-1) Aligns the HGW-1 interface with the HGW-W1 issue (#74): - routes are GET /sessions/:id/approvals/pending and POST /sessions/:id/approvals/:aid/decide {allow, subject}; - POST /sessions/:id/taint/clear {note}: the member clears the taint from an explicit card (TaintState::clear_by_member). Recorded write-ahead (kind taint.clear); with no records log, or when the record cannot be written, nothing is cleared (503). Scheduled (unattended) runs are never cleared (409); empty note 400; not tainted 409. Approving one action still never clears the taint; - a declined card tells the model to ask the member instead of retrying (shell_run and approval cards). Test support opens one undo store per manager (a store is locked while open). Tests: action_approvals_tests (+2). Workspace: 2253 -> 2255 passed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe * feat(sidecar,shell): project write policy and git inside the sandbox (HGW-2) Owner decision 2026-10-15 (HGW). Hermes could not set up a project: the file tools refused package.json, foundry.toml, remappings.txt and the rest of the build configuration everywhere, and the macOS sandbox denied every write to .git/config and .git/hooks, so `git init` failed even offline. Project roots (agent-sidecar): - POST /sessions takes `projectRoots` (absolute, existing folders under live read+write subtree folder grants; needs `grants`). SessionGrants keeps them; project_create (next PR) adds the projects it makes. - Inside a project root the file tools (fs_*, file_write, sheet_write) may create and change build configuration. Outside, refused as before (the message now points at projects). A marker file alone never makes a folder a project. - Env files stay the member's everywhere. The file tools never write inside a .git folder (git does, through the sandboxed shell_run). - The toolchain's own config check (ffi, fs_permissions, ...) is unchanged: forge still refuses risky config whoever wrote it. Sandbox (agent-shell): - Seatbelt: .git/ is writable inside write roots (git init/config/commit work); a hook file other than git's *.sample files is still denied, as are removing or moving .git/hooks and symlinking a new one in. - bwrap: .git/config is no longer masked (parity); existing .git/hooks stays read-only. - RunReport.git_warnings: risky git state a run leaves behind that would run a program outside the sandbox later (hooks, core.hooksPath, core.fsmonitor, `!` aliases, filters, includes, ...), new since before the run. shell_run puts it in the run facts and the summary. - Linux bwrap run tests (skip unless bwrap works; CITRATE_REQUIRE_BWRAP=1 makes them fail instead, for the Linux CI lane). Tests: project_policy_tests (+6), agent-shell sandbox (+3 macOS real Seatbelt incl. git init/commit and hooks closed; +3 Linux), git_state unit (+2), shell_run git warning (+1). Workspace: 2253 -> 2265 passed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe * feat(sidecar): project_create tool, HIC-approved (HGW-3) Hermes had no way to start a project: there was no default folder and no tool to make one, and template rendering was a panel action only. project_create {name, template?, params?} (sidecar-hosted, offered to sessions with folder grants when CITRATE_HERMES_PROJECTS=1): - name is one plain folder name (no separators, no `..`, no leading `.`/`-`/`~`, at most 64 chars); refused before anything is asked; - the projects root (CITRATE_HERMES_PROJECTS_ROOT, passed by core; default ~/Citrate/Projects) must exist and be under live read+write subtree folder grants; <root>/<name> must not exist (never reused); - always waits on an action card (HGW-1 queue) showing the exact folder and template; no HIC-aware app = nothing created; a tainted session shows one card carrying the taint reason; - after approval (grants re-checked): creates the folder, renders the template if asked, writes .citrate/project.json, and makes the folder a session project root (HGW-2 build-config writes apply); a failed render removes the new folder and says why; returns the path. Template rendering stays core's: the TemplateRenderer trait is the seam, and CliTemplateRenderer runs core's `citrate-templates render --root <templates> --template <id> --tier <T> --out <dest> [--param k=v]...` (CITRATE_HERMES_TEMPLATE_RENDERER + CITRATE_HERMES_TEMPLATES_ROOT, CITRATE_HERMES_TEMPLATE_TIER) through the shell runner, in the OS sandbox when there is one, writing only the new folder. Tests: projects unit (+4), projects_tests session/route (+10, incl. the CLI renderer under real Seatbelt). Workspace: 2265 -> 2279 passed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe * feat(shell): .env.example and .env.sample stay readable in the sandbox (HGW-2, #75) Per the HGW-W2 issue (#75): the Seatbelt `.env*` deny and the bwrap `.env*` masks exempt the templates' `.env.example` and `.env.sample` (not secrets; agent-guard exempts them the same way). `.env` and `.env.local` stay neither readable nor writable. Tests: sandbox env_files unit extended; real-Seatbelt test (+1). Workspace: 2267 -> 2268 passed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe * feat(sidecar): auto-approve list for sandboxed project commands + e2e (HGW-5) Owner decision 2026-10-15 (HGW (3), D-16 as amended): sandboxed, offline, project-scoped read and build commands run without a card. - The list is data: agent-sidecar/policy/auto_approve.toml (compiled in; CITRATE_HERMES_AUTO_APPROVE=0 turns it off, an absolute path replaces it). Rules are argv templates (program, subcommands, flags, value flags with kinds, required flags, operand kind and count). The shipped list: ls, pwd, cat, head, tail, wc, node <file>, npm run <script>, npm test, npx tsc, forge build/test, cargo build/test/check --offline, git init/status/diff/add/commit -m. - shell_run releases a match without a card only when the session is untainted, the OS sandbox is enforced, and the folder is inside a project root; that run may write only the project (not the wider grant). Network-capable or unlisted commands, or anything outside a project, still ask. After taint every command asks (HGW-1). - Each auto run is recorded write-ahead (kind shell.run.auto, actor agent, HIC-2); if it cannot be recorded it asks instead. Its result says so (run.approval = {mode: auto, rule, project}); member-approved runs say {mode: member}. - agent-loop: ToolOutcome::Vouched. An auto run's output derives from the project's own files (no network, project-only writes, untainted session), like a folder-grant file read, so it does not taint; the worker wire maps it to Ok (fail closed across the process boundary). - git through shell_run commits as "Hermes <hermes@citrate.invalid>" (the scratch HOME has no identity; git refused to commit). - shell_run's description now states the approval semantics and says to ask, not retry, after a decline. End to end (real Seatbelt, scratch home, scripted model): create the project (one card) -> write package.json, build.js and a buggy source -> git init/add/commit -> npm run build fails -> the model reads "ReferenceError: greeet is not defined" from the tool result, fixes it with fs_edit -> build passes -> commit; then untrusted output makes the next listed command ask, with the taint reason on its card. Negative checks in the same run: no write outside the project, no network, no risky git state. Linux (bwrap) runs the same tests; they skip without a working bwrap unless CITRATE_REQUIRE_BWRAP=1 (the CI lane). Tests: auto_approve unit (+4), auto_approve_tests (+7), e2e (+1), taint_tests vouched (+1), shell_run git identity (+1). Workspace: 2285 -> 2299 passed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe * docs(sidecar): project_create names the approvals routes (HGW-3) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe * feat(sidecar,shell): longer turns and failure output the model can act on (HGW-4) A build needs more than 8 model calls, and a failing step has to show the model why it failed. - Turns: default maxSteps 8 -> 24 (sessions::DEFAULT_MAX_STEPS), cap stays 32. A session's maxSteps still wins; the sidecar-wide default is configurable with CITRATE_HERMES_MAX_STEPS (clamped 1..=32). - Capture (agent-shell): a capped stream now keeps its first half-cap and its last half-cap (first error and final cause), with the truncation marker between them, instead of only the head. - shell_run: a non-zero exit puts the exit status and the end of stderr (or stdout when stderr is empty), at most FAILURE_TAIL_BYTES = 2048, in the summary the model reads first, and as stderr_tail/stdout_tail in the run facts. Successful runs are unchanged. - Toolchain: a run with no parseable report now states the exit code. Recognized compiler errors stay sanitized and trusted (the S6.3 guarantee: injected text in sources never reaches trusted output). When no cause is recognized, the end of stderr is handed over raw, bounded, and the result is Untrusted, so it taints the session (and, with HGW-1, later actions go to HIC cards). Tests: agent-shell runner (+1, two capture assertions extended), loop_capability_tests (+3), shell_run failure tail (+1), toolchain unparsed stderr (+1). Workspace: 2279 -> 2285 passed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe * feat(sidecar): narrow npm scripts, git hooks off, list route (HGW-5, #77) Aligns HGW-5 with the HGW-W11 issue (#77): - npm run is auto-approved only for the script names build, test, lint and typecheck (new rule field operand_values); any other script asks; - every git run through shell_run gets command-scope config core.hooksPath=/dev/null and core.fsmonitor=false (outranks the repository's own config), so Hermes's git never runs hooks; - GET /shell/auto-approve returns the list in effect (enabled, conditions, rules) for the app's Settings, behind the bearer. Tests: matrix extended; route test (+1); git hooks/fsmonitor off (+1). Workspace: 2302 -> 2304 passed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe * feat(shell): revert risky git state a run adds, report it (HGW-2, owner decision 2026-10-15) After every run the runner reverts what the run added that would make git start a program outside the sandbox later: risky config entries (hooksPath, fsmonitor, sshCommand, pager/editor, aliases, filters, diff/merge drivers, credential helpers, includes, non-https insteadOf, protocol allow=always, gpg.program, ...), new hook files, and a hooks folder or config swapped for a symbolic link. Normal config (user.*, remote.*, branch.*) stays; what was there before the run is never touched. Each revert is a line in RunReport.git_warnings and the shell_run summary says the changes were undone. A .git the run turned into a pointer (gitdir: file or symlink) is reported with the risky state where it points, not changed: that folder may be the member's own. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe * feat(sidecar): commits name the member and carry a Hermes trailer (HGW, owner decision 2026-10-15) Author and committer of commits made through shell_run are the member's: the identity the app passes (CITRATE_HERMES_GIT_USER_NAME/_EMAIL, both set and valid), else the member's own global git config (read once with the real git outside the sandbox, empty env but HOME), else "Hermes <hermes@citrate.invalid>". They reach git as GIT_AUTHOR_*/ GIT_COMMITTER_*, which outrank every config file. `git commit` gets --trailer "Co-authored-by: Hermes <hermes@citrate.invalid>", and command-scope config keeps git adding it (trailer.ifmissing=add, ifexists=addIfDifferent, commentChar #) over the repository's own. Not the model's to change, so refused before the member is asked: -c/--config-env for user.*, author.*, committer.*, trailer.*, include*, core.commentChar/String, commit.cleanup; commit --author, --cleanup, -C/-c/--reuse-message/--reedit-message (abbreviations included); and git commit-tree. Trailer/cleanup/comment-char config a run writes into the repository is reverted after it with the other risky git state. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe * feat(shell): git cleanup covers every repository in the write roots The post-run git cleanup (HGW-2, owner decision 2026-10-15) now finds repositories by walking every write root, before and after the run, instead of checking only the run's folder, its parents and its direct children: - `.git` folders at any depth, bare repositories, linked worktrees (`.git/worktrees/*`) and submodule git folders (`.git/modules/**`); - `config.worktree` is checked alongside `config`, and `hooks/` in each; - repositories the run creates are reverted against an empty baseline; - `.git` files/links and `commondir` are followed when they point at a git folder inside the write roots; ones pointing outside are reported and left unchanged; - more keys that run a program or move the worktree are treated as risky (core.worktree, core.alternateRefsCommand, submodule.*.update = !..., hook.*, interactive.diffFilter, sendemail commands, gpg.ssh.defaultKeyCommand, tar.*.command, guitool.*.cmd). The walk is bounded (300,000 entries, 48 levels). When it stops early the report says so, and a repository the before-run walk did not reach is reported rather than changed. Tests cover each case through the runner, under Seatbelt on macOS and under bubblewrap on Linux. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
HGW Week 1, runtime, 2 of 5. Stack: #79 → this PR (base
hgw/w1-taint-hic) → #81 → #82 → #83. Green on its own. Refs #75 (HGW-W2). Owner decision 2026-10-15 (HGW).What changes
Project roots and the file tools (agent-sidecar)
POST /sessionstakesprojectRoots: [abs path]. Each must be an existing folder under live read and write subtree folder grants, and it needsgrants. A bad root refuses the session.project_create(feat(sidecar): project_create, HIC-approved (v0.5.1 HGW-W3, runtime half) #81) adds the folders it creates.SessionGrantskeeps the list.fs_*,file_write,sheet_write) may create and change build configuration:package.json,foundry.toml,remappings.txt,medusa.json, Makefile,.cargo/config*, and so on. Outside a project root it is refused as before; the message now points to projects.projectRootsplus this session'sproject_create. A cloned repo could carry a marker..git/; git does that through the sandboxedshell_run. This closes an existing gap: before,fs_write .git/hooks/pre-commitwas allowed in any granted folder.ffi,fs_permissions,solcpath) is unchanged: forge still refuses risky config, whoever wrote it.Sandbox (agent-shell)
.git/is now writable inside write roots, sogit init,git configandgit commitwork. Still denied:*.samplefiles;.git/hooks;.git/hooks..git/configis no longer masked, to match macOS. An existing.git/hooksstays read-only..env.exampleand.env.sampleare readable ([v0.5.1 HGW-W2] Project-created write policy (build config, .git, .env templates, search path) #75)..envand.env.localstay unreadable and unwritable.RunReport.git_warningslists git state a run leaves behind that would make the member's own git run a program later, outside the sandbox. It covers hooks,core.hooksPath,core.fsmonitor,!aliases, filters and drivers, and includes. It reports only what is new since before the run.shell_runputs it in the run facts and the summary.Interface for the core lane
projectRoots(absolute) when opening a session: projects Hermes created before (fromproject_createresults, feat(sidecar): project_create, HIC-approved (v0.5.1 HGW-W3, runtime half) #81) and folders the member marked as projects.run.git_warningsfromshell_runresults on the Activity card. These are warnings only; nothing is changed.Needs a decision
.git/configwrites (security sign-off). As directed,.git/is writable inside grants. Path rules cannot stop config that makes git run a program (core.hooksPath,core.fsmonitor). This PR reports such config after each run (git_warnings). feat(sidecar): auto-approve list + build e2e (v0.5.1 HGW-W11) #83 also pinscore.hooksPath=/dev/nullandcore.fsmonitor=falsefor every git run Hermes makes. The member's own terminal git is still exposed to anything Hermes wrote. Options:git initin core, as [v0.5.1 HGW-W2] Project-created write policy (build config, .git, .env templates, search path) #75 and core#289 propose, and keep.git/configread-only for sandboxed runs.projectRoots, so a member who marks a folder as a project in the app gets the same result.NODE_ENV,CI, …).Tests (red first)
project_policy_tests: +6. Build config is writable inside a project and nowhere else; env files are always refused; a marker alone is not a project; no.gitwrites;projectRootsis validated.tests/sandbox.rs, real Seatbelt: +4.git init/add/commitwork; hooks stay closed against write, rename, copy, rm and symlink; env example files are readable and real env files are not; risky git state is reported.CITRATE_REQUIRE_BWRAP=1, which the Linux CI lane should set.git_stateunit: +2.shell_rungit warning: +1.🤖 Generated with Claude Code
https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe