agent-exec transport ba8f131d4f2b8d718c2fa2b84ec799b9/b503d085cc5b

This commit is contained in:
agent-exec-transport
2026-10-18 12:31:40 +00:00
parent c88311bbf6
commit d5b8e74c43
2 changed files with 80 additions and 0 deletions
+30
View File
@@ -526,6 +526,36 @@ class EvidenceTests(unittest.TestCase):
self.assertIn("local continuation validated", result.stdout)
self.assertEqual(self.git("rev-parse", "HEAD", cwd=wt), task_head)
def test_local_continuation_accepts_when_target_ref_reappears(self):
"""A resume-local receipt captured with no remote-tracking target ref
validates after a forward target ref reappears, provided R0 stays locally
reachable and the reappeared target is a forward descendant of R0.
The receipt records target_head=None; the rewind check is skipped because
either side is missing, and the reappeared ref (a forward descendant of R0)
must satisfy the ancestry guard that the registered task is still current."""
wt = self.local_task()
head_before = self.git("rev-parse", "HEAD", cwd=wt)
# Drop refs/remotes/origin/main BEFORE capture so the receipt records
# target_head=None. R0 (the recorded initial baseline) must remain
# reachable as a local object.
self.git("update-ref", "-d", "refs/remotes/origin/main")
self.git("cat-file", "-e", self.head)
self.capture("make precheck", cwd=wt)
captured = json.loads(self.receipt.read_text())
self.assertIsNone(captured["after"]["target_head"])
# Recreate refs/remotes/origin/main as a forward descendant of R0.
forward = self.git("commit-tree", "-p", self.head,
"-m", "Forward target ref reappeared", self.head + "^{tree}")
self.git("update-ref", "refs/remotes/origin/main", forward)
# Validation must succeed: receipt has no target_head (rewind check is
# skipped) and the reappeared target descends from R0 (ancestry guard
# accepts a forward target ref).
result = self.validate(wt)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertIn("local continuation validated", result.stdout)
self.assertEqual(self.git("rev-parse", "HEAD", cwd=wt), head_before)
def test_local_precheck_captures_without_remote_tracking_ref(self):
wt = self.local_task()
head_before = self.git("rev-parse", "HEAD", cwd=wt)
+50
View File
@@ -144,6 +144,56 @@ class TaskRecoveryTests(unittest.TestCase):
self.assertNotIn("[verdict] LOCAL_READY", delivery.stdout)
self.assertEqual(f.git(self.wt, "rev-parse", "HEAD").stdout, head_before)
def test_local_precheck_rejects_closed_task(self):
"""A closed registered task (已完成(合入前闭合)) must not advertise LOCAL_READY.
The current local precheck emits LOCAL_READY for any registered in-progress
card text and never inspects the card status, so this test records the
expected red result: the verdict is wrong and the exit code is wrong."""
f = self.fixture
self.card.write_text(self.card.read_text().replace("状态:进行中",
"状态:已完成(合入前闭合)"))
self.save_card_change()
self.assertIn("已完成(合入前闭合)", self.card.read_text())
head_before = f.git(self.wt, "rev-parse", "HEAD").stdout
local = self.precheck()
self.assertNotEqual(local.returncode, 0, local.stdout)
self.assertNotIn("[verdict] LOCAL_READY", local.stdout)
self.assertEqual(f.git(self.wt, "rev-parse", "HEAD").stdout, head_before)
def test_local_precheck_rejects_reachable_target_rewind(self):
"""Origin is reachable but its target was rewound after the task cached a
newer descendant. Local precheck must not emit LOCAL_READY under that
divergence — the cached refs/remotes/origin/main is no longer trustworthy.
The current implementation lets fetch_target() surface a non-FFREMOTE_UNVERIFIED
line, then unconditionally prints LOCAL_READY in local mode. The exit code
is wrong and the verdict is wrong; this test records both expected failures."""
f = self.fixture
r0 = f.git(self.wt, "rev-parse", "origin/main").stdout.strip()
# (1) Advance the remote and let local precheck refresh the cached origin/main.
f.advance_remote()
refresh = self.precheck()
self.assertEqual(refresh.returncode, 0, refresh.stdout)
forward = f.git(self.wt, "rev-parse", "origin/main").stdout.strip()
self.assertNotEqual(forward, r0)
# (2) Rewind the bare remote target back to R0; the worktree cache still
# points at the forward descendant because we have not fetched since.
f.git(f.remote, "update-ref", "refs/heads/main", r0, forward)
self.assertEqual(f.git(f.remote, "rev-parse", "refs/heads/main").stdout.strip(), r0)
self.assertEqual(f.git(self.wt, "rev-parse", "origin/main").stdout.strip(), forward)
head_before = f.git(self.wt, "rev-parse", "HEAD").stdout
# (3) Local precheck sees a reachable origin whose target is older than the
# cache. fetch_target() refuses the non-fast-forward update; the precheck
# must still refuse LOCAL_READY.
local = self.precheck()
self.assertNotEqual(local.returncode, 0, local.stdout)
self.assertNotIn("[verdict] LOCAL_READY", local.stdout)
self.assertEqual(f.git(self.wt, "rev-parse", "HEAD").stdout, head_before)
# After the failed fetch the cache still points at the forward descendant
# so a follow-up run can repeat the same check.
self.assertEqual(f.git(self.wt, "rev-parse", "origin/main").stdout.strip(), forward)
def test_unknown_work_mode_is_rejected(self):
result = self.precheck(mode="anything")
self.assertNotEqual(result.returncode, 0, result.stdout)