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 @@ -119,7 +119,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` (1277) — import check: `uv run python -c "from emrg.client.app import run_client"`
Python: `uv run pytest tests/ -v` (1284) — import check: `uv run python -c "from emrg.client.app import run_client"`
GUI: `cd emrg/gui && npm test` (100: 44 daemon_client + 20 conn-manager + 7 integration + 7 nav-policy + 7 gui-state + 6 build-config + 4 boot-contract + 3 preload-api + 2 theme-guard) — syntax: `node --check main.js preload.js daemon_client.js`
Renderer: `cd emrg/gui/renderer && npm run typecheck && npm test` (514: 5 snapshot-store + 9 utils + 3 ErrorBoundary + 2 App smoke + 11 commands + 4 copywriting + 11 i18n + 13 markdown + 21 transcript + 11 TranscriptView + 15 history + 31 composer + 41 Composer + 6 LinkDialog + 16 sidebar + 17 Sidebar + 9 fileTree + 9 FileTree + 16 resultPanel + 8 ResultPanel + 27 workspaceView + 10 WorkspaceView + 10 dialog + 6 Dialog + 9 ConfirmDialog + 9 RenameDialog + 10 dialogLists + 3 HelpDialog + 9 MemoryDialog + 6 SkillsDialog + 8 openSession + 6 WelcomeDialog + 9 OpenSessionDialog + 7 NewSessionDialog + 7 rewind + 8 RewindDialog + 7 GithubDeviceDialog + 18 daemonBridge + 7 DaemonBridgeProvider + 30 Shell + 15 DialogHost + 20 SettingsPanel + 6 TaskFormDialog + 5 RantDialog + 4 vendorMarkdown) + `npm run build` → `renderer/dist/`
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 上下文)
Expand Down
8 changes: 4 additions & 4 deletions scripts/calibrate_silent_drift_threshold.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ def main(argv: list[str] | None = None) -> int:
print(
f"\nNo sub-threshold observations accumulated yet "
f"(n_noise=0, n_drift={stats['n_drift']}). The detector writes an "
f"anchor_bias_observation per anchored round — re-run after the "
f"anchor_bias_observation per anchored round - re-run after the "
f"daemon has seen some usage. Current threshold stays "
f"{args.current}."
)
Expand Down Expand Up @@ -316,15 +316,15 @@ def main(argv: list[str] | None = None) -> int:
if reason == "noise-crowding-boundary":
print(
f"\nNOISE CROWDING THE BOUNDARY: p99 ({_fmt(stats['p99'])}) is within "
f"10% of the current threshold {args.current} — per-round estimate "
f"10% of the current threshold {args.current} - per-round estimate "
f"noise is close to tripping the guard. Recommended "
f"_SILENT_DRIFT_THRESHOLD: {stats['recommended']} "
f"(= max(current, p99 * 1.5))."
)
elif reason == "noise-well-below-boundary":
print(
f"\nNoise is well below the boundary (p99={_fmt(stats['p99'])}, "
f"current={args.current}) — the guard demonstrably fires only on "
f"current={args.current}) - the guard demonstrably fires only on "
f"real drift. Recommended _SILENT_DRIFT_THRESHOLD: "
f"{stats['recommended']} (unchanged)."
)
Expand All @@ -333,7 +333,7 @@ def main(argv: list[str] | None = None) -> int:
f"\nNO CLEAN SEPARATION: noise tail p99 ({_fmt(stats['p99'])}) * 1.5 "
f"exceeds the smallest observed drift "
f"({_fmt(stats['min_drift']) if stats['min_drift'] is not None else 'n/a'} "
f"* 0.75 headroom) — a single fixed threshold cannot separate the "
f"* 0.75 headroom) - a single fixed threshold cannot separate the "
f"two populations. Keeping current threshold {args.current}; "
f"consider per-provider thresholds or investigating the noisy "
f"providers."
Expand Down
2 changes: 1 addition & 1 deletion scripts/check_nonlocal.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ def check_nonlocal(app_path: str) -> int:
exit_code = 1

if exit_code == 0:
print("✅ nonlocal integrity check passed")
print("OK: nonlocal integrity check passed")

return exit_code

Expand Down
10 changes: 5 additions & 5 deletions scripts/push-branch-from-api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@
Counterpart of scripts/sync-master-from-api.py (fetch direction). EMRG repeatedly
hits github.com:443 unreachable while api.github.com stays up (10+ documented
cycles, 08-22..08-26). When a fix must be pushed during an outage, the old flow
was a hand-written ephemeral script re-derived each time from memory notes —
was a hand-written ephemeral script re-derived each time from memory notes -
cycle 2026-08-26 00:59 recorded four gotchas learned the hard way:

1. blobs must be uploaded from the *committed* object bytes (`git cat-file
blob <sha>`), not working-tree bytes (CRLF normalization differs)
2. `git ls-tree` needs `-r` to enumerate nested paths
3. the API displays dates as UTC ('Z') but stores the raw offset (+0800) —
3. the API displays dates as UTC ('Z') but stores the raw offset (+0800) -
recreate commits with the original epoch+offset or the sha will not match
4. subprocess text I/O must use encoding='utf-8', errors='replace' (GBK
console crashes on non-ASCII commit messages)

This script automates that recipe:

* walks the local chain from <ref> (default HEAD) down to the remote base —
* walks the local chain from <ref> (default HEAD) down to the remote base -
the existing branch head, or the first ancestor the remote already has
(GET /repos/{repo}/commits/{sha})
* uploads blobs (raw bytes, byte-exact) and trees (structured entries,
Expand Down Expand Up @@ -272,7 +272,7 @@ def push_branch(repo: str, branch: str, ref: str, force: bool, cwd: str | None =
if not chain:
raise PushError("internal: empty commit chain")

print(f" base: {base or '(new branch — nothing on remote yet)'}")
print(f" base: {base or '(new branch - nothing on remote yet)'}")
print(f" uploading {len(chain)} commit(s)...", flush=True)

# ---- upload objects bottom-up, mapping local -> remote shas
Expand Down Expand Up @@ -428,7 +428,7 @@ def main() -> int:
print(f"repo {repo} branch {args.branch}")
print(f" remote sha: {result['sha']}")
if result["result"] == "no-op":
print(" nothing to do — ref already at that commit")
print(" nothing to do - ref already at that commit")
else:
print(f" original local tip: {result['original_local_tip']}")
print(f" content identical to local tip: {result['content_identical']}")
Expand Down
8 changes: 4 additions & 4 deletions scripts/reader_fix_latency.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"""Measure the reader-feedback -> merged-fix loop: median issue-to-fix latency.

Issue #1027 secondary suggestion (reader comment heinrichneb, Dev.to 3dicj):
"add a README metric — median time from reader-found boundary to merged fix
"add a README metric - median time from reader-found boundary to merged fix
(the 50-min loop claim becomes a measured number)."

The Dev.to article claimed a ~50-minute issue-to-merged-fix loop (#1000: issue
Expand All @@ -16,10 +16,10 @@
issue.created_at -> merged_at of the EARLIEST merged linked PR

The median across all such issues is the headline number. Only the earliest
merged PR counts — a fix is "shipped" when its first merged PR lands.
merged PR counts - a fix is "shipped" when its first merged PR lands.

The script also reports the OPEN side of the loop (issue #1056, Dev.to
comment 3djoa, heinrichneb 2026-08-27): an open-issue age counter — how
comment 3djoa, heinrichneb 2026-08-27): an open-issue age counter - how
many open issues exist, their median age in days, and the oldest few. A
median only over already-fixed issues hides issues that are still waiting;
the two numbers together bound the loop from both ends.
Expand Down Expand Up @@ -180,7 +180,7 @@ def main(argv: list[str] | None = None) -> int:

samples = collect_latencies(args.repo, args.limit)
if not samples:
print("no closed issues with a merged fix PR found — nothing to measure")
print("no closed issues with a merged fix PR found - nothing to measure")
return 0

vals = [s[1] for s in samples]
Expand Down
8 changes: 4 additions & 4 deletions scripts/sync-master-from-api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
stays up (10+ documented cycles, e.g. 08-22..08-26). The usual fallback flow:
the local repo already contains the content (a branch pushed via the Git Data
API that later got squash-merged upstream), so advancing local refs only needs
the missing *commit* objects — reconstructed byte-exact from the API's
the missing *commit* objects - reconstructed byte-exact from the API's
verification payload + signature, including web-flow GPG-signed squash merges.


When the head commit's *content* objects (blobs/trees) are also missing locally
(e.g. a parallel PR introduced files this repo never had — first hit in cycle
(e.g. a parallel PR introduced files this repo never had - first hit in cycle
cyc20260826-154904 with #994's GUI assets), the script now auto-fetches them via
the Git Data API: blobs via `git/blobs/{sha}` + `git hash-object -w`, trees via
`git/trees/{sha}` + `git mktree` (canonical ordering), recursing bottom-up. The
Expand All @@ -27,7 +27,7 @@
`git hash-object -t commit -w` (byte-exact, GPG signature preserved)
* verifies the root tree sha matches the remote; if content objects are
missing, fetches missing blobs/trees via the Git Data API (disable with
--no-fetch-objects) and re-verifies — fail-loud only if still mismatched
--no-fetch-objects) and re-verifies - fail-loud only if still mismatched
* updates refs/heads/<ref> and refs/remotes/origin/<ref>

Requirements: git on PATH; api.github.com reachable. Auth: optional for public
Expand Down Expand Up @@ -228,7 +228,7 @@ def main() -> int:
capture_output=True, text=True)
if local_tree.returncode != 0 or local_tree.stdout.strip() != tree:
if not args.no_fetch_objects:
print(f" root tree {tree[:7]} missing locally — fetching blobs/trees via Git Data API")
print(f" root tree {tree[:7]} missing locally - fetching blobs/trees via Git Data API")
_fetch_tree(repo, tree)
local_tree = subprocess.run(["git", "rev-parse", head + "^{tree}"],
capture_output=True, text=True)
Expand Down
Loading
Loading