@frankzhang2026/opencode-android-orchestrator 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/README.md +66 -31
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/dist/cli.js +64 -2
- package/dist/cli.js.map +1 -1
- package/dist/config/long-command-timeout.d.ts +20 -0
- package/dist/config/long-command-timeout.d.ts.map +1 -0
- package/dist/config/long-command-timeout.js +65 -0
- package/dist/config/long-command-timeout.js.map +1 -0
- package/dist/doctor/installation.d.ts +1 -1
- package/dist/doctor/installation.d.ts.map +1 -1
- package/dist/doctor/installation.js +3 -1
- package/dist/doctor/installation.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/installer/adaptive-templates.d.ts +4 -1
- package/dist/installer/adaptive-templates.d.ts.map +1 -1
- package/dist/installer/adaptive-templates.js +8 -0
- package/dist/installer/adaptive-templates.js.map +1 -1
- package/dist/installer/gradle-verification.d.ts.map +1 -1
- package/dist/installer/gradle-verification.js +3 -0
- package/dist/installer/gradle-verification.js.map +1 -1
- package/dist/installer/init.d.ts.map +1 -1
- package/dist/installer/init.js +4 -1
- package/dist/installer/init.js.map +1 -1
- package/dist/installer/opencode-config.d.ts +3 -3
- package/dist/installer/opencode-config.js +1 -1
- package/dist/installer/upgrade.d.ts +1 -0
- package/dist/installer/upgrade.d.ts.map +1 -1
- package/dist/installer/upgrade.js +7 -0
- package/dist/installer/upgrade.js.map +1 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +5 -0
- package/dist/plugin/index.js.map +1 -1
- package/docs/MIGRATION.md +19 -13
- package/docs/SECURITY.md +17 -6
- package/docs/TROUBLESHOOTING.md +23 -3
- package/package.json +1 -1
- package/templates/.opencode/agents/scheduled-planner.md +5 -0
- package/templates/.opencode/commands/resume-task.md +28 -0
- package/templates/.opencode/skills/scheduled-quality-orchestrator/SKILL.md +38 -3
- package/templates/README.md +3 -3
- package/templates/automation/config.json +3 -1
- package/templates/automation/config.schema.json +9 -2
- package/templates/scripts/automation/lib.sh +2 -0
- package/templates/scripts/automation/preflight.sh +1 -0
- package/templates/scripts/automation/resume-task.sh +135 -0
- package/templates/scripts/automation/tests/run-tests.sh +39 -2
|
@@ -8,13 +8,15 @@
|
|
|
8
8
|
"maxFixLoops": 1,
|
|
9
9
|
"maxReviewCycles": 1,
|
|
10
10
|
"maxReviewerRestarts": 2,
|
|
11
|
+
"longCommandTimeoutMs": 1800000,
|
|
11
12
|
"autoCleanupWorktrees": true,
|
|
12
13
|
"pushAfterAcceptance": false,
|
|
13
14
|
"approvalPhrases": {
|
|
14
15
|
"proposal": "批准方案,生成计划和任务合同。",
|
|
15
16
|
"contract": "合同已复核,批准自动执行到人工验收阶段。",
|
|
16
17
|
"acceptance": "验收通过,提交到原分支。",
|
|
17
|
-
"abort": "中止任务,封存修改并恢复原分支。"
|
|
18
|
+
"abort": "中止任务,封存修改并恢复原分支。",
|
|
19
|
+
"resume": "恢复任务,重新捕获基线并继续自动执行。"
|
|
18
20
|
},
|
|
19
21
|
"plugins": {
|
|
20
22
|
"superpowers": "superpowers@git+https://github.com/obra/superpowers.git#v6.2.0"
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"maxFixLoops",
|
|
37
37
|
"maxReviewCycles",
|
|
38
38
|
"maxReviewerRestarts",
|
|
39
|
+
"longCommandTimeoutMs",
|
|
39
40
|
"autoCleanupWorktrees",
|
|
40
41
|
"pushAfterAcceptance",
|
|
41
42
|
"approvalPhrases",
|
|
@@ -57,17 +58,23 @@
|
|
|
57
58
|
"maxFixLoops": { "type": "integer", "minimum": 0, "maximum": 1 },
|
|
58
59
|
"maxReviewCycles": { "type": "integer", "minimum": 0, "maximum": 2 },
|
|
59
60
|
"maxReviewerRestarts": { "type": "integer", "minimum": 0, "maximum": 3 },
|
|
61
|
+
"longCommandTimeoutMs": {
|
|
62
|
+
"type": "integer",
|
|
63
|
+
"minimum": 120000,
|
|
64
|
+
"maximum": 7200000
|
|
65
|
+
},
|
|
60
66
|
"autoCleanupWorktrees": { "type": "boolean" },
|
|
61
67
|
"pushAfterAcceptance": { "const": false },
|
|
62
68
|
"approvalPhrases": {
|
|
63
69
|
"type": "object",
|
|
64
70
|
"additionalProperties": false,
|
|
65
|
-
"required": ["proposal", "contract", "acceptance", "abort"],
|
|
71
|
+
"required": ["proposal", "contract", "acceptance", "abort", "resume"],
|
|
66
72
|
"properties": {
|
|
67
73
|
"proposal": { "type": "string", "minLength": 4 },
|
|
68
74
|
"contract": { "type": "string", "minLength": 4 },
|
|
69
75
|
"acceptance": { "type": "string", "minLength": 4 },
|
|
70
|
-
"abort": { "type": "string", "minLength": 4 }
|
|
76
|
+
"abort": { "type": "string", "minLength": 4 },
|
|
77
|
+
"resume": { "type": "string", "minLength": 4 }
|
|
71
78
|
}
|
|
72
79
|
},
|
|
73
80
|
"plugins": {
|
|
@@ -120,12 +120,14 @@ automation_validate_config() {
|
|
|
120
120
|
(.maxFixLoops | type == "number" and . >= 0 and . <= 1 and floor == .) and
|
|
121
121
|
(.maxReviewCycles | type == "number" and . >= 0 and . <= 2 and floor == .) and
|
|
122
122
|
(.maxReviewerRestarts | type == "number" and . >= 0 and . <= 3 and floor == .) and
|
|
123
|
+
(.longCommandTimeoutMs | type == "number" and . >= 120000 and . <= 7200000 and floor == .) and
|
|
123
124
|
(.autoCleanupWorktrees | type == "boolean") and
|
|
124
125
|
.pushAfterAcceptance == false and
|
|
125
126
|
(.approvalPhrases.proposal | type == "string" and length >= 4) and
|
|
126
127
|
(.approvalPhrases.contract | type == "string" and length >= 4) and
|
|
127
128
|
(.approvalPhrases.acceptance | type == "string" and length >= 4) and
|
|
128
129
|
(.approvalPhrases.abort | type == "string" and length >= 4) and
|
|
130
|
+
(.approvalPhrases.resume | type == "string" and length >= 4) and
|
|
129
131
|
(.plugins | type == "object" and keys == ["superpowers"]) and
|
|
130
132
|
(.plugins.superpowers | type == "string" and length > 0) and
|
|
131
133
|
(.requiredSkills | type == "array" and length >= 6) and
|
|
@@ -155,6 +155,7 @@ else
|
|
|
155
155
|
(last_rule("edit"; "**/src/**") == "deny") and
|
|
156
156
|
(last_rule("bash"; "./scripts/automation/prepare-contract-review.sh *") == "allow") and
|
|
157
157
|
(last_rule("bash"; "./scripts/automation/approve-and-run.sh *") == "allow") and
|
|
158
|
+
(last_rule("bash"; "./scripts/automation/resume-task.sh *") == "allow") and
|
|
158
159
|
(last_rule("bash"; "./scripts/automation/resume-review.sh *") == "allow") and
|
|
159
160
|
(last_rule("bash"; "./scripts/automation/accept-and-integrate.sh *") == "allow") and
|
|
160
161
|
(last_rule("bash"; "./scripts/automation/abort-task.sh *") == "allow") and
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
5
|
+
# shellcheck source=lib.sh
|
|
6
|
+
source "$SCRIPT_DIR/lib.sh"
|
|
7
|
+
|
|
8
|
+
task_id="${1:-}"
|
|
9
|
+
approval="${2:-}"
|
|
10
|
+
if [[ "$#" -ne 2 ]]; then
|
|
11
|
+
printf 'Usage: %s TASK-ID "RESUME-APPROVAL"\n' "$0" >&2
|
|
12
|
+
exit 2
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
automation_validate_task_id "$task_id"
|
|
16
|
+
automation_require_orchestrated
|
|
17
|
+
automation_require_approval resume "$approval"
|
|
18
|
+
|
|
19
|
+
workspace_file="$(automation_workspace_path "$task_id")"
|
|
20
|
+
origin_file="$(automation_origin_path "$task_id")"
|
|
21
|
+
state_file="$(automation_state_path "$task_id")"
|
|
22
|
+
evidence_dir="$(automation_evidence_path "$task_id")"
|
|
23
|
+
transitions_file="$evidence_dir/transitions.jsonl"
|
|
24
|
+
resumptions_file="$evidence_dir/baseline-resumptions.jsonl"
|
|
25
|
+
|
|
26
|
+
[[ -f "$workspace_file" ]] || automation_die "workspace metadata is missing for $task_id"
|
|
27
|
+
[[ -f "$origin_file" ]] || automation_die "origin evidence is missing for $task_id"
|
|
28
|
+
[[ -f "$state_file" ]] || automation_die "state is missing for $task_id"
|
|
29
|
+
[[ -f "$transitions_file" ]] || automation_die "transition evidence is missing for $task_id"
|
|
30
|
+
|
|
31
|
+
automation_acquire_run_lock "$task_id"
|
|
32
|
+
trap 'automation_release_run_lock' EXIT
|
|
33
|
+
|
|
34
|
+
[[ "$(automation_read_state "$task_id")" == "BLOCKED" ]] || automation_die "$task_id is not BLOCKED"
|
|
35
|
+
|
|
36
|
+
source_root="$(jq -er '.sourceRoot' "$workspace_file")"
|
|
37
|
+
task_root="$(automation_workspace_task_root "$workspace_file")"
|
|
38
|
+
workspace_strategy="$(automation_workspace_strategy "$workspace_file")"
|
|
39
|
+
original_branch="$(jq -er '.originalBranch' "$workspace_file")"
|
|
40
|
+
task_branch="$(jq -er '.taskBranch' "$workspace_file")"
|
|
41
|
+
baseline_head="$(jq -er '.baselineHead' "$workspace_file")"
|
|
42
|
+
|
|
43
|
+
[[ "$(jq -r '.repositoryLeaseRequired // false' "$workspace_file")" == "true" ]] || \
|
|
44
|
+
automation_die "baseline recovery requires a leased transactional workspace"
|
|
45
|
+
[[ -d "$task_root" ]] || automation_die "task root is missing: $task_root"
|
|
46
|
+
automation_require_repository_lease "$task_id" "$source_root" "$workspace_strategy"
|
|
47
|
+
task_root="$(cd "$task_root" && pwd -P)"
|
|
48
|
+
source_root="$(cd "$source_root" && pwd -P)"
|
|
49
|
+
[[ "$source_root" == "$AUTOMATION_ROOT" ]] || automation_die "workspace sourceRoot does not match this repository"
|
|
50
|
+
[[ "$(jq -er '.sourceRoot' "$origin_file")" == "$source_root" ]] || automation_die "origin sourceRoot does not match workspace metadata"
|
|
51
|
+
[[ "$(jq -er '.originalBranch' "$origin_file")" == "$original_branch" ]] || automation_die "origin branch does not match workspace metadata"
|
|
52
|
+
[[ "$(automation_current_branch "$task_root")" == "$task_branch" ]] || automation_die "task branch changed"
|
|
53
|
+
[[ "$(git -C "$task_root" rev-parse HEAD)" == "$baseline_head" ]] || automation_die "task HEAD changed before baseline sealing"
|
|
54
|
+
git -C "$source_root" show-ref --verify --quiet "refs/heads/$original_branch" || automation_die "recorded original branch no longer exists"
|
|
55
|
+
[[ "$(git -C "$source_root" rev-parse "refs/heads/$original_branch")" == "$baseline_head" ]] || automation_die "original branch drifted after task preparation"
|
|
56
|
+
|
|
57
|
+
for sealed_evidence in baseline.json red.json ready.json review.json reviews.jsonl; do
|
|
58
|
+
[[ ! -e "$evidence_dir/$sealed_evidence" ]] || \
|
|
59
|
+
automation_die "$sealed_evidence already exists; baseline-only recovery is forbidden"
|
|
60
|
+
done
|
|
61
|
+
|
|
62
|
+
state_revision="$(jq -er '.revision' "$state_file")"
|
|
63
|
+
if ! jq -s -e --argjson revision "$state_revision" '
|
|
64
|
+
length == $revision and
|
|
65
|
+
length >= 2 and
|
|
66
|
+
.[-2].from == "PENDING" and
|
|
67
|
+
.[-2].to == "CODING" and
|
|
68
|
+
.[-2].actor == "coder-launcher" and
|
|
69
|
+
.[-2].note == "preflight passed; capturing baseline" and
|
|
70
|
+
.[-1].from == "CODING" and
|
|
71
|
+
.[-1].to == "BLOCKED" and
|
|
72
|
+
(.[-1].actor == "coder" or .[-1].actor == "preflight" or .[-1].actor == "orchestrator") and
|
|
73
|
+
(.[-1].note | test("baseline|claim|基线"; "i"))
|
|
74
|
+
' "$transitions_file" >/dev/null; then
|
|
75
|
+
automation_die "BLOCKED state is not a recoverable baseline-capture interruption"
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
automation_assert_planning_artifacts_sealed "$task_id" "$task_root"
|
|
79
|
+
changed_paths=()
|
|
80
|
+
while IFS= read -r path; do
|
|
81
|
+
[[ -n "$path" ]] && changed_paths+=("$path")
|
|
82
|
+
done < <(automation_changed_paths_at "$task_root")
|
|
83
|
+
[[ "${#changed_paths[@]}" -eq 2 ]] || automation_die "baseline recovery requires exactly the sealed plan and contract changes"
|
|
84
|
+
for path in "${changed_paths[@]}"; do
|
|
85
|
+
automation_is_planning_artifact "$task_id" "$path" || automation_die "product or unrelated change blocks baseline recovery: $path"
|
|
86
|
+
if git -C "$task_root" ls-files --error-unmatch -- "$path" >/dev/null 2>&1; then
|
|
87
|
+
automation_die "planning artifact became tracked before baseline recovery: $path"
|
|
88
|
+
fi
|
|
89
|
+
done
|
|
90
|
+
|
|
91
|
+
restart_count=0
|
|
92
|
+
if [[ -f "$resumptions_file" ]]; then
|
|
93
|
+
restart_count="$(jq -s --arg taskId "$task_id" '[.[] | select(.taskId == $taskId)] | length' "$resumptions_file")"
|
|
94
|
+
fi
|
|
95
|
+
max_restarts=1
|
|
96
|
+
[[ "$restart_count" -lt "$max_restarts" ]] || automation_die "baseline restart limit exhausted"
|
|
97
|
+
next_restart=$((restart_count + 1))
|
|
98
|
+
blocked_revision="$state_revision"
|
|
99
|
+
current_diff_sha="$(automation_worktree_diff_sha "$task_root")"
|
|
100
|
+
|
|
101
|
+
jq -nc \
|
|
102
|
+
--arg taskId "$task_id" \
|
|
103
|
+
--arg kind "resume-baseline" \
|
|
104
|
+
--arg at "$(automation_now)" \
|
|
105
|
+
--arg diffSha256 "$current_diff_sha" \
|
|
106
|
+
--argjson blockedRevision "$blocked_revision" \
|
|
107
|
+
'{taskId: $taskId, kind: $kind, at: $at,
|
|
108
|
+
blockedRevision: $blockedRevision, diffSha256: $diffSha256}' \
|
|
109
|
+
| automation_append_json "$(automation_approvals_path "$task_id")"
|
|
110
|
+
|
|
111
|
+
automation_transition_state \
|
|
112
|
+
"$task_id" \
|
|
113
|
+
"BLOCKED" \
|
|
114
|
+
"PENDING" \
|
|
115
|
+
"human" \
|
|
116
|
+
"baseline-only recovery $next_restart/$max_restarts; no product diff and baseline evidence absent"
|
|
117
|
+
|
|
118
|
+
jq -nc \
|
|
119
|
+
--arg taskId "$task_id" \
|
|
120
|
+
--arg at "$(automation_now)" \
|
|
121
|
+
--arg diffSha256 "$current_diff_sha" \
|
|
122
|
+
--argjson blockedRevision "$blocked_revision" \
|
|
123
|
+
--argjson restart "$next_restart" \
|
|
124
|
+
--argjson maxRestarts "$max_restarts" \
|
|
125
|
+
'{taskId: $taskId, at: $at, blockedRevision: $blockedRevision,
|
|
126
|
+
diffSha256: $diffSha256, restart: $restart,
|
|
127
|
+
maxRestarts: $maxRestarts, route: "BLOCKED->PENDING",
|
|
128
|
+
baselineEvidencePresent: false, productDiffPresent: false}' \
|
|
129
|
+
| automation_append_json "$resumptions_file"
|
|
130
|
+
|
|
131
|
+
automation_info "$task_id baseline-only recovery accepted; restarting deterministic claim flow"
|
|
132
|
+
automation_release_run_lock
|
|
133
|
+
trap - EXIT
|
|
134
|
+
|
|
135
|
+
"$SCRIPT_DIR/orchestrate-task.sh" "$task_id"
|
|
@@ -119,13 +119,15 @@ jq -n '{
|
|
|
119
119
|
maxFixLoops: 1,
|
|
120
120
|
maxReviewCycles: 1,
|
|
121
121
|
maxReviewerRestarts: 2,
|
|
122
|
+
longCommandTimeoutMs: 1800000,
|
|
122
123
|
autoCleanupWorktrees: true,
|
|
123
124
|
pushAfterAcceptance: false,
|
|
124
125
|
approvalPhrases: {
|
|
125
126
|
proposal: "批准方案,生成计划和任务合同。",
|
|
126
127
|
contract: "合同已复核,批准自动执行到人工验收阶段。",
|
|
127
128
|
acceptance: "验收通过,提交到原分支。",
|
|
128
|
-
abort: "中止任务,封存修改并恢复原分支。"
|
|
129
|
+
abort: "中止任务,封存修改并恢复原分支。",
|
|
130
|
+
resume: "恢复任务,重新捕获基线并继续自动执行。"
|
|
129
131
|
},
|
|
130
132
|
plugins: {
|
|
131
133
|
superpowers: "superpowers@git+https://github.com/obra/superpowers.git#v6.2.0"
|
|
@@ -483,9 +485,12 @@ fi
|
|
|
483
485
|
if run_fixture env AUTOMATION_SKIP_AGENT_RUN=1 ./scripts/automation/resume-review.sh TASK-TEST-003 >/dev/null 2>&1; then
|
|
484
486
|
fail 'reviewer-only recovery accepted a non-review blocker'
|
|
485
487
|
fi
|
|
488
|
+
if run_fixture env AUTOMATION_SKIP_AGENT_RUN=1 ./scripts/automation/resume-task.sh TASK-TEST-003 '恢复任务,重新捕获基线并继续自动执行。' >/dev/null 2>&1; then
|
|
489
|
+
fail 'baseline-only recovery accepted a preflight blocker'
|
|
490
|
+
fi
|
|
486
491
|
rm "$fixture/unapproved-change.txt"
|
|
487
492
|
pass 'preflight blocks dirty task roots without retrying implicitly'
|
|
488
|
-
pass 'reviewer
|
|
493
|
+
pass 'reviewer and baseline recovery reject unrelated BLOCKED states'
|
|
489
494
|
|
|
490
495
|
printf '%s\n' '# Approved reviewer-fix plan' > "$fixture/docs/plans/TASK-TEST-005.md"
|
|
491
496
|
jq \
|
|
@@ -745,6 +750,38 @@ run_fixture ./scripts/automation/abort-task.sh TASK-TEST-009 '中止任务,封
|
|
|
745
750
|
[[ ! -e "$fixture/docs/plans/TASK-TEST-009.md" && ! -e "$fixture/automation/tasks/TASK-TEST-009.json" ]] || fail 'planning-only abort left generated artifacts in the original worktree'
|
|
746
751
|
pass 'aborting before product edits archives planning artifacts without creating a planning-only commit'
|
|
747
752
|
|
|
753
|
+
printf '%s\n' '# Baseline interruption recovery plan' > "$fixture/docs/plans/TASK-TEST-010.md"
|
|
754
|
+
jq \
|
|
755
|
+
'.id = "TASK-TEST-010" |
|
|
756
|
+
.title = "Recover one interrupted baseline capture" |
|
|
757
|
+
.planPath = "docs/plans/TASK-TEST-010.md" |
|
|
758
|
+
.targetTests = [{gradleTask: "testDebugUnitTest", filter: "dev.example.orchestratorfixture.BaselineRecoveryTest"}]' \
|
|
759
|
+
"$fixture/automation/tasks/TASK-TEST-001.json" \
|
|
760
|
+
> "$fixture/automation/tasks/TASK-TEST-010.json"
|
|
761
|
+
run_fixture ./scripts/automation/prepare-contract-review.sh TASK-TEST-010 '批准方案,生成计划和任务合同。' >/dev/null
|
|
762
|
+
run_fixture env AUTOMATION_SKIP_AGENT_RUN=1 ./scripts/automation/approve-and-run.sh TASK-TEST-010 '合同已复核,批准自动执行到人工验收阶段。' >/dev/null
|
|
763
|
+
run_fixture ./scripts/automation/transition-state.sh TASK-TEST-010 PENDING CODING coder-launcher 'preflight passed; capturing baseline' >/dev/null
|
|
764
|
+
run_fixture ./scripts/automation/block-task.sh TASK-TEST-010 'claim baseline capture interrupted before metadata sealing' >/dev/null
|
|
765
|
+
if run_fixture env AUTOMATION_SKIP_AGENT_RUN=1 ./scripts/automation/resume-task.sh TASK-TEST-010 '拒绝' >/dev/null 2>&1; then
|
|
766
|
+
fail 'baseline recovery accepted an invalid confirmation'
|
|
767
|
+
fi
|
|
768
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-010.json")" == "BLOCKED" ]] || fail 'rejected baseline recovery changed task state'
|
|
769
|
+
run_fixture env AUTOMATION_SKIP_AGENT_RUN=1 ./scripts/automation/resume-task.sh TASK-TEST-010 '恢复任务,重新捕获基线并继续自动执行。' >/dev/null
|
|
770
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-010.json")" == "PENDING" ]] || fail 'baseline recovery did not return to PENDING'
|
|
771
|
+
[[ "$(jq -s 'length' "$runtime_root/evidence/TASK-TEST-010/baseline-resumptions.jsonl")" -eq 1 ]] || fail 'baseline recovery did not record one bounded resumption'
|
|
772
|
+
[[ "$(jq -s -r '.[-1].kind' "$runtime_root/evidence/TASK-TEST-010/approvals.jsonl")" == "resume-baseline" ]] || fail 'baseline recovery approval was not audited'
|
|
773
|
+
[[ ! -f "$runtime_root/evidence/TASK-TEST-010/baseline.json" ]] || fail 'baseline recovery manufactured baseline evidence'
|
|
774
|
+
pass 'one explicitly approved baseline interruption resumes through PENDING without product changes'
|
|
775
|
+
|
|
776
|
+
run_fixture ./scripts/automation/transition-state.sh TASK-TEST-010 PENDING CODING coder-launcher 'preflight passed; capturing baseline' >/dev/null
|
|
777
|
+
run_fixture ./scripts/automation/block-task.sh TASK-TEST-010 'claim baseline capture interrupted a second time before metadata sealing' >/dev/null
|
|
778
|
+
if run_fixture env AUTOMATION_SKIP_AGENT_RUN=1 ./scripts/automation/resume-task.sh TASK-TEST-010 '恢复任务,重新捕获基线并继续自动执行。' >/dev/null 2>&1; then
|
|
779
|
+
fail 'baseline recovery exceeded its one-restart limit'
|
|
780
|
+
fi
|
|
781
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-010.json")" == "BLOCKED" ]] || fail 'exhausted baseline recovery changed task state'
|
|
782
|
+
pass 'baseline recovery stops after one auditable restart'
|
|
783
|
+
run_fixture ./scripts/automation/abort-task.sh TASK-TEST-010 '中止任务,封存修改并恢复原分支。' >/dev/null
|
|
784
|
+
|
|
748
785
|
isolated_config_tmp="$(mktemp "$fixture/automation/.config.XXXXXX")"
|
|
749
786
|
jq '.workspaceStrategy = "isolatedWorktree"' "$fixture/automation/config.json" > "$isolated_config_tmp"
|
|
750
787
|
mv "$isolated_config_tmp" "$fixture/automation/config.json"
|