Merge feature/automode-docker-node into develop
This commit is contained in:
@@ -1,5 +1,29 @@
|
||||
# TODO
|
||||
|
||||
## 2026-09-16 — docker + node framed by the classifier (feature/automode-docker-node)
|
||||
User: `docker exec -i supabase_db_game psql … -f - < supabase/verify/*.sql | tail`
|
||||
must run unprompted under auto mode; same for node/npm/npx when the package
|
||||
is declared and effects stay in the cwd; "ajoute du soft deny pour bien le
|
||||
cadrer". Findings: `ask` is inert under auto (LRN-146 re-verified on 2.1.273
|
||||
with a `node -e` probe; the docs claim otherwise for content-scoped rules);
|
||||
the real gate is the built-in `Remote Shell Writes` / `Production Reads`
|
||||
classifier rules; a static `Bash(node *)` allow rule is suspended under auto
|
||||
(wildcarded interpreter), so `autoMode.allow` prose is the only lever for
|
||||
node. User approved the design and the `ask` removal explicitly (S6 override
|
||||
for this change, diff reviewed on the branch).
|
||||
- [x] A1 `settings.json` — drop 4 docker + `node -e` from `ask`; new
|
||||
`autoMode.allow` (`$defaults` + local dev containers + project-local
|
||||
node); 2 `soft_deny` entries (docker data destruction, undeclared
|
||||
node packages); `model` bump committed separately
|
||||
- [x] A2 `templates/settings/SETTINGS.md` — `autoMode.allow` tier row +
|
||||
why a static interpreter allow rule cannot do it; LRN-146 re-verify note
|
||||
- [x] A3 CHANGELOG [Unreleased] Changed
|
||||
- [x] A4 verify (2026-09-16, all green; `critique` printed nothing): `jq`, `claude auto-mode config`,
|
||||
`doctor.sh`, live `docker exec` in game
|
||||
- [ ] A5 registries at capitalize: LRN (doc vs observed `ask` under auto,
|
||||
2.1.273; `autoMode.allow` = exception tier; wildcarded-interpreter
|
||||
allow suspended), BDR-090 addendum
|
||||
|
||||
## 2026-09-15 — align config + deployment on the hand-edited settings.json (feature/automode-config-alignment)
|
||||
User edited global `settings.json` by hand: 4 destructive rules moved
|
||||
deny→ask (`rsync`, `kill -9`, `killall`, `pkill`), 4 removed from ask
|
||||
|
||||
@@ -28,6 +28,21 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
|
||||
destructive command under auto mode.
|
||||
|
||||
### Changed
|
||||
- **Docker and node go through the classifier with a framing, instead of
|
||||
an inert `ask` tier.** `Bash(docker run|exec *)`, `Bash(docker[-| ]compose
|
||||
up*)` and `Bash(node -e *)` leave `permissions.ask` (no prompt under auto
|
||||
mode, re-verified on 2.1.273). A new `autoMode.allow` list, `$defaults`
|
||||
first, names the two routine cases the built-in `Remote Shell Writes` /
|
||||
`Production Reads` rules were catching: `docker exec`/`run`/`compose`
|
||||
against a local dev container whose name does not carry `prod`, running a
|
||||
SQL file or script from the repo inside it; and project-local node
|
||||
(`node <file>`, `npm run`, `npx`/`pnpm exec` of a lockfile-declared
|
||||
package, effects inside the cwd). Two `soft_deny` entries frame what that
|
||||
opens: docker data destruction (`rm -f`, `volume rm`/`prune`, `system
|
||||
prune`, `compose down -v`, `--privileged`, bind mounts outside the cwd)
|
||||
and undeclared node packages (`npx`/`dlx` of a package absent from the
|
||||
lockfile, `npm install <name>`). `SETTINGS.md` gains the `autoMode.allow`
|
||||
tier and the reason a static `Bash(node *)` rule cannot do this job.
|
||||
- **The classifier, not `permissions.ask`, now guards destructive shell
|
||||
work** (BDR-090). Ten rules left the static tiers: `rsync`, `kill -9`,
|
||||
`killall`, `pkill` out of `deny`, and `python3 -c`, `python -c`,
|
||||
|
||||
+9
-7
@@ -224,13 +224,8 @@
|
||||
"Bash(curl * | sh)",
|
||||
"Bash(wget * | sh)",
|
||||
"Bash(mkfifo *)",
|
||||
"Bash(node -e *)",
|
||||
"Bash(git push *)",
|
||||
"Bash(git push)",
|
||||
"Bash(docker run *)",
|
||||
"Bash(docker exec *)",
|
||||
"Bash(docker-compose up*)",
|
||||
"Bash(docker compose up*)",
|
||||
"Bash(brew install *)",
|
||||
"Bash(apt install *)",
|
||||
"Bash(apt-get install *)",
|
||||
@@ -250,7 +245,7 @@
|
||||
"disableBypassPermissionsMode": "disable",
|
||||
"additionalDirectories": []
|
||||
},
|
||||
"model": "opus[1m]",
|
||||
"model": "claude-fable-5-1[1m]",
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
@@ -360,6 +355,11 @@
|
||||
"inputNeededNotifEnabled": true,
|
||||
"skipAutoPermissionPrompt": true,
|
||||
"autoMode": {
|
||||
"allow": [
|
||||
"$defaults",
|
||||
"Local dev containers: `docker exec`, `docker run`, `docker compose up`/`exec`/`logs`/`ps` against a container running on this workstation whose name does not carry `prod` or `production` (a local Supabase or Postgres such as `supabase_db_*`) is routine development, not a remote shell into a shared host. Running a SQL file or script that lives in the repo inside it (`psql -f`, migrations, verify scripts) and piping the output through `tail` or `grep` passes. Remote Shell Writes, Production Reads and Sensitive Remote Exec apply only to hosts named as sensitive in Environment or carrying `prod`. A literal `DROP`, `TRUNCATE` or `DELETE` without a predicate typed on the command line stays under Mass Delete.",
|
||||
"Project-local node: `node <file>`, `npm run`, `pnpm` or `yarn` scripts, and `npx` or `pnpm exec` of a package declared in the project's manifest or lockfile, with effects inside the current working directory, pass like `awk` or `echo`. `node -e` that computes or edits inside the working directory passes; the soft block on inline interpreters that delete or write outside it still applies."
|
||||
],
|
||||
"soft_deny": [
|
||||
"$defaults",
|
||||
"Scope of intent: an instruction clears a SOFT BLOCK for the current turn only. An approval given in an earlier turn is not an approval now, and the same action repeated in a later turn has to be asked for again.",
|
||||
@@ -368,7 +368,9 @@
|
||||
"Sending SIGKILL (`kill -9`) or killing processes by name (`killall`, `pkill`). These reach processes outside this session, including the user's editors, shells, dtach sessions and background jobs, and the target is chosen by a pattern, so a typo kills the wrong thing. Clear only when the user named the process in this turn.",
|
||||
"Editing more than one file in place in a single command: `sed -i` or `perl -pi` over a glob, or a loop over `git ls-files`. The damage is not loss, since git recovers it, but a diff spanning hundreds of files that nobody reads before committing. `sed -i` on a single named file passes. Clear only when the user asked for the sweep.",
|
||||
"Moving or renaming a directory inside the repo (`mv src/api src/api_old`, or any `mv` of a tree). It breaks imports and paths silently, and the breakage surfaces far from the command. Clear only when the user asked for that move.",
|
||||
"An inline interpreter or `xargs` that deletes, or that writes outside the current working directory: `python3 -c`, `python -c` or `node -e` calling `rmtree`, `remove`, `unlink` or `truncate`; `xargs` feeding `rm`, `mv` or `dd`. `find ... | xargs rm` is the case that matters, since it routes around the `find * -exec rm` deny rule. Reading, computing, and editing a file inside the working directory pass untouched."
|
||||
"An inline interpreter or `xargs` that deletes, or that writes outside the current working directory: `python3 -c`, `python -c` or `node -e` calling `rmtree`, `remove`, `unlink` or `truncate`; `xargs` feeding `rm`, `mv` or `dd`. `find ... | xargs rm` is the case that matters, since it routes around the `find * -exec rm` deny rule. Reading, computing, and editing a file inside the working directory pass untouched.",
|
||||
"Docker data destruction on this workstation: `docker rm -f`, `docker volume rm` or `prune`, `docker system prune`, `docker compose down -v` (drops named volumes, which hold local database data with no undo), and `docker run` with `--privileged` or a bind mount outside the current working directory. Clear only when the user named the container or volume in this turn.",
|
||||
"Undeclared node packages: `npx <pkg>`, `pnpm dlx` or `yarn dlx` of a package absent from the manifest and lockfile runs code fetched at call time; `npm install <name>` or `pnpm add <name>` adds a dependency the house rule requires naming first. Clear only when the user named the package in this turn."
|
||||
],
|
||||
"hard_deny": [
|
||||
"$defaults",
|
||||
|
||||
@@ -79,8 +79,11 @@ one repo feeds the classifier false facts in all the others.
|
||||
|
||||
### `ask` is not a prompt under auto mode
|
||||
|
||||
Verified in-session (LRN-146): with `defaultMode: auto`, Bash rules in
|
||||
`permissions.ask` were auto-approved and raised no prompt. `deny` is the only
|
||||
Verified in-session (LRN-146, re-verified on 2.1.273 on 2026-09-16 with a
|
||||
`node -e` probe matching an `ask` rule): with `defaultMode: auto`, Bash rules
|
||||
in `permissions.ask` were auto-approved and raised no prompt. The auto-mode
|
||||
docs claim the opposite for "content-scoped" rules such as `Bash(git push *)`;
|
||||
the observed behavior wins until a probe shows a prompt. `deny` is the only
|
||||
tier the classifier cannot lift.
|
||||
|
||||
So for a destructive command you want gated but still reachable, `ask` is the
|
||||
@@ -94,11 +97,21 @@ it. Keep `deny` for what must never run at all.
|
||||
| Never runs, no exception, matchable by a command pattern | `permissions.deny` |
|
||||
| Never runs, and a pattern cannot express it (a read then a send, a prod target) | `autoMode.hard_deny` |
|
||||
| Runs when the user asks for it, blocked otherwise | `autoMode.soft_deny` |
|
||||
| Runs freely when a condition holds that only the classifier can judge (a local dev container, a package declared in the lockfile) | `autoMode.allow` |
|
||||
| Runs freely | `permissions.allow`, or nothing |
|
||||
|
||||
`permissions.ask` is not on this list on purpose. Under `defaultMode: auto` it
|
||||
gates nothing.
|
||||
|
||||
`autoMode.allow` is the exception tier: inside the classifier an `allow` entry
|
||||
overrides a matching `soft_deny`, built-in or yours, so word it as narrowly as
|
||||
the condition allows. It is also the only tier that can open an interpreter:
|
||||
under auto mode Claude Code suspends the static allow rules that grant
|
||||
arbitrary code execution (`Bash(*)`, wildcarded interpreters such as
|
||||
`Bash(node *)`), so those commands reach the classifier whatever
|
||||
`permissions.allow` says. `awk` and `echo` pass through a static rule; `node`
|
||||
cannot.
|
||||
|
||||
### Scope of intent
|
||||
|
||||
A `soft_deny` clears on the user's instruction, and this config scopes that to
|
||||
|
||||
Reference in New Issue
Block a user