Repository navigation
Mission role gates: rounds 3 and 4 review fixes (M9.9, M9.42) - #92
Merged
Merged
Conversation
Adversarial review of the Bash reader (_shellread.py) and the scope gate's
plumbing around it. Reader changes only; the rule tables are untouched.
Coverage (writes that slipped past the gate, now judged):
- `]]` glued to `;` `&&` `||` `)` ends the [[ test, so the rest of the
command (and later lines) is read again (reader-coverage/F1).
- a `for`/`select` loop variable is bound to its list, so `"$f"` writes are
judged (reader-coverage/F2).
- export/declare/local/readonly/typeset NAME=VALUE records NAME, like a bare
assignment (reader-coverage/F3).
- a heredoc or here-string fed to a shell on stdin is read as its program
(reader-coverage/F7).
- xargs -I{} replaces only the {} inside the sh -c string, so the inner
command is read (reader-coverage/F8).
- perl -0pi / -lpi: -0 and -l take only an octal value, so a clustered -i is
found (reader-coverage/F13).
- a simple {a,b} brace at the start of a word expands to its words
(reader-coverage/F14).
- `cd` inside a function body, an untaken branch or a pipeline element no
longer moves where a later write lands (reader-coverage/F15).
- su finds `-c` after the user (`su USER -c`); setsid and runuser are looked
through (reader-coverage/F20).
False blocks (legitimate commands, now allowed):
- the same `]]`-glued fix lets writes to /tmp after a glued test through, and
reads the heredoc body as data (reader-false-positive/F2).
- a `$` right before a closing double quote is a literal `$` (an end-anchored
regex), not a nested locale string (reader-false-positive/F6).
- the redirect of a `{ ... }` group is judged where the shell opens it, before
a `cd` inside the group (reader-false-positive/F7).
Tests: scripts/test/mission-scope-bash.test.py grows a "round 3: the reader"
section (220 -> 279 rows). mission-roles and opencode-plugin unchanged; make ci
green. The reader stays linear on long inputs (brace expansion is capped).
Deferred: `CMD | while read VAR` (read's input is runtime-unknown), the
`echo ... | bash` pipe form of F7, and the `at`/`crontab` stdin forms of F20 —
each needs machinery beyond a reader change and the findings mark them optional.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj
…42 (round 3)
Fixes the confirmed adversarial-review findings for the scope track (ROADMAP
§4.12, M9.9), each with its reproduction and allow-controls added as a new
"round 3: the rules" section of mission-scope-bash.test.py (159 new rows).
Coverage (writes that slipped through):
- F4 curl -O/-J/--remote-name and bare/`-r`/`--mirror` wget save into the cwd
(judged: --output-dir / -P, else ".").
- F5 the supervisor's bare-dir exemption now only covers mkdir/touch; a copy,
extract or recursive removal into or over docs/agents/mission/ (or an
ancestor it reaches) is a write of its contents. A tree removal bounded by
find's -name tests is checked against what is actually there.
- F6 mv/rsync --remove-source-files/tar --remove-files sources are removed → judged.
- F9 gh repo clone / release download and npm/pip installs judged by their fixed
output location (operand/basename, --prefix/-d, else ".").
- F10 git bisect/remote/fetch/update-index/read-tree/checkout-index/symbolic-ref/
gc/prune/repack/maintenance/lfs/hash-object added to GIT_WRITES with their
read forms; format-patch/archive/bundle/init judged by output target; bisect
run / rebase -x / submodule foreach run their command.
- F11 a builder's git rm/mv/checkout/restore/reset/stash pathspecs into
docs/agents/mission/ are judged (role repo-writes unchanged; supervisor
git `--` left by design).
- F12 in-place tools and formatters (sort -o, sponge, yq -i, ed/ex/vim -c,
dos2unix, iconv -o, split, mktemp, scp, gzip, ruff --fix, black, prettier
-w, gofmt -w) and the npx wrapper.
- F19 `events decide --role human` and the plan.approved human type.
False positives (legitimate work blocked):
- F1 git -C <abs> after an unresolved cd no longer crashes the gate (join d or "/").
- F3 BSD `sed -i ''` drops the empty suffix before splitting script from files.
- F4 an unknown tar -C / unzip -d / patch -d / cp -t value is judged by its known
prefix, not treated as ".".
- F5 find substitutes {} inside any argument against the start path; -execdir
runs in the match's dir.
- F8 cp -t with an unknown dir judges the dir, never a source operand.
- F9 tar -xO / --to-stdout writes nothing.
- F10 git config get/list, tag/branch --sort <key>, unzip -h, find -L … -delete.
New items:
- M9.42/judge-cmd: no mission role or builder may set ALOOP_JUDGE_CMD/
ALOOP_JUDGE_TIMEOUT (env prefix, env VAR=, export/declare, or a mention in a
bash -c / interpreter string); the supervisor may, and `mission judge run` stays.
- real-areas/F5: `intent new` and `aladdin [run] "<intent>"` refused for a mission
role other than mission-planner.
- real-areas/F9: corrected the ledger-recovery advice (put the file back; name that
rows after the last commit are lost; the artifact-gate part is another track's).
- reader-false-positive/X1: a builder's bare `mission evaluate` / `mission assess`
(which write a report) are blocked; read-only forms stay allowed.
make ci green (mission-scope-bash: 379 rows, 235 blocked / 144 allowed; mission-roles: 32 checks).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj
x=( $(git commit …) ) and declare -a x=( `…` ) now judge the commands they run, and the `--` after time [-p] no longer hides the command. Both found by M9.30's third review of the shared reader. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj
…42 and the reader Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj
adeelahmad
marked this pull request as ready for review
October 20, 2026 08:53
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj
adeelahmad
marked this pull request as draft
October 20, 2026 09:25
…edger advice, narrow items)
- find -exec/-execdir: CMD is judged once per start path, with {} a match
under it and the text around {} kept (the round-3 regression); -name tests
bound a removal of an ancestor only when every match must pass them.
- Over-blocks fixed: wget --spider/--version/--help, the supervisor's
`find -prune -exec rm|mv {}`, gh release download -O, global npm/pnpm/yarn
installs, pip wheel -w, git format-patch --stdout, isort --check-only,
dos2unix -n IN OUT, `aladdin stats|sweep|capabilities|ids|config` (tools in
BIN), ALOOP_JUDGE_CMD named as data (only a set blocks), and the maxsplit
DeprecationWarning on export/declare.
- F9: a blocked ledger write says to leave the ledger as it was, and names
the repair for its layout (tracked: git diff/checkout HEAD only when the
damage is all that changed, else the human; untracked: the human).
mission-roles checks both layouts end on a ledger that verifies.
- Narrow items: removals and extracts through an ancestor (the tree root too),
the supervisor's git rm/mv/clean/stash -u, builder pathspecs through an
ancestor and without `--`, mv -t and zip -m sources, git merge-file, clone/
download/install forms, in-place tools and formatters, `intent new` and
`intent check|normalize` by non-planner roles, the `aloop|aladdin <tool>`
chain and package runners, and the judge var set by read/printf -v/mapfile/
let/nameref and interpreter env mappings.
- Linear-time bounds: an oversized find (start paths x -exec words), a
{}/.. climb past 32 levels, and more than 16 distinct git clean/stash
filters block; reaches() and git ls-files answers are memoized per call.
Rows: 379 -> 601 (110 of them fail on the base gate); checks 1 -> 2;
mission-roles 33 -> 34 checks. make ci passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj
…er and rule fixes The merge follows the #86 thread's recipe for _shellread.py (Word.body ours, Cmd keeps end plus loop/pipe, their heredoc registration, `for NAME do`), and doors.py skips the reader's compound markers and keeps a pipeline element's cd in the element. Round 4 (the verifiers of round 3): the scope gate walks every state a command may leave the shell in. A cd inside an if/case/loop body, group or function, or after && / ||, may or may not have run, so the rest is judged from each dir (a compound's own `} > f` from where it started); a loop body once per item; unquoted $VAR splits; eval and source /dev/stdin run in the calling shell; a background job's cd stays in it; nesting past 8 levels, more than 1024 walks or 50 000 judged commands blocks. find's -name tests bound a removal through -o alternatives and negations; -fprint/-fls write; wget --spider -O, npm -g --prefix, --global=false, os.environ.__setitem__/ update, xargs -i/-I{}, runuser options and `command -p export` are read. Residuals are ROADMAP M9.54. mission-scope-bash.test.py: 718 commands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj
adeelahmad
marked this pull request as ready for review
October 20, 2026 13:52
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj
adeelahmad
pushed a commit
that referenced
this pull request
Oct 20, 2026
…e M9.31 branch ROADMAP: master's M9.54 kept; this branch's new items renumbered M9.55-M9.60. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012rsDo9GYzNx8tXucywZHnA
adeelahmad
pushed a commit
that referenced
this pull request
Oct 20, 2026
…nto the dsh follow-ups Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AiLhAWTRtwbgMCDQNJUbea
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Adeel · project thread
Before: two adversarial reviews of the M9.9 gates (#74) confirmed findings that let common shell idioms slip past the mission role path guard, such as
if [[ … ]]; then … fi,for f in src/*.py; do sed -i … "$f"; done,export OUT=…,curl -O, a script fed tobashon a heredoc,[ -d x ] && cd x; touch src/a.pyand{ cd docs/agents/mission; }; echo > eval/x.json. Other everyday commands were blocked by mistake: BSDsed -i '',find -exec … {}.bak,find . -path ./node_modules -prune -o -name '*.pyc' -exec rm -f {} +, and… | xargs -I{} rm -f {}.After: every confirmed reader and rule finding from rounds 3 and 4 is a row in
mission-scope-bash.test.pythat failed before its fix, with its allow-controls as rows too. No mission role or builder can setALOOP_JUDGE_CMD(M9.42), a non-planner role creates no Aladdin intent, and a builder runs no writingmission evaluate|assess.The reader now follows every state a command may leave the shell in. A
cdinside an if, case, loop body,{ …; }group or function, or after&&or||, may or may not have run, so the rest of the command is judged from each dir it may leave.How:
_shellread.py, pluswalk/forkingate-mission-scope):cwd+,cwd|(elif, else,;;) andcwd-. A compound's own} > fis markedownand judged where the compound started.$VARsplits into words.evalandsource /dev/stdinrun in the calling shell.cdstays in the job.]]glued to an operator, loop variables, export/declare/local (also behindcommand -pandbuiltin), arrays, heredoc and here-string programs, perl-0pi, brace expansion,time -p --, and the su, setsid and runuser wrappers. runuser options after-u USERare now read too.gate-mission-scope):wget --spider -O, a global npm install with--prefix, and--global=false.mv/rsyncsources, and removals that reachdocs/agents/mission/.-nametests bound a removal through alternatives:-ogroups count, and a negation or-pathno longer voids the bound. A test after the action, or one past an-oor a comma, bounds nothing. An expression the gate cannot read bounds nothing either.-fprint,-flsand-fprintfcount as writes.os.environ.__setitem__andupdate([(…)])count as judge-variable sets.xargs -iand-I{}: a plain{}operand is an unknown stdin word, and a{}inside code is re-read.doors.pyskips the new markers, and a pipeline element'scddoes not leak there either.Not in this PR:
cd, acdthat fails before;,cds that accumulate over loop iterations,;&,evalwith a redirect, function bodies judged at their definition, and interpreter code. The backstop for all of them is M9.41, the tree diff at a role's stop.Evidence:
mission-scope-bash.test.py: 718 commands (472 blocked, 246 allowed); it had 225 on master.mission-doors.test.py: 21 checks.mission-roles.test.py: 34 checks.make cipasses locally on 539918e: conformance 78/78, tracking 95 requirements.🤖 Generated with Claude Code
https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj