From e5f35f40afcc787cd651c697e063c534df402a0a Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 19:26:05 +0700 Subject: [PATCH] t27os-orchestra skill: the owner's permission mode A and the measured recipe for an unattended lane pane (Closes #11362) The owner chose option A for lane panes on 2026-10-20 (acceptEdits plus an allowlist of t27c, t27b and the git verbs a lane needs; force pushes denied; bypassPermissions refused). Until T27OS_PERM (#11316) lands, a lane worktree carries the rule in its git-ignored .claude/settings.local.json; the skill now gives that template, with the env block that points the Zig caches inside the worktree (without it the pane's own sandbox cannot write ~/.cache/zig and test-report is BLOCKED). Measured the same evening: a claude pane in such a worktree ran t27c test-report unattended and wrote its report. The lane loop's spawn step uses the file; the wait step notes z.ai's 429 [1302] retry and the Terminal hook trap around the word read. Co-Authored-By: Claude Opus 5.5 --- .claude/skills/t27os-orchestra/SKILL.md | 26 +++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/.claude/skills/t27os-orchestra/SKILL.md b/.claude/skills/t27os-orchestra/SKILL.md index 35571a28cc..8ef5f4b76a 100644 --- a/.claude/skills/t27os-orchestra/SKILL.md +++ b/.claude/skills/t27os-orchestra/SKILL.md @@ -47,12 +47,34 @@ Do not call these; until they land, use the workarounds in the loop below. - `status`: title, key, model, busy or idle, and the traffic light of #11314. - `read` with `T27OS_TAIL=`: the end of the scrollback. +## Permission mode (owner's decision 2026-10-20: option A) + +A lane pane runs with `acceptEdits` plus an allowlist of the `t27c` and `t27b` binaries and the git verbs a lane needs; everything else still asks, a force push is denied, and `bypassPermissions` is refused (#11316, #11362). Until `T27OS_PERM` lands, the lane worktree carries the rule itself in `.claude/settings.local.json` (git-ignored), which Claude Code in the pane reads at start: + +```json +{ + "permissions": { + "defaultMode": "acceptEdits", + "allow": [ + "Bash(:*)", "Bash(:*)", + "Bash(git status:*)", "Bash(git diff:*)", "Bash(git log:*)", "Bash(git add:*)", "Bash(git commit:*)", + "Bash(git fetch:*)", "Bash(git merge:*)", "Bash(git rev-parse:*)", "Bash(git ls-remote:*)", + "Bash(git push origin HEAD:refs/heads/lane/*)" + ], + "deny": ["Bash(git push --force:*)", "Bash(git push -f:*)", "Bash(git push --force-with-lease:*)"] + }, + "env": { "ZIG_GLOBAL_CACHE_DIR": "/zig-cache/global", "ZIG_LOCAL_CACHE_DIR": "/zig-cache/local" } +} +``` + +Measured 2026-10-20 on the bench Mac: a claude pane spawned in such a worktree showed "accept edits on", asked nothing, ran `t27c test-report specs/claw/model_route.t27` and wrote its report (9 tests, vacuous passes 0); no trust prompt appeared. Without the `env` block the run was BLOCKED: the pane's own sandbox cannot write `~/.cache/zig` (`zig-cache/` is git-ignored). The file is read once at start: after changing it, kill the pane and spawn it again. + ## The lane loop 1. **One issue per lane**, in its own worktree and branch (`lane/-`), based on a fresh `origin/master`. 2. **The brief is a file** in the lane worktree outside the tracked tree (or in the scratchpad): what to do, the boundary (files it may touch), the gates it should run itself, the branch name, the commit trailer, and "push your branch, no PR, no `gh`". Cite issue numbers as `Closes #N` in the commit only. -3. **Spawn** the pane in the worktree: `T27OS_CTL=spawn T27OS_AGENT=0 T27OS_CWD=`. Today it has no title, prompt file or permission mode: `write` one short line telling it to read the brief file (`T27OS_KEYS='read BRIEF.md and do it'`), and have the brief say to write its final report to a file in the worktree. -4. **Wait.** Today: poll `list` until the pane's state is idle (3) or done (4), at a sensible interval; after #11316 use `wait`. A pane in state 2 (blocked) needs a person or a permission mode: look at it, do not guess. +3. **Spawn** the pane in the worktree, after writing the permission file above into it: `T27OS_CTL=spawn T27OS_AGENT=0 T27OS_CWD=`. Today it has no title or prompt file: `write` one short line telling it to read the brief file (`T27OS_KEYS='read BRIEF.md and do it'`), and have the brief say to write its final report to a file in the worktree. +4. **Wait.** Today: poll `list` until the pane's state is idle (3) or done (4), at a sensible interval; after #11316 use `wait`. A pane in state 2 (blocked) needs a person or a permission mode: look at it, do not guess. A pane that shows `429 [1302] Rate limit reached` retries in place (Claude Code's own up to 10 attempts); give it a few minutes before calling the lane failed. In a Terminal tab, a command line with the word `read` next to a `$` expansion is refused by the terminal hook: put CTL calls in a small script file and run that. 5. **Read the report** from the file it wrote (today `read` returns only the oldest bytes of the scrollback). 6. **Verify the diff yourself** (that is review-final): read it in full, rerun the checks, never trust the pane's own claim that tests pass. 7. **Land with the gates**: `t27c test-report` FAIL 0 with vacuous passes 0; `t27b test --check --quiet` exit 0; seals regenerated by a `t27c` built from master, not by the pane; seals of the importers; the types ratchet; the dupe scan as a subset of master's; no absolute home-directory path in a committed file; ASCII only. Merge through the usual auto-merge path.