Skip to content

feat(sidecar): auto-approve list + build e2e (v0.5.1 HGW-W11) - #83

Closed
SaulBuilds wants to merge 3 commits into
hgw/w1-loop-capabilityfrom
hgw/w1-auto-approve
Closed

SaulBuilds wants to merge 3 commits into
hgw/w1-loop-capabilityfrom
hgw/w1-auto-approve

Conversation

@SaulBuilds

Copy link
Copy Markdown
Contributor

HGW Week 1, runtime, 5 of 5. Stack: #79 → #80 → #81 → #82 → this PR (base hgw/w1-loop-capability). Green on its own. Refs #77 (HGW-W11). Owner decision 2026-10-15, HGW (3) (D-16 as amended). The command list needs owner confirmation before merge.

What changes

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.
  • Each rule is an argv template: program, subcommands, allowed flags, value flags with kinds, required flags, operand kind, count and allowed values. -- is never allowed.

Shipped list:

  • ls, pwd, cat, head, tail, wc (relative paths only)
  • node <relative file>
  • npm run build|test|lint|typecheck, npm test, npx tsc --noEmit
  • forge build|test (no --ffi)
  • cargo build|test|check with --offline or --frozen
  • git init, git status, git diff, git add <relative paths>, git commit -m

When a listed command runs without a card (all must hold):

Anything unlisted, network-capable or outside a project asks, as before. After taint every command asks (#79). With no sandbox (Windows), nothing is auto-approved.

Records and results:

  • Each auto run is recorded write-ahead (shell.run.auto, actor agent, HIC-2). If it cannot be recorded, it asks instead.
  • Results carry run.approval = {mode: "auto", rule, project}; card runs carry {mode: "member"}.

agent-loop: ToolOutcome::Vouched. An auto run's output comes from the project's own files: no network, writes only in the project, untainted session. Like a folder-grant file read, it does not taint. The worker wire maps it to Ok, which fails closed across the process boundary.

git through shell_run:

Other:

End to end (e2e_build_tests; real Seatbelt, scratch home, scripted model, real node, npm and git)

  1. Create the project with project_create (the only card).
  2. Write package.json, build.js and a source file with a bug.
  3. git init, git add, git commit.
  4. npm run build fails. The model reads ReferenceError: greeet is not defined from the tool result and fixes it with fs_edit.
  5. The build passes; commit again.
  6. An unlisted command, approved on a card, brings in untrusted output. The next listed command (git status) then asks, with the taint reason on its card.

Negative checks in the same run:

  • the build cannot write outside its project;
  • it cannot reach a local listener (no network);
  • no risky git state is left behind;
  • nothing outside the grants changes.

On Linux the same tests run under bwrap. They skip without a working bwrap unless CITRATE_REQUIRE_BWRAP=1.

Interface for the core lane

  • No new cards. The Activity monitor should label run.approval.mode == "auto" runs, showing the rule and project.
  • Settings reads GET /shell/auto-approve.

Needs a decision

  1. Confirm the command list (D-16 as amended). Two choices are mine: git init (needed so that later commands are not tainted by a carded git init) and node <file>. forge/cargo without --offline: forge is allowed without --offline because the sandbox already blocks the network, while cargo must pass --offline or --frozen.
  2. The commit identity Hermes <hermes@citrate.invalid> overrides the repo's user.name/user.email for Hermes's git runs. Is that wording and policy right?
  3. Is Vouched right? It treats auto-run output as trusted, consistent with the 2026-10-01 decision that folder-grant reads are trusted.

Tests (red first)

  • auto_approve unit: +4. Covers the accept and refuse matrix (including the network cases, git -c, --ffi, unlisted npm scripts, --, absolute and .. paths), list validation, and the environment switch.
  • auto_approve_tests: +8. Real sandbox: no card for listed commands; asks off the list or outside a project; taint blocks auto; no sandbox means no auto; project-only writes; switch off; recorded; list route.
  • e2e_build_tests: +1.
  • taint_tests vouched: +1.
  • shell_run: git identity +1, hooks and fsmonitor off +1.
  • Workspace: 2288 → 2304 passed, 0 failed.

🤖 Generated with Claude Code

https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe

SaulBuilds and others added 2 commits October 15, 2026 12:33
… (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
)

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

@BerryManifold BerryManifold left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Holding auto-approval pending resolution of the blocking git-state security boundary issue confirmed in the #84 follow-up. The owner-approved command-list scope is understood; the blocker concerns enforcement around command effects. Reproduction details are retained privately under the organization security policy. Please complete the safeguard and add regression coverage before enabling this path.

…pendency updates, #85)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018wWZZ8GRVU9USh3kKQHsFe

@BerryManifold BerryManifold left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 Matr0xshka left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@SaulBuilds

Copy link
Copy Markdown
Contributor Author

Landed in main via #84 (squash 75e21cd), per the owner's 2026-10-15 decision to land the HGW stack #80–#84 as one squash. Reviewed and approved here.

@SaulBuilds SaulBuilds closed this Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants