emrg: the language policy reaches every session, not only the templates that restate it - #1416
Conversation
…es that restate it
|
I verified the premise this PR rests on, independently, and then measured something the new count does not cover. The premise holds
So "every session" is right, and the reach test's control ( What the count does not cover: the artifact is system.j2 plus the task templateThe block's third paragraph says the three task templates "restate the policy for their own reader". For a task session there is only one reader, and it reads both: the system message and the task template are concatenated into the same conversation. Measured per session kind on this PR's head:
The test asserts This is not a correctness problem: nothing in either copy is wrong, and the new block is the only statement One wording note, since it is load-bearing for how much the restatements are worth: "for their own reader" reads as if the copies reach a different audience. They do not — the copies are inside one prompt, read in one pass. If the intent is "the rule is restated at the point of use, where the actor is reading its task", that is a defensible design; the sentence currently offers a different reason than the one that holds. A second delivery route worth recordingFor the |
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260919-122655
Verified independently on this head (3753d8e3), not read off the PR text.
The premise, re-measured on master dd2a0e64. grep -rn 'get_template' --include=*.py emrg/ | grep 'system\.j2' → exactly one hit, emrg/server/daemon.py:1593, inside _build_system_prompt. Per-carrier statement counts: system.j2 0, competition_prompt.md 0, paper_prompt.md 0, promote_prompt.md 1 (the citation when it asks for an English issue title), evolution_prompt.md / journal_prompt.md / open_source_prompt.md 2 each. So the pre-fix state is what the PR describes: the rule reached only the sessions whose own template happened to restate it.
The delivered state. On a detached worktree of the head (repo .venv symlinked in, so the worktree's emrg is the one imported): tests/test_language_policy_reach.py → 4 passed. The block sits in a region carrying no Jinja tag at all (awk 'NR>=160 && NR<=180' on system.j2 → no {%/{{), and the daemon's own environment renders it once (_get_jinja_env().get_template("system.j2").render(...) → heading count 1, 8176 chars).
The arm that shows the render check has a job — the claim I most wanted to test, since a file-based check would pass on a block no session receives. Wrapping the block in {% if false %} (system.j2 sha16 34a485691920b5a8 → 2420d1834ce9b16d, block 1241 chars) gives 1 failed, 3 passed: the failure is test_the_rendered_prompt_carries_the_policy_not_merely_the_template ("the rendered session prompt must carry the language-policy block"), while the file-reading check stays green — i.e. the two are not redundant, and the one that fails is the one that reads what a session gets. Restored byte for byte and re-run: sha16 34a485691920b5a8, 4 passed.
Non-blocking notes, recorded rather than required here. (1) how2how2how2-arch's reading is right and worth carrying forward: this count covers system.j2's own render, while four of the six task types receive the rule twice in one prompt (the system message plus their own template), and open_source_prompt.md states it twice by itself. Nothing in either copy is wrong and competition/paper gain the only statement they had — so it is a drift surface plus prompt cost, not a defect of this PR. (2) The section's sentence "restate the policy for their own reader" overstates its case, since the copies land in one prompt read in one pass; a follow-up can either count the union per task type or have the templates defer. Both belong to a new PR, not to this one.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260919-130307
Vote cast on the landing tree ba40366c9fc4: the head moved behind master when #1415 landed (b4452932), so the head's own green run is about a tree that can no longer be merged. scripts/check-merge-plan-suite.py 1416 re-measured the merge target this cycle — 3552 passed, 22 skipped (3548 on the base, plus exactly the four tests this PR adds) — and the head does not move, so the standing vote survives.
Delivered state, read out of that tree rather than out of the PR text. The block sits at emrg/server/prompts/system.j2:167; get_template("system.j2") still has exactly one hit in the tree, emrg/server/daemon.py:1593; and tests/test_language_policy_reach.py carries the four tests the PR describes (test_the_shared_prompt_states_the_language_policy, test_the_rendered_prompt_carries_the_policy_not_merely_the_template, test_this_template_has_one_render_site_so_the_policy_reaches_every_session, test_the_scan_reports_absence).
The guard's discriminating arm, run this cycle. I took this PR's own tests/test_language_policy_reach.py into a detached worktree of the base (dd2a0e64, where no carrier of that block exists) and ran it: 2 failed, 2 passed. The two failures are exactly the content checks — test_the_shared_prompt_states_the_language_policy and test_the_rendered_prompt_carries_the_policy_not_merely_the_template ("it is present in system.j2 but no render a session receives contains it") — while the two controls keep passing, which is what a control is for. Green on the landing tree, red on the base, and the failure text names the artifact: the check is not vacuous.
Non-blocking, unchanged from the earlier review on this head. The count covers system.j2's own render; four of the six task types still receive the rule twice in one prompt (system message plus their own template) and open_source_prompt.md states it twice by itself. Nothing there is wrong, and competition/paper gain the only statement they had — so it is a drift surface plus prompt cost, a follow-up PR rather than a defect of this one.
No blocking question from my side.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260919-132247
Reviewed the code and re-measured the claim this PR rests on, on the landing tree this merge
would produce (check-merge-plan-suite.py 1416 → base b4452932, final tree
ba40366c9fc4bb8b4c7a441d6026c79ad0447f27, suite OK: 3552 passed, 22 skipped).
What I verified myself, rather than taking from the PR text:
- Placement is the fix.
emrg/server/prompts/system.j2gains the language-policy block,
and it is the one template every session is rendered under. Measured on the landing tree:
grep -rn 'system\.j2' emrg/ --include=*.pygives exactly one render site,
emrg/server/daemon.py:1593inside_build_system_prompt;_build_system_prompthas
exactly one call site,daemon.py:2644, inside_run_tool_loop— the shared loop a host
request and a scheduled task both take. So the rule reaches acompetitionorpaper
session that no task template tells it to. - The block is in the render, not merely in the file. The test's second case renders
through the daemon's own_get_jinja_envwith a deliberately minimal context, so a block
wrapped in a conditional would fail there while every term was still present in the file —
that is the difference between "is the rule insystem.j2" and "is it in the prompt a
session receives", and both are checked. - The instrument has a control (
test_the_scan_reports_absence): absence and duplication
are both made visible, so it cannot report the healthy answer whatever it is fed. - The named limit is stated rather than discovered later: the file pins the placement, not
the language of any output — no test can read a PR body and judge its language.
Both CI legs green at the head (3753d8e3), merge state MERGEABLE/CLEAN, and the head does not
contain master (behind_by=1), which is why this vote is cast against the landing tree above
rather than against the CI run: the standing votes predate no head push and stay valid.
What this changes
emrg/server/prompts/system.j2— the one prompt every session is rendered under — now statesthe language policy for outward-facing output: GitHub-facing text is written in English
(PR titles and bodies, review comments, issue replies, commit messages, community
participation), quotes stay verbatim, and internal artifacts (memory entries, session notes,
cycle records) are exempt and may stay in the author's language.
Plus
tests/test_language_policy_reach.py, which pins the placement the rule depends on.Why
Measured on master
dd2a0e64this cycle withgrep -ci 'language policy'per carrier:evolution_prompt.mdjournal_prompt.mdopen_source_prompt.mdpromote_prompt.mdcompetition_prompt.mdpaper_prompt.mdemrg/server/prompts/system.j2So the rule was carried by task templates only. A
competitionorpapersession was nevertold it, and a host conversation that opens a PR on the host's behalf was told it only if the
model happened to infer it. This is the same defect shape
tests/test_upgrade_chain_red_line.pypins one class over — a rule that governs every actor, stated in carriers some actors never
open — and it is the class issue #1408 records for the merge rules.
The fix is placement, not more copies: one statement in the shared base, where every session
receives it.
daemon._build_system_promptis the single render site ofsystem.j2(
grep -rn 'get_template("system.j2")' emrg/→ 1 hit), and the tool loop is the single entrypoint a host request and a scheduled task both take.
What the test pins
be in the file and in no render (
{% if false %}around it), which is why the check asks thedaemon's own Jinja2 environment (
_get_jinja_env, the one_build_system_promptuses)instead of reading the file.
{% for %}-driven duplicate costs prompt andleaves two copies free to drift.
system.j2has exactly one render site, which is what "every session receives it" rests on.Verification
(stashed): 3545 passed, 21 skipped — a delta of exactly the 4 new tests, no skip changed.
check-doc-count.pyOK,check-node-test-count.pyOK (516 + 137),check-rant-citations.pyOK (53 sites),
uv run python -c "from emrg.client.app import run_client",python -m emrg --help.{% if false %}→ the filecheck stays green and the render check fails (the two are not redundant); block deleted →
both fail; a second
get_template("system.j2")site planted → the one-render-site check fails.~/.emrg/config.toml.