From b59a7ba5c5704adfd24a1042eb59ba3bf8937243 Mon Sep 17 00:00:00 2001 From: EMRG Evolution Date: Wed, 26 Aug 2026 00:23:26 +0800 Subject: [PATCH] emrg: prompt rules for temp-file hygiene + direct-CDP browser ops (rants 2026-08-25T18:10:57 / 17:57:15) --- Agent.md | 2 +- emrg/server/promote_prompt.md | 1 + emrg/server/prompts/system.j2 | 5 +++++ tests/test_daemon.py | 23 +++++++++++++++++++++++ tests/test_scheduler.py | 30 ++++++++++++++++++++++++++++++ 5 files changed, 60 insertions(+), 1 deletion(-) diff --git a/Agent.md b/Agent.md index 813518d1..cbdb188e 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.token; python -m emrg ``` -Python: `uv run pytest tests/ -v` (1079) — import check: `uv run python -c "from emrg.client.app import run_client"` +Python: `uv run pytest tests/ -v` (1082) — import check: `uv run python -c "from emrg.client.app import run_client"` GUI: `cd emrg/gui && npm test` (262: 45 daemon_client + 20 conn-manager + 22 app-commands + 132 renderer smoke + 15 i18n + 8 integration + 3 commands + 8 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/promote_prompt.md b/emrg/server/promote_prompt.md index 38446d5c..5e20f0fb 100644 --- a/emrg/server/promote_prompt.md +++ b/emrg/server/promote_prompt.md @@ -58,6 +58,7 @@ Read the full config of `{{ project.name }}` from `~/.emrg/projects.yml` (path, #### 0.3 Confirm promotion channel availability - **Browser-first channel probing (MUST, rant 2026-08-19T21:08:55)**: use `browser-harness` to operate the **real browser** for all channel detection and actions — the login state (cookies/sessions) exists **only in the real browser**. curl/API probes see a logged-out view and misjudge channel availability (e.g. curl returns 000 for Reddit while the browser is logged in and fully usable). When checking whether a channel works / is logged in / can post: **open it in the real browser via browser-harness first**; only fall back to curl for plain public-data reads (public JSON endpoints, docs) when the browser path is unavailable or the data is genuinely public. +- **Direct CDP connection (MUST, rant 2026-08-25T17:57:15)**: all browser operations MUST connect **directly** to the local CDP endpoint `ws://127.0.0.1:57000/devtools/page/...` (HTTP `127.0.0.1:57000/json` returns 200 with the tab list) — following the r47/r48 `_post_*.py` CDP script pattern (websocket to `ws://127.0.0.1:57000/devtools/page/`). **FORBIDDEN**: calling browser-harness's `remote-debugging-setup` / opening `chrome://inspect` — that pops Chrome's "Allow remote debugging?" authorization dialog and blocks waiting for host clicks. The 57000 endpoint is always available on this host; if a direct connection fails, **retry the direct connection** (the tab list may have changed), never switch to the popup flow. - Check the CLI: `which curl` (public-data reads only — never for login-state judgment) - Check whether the browser harness skill is available (`/skills` or `ls ~/.emrg/skills/`) - Channel unavailable → record it in the state file (blocked = channel unavailable); skip channel actions this round, but still write the reflection diff --git a/emrg/server/prompts/system.j2 b/emrg/server/prompts/system.j2 index c1a8d245..edb86fe6 100644 --- a/emrg/server/prompts/system.j2 +++ b/emrg/server/prompts/system.j2 @@ -85,6 +85,11 @@ Index: `{{ session_memory_index_path }}` **To read history**: use the `read` tool on `history.jsonl` for the current context, or on a specific `history_YYMMDD.jsonl` file for older messages. Each line is a JSON record with `type`, `role`, `content`, `timestamp` fields. Message records: `type=message`, tool calls: `type=tool_call`/`tool_result`, compacted summaries: `type=summary`. + +## Temp File Rules (rant 2026-08-25T18:10:57) +- Throwaway scripts/scratch files (`.py`, `.ps1`, `.sh`, `.json` payloads) MUST be written under **`{{ session.dir_path }}/tmp/`** (create the directory if missing) — never in the project root, working directory, or `~/.emrg` root. +- Clean up: at session/round end, delete temp files that have already been executed successfully; do not leave `tmp_*.py` / `.tmp-*` clutter in the working root. +- If historical `tmp_*.py` clutter already exists in the working root, move it into `{{ session.dir_path }}/tmp/` or delete it once no longer needed. {% endif %} {% if config_dir %} diff --git a/tests/test_daemon.py b/tests/test_daemon.py index f8fc496e..d97d269c 100644 --- a/tests/test_daemon.py +++ b/tests/test_daemon.py @@ -282,6 +282,29 @@ def test_system_prompt_rant_handling_section(tmp_path): assert "/rant" in rendered +def test_system_prompt_temp_file_rules_section(tmp_path): + """Temp File Rules section renders with the session tmp dir (rant + 2026-08-25T18:10:57): throwaway scripts must go under the session + directory's tmp/ subdir, not the project root / workdir / ~/.emrg root.""" + server = _make_server() + session = Session.create_with_id("tmpfile-test", tmp_path) + rendered = server._build_system_prompt(session) + assert "## Temp File Rules" in rendered + assert "tmp/" in rendered + # the rendered path must point into the session directory + assert str(session.dir_path) in rendered + assert "never in the project root" in rendered or "project root" in rendered + + +def test_system_prompt_temp_file_rules_absent_without_session(): + """Without a session the temp-file rules section is skipped (no path to + anchor it to), same as Session & History.""" + server = _make_server() + rendered = server._build_system_prompt() + assert "## Temp File Rules" not in rendered + assert "## Session & History" not in rendered + + def test_submit_rant_tool_registered(): """submit_rant is in the daemon tool registry (available in all sessions).""" server = _make_server() diff --git a/tests/test_scheduler.py b/tests/test_scheduler.py index 785635aa..eb55e023 100644 --- a/tests/test_scheduler.py +++ b/tests/test_scheduler.py @@ -1259,6 +1259,36 @@ def test_promote_template_homework_first_dehardening(): assert "mention stats" in out, "状态文件提及统计字段应渲染" +def test_promote_template_direct_cdp_rule(): + """promote_prompt.md §0.3 mandates direct CDP 127.0.0.1:57000 for all + browser ops, forbidding the remote-debugging-setup popup flow + (rant 2026-08-25T17:57:15, R49: popup blocked waiting for host Allow).""" + import jinja2 + + template_path = ( + Path(__file__).resolve().parent.parent + / "emrg" / "server" / "promote_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/x/y.git", owner="x", repo="y", + local_source="/tmp/pm", source_dir="/tmp/pm", session_id="s1", + evolution_cwd="/tmp/evo", timestamp="20260825", + task={"project": "aitokenpool"}, + project={"path": "/tmp/proj", "name": "aitokenpool", "description": "d"}, + evolution_count=0, git_path="git", gh_path="gh", + ) + # A. direct CDP endpoint mandated (positive discrimination) + assert "127.0.0.1:57000" in out, "直连 CDP 端点应渲染" + assert "Direct CDP connection (MUST" in out, "直连 CDP MUST 规则应渲染" + assert "remote-debugging-setup" in out, "禁止的 remote-debugging-setup 应点名" + assert "FORBIDDEN" in out, "禁止词应渲染" + # B. retry-direct-not-popup behavior + assert "retry the direct connection" in out, "直连失败应重试直连而非弹窗" + + def test_promote_template_registration_blog_sections(): """promote_prompt.md §2.x host-authorized account registration + §2.y blog publishing (rants 2026-08-15T09:04:28 / 09:06:12)."""