emrg: structural dirty-tree guard — dirty source dir forces read-only sandbox + git mutators blocked - #980
Conversation
… sandbox + git mutators blocked (community issue #979)
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle 20260825-192404. CI green (test + test-windows). Fresh review of da24f61: (1) scheduler guard fail-open by design (non-git dir → not dirty), runs via asyncio.to_thread so the event loop is never blocked, local subprocess import preserves the module invariant; EMRG_TASK_DIRTY_OVERRIDE receipt semantics correct (named task / wildcard / other-task). (2) bash_tool read-only blocks git mutators while allowing read-only git reads — the 08-20 killers are now structurally blocked, not just discouraged. 8 regression tests + Agent.md count sync. Full local suite 1071 passed/1 skipped.
…n dirty source tree (community issue #979)
argszero
commented
Aug 25, 2026
Extended the guard (new head
Rationale: the 08-20 incident was about the agent mutating the host's uncommitted tree — bash-only coverage left a hole where a read-only task could still overwrite host files via write/edit without touching git. Now all three file-mutating tools are structurally read-only in a dirty tree. |
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle 20260825-193548 (LGTM 2/3: 192404, 193548; no ❌ between). Reviewed new head 6f06e4b (write/edit read-only extension): check_read_only_file_write correctly blocks workspace-internal writes (host's protected tree) and protected daemon files while allowing outside-workspace writes so a read-only cycle can still Record; daemon injection of sandbox + workspace mirrors the existing bash pattern; write/edit gate before any file mutation. 6 regression tests cover blocked-inside / allowed-outside / no-sandbox unchanged. CI green on the new head (test + test-windows). Local suite 1077 passed/1 skipped.
…ly/am/archive/submodule/worktree)
argszero
commented
Aug 25, 2026
Further hardened the read-only tier (new head |
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle 20260825-194513 (3rd consecutive LGTM: 192404, 193548, 194513; no ❌ between). Reviewed head 2d275ab: git-mutator block extended to working-tree writers (apply/am/archive/submodule/worktree) — closes the remaining hole where a read-only cycle could still mutate the host tree via patch application or submodule/worktree ops; 5 new test cases; CI green on this head (test + test-windows, run 32843417822). Merge condition met.
Uh oh!
There was an error while loading. Please reload this page.
…ard, GUI session fix, pid/port deprecation, structural dirty-tree guard) (#982) * emrg: bump version to v0.2.79 (release: #976-#980 — plist-codesign guard, GUI session fix, pid/port deprecation, structural dirty-tree guard) * emrg: drop stray PR-body artifacts from release branch --------- Co-authored-by: EMRG Evolution <emrg@argszero.dev>
Summary
Implements the structural half of the community feedback in issue #979 (dev.to postmortem comment by heinrichneb): "The stronger structural fix than read-only-on-dirty might be to never point the agent at the host's tree at all... Rules can regress; topology can't." — plus the audited override pattern: "inconvenient by default, possible on explicit human override... every exception itself a receipt."
The 2026-08-20 data-loss guard (#881) is currently prompt-level only (open_source_prompt.md). This PR makes it structural in two layers:
Changes
emrg/server/scheduler.py— per-cycle effective sandbox guard:TaskHandler._is_dirty_tree_sync(): probesgit status --porcelainin the task's source dir (localsubprocessimport keeps the module's no-subprocess invariant, rant 2026-08-19T14:20:52; run viaasyncio.to_threadso the event loop is never blocked). Fail-open: non-git dirs / git errors are treated as not dirty.TaskHandler._effective_sandbox()(async): when the source dir has uncommitted changes, the cycle's bash sandbox is forced to read-only regardless of task config — the host's live edits are structurally out of reach, not merely discouraged.EMRG_TASK_DIRTY_OVERRIDE(comma-separated task names, or*) lets a human lift the guard; every override is logged withlogger.warningas a receipt.await self._effective_sandbox()instead of the static configured tier.emrg/tools/bash_tool.py— read-only blocks git mutators:rm -r / rmdir / mv / cp -rplus shell redirects — the actual 2026-08-20 killers (git stash,git checkout .,git reset --hard,git clean) escaped the target scan entirely.stash / checkout / restore / clean / reset / commit / push / pull / merge / rebase / cherry-pick / revert / rm / mv / switch / branch -d/-D / tag -d.status / fetch / log / diff / show / branch -a / remote -v) stay allowed — the read-only cycle still scans and reviews.Tests (8 new):
test_bash_tool_sandbox.py: git mutators blocked under read-only (positive), git reads allowed (negative), workspace-write keeps git mutation enabled (the normal working tier).test_scheduler.py: dirty-tree detection (clean repo vs untracked file vs non-git dir), dirty tree forces read-only (incl. configured read-only not weakened),EMRG_TASK_DIRTY_OVERRIDEreceipt semantics (named task / wildcard / other-task), clean tree keeps configured tier.Verification
test_saturation_heartbeat_makes_no_network_callspasses)Notes
~/.emrg/upgrade-work/emrg); full worktree isolation for scheduled tasks is a larger follow-up.