RUL-037 — Every agent writes Python only as files through the guarded tools; enforced on every channel, never promised
Ruling
Mohamed, 2026-10-17, after the orchestrator explained that the Bash write hook's
/tmp/claude- scratch escape was why its probes lived in /tmp, verbatim:
- "THE HOOK IS DESIGNED SO YOU WOULD USE THE TOOLS TO WRITE PYTHON FILES AND GO THROUGH THE CHEKS NOT WORK AROUND THEM USING THE BASH"
- "THE SAME RULES MUST APPLY TO THE AGENTS AND SUBAGENTS AND MUST ALSO BE A RULE THAT IS PERMANENT SO YOUR PROMISE IS MEANINGLES"
What this rules:
-
Python is written only as
.pyfiles, through editor tools that run the formatter and the convention guards (Claude Code's Write/Edit; for any other agent, the equivalent guarded write path). -
No route around those guards. Specifically:
- no inline Python programs in a shell command (
python -c "...",python - <<EOF, a heredoc or pipe into an interpreter); - no inline programs in a dispatch brief's
checksblock. A check that needs logic calls a committed checker file; - no shell file writes (
>redirect,tee,sed -i,write_text(),open(..., 'w')).
- no inline Python programs in a shell command (
-
The
/tmp/claude-scratch escape inintercept_bash.pyis for throwaway command output only, never for code. -
Every channel is bound:
- the main Claude session;
- every Claude subagent;
- every dispatched worker (opencode with DeepSeek, Qwen or Kimi);
- Hermes;
- any future executor.
A channel that cannot be mechanically guarded is not used for code work.
-
Permanent and mechanical. A promise in chat, in memory or in a brief's prose is not enforcement.
-
Strict, with one named exemption (Mohamed, 2026-10-17, verbatim: "AGREED", to the orchestrator's recommendation).
- Every inline interpreter program is refused. There is no "trivial one-liner" exception.
- The single exemption is pi-task check lines (
VERIFYlines in a brief or a hidden-checks file). They run only inside the judge's throwaway container, never on this machine or through its shell, and the judge receives only that one file. - The exemption is scoped to the channel, not to a judgment about size.
Evidence
Measured 2026-10-17 on session 18108376 (the components refactor orchestrator):
- Inline programs: 2,455 Bash calls, of which 587 were inline
python -cprograms, i.e. Python that never met black, ruff or the convention guards.- 8 of them contained
write_textoropen(..., 'w'). - The write hook refused Bash file writes 21 times, and several retries were
re-aimed at
/tmp/claude-.
- 8 of them contained
- Brief checks: the six wiki and codetools briefs in
wt-COMPONENTS/packages/design-scripts/{wiki-publish,wiki-verify}/carry 37 inlinepython -I -cprograms.- One of them, WIKI-2's diagram check, started six headless Chromium renderers at a time; it ran 66 renders in all.
- Together with an orphaned 195-process Chromium from an inline mmdc probe, they filled 21 of 24 GB of swap.
- Workers have no guard at all.
- The dispatched workers' opencode config
(
dispatch/templates/opencode-headless.json) has one permission key,external_directory: no bash rules and no plugin. - The repo's OpenCode plugin (
opencode/plugins/orchestrata-ultimate.ts) is not loaded by dispatched workers. Its 1,187checkpoint: before ...commits (git add -A && git commit --no-verifybefore every edit) end on 2026-08-24, with 0 since 2026-10-01. - That plugin bridges none of the Claude hooks.
- The dispatched workers' opencode config
(
- The Claude hook's gap.
intercept_bash.py(orchestrata plugin 1.5.0, PreToolUse:Bash) refuses Bash file writes, but it does not refuse inline programs.
Enforcement
- none (judgment)
Judgment-only at recording time (2026-10-17 18:24 EDT). This is a recorded gap, not the intended end state. Each mechanism below replaces this line with its gate entry when it lands, and the ruling is complete only when all four are in force.
Mechanism (owed, RUL-014)
- Claude main session and subagents. Extend the source of
plugins/orchestrata/hooks/intercept_bash.pyon master (the installed plugin 1.5.0 is a copy of it) to refuse inline interpreter programs. Hooks are user-level (RUL-035), so subagents are covered, but this is verified by observation: a subagent'spython -ccall must be refused. - Dispatched opencode workers: one rule set, two adapters. An opencode
tool.execute.beforeplugin, loaded by the headless config, passes each bash, edit and write call to the orchestrata plugin's own PreToolUse hooks through the same JSON contract Claude Code uses.- Workers then obey exactly the hooks the main session obeys: the same rules, one implementation.
- Add an opencode formatter entry for
.py, so worker writes are formatted. - The plugin never commits and never uses
--no-verify.
- Brief authoring.
dispatch.py runrefuses a brief whosechecksblock contains an inline program. Checks are one-line calls to committed checker files. - Other executors (Hermes, Kimi, pi-task). Each one gets the equivalent guard, verified in its real channel, or is barred from code work.
Build routing (Mohamed, 2026-10-17, verbatim: "AGREE WITH THIS"). Until M2 is in
force, every brief that changes Python goes to the sonnet executor, which runs under
the Claude hooks. DeepSeek and Qwen get no Python work.
- Build order: M3 → M2 → M1 → M4. M1 goes through dev → test → promote, because it changes the deployed plugin.
- M3 enforces this routing too. Until M2 lands,
dispatch.py runrefuses a non-sonnet executor for any brief whosefilesfence lists a.pyfile.
Verification
For each mechanism, run it in the real channel and observe:
- an inline Python program is refused;
- a call to a committed
.pyfile is allowed; - a shell write to a source path is refused.
scripts/rulings_lint.py checks each declared gate entry once it replaces the
judgment line.