@frankzhang2026/opencode-android-orchestrator 0.1.0 → 0.3.0
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 +92 -2
- package/LICENSE +21 -0
- package/README.md +402 -6
- package/THIRD_PARTY_NOTICES.md +119 -0
- package/dist/cli.js +360 -1
- package/dist/cli.js.map +1 -1
- package/dist/compatibility/hooks.d.ts +23 -0
- package/dist/compatibility/hooks.d.ts.map +1 -0
- package/dist/compatibility/hooks.js +44 -0
- package/dist/compatibility/hooks.js.map +1 -0
- package/dist/compatibility/versions.d.ts +19 -0
- package/dist/compatibility/versions.d.ts.map +1 -1
- package/dist/compatibility/versions.js +116 -0
- package/dist/compatibility/versions.js.map +1 -1
- package/dist/doctor/index.d.ts +36 -0
- package/dist/doctor/index.d.ts.map +1 -0
- package/dist/doctor/index.js +289 -0
- package/dist/doctor/index.js.map +1 -0
- package/dist/doctor/installation.d.ts +4 -0
- package/dist/doctor/installation.d.ts.map +1 -0
- package/dist/doctor/installation.js +497 -0
- package/dist/doctor/installation.js.map +1 -0
- package/dist/index.d.ts +13 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -2
- package/dist/index.js.map +1 -1
- package/dist/installer/adaptive-templates.d.ts +78 -0
- package/dist/installer/adaptive-templates.d.ts.map +1 -0
- package/dist/installer/adaptive-templates.js +233 -0
- package/dist/installer/adaptive-templates.js.map +1 -0
- package/dist/installer/agents-config.d.ts +19 -0
- package/dist/installer/agents-config.d.ts.map +1 -0
- package/dist/installer/agents-config.js +147 -0
- package/dist/installer/agents-config.js.map +1 -0
- package/dist/installer/android-project.d.ts +36 -0
- package/dist/installer/android-project.d.ts.map +1 -0
- package/dist/installer/android-project.js +418 -0
- package/dist/installer/android-project.js.map +1 -0
- package/dist/installer/index.d.ts +8 -0
- package/dist/installer/index.d.ts.map +1 -1
- package/dist/installer/index.js +8 -1
- package/dist/installer/index.js.map +1 -1
- package/dist/installer/init.d.ts +68 -0
- package/dist/installer/init.d.ts.map +1 -0
- package/dist/installer/init.js +398 -0
- package/dist/installer/init.js.map +1 -0
- package/dist/installer/install-manifest.d.ts +132 -0
- package/dist/installer/install-manifest.d.ts.map +1 -0
- package/dist/installer/install-manifest.js +1047 -0
- package/dist/installer/install-manifest.js.map +1 -0
- package/dist/installer/opencode-config.d.ts +32 -0
- package/dist/installer/opencode-config.d.ts.map +1 -0
- package/dist/installer/opencode-config.js +302 -0
- package/dist/installer/opencode-config.js.map +1 -0
- package/dist/installer/uninstall.d.ts +70 -0
- package/dist/installer/uninstall.d.ts.map +1 -0
- package/dist/installer/uninstall.js +770 -0
- package/dist/installer/uninstall.js.map +1 -0
- package/dist/installer/upgrade.d.ts +101 -0
- package/dist/installer/upgrade.d.ts.map +1 -0
- package/dist/installer/upgrade.js +1140 -0
- package/dist/installer/upgrade.js.map +1 -0
- package/dist/opencode-plugin.d.ts +2 -0
- package/dist/opencode-plugin.d.ts.map +1 -0
- package/dist/opencode-plugin.js +4 -0
- package/dist/opencode-plugin.js.map +1 -0
- package/dist/plugin/index.d.ts +7 -2
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +23 -6
- package/dist/plugin/index.js.map +1 -1
- package/dist/tools/index.d.ts +35 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +197 -1
- package/dist/tools/index.js.map +1 -1
- package/docs/MIGRATION.md +184 -0
- package/docs/SECURITY.md +219 -0
- package/docs/TROUBLESHOOTING.md +171 -0
- package/package.json +23 -5
- package/templates/.opencode/agents/scheduled-coder.md +115 -0
- package/templates/.opencode/agents/scheduled-planner.md +166 -0
- package/templates/.opencode/agents/scheduled-reviewer.md +88 -0
- package/templates/.opencode/commands/abort-task.md +20 -0
- package/templates/.opencode/commands/acceptance.md +20 -0
- package/templates/.opencode/commands/change.md +23 -0
- package/templates/.opencode/commands/resume-review.md +19 -0
- package/templates/.opencode/skills/scheduled-quality-coder/SKILL.md +93 -0
- package/templates/.opencode/skills/scheduled-quality-orchestrator/SKILL.md +216 -0
- package/templates/.opencode/skills/scheduled-quality-reviewer/SKILL.md +68 -0
- package/templates/AGENTS.md.fragment +19 -0
- package/templates/README.md +55 -9
- package/templates/automation/config.json +67 -0
- package/templates/automation/config.schema.json +194 -0
- package/templates/automation/task-contract.schema.json +92 -0
- package/templates/automation/tasks/TASK-TEMPLATE.json.example +56 -0
- package/templates/docs/plans/README.md +26 -0
- package/templates/installation-manifest.schema.json +201 -0
- package/templates/scripts/automation/abort-task.sh +171 -0
- package/templates/scripts/automation/accept-and-integrate.sh +263 -0
- package/templates/scripts/automation/acceptance-report.sh +132 -0
- package/templates/scripts/automation/approve-and-run.sh +175 -0
- package/templates/scripts/automation/begin-review.sh +18 -0
- package/templates/scripts/automation/block-task.sh +26 -0
- package/templates/scripts/automation/claim-task.sh +74 -0
- package/templates/scripts/automation/integration-scope-gate.sh +103 -0
- package/templates/scripts/automation/lib.sh +815 -0
- package/templates/scripts/automation/orchestrate-task.sh +119 -0
- package/templates/scripts/automation/preflight.sh +206 -0
- package/templates/scripts/automation/prepare-contract-review.sh +101 -0
- package/templates/scripts/automation/quality-gate.sh +72 -0
- package/templates/scripts/automation/queue-task.sh +35 -0
- package/templates/scripts/automation/record-red.sh +55 -0
- package/templates/scripts/automation/resume-review-fix.sh +32 -0
- package/templates/scripts/automation/resume-review.sh +120 -0
- package/templates/scripts/automation/scope-gate.sh +110 -0
- package/templates/scripts/automation/select-task.sh +34 -0
- package/templates/scripts/automation/shadow-run.sh +24 -0
- package/templates/scripts/automation/show-acceptance-review.sh +111 -0
- package/templates/scripts/automation/status.sh +125 -0
- package/templates/scripts/automation/submit-review.sh +73 -0
- package/templates/scripts/automation/tests/run-tests.sh +716 -0
- package/templates/scripts/automation/transition-state.sh +21 -0
- package/templates/scripts/automation/validate-contract.sh +98 -0
- package/templates/scripts/automation/verify-integration.sh +38 -0
- package/templates/scripts/automation/verify-task.sh +39 -0
|
@@ -0,0 +1,716 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
TEST_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
5
|
+
SOURCE_SCRIPT_DIR="$(cd "$TEST_DIR/.." && pwd)"
|
|
6
|
+
fixture="$(mktemp -d "${TMPDIR:-/tmp}/scheduled-quality-gate-tests.XXXXXX")"
|
|
7
|
+
runtime_root="$(mktemp -d "${TMPDIR:-/tmp}/scheduled-quality-runtime.XXXXXX")"
|
|
8
|
+
worktree_base="$(mktemp -d "${TMPDIR:-/tmp}/scheduled-quality-worktrees.XXXXXX")"
|
|
9
|
+
fixture="$(cd "$fixture" && pwd -P)"
|
|
10
|
+
runtime_root="$(cd "$runtime_root" && pwd -P)"
|
|
11
|
+
worktree_base="$(cd "$worktree_base" && pwd -P)"
|
|
12
|
+
|
|
13
|
+
cleanup() {
|
|
14
|
+
if [[ -d "$fixture/.git" ]]; then
|
|
15
|
+
while IFS= read -r path; do
|
|
16
|
+
[[ -n "$path" && "$path" != "$fixture" ]] || continue
|
|
17
|
+
git -C "$fixture" worktree remove --force "$path" >/dev/null 2>&1 || true
|
|
18
|
+
done < <(git -C "$fixture" worktree list --porcelain 2>/dev/null | awk '/^worktree / { sub(/^worktree /, ""); print }')
|
|
19
|
+
fi
|
|
20
|
+
rm -rf "$fixture" "$runtime_root" "$worktree_base"
|
|
21
|
+
}
|
|
22
|
+
trap cleanup EXIT
|
|
23
|
+
|
|
24
|
+
pass_count=0
|
|
25
|
+
|
|
26
|
+
pass() {
|
|
27
|
+
pass_count=$((pass_count + 1))
|
|
28
|
+
printf 'ok %d - %s\n' "$pass_count" "$1"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
fail() {
|
|
32
|
+
printf 'not ok - %s\n' "$1" >&2
|
|
33
|
+
exit 1
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
run_fixture() {
|
|
37
|
+
(
|
|
38
|
+
cd "$fixture"
|
|
39
|
+
unset AUTOMATION_PROJECT_ROOT
|
|
40
|
+
export AUTOMATION_TEST_MODE=1
|
|
41
|
+
export AUTOMATION_RUNTIME_ROOT="$runtime_root"
|
|
42
|
+
export AUTOMATION_WORKTREE_BASE="$worktree_base"
|
|
43
|
+
"$@"
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
run_task() {
|
|
48
|
+
local task_root="$1"
|
|
49
|
+
shift
|
|
50
|
+
(
|
|
51
|
+
cd "$task_root"
|
|
52
|
+
unset AUTOMATION_PROJECT_ROOT
|
|
53
|
+
export AUTOMATION_TEST_MODE=1
|
|
54
|
+
export AUTOMATION_RUNTIME_ROOT="$runtime_root"
|
|
55
|
+
export AUTOMATION_WORKTREE_BASE="$worktree_base"
|
|
56
|
+
"$@"
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
write_workspace() {
|
|
61
|
+
local task_id="$1"
|
|
62
|
+
local head branch
|
|
63
|
+
head="$(git -C "$fixture" rev-parse HEAD)"
|
|
64
|
+
branch="$(git -C "$fixture" symbolic-ref --short HEAD)"
|
|
65
|
+
mkdir -p "$runtime_root/workspaces"
|
|
66
|
+
jq -n \
|
|
67
|
+
--arg taskId "$task_id" \
|
|
68
|
+
--arg sourceRoot "$fixture" \
|
|
69
|
+
--arg originalBranch "$branch" \
|
|
70
|
+
--arg baselineHead "$head" \
|
|
71
|
+
--arg taskRoot "$fixture" \
|
|
72
|
+
--arg taskBranch "$branch" \
|
|
73
|
+
--arg worktreeBase "$worktree_base" \
|
|
74
|
+
'{taskId: $taskId, sourceRoot: $sourceRoot,
|
|
75
|
+
originalBranch: $originalBranch, baselineHead: $baselineHead,
|
|
76
|
+
workspaceStrategy: "inPlaceExclusive", taskRoot: $taskRoot,
|
|
77
|
+
repositoryLeaseRequired: false, taskBranch: $taskBranch,
|
|
78
|
+
worktreeBase: $worktreeBase, codingCycle: 0, reviewCycles: 0}' \
|
|
79
|
+
> "$runtime_root/workspaces/$task_id.json"
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
mkdir -p \
|
|
83
|
+
"$fixture/automation/tasks" \
|
|
84
|
+
"$fixture/docs/plans" \
|
|
85
|
+
"$fixture/scripts/automation" \
|
|
86
|
+
"$fixture/mobile-client/src/main/java/dev/example/orchestratorfixture" \
|
|
87
|
+
"$fixture/mobile-client/src/test/java/dev/example/orchestratorfixture"
|
|
88
|
+
|
|
89
|
+
cp "$SOURCE_SCRIPT_DIR"/*.sh "$fixture/scripts/automation/"
|
|
90
|
+
chmod +x "$fixture/scripts/automation/"*.sh
|
|
91
|
+
printf '%s\n' '# Approved test plan' > "$fixture/docs/plans/TASK-TEST-001.md"
|
|
92
|
+
printf '%s\n' 'class ExistingFeature' > "$fixture/mobile-client/src/main/java/dev/example/orchestratorfixture/ExistingFeature.kt"
|
|
93
|
+
|
|
94
|
+
printf '%s\n' \
|
|
95
|
+
'#!/usr/bin/env bash' \
|
|
96
|
+
'set -euo pipefail' \
|
|
97
|
+
'task="${1:-}"' \
|
|
98
|
+
'if [[ "$task" == "testDebugUnitTest" && "${2:-}" == "--tests" && "${AUTOMATION_FAKE_GREEN:-0}" != "1" ]]; then' \
|
|
99
|
+
' printf "%s\n" "expected missing behavior"' \
|
|
100
|
+
' exit 1' \
|
|
101
|
+
'fi' \
|
|
102
|
+
'case "$task" in' \
|
|
103
|
+
' testDebugUnitTest|assembleDebug|lint|connectedDebugAndroidTest) printf "%s\n" "BUILD SUCCESSFUL" ;;' \
|
|
104
|
+
' *) printf "%s\n" "unexpected Gradle task: $task" >&2; exit 9 ;;' \
|
|
105
|
+
'esac' \
|
|
106
|
+
> "$fixture/gradlew"
|
|
107
|
+
chmod +x "$fixture/gradlew"
|
|
108
|
+
|
|
109
|
+
jq -n '{
|
|
110
|
+
schemaVersion: 3,
|
|
111
|
+
enabled: true,
|
|
112
|
+
mode: "orchestrated",
|
|
113
|
+
workspaceStrategy: "inPlaceExclusive",
|
|
114
|
+
originalBranchDriftPolicy: "block",
|
|
115
|
+
worktreeBase: "",
|
|
116
|
+
maxFixLoops: 1,
|
|
117
|
+
maxReviewCycles: 1,
|
|
118
|
+
maxReviewerRestarts: 2,
|
|
119
|
+
autoCleanupWorktrees: true,
|
|
120
|
+
pushAfterAcceptance: false,
|
|
121
|
+
approvalPhrases: {
|
|
122
|
+
proposal: "批准方案,生成计划和任务合同。",
|
|
123
|
+
contract: "合同已复核,批准自动执行到人工验收阶段。",
|
|
124
|
+
acceptance: "验收通过,提交到原分支。",
|
|
125
|
+
abort: "中止任务,封存修改并恢复原分支。"
|
|
126
|
+
},
|
|
127
|
+
plugins: {
|
|
128
|
+
superpowers: "superpowers@git+https://github.com/obra/superpowers.git#v6.2.0"
|
|
129
|
+
},
|
|
130
|
+
requiredSkills: [
|
|
131
|
+
"brainstorming",
|
|
132
|
+
"writing-plans",
|
|
133
|
+
"scheduled-quality-orchestrator",
|
|
134
|
+
"scheduled-quality-coder",
|
|
135
|
+
"scheduled-quality-reviewer",
|
|
136
|
+
"test-driven-development",
|
|
137
|
+
"systematic-debugging",
|
|
138
|
+
"verification-before-completion"
|
|
139
|
+
],
|
|
140
|
+
gradleVerification: {
|
|
141
|
+
fullUnitTestTasks: ["testDebugUnitTest"],
|
|
142
|
+
focusedTestTasks: ["testDebugUnitTest"],
|
|
143
|
+
assembleTasks: ["assembleDebug"],
|
|
144
|
+
lintTasks: ["lint"],
|
|
145
|
+
deviceTestTasks: ["connectedDebugAndroidTest"]
|
|
146
|
+
},
|
|
147
|
+
androidProject: {
|
|
148
|
+
name: "automation-shell-fixture",
|
|
149
|
+
gradleDsl: "kotlin",
|
|
150
|
+
settingsFile: "settings.gradle.kts",
|
|
151
|
+
moduleScope: "all",
|
|
152
|
+
primaryModule: ":mobile-client",
|
|
153
|
+
modules: [{
|
|
154
|
+
gradlePath: ":mobile-client",
|
|
155
|
+
directory: "mobile-client",
|
|
156
|
+
buildFile: "mobile-client/build.gradle.kts",
|
|
157
|
+
dsl: "kotlin",
|
|
158
|
+
type: "application",
|
|
159
|
+
namespace: "dev.example.orchestratorfixture",
|
|
160
|
+
applicationId: "dev.example.orchestratorfixture"
|
|
161
|
+
}],
|
|
162
|
+
productionPaths: ["mobile-client/src/main/**"],
|
|
163
|
+
testPaths: [
|
|
164
|
+
"mobile-client/src/test/**",
|
|
165
|
+
"mobile-client/src/androidTest/**"
|
|
166
|
+
]
|
|
167
|
+
},
|
|
168
|
+
protectedPaths: [
|
|
169
|
+
".opencode/",
|
|
170
|
+
".automation-plugin/",
|
|
171
|
+
"automation/",
|
|
172
|
+
"scripts/automation/",
|
|
173
|
+
"opencode.json",
|
|
174
|
+
"opencode.jsonc",
|
|
175
|
+
"AGENTS.md",
|
|
176
|
+
"gradle/",
|
|
177
|
+
"gradlew",
|
|
178
|
+
"gradlew.bat",
|
|
179
|
+
"settings.gradle",
|
|
180
|
+
"settings.gradle.kts",
|
|
181
|
+
"build.gradle",
|
|
182
|
+
"build.gradle.kts",
|
|
183
|
+
"**/build.gradle",
|
|
184
|
+
"**/build.gradle.kts",
|
|
185
|
+
"mobile-client/build.gradle.kts"
|
|
186
|
+
]
|
|
187
|
+
}' > "$fixture/automation/config.json"
|
|
188
|
+
|
|
189
|
+
jq -n '{
|
|
190
|
+
schemaVersion: 1,
|
|
191
|
+
id: "TASK-TEST-001",
|
|
192
|
+
title: "Add an observable greeting behavior",
|
|
193
|
+
designApproved: true,
|
|
194
|
+
planPath: "docs/plans/TASK-TEST-001.md",
|
|
195
|
+
ambiguityPolicy: "BLOCKED",
|
|
196
|
+
maxFixLoops: 1,
|
|
197
|
+
maxChangedFiles: 4,
|
|
198
|
+
allowedPaths: [
|
|
199
|
+
"mobile-client/src/main/java/dev/example/orchestratorfixture/**",
|
|
200
|
+
"mobile-client/src/test/java/dev/example/orchestratorfixture/**"
|
|
201
|
+
],
|
|
202
|
+
forbiddenPaths: [
|
|
203
|
+
".opencode/**",
|
|
204
|
+
".automation-plugin/**",
|
|
205
|
+
"automation/**",
|
|
206
|
+
"scripts/automation/**",
|
|
207
|
+
"opencode.json",
|
|
208
|
+
"opencode.jsonc",
|
|
209
|
+
"AGENTS.md",
|
|
210
|
+
"gradle/**",
|
|
211
|
+
"gradlew",
|
|
212
|
+
"gradlew.bat",
|
|
213
|
+
"settings.gradle",
|
|
214
|
+
"settings.gradle.kts",
|
|
215
|
+
"build.gradle",
|
|
216
|
+
"build.gradle.kts",
|
|
217
|
+
"**/build.gradle",
|
|
218
|
+
"**/build.gradle.kts",
|
|
219
|
+
"mobile-client/build.gradle.kts"
|
|
220
|
+
],
|
|
221
|
+
allowedSuperpowers: [
|
|
222
|
+
"test-driven-development",
|
|
223
|
+
"systematic-debugging",
|
|
224
|
+
"verification-before-completion"
|
|
225
|
+
],
|
|
226
|
+
acceptanceCriteria: ["Greeting returns the approved value"],
|
|
227
|
+
nonGoals: ["No unrelated refactoring"],
|
|
228
|
+
targetTests: [{
|
|
229
|
+
gradleTask: "testDebugUnitTest",
|
|
230
|
+
filter: "dev.example.orchestratorfixture.GreetingTest"
|
|
231
|
+
}],
|
|
232
|
+
deviceTestsRequired: false,
|
|
233
|
+
testPolicy: "required",
|
|
234
|
+
testPolicyReason: "The behavior requires a focused regression test"
|
|
235
|
+
}' > "$fixture/automation/tasks/TASK-TEST-001.json"
|
|
236
|
+
|
|
237
|
+
(
|
|
238
|
+
cd "$fixture"
|
|
239
|
+
git init -q
|
|
240
|
+
git config user.email 'automation-tests@example.invalid'
|
|
241
|
+
git config user.name 'Automation Tests'
|
|
242
|
+
git add .
|
|
243
|
+
git commit -qm 'Create automation fixture'
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
run_fixture ./scripts/automation/validate-contract.sh TASK-TEST-001 >/dev/null
|
|
247
|
+
pass 'valid version-3 configuration and task contract are accepted'
|
|
248
|
+
|
|
249
|
+
if run_fixture env AUTOMATION_HUMAN_APPROVED=0 ./scripts/automation/queue-task.sh TASK-TEST-001 >/dev/null 2>&1; then
|
|
250
|
+
fail 'legacy queue accepted without explicit human approval'
|
|
251
|
+
fi
|
|
252
|
+
pass 'legacy queue rejects missing human approval'
|
|
253
|
+
|
|
254
|
+
run_fixture env AUTOMATION_HUMAN_APPROVED=1 ./scripts/automation/queue-task.sh TASK-TEST-001 >/dev/null
|
|
255
|
+
write_workspace TASK-TEST-001
|
|
256
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-001.json")" == "PENDING" ]] || fail 'queue did not create PENDING state'
|
|
257
|
+
pass 'explicit queue creates one auditable PENDING state'
|
|
258
|
+
|
|
259
|
+
[[ "$(run_fixture ./scripts/automation/select-task.sh PENDING)" == "TASK-TEST-001" ]] || fail 'selector did not return the only pending task'
|
|
260
|
+
pass 'selector returns exactly one eligible task'
|
|
261
|
+
|
|
262
|
+
jq '.taskId = "TASK-TEST-999"' \
|
|
263
|
+
"$runtime_root/state/TASK-TEST-001.json" \
|
|
264
|
+
> "$runtime_root/state/TASK-TEST-999.json"
|
|
265
|
+
if run_fixture ./scripts/automation/select-task.sh PENDING >/dev/null 2>&1; then
|
|
266
|
+
fail 'selector chose among multiple pending tasks'
|
|
267
|
+
fi
|
|
268
|
+
rm "$runtime_root/state/TASK-TEST-999.json"
|
|
269
|
+
pass 'selector refuses to choose among multiple eligible tasks'
|
|
270
|
+
|
|
271
|
+
mkdir -p "$runtime_root/locks/TASK-TEST-001.state.lock"
|
|
272
|
+
printf '%s\n' '99999999' > "$runtime_root/locks/TASK-TEST-001.state.lock/pid"
|
|
273
|
+
if run_fixture ./scripts/automation/transition-state.sh TASK-TEST-001 PENDING READY_FOR_REVIEW human invalid >/dev/null 2>&1; then
|
|
274
|
+
fail 'illegal transition was accepted'
|
|
275
|
+
fi
|
|
276
|
+
[[ ! -d "$runtime_root/locks/TASK-TEST-001.state.lock" ]] || fail 'stale state lock was not recovered'
|
|
277
|
+
pass 'stopped-process locks are recovered without bypassing state rules'
|
|
278
|
+
|
|
279
|
+
if run_fixture ./scripts/automation/transition-state.sh TASK-TEST-001 PENDING READY_FOR_REVIEW human invalid >/dev/null 2>&1; then
|
|
280
|
+
fail 'illegal transition was accepted after lock recovery'
|
|
281
|
+
fi
|
|
282
|
+
pass 'illegal state transition is rejected'
|
|
283
|
+
|
|
284
|
+
run_fixture ./scripts/automation/claim-task.sh TASK-TEST-001 >/dev/null
|
|
285
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-001.json")" == "CODING" ]] || fail 'claim did not create CODING state'
|
|
286
|
+
[[ -f "$runtime_root/evidence/TASK-TEST-001/baseline.json" ]] || fail 'baseline metadata missing'
|
|
287
|
+
jq -e \
|
|
288
|
+
'.command == ["./gradlew", "testDebugUnitTest"]' \
|
|
289
|
+
"$runtime_root/evidence/TASK-TEST-001/baseline.json" >/dev/null || fail 'baseline command does not match the configured task matrix'
|
|
290
|
+
pass 'claim verifies workspace identity and records a green baseline'
|
|
291
|
+
|
|
292
|
+
printf '%s\n' 'class GreetingTest { fun expectedBehavior() = Unit }' > "$fixture/mobile-client/src/test/java/dev/example/orchestratorfixture/GreetingTest.kt"
|
|
293
|
+
run_fixture ./scripts/automation/record-red.sh TASK-TEST-001 'expected missing behavior' -- dev.example.orchestratorfixture.GreetingTest >/dev/null
|
|
294
|
+
[[ "$(jq -r '.exitCode' "$runtime_root/evidence/TASK-TEST-001/red.json")" -ne 0 ]] || fail 'RED evidence exit code was not captured'
|
|
295
|
+
jq -e \
|
|
296
|
+
'.command == ["./gradlew", "testDebugUnitTest", "--tests", "dev.example.orchestratorfixture.GreetingTest"]' \
|
|
297
|
+
"$runtime_root/evidence/TASK-TEST-001/red.json" >/dev/null || fail 'RED command does not bind the configured task and filter'
|
|
298
|
+
pass 'focused failing test records genuine RED evidence'
|
|
299
|
+
|
|
300
|
+
printf '%s\n' 'unsafe' > "$fixture/automation/forbidden-change.txt"
|
|
301
|
+
if run_fixture ./scripts/automation/scope-gate.sh TASK-TEST-001 >/dev/null 2>&1; then
|
|
302
|
+
fail 'scope gate accepted protected path change'
|
|
303
|
+
fi
|
|
304
|
+
rm "$fixture/automation/forbidden-change.txt"
|
|
305
|
+
pass 'scope gate rejects protected path changes'
|
|
306
|
+
|
|
307
|
+
printf '%s\n' 'class GreetingTest { fun expectedBehavior() { assertTrue(true) } }' > "$fixture/mobile-client/src/test/java/dev/example/orchestratorfixture/GreetingTest.kt"
|
|
308
|
+
printf '%s\n' 'class Greeting { fun value() = "hello" }' > "$fixture/mobile-client/src/main/java/dev/example/orchestratorfixture/Greeting.kt"
|
|
309
|
+
if run_fixture ./scripts/automation/scope-gate.sh TASK-TEST-001 >/dev/null 2>&1; then
|
|
310
|
+
fail 'scope gate accepted weakened test'
|
|
311
|
+
fi
|
|
312
|
+
pass 'scope gate rejects obvious test weakening'
|
|
313
|
+
|
|
314
|
+
printf '%s\n' 'class GreetingTest { fun expectedBehavior() { check(Greeting().value() == "hello") } }' > "$fixture/mobile-client/src/test/java/dev/example/orchestratorfixture/GreetingTest.kt"
|
|
315
|
+
run_fixture env AUTOMATION_FAKE_GREEN=1 ./scripts/automation/quality-gate.sh TASK-TEST-001 >/dev/null
|
|
316
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-001.json")" == "READY_FOR_REVIEW" ]] || fail 'quality gate did not create READY_FOR_REVIEW state'
|
|
317
|
+
pass 'G1-G6 seal all tracked and untracked product changes'
|
|
318
|
+
|
|
319
|
+
run_fixture ./scripts/automation/begin-review.sh TASK-TEST-001 >/dev/null
|
|
320
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-001.json")" == "REVIEWING" ]] || fail 'review handoff did not create REVIEWING state'
|
|
321
|
+
review_status="$(run_fixture ./scripts/automation/status.sh TASK-TEST-001)"
|
|
322
|
+
[[ "$(jq -r '.evidence.red.exitCode' <<< "$review_status")" -ne 0 ]] || fail 'review status omitted RED evidence'
|
|
323
|
+
[[ "$(jq -r '.evidence.latestGate.lastExitCode' <<< "$review_status")" -eq 0 ]] || fail 'review status omitted gate evidence'
|
|
324
|
+
[[ "$(jq -r '.evidence.sealedDiffMatches' <<< "$review_status")" == "true" ]] || fail 'review status did not verify the sealed diff'
|
|
325
|
+
pass 'orchestrator creates an explicit sealed reviewer handoff'
|
|
326
|
+
|
|
327
|
+
run_fixture ./scripts/automation/transition-state.sh \
|
|
328
|
+
TASK-TEST-001 REVIEWING BLOCKED orchestrator \
|
|
329
|
+
'reviewer exited without submitting a decision' >/dev/null
|
|
330
|
+
sealed_sha_before_resume="$(jq -r '.diffSha256' "$runtime_root/evidence/TASK-TEST-001/ready.json")"
|
|
331
|
+
run_fixture env AUTOMATION_SKIP_AGENT_RUN=1 ./scripts/automation/resume-review.sh TASK-TEST-001 >/dev/null
|
|
332
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-001.json")" == "REVIEWING" ]] || fail 'reviewer-only recovery did not return directly to REVIEWING'
|
|
333
|
+
[[ "$(jq -r '.codingCycle' "$runtime_root/workspaces/TASK-TEST-001.json")" -eq 0 ]] || fail 'reviewer-only recovery consumed a coding cycle'
|
|
334
|
+
[[ "$(jq -r '.reviewCycles' "$runtime_root/workspaces/TASK-TEST-001.json")" -eq 0 ]] || fail 'reviewer-only recovery consumed a repair cycle'
|
|
335
|
+
[[ "$(jq -r '.diffSha256' "$runtime_root/evidence/TASK-TEST-001/ready.json")" == "$sealed_sha_before_resume" ]] || fail 'reviewer-only recovery changed ready evidence'
|
|
336
|
+
[[ "$(jq -r '.coderRerun' "$runtime_root/evidence/TASK-TEST-001/review-resumptions.jsonl")" == "false" ]] || fail 'reviewer-only recovery did not audit the coder bypass'
|
|
337
|
+
pass 'a budget-exhausted reviewer resumes from the sealed diff without rerunning Coder'
|
|
338
|
+
|
|
339
|
+
run_fixture env AUTOMATION_FAKE_GREEN=1 ./scripts/automation/submit-review.sh TASK-TEST-001 APPROVED 'Independent diff review found no material issue.' >/dev/null
|
|
340
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-001.json")" == "AWAITING_HUMAN" ]] || fail 'review did not create AWAITING_HUMAN state'
|
|
341
|
+
pass 'independent approval stops at AWAITING_HUMAN'
|
|
342
|
+
|
|
343
|
+
transition_count="$(wc -l < "$runtime_root/evidence/TASK-TEST-001/transitions.jsonl" | tr -d ' ')"
|
|
344
|
+
[[ "$transition_count" -eq 8 ]] || fail "unexpected transition audit count: $transition_count"
|
|
345
|
+
pass 'append-only transition audit contains the complete gated lifecycle'
|
|
346
|
+
|
|
347
|
+
(
|
|
348
|
+
cd "$fixture"
|
|
349
|
+
git add mobile-client
|
|
350
|
+
git commit -qm 'Preserve first fixture task'
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
printf '%s\n' '# Approved failure-loop plan' > "$fixture/docs/plans/TASK-TEST-002.md"
|
|
354
|
+
jq \
|
|
355
|
+
'.id = "TASK-TEST-002" |
|
|
356
|
+
.title = "Exercise the bounded verification failure loop" |
|
|
357
|
+
.planPath = "docs/plans/TASK-TEST-002.md" |
|
|
358
|
+
.targetTests = [{gradleTask: "testDebugUnitTest", filter: "dev.example.orchestratorfixture.FailureLoopTest"}]' \
|
|
359
|
+
"$fixture/automation/tasks/TASK-TEST-001.json" \
|
|
360
|
+
> "$fixture/automation/tasks/TASK-TEST-002.json"
|
|
361
|
+
(
|
|
362
|
+
cd "$fixture"
|
|
363
|
+
git add automation/tasks/TASK-TEST-002.json docs/plans/TASK-TEST-002.md
|
|
364
|
+
git commit -qm 'Add second approved fixture task'
|
|
365
|
+
)
|
|
366
|
+
run_fixture env AUTOMATION_HUMAN_APPROVED=1 ./scripts/automation/queue-task.sh TASK-TEST-002 >/dev/null
|
|
367
|
+
write_workspace TASK-TEST-002
|
|
368
|
+
run_fixture ./scripts/automation/claim-task.sh TASK-TEST-002 >/dev/null
|
|
369
|
+
printf '%s\n' 'class FailureLoopTest { fun expectedBehavior() = Unit }' > "$fixture/mobile-client/src/test/java/dev/example/orchestratorfixture/FailureLoopTest.kt"
|
|
370
|
+
run_fixture ./scripts/automation/record-red.sh TASK-TEST-002 'expected missing behavior' -- dev.example.orchestratorfixture.FailureLoopTest >/dev/null
|
|
371
|
+
printf '%s\n' 'class FailureLoop { fun value() = "still failing" }' > "$fixture/mobile-client/src/main/java/dev/example/orchestratorfixture/FailureLoop.kt"
|
|
372
|
+
|
|
373
|
+
if run_fixture ./scripts/automation/quality-gate.sh TASK-TEST-002 >/dev/null 2>&1; then
|
|
374
|
+
fail 'first failing gate attempt unexpectedly passed'
|
|
375
|
+
fi
|
|
376
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-002.json")" == "CODING" ]] || fail 'first failure did not preserve CODING'
|
|
377
|
+
pass 'first verification failure allows one systematic fix attempt'
|
|
378
|
+
|
|
379
|
+
if run_fixture ./scripts/automation/quality-gate.sh TASK-TEST-002 >/dev/null 2>&1; then
|
|
380
|
+
fail 'second failing gate attempt unexpectedly passed'
|
|
381
|
+
fi
|
|
382
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-002.json")" == "TEST_FAILED" ]] || fail 'second failure did not create TEST_FAILED'
|
|
383
|
+
pass 'second verification failure deterministically stops in TEST_FAILED'
|
|
384
|
+
|
|
385
|
+
(
|
|
386
|
+
cd "$fixture"
|
|
387
|
+
git add mobile-client
|
|
388
|
+
git commit -qm 'Preserve second fixture outcome'
|
|
389
|
+
)
|
|
390
|
+
printf '%s\n' '# Approved preflight-block plan' > "$fixture/docs/plans/TASK-TEST-003.md"
|
|
391
|
+
jq \
|
|
392
|
+
'.id = "TASK-TEST-003" |
|
|
393
|
+
.title = "Verify dirty worktree preflight blocking" |
|
|
394
|
+
.planPath = "docs/plans/TASK-TEST-003.md" |
|
|
395
|
+
.targetTests = [{gradleTask: "testDebugUnitTest", filter: "dev.example.orchestratorfixture.PreflightBlockTest"}]' \
|
|
396
|
+
"$fixture/automation/tasks/TASK-TEST-001.json" \
|
|
397
|
+
> "$fixture/automation/tasks/TASK-TEST-003.json"
|
|
398
|
+
(
|
|
399
|
+
cd "$fixture"
|
|
400
|
+
git add automation/tasks/TASK-TEST-003.json docs/plans/TASK-TEST-003.md
|
|
401
|
+
git commit -qm 'Add preflight-block fixture task'
|
|
402
|
+
)
|
|
403
|
+
run_fixture env AUTOMATION_HUMAN_APPROVED=1 ./scripts/automation/queue-task.sh TASK-TEST-003 >/dev/null
|
|
404
|
+
write_workspace TASK-TEST-003
|
|
405
|
+
printf '%s\n' 'dirty' > "$fixture/unapproved-change.txt"
|
|
406
|
+
if run_fixture ./scripts/automation/claim-task.sh TASK-TEST-003 >/dev/null 2>&1; then
|
|
407
|
+
fail 'claim accepted a dirty worktree'
|
|
408
|
+
fi
|
|
409
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-003.json")" == "BLOCKED" ]] || fail 'preflight failure did not create BLOCKED'
|
|
410
|
+
if run_fixture env AUTOMATION_SKIP_AGENT_RUN=1 ./scripts/automation/resume-review.sh TASK-TEST-003 >/dev/null 2>&1; then
|
|
411
|
+
fail 'reviewer-only recovery accepted a non-review blocker'
|
|
412
|
+
fi
|
|
413
|
+
rm "$fixture/unapproved-change.txt"
|
|
414
|
+
pass 'preflight blocks dirty task roots without retrying implicitly'
|
|
415
|
+
pass 'reviewer-only recovery rejects unrelated BLOCKED states'
|
|
416
|
+
|
|
417
|
+
printf '%s\n' '# Approved reviewer-fix plan' > "$fixture/docs/plans/TASK-TEST-005.md"
|
|
418
|
+
jq \
|
|
419
|
+
'.id = "TASK-TEST-005" |
|
|
420
|
+
.title = "Exercise the bounded reviewer repair cycle" |
|
|
421
|
+
.planPath = "docs/plans/TASK-TEST-005.md" |
|
|
422
|
+
.targetTests = [{gradleTask: "testDebugUnitTest", filter: "dev.example.orchestratorfixture.ReviewerFixTest"}]' \
|
|
423
|
+
"$fixture/automation/tasks/TASK-TEST-001.json" \
|
|
424
|
+
> "$fixture/automation/tasks/TASK-TEST-005.json"
|
|
425
|
+
(
|
|
426
|
+
cd "$fixture"
|
|
427
|
+
git add automation/tasks/TASK-TEST-005.json docs/plans/TASK-TEST-005.md
|
|
428
|
+
git commit -qm 'Add reviewer-fix fixture task'
|
|
429
|
+
)
|
|
430
|
+
run_fixture env AUTOMATION_HUMAN_APPROVED=1 ./scripts/automation/queue-task.sh TASK-TEST-005 >/dev/null
|
|
431
|
+
write_workspace TASK-TEST-005
|
|
432
|
+
run_fixture ./scripts/automation/claim-task.sh TASK-TEST-005 >/dev/null
|
|
433
|
+
printf '%s\n' 'class ReviewerFixTest { fun expectedBehavior() = Unit }' > "$fixture/mobile-client/src/test/java/dev/example/orchestratorfixture/ReviewerFixTest.kt"
|
|
434
|
+
run_fixture ./scripts/automation/record-red.sh TASK-TEST-005 'expected missing behavior' -- dev.example.orchestratorfixture.ReviewerFixTest >/dev/null
|
|
435
|
+
printf '%s\n' 'class ReviewerFix { fun value() = "first pass" }' > "$fixture/mobile-client/src/main/java/dev/example/orchestratorfixture/ReviewerFix.kt"
|
|
436
|
+
run_fixture env AUTOMATION_FAKE_GREEN=1 ./scripts/automation/quality-gate.sh TASK-TEST-005 >/dev/null
|
|
437
|
+
run_fixture ./scripts/automation/begin-review.sh TASK-TEST-005 >/dev/null
|
|
438
|
+
if run_fixture ./scripts/automation/submit-review.sh TASK-TEST-005 CHANGES_REQUESTED 'Reviewer found one material in-contract behavior issue.' >/dev/null 2>&1; then
|
|
439
|
+
fail 'changes-requested review unexpectedly returned success'
|
|
440
|
+
fi
|
|
441
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-005.json")" == "CHANGES_REQUESTED" ]] || fail 'review finding did not create CHANGES_REQUESTED'
|
|
442
|
+
pass 'reviewer findings are recorded as a distinct state'
|
|
443
|
+
|
|
444
|
+
run_fixture ./scripts/automation/resume-review-fix.sh TASK-TEST-005 >/dev/null
|
|
445
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-005.json")" == "CODING" ]] || fail 'bounded repair did not return to CODING'
|
|
446
|
+
[[ "$(jq -r '.codingCycle' "$runtime_root/workspaces/TASK-TEST-005.json")" -eq 1 ]] || fail 'repair did not create a fresh gate cycle'
|
|
447
|
+
pass 'one configured reviewer repair gets a fresh quality-gate cycle'
|
|
448
|
+
|
|
449
|
+
printf '%s\n' 'class ReviewerFix { fun value() = "second pass" }' > "$fixture/mobile-client/src/main/java/dev/example/orchestratorfixture/ReviewerFix.kt"
|
|
450
|
+
run_fixture env AUTOMATION_FAKE_GREEN=1 ./scripts/automation/quality-gate.sh TASK-TEST-005 >/dev/null
|
|
451
|
+
run_fixture ./scripts/automation/begin-review.sh TASK-TEST-005 >/dev/null
|
|
452
|
+
if run_fixture ./scripts/automation/submit-review.sh TASK-TEST-005 CHANGES_REQUESTED 'Fresh reviewer still found a material behavior issue.' >/dev/null 2>&1; then
|
|
453
|
+
fail 'second changes-requested review unexpectedly returned success'
|
|
454
|
+
fi
|
|
455
|
+
if run_fixture ./scripts/automation/resume-review-fix.sh TASK-TEST-005 >/dev/null 2>&1; then
|
|
456
|
+
fail 'review repair limit was bypassed'
|
|
457
|
+
fi
|
|
458
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-005.json")" == "NEEDS_HUMAN" ]] || fail 'repair exhaustion did not create NEEDS_HUMAN'
|
|
459
|
+
pass 'reviewer repair limit prevents an unbounded agent loop'
|
|
460
|
+
|
|
461
|
+
(
|
|
462
|
+
cd "$fixture"
|
|
463
|
+
git add mobile-client
|
|
464
|
+
git commit -qm 'Preserve reviewer-fix fixture outcome'
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
printf '%s\n' '# End-to-end orchestrated plan' > "$fixture/docs/plans/TASK-TEST-004.md"
|
|
468
|
+
jq \
|
|
469
|
+
'.id = "TASK-TEST-004" |
|
|
470
|
+
.title = "Exercise in-place transactional integration" |
|
|
471
|
+
.planPath = "docs/plans/TASK-TEST-004.md" |
|
|
472
|
+
.targetTests = [{gradleTask: "testDebugUnitTest", filter: "dev.example.orchestratorfixture.OrchestratedFlowTest"}]' \
|
|
473
|
+
"$fixture/automation/tasks/TASK-TEST-001.json" \
|
|
474
|
+
> "$fixture/automation/tasks/TASK-TEST-004.json"
|
|
475
|
+
|
|
476
|
+
if run_fixture ./scripts/automation/prepare-contract-review.sh TASK-TEST-004 '确认' >/dev/null 2>&1; then
|
|
477
|
+
fail 'proposal preparation accepted an unbound confirmation'
|
|
478
|
+
fi
|
|
479
|
+
pass 'proposal gate requires the configured approval option token'
|
|
480
|
+
|
|
481
|
+
run_fixture ./scripts/automation/prepare-contract-review.sh TASK-TEST-004 '批准方案,生成计划和任务合同。' >/dev/null
|
|
482
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-004.json")" == "CONTRACT_REVIEW" ]] || fail 'proposal gate did not enter CONTRACT_REVIEW'
|
|
483
|
+
pass 'approved proposal seals plan, contract, branch, HEAD, and hashes'
|
|
484
|
+
|
|
485
|
+
if run_fixture ./scripts/automation/approve-and-run.sh TASK-TEST-004 '确认执行' >/dev/null 2>&1; then
|
|
486
|
+
fail 'contract execution accepted an unbound confirmation'
|
|
487
|
+
fi
|
|
488
|
+
pass 'contract gate rejects execution without the exact approval option token'
|
|
489
|
+
|
|
490
|
+
run_fixture env AUTOMATION_SKIP_AGENT_RUN=1 ./scripts/automation/approve-and-run.sh TASK-TEST-004 '合同已复核,批准自动执行到人工验收阶段。' >/dev/null
|
|
491
|
+
task_root="$(jq -er '.taskRoot' "$runtime_root/workspaces/TASK-TEST-004.json")"
|
|
492
|
+
approved_baseline="$(jq -er '.baselineHead' "$runtime_root/workspaces/TASK-TEST-004.json")"
|
|
493
|
+
[[ "$task_root" == "$fixture" ]] || fail 'in-place strategy did not keep the task in the source directory'
|
|
494
|
+
[[ "$(jq -r '.workspaceStrategy' "$runtime_root/workspaces/TASK-TEST-004.json")" == "inPlaceExclusive" ]] || fail 'workspace strategy was not recorded'
|
|
495
|
+
[[ "$(git -C "$fixture" symbolic-ref --short HEAD)" == "automation/task-test-004" ]] || fail 'source directory did not switch to the task branch'
|
|
496
|
+
[[ "$(git -C "$fixture" rev-list --count "$approved_baseline..automation/task-test-004")" -eq 0 ]] || fail 'contract approval created a planning-only commit'
|
|
497
|
+
[[ "$(jq -r '.contractCommit' "$runtime_root/evidence/TASK-TEST-004/origin.json")" == "null" ]] || fail 'contract approval recorded a commit before product integration'
|
|
498
|
+
[[ "$(git -C "$fixture" status --porcelain --untracked-files=all -- docs/plans/TASK-TEST-004.md automation/tasks/TASK-TEST-004.json | wc -l | tr -d ' ')" -eq 2 ]] || fail 'sealed planning artifacts were not left pending on the task branch'
|
|
499
|
+
[[ -f "$runtime_root/locks/repository.workspace.lease/lease.json" ]] || fail 'persistent repository lease was not created'
|
|
500
|
+
leased_status="$(run_fixture ./scripts/automation/status.sh TASK-TEST-004)"
|
|
501
|
+
[[ "$(jq -r '.runtime.repositoryLeaseMatches' <<< "$leased_status")" == "true" ]] || fail 'status did not verify the repository lease'
|
|
502
|
+
[[ "$(jq -r '.runtime.originalBranchDrifted' <<< "$leased_status")" == "false" ]] || fail 'status reported false branch drift'
|
|
503
|
+
if run_fixture bash -c 'source ./scripts/automation/lib.sh; automation_acquire_repository_lease TASK-TEST-999 "$PWD" inPlaceExclusive' >/dev/null 2>&1; then
|
|
504
|
+
fail 'a second task acquired the persistent repository lease'
|
|
505
|
+
fi
|
|
506
|
+
pass 'persistent repository lease rejects a concurrent automation task'
|
|
507
|
+
[[ "$(git -C "$fixture" worktree list --porcelain | awk '/^worktree / { count++ } END { print count + 0 }')" -eq 1 ]] || fail 'in-place preparation created an unexpected worktree'
|
|
508
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-004.json")" == "PENDING" ]] || fail 'prepared task did not become PENDING'
|
|
509
|
+
pass 'contract approval switches to a leased task branch while deferring the sealed plan and contract to the product commit'
|
|
510
|
+
|
|
511
|
+
run_task "$task_root" ./scripts/automation/claim-task.sh TASK-TEST-004 >/dev/null
|
|
512
|
+
printf '%s\n' 'class OrchestratedFlowTest { fun expectedBehavior() = Unit }' > "$task_root/mobile-client/src/test/java/dev/example/orchestratorfixture/OrchestratedFlowTest.kt"
|
|
513
|
+
run_task "$task_root" ./scripts/automation/record-red.sh TASK-TEST-004 'expected missing behavior' -- dev.example.orchestratorfixture.OrchestratedFlowTest >/dev/null
|
|
514
|
+
printf '%s\n' 'class OrchestratedFlow { fun value() = "integrated" }' > "$task_root/mobile-client/src/main/java/dev/example/orchestratorfixture/OrchestratedFlow.kt"
|
|
515
|
+
run_task "$task_root" env AUTOMATION_FAKE_GREEN=1 ./scripts/automation/quality-gate.sh TASK-TEST-004 >/dev/null
|
|
516
|
+
run_task "$task_root" ./scripts/automation/begin-review.sh TASK-TEST-004 >/dev/null
|
|
517
|
+
run_task "$task_root" env AUTOMATION_FAKE_GREEN=1 ./scripts/automation/submit-review.sh TASK-TEST-004 APPROVED 'Fresh review confirms the sealed behavior and scope.' >/dev/null
|
|
518
|
+
run_task "$task_root" ./scripts/automation/acceptance-report.sh TASK-TEST-004 >/dev/null
|
|
519
|
+
[[ "$(jq -r '.changedPaths | length' "$runtime_root/evidence/TASK-TEST-004/acceptance-report.json")" -eq 2 ]] || fail 'acceptance package omitted product paths'
|
|
520
|
+
[[ "$(jq -r '.evidence.qualityGate' "$runtime_root/evidence/TASK-TEST-004/acceptance-report.json")" == "PASSED" ]] || fail 'acceptance package omitted quality-gate status'
|
|
521
|
+
acceptance_card="$(run_fixture ./scripts/automation/show-acceptance-review.sh TASK-TEST-004)"
|
|
522
|
+
[[ "$acceptance_card" == *"人工验收提醒"* ]] || fail 'acceptance review did not actively identify the human gate'
|
|
523
|
+
[[ "$acceptance_card" == *"P0 · 真实行为是否满足合同"* ]] || fail 'acceptance review omitted behavioral focus'
|
|
524
|
+
[[ "$acceptance_card" == *"P0 · 旧行为与范围是否被误伤"* ]] || fail 'acceptance review omitted regression and scope focus'
|
|
525
|
+
[[ "$acceptance_card" == *"sealed diff SHA"* ]] || fail 'acceptance review omitted sealed binding'
|
|
526
|
+
[[ "$acceptance_card" == *"成功集成后自动删除;失败或阻塞时保留"* ]] || fail 'acceptance review omitted task branch cleanup policy'
|
|
527
|
+
pass 'automated evidence becomes one focused, SHA-verified human acceptance card'
|
|
528
|
+
|
|
529
|
+
printf '%s\n' 'class OrchestratedFlow { fun value() = "tampered after review" }' > "$task_root/mobile-client/src/main/java/dev/example/orchestratorfixture/OrchestratedFlow.kt"
|
|
530
|
+
if run_fixture ./scripts/automation/show-acceptance-review.sh TASK-TEST-004 >/dev/null 2>&1; then
|
|
531
|
+
fail 'acceptance review displayed a diff changed after sealing'
|
|
532
|
+
fi
|
|
533
|
+
printf '%s\n' 'class OrchestratedFlow { fun value() = "integrated" }' > "$task_root/mobile-client/src/main/java/dev/example/orchestratorfixture/OrchestratedFlow.kt"
|
|
534
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-004.json")" == "AWAITING_HUMAN" ]] || fail 'read-only acceptance display changed task state'
|
|
535
|
+
pass 'acceptance display rejects a changed diff and never advances state'
|
|
536
|
+
|
|
537
|
+
original_branch="$(jq -er '.originalBranch' "$runtime_root/evidence/TASK-TEST-004/origin.json")"
|
|
538
|
+
if run_fixture env AUTOMATION_FAKE_GREEN=1 ./scripts/automation/accept-and-integrate.sh TASK-TEST-004 '拒绝' >/dev/null 2>&1; then
|
|
539
|
+
fail 'integrator accepted an invalid final confirmation'
|
|
540
|
+
fi
|
|
541
|
+
pass 'integrator requires final acceptance bound to the sealed diff'
|
|
542
|
+
|
|
543
|
+
run_fixture env AUTOMATION_FAKE_GREEN=1 ./scripts/automation/accept-and-integrate.sh TASK-TEST-004 '验收通过,提交到原分支。' >/dev/null
|
|
544
|
+
combined_commit="$(jq -er '.productCommit' "$runtime_root/workspaces/TASK-TEST-004.json")"
|
|
545
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-004.json")" == "COMPLETED" ]] || fail 'final integration did not reach COMPLETED'
|
|
546
|
+
[[ "$(git -C "$fixture" symbolic-ref --short HEAD)" == "$original_branch" ]] || fail 'integrator changed the original branch identity'
|
|
547
|
+
[[ -f "$fixture/mobile-client/src/main/java/dev/example/orchestratorfixture/OrchestratedFlow.kt" ]] || fail 'product change was not integrated into original branch'
|
|
548
|
+
[[ "$(git -C "$fixture" rev-list --count "$approved_baseline..$combined_commit")" -eq 1 ]] || fail 'final integration created more than one task commit'
|
|
549
|
+
for combined_path in \
|
|
550
|
+
docs/plans/TASK-TEST-004.md \
|
|
551
|
+
automation/tasks/TASK-TEST-004.json \
|
|
552
|
+
mobile-client/src/main/java/dev/example/orchestratorfixture/OrchestratedFlow.kt \
|
|
553
|
+
mobile-client/src/test/java/dev/example/orchestratorfixture/OrchestratedFlowTest.kt; do
|
|
554
|
+
git -C "$fixture" diff-tree --no-commit-id --name-only -r "$combined_commit" | \
|
|
555
|
+
awk -v expected="$combined_path" '$0 == expected { found = 1 } END { exit !found }' || \
|
|
556
|
+
fail "combined task commit omitted $combined_path"
|
|
557
|
+
done
|
|
558
|
+
[[ "$(jq -r '.contractCommit' "$runtime_root/evidence/TASK-TEST-004/origin.json")" == "$combined_commit" ]] || fail 'origin evidence did not bind the contract to the combined task commit'
|
|
559
|
+
[[ "$(jq -r '.pushed' "$runtime_root/evidence/TASK-TEST-004/integration.json")" == "false" ]] || fail 'integration evidence did not forbid push'
|
|
560
|
+
[[ "$(jq -r '.method' "$runtime_root/evidence/TASK-TEST-004/integration.json")" == "inPlaceExclusive-fast-forward" ]] || fail 'integration did not use the in-place fast-forward path'
|
|
561
|
+
[[ "$(jq -r '.taskBranchDeleted' "$runtime_root/evidence/TASK-TEST-004/integration.json")" == "true" ]] || fail 'integration evidence did not record task branch deletion'
|
|
562
|
+
[[ "$(jq -r '.taskBranchDeleted' "$runtime_root/workspaces/TASK-TEST-004.json")" == "true" ]] || fail 'workspace metadata did not record task branch deletion'
|
|
563
|
+
if git -C "$fixture" show-ref --verify --quiet refs/heads/automation/task-test-004; then
|
|
564
|
+
fail 'successful in-place integration left the local task branch behind'
|
|
565
|
+
fi
|
|
566
|
+
completed_status="$(run_fixture ./scripts/automation/status.sh TASK-TEST-004)"
|
|
567
|
+
[[ "$(jq -r '.runtime.taskBranchExists' <<< "$completed_status")" == "false" ]] || fail 'completed status did not report task branch deletion'
|
|
568
|
+
[[ ! -d "$runtime_root/locks/repository.workspace.lease" ]] || fail 'successful integration did not release the repository lease'
|
|
569
|
+
[[ "$(git -C "$fixture" worktree list --porcelain | awk '/^worktree / { count++ } END { print count + 0 }')" -eq 1 ]] || fail 'final integration created an unexpected candidate worktree'
|
|
570
|
+
pass 'one verified commit reaches the original branch and deletes the integrated local task branch without push'
|
|
571
|
+
|
|
572
|
+
printf '%s\n' '# Abort archival safety plan' > "$fixture/docs/plans/TASK-TEST-008.md"
|
|
573
|
+
jq \
|
|
574
|
+
'.id = "TASK-TEST-008" |
|
|
575
|
+
.title = "Archive an interrupted in-place task" |
|
|
576
|
+
.planPath = "docs/plans/TASK-TEST-008.md" |
|
|
577
|
+
.targetTests = [{gradleTask: "testDebugUnitTest", filter: "dev.example.orchestratorfixture.AbortArchiveTest"}]' \
|
|
578
|
+
"$fixture/automation/tasks/TASK-TEST-001.json" \
|
|
579
|
+
> "$fixture/automation/tasks/TASK-TEST-008.json"
|
|
580
|
+
run_fixture ./scripts/automation/prepare-contract-review.sh TASK-TEST-008 '批准方案,生成计划和任务合同。' >/dev/null
|
|
581
|
+
run_fixture env AUTOMATION_SKIP_AGENT_RUN=1 ./scripts/automation/approve-and-run.sh TASK-TEST-008 '合同已复核,批准自动执行到人工验收阶段。' >/dev/null
|
|
582
|
+
printf '%s\n' 'class AbortArchive { fun value() = "preserved" }' > "$fixture/mobile-client/src/main/java/dev/example/orchestratorfixture/AbortArchive.kt"
|
|
583
|
+
printf '%s\n' 'must not be archived automatically' > "$fixture/out-of-contract.txt"
|
|
584
|
+
if run_fixture ./scripts/automation/abort-task.sh TASK-TEST-008 '中止任务,封存修改并恢复原分支。' >/dev/null 2>&1; then
|
|
585
|
+
fail 'abort archived an out-of-contract path'
|
|
586
|
+
fi
|
|
587
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-008.json")" == "PENDING" ]] || fail 'rejected abort changed task state'
|
|
588
|
+
[[ -d "$runtime_root/locks/repository.workspace.lease" ]] || fail 'rejected abort released the repository lease'
|
|
589
|
+
rm "$fixture/out-of-contract.txt"
|
|
590
|
+
run_fixture ./scripts/automation/abort-task.sh TASK-TEST-008 '中止任务,封存修改并恢复原分支。' >/dev/null
|
|
591
|
+
abort_recovery_commit="$(jq -er '.recoveryCommit' "$runtime_root/evidence/TASK-TEST-008/abort.json")"
|
|
592
|
+
git -C "$fixture" cat-file -e "$abort_recovery_commit:mobile-client/src/main/java/dev/example/orchestratorfixture/AbortArchive.kt" || fail 'abort recovery commit omitted the allowed change'
|
|
593
|
+
git -C "$fixture" cat-file -e "$abort_recovery_commit:docs/plans/TASK-TEST-008.md" || fail 'abort recovery commit omitted the sealed plan'
|
|
594
|
+
git -C "$fixture" cat-file -e "$abort_recovery_commit:automation/tasks/TASK-TEST-008.json" || fail 'abort recovery commit omitted the sealed contract'
|
|
595
|
+
[[ ! -f "$fixture/mobile-client/src/main/java/dev/example/orchestratorfixture/AbortArchive.kt" ]] || fail 'abort left archived code on the original branch'
|
|
596
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-008.json")" == "ABORTED" ]] || fail 'allowed abort did not reach ABORTED'
|
|
597
|
+
[[ ! -d "$runtime_root/locks/repository.workspace.lease" ]] || fail 'allowed abort did not release the repository lease'
|
|
598
|
+
pass 'abort refuses unrelated files and archives allowed uncommitted changes in a recovery commit'
|
|
599
|
+
|
|
600
|
+
planning_only_baseline="$(git -C "$fixture" rev-parse HEAD)"
|
|
601
|
+
printf '%s\n' '# Planning-only abort plan' > "$fixture/docs/plans/TASK-TEST-009.md"
|
|
602
|
+
jq \
|
|
603
|
+
'.id = "TASK-TEST-009" |
|
|
604
|
+
.title = "Abort before product editing" |
|
|
605
|
+
.planPath = "docs/plans/TASK-TEST-009.md" |
|
|
606
|
+
.targetTests = [{gradleTask: "testDebugUnitTest", filter: "dev.example.orchestratorfixture.PlanningOnlyAbortTest"}]' \
|
|
607
|
+
"$fixture/automation/tasks/TASK-TEST-001.json" \
|
|
608
|
+
> "$fixture/automation/tasks/TASK-TEST-009.json"
|
|
609
|
+
run_fixture ./scripts/automation/prepare-contract-review.sh TASK-TEST-009 '批准方案,生成计划和任务合同。' >/dev/null
|
|
610
|
+
run_fixture env AUTOMATION_SKIP_AGENT_RUN=1 ./scripts/automation/approve-and-run.sh TASK-TEST-009 '合同已复核,批准自动执行到人工验收阶段。' >/dev/null
|
|
611
|
+
run_fixture ./scripts/automation/abort-task.sh TASK-TEST-009 '中止任务,封存修改并恢复原分支。' >/dev/null
|
|
612
|
+
[[ "$(jq -r '.recoveryCommit' "$runtime_root/evidence/TASK-TEST-009/abort.json")" == "null" ]] || fail 'planning-only abort created a recovery commit'
|
|
613
|
+
[[ "$(jq -r '.planningOnlyArchivedWithoutCommit' "$runtime_root/evidence/TASK-TEST-009/abort.json")" == "true" ]] || fail 'planning-only abort did not record its no-commit archival policy'
|
|
614
|
+
[[ "$(git -C "$fixture" rev-parse HEAD)" == "$planning_only_baseline" ]] || fail 'planning-only abort changed original branch history'
|
|
615
|
+
[[ ! -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'
|
|
616
|
+
pass 'aborting before product edits archives planning artifacts without creating a planning-only commit'
|
|
617
|
+
|
|
618
|
+
isolated_config_tmp="$(mktemp "$fixture/automation/.config.XXXXXX")"
|
|
619
|
+
jq '.workspaceStrategy = "isolatedWorktree"' "$fixture/automation/config.json" > "$isolated_config_tmp"
|
|
620
|
+
mv "$isolated_config_tmp" "$fixture/automation/config.json"
|
|
621
|
+
(
|
|
622
|
+
cd "$fixture"
|
|
623
|
+
git add automation/config.json
|
|
624
|
+
git commit -qm 'Use isolated workspace fixture mode'
|
|
625
|
+
)
|
|
626
|
+
printf '%s\n' '# Optional isolated-workspace plan' > "$fixture/docs/plans/TASK-TEST-007.md"
|
|
627
|
+
jq \
|
|
628
|
+
'.id = "TASK-TEST-007" |
|
|
629
|
+
.title = "Keep optional isolated workspace support" |
|
|
630
|
+
.planPath = "docs/plans/TASK-TEST-007.md" |
|
|
631
|
+
.targetTests = [{gradleTask: "testDebugUnitTest", filter: "dev.example.orchestratorfixture.IsolatedFlowTest"}]' \
|
|
632
|
+
"$fixture/automation/tasks/TASK-TEST-001.json" \
|
|
633
|
+
> "$fixture/automation/tasks/TASK-TEST-007.json"
|
|
634
|
+
run_fixture ./scripts/automation/prepare-contract-review.sh TASK-TEST-007 '批准方案,生成计划和任务合同。' >/dev/null
|
|
635
|
+
run_fixture env AUTOMATION_SKIP_AGENT_RUN=1 ./scripts/automation/approve-and-run.sh TASK-TEST-007 '合同已复核,批准自动执行到人工验收阶段。' >/dev/null
|
|
636
|
+
isolated_task_root="$(jq -er '.taskRoot' "$runtime_root/workspaces/TASK-TEST-007.json")"
|
|
637
|
+
[[ "$isolated_task_root" != "$fixture" && -d "$isolated_task_root" ]] || fail 'optional isolated strategy did not create a separate task root'
|
|
638
|
+
[[ -f "$isolated_task_root/docs/plans/TASK-TEST-007.md" && -f "$isolated_task_root/automation/tasks/TASK-TEST-007.json" ]] || fail 'isolated task root did not receive the sealed planning artifacts'
|
|
639
|
+
[[ ! -e "$fixture/docs/plans/TASK-TEST-007.md" && ! -e "$fixture/automation/tasks/TASK-TEST-007.json" ]] || fail 'isolated preparation left duplicate planning artifacts in the source root'
|
|
640
|
+
[[ "$(git -C "$fixture" worktree list --porcelain | awk '/^worktree / { count++ } END { print count + 0 }')" -eq 2 ]] || fail 'isolated strategy created more than one additional worktree'
|
|
641
|
+
run_task "$isolated_task_root" ./scripts/automation/claim-task.sh TASK-TEST-007 >/dev/null
|
|
642
|
+
printf '%s\n' 'class IsolatedFlowTest { fun expectedBehavior() = Unit }' > "$isolated_task_root/mobile-client/src/test/java/dev/example/orchestratorfixture/IsolatedFlowTest.kt"
|
|
643
|
+
run_task "$isolated_task_root" ./scripts/automation/record-red.sh TASK-TEST-007 'expected missing behavior' -- dev.example.orchestratorfixture.IsolatedFlowTest >/dev/null
|
|
644
|
+
printf '%s\n' 'class IsolatedFlow { fun value() = "integrated" }' > "$isolated_task_root/mobile-client/src/main/java/dev/example/orchestratorfixture/IsolatedFlow.kt"
|
|
645
|
+
run_task "$isolated_task_root" env AUTOMATION_FAKE_GREEN=1 ./scripts/automation/quality-gate.sh TASK-TEST-007 >/dev/null
|
|
646
|
+
run_task "$isolated_task_root" ./scripts/automation/begin-review.sh TASK-TEST-007 >/dev/null
|
|
647
|
+
run_task "$isolated_task_root" env AUTOMATION_FAKE_GREEN=1 ./scripts/automation/submit-review.sh TASK-TEST-007 APPROVED 'Independent review approves the isolated fixture.' >/dev/null
|
|
648
|
+
run_fixture env AUTOMATION_FAKE_GREEN=1 ./scripts/automation/accept-and-integrate.sh TASK-TEST-007 '验收通过,提交到原分支。' >/dev/null
|
|
649
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-007.json")" == "COMPLETED" ]] || fail 'isolated task did not reach COMPLETED'
|
|
650
|
+
[[ "$(jq -r '.method' "$runtime_root/evidence/TASK-TEST-007/integration.json")" == "isolatedWorktree-fast-forward" ]] || fail 'isolated task used the wrong integration method'
|
|
651
|
+
[[ ! -d "$isolated_task_root" ]] || fail 'isolated task root was not cleaned after successful integration'
|
|
652
|
+
[[ "$(git -C "$fixture" worktree list --porcelain | awk '/^worktree / { count++ } END { print count + 0 }')" -eq 1 ]] || fail 'isolated integration left an extra candidate worktree'
|
|
653
|
+
if git -C "$fixture" show-ref --verify --quiet refs/heads/automation/task-test-007; then
|
|
654
|
+
fail 'successful isolated integration left the local task branch behind'
|
|
655
|
+
fi
|
|
656
|
+
[[ "$(jq -r '.taskBranchDeleted' "$runtime_root/evidence/TASK-TEST-007/integration.json")" == "true" ]] || fail 'isolated integration did not record task branch deletion'
|
|
657
|
+
[[ -f "$fixture/mobile-client/src/main/java/dev/example/orchestratorfixture/IsolatedFlow.kt" ]] || fail 'isolated product change was not integrated'
|
|
658
|
+
pass 'optional isolation removes its task worktree and integrated local task branch without a second candidate'
|
|
659
|
+
|
|
660
|
+
in_place_config_tmp="$(mktemp "$fixture/automation/.config.XXXXXX")"
|
|
661
|
+
jq '.workspaceStrategy = "inPlaceExclusive"' "$fixture/automation/config.json" > "$in_place_config_tmp"
|
|
662
|
+
mv "$in_place_config_tmp" "$fixture/automation/config.json"
|
|
663
|
+
(
|
|
664
|
+
cd "$fixture"
|
|
665
|
+
git add automation/config.json
|
|
666
|
+
git commit -qm 'Restore in-place fixture mode'
|
|
667
|
+
)
|
|
668
|
+
|
|
669
|
+
printf '%s\n' '# Advanced-original integration plan' > "$fixture/docs/plans/TASK-TEST-006.md"
|
|
670
|
+
jq \
|
|
671
|
+
'.id = "TASK-TEST-006" |
|
|
672
|
+
.title = "Block an advanced original branch" |
|
|
673
|
+
.planPath = "docs/plans/TASK-TEST-006.md" |
|
|
674
|
+
.targetTests = [{gradleTask: "testDebugUnitTest", filter: "dev.example.orchestratorfixture.AdvancedOriginalTest"}]' \
|
|
675
|
+
"$fixture/automation/tasks/TASK-TEST-001.json" \
|
|
676
|
+
> "$fixture/automation/tasks/TASK-TEST-006.json"
|
|
677
|
+
run_fixture ./scripts/automation/prepare-contract-review.sh TASK-TEST-006 '批准方案,生成计划和任务合同。' >/dev/null
|
|
678
|
+
run_fixture env AUTOMATION_SKIP_AGENT_RUN=1 ./scripts/automation/approve-and-run.sh TASK-TEST-006 '合同已复核,批准自动执行到人工验收阶段。' >/dev/null
|
|
679
|
+
advanced_task_root="$(jq -er '.taskRoot' "$runtime_root/workspaces/TASK-TEST-006.json")"
|
|
680
|
+
run_task "$advanced_task_root" ./scripts/automation/claim-task.sh TASK-TEST-006 >/dev/null
|
|
681
|
+
printf '%s\n' 'class AdvancedOriginalTest { fun expectedBehavior() = Unit }' > "$advanced_task_root/mobile-client/src/test/java/dev/example/orchestratorfixture/AdvancedOriginalTest.kt"
|
|
682
|
+
run_task "$advanced_task_root" ./scripts/automation/record-red.sh TASK-TEST-006 'expected missing behavior' -- dev.example.orchestratorfixture.AdvancedOriginalTest >/dev/null
|
|
683
|
+
printf '%s\n' 'class AdvancedOriginal { fun value() = "must not integrate after drift" }' > "$advanced_task_root/mobile-client/src/main/java/dev/example/orchestratorfixture/AdvancedOriginal.kt"
|
|
684
|
+
run_task "$advanced_task_root" env AUTOMATION_FAKE_GREEN=1 ./scripts/automation/quality-gate.sh TASK-TEST-006 >/dev/null
|
|
685
|
+
run_task "$advanced_task_root" ./scripts/automation/begin-review.sh TASK-TEST-006 >/dev/null
|
|
686
|
+
run_task "$advanced_task_root" env AUTOMATION_FAKE_GREEN=1 ./scripts/automation/submit-review.sh TASK-TEST-006 APPROVED 'Independent review approves the advanced-branch fixture.' >/dev/null
|
|
687
|
+
|
|
688
|
+
advanced_baseline="$(jq -er '.baselineHead' "$runtime_root/workspaces/TASK-TEST-006.json")"
|
|
689
|
+
advanced_original_branch="$(jq -er '.originalBranch' "$runtime_root/workspaces/TASK-TEST-006.json")"
|
|
690
|
+
advanced_original_commit="$(printf '%s\n' 'Advance original branch independently' | git -C "$fixture" commit-tree "$advanced_baseline^{tree}" -p "$advanced_baseline")"
|
|
691
|
+
git -C "$fixture" update-ref "refs/heads/$advanced_original_branch" "$advanced_original_commit" "$advanced_baseline"
|
|
692
|
+
[[ "$(git -C "$fixture" rev-parse "refs/heads/$advanced_original_branch")" == "$advanced_original_commit" ]] || fail 'original branch did not advance for drift test'
|
|
693
|
+
pass 'a clean descendant commit is recognized as original-branch drift'
|
|
694
|
+
|
|
695
|
+
if run_fixture env AUTOMATION_FAKE_GREEN=1 ./scripts/automation/accept-and-integrate.sh TASK-TEST-006 '验收通过,提交到原分支。' >/dev/null 2>&1; then
|
|
696
|
+
fail 'integrator accepted an original branch that drifted from the recorded pre-task baseline'
|
|
697
|
+
fi
|
|
698
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-006.json")" == "INTEGRATION_BLOCKED" ]] || fail 'branch drift did not create INTEGRATION_BLOCKED'
|
|
699
|
+
[[ "$(git -C "$fixture" rev-parse "refs/heads/$advanced_original_branch")" == "$advanced_original_commit" ]] || fail 'blocked integration changed the advanced original branch'
|
|
700
|
+
git -C "$fixture" show-ref --verify --quiet refs/heads/automation/task-test-006 || fail 'blocked integration deleted the recoverable task branch'
|
|
701
|
+
[[ ! -f "$runtime_root/evidence/TASK-TEST-006/integration.json" ]] || fail 'blocked integration recorded false success evidence'
|
|
702
|
+
pass 'original-branch drift blocks integration while preserving the original and task branches'
|
|
703
|
+
|
|
704
|
+
if run_fixture ./scripts/automation/abort-task.sh TASK-TEST-006 '中止' >/dev/null 2>&1; then
|
|
705
|
+
fail 'abort accepted an unbound confirmation'
|
|
706
|
+
fi
|
|
707
|
+
run_fixture ./scripts/automation/abort-task.sh TASK-TEST-006 '中止任务,封存修改并恢复原分支。' >/dev/null
|
|
708
|
+
[[ "$(jq -r '.state' "$runtime_root/state/TASK-TEST-006.json")" == "ABORTED" ]] || fail 'deterministic abort did not reach ABORTED'
|
|
709
|
+
[[ "$(git -C "$fixture" symbolic-ref --short HEAD)" == "$advanced_original_branch" ]] || fail 'abort did not restore the source directory to the original branch'
|
|
710
|
+
[[ "$(git -C "$fixture" rev-parse HEAD)" == "$advanced_original_commit" ]] || fail 'abort changed the advanced original branch ref'
|
|
711
|
+
[[ "$(jq -r '.recoveryCommit' "$runtime_root/evidence/TASK-TEST-006/abort.json")" == "$(jq -r '.productCommit' "$runtime_root/workspaces/TASK-TEST-006.json")" ]] || fail 'abort did not preserve the product commit as recovery evidence'
|
|
712
|
+
[[ ! -f "$fixture/mobile-client/src/main/java/dev/example/orchestratorfixture/AdvancedOriginal.kt" ]] || fail 'aborted product code remained in the restored original working tree'
|
|
713
|
+
[[ ! -d "$runtime_root/locks/repository.workspace.lease" ]] || fail 'abort did not release the repository lease'
|
|
714
|
+
pass 'explicit abort preserves recovery evidence and restores the original branch without changing its ref'
|
|
715
|
+
|
|
716
|
+
printf '1..%d\n' "$pass_count"
|