You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
T27 OS orchestra: lanes as panes -- spawn with a title, a brief file and a permission mode; wait, status and a read tail for T27OS_CTL #11316
Owner directive 2026-10-20 (translated): "Switch to working through T27 OS, it is cheaper. Update the skills to work this way, improving the tools for remote control of the orchestra. And I must see in the app how the work goes."
Today the orchestrating session runs lanes as paid subagents. The cheaper path is a lane as an agent PANE of T27 OS: Claude Code in the pane runs on the key master's free tier (glm-4.5-flash, then OpenRouter :free, #11284, #11285), the owner watches every lane live in the window, and the orchestrator drives it with T27OS_CTL. Measured on the bench Mac: T27OS_CTL=list answers (pane 0 agent 0 state 2 flags 3 key 2); a restored pane answered a prompt in 7 s on glm-4.5-flash (#11284 acceptance).
What T27OS_CTL lacks for that (pty_server.t27 header: list, spawn, kill, read, write):
A pane spawned for a lane starts claude --session-id ... --model ... with no permission mode, so the agent stops at the first tool prompt and the lane never runs unattended.
No way to hand a pane a long brief: write types keys; a multi-line brief needs the text from a file.
No "is it done": the orchestrator can only poll read. The server already receives the agent's hook payloads (T27OS_HOOK=1), so it can know when the agent stopped.
No lane name: the owner cannot tell which pane is which lane.
Only .t27 (skill only-t27): every rule a tested fn in pty_logic.t27 / session_store.t27; pty_server.t27 only calls them.
Requirements
FR-001 (spawn of a lane): T27OS_CTL=spawn MUST accept T27OS_TITLE=<lane> (shown as the pane's title in the window and in list), T27OS_PROMPT_FILE=<path> (the brief given to the agent as its first prompt) and T27OS_PERM=<mode>, which puts --permission-mode <mode> on the claude command line. Only the modes the owner allows are accepted (see the owner's decision below); anything else is refused with a named error, never passed through.
FR-002 (wait): T27OS_CTL=wait T27OS_PANE=<n> [T27OS_TIMEOUT=<s>] MUST return when the pane's agent reports Stop through its hook (or the pane exits), printing idle, exited <code> or timeout.
FR-004 (read): T27OS_TAIL=<n> lines, so a report can be read without the whole scrollback.
FR-005: tests for every new decision (the command line with and without each flag, the refused modes, the wait outcomes from hook events, the status line).
The owner's decision this needs
Which permission mode a lane pane may get. Proposed default: acceptEdits (edits in the lane's own worktree without a prompt; every shell command still asks), plus nothing broader until the owner says so. bypassPermissions only on the owner's explicit word.
Boundary
specs/t27os/pty_server.t27
specs/t27os/pty_logic.t27
specs/t27os/session_store.t27
Success Criteria
t27c test-report on pty_logic.t27 and session_store.t27 prints FAIL 0 and vacuous passes 0; t27b test <spec> --check exit 0 for every changed spec
on the bench Mac: a lane pane spawned with a title and a brief file runs, wait returns idle, read with a tail shows its report, and the window shows the title
Owner's decision, 2026-10-20 (translated): option A. A lane pane gets --permission-mode acceptEdits plus an allowlist of t27c, t27b and git commands; everything else still asks. bypassPermissions stays refused.
How this lands in #11316: T27OS_PERM accepts exactly acceptEdits (any other value is refused with a named error), and the pane's command line also carries --allowedTools with the allowlist: the t27c and t27b binaries, and the git verbs a lane needs (status, diff, log, add, commit, fetch, merge, rev-parse, ls-remote, and push origin HEAD:refs/heads/lane/*, never a force push). Until #11316 lands, the same rule is written into the lane worktree's own .claude/settings.local.json (git-ignored), so a pane started there runs unattended under the same limits.
Measured while running the first two lanes as T27 OS panes (#11378, #11380), for the verbs of this issue:
write sends the text and its carriage return as one burst. Claude Code in the pane treats that as a paste, strips the carriage return ("Removed 1 invisible character - review and press Enter to send") and waits, so the brief is typed but never sent. A separate bare carriage return a second later (T27OS_KEYS empty) sends it. FR for this issue: write (or the brief of spawn with T27OS_PROMPT_FILE) MUST send the text, wait, then send the Enter as its own keystroke, and only after the agent's prompt is ready (a write about 10 s after spawn arrived before Claude Code was ready).
list showed state 3 for both panes while their agents were working; the agent's progress was readable only from its Claude Code session transcript (~/.claude/projects/<the pane's cwd, with "/" and "." as "-">/<session>.jsonl). FR for status: report busy or idle from the agent's hook events, and the last tool the agent ran.
Measured, the first two real lanes run as T27 OS panes (#11378, #11380), on the running build (claude panes on glm-4.7-flash):
Both agents ignored the brief's paths and command lines. One read the specs of a different, stale checkout by absolute path, then the orchestrator's memory and session transcripts under the user's Claude directory; the other ran cd <that checkout> && t27c test-report ... with a bare t27c that is not on the allowlist. Under permission mode A every such read or command outside the lane worktree stops the pane at a permission prompt, so neither lane progressed (one pane twice). Both lanes were escalated to Sonnet subagents, as specs/claw/model_route.t27 prescribes after two failed pane attempts.
For this issue: a lane pane's permission file SHOULD also deny reads and edits outside the lane worktree and the stale-checkout cd, so the agent gets an error and goes on instead of waiting at a prompt; status SHOULD report a pane that is waiting at a permission prompt (it showed state 3, as if idle).
Activity
model routing: a free T27 OS pane tier below Sonnet for sub-tasks, an…
docs and skills: state the pane-first model routing of #11317 where t…
gHashTag commentedon Oct 20, 2026
Owner's decision, 2026-10-20 (translated): option A. A lane pane gets
--permission-mode acceptEditsplus an allowlist oft27c,t27bandgitcommands; everything else still asks.bypassPermissionsstays refused.How this lands in #11316:
T27OS_PERMaccepts exactlyacceptEdits(any other value is refused with a named error), and the pane's command line also carries--allowedToolswith the allowlist: thet27candt27bbinaries, and the git verbs a lane needs (status, diff, log, add, commit, fetch, merge, rev-parse, ls-remote, andpush origin HEAD:refs/heads/lane/*, never a force push). Until #11316 lands, the same rule is written into the lane worktree's own.claude/settings.local.json(git-ignored), so a pane started there runs unattended under the same limits.🤖 Generated with Claude Code
t27os-orchestra skill: the owner's permission mode A and the measured…
gHashTag commentedon Oct 20, 2026
Measured while running the first two lanes as T27 OS panes (#11378, #11380), for the verbs of this issue:
writesends the text and its carriage return as one burst. Claude Code in the pane treats that as a paste, strips the carriage return ("Removed 1 invisible character - review and press Enter to send") and waits, so the brief is typed but never sent. A separate bare carriage return a second later (T27OS_KEYSempty) sends it. FR for this issue:write(or the brief ofspawnwithT27OS_PROMPT_FILE) MUST send the text, wait, then send the Enter as its own keystroke, and only after the agent's prompt is ready (a write about 10 s after spawn arrived before Claude Code was ready).listshowed state 3 for both panes while their agents were working; the agent's progress was readable only from its Claude Code session transcript (~/.claude/projects/<the pane's cwd, with "/" and "." as "-">/<session>.jsonl). FR forstatus: report busy or idle from the agent's hook events, and the last tool the agent ran.🤖 Generated with Claude Code
gHashTag commentedon Oct 20, 2026
Measured, the first two real lanes run as T27 OS panes (#11378, #11380), on the running build (claude panes on glm-4.7-flash):
cd <that checkout> && t27c test-report ...with a baret27cthat is not on the allowlist. Under permission mode A every such read or command outside the lane worktree stops the pane at a permission prompt, so neither lane progressed (one pane twice). Both lanes were escalated to Sonnet subagents, asspecs/claw/model_route.t27prescribes after two failed pane attempts.cd, so the agent gets an error and goes on instead of waiting at a prompt;statusSHOULD report a pane that is waiting at a permission prompt (it showed state 3, as if idle).🤖 Generated with Claude Code