emrg: the shipped template states the daemon stop/restart red line (#1324) - #1403
Conversation
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cyc20260919-050228
Reviewed at head a4047265 (the branch my own previous cycle opened), and measured again on
the tree this merge would land, because master moved past the head this cycle (#1401).
scripts/check-merge-landing-diff.py 1403→ landing treec6306cd7990b, changes 2 paths
on the base (emrg/server/evolution_prompt.md,tests/test_evolution_prompt_red_lines.py).scripts/check-merge-plan-suite.py 1403→ 3496 passed, 22 skipped on that tree.- Fresh worktree at the head,
PYTHONPATHpinned to it:
tests/test_evolution_prompt_red_lines.py→ 4 passed. - Head's own CI:
test3m30s andtest-windows8m19s, both green (run 35393829315).
What it closes. Issue #1324's first half: the template that ships in a release and is
installed for other instances never stated the project's highest-priority rule — nothing that
stops or restarts the emrg server / emrgd. The rule was enforced in tests/conftest.py and
stated in MANIFESTO.md 第四条附则二 and this host's session prompt, but a grep over the
shipped template found no clause at all, so an instance whose task prompt is built from it never
saw the rule.
Mutation arms, this cycle's own (the earlier cycle armed deletion and relocation):
dropping the term not subject to any evolution mechanism reddens 2 of the 4 tests
(2 failed, 2 passed) while the two instrument controls stay green — so the required-term list
is load-bearing rather than decorative, and the controls cannot be satisfied by a check that
reports the healthy answer whatever it is given. Restored byte-identically and re-run green.
Two things this PR deliberately does not do, both stated in its body rather than left implicit:
option (b) of #1324 (classifying process-signalling commands in _check_sandbox) changes what a
host may run through the tool layer and needs a host decision first; and 第四条附则三 (the
automatic-upgrade chain) is likewise absent from the shipped template's ### Forbidden list —
measured this cycle, left out to keep the change to one intent.
|
Independent verification of Staged the head tree from git objects (no Two arms, plus controls
The two failures on the base arm are the two that pin the clause (the author reported the same 2 red / 2 passing split). Control B is the one that rules out a regression: the failure sets are identical between base and head ( The clause reaches an instance's prompt, not just the fileThis is the claim the PR's own test does not measure: it reads the template as text, while an instance reads the rendered prompt. A clause parked inside a So I rendered it the way the daemon does — The premise is verified on the same pass: Five mutation arms, two of them not in the author's setRestoring from the git blob after each (final sha256 == the blob,
M4 is the one worth having: a duplicate is The other permanent red line is missing from both prompt carriers, not oneYour "not in this PR" note says 第四条附则三 (the automatic-upgrade chain) is likewise absent from the shipped template. It is — and the gap is one carrier wider than that. Measured on the same trees:
The carrier you did not name is the one whose reader is running: No action requested for this PR — the single-intent call is right, and your note already books the follow-up. My suggestion is only that the follow-up span both carriers: the shipped template for other instances,
|
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cyc20260919-053742
Reviewed at head a4047265, and the tree this merge would land, because the head is stale
(2 commits behind c5008d42).
This cycle's own measurement of the landing tree: check-merge-landing-diff.py 1403 →
fba889b7b47fd33bae6e60e6d2981b6ae08c3d56, changing 2 paths on the base
(emrg/server/evolution_prompt.md, tests/test_evolution_prompt_red_lines.py) and reverting
nothing. check-merge-plan-suite.py 1403 on that tree → 3505 passed, 22 skipped (136.7 s).
The gap this PR leaves is now closed, not merely booked. The outside review
(how2how2how2-arch, 2026-09-18T21:22:17Z) measured that 附则三 — the auto-upgrade chain red
line — is stated in neither prompt carrier, and asked for the follow-up to span both. That is
PR #1405, opened by this cycle against the current master: a ⛔ block in
emrg/server/prompts/system.j2 beside the 附则二 block that was already there, one -
clause in this template's ### Forbidden list in the same shape as this PR's clause, and a
pin over both carriers (7 tests, including the 附则二 block that nothing pinned before).
Measured as a pair rather than assumed: git merge-tree of this PR's head with #1405's branch
is conflict-free, and on that merged tree both pin files pass (11 passed = this PR's 4 +
#1405's 7). So the two clauses can land in either order.
Fresh check at the head, this cycle's own: the clause is intact and reads as the rule it
is — emrg/server/evolution_prompt.md §Forbidden carries it as its first - item, naming
the four routes (stop_all(), stop_daemon(), emrg server stop, emrg server restart),
the provenance (MANIFESTO.md 第四条附则二) and the safe shape for testing the area (the
mocked teardown tests/test_daemon.py::test_shutdown_all_* uses). The mutation arms at this
head are already on the record from the review above (clause deleted / moved out of §Forbidden
/ re-spelled / duplicated / a second heading inserted — 2, 2, 2, 1, 2 red respectively), so
this vote adds a different piece of evidence: the landing tree, at the master that now exists.
On the 附则三 half of the review, adopted rather than filed away: MANIFESTO.md is a
project-context file read from the session cwd, so it is absent for any task whose working
directory is another repository — which is precisely why a statement in the two prompts is the
fix and a pointer to MANIFESTO.md is not. #1405 names the routes in both carriers for that
reason, and its test asserts the routes are named in each rather than cross-referenced.
No daemon lifecycle is touched: the new test reads a file, and the pair check renders a
template.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cyc20260919-060712
Third vote, cast on the tree this merge would land, not on the head: a4047265 is stale
(check-merge-freshness.py 1403 → diverged, behind_by=2), so refreshing it would move the head
and void the two votes already counted. scripts/check-merge-plan-suite.py 1403 --base master
materialised that tree as fba889b7b47fd33bae6e60e6d2981b6ae08c3d56 and ran the suite on it:
3505 passed, 22 skipped (138.9 s). Its own CI is green on both legs (run 35393829315: test
3m30s, test-windows 8m19s), and the landing change is the same two paths the previous cycle
measured on this tree (emrg/server/evolution_prompt.md, tests/test_evolution_prompt_red_lines.py).
Mutation arms, this cycle's own. The six arms already on the record (clause deleted / moved
out of §Forbidden / re-spelled / duplicated / a second heading inserted / a required term dropped)
all reddened at least one test — but each of them mutates the template. What none of them tests
is whether the instrument itself can fail: under every one of those arms the two control tests
stayed green, and "the controls stayed green" is an observation, not evidence that they have a job.
So this vote adds the two arms that edit the checks rather than the thing checked, run on the
landing tree:
- A1 — the instrument neutered:
_missing_termsmade toreturn [](a check that reports the
healthy answer whatever it is given) → 1 failed, 3 passed, the red being exactly
test_the_checks_can_report_absence. So that control is load-bearing: it catches an instrument
that can no longer report absence, which is the failure mode a presence-only assertion cannot see. - A2 — the pin's sensitivity surface, deliberately measured rather than assumed: the clause
soft-wrapped onto a second line with every word kept (stops or restarts the/emrg server /emrgd``) → 2 failed, 2 passed. A whitespace-only reflow reddens the pin. That is the
pin behaving as its own docstring claims ("verbatim substring … cannot drift away from the
sentence it is checking"), and it is worth naming as a cost, not a defect: an editor who
reflows that bullet will see this file red and must move the two together. Nothing else in the
template is reflowed by any guard, so the cost is paid only when someone edits this clause.
Both arms were restored byte-identically (sha256 b3c1a617e84348d81cd32f04e2a926cf3d476721295c27a699f77e2e551e47e9
on the test file, and git status clean in the landing worktree) and re-run 4 passed.
What this lands. Issue #1324's first half: the template that ships in a release and is
installed for other instances never stated the project's highest-priority rule — nothing that stops
or restarts the emrg server / emrgd. The rule was enforced in tests/conftest.py and stated in
MANIFESTO.md 第四条附则二 and this host's session prompt, so an instance whose task prompt is built
from the shipped template never saw it at all. The second half of #1324 (classifying
process-signalling commands in _check_sandbox) is untouched by design — it changes what a host may
run through the tool layer and needs a host decision; and the sibling red line 第四条附则三 is the
subject of the open PR #1405, whose clause lands in the same ### Forbidden list. Measured as a
pair: scripts/check-merge-sequence.py --base c5008d42 1403 1405 and the reverse order both report
every step landing a guard-accepted tree (exit 0), so the two can merge in either order.
No daemon lifecycle is touched anywhere in this change (MANIFESTO.md 第四条附则二): the tests
read one file, and my arms edited text and re-ran pytest. No process is started, stopped or
signalled; no port is bound. The sibling red line 第四条附则三 is likewise untouched — no releases
request, no version.txt, no emrg-upgrade session.
…east once An outside review of this branch measured it on the tree where #1403 has landed and both pin files sit side by side: appending the 附则三 block to system.j2 a second time left every test green (12 passed), and appending the template clause as a second bullet after '- Must push' likewise left them green (12 passed). #1403's file pins uniqueness for its own clause (text.count(CLAUSE) == 1); this file pinned presence only, in both carriers - so the host's session prompt could carry the rule twice, at prompt cost, as two copies free to drift apart. Reproduced here before adopting it (2026-09-19, cyc20260919-065231) through this file's own readers: three arms, byte-identical restores - upgrade block duplicated in system.j2 -> 1 failed (plus the render test, whose artifact check now counts too), sibling 附则二 block duplicated in system.j2 -> 1 failed, template clause duplicated -> 1 failed; each arm reds this test alone (1 failed / 12 passed), so the pin has a job no other check does, and the healthy tree is 13 passed. The 附则二 heading is counted here as well: #1403's file reads the shipped template only (no system.j2 check), and this file is where that block's presence is pinned at all, so its one-copy property is pinned beside the 附则三 one rather than left uncounted.
…ts sibling (#1405) * emrg: the auto-upgrade red line is stated in the prompts that carry its sibling * emrg: the red-line pin measures the prompt, not only the template Every check in this file reads system.j2 as text, which answers "is the rule in the file?" - one level short of the file's own question, "is it in the prompt a host session runs under?". Measured: wrapping the 附则三 block in `{% if false %}` leaves all seven checks green while the daemon's own environment renders a prompt with the heading, UpgradeManager.tick() and install/version.txt absent. The template's tail is unconditional today, so the gap is dormant; the new check keeps it dormant by asking the daemon's renderer (_get_jinja_env) instead of the file, and the arm above reds it (1 failed, 7 passed). * emrg: each carrier states the auto-upgrade rule once, not merely at least once An outside review of this branch measured it on the tree where #1403 has landed and both pin files sit side by side: appending the 附则三 block to system.j2 a second time left every test green (12 passed), and appending the template clause as a second bullet after '- Must push' likewise left them green (12 passed). #1403's file pins uniqueness for its own clause (text.count(CLAUSE) == 1); this file pinned presence only, in both carriers - so the host's session prompt could carry the rule twice, at prompt cost, as two copies free to drift apart. Reproduced here before adopting it (2026-09-19, cyc20260919-065231) through this file's own readers: three arms, byte-identical restores - upgrade block duplicated in system.j2 -> 1 failed (plus the render test, whose artifact check now counts too), sibling 附则二 block duplicated in system.j2 -> 1 failed, template clause duplicated -> 1 failed; each arm reds this test alone (1 failed / 12 passed), so the pin has a job no other check does, and the healthy tree is 13 passed. The 附则二 heading is counted here as well: #1403's file reads the shipped template only (no system.j2 check), and this file is where that block's presence is pinned at all, so its one-copy property is pinned beside the 附则三 one rather than left uncounted. --------- Co-authored-by: EMRG Evolution <emrg@argszero.dev>
Closes the first half of #1324 — option (a) of the three the issue left open.
What was missing
The project's highest-priority rule — never write, restore or introduce anything that
stops or restarts the emrg server /
emrgd— was enforced in one place and stated intwo:
tests/conftest.py(_guard_stop_all_hermeticity,_guard_no_live_daemon_is_signalled— the in-process,os.killand spawned-child routes);MANIFESTO.md第四条附则二, and this host's session prompt for the evolution task.The template that ships in a release and is installed for other instances
(
emrg/server/evolution_prompt.md) said nothing about it. Its### Forbiddenlist named~/.emrg/config.toml,max_tool_rounds, paths outside{{ source_dir }}/, and itself —the server rule was absent. An instance whose task prompt is built from the shipped
template therefore never saw the rule at all.
What this changes
One clause in that
### Forbiddenlist, naming the rule, the four routes it covers(
stop_all(),stop_daemon(),emrg server stop,emrg server restart), itsprovenance (
MANIFESTO.md第四条附则二, PR emrg: process-name single-instance admission + codify server-integrity test red line (rants 2026-08-18T22:15:04/23:07:14) #854), the fact that it is not subject to anyevolution mechanism, the removal duty if an existing test violates it, and the shape
that makes this area testable without touching a real daemon (the mocked teardown
tests/test_daemon.py::test_shutdown_all_*uses).tests/test_evolution_prompt_red_lines.py— pins the clause where it has to sit, so alater prompt edit cannot drop it in silence. Its docstring names what it does not
measure: presence, not effect. Effect is the conftest guards' job; the clause exists
beside them rather than instead of them.
Verification
uv run pytest tests/ -q→ 3494 passed, 21 skipped (first run, before the new filewas
git added, the suite was green and wrong:test_the_index_derived_scans_reach_new_filescaught the untracked file — the guard doing exactly its job).
scripts/check-rant-citations.py→ rc=0, 53 sites (52 before; the new citation namesPR #854).scripts/check-doc-count.py→ rc=0.python -c "from emrg.client.app import run_client"→ OK.green: clause deleted → 2 failed / 2 passed; clause moved out of
### Forbiddeninto thetemplate's front matter → 2 failed / 2 passed. Template restored byte-identically
(sha256
06432e5b1c89b65f…).Not in this PR (recorded rather than silently skipped)
Option (b) of #1324 — classifying process-signalling commands in
_check_sandbox— is leftalone: it changes what a host may run through the tool layer, and the issue itself asks for a
host decision before that. Observation from this cycle, for a future issue: the other
permanent red line (
MANIFESTO.md第四条附则三, the automatic-upgrade chain) is likewiseabsent from the shipped template's
### Forbiddenlist; it is not folded in here to keep thisPR to one intent.