From 05a9a3bc89b65eebd92cac6f82c3e4f28e13ab71 Mon Sep 17 00:00:00 2001 From: agent-exec-transport Date: Sun, 18 Oct 2026 12:41:18 +0000 Subject: [PATCH] agent-exec transport a41de1816057eb4de14b0175a36c9c68/dfebc3695180 --- scripts/evidence.py | 15 +++++++++++---- scripts/task.sh | 15 ++++++++++++++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/scripts/evidence.py b/scripts/evidence.py index e53f6861..3e3d7e92 100644 --- a/scripts/evidence.py +++ b/scripts/evidence.py @@ -322,12 +322,17 @@ def validate_precheck(receipt, purpose="start"): validate_local_scope(receipt, current, context) -def validate_local_scope(receipt, current, context): - """LOCAL_READY continues one active registered task; it remains SELF local-work evidence only.""" +def require_active_task(root): + """Closed, archived or abandoned cards cannot authorize local continuation.""" card = find_card() status = re.search(r";状态:\s*([^;\s][^;\n]*)", card.read_text(encoding="utf-8")) - if card.resolve().parent != context["root"] / ".task" or not status or status.group(1).startswith("已"): + if card.resolve().parent != root / ".task" or not status or status.group(1).startswith("已"): raise EvidenceError("Local continuation requires an active registered task") + + +def validate_local_scope(receipt, current, context): + """LOCAL_READY continues one active registered task; it remains SELF local-work evidence only.""" + require_active_task(context["root"]) states = TASK_STATE.findall(receipt["output"]) if len(states) != 1 or Path(states[0][0]) != context["root"] or states[0][1] != context["r0"]: raise EvidenceError("Precheck belongs to a different task identity or initial baseline") @@ -467,8 +472,10 @@ def main(): try: if os.environ.get("SRC", "SELF") not in ("", "SELF"): raise EvidenceError("Local evidence is SELF; GATE/REPLAY require external execution identity") - if sys.argv[1:] == ["--local-context"]: + if sys.argv[1:] in (["--local-context"], ["--local-context", "--active"]): context = task_context(require_target=False) + if sys.argv[2:]: + require_active_task(context["root"]) print("[task-state] registered=" + str(context["root"]) + " initial_base=" + context["r0"]) return 0 if sys.argv[1:]: diff --git a/scripts/task.sh b/scripts/task.sh index 2d908fd8..02a0607d 100755 --- a/scripts/task.sh +++ b/scripts/task.sh @@ -18,6 +18,15 @@ fetch_target() { git rev-parse --verify "refs/remotes/origin/${TARGET_BRANCH}^{commit}" >/dev/null \ || { echo "[verdict] REMOTE_UNVERIFIED — target commit missing" >&2; return 6; } } +target_rewound() { # 0 仅当 origin 通告的目标提交本地可用且不是缓存 origin/ 的后代;不可达或无法比较返回 1 + local cached advertised rc=0 + cached=$(git rev-parse -q --verify "refs/remotes/origin/${TARGET_BRANCH}^{commit}" 2>/dev/null) || return 1 + advertised=$(git ls-remote origin "refs/heads/${TARGET_BRANCH}" 2>/dev/null \ + | awk -v ref="refs/heads/${TARGET_BRANCH}" '$2 == ref && !seen { print $1; seen = 1 }') || return 1 + [ -n "$advertised" ] && git cat-file -e "${advertised}^{commit}" 2>/dev/null || return 1 + git merge-base --is-ancestor "$cached" "$advertised" 2>/dev/null || rc=$? + [ "$rc" -eq 1 ] +} check_worktree() { python3 "${SCRIPT_DIR}/check-worktree.py" "$1"; } main_worktree_root() { git worktree list --porcelain -z | python3 -c 'import os,sys; print(os.fsdecode(sys.stdin.buffer.read().split(bytes([0]),1)[0][9:]))'; } work_mode() { @@ -140,7 +149,11 @@ cmd_precheck() { exit 3 fi if [ "$mode" = local ]; then - python3 "${SCRIPT_DIR}/evidence.py" --local-context || return 2 + python3 "${SCRIPT_DIR}/evidence.py" --local-context --active || return 2 + if [ "$remote_fresh" -eq 0 ] && target_rewound; then + echo "[verdict] REMOTE_UNVERIFIED — origin/${TARGET_BRANCH} rewound or diverged from the cached target; preserve local work and reconcile the target history" + return 6 + fi [ "$remote_fresh" -eq 1 ] || echo "[remote-freshness] unavailable — LOCAL_READY rests on local task context only" echo "[verdict] LOCAL_READY — existing task may continue authorized local work; final delivery still requires latest-target sync and checks" return 0