mirror of
https://github.com/razzant/ouroboros.git
synced 2026-10-03 04:07:04 +00:00
fix(e2e_live): SM1 clean-worktree check tolerates the runtime's transient scratch and records the porcelain
rc.15 run3 on b6ac8c13, lane SM1_a1: the reviewed commit landed, and the
post-task promotion enabled a one-shot evolution cycle that started
seconds later in the same clone. Its run_script wrote
<clone>/.ouroboros/tmp_scripts/script_<uuid>.py (the active-workspace
scratch root, tools/shell.py, unlinked in a finally), nothing ignores that
directory, and the stand's worktree_clean_after_commit check — a bare
`git status --porcelain == ""` right after the commit — saw
"?? .ouroboros/" and failed a lane whose commit had left the tree clean.
run2's lanes used run_script 33 and 13 times without tripping it: a race
with the neighbouring task, not a constant. The product side is issue
#701 (the runtime's scratch inside the self-modified worktree).
worktree_after_commit() records the porcelain and the transient entries
as lane facts and counts only the runtime's own `.ouroboros/` scratch as
transient; any other untracked or modified path still fails the check.
A focused test module pins both directions on a throwaway repository.
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
This commit is contained in:
parent
b6ac8c13c9
commit
19128ad7f6
3 changed files with 61 additions and 2 deletions
|
|
@ -439,6 +439,20 @@ def vision_evidence_rows(tools_rows: list) -> list:
|
|||
return out
|
||||
|
||||
|
||||
RUNTIME_SCRATCH_PREFIX = ".ouroboros/" # run_script's active-workspace scratch (tools/shell.py), unlinked in a finally
|
||||
|
||||
|
||||
def worktree_after_commit(clone: pathlib.Path) -> tuple[bool, str, list[str]]:
|
||||
"""``(clean, porcelain, transient)`` for the clean-worktree check: the runtime's OWN transient scratch under
|
||||
``.ouroboros/`` does not count — a post-task evolution cycle starts seconds after the commit in the same clone
|
||||
and its ``run_script`` files live there until unlinked (rc.15 run3, SM1_a1: ``?? .ouroboros/`` at check time,
|
||||
issue #701) — but it is recorded, and every other untracked or modified path fails the check."""
|
||||
porcelain = _git(["status", "--porcelain"], clone)
|
||||
entries = [line for line in porcelain.splitlines() if line.strip()]
|
||||
transient = [line for line in entries if line.split(None, 1)[-1].startswith(RUNTIME_SCRATCH_PREFIX)] # ``_git`` strips
|
||||
return len(entries) == len(transient), porcelain, transient
|
||||
|
||||
|
||||
def _git_show(clone: pathlib.Path, rev: str, path: str) -> str:
|
||||
"""The exact text of ``path`` at ``rev`` ('' when absent there)."""
|
||||
proc = subprocess.run(["git", "show", f"{rev}:{path}"], cwd=str(clone), check=False, capture_output=True, text=True)
|
||||
|
|
@ -510,7 +524,8 @@ def run_sm1(ctx: LaneContext) -> None:
|
|||
# colour on the unlock page, the site stylesheet), and the reviewers own that judgment.
|
||||
ctx.check("committed_diff_includes_sheets", all(path in files for path in SM1_CSS_PATHS),
|
||||
committed_files=files, committed_companions=sm1_out_of_scope(ctx.clone, rev, files))
|
||||
ctx.check("worktree_clean_after_commit", _git(["status", "--porcelain"], ctx.clone) == "")
|
||||
clean, porcelain, transient = worktree_after_commit(ctx.clone)
|
||||
ctx.check("worktree_clean_after_commit", clean, worktree_porcelain=porcelain, worktree_transient=transient)
|
||||
task_oracle = ctx.oracle.task_drive(task_id)
|
||||
ledger = task_oracle.advisory_review()
|
||||
runs = [r for r in (ledger.get("advisory_runs") or []) if isinstance(r, dict)]
|
||||
|
|
|
|||
|
|
@ -1375,7 +1375,9 @@ lane seeds `owner_chat_id` ONLY, never a campaign: the scenario task's post-task
|
|||
A pre-seeded active campaign (the benchmark helper's form, rc.15 paid run2) runs generic cycles from
|
||||
t=0, gets the promotion refused (evolution already enabled) and its kept request file blocks the
|
||||
`no_promotion` exit of the absorb wait. `--self-mod` REQUIRES a confirmed absorb per lane whose scenario
|
||||
`expects_absorb` (SM1, the one that lands a commit): a pre-task snapshot
|
||||
`expects_absorb` (SM1, the one that lands a commit; its clean-worktree check records the
|
||||
porcelain and tolerates only the runtime's transient `.ouroboros/` scratch, which a post-task
|
||||
cycle in the same clone writes seconds after the commit): a pre-task snapshot
|
||||
(clone HEAD, served sha, uptime, absorbed-cycle counter) and, afterwards, the
|
||||
counter advanced, the served sha moved, the uptime reset and the server ready;
|
||||
anything less is a typed `self_mod_absorb_confirmed=false` and the run fails. The
|
||||
|
|
|
|||
42
tests/test_e2e_live_sm1_checks.py
Normal file
42
tests/test_e2e_live_sm1_checks.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
"""SM1's clean-worktree check after the reviewed commit: the runtime's own transient scratch under ``.ouroboros/``
|
||||
(``run_script`` in the active workspace, tools/shell.py) is recorded but does not fail the check — the post-task
|
||||
evolution cycle starts seconds after the commit in the same clone (rc.15 run3, SM1_a1, issue #701) — while any other
|
||||
untracked or modified path still does."""
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import subprocess
|
||||
|
||||
from devtools.e2e_live import scenarios
|
||||
|
||||
|
||||
def _repo(root: pathlib.Path) -> pathlib.Path:
|
||||
subprocess.run(["git", "init", "-q", str(root)], check=True)
|
||||
for key, value in (("user.name", "t"), ("user.email", "t@example.com")):
|
||||
subprocess.run(["git", "-C", str(root), "config", key, value], check=True)
|
||||
(root / "a.txt").write_text("a\n", encoding="utf-8")
|
||||
subprocess.run(["git", "-C", str(root), "add", "a.txt"], check=True)
|
||||
subprocess.run(["git", "-C", str(root), "commit", "-q", "-m", "base"], check=True)
|
||||
return root
|
||||
|
||||
|
||||
def test_the_runtimes_transient_scratch_is_recorded_but_does_not_fail_the_clean_check(tmp_path):
|
||||
root = _repo(tmp_path)
|
||||
assert scenarios.worktree_after_commit(root) == (True, "", [])
|
||||
(root / ".ouroboros" / "tmp_scripts").mkdir(parents=True)
|
||||
(root / ".ouroboros" / "tmp_scripts" / "script_deadbeef.py").write_text("print(1)\n", encoding="utf-8")
|
||||
clean, porcelain, transient = scenarios.worktree_after_commit(root)
|
||||
assert clean is True and porcelain == "?? .ouroboros/" and transient == ["?? .ouroboros/"]
|
||||
|
||||
|
||||
def test_any_other_untracked_or_modified_path_still_fails_the_clean_check(tmp_path):
|
||||
root = _repo(tmp_path)
|
||||
(root / ".ouroboros" / "tmp_scripts").mkdir(parents=True)
|
||||
(root / ".ouroboros" / "tmp_scripts" / "script_deadbeef.py").write_text("print(1)\n", encoding="utf-8")
|
||||
(root / "stray.txt").write_text("x\n", encoding="utf-8")
|
||||
clean, porcelain, transient = scenarios.worktree_after_commit(root)
|
||||
assert clean is False and "?? stray.txt" in porcelain and transient == ["?? .ouroboros/"]
|
||||
(root / "stray.txt").unlink()
|
||||
(root / "a.txt").write_text("changed\n", encoding="utf-8")
|
||||
clean, porcelain, transient = scenarios.worktree_after_commit(root)
|
||||
assert clean is False and "M a.txt" in porcelain and transient == ["?? .ouroboros/"] # ``_git`` strips the leading column
|
||||
Loading…
Add table
Add a link
Reference in a new issue