Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,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` (597) — import check: `uv run python -c "from emrg.client.app import run_client"`
Python: `uv run pytest tests/ -v` (599) — import check: `uv run python -c "from emrg.client.app import run_client"`
GUI: `cd emrg/gui && npm test` (93: 22 daemon_client + 22 app-commands + 24 renderer smoke + 15 i18n + 7 integration + 3 commands) — syntax: `node --check main.js preload.js daemon_client.js renderer/js/*.js`
CI: `uv run pytest` + 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 路径不受影响)
Expand Down
2 changes: 1 addition & 1 deletion README.cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ EMRG 不只是追赶——它自己追上来。
git clone https://github.com/argszero/emrg.git
cd emrg
uv sync # 安装依赖
uv run pytest tests/ -v # 跑测试(当前 597 项)
uv run pytest tests/ -v # 跑测试(当前 599 项)
uv run python -m emrg # 启动 TUI
# CI 含 actionlint workflow 门禁(#444):workflow 解析错误在 PR 即失败

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ EMRG doesn't just keep up — it catches up on its own.
git clone https://github.com/argszero/emrg.git
cd emrg
uv sync # install deps
uv run pytest tests/ -v # run tests (currently 597 items)
uv run pytest tests/ -v # run tests (currently 599 items)
uv run python -m emrg # launch TUI
# CI includes actionlint workflow gate (#444): workflow parse errors fail PR CI

Expand Down
18 changes: 14 additions & 4 deletions emrg/server/git_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,15 +172,25 @@ def _cache_tool_paths(git: str, gh: str) -> None:
Also persists the EMRG repo URL (``repo``) so the evolution workspace
self-heal (rant 2026-08-06T20:42:05) can clone on demand without
hardcoding — packaged installs have no git remote to detect.

The write is atomic (temp file + os.replace) so concurrent readers
never observe a partially-written file; the read is guarded like
``_cached_tool_path`` so a corrupt/partial cache (e.g. a crashed or
concurrent writer) degrades to an empty dict instead of raising
JSONDecodeError (observed as a flaky test_daemon failure when the
live daemon rewrote install-info.json mid-suite).
"""
try:
data = {}
if INSTALL_INFO.exists():
data = json.loads(INSTALL_INFO.read_text(encoding="utf-8"))
try:
data = json.loads(INSTALL_INFO.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError, AttributeError):
data = {}
data.update({"git_path": git, "gh_path": gh, "repo": "https://github.com/argszero/emrg.git"})
INSTALL_INFO.write_text(
json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8"
)
tmp = INSTALL_INFO.with_name(INSTALL_INFO.name + ".tmp")
tmp.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
os.replace(tmp, INSTALL_INFO)
except OSError:
pass

Expand Down
40 changes: 40 additions & 0 deletions tests/test_git_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,46 @@ def test_cache_tool_paths_preserves_existing_fields(tmp_path, monkeypatch):
assert data["repo"] == "https://github.com/argszero/emrg.git"


def test_cache_tool_paths_tolerates_corrupt_cache(tmp_path, monkeypatch):
"""A corrupt/partial install-info.json must not raise — degrades to {}.

Regression for the flaky test_daemon::test_build_prompt_with_project
(json.decoder.JSONDecodeError): the live daemon rewrites this shared
file non-atomically; a concurrent reader could catch a partial write.
"""
from emrg.server import git_utils as mod
import json as _json

info = tmp_path / "install-info.json"
info.write_text('{"git_path": "/partial', encoding="utf-8") # truncated JSON
monkeypatch.setattr(mod, "INSTALL_INFO", info)

mod._cache_tool_paths("/usr/bin/git", "/usr/bin/gh") # must not raise

data = _json.loads(info.read_text(encoding="utf-8"))
assert data["git_path"] == "/usr/bin/git"
assert data["repo"] == "https://github.com/argszero/emrg.git"


def test_cache_tool_paths_atomic_write_no_temp_leftover(tmp_path, monkeypatch):
"""Write is atomic: target is valid JSON and no .tmp file remains."""
from emrg.server import git_utils as mod
import json as _json

info = tmp_path / "install-info.json"
info.write_text(
_json.dumps({"custom": "old"}), encoding="utf-8"
)
monkeypatch.setattr(mod, "INSTALL_INFO", info)

mod._cache_tool_paths("/usr/bin/git", "/usr/bin/gh")

assert not (tmp_path / "install-info.json.tmp").exists()
data = _json.loads(info.read_text(encoding="utf-8"))
assert data["git_path"] == "/usr/bin/git"
assert data["custom"] == "old"


# ── no_prompt_env / parse_gh_auth_user (rant 2026-08-07T10:17:27) ──


Expand Down
Loading