8000 The door rule on every host and role (M9.31) by adeelahmad · Pull Request #105 · adeelahmad/aloop · GitHub
[go: up one dir, main page]

Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
345d6fa
M9.31: door gate on every host and role
claude Oct 20, 2026
2eb9b50
M9.31: dispatched roles carry the core principles; hosts name the doo…
claude Oct 20, 2026
23f8fff
OpenCode: door rule on every call and role (M9.31)
claude Oct 20, 2026
648c87a
dsh: the door rule on every tool and role (M9.31)
claude Oct 20, 2026
091c598
M9.31 integration: decode a dsh MCP input once, and only its own shape
claude Oct 20, 2026
b169796
M9.31 review: the door gate holds where its steps fail, patch and pat…
claude Oct 20, 2026
8604b67
Merge origin/master (#99, #101, #102, #106, #107) into the M9.31 bran…
claude Oct 20, 2026
96f5c29
ROADMAP M9.54: the static evidence for Claude Code's `!` command
claude Oct 20, 2026
36794cc
Merge origin/master (#92 M9.9 roles, the shared shell reader) into th…
claude Oct 20, 2026
8bcd9de
M9.31: second review fixes (groups, conditional cd, copies, symlinks,…
claude Oct 20, 2026
88204c6
M9.31: third review fixes (cd candidates, copies placed where they ru…
claude Oct 20, 2026
44ec2ca
M9.31: fourth review fixes (copy options, glob and contents copies, c…
claude Oct 20, 2026
5b2ab4c
Merge origin/master (dsh follow-ups #104, run 52 #112, M9.50 #103, al…
claude Oct 20, 2026
2ffdd59
M9.31: fifth review fixes (conditional exits, background lists, find …
claude Oct 20, 2026
c6ee30a
Merge origin/master (#114 artifact gate, #113 approval pins, #116 fre…
claude Oct 20, 2026
4d0b1de
M9.31: sixth review fixes (the reading shell's $PWD, echo && exit, un…
claude Oct 20, 2026
b12daf7
Door test: a held lock runs the decision's clock out, not a slow runn…
claude Oct 20, 2026
2975cd2
M9.31: seventh review fixes (a mark is a path only where it starts a …
claude Oct 20, 2026
9a4e9a0
Merge origin/master (#117 fetched web content is data, M10.3) into th…
claude Oct 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ tag in lockstep. Tag releases as `vMAJOR.MINOR.PATCH`.
### Added
- Plan approval is a gate (ROADMAP M0.9): `gate-dispatch` starts no execution role (RED, SCAFFOLD, GREEN, structural review, final gate) of a sprint until the human approved that sprint's plan as it is now. `events authority --sprint N` exits 3 with the line to type, `approve SN <code>` (reviewed, the code naming the sha256 of the sprint's plan files, its intake and standards included) or `autonomous SN <code>` (autonomy for that sprint, re-plans included, the code naming the authority record); the task contract's `STORY_DIR` and task must be a live story and task of that plan's frozen `ids.json`; the line must be among the human's captured prompts and typed after the sprint's last decision (M9.17's mechanism). A changed plan, a re-plan in reviewed mode, a roll-back to an old line, another sprint's line, a negated line and a hand-edited or emitted record all block (`plan.approved` and `human.approval` cannot be `events emit`ted); an approved autonomous mission grants its own sprints autonomy with no line. `events approval SPRINT|TASK_ID` is the check. Codex's `spawn_agent` (`agent_type`, `message`) and OpenCode's `task` now reach `gate-dispatch` too, and `claude -p`, `codex exec` and `opencode run` from a tool call are blocked in every aloop project, not only where a mission is set. `e2e-live.sh` pre-approves with an `autonomous S1 <code>` line in its prompt. Reason: the north star allows no human help beyond approval, and the approval was a playbook rule a supervisor could skip. A 4-lens adversarial review found 9 defects: 7 fixed with a check red on the first head (cross-sprint or unplanned `STORY_DIR`, a retyped line kept stale, mission autonomy over a non-mission sprint, `transcripts prune` dropping the prompts grants cite, `codex plugin add` refused, a code past column 200, a crash on a malformed run-log line), 2 in the playbook (intake reuses a mission's handed-off intent; a reviewed re-plan waits for the human's new line). `transcripts prune` now keeps the human's prompts. Test: `plan-approval.test.py` (20 checks, red on master), plus the host suites. The approved plan's files include the sprint's `intake.md` and `standards.md`, and a grant pins them file by file: GREEN's check for an unplanned dependency reads only the text the human's grant pins, the plan they approved or, under their autonomy grant, the plan as the planner's gate last passed it (`gate-stage2-complete` records `plan.gated` on the planner's stop), as it was then, so text added later names nothing and a re-plan does not undo an approved dependency.
- Core agent principles and the one-way / two-way door rule (ROADMAP §4.12, M9.30): seven core principles from Amazon's Leadership Principles, the door rule first, go to every aloop agent at session start (`aloop mission principles show --core`). A two-way door (a branch, a local edit, a test, a draft PR) is decided, not asked. A one-way door (`aloop mission doors list`: a push or merge to the default branch, a force-push, a release, a deploy, a message sent outside, a secret, dropped data, …) is stopped by a PreToolUse gate with `ASK[door] <id>` until the human records an authorization in their own terminal (`aloop mission doors authorize ID --by NAME --reason TEXT [--until DATE] [--once DIGEST]`, a hash-chained ledger row); a wall (switching a gate off, forging a human record) is never crossed. While a run is live, a question to the human must name a door the gate stopped and nobody authorized. Reason: Adeel's request that agents decide two-way doors themselves and ask only at a one-way door not already authorized. Test: `mission-doors.test.py`. Hardened by an adversarial review (15 findings, each a test): the gate reads Bash as the shell does (comments, line continuations, heredoc bodies, `xargs`, `find -exec`, `echo … | sh`), a branch condition reads the directory the command acts on (`git -C`, `cd`), settings are guarded wherever they live, dry runs and `command -v` are two-way, the newest decision by time counts and a revoke holds on every branch, a once-grant is crossed once under a lock, a broken ledger is named, and `doors authorize` refuses an agent's shell.
- The door rule on every host and role (ROADMAP §4.12, M9.31): every dispatched role gets the core principles (`aloop mission principles hook` at SubagentStart, Codex agents' instructions, OpenCode's system prompt, each dsh role persona), and the door gate runs on Codex, OpenCode and dsh with each host's question tool mapped. In the OpenCode and dsh plugins it runs last, after every other gate admitted the call, and fails closed after 15 s. A bound worker's command is judged where it runs (its `workdir`), an MCP tool by its own arguments (`mcp__<server>__<tool>` on OpenCode too), and a subagent stopped at a door reports `ASK[door]` to its supervisor. Hardened by seven reviews (101 findings, each a test that failed first): a stalled decision can no longer fall through to an allow, `apply_patch` is read with any header spacing (a U+FEFF too) and through the shell, a bare redirect or a group's redirect reaches the commands inside, a `cd` that may not run leaves the directory unknown, a symlink then `..` is read as the kernel walks it, replacing a settings file with `cp`, `mv`, `ln`, `install`, `rsync` (into a settings directory too), `rm`, `sed -i` or `perl -i`, or removing a directory that holds one, is the settings door, and an MCP tool of a hyphenated server or an aggregator (`mcp__zapier__gmail_send_email`) crosses the same door as the service's own. A `cd` that may not run leaves each later command in either directory, a copy is placed where it runs (so backing up `.claude` passes), and Composio, kebab-case, scheduled and verb-first sends, GitLab and Bitbucket merges and Drive access grants are doors too. After `cd A || cd B` a line may be in A, B or where it started, a glob or contents copy names the files it copies, a name the gate cannot see is no settings change at the project root, a wrapped `cd` (`nohup cd`) or a background list (`cd X && npm run dev &`) moves no shell, and a copy names what the line staged before it. The shell's own `$PWD` stays where the shell expanded it (in a nested shell's text, under `env -C` or `find -execdir`), and a source directory the gate cannot list, a copy that becomes `"$VAR/.claude"` and a wholly substituted last name in a guarded directory are read as any name, and a word that only contains a directory (`PREFIX=$PWD/out`) no longer stops the gate. Tests: `mission-doors.test.py`, `opencode-plugin.test.mjs`, `dsh-plugin.test.mjs`, `codex-hooks.test.py`.
- DeepSeek Harness (dsh) host (ROADMAP §4.9): `aloop install dsh` (alias `deepseek`; `--project P` or `--global`) renders a `dsh --profile aloop` profile in `$DSH_HOME` and installs the skills. Its native plugin (`plugin/hosts/dsh/aloop.mjs`) runs the same gates as Claude Code on dsh's own events: a deny before a tool runs, a block after an invalid artifact write, and a stop gate that steers a role back up to 3 times before the supervisor is told it is BLOCKED (or STUCK, released unverified). Roles are agent presets on two subagent tools, one per model route (`deepseek-v4-pro` for planning, `deepseek-flash` for work by default; `MODEL_PLANNING_DSH` / `MODEL_WORKER_DSH` override). A writer's `aloop bind` moves its working directory into its worktree, and `host-adapter` confines its commands, edits and `working_directory` there. Usage is recorded Claude-shaped per role, so budgets, the ledger and `run-summary` work unchanged. `make e2e-live HOST=dsh` drives it live and exits 2 with the fix when no `DEEPSEEK_API_KEY` is set. Reason: DeepSeek's own harness is the cheapest route to a strong model. The profile records the project it serves (or `--global`), so another project's `aloop-init` never re-renders it, and gates get the worker shell's environment (no API key). The mission layer's six roles run on dsh too: presets on their route group's subagent tool, `gate-mission-scope` on every write and command, and `gate-mission-artifact` at each role's start and stop; the mission skill installs as `aloop-mission`. A second review (3 lenses; 11 confirmed findings, each fixed with a test) closed: a working directory that vanished (dsh's tools move it back to the main tree, so the plugin judges `ensure()`'s directory and `host-adapter` refuses to run from a missing one), gate switches from the project's `.env` (dsh loads it; `SKIP_HOOKS`, `GATE_RUN_TESTS`, `REPO_DIR`, `AGENTIC_*` from it never reach a gate), an interrupted Stop that dropped the sprint-close notice, and the shared profile: its owner is the main tree, an older unrecorded profile is claimed only by the project that installed it, and a project it does not serve is told so by `install status`, `aloop-init` and the session itself. Tests: `dsh-plugin.test.mjs` (46 checks, emulated), `dsh-runtime.test.mjs` (50 checks on the real pinned dsh 0.2.1-rc.2 with a scripted model; the CI hosts job installs it) and `dsh-install.test.py` (10).
- dsh follow-ups (ROADMAP §4.9). One `aloop install dsh` serves every project: the plugin routes each role's requests through dsh's `agent/request` to its own project's model (`aloop models resolve <role>`), so gate-dispatch no longer refuses another project's roles until a re-install, and a role whose model cannot be resolved is refused (fail closed). Roles are no longer offered `send_message`, so dsh adds no "send your result" guidance and no call is spent on a deny. dsh compaction is recorded as `context.compacted` and the handoff comes back after it; the capability matrix marks dsh compaction and live messaging as advertised, proven on the real runtime. Two mission roles (one per subagent tool) run through both of their gates on the real runtime. `run-summary --host dsh` reports a dollar cost: each logged call is priced from a dated, sourced DeepSeek table (`plugin/bin/_prices.py`; cache hit, cache miss, output, peak or off-peak), with `cost_by_model` in `summary.json`; an unpriced model id keeps its tokens and leaves the cost unmeasured, never guessed.
- Brand kit and repo front page: `docs/brand/` holds the mark (an **a** whose bowl is a red-to-green loop passing a gold gate, with a 16 px cut), lockups, light and dark README banners, favicons, app icons, the site's OG card and the GitHub social preview, all generated by `docs/brand/gen_brand.py` from one palette with WCAG checks (rules in `docs/brand/README.md`). The README leads with the mission layer, the agent principles and the door rule, the supported hosts and the measured demo runs; the site carries the favicon and Open Graph/Twitter card metadata; `SECURITY.md` and bug/feature issue forms are new.
Expand Down
10 changes: 6 additions & 4 deletions docs/OVERVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,10 @@ The model can ignore a skill; it cannot ignore an exit code.
`events trace`; each of these runs, and every public figure, is a row with its reproduce command
in [docs/evidence/README.md](evidence/README.md). Every run so far has found a gate false positive
or a dead path that unit tests missed; ROADMAP M0.8 (runs 1–35) and M8 (from run 36) list them.
- The door rule (ROADMAP §4.12, M9.30): every agent gets the core principles at session start, and a
PreToolUse gate stops a one-way door (a push to the default branch, a release, a deploy, a secret, …)
until the human records an authorization; two-way doors pass without a question.
- The door rule (ROADMAP §4.12, M9.30, M9.31): every agent on every host (Claude Code, Codex, OpenCode,
dsh) and every dispatched role gets the core principles, and a PreToolUse gate stops a one-way door (a
push to the default branch, a release, a deploy, a secret, …) until the human records an
authorization; two-way doors pass without a question.
- One measured real run (*TheNoteBook*, a Go backend): ~17K LOC, 322 tests, 74 commits in 6
sprints for ~1.55M output tokens, 76% of it on Sonnet. See the
[README](../README.md#evidence).
Expand All @@ -148,7 +149,8 @@ The model can ignore a skill; it cannot ignore an exit code.
the worker worktree's `.aladdin/` too, and are read from `.agentic/` where a host or an
older sprint still writes them there.
- The door catalogue is a finite list: an unlisted action is two-way, and a script or alias can hide a
listed one. Codex and OpenCode get the door hook with M9.31.
listed one, and a command whose text is computed (`$VAR`, `eval "$X"`) is read only as far as it is
written (M9.47). On Claude Code a call another gate denies can still use up a once-authorization (M9.56).
- Measured runs: TheNoteBook plus the `make e2e-live` runs in ROADMAP §0; no live-session test in CI.

## The docs, and which ones are current
Expand Down
Loading
Loading
0