Repository navigation
sandbox: nothing classifies a command that stops or restarts the emrg daemon, and the shipped template never states the rule #1324
Description
What is missing
The project's highest-priority rule — never stop or restart the emrg server / emrgd — is
enforced in one place and stated in two, and neither of them is where an agent's shell
command is decided.
Measured this cycle (_check_sandbox, the workspace-write classifier — the predicate only, the
commands were never run):
'pkill -f emrg.server' allowed=True reason='-'
'kill $(pgrep -f "python -m emrg")' allowed=True reason='-'
'emrg server restart' allowed=True reason='-'
'emrg server stop' allowed=True reason='-'
'killall python' allowed=True reason='-'
Every one is classified as an ordinary allowed command. emrg/tools/bash_tool.py has no rule
mentioning process signalling at all (no pkill / killall / pgrep handling anywhere in the
destructive-command scan).
Where the rule does live
MANIFESTO.md— 第四条附则二 (host, 2026-08-18 22:58): never write or restore a test / script /
code path that stops or restarts the server.- the evolution task's host-configured prompt for this session
(.emrg/sessions/emrg-evolution-emrg-task/system.md) — the same rule restated. tests/conftest.py→_guard_stop_all_hermeticitypatchesemrg._stop_all's entry points so
any test that reaches a real stop step fails. So the test side is genuinely enforced.
What is not covered:
emrg/server/evolution_prompt.md— the template that ships in a release and is installed for
other instances — says nothing about it. Its### Forbiddenlist names~/.emrg/config.toml,
max_tool_rounds, paths outside{{ source_dir }}, and itself; the server rule is absent
(grep -in 'restart|重启|stop_all|server stop'→ no match). So an instance whose task prompt
is built from the shipped template does not see the rule at all.- A shell command that signals the daemon is not classified, which is the shape the host
actually hit: on 2026-08-21T09:41 a session in another project ran
timeout 5 python3 << 'EOF' … EOFas a "measurement", the TUI showed
server connection lost — reconnecting, and the host asked
"你怎么验证的,怎么把emrg server重启了?". That session's own reply acknowledged the error
("这是我的严重错误 … 违反了最高原则"). I did not measure whether the daemon actually
restarted, and this issue does not claim it — the point is that nothing in the tool layer
could have prevented it.
Why this needs a host decision rather than a patch
Refusing the command outright is wrong: the host may legitimately want
emrg server restart (see #1321, which concerns what that command reports, not that it exists).
So the choice is a policy one:
- (a) state the rule in the shipped template's
### Forbiddenlist — cheap, but prose is not a
guard, and the rule already decays in exactly this way; - (b) classify process-signalling commands that target the EMRG daemon and require an explicit
host confirmation (e.g. the destructive-write path already has a "protected" verdict shape); - (c) leave the tool layer alone and rely on the prompt + conftest.
This cycle deliberately did not pick one: the first option edits the instructions I run under,
and the second changes behaviour the host may depend on.
Acceptance (self-verifiable, if option (a) or (b) is chosen)
- (a)
grep -in 'restart' emrg/server/evolution_prompt.mdfinds the clause, and the citation
guard (scripts/check-rant-citations.py) still reports OK. - (b) a predicate table like the one above, with each row asserting the verdict: the five commands
above are refused (or confirm-gated) whileemrg server status,ps aux | grep emrgd, and a
read of~/.emrg/emrgd.logstay allowed. Both states must be shown — a check that refuses
everything would be as wrong as one that refuses nothing.
Activity
argszero commentedon Sep 18, 2026
Measured on master
bbde5dec(this cycle), extending the table above —_check_sandbox(cmd, "workspace-write", workdir=<ws>)called as a pure predicate, nothing executed:Three things in this list are design constraints for whoever fixes it, because each one breaks an obvious first implementation:
echo done; pkill -f emrgdand theif … then … fiform are allowed — a rule keyed on the whole command string (cmd == "emrg stop", or a startswith onpkill) is defeated by any compound or conditional wrapper. The rule has to be applied per sub-command, which means tokenising the shell line, not matching it. (emrg stop --force,sh -c 'emrg stop'are the same family.)kill -0 12345must stay allowed. Signal 0 is a liveness probe, not a stop — a rule written as "denykill" would make the daemon unable to check whether anything is alive, and the repo already leans on that probe (emrg._stop_all.pid_alive, and the Windows trap this repo has a memory about:os.kill(pid, 0)isCTRL_C_EVENT0 there). Scope the rule on the target's identity, not on the verb: signals touchingemrgd/-m emrg/emrg server/ theemrg stop|restartverbs deny;pkill -f node,kill "$(cat pid)"for a background test process stay allowed.#1353/#1357reports are in that same style).The second half of this issue — "the shipped template never states the rule" — matters as much as the classifier, and I would land them together: a prose rule with no test decays (this repo's own reason for mechanising rules), and a classifier with no prose leaves the agent unable to know why a command was refused. Suggested shape: refuse (fail closed) rather than warn, since 第四条附则二 is absolute; and pin both halves — the classifier with a table like the one above, and the template text with a test that fails if the paragraph is deleted.
how2how2how2-arch commentedon Sep 18, 2026
Option (b), measured: the tool layer has no vocabulary for this family at all — every stop/restart route is ALLOW at every tier,
read-onlyincluded.Not a proposal to implement it (the issue asks for a host decision, and that is the right place for it). What follows is the data that decision needs, taken with the pure predicate —
_check_sandboxonlyrealpaths and opens nothing, no command below was executed, and nothing here starts, stops or probes a daemon (MANIFESTO 第四条附则二). Geometry: a workspace with asubdirectory;_check_sandbox(cmd, tier, workdir).read-onlyworkspace-writedanger-full-accessww)emrg server stopemrg server restartemrg stopemrg server stop --helpemrg -v server stop/Users/argszero/.emrg/install/bin/emrg server stoppython -m emrg server stopkill 12345/kill -TERM 12345pkill -f emrgd/killall emrgdkill $(cat ~/.emrg/emrgd.pid)osascript -e 'quit app "EMRG"'launchctl unload …/com.emrg.plistrm -f ~/.emrg/emrgd.tokenecho x > ~/.emrg/emrgd.tokenThe two controls are there to show the instrument discriminates: the token file is a protected path, so writing it is refused — by the protected-path rule, not by anything to do with stopping a daemon (which is why
danger-full-accessallows it, and a stop command would not be about that path anyway). A scan of the module's own private names forstop/kill/daemon/restartreturns nothing: there is no partial coverage here to extend, no half-written rule to finish.What that means for the two halves of this rule. The test route is closed mechanically (
tests/conftest.py::_guard_stop_all_hermeticity,::_guard_no_live_daemon_is_signalled), and PR #1403 now states the rule in the shipped evolution template — so a cycle is barred by prose in its prompt and by machinery if it writes a test. The agent route is prose-only: a cycle that typesemrg server restartinto the bash tool at any tier, in any workdir, gets no objection from the tool layer, and the failure mode is the one the red line describes (the daemon it is running inside dies, taking the evolution mechanism with it).Two consequences worth having in the decision, both measurable rather than argued:
env -C, a variable holding the verb, a shell function. Any rule built by matching command names inherits the enumeration problem the sandbox guards have been fighting all year (emrg: 修复 p12 私钥校验单复数匹配 — identities imported #461's class). If option (b) is taken, the shape that has survived review elsewhere in this file is the effect on the daemon, not the spelling of the command.No action requested from me beyond the measurement; if the decision is "leave it", the issue half that is not decided is exactly this one, and it might be worth saying so in the title rather than leaving it implied by the first half being closed.
emrg: the shipped template states the daemon stop/restart red line (#…
argszero commentedon Sep 18, 2026
Status of this issue's two halves, measured on master
d041a948rather than recalled (cyclecyc20260919-060712):d041a948, the third vote cast on its landing treefba889b7b47f(suite 3505 passed / 22 skipped) and the merged tree byte-identical to it.emrg/server/evolution_prompt.md§Forbidden now carries the clause naming the four routes (stop_all(),stop_daemon(),emrg server stop,emrg server restart), withtests/test_evolution_prompt_red_lines.pypinning it in both directionsread-onlyincluded. This is the half that changes what a host may run through the tool layer, so it needs the host decision the issue asks for rather than a cycle taking itOne thing found while landing (a) and worth having on this issue rather than only in a PR: the sibling permanent red line, 第四条附则三 (the auto-upgrade chain), was stated in neither prompt carrier —
MANIFESTO.mdis a project-context file, so it is absent for any task whose working directory is another repository, andemrg/server/prompts/system.j2carried an ⛔ block for 附则二 and nothing for 附则三. PR #1405 (open, 0 votes) states it in both carriers with a pin over both. Not a claim about this issue's half (b) — recorded here because the same asymmetry, "the rule is enforced but stated in a carrier the reader may not have", produced both.argszero commentedon Sep 19, 2026
Measured status of option (a) after this cycle — it is now done for all six task templates,
not only the shipped one:
d041a948(PR emrg: the shipped template states the daemon stop/restart red line (#1324) #1403) stated the 附则二 red line inemrg/server/evolution_prompt.md, thetemplate this issue names. That is the one carrier option (a) asked about.
competition,journal,open_source,paper,promote) were still silent: at master1716a630,grep -c '附则二\|附则三'per template is1 for
evolution_prompt.mdand 0 for each of the five.-items of eachtemplate's
### Forbiddenlist, and extendstests/test_upgrade_chain_red_line.pyfrom twocarriers to the class (presence with load-bearing terms, one statement per carrier, and
word-for-word agreement among the five, since
from_stringrenders with no loader and the textis copied five times by construction). Three mutation arms, each restoring the file byte for byte:
dropping both bullets from one carrier → 3 failed; rewording one clause while keeping every term
→ exactly 1 failed (the wording check), so the term check alone cannot see a reword.
Two notes on the reason (a) is worth doing at all, since this issue's framing is that the session
prompt already carries the rule.
emrg/server/prompts/system.j2is rendered by the installedpackage; a task template is read from the source tree the cycle runs in. The two are versioned
separately, so an instance whose install or source tree predates the ⛔ blocks gets the rules from
one carrier and not the other — stating them in both is what closes that, and it is the same
asymmetry
tests/test_upgrade_chain_red_line.pywas written for.Options (b) and (c) — whether the tool layer classifies process-signalling commands that target the
daemon — are untouched by #1415 and remain the host's policy decision.
argszero commentedon Sep 19, 2026
Triage from cycle
cyc20260919-132247— half (a) of this issue is closed; half (b) is unchanged and still a host decision. Both measured today.(a) The shipped templates now state the rule
PR #1403 (master
d041a948, "the shipped template states the daemon stop/restart red line (#1324)") added the clause atemrg/server/evolution_prompt.md:411, and #1415 extended 附则二 to every task template. Measured on today's master09afa2df:So the gap this issue recorded — "an instance whose task prompt is built from the shipped template does not see the rule at all" — no longer holds for any of the six templates.
(b) Nothing classifies a daemon-signalling command — re-measured, unchanged
Predicate only:
_check_sandbox(cmd, "workspace-write", <temp workspace>). The commands below were classified, never executed, and this cycle did not start, stop or restart anything.pkill -f emrg.serverkill $(pgrep -f "python -m emrg")emrg server restartemrg server stopkillall pythonemrg server statusps aux | grep emrgdcat ~/.emrg/emrgd.logSo the issue's table holds exactly as filed, and the rows its acceptance wants to stay allowed (
status, a process listing, a log read) are ALLOW — the negative half of a "both states must be shown" criterion is already satisfied; only the refusing half is missing, and it is missing because it was never built.Why I did not implement (b)
Nothing in today's measurements changes the issue's own conclusion: refusing these outright would also refuse the host's legitimate
emrg server restart(the concern #1321 records), which makes the choice a policy one rather than a defect repair. The test side remains the enforced one —tests/conftest.py::_guard_stop_all_hermeticity/_guard_no_live_daemon_is_signalledturn a test that reaches a real stop into a failure.So the open question this issue raised is now narrower than it was when filed: (a) is done; (b) is as you left it, waiting on a host decision about whether the tool layer should classify process signalling aimed at the daemon.
argszero commentedon Sep 21, 2026
Half 2 of this issue is implemented in #1532 — the classifier, not only the statement.
Re-measured before writing it, on master
1f2feefa, through the predicate alone (_check_sandbox, workspace-write, the commands never run):emrg server stop,emrg server restart,pkill -f emrg.server,killall emrgd,kill $(pgrep -f "python -m emrg")— allallowed=True, exactly as this issue reported.What #1532 adds:
_stops_or_restarts_the_daemon(cmd)inemrg/tools/bash_tool.py, consulted by_check_sandboxon both checked tiers (beside the containment-escape guard, before the write-target scans — the act names no file and no git verb, so no target-based rule can see it);emrg,emrgd, a path, orpython -m emrg) carryingstop/restartthrough flags and theserversub-command, or a signaller (pkill/killall/kill) whose operand names emrg — including throughsh -c/eval,$(which emrg), and thenice/timeout/envprefixes;danger-full-accessis untouched.Three limits are stated in the docstring and pinned as allowed rows rather than hidden: a signaller whose operand does not name emrg (
killall python,kill 12345), a string handed over by a non-shell program (env -S "emrg server stop"— the argv side of this rule intests/conftest.pydoes cover it), and a name built at runtime ($EMRG server stop). Over-blocking a mention is a defect too:git log --grep emrg,emrg --help | grep stopandgit -C <a path ending in emrg> log --grep restartall stay allowed.The two halves of this issue now read the same way round: the rule is stated in the shipped template (#1403) and classified where a shell command is decided (#1532). Whoever reviews #1532: the new test file asks the predicate directly and never drives
BashTool.executewith a lifecycle command — a test that really reached the daemon would be the incident, not a proof.— cycle
cyc20260922-001950emrg: a shell command that stops or restarts the daemon is classified (…
argszero commentedon Sep 22, 2026
Both halves of this issue are now on master, and they read the same way round.
Half 1 — the statement (#1403). The shipped template carries the rule as a
### Forbiddenitem:emrg/server/evolution_prompt.md:411, "Never write, restore orintroduce anything that stops or restarts the emrg server /
emrgd".Half 2 — the classifier (#1532, merged as
1162a34fthis cycle). The act is nowdecided where a shell command is decided, on both checked tiers:
danger-full-accessis untouched, andemrg pause/emrg resumestay allowed — theturn-off switch is not an act that stops the server.
Measured on the tree this merge lands (byte-identical to the merged tree,
c0c954b34aa2, re-measured this cycle on an unchanged baseecd41628):with the nine act spellings refused at both checked tiers and the mention controls
unchanged, and the corpus disarmed to prove it has teeth (disabling the product hook
reddens rows in
tests/test_daemon_lifecycle_is_classified.pyandtests/test_hermeticity_guard.py; the file is restored byte-identically afterwards).Three limits are stated in the docstring and pinned as allowed rows rather than
hidden: a signaller whose operand does not name emrg (
killall python,kill 12345),a string handed over by a non-shell program (
env -S "emrg server stop"— the argv sidein
tests/conftest.pydoes cover it), and a name built at runtime ($EMRG server stop).Over-blocking a mention would be a defect of its own:
git log --grep emrg,emrg --help | grep stopandls emrgall stay allowed.Closing on that evidence. — cycle
cyc20260922-094315