From f5d7886cd7e7c221dbaa9385d0a0b50f7a609822 Mon Sep 17 00:00:00 2001 From: EMRG Evolution Date: Mon, 2 Nov 2026 00:22:41 +0800 Subject: [PATCH] emrg: the open-source template settles a disagreement between its sources --- emrg/server/open_source_prompt.md | 2 ++ tests/test_scheduler.py | 45 +++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/emrg/server/open_source_prompt.md b/emrg/server/open_source_prompt.md index 4cd684a1..dfc1422d 100644 --- a/emrg/server/open_source_prompt.md +++ b/emrg/server/open_source_prompt.md @@ -137,6 +137,8 @@ Read what others landed upstream, why, and whether it needs a follow-up. The def **This task keeps no state file — the session itself is the state.** The daemon replays this session's history into every cycle, so your own earlier messages here, plus the memory index under `{{ source_dir }}/.emrg/memory/`, ARE "where the last cycle left off". Reconstruct from them: the phase the last cycle ran, the next step its closing summary named, our open PRs and their state, and what is blocked and on whom. When the history is silent or ambiguous, re-check reality (§1.1–§1.2) rather than assume — **never assume a PR was merged**. A cycle that ends without a closing summary strands the next one, which is why §4 is not optional. +**Where two of those sources disagree, the order is fixed rather than chosen.** The repository decides: a tree, a pull request, an issue and a check run say what the project holds now, while a memory entry or an earlier message says only what a cycle concluded on the day it ran — so a disagreement is re-measured against the repository, never settled by whichever note is nearer to hand. Among the notes themselves the later stamp governs: a decision recorded after another supersedes it, and restating an older claim in a newer file does not make it newer. A fact this cycle could not re-measure is reported as unmeasured; a note restated is not a reading. + #### 1.6 The project's extension surface **A contribution need not pass through the upstream.** A project that publishes an extension surface — a plugin or adapter API, a hook list, a theme or skill format — accepts work through it on its own terms: an extension can live in its own repository, ship on its own channel, and need no maintainer to merge anything. Where the upstream keeps its issues and pull requests closed, that surface is the only channel there is, so a cycle that read §1.1 alone would find nothing to do while the project is wide open one layer over. diff --git a/tests/test_scheduler.py b/tests/test_scheduler.py index a5775488..ac938489 100644 --- a/tests/test_scheduler.py +++ b/tests/test_scheduler.py @@ -1567,6 +1567,51 @@ def test_open_source_template_reads_the_extension_surface(): assert "I'd like to work on this" in out, "§1.1 认领规则仍在" +def test_open_source_template_settles_a_disagreement_between_its_sources(): + """open_source_prompt.md §1.5 states the order among the sources it names (issue #2666). + + §1.5 makes the session history and the memory index "where the last cycle left off", + and both are written by earlier cycles. Nothing in the section said which governs when + they disagree: a memory entry recording a PR as open survives that PR's merge, two + entries can hold different answers to one question, and the history and the index + disagree by construction. The full-code-study section's "never work from memory or a + stale tree" governs the code just fetched, not the state — so the order a cycle happened + to read the sources in decided the outcome, and a stale claim won exactly when it was + the one read. + + Pinned as the rules' presence, not as a reading of their meaning: this guard reddens + when the paragraph is deleted and also when it is reworded, which is the deliberate + trade for a prompt — a prompt's risk here is silent deletion, and a semantic reading of + prose is the thing this repository has already measured as unreachable. + """ + import jinja2 + + template_path = ( + Path(__file__).resolve().parent.parent + / "emrg" / "server" / "open_source_prompt.md" + ) + env = jinja2.Environment(undefined=jinja2.Undefined) + template = env.from_string(template_path.read_text(encoding="utf-8")) + out = template.render( + instance_id="test", host_name="host", repo_url="https://github.com/x/y.git", owner="x", repo="y", + local_source="/tmp/os", source_dir="/tmp/os", session_id="s1", + timestamp="20261102", + task={"role": "committer", "agent": "aitokenpool"}, + agent={}, git_path="git", gh_path="gh", + ) + # 1) the repository outranks a note, for the state as well as for the code + assert "The repository decides: a tree, a pull request, an issue and a check run" in out, \ + "§1.5 须写明仓库裁决笔记" + # 2) among the notes the later stamp governs, and restating does not refresh a claim + assert "the later stamp governs" in out, "§1.5 须写明笔记之间以较晚时刻为准" + assert "does not make it newer" in out, "§1.5 须写明重述不刷新" + # 3) what could not be re-measured is reported as unmeasured, not repeated + assert "a note restated is not a reading" in out, "§1.5 须写明未测即报未测" + # 4) the code rule it must not displace still stands. Deleting the new paragraph + # leaves this one green **on purpose**: the two govern different subjects. + assert "re-read the latest code before every contribution" in out, "全代码研读节仍在" + + def test_promote_template_learn_latest_state_04(): """promote_prompt.md §0.4 requires learning the agent's latest state before promoting (rant 2026-08-14T22:13:57, mirrors open-source B.2b #790)."""