Separate local task recovery from delivery readiness

Why: an existing authorized task should not merge a moving target merely to resume local work, while new task startup and delivery remain strict.

Evidence: SELF red-green recovery and environment regressions; task, recovery, evidence and local-mode write-guard suites passed through make evidence.

Impact: add explicit LOCAL_READY and task-resume, validate worktree/card/baseline and receipt purpose, preserve schema 1 and prevent local receipts from starting or releasing tasks.

Refs: U2; policy/merge-queue.md; .task/feature-merge-queue-policy-draft.md
This commit is contained in:
2026-10-13 07:42:12 +00:00
parent 03cbeac8e6
commit eb46bbb296
6 changed files with 314 additions and 13 deletions
+35 -4
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# 任务启动门禁:precheck / start / sync / close / finish(配套 Makefile 同名目标)。
# 任务启动门禁:precheck / start / resume / sync / close / finish(配套 Makefile 同名目标)。
# 规范依据:collaboration.md 共同基线与同步节点、治理清理;delivery.md §1/§5。
set -euo pipefail
@@ -20,9 +20,17 @@ fetch_target() {
}
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() {
case "${TASK_WORK_MODE:-delivery}" in
delivery|local) printf '%s\n' "${TASK_WORK_MODE:-delivery}" ;;
*) die "TASK_WORK_MODE must be delivery or local" ;;
esac
}
cmd_precheck() {
need_repo
local mode
mode=$(work_mode)
fetch_target
local branch head rmain dirty sub_drift sub_uninit
branch=$(git rev-parse --abbrev-ref HEAD)
@@ -100,11 +108,20 @@ cmd_precheck() {
echo "[facts] WARN policy/facts.md 不存在——外部事实登记表缺失(AGENTS.md 证据与验证门禁)"
fi
echo "[required-reading] core/baseline.md, workflows/delivery.md §1$([ -f policy/delivery.md ] && echo ", policy/delivery.md")$([ "$dirty" -gt 0 ] && echo ", standards/collaboration.md(树脏=他人或未收尾现场,不得写入)")"
echo "[R0] $(echo "$rmain" | cut -c1-10)(完整:$rmain)"
if [ "$mode" = local ]; then
echo "[target] ${rmain}(observed; task initial baseline is reported separately)"
else
echo "[R0] $(echo "$rmain" | cut -c1-10)(完整:$rmain)"
fi
if [ "$dirty" -gt 0 ] || [ "${sub_drift:-0}" -gt 0 ]; then
echo "[verdict] SHARED_WORKSPACE_BUSY — 工作树不干净,不得写入;先收尾或经 owner 确认"
exit 3
fi
if [ "$mode" = local ]; then
python3 "${SCRIPT_DIR}/evidence.py" --local-context || return 2
echo "[verdict] LOCAL_READY — existing task may continue authorized local work; final delivery still requires latest-target sync and checks"
return 0
fi
if ! check_worktree before-sync; then
echo "[verdict] SHARED_WORKSPACE_BUSY — component or active operation check failed; preserve the workspace"
exit 3
@@ -123,7 +140,7 @@ cmd_start() {
fetch_target
[ -n "${PRECHECK_RECEIPT:-}" ] || die "PRECHECK_RECEIPT required: capture RECEIPT=/tmp/task-precheck.json CMD='make precheck' make evidence first"
PRECHECK_RECEIPT=$(realpath -- "$PRECHECK_RECEIPT")
VALIDATE_RECEIPT="$PRECHECK_RECEIPT" bash "${SCRIPT_DIR}/evidence.sh"
VALIDATE_RECEIPT="$PRECHECK_RECEIPT" VALIDATE_RECEIPT_PURPOSE=start bash "${SCRIPT_DIR}/evidence.sh"
check_worktree before-sync
# 已合入的同名分支不得重建(2026-10-05 实测:合入后再 task-start 同名会从新 main 长出
# 第二段历史,空卡覆盖已合入任务卡,两轮证据差点丢失)。复核/追加沿用原分支或换新 slug。
@@ -274,6 +291,19 @@ EOF
echo "[started] branch=${name} worktree=${wt} R0=$(echo "$rmain" | cut -c1-10)"
}
cmd_resume() {
need_repo
local mode
mode=$(work_mode)
fetch_target
[ -n "${PRECHECK_RECEIPT:-}" ] || die "PRECHECK_RECEIPT required: capture the current task precheck before resume"
local receipt
receipt=$(realpath -- "$PRECHECK_RECEIPT")
VALIDATE_RECEIPT="$receipt" VALIDATE_RECEIPT_PURPOSE="resume-${mode}" bash "${SCRIPT_DIR}/evidence.sh"
IMPORT_RECEIPT="$receipt" bash "${SCRIPT_DIR}/evidence.sh"
echo "[resumed] work_mode=${mode} source_head=$(git rev-parse HEAD) observed_target=$(git rev-parse "origin/${TARGET_BRANCH}")"
}
cmd_sync() {
need_repo
fetch_target
@@ -542,8 +572,9 @@ cmd_finish() {
case "${1:-}" in
precheck) shift; cmd_precheck "$@" ;;
start) shift; cmd_start "$@" ;;
resume) shift; cmd_resume "$@" ;;
sync) shift; cmd_sync "$@" ;;
close) shift; cmd_close "$@" ;;
finish) shift; cmd_finish "$@" ;;
*) echo "usage: task.sh <precheck|start|sync|close|finish> [args]"; exit 2 ;;
*) echo "usage: task.sh <precheck|start|resume|sync|close|finish> [args]"; exit 2 ;;
esac