agent-exec transport 973ce511e924ce5ab0953e16e46a42ec/7671c49f7667

This commit is contained in:
agent-exec-transport
2026-10-18 14:04:13 +00:00
parent 5b50d50b2a
commit fbb2aacb71
+203
View File
@@ -469,6 +469,209 @@ class TaskRecoveryTests(unittest.TestCase):
self.assertEqual(f.git(self.wt, "rev-parse", "refs/remotes/origin/main").stdout,
cached_before)
def write_no_cache_wrapper(self, bin_dir, advertised, log_name="git.log"):
"""Create a disposable git wrapper for the missing-tracking-ref scenarios.
Intercepts:
fetch -> fail (origin unreachable; the wrapper itself exits 128
and writes the standard "unable to access origin" stderr line
so the precheck captures the same `fetch_err` it would observe
against a real unreachable remote)
ls-remote origin refs/heads/main -> configured ADVERTISED (or
nothing when ADVERTISED is empty, simulating an origin that
has no configured target branch)
branch --merged origin/main -> empty output (the cached
tracking ref is deleted locally; the real git would otherwise
fail the pipeline under set -e/pipefail and the precheck would
silently exit before the verdict is printed)
All other git calls delegate to the real binary via the resolved
REAL_GIT absolute path (and a stripped PATH so the wrapper cannot
recurse into itself) so the registered task context stays
observable normally.
Returns (bin_dir, wrapper_log_path)."""
f = self.fixture
real_git = subprocess.check_output(
["bash", "-c", "command -v git"], env=f.env, text=True).strip()
bin_dir.mkdir(exist_ok=True)
wrapper_log = bin_dir / log_name
wrapper = bin_dir / "git"
wrapper.write_text(
"#!/usr/bin/env bash\n"
"# Disposable fixture wrapper for the missing-tracking-ref\n"
"# scenarios:\n"
"# fetch -> fail (128)\n"
"# ls-remote origin refs/heads/main -> $ADVERTISED\n"
"# branch --merged origin/main -> empty\n"
"# Anything else delegates to the real binary so the registered\n"
"# task context (HEAD, branch, R0, worktree, .task/) stays\n"
"# observable normally.\n"
f"REAL_GIT={shlex.quote(real_git)}\n"
f"LOG={shlex.quote(str(wrapper_log))}\n"
f"ADVERTISED={shlex.quote(advertised)}\n"
"WRAPPER_BIN=$(cd \"$(dirname \"$0\")\" && pwd)\n"
"echo \"configured advertised-target=$ADVERTISED\" >> \"$LOG\"\n"
"stripped=\"\"\n"
"old_ifs=\"$IFS\"\n"
"IFS=':'\n"
"for p in $PATH; do\n"
" [ \"$p\" = \"$WRAPPER_BIN\" ] && continue\n"
" if [ -z \"$stripped\" ]; then stripped=\"$p\"; else stripped=\"$stripped:$p\"; fi\n"
"done\n"
"IFS=\"$old_ifs\"\n"
"export PATH=\"$stripped\"\n"
"case \"$1\" in\n"
" fetch)\n"
" echo \"fetch-intercepted\" >> \"$LOG\"\n"
" echo 'fatal: unable to access origin' >&2\n"
" exit 128 ;;\n"
" ls-remote)\n"
" if [ \"${2:-}\" = origin ] && [ \"${3:-}\" = refs/heads/main ]; then\n"
" echo \"ls-remote-served advertised-target=$ADVERTISED\" >> \"$LOG\"\n"
" if [ -n \"$ADVERTISED\" ]; then\n"
" printf '%s\\trefs/heads/main\\n' \"$ADVERTISED\"\n"
" fi\n"
" exit 0\n"
" fi\n"
" exec \"$REAL_GIT\" \"$@\" ;;\n"
" branch)\n"
" # refs/remotes/origin/main is deleted locally; the real git\n"
" # `git branch --merged origin/main` would fail and the\n"
" # precheck pipeline (with pipefail + set -e) would silently\n"
" # exit. Stub it so the verdict is actually emitted.\n"
" if [ \"${2:-}\" = --merged ] && [ \"${3:-}\" = origin/main ]; then\n"
" echo \"branch-merged-stub no-merged-branches\" >> \"$LOG\"\n"
" exit 0\n"
" fi\n"
" exec \"$REAL_GIT\" \"$@\" ;;\n"
"esac\n"
"exec \"$REAL_GIT\" \"$@\"\n")
wrapper.chmod(0o755)
return bin_dir, wrapper_log
def test_local_precheck_without_cached_ref_rejects_uncomparable_remote(self):
"""Cached refs/remotes/origin/main is absent; origin advertises a
SHA whose object is unavailable locally. Local precheck must not
advertise LOCAL_READY because ancestry cannot be compared against
any locally known target — the cached target cannot be trusted
as authoritative without an ancestry check, and the advertised
SHA is not locally reachable so no forward-descendant
relationship can be established either.
The current implementation lets target_history() short-circuit at
the missing cached-ref step (return "ok") and unconditionally
print LOCAL_READY in local mode. The verdict is wrong; this test
records the expected red result."""
f = self.fixture
head_before = f.git(self.wt, "rev-parse", "HEAD").stdout
r0 = f.git(self.wt, "rev-parse", "origin/main").stdout.strip()
# Detach the registered branch from its (now stale) upstream so
# no in-process code accidentally queries HEAD@{u} / remote.merge
# against the deleted cached ref.
f.git(self.wt, "branch", "--unset-upstream", "feature/test")
f.git(self.wt, "update-ref", "-d", "refs/remotes/origin/main")
synthetic = "1" * 40
bin_dir, wrapper_log = self.write_no_cache_wrapper(
f.root / "no-cache-uncomparable-bin", synthetic)
path_env = f"{bin_dir}{os.pathsep}{f.env.get('PATH', os.environ['PATH'])}"
wrapper_env = f.env | {"PATH": path_env}
local = f.task(self.wt, "precheck", check=False,
env=wrapper_env | {"TASK_WORK_MODE": "local"})
# Proof: the wrapper was loaded (configured marker) and served
# the failure case (fetch intercepted). ls-remote is served by
# the wrapper if and when the post-fix implementation queries it
# for the missing cached-ref path; the current implementation
# short-circuits before ls-remote, so the assertion below does
# not require an ls-remote entry.
log = wrapper_log.read_text()
self.assertIn(f"configured advertised-target={synthetic}", log)
self.assertIn("fetch-intercepted", log)
self.assertNotEqual(local.returncode, 0, local.stdout)
self.assertNotIn("[verdict] LOCAL_READY", local.stdout)
self.assertIn("[verdict] REMOTE_UNVERIFIED", local.stdout)
self.assertEqual(f.git(self.wt, "rev-parse", "HEAD").stdout, head_before)
def test_local_precheck_without_cached_ref_rejects_missing_remote_target(self):
"""Cached refs/remotes/origin/main is absent; origin returns no
configured target branch (ls-remote outputs nothing). Local
precheck must not advertise LOCAL_READY because nothing can be
verified against the registered task's R0 — the cached target
is the only handle on what origin claimed, and without it (and
without an ls-remote response) the precheck has no
authoritative signal to confirm the target even exists.
The current implementation lets target_history() short-circuit at
the missing cached-ref step (return "ok") and unconditionally
print LOCAL_READY in local mode. The verdict is wrong; this test
records the expected red result."""
f = self.fixture
head_before = f.git(self.wt, "rev-parse", "HEAD").stdout
r0 = f.git(self.wt, "rev-parse", "origin/main").stdout.strip()
f.git(self.wt, "branch", "--unset-upstream", "feature/test")
f.git(self.wt, "update-ref", "-d", "refs/remotes/origin/main")
bin_dir, wrapper_log = self.write_no_cache_wrapper(
f.root / "no-cache-missing-target-bin", "")
path_env = f"{bin_dir}{os.pathsep}{f.env.get('PATH', os.environ['PATH'])}"
wrapper_env = f.env | {"PATH": path_env}
local = f.task(self.wt, "precheck", check=False,
env=wrapper_env | {"TASK_WORK_MODE": "local"})
log = wrapper_log.read_text()
# Empty advertised value means the wrapper emits no ls-remote line.
self.assertIn("configured advertised-target=", log)
self.assertIn("fetch-intercepted", log)
self.assertNotEqual(local.returncode, 0, local.stdout)
self.assertNotIn("[verdict] LOCAL_READY", local.stdout)
self.assertIn("[verdict] REMOTE_UNVERIFIED", local.stdout)
self.assertEqual(f.git(self.wt, "rev-parse", "HEAD").stdout, head_before)
def test_local_precheck_without_cached_ref_allows_comparable_forward_target(self):
"""Cached refs/remotes/origin/main is absent; origin advertises
the task's R0 (or a forward descendant that is locally
available). Local precheck must continue to advertise
LOCAL_READY — the explicit missing-tracking-ref continuation
contract — because the registered task context is the same as
the cached ref would have represented, and the ls-remote
response can be compared against the registered baseline to
confirm origin is at or past the task's initial state.
The current implementation short-circuits at the missing
cached-ref step (return "ok"), which coincidentally returns
LOCAL_READY for any registered task. The verdict is right; this
test records the expected green result so the post-fix
implementation preserves the contract when the ls-remote check
is added against the registered R0."""
f = self.fixture
head_before = f.git(self.wt, "rev-parse", "HEAD").stdout
r0 = f.git(self.wt, "rev-parse", "origin/main").stdout.strip()
f.git(self.wt, "branch", "--unset-upstream", "feature/test")
f.git(self.wt, "update-ref", "-d", "refs/remotes/origin/main")
bin_dir, wrapper_log = self.write_no_cache_wrapper(
f.root / "no-cache-comparable-bin", r0)
path_env = f"{bin_dir}{os.pathsep}{f.env.get('PATH', os.environ['PATH'])}"
wrapper_env = f.env | {"PATH": path_env}
local = f.task(self.wt, "precheck", check=False,
env=wrapper_env | {"TASK_WORK_MODE": "local"})
log = wrapper_log.read_text()
self.assertIn(f"configured advertised-target={r0}", log)
self.assertIn("fetch-intercepted", log)
self.assertEqual(local.returncode, 0, local.stdout)
self.assertIn("[verdict] LOCAL_READY", local.stdout)
self.assertNotIn("[verdict] REMOTE_UNVERIFIED", local.stdout)
self.assertEqual(f.git(self.wt, "rev-parse", "HEAD").stdout, head_before)
if __name__ == "__main__":
unittest.main()