diff --git a/Agent.md b/Agent.md index 75afdba5..56ce31f3 100644 --- a/Agent.md +++ b/Agent.md @@ -118,7 +118,7 @@ Community needs voiced in HN agent-UI discussions map directly to EMRG's design: pkill -f "emrg.server"; rm -f ~/.emrg/emrgd.port; python -m emrg ``` -Python: `uv run pytest tests/ -v` (867) — import check: `uv run python -c "from emrg.client.app import run_client"` +Python: `uv run pytest tests/ -v` (868) — import check: `uv run python -c "from emrg.client.app import run_client"` GUI: `cd emrg/gui && npm test` (257: 45 daemon_client + 19 conn-manager + 22 app-commands + 129 renderer smoke + 16 i18n + 7 integration + 3 commands + 7 build-config + 7 gui-state + 2 tool-group) — syntax: `node --check main.js preload.js daemon_client.js renderer/js/*.js` CI: `uv run pytest` (ubuntu + **windows-2025 matrix** — Windows pytest 回归在 PR CI 即失败,v0.2.29 教训 #725) + GUI tests + **actionlint workflow lint** (`rhysd/actionlint@v1.7.12` gate, #444 — workflow 解析错误在 PR CI 即失败,如 `if:` secrets 上下文) Re-trigger: `scripts/re-trigger-ci.sh [branch]` (workflow_dispatch, #527 — 替代空 commit 重触发:Actions outage 会整段丢弃 push 事件,dispatch 走 API 路径不受影响) diff --git a/emrg/server/evolution_prompt.md b/emrg/server/evolution_prompt.md index bca96a0f..b0f7e831 100644 --- a/emrg/server/evolution_prompt.md +++ b/emrg/server/evolution_prompt.md @@ -268,7 +268,7 @@ Old entries without a `status` field are treated as pending. When reading rants, follow these rules: - Any unhandled rants? Previously skipped? Large changes can be staged -- Only read rants whose `project` field matches the current task's `config.project`; **ignore rants without a `project` field entirely** +- Match the rant's `project` field against **either** this task's `config.project` (**`{{ task.project }}`**) or the owner/repo form (**`{{ owner }}/{{ repo }}`**) — equal to either counts as a match; **ignore rants without a `project` field entirely** (rant 2026-08-17T12:09:57: the two forms must both match — a rant written with one form must never silently fail to match the other) > **Note**: first check whether a rant was already handled, to avoid duplicate work: > 1. Check `git log --oneline -20` for commits referencing the rant (search the rant's timestamp or message keywords) — **note: a commit referencing the rant timestamp is only evidence the rant was touched, NOT sufficient proof of completion**. You must further verify: does the rant have unmet acceptance items? Are there unmerged branches? An early PR merge in a multi-stage effort does not mean the rant is done. diff --git a/emrg/server/open_source_prompt.md b/emrg/server/open_source_prompt.md index 58334fd2..db2ec553 100644 --- a/emrg/server/open_source_prompt.md +++ b/emrg/server/open_source_prompt.md @@ -141,9 +141,15 @@ cat ~/.emrg/rants.jsonl 2>/dev/null || echo "[no rants.jsonl — skip rant scan] Filter rules (aligned with evolution_prompt.md): -- Only rants whose `project` field == this task's `config.project`; **ignore rants without a `project` field entirely** +- Match the rant's `project` field against **either** of these two values — equal to either counts as a match: + - this task's `config.project` (tasks.yml): **`{{ task.project }}`** + - owner/repo form: **`{{ owner }}/{{ repo }}`** +- Example: if `config.project` is `aitokenpool` and the repo is `argszero/aitokenpool`, then a rant with `project: aitokenpool` **or** `project: argszero/aitokenpool` both match. +- **Ignore rants without a `project` field entirely** - Only consider rants with status `pending` or `in_progress` +**⚠️ Unmatched-rant hint**: after the scan, if there exist rants with status `pending`/`in_progress` whose `project` starts with `{{ task.project }}` or `{{ owner }}/{{ repo }}` but did NOT match the filter above, record the count in the state file / reflection (e.g. "存在 N 条 project 疑似本项目但未匹配的 rant" / "N rants with a project resembling this repo were not matched") — never silently skip them. + **Dedup check — before treating any candidate rant as actionable** (run for each candidate): ```bash diff --git a/tests/test_scheduler.py b/tests/test_scheduler.py index 709fb8ca..e4a93787 100644 --- a/tests/test_scheduler.py +++ b/tests/test_scheduler.py @@ -601,6 +601,11 @@ def test_open_source_template_renders_with_context(): assert "0.5 Rant scan" in out, "rant-scan 0.5 节应渲染" assert "rants.jsonl" in out, "rant 扫描命令应渲染" assert "config.project" in out, "project 匹配过滤应渲染" + # Rant 2026-08-17T12:09:57: dual-compatible project matching (config.project + # OR owner/repo) must be rendered with the actual values. + assert "aitokenpool" in out, "task.project 值应渲染进 0.5 节" + assert "x/y" in out, "owner/repo 形式应渲染进 0.5 节" + assert "Unmatched-rant hint" in out, "未匹配疑似 rant 提示应渲染" assert "B.1b Rant-driven mode" in out, "rant 驱动模式应渲染" assert "ROLE LOCK" in out, "既有 ROLE LOCK 应保留" assert "json.dumps(..., ensure_ascii=False)" in out, "rant 状态写入要求应渲染" @@ -2113,3 +2118,28 @@ async def _run(): asyncio.run(_run()) finally: mod.config_dir = orig + + +def test_evolution_template_renders_dual_project_match(): + """evolution_prompt.md renders the dual-compatible rant project match + (rant 2026-08-17T12:09:57): both config.project AND owner/repo forms + must be accepted when scanning rants.""" + import jinja2 + + template_path = ( + Path(__file__).resolve().parent.parent + / "emrg" / "server" / "evolution_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", uptime="0h 0m", + repo_url="https://github.com/argszero/emrg.git", owner="argszero", + repo="emrg", local_source="/tmp/evo", source_dir="/tmp/evo", + session_id="s1", evolution_cwd="/tmp/evo", timestamp="20260817", + task={"role": "committer", "project": "emrg"}, + project={}, evolution_count=0, git_path="git", gh_path="gh", + ) + assert "emrg" in out, "task.project 值应渲染" + assert "argszero/emrg" in out, "owner/repo 形式应渲染" + assert "ignore rants without a `project` field entirely" in out