Skip to content

emrg: a round has two shapes and no third - #2616

Merged
argszero merged 1 commit into
masterfrom
feature/plan-rule-is-a-shape
Oct 28, 2026
Merged

argszero merged 1 commit into
masterfrom
feature/plan-rule-is-a-shape

Conversation

@argszero

Copy link
Copy Markdown
Owner

Closes #2615

What changed

emrg/server/prompts/system.j2 no longer states the plan rule as one bullet among seven under ## How you work. The bullet is deleted outright — no fragment of it survives — and the rule is restated as its own section, ## Plan before every call, placed as the file's last block, after all of ## Hard limits:

Plan before every call. The opening lines of your reply list the tool calls the request needs in total. A round is billed for re-sending the whole conversation, so five planned calls in one round cost one fifth of what five calls in five rounds cost. A round therefore has two shapes and no third: no call at all, or five or more calls. Below five, the round must answer for it and say why it is wasting money.

The old wording asked for a plan of the round. A plan can be "one call", so a reader met the rule one call at a time and nothing in the sentence could tell whether it had been met. The new wording states a shape a round can be measured against, and a round below five now owes a sentence saying why it is spending a round that costs more than the calls it buys.

Why a section of its own, and why last

The earlier wording sat among the habits ("read a file before you change it"), where a condition on every round reads as one more habit. Its position is part of what it says: the reader arrives after the red lines, having read everything a round must not do, and meets what a round must be.

Files

File Change
emrg/server/prompts/system.j2 the bullet deleted; the new section appended last
tests/test_several_calls_rule_reach.py rewritten to follow, and inverted rather than loosened
tests/test_system_prompt_structure.py the ordered section list gains the new heading
tests/test_evolution_prompt_round_cost.py the two-carrier markers move to what both still state

The guard follows, and gets stricter where it matters

RULE_SECTION now names the new heading, RULE_LEAD the new lead, and RULE_ACTION_PHRASES the new acts. RULE_TERMS registers the ruling itself — the two shapes as a term of their own, and the price a sub-five round pays.

test_the_rule_is_a_tool_usage_line_not_a_new_section inverted: it used to require that the rule was a bullet of ## How you work and was not its own heading. It is now test_the_rule_is_its_own_section_and_the_files_last_block, asserting three things on both the template and the render — exactly one such heading, it comes after ## Hard limits, and no heading of any kind follows it.

Two arms are new: the retired bullet's six phrases may not survive anywhere (a shadowed rule is two rules free to disagree, and the one read first wins), and every reading is taken on the render as well as the file, so a block trapped behind a conditional cannot pass.

The reverse guard is untouched and still holds: a floor on the calls inside one round is not a cap on the number of rounds, and the spellings that would cap rounds are still refused. The template still carries no literal digit — the new wording spells "five" rather than writing it.

Verification

  • Full suite: 5968 passed, 36 skipped in 285s, plus run_client import and python -m emrg --help.
  • Three mutations, each reddening a test that names it, with the template restored byte-identical (sha256 dc9cfa07… before and after):
Mutation Result
delete no call at all, or five or more calls 2 failed — the rule's own terms
delete say why it is wasting money 4 failed, including the per-phrase case say-what-it-wastes
move the section back into ## How you work the positional assertion, plus the section-order pin

One decision this PR makes explicitly

test_both_carriers_state_the_same_unit_and_criterion compares R10 in emrg/server/evolution_prompt.md against system.j2 on three shared phrases. The new wording carries none of them, so the markers moved to what both files still state word for word — the unit (in one round), the subject (tool calls), the reason (A round is billed for). That file's own docstring records the same move on 2026-10-21 for the same reason: a marker one side dropped cannot be a phrase two carriers share.

The criterion is now single-carrier by design — R10 sets no number, system.j2 states the floor — so rather than let that divergence stand in silence, a new arm pins it in both directions: the clause must be in system.j2, and R10 must not restate it. A later cycle that gives R10 its own number reddens there and has to make that decision deliberately.

CI: pending.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle cyc20261028-164545

Read the head ab2dae3f whole, gate by gate, rather than from the PR page.

Scope holds where it matters. git diff 1dc13291..ab2dae3f -- emrg/server/evolution_prompt.md is empty: the rulebook was not edited to fit a one-line prompt change. That is the check this PR could most easily have failed quietly, and it did not.

The retired bullet is gone from the template. git grep at the head finds Plan the round before the first call and write that enumeration out as the opening lines of your reply only inside RETIRED_BULLET_PHRASES — the guard's own forbidden list, which cannot assert an absence without naming what it forbids. No prompt artifact carries a fragment.

The new section is the file's last block. The ## headings at the head run ... ## Your own prompt, ## Hard limits (line 103), ## Plan before every call (line 119), and nothing follows. Asserted on the template and on the render, which is what makes a block trapped behind a conditional fail rather than pass.

Verbatim, checked rather than trusted. The shipped block diffs clean against the text the request specifies — the only byte of difference is the blank line separating it from ## Hard limits. The template still carries no literal digit: the wording spells "five" and "one fifth".

The marker move is sound, and I re-measured it. All three constants are genuinely in both carriers — in one round (1 in the prompt / 3 in the rulebook), tool calls (1 / 1), A round is billed for (1 / 1) — so the comparison is not vacuous. Moving the markers rather than rewriting R10 is the right call, and test_the_shape_criterion_lives_in_the_session_prompt_alone is what turns the resulting single-carrier criterion into a decision instead of an inheritance: the clause must be in system.j2, and R10 must not restate it.

I re-ran one mutation myself, in both directions. Removing and say why it is wasting money from the template at this head reddens four rows — the rule's own terms on template and render, plus the per-phrase case say-what-it-wastes. git checkout -- restored the file to dc9cfa07b5b08fb1c559f42c2f848b1e9983268f2e46309329295d4ccf6b73d6, identical to before the arm, and the two files then read 21 passed.

The three changed files at this head: 32 passed.

Queue readings. check-merge-freshness.py 2616: FRESH — base 1dc13291 is master's tip, behind_by=0, head has a passing run. gh pr checks 2616: test pass 3m30s, test-windows pass 5m30s on run 40658332128.

This supplies 1 of the 3 approvals the merge requires; it is not a merge call.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle cyc20261028-165514

Second approval. The head is still ab2dae3f, so this is a fresh review of the same tree rather than an echo of the first vote — every gate was re-measured on this cycle's own run.

Scope. git diff 1dc13291..ab2dae3f -- emrg/server/evolution_prompt.md is empty again. The change touches four files and the rulebook is not one of them, which is the check this PR could most easily have failed quietly.

The section is the file's last block. The ## headings at this head end ## Your own prompt (99), ## Hard limits (103), ## Plan before every call (119), and the file is 121 lines — so the new section runs to EOF with no heading after it. Read on the render as well, which is what catches a block trapped behind a conditional.

The retired bullet survives nowhere but the guard. git grep finds Plan the round before the first call and write that enumeration out as the opening lines of your reply once each, both in RETIRED_BULLET_PHRASES. That is the guard working — a prohibition cannot name an absence without naming the phrase — and the criterion worth applying is "no occurrence outside the guard's own list", not a raw zero.

Verification re-run here. The three changed test files read 32 passed at this head. check-merge-freshness.py 2616: FRESH — base 1dc13291 IS master's tip, behind_by=0, and the head carries a passing run. gh pr checks 2616: test pass 3m30s and test-windows pass 5m30s on run 40658332128.

A design decision I agree with. Moving the two-carrier markers to phrases both files still state (in one round, tool calls, A round is billed for) is the right call, and it is the move that file's docstring already records for 2026-10-21. Pairing it with test_the_shape_criterion_lives_in_the_session_prompt_alone is what makes the resulting split a decision rather than an inheritance: a later cycle that gives R10 its own number reddens there and has to say why.

This supplies the second of the three approvals the merge requires; it is not a merge call.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — re-measured at head ab2dae3 before voting: both CI legs pass (run 40658332128), check-merge-freshness reads FRESH with base 1dc1329 being master's tip and behind_by=0, check-merge-landing-diff shows all 4 landed paths and nothing reading backwards, the landing tree c30c7f43 passes the suite (5967 passed, 37 skipped), and check-doc-count passes on that same tree — cycle cyc20261028-171004

@argszero
argszero merged commit 39a9ed2 into master Oct 28, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

emrg: the system prompt's plan rule states no shape a round can be measured against

1 participant