@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,201 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "urn:frankzhang2026:opencode-android-orchestrator:installation-manifest:v1",
|
|
4
|
+
"title": "OpenCode Android Orchestrator installation manifest",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": [
|
|
8
|
+
"schemaVersion",
|
|
9
|
+
"package",
|
|
10
|
+
"installation",
|
|
11
|
+
"backupDirectory",
|
|
12
|
+
"files"
|
|
13
|
+
],
|
|
14
|
+
"$defs": {
|
|
15
|
+
"sha256": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"pattern": "^[a-f0-9]{64}$"
|
|
18
|
+
},
|
|
19
|
+
"timestamp": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"format": "date-time"
|
|
22
|
+
},
|
|
23
|
+
"relativePath": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"minLength": 1,
|
|
26
|
+
"pattern": "^(?!/)(?!\\s)(?!.*\\s$)(?!.*(?:^|/)\\.{1,2}(?:/|$))(?!.*\\\\)(?!.*//)(?!.*\\u0000)(?!.*\\/$).+"
|
|
27
|
+
},
|
|
28
|
+
"managedPath": {
|
|
29
|
+
"allOf": [
|
|
30
|
+
{ "$ref": "#/$defs/relativePath" },
|
|
31
|
+
{ "pattern": "^(?!\\.automation-plugin(?:/|$)).+" }
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"properties": {
|
|
36
|
+
"schemaVersion": { "const": 1 },
|
|
37
|
+
"package": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"additionalProperties": false,
|
|
40
|
+
"required": ["name", "version"],
|
|
41
|
+
"properties": {
|
|
42
|
+
"name": { "type": "string", "minLength": 1 },
|
|
43
|
+
"version": { "type": "string", "minLength": 1 }
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"installation": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"additionalProperties": false,
|
|
49
|
+
"required": [
|
|
50
|
+
"id",
|
|
51
|
+
"state",
|
|
52
|
+
"preparedAt",
|
|
53
|
+
"installedAt",
|
|
54
|
+
"rolledBackAt"
|
|
55
|
+
],
|
|
56
|
+
"properties": {
|
|
57
|
+
"id": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$"
|
|
60
|
+
},
|
|
61
|
+
"state": { "enum": ["prepared", "installed", "rolledBack"] },
|
|
62
|
+
"preparedAt": { "$ref": "#/$defs/timestamp" },
|
|
63
|
+
"installedAt": {
|
|
64
|
+
"oneOf": [
|
|
65
|
+
{ "$ref": "#/$defs/timestamp" },
|
|
66
|
+
{ "type": "null" }
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
"rolledBackAt": {
|
|
70
|
+
"oneOf": [
|
|
71
|
+
{ "$ref": "#/$defs/timestamp" },
|
|
72
|
+
{ "type": "null" }
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"allOf": [
|
|
77
|
+
{
|
|
78
|
+
"if": { "properties": { "state": { "const": "prepared" } } },
|
|
79
|
+
"then": {
|
|
80
|
+
"properties": {
|
|
81
|
+
"installedAt": { "type": "null" },
|
|
82
|
+
"rolledBackAt": { "type": "null" }
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"if": { "properties": { "state": { "const": "installed" } } },
|
|
88
|
+
"then": {
|
|
89
|
+
"properties": {
|
|
90
|
+
"installedAt": { "$ref": "#/$defs/timestamp" },
|
|
91
|
+
"rolledBackAt": { "type": "null" }
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"if": { "properties": { "state": { "const": "rolledBack" } } },
|
|
97
|
+
"then": {
|
|
98
|
+
"properties": {
|
|
99
|
+
"rolledBackAt": { "$ref": "#/$defs/timestamp" }
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
},
|
|
105
|
+
"backupDirectory": {
|
|
106
|
+
"allOf": [
|
|
107
|
+
{ "$ref": "#/$defs/relativePath" },
|
|
108
|
+
{
|
|
109
|
+
"pattern": "^\\.automation-plugin/backups/[A-Za-z0-9][A-Za-z0-9._-]{0,127}$"
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
"files": {
|
|
114
|
+
"type": "array",
|
|
115
|
+
"minItems": 1,
|
|
116
|
+
"items": {
|
|
117
|
+
"type": "object",
|
|
118
|
+
"additionalProperties": false,
|
|
119
|
+
"required": [
|
|
120
|
+
"path",
|
|
121
|
+
"source",
|
|
122
|
+
"strategy",
|
|
123
|
+
"sha256",
|
|
124
|
+
"size",
|
|
125
|
+
"mode",
|
|
126
|
+
"previous"
|
|
127
|
+
],
|
|
128
|
+
"properties": {
|
|
129
|
+
"path": { "$ref": "#/$defs/managedPath" },
|
|
130
|
+
"source": { "$ref": "#/$defs/relativePath" },
|
|
131
|
+
"strategy": { "enum": ["copy", "generate", "merge"] },
|
|
132
|
+
"sha256": { "$ref": "#/$defs/sha256" },
|
|
133
|
+
"size": { "type": "integer", "minimum": 0 },
|
|
134
|
+
"mode": { "type": "integer", "minimum": 0, "maximum": 511 },
|
|
135
|
+
"previous": {
|
|
136
|
+
"type": "object",
|
|
137
|
+
"additionalProperties": false,
|
|
138
|
+
"required": [
|
|
139
|
+
"existed",
|
|
140
|
+
"sha256",
|
|
141
|
+
"size",
|
|
142
|
+
"mode",
|
|
143
|
+
"backupPath"
|
|
144
|
+
],
|
|
145
|
+
"properties": {
|
|
146
|
+
"existed": { "type": "boolean" },
|
|
147
|
+
"sha256": {
|
|
148
|
+
"oneOf": [
|
|
149
|
+
{ "$ref": "#/$defs/sha256" },
|
|
150
|
+
{ "type": "null" }
|
|
151
|
+
]
|
|
152
|
+
},
|
|
153
|
+
"size": {
|
|
154
|
+
"oneOf": [
|
|
155
|
+
{ "type": "integer", "minimum": 0 },
|
|
156
|
+
{ "type": "null" }
|
|
157
|
+
]
|
|
158
|
+
},
|
|
159
|
+
"mode": {
|
|
160
|
+
"oneOf": [
|
|
161
|
+
{ "type": "integer", "minimum": 0, "maximum": 511 },
|
|
162
|
+
{ "type": "null" }
|
|
163
|
+
]
|
|
164
|
+
},
|
|
165
|
+
"backupPath": {
|
|
166
|
+
"oneOf": [
|
|
167
|
+
{ "$ref": "#/$defs/relativePath" },
|
|
168
|
+
{ "type": "null" }
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
"allOf": [
|
|
173
|
+
{
|
|
174
|
+
"if": { "properties": { "existed": { "const": false } } },
|
|
175
|
+
"then": {
|
|
176
|
+
"properties": {
|
|
177
|
+
"sha256": { "type": "null" },
|
|
178
|
+
"size": { "type": "null" },
|
|
179
|
+
"mode": { "type": "null" },
|
|
180
|
+
"backupPath": { "type": "null" }
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"if": { "properties": { "existed": { "const": true } } },
|
|
186
|
+
"then": {
|
|
187
|
+
"properties": {
|
|
188
|
+
"sha256": { "$ref": "#/$defs/sha256" },
|
|
189
|
+
"size": { "type": "integer", "minimum": 0 },
|
|
190
|
+
"mode": { "type": "integer", "minimum": 0, "maximum": 511 },
|
|
191
|
+
"backupPath": { "$ref": "#/$defs/relativePath" }
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
]
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
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 "ABORT-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 abort "$approval"
|
|
18
|
+
|
|
19
|
+
current_state="$(automation_read_state "$task_id")"
|
|
20
|
+
case "$current_state" in
|
|
21
|
+
PREPARING|PENDING|CODING|READY_FOR_REVIEW|REVIEWING|CHANGES_REQUESTED|AWAITING_HUMAN|BLOCKED|TEST_FAILED|NEEDS_HUMAN|INTEGRATION_BLOCKED) ;;
|
|
22
|
+
*) automation_die "cannot abort $task_id from state $current_state" ;;
|
|
23
|
+
esac
|
|
24
|
+
|
|
25
|
+
origin_file="$(automation_origin_path "$task_id")"
|
|
26
|
+
workspace_file="$(automation_workspace_path "$task_id")"
|
|
27
|
+
evidence_dir="$(automation_evidence_path "$task_id")"
|
|
28
|
+
[[ -f "$origin_file" && -f "$workspace_file" ]] || automation_die "abort metadata is incomplete for $task_id"
|
|
29
|
+
|
|
30
|
+
source_root="$(jq -er '.sourceRoot' "$origin_file")"
|
|
31
|
+
original_branch="$(jq -er '.originalBranch' "$origin_file")"
|
|
32
|
+
task_root="$(automation_workspace_task_root "$workspace_file")"
|
|
33
|
+
task_branch="$(jq -er '.taskBranch' "$workspace_file")"
|
|
34
|
+
workspace_strategy="$(automation_workspace_strategy "$workspace_file")"
|
|
35
|
+
contract="$(automation_contract_path "$task_id")"
|
|
36
|
+
original_ref="refs/heads/$original_branch"
|
|
37
|
+
|
|
38
|
+
[[ "$source_root" == "$AUTOMATION_ROOT" ]] || automation_die "abort must run from the recorded source root"
|
|
39
|
+
automation_require_repository_lease "$task_id" "$source_root" "$workspace_strategy"
|
|
40
|
+
git -C "$source_root" show-ref --verify --quiet "$original_ref" || automation_die "recorded original branch no longer exists"
|
|
41
|
+
|
|
42
|
+
product_commit="$(jq -r '.productCommit // ""' "$workspace_file")"
|
|
43
|
+
original_head="$(git -C "$source_root" rev-parse "$original_ref")"
|
|
44
|
+
if [[ -n "$product_commit" && "$original_head" == "$product_commit" ]]; then
|
|
45
|
+
automation_die "the product commit is already on the original branch; finalize recovery instead of aborting"
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
automation_acquire_run_lock "$task_id"
|
|
49
|
+
trap 'automation_release_run_lock' EXIT
|
|
50
|
+
|
|
51
|
+
recovery_commit=""
|
|
52
|
+
planning_only_archived_without_commit=false
|
|
53
|
+
: > "$evidence_dir/abort-status.txt"
|
|
54
|
+
: > "$evidence_dir/aborted.diff"
|
|
55
|
+
if [[ -d "$task_root" ]]; then
|
|
56
|
+
task_current_branch="$(automation_current_branch "$task_root")"
|
|
57
|
+
if [[ "$task_current_branch" == "$task_branch" ]]; then
|
|
58
|
+
automation_assert_planning_artifacts_sealed "$task_id" "$task_root"
|
|
59
|
+
changed_paths=()
|
|
60
|
+
changed_path_count=0
|
|
61
|
+
product_path_count=0
|
|
62
|
+
while IFS= read -r path; do
|
|
63
|
+
if [[ -n "$path" ]]; then
|
|
64
|
+
changed_paths+=("$path")
|
|
65
|
+
changed_path_count=$((changed_path_count + 1))
|
|
66
|
+
if ! automation_is_planning_artifact "$task_id" "$path"; then
|
|
67
|
+
product_path_count=$((product_path_count + 1))
|
|
68
|
+
fi
|
|
69
|
+
fi
|
|
70
|
+
done < <(automation_changed_paths_at "$task_root")
|
|
71
|
+
|
|
72
|
+
git -C "$task_root" status --short --branch > "$evidence_dir/abort-status.txt"
|
|
73
|
+
{
|
|
74
|
+
git -C "$task_root" diff --binary HEAD --
|
|
75
|
+
while IFS= read -r path; do
|
|
76
|
+
[[ -n "$path" ]] || continue
|
|
77
|
+
git -C "$task_root" diff --binary --no-index -- /dev/null "$task_root/$path" || [[ "$?" -eq 1 ]]
|
|
78
|
+
done < <(git -C "$task_root" ls-files --others --exclude-standard | LC_ALL=C sort)
|
|
79
|
+
} > "$evidence_dir/aborted.diff"
|
|
80
|
+
|
|
81
|
+
if [[ "$changed_path_count" -gt 0 ]]; then
|
|
82
|
+
for path in "${changed_paths[@]}"; do
|
|
83
|
+
if automation_is_planning_artifact "$task_id" "$path"; then
|
|
84
|
+
continue
|
|
85
|
+
fi
|
|
86
|
+
automation_array_matches_path "$contract" '.allowedPaths' "$path" || \
|
|
87
|
+
automation_die "abort refuses to archive an out-of-contract path: $path"
|
|
88
|
+
if automation_array_matches_path "$contract" '.forbiddenPaths' "$path" || \
|
|
89
|
+
automation_array_matches_path "$AUTOMATION_CONFIG" '.protectedPaths' "$path"; then
|
|
90
|
+
automation_die "abort refuses to archive a protected path: $path"
|
|
91
|
+
fi
|
|
92
|
+
done
|
|
93
|
+
if [[ "$product_path_count" -gt 0 ]]; then
|
|
94
|
+
git -C "$task_root" add -- "${changed_paths[@]}"
|
|
95
|
+
git -C "$task_root" commit -m "Archive aborted work for $task_id"
|
|
96
|
+
recovery_commit="$(git -C "$task_root" rev-parse HEAD)"
|
|
97
|
+
else
|
|
98
|
+
plan_rel="$(jq -er '.planPath' "$origin_file")"
|
|
99
|
+
contract_rel="$(jq -er '.contractPath' "$origin_file")"
|
|
100
|
+
for planning_path in "$plan_rel" "$contract_rel"; do
|
|
101
|
+
if git -C "$task_root" ls-files --error-unmatch -- "$planning_path" >/dev/null 2>&1; then
|
|
102
|
+
automation_die "abort refuses to discard a tracked planning artifact without product changes: $planning_path"
|
|
103
|
+
fi
|
|
104
|
+
rm -f "$task_root/$planning_path"
|
|
105
|
+
done
|
|
106
|
+
planning_only_archived_without_commit=true
|
|
107
|
+
fi
|
|
108
|
+
fi
|
|
109
|
+
if [[ "$changed_path_count" -eq 0 ]]; then
|
|
110
|
+
recovery_commit="$(git -C "$task_root" rev-parse HEAD)"
|
|
111
|
+
fi
|
|
112
|
+
automation_worktree_is_clean "$task_root" || automation_die "task root is not clean after archival"
|
|
113
|
+
elif [[ "$workspace_strategy" == "inPlaceExclusive" && "$task_current_branch" == "$original_branch" ]]; then
|
|
114
|
+
automation_worktree_is_clean "$source_root" || automation_die "source root is dirty after a partial abort; manual recovery is required"
|
|
115
|
+
recovery_commit="$(git -C "$source_root" rev-parse "refs/heads/$task_branch")"
|
|
116
|
+
else
|
|
117
|
+
automation_die "task root is on unexpected branch $task_current_branch"
|
|
118
|
+
fi
|
|
119
|
+
else
|
|
120
|
+
[[ "$workspace_strategy" == "isolatedWorktree" ]] || automation_die "in-place task root is missing"
|
|
121
|
+
recovery_commit="$(git -C "$source_root" rev-parse "refs/heads/$task_branch")"
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
if [[ "$workspace_strategy" == "inPlaceExclusive" ]]; then
|
|
125
|
+
if [[ "$(automation_current_branch "$source_root")" == "$task_branch" ]]; then
|
|
126
|
+
git -C "$source_root" switch "$original_branch"
|
|
127
|
+
fi
|
|
128
|
+
[[ "$(automation_current_branch "$source_root")" == "$original_branch" ]] || automation_die "source root did not return to the original branch"
|
|
129
|
+
elif [[ -d "$task_root" ]]; then
|
|
130
|
+
cleanup_log="$evidence_dir/cleanup.log"
|
|
131
|
+
git -C "$source_root" worktree remove "$task_root" >> "$cleanup_log" 2>&1
|
|
132
|
+
printf '%s removed aborted isolated task worktree %s\n' "$(automation_now)" "$task_root" >> "$cleanup_log"
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
jq -n \
|
|
136
|
+
--arg taskId "$task_id" \
|
|
137
|
+
--arg abortedAt "$(automation_now)" \
|
|
138
|
+
--arg previousState "$current_state" \
|
|
139
|
+
--arg originalBranch "$original_branch" \
|
|
140
|
+
--arg originalHead "$(git -C "$source_root" rev-parse "$original_ref")" \
|
|
141
|
+
--arg taskBranch "$task_branch" \
|
|
142
|
+
--arg recoveryCommit "$recovery_commit" \
|
|
143
|
+
--arg workspaceStrategy "$workspace_strategy" \
|
|
144
|
+
--arg archivedDiffSha256 "$(automation_file_sha256 "$evidence_dir/aborted.diff")" \
|
|
145
|
+
--argjson planningOnlyArchivedWithoutCommit "$planning_only_archived_without_commit" \
|
|
146
|
+
'{taskId: $taskId, abortedAt: $abortedAt, previousState: $previousState,
|
|
147
|
+
originalBranch: $originalBranch, originalHead: $originalHead,
|
|
148
|
+
taskBranch: $taskBranch,
|
|
149
|
+
recoveryCommit: ($recoveryCommit | if length == 0 then null else . end),
|
|
150
|
+
workspaceStrategy: $workspaceStrategy,
|
|
151
|
+
planningOnlyArchivedWithoutCommit: $planningOnlyArchivedWithoutCommit,
|
|
152
|
+
archivedDiffSha256: $archivedDiffSha256, pushed: false}' \
|
|
153
|
+
| automation_record_json "$evidence_dir/abort.json"
|
|
154
|
+
|
|
155
|
+
jq -nc \
|
|
156
|
+
--arg taskId "$task_id" \
|
|
157
|
+
--arg kind "abort" \
|
|
158
|
+
--arg at "$(automation_now)" \
|
|
159
|
+
--arg previousState "$current_state" \
|
|
160
|
+
--arg originalBranch "$original_branch" \
|
|
161
|
+
--arg recoveryCommit "$recovery_commit" \
|
|
162
|
+
'{taskId: $taskId, kind: $kind, at: $at,
|
|
163
|
+
previousState: $previousState, originalBranch: $originalBranch,
|
|
164
|
+
recoveryCommit: ($recoveryCommit | if length == 0 then null else . end)}' \
|
|
165
|
+
| automation_append_json "$(automation_approvals_path "$task_id")"
|
|
166
|
+
|
|
167
|
+
automation_transition_state "$task_id" "$current_state" "ABORTED" "human" "human aborted the task after deterministic archival"
|
|
168
|
+
automation_release_repository_lease "$task_id"
|
|
169
|
+
automation_release_run_lock
|
|
170
|
+
trap - EXIT
|
|
171
|
+
jq . "$evidence_dir/abort.json"
|
|
@@ -0,0 +1,263 @@
|
|
|
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 "FINAL-ACCEPTANCE"\n' "$0" >&2
|
|
12
|
+
exit 2
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
automation_validate_task_id "$task_id"
|
|
16
|
+
automation_require_orchestrated
|
|
17
|
+
automation_require_approval acceptance "$approval"
|
|
18
|
+
[[ "$(automation_read_state "$task_id")" == "AWAITING_HUMAN" ]] || automation_die "$task_id is not awaiting human acceptance"
|
|
19
|
+
[[ "$(automation_config_value '.pushAfterAcceptance')" == "false" ]] || automation_die "automatic push is forbidden"
|
|
20
|
+
[[ "$(automation_config_value '.originalBranchDriftPolicy')" == "block" ]] || automation_die "unsupported original branch drift policy"
|
|
21
|
+
|
|
22
|
+
origin_file="$(automation_origin_path "$task_id")"
|
|
23
|
+
workspace_file="$(automation_workspace_path "$task_id")"
|
|
24
|
+
evidence_dir="$(automation_evidence_path "$task_id")"
|
|
25
|
+
ready_file="$evidence_dir/ready.json"
|
|
26
|
+
review_file="$evidence_dir/review.json"
|
|
27
|
+
[[ -f "$origin_file" && -f "$workspace_file" && -f "$ready_file" && -f "$review_file" ]] || automation_die "integration evidence is incomplete"
|
|
28
|
+
|
|
29
|
+
source_root="$(jq -er '.sourceRoot' "$origin_file")"
|
|
30
|
+
original_branch="$(jq -er '.originalBranch' "$origin_file")"
|
|
31
|
+
baseline_head="$(jq -er '.baselineHead' "$workspace_file")"
|
|
32
|
+
task_root="$(automation_workspace_task_root "$workspace_file")"
|
|
33
|
+
task_branch="$(jq -er '.taskBranch' "$workspace_file")"
|
|
34
|
+
workspace_strategy="$(automation_workspace_strategy "$workspace_file")"
|
|
35
|
+
original_ref="refs/heads/$original_branch"
|
|
36
|
+
task_ref="refs/heads/$task_branch"
|
|
37
|
+
|
|
38
|
+
[[ "$source_root" == "$AUTOMATION_ROOT" ]] || automation_die "final acceptance must run from the recorded source root"
|
|
39
|
+
[[ -d "$task_root" ]] || automation_die "task root is missing: $task_root"
|
|
40
|
+
[[ "$task_branch" != "$original_branch" ]] || automation_die "task branch must differ from the recorded original branch"
|
|
41
|
+
git -C "$source_root" show-ref --verify --quiet "$task_ref" || automation_die "recorded task branch no longer exists: $task_branch"
|
|
42
|
+
[[ "$(automation_current_branch "$task_root")" == "$task_branch" ]] || automation_die "task branch identity changed"
|
|
43
|
+
[[ "$(git -C "$task_root" rev-parse HEAD)" == "$baseline_head" ]] || automation_die "task branch HEAD changed before deterministic commit"
|
|
44
|
+
automation_require_repository_lease "$task_id" "$source_root" "$workspace_strategy"
|
|
45
|
+
|
|
46
|
+
case "$workspace_strategy" in
|
|
47
|
+
inPlaceExclusive)
|
|
48
|
+
[[ "$task_root" == "$source_root" ]] || automation_die "in-place task root must equal the source root"
|
|
49
|
+
;;
|
|
50
|
+
isolatedWorktree)
|
|
51
|
+
[[ "$task_root" != "$source_root" ]] || automation_die "isolated task root must differ from the source root"
|
|
52
|
+
[[ "$(automation_current_branch "$source_root")" == "$original_branch" ]] || automation_die "source root is no longer on $original_branch"
|
|
53
|
+
automation_worktree_is_clean "$source_root" || automation_die "source root must be clean before integration"
|
|
54
|
+
;;
|
|
55
|
+
*)
|
|
56
|
+
automation_die "unsupported workspace strategy: $workspace_strategy"
|
|
57
|
+
;;
|
|
58
|
+
esac
|
|
59
|
+
|
|
60
|
+
git -C "$source_root" show-ref --verify --quiet "$original_ref" || automation_die "recorded original branch no longer exists: $original_branch"
|
|
61
|
+
source_head="$(git -C "$source_root" rev-parse "$original_ref")"
|
|
62
|
+
git -C "$source_root" merge-base --is-ancestor "$baseline_head" "$source_head" || automation_die "original branch no longer contains the recorded pre-task baseline"
|
|
63
|
+
|
|
64
|
+
automation_assert_planning_artifacts_sealed "$task_id" "$task_root"
|
|
65
|
+
sealed_diff_sha="$(automation_worktree_diff_sha "$task_root")"
|
|
66
|
+
[[ "$sealed_diff_sha" == "$(jq -er '.diffSha256' "$ready_file")" ]] || automation_die "task diff changed after quality gate"
|
|
67
|
+
[[ "$sealed_diff_sha" == "$(jq -er '.diffSha256' "$review_file")" ]] || automation_die "task diff changed after review"
|
|
68
|
+
[[ "$(jq -er '.decision' "$review_file")" == "APPROVED" ]] || automation_die "latest review is not approved"
|
|
69
|
+
|
|
70
|
+
(
|
|
71
|
+
cd "$task_root"
|
|
72
|
+
./scripts/automation/scope-gate.sh "$task_id" >/dev/null
|
|
73
|
+
./scripts/automation/acceptance-report.sh "$task_id" >/dev/null
|
|
74
|
+
)
|
|
75
|
+
report_file="$evidence_dir/acceptance-report.json"
|
|
76
|
+
[[ "$(jq -er '.sealedDiffSha256' "$report_file")" == "$sealed_diff_sha" ]] || automation_die "acceptance report is stale"
|
|
77
|
+
|
|
78
|
+
automation_acquire_run_lock "$task_id"
|
|
79
|
+
integration_complete=0
|
|
80
|
+
integration_exit() {
|
|
81
|
+
local exit_code=$?
|
|
82
|
+
trap - EXIT
|
|
83
|
+
if [[ "$integration_complete" != "1" ]] && [[ -f "$(automation_state_path "$task_id")" ]] && \
|
|
84
|
+
[[ "$(automation_read_state "$task_id" 2>/dev/null || true)" == "INTEGRATING" ]]; then
|
|
85
|
+
jq -n \
|
|
86
|
+
--arg taskId "$task_id" \
|
|
87
|
+
--arg failedAt "$(automation_now)" \
|
|
88
|
+
--arg originalBranch "$original_branch" \
|
|
89
|
+
--arg originalHead "$(git -C "$source_root" rev-parse "$original_ref" 2>/dev/null || true)" \
|
|
90
|
+
--arg productCommit "$(jq -r '.productCommit // ""' "$workspace_file" 2>/dev/null || true)" \
|
|
91
|
+
--argjson exitCode "$exit_code" \
|
|
92
|
+
'{taskId: $taskId, failedAt: $failedAt, exitCode: $exitCode,
|
|
93
|
+
originalBranch: $originalBranch, originalHead: $originalHead,
|
|
94
|
+
productCommit: ($productCommit | if length == 0 then null else . end),
|
|
95
|
+
message: "Integration stopped. Inspect the recorded refs before recovery; no automatic conflict resolution was attempted."}' \
|
|
96
|
+
| automation_record_json "$evidence_dir/integration-failure.json" || true
|
|
97
|
+
automation_transition_state "$task_id" "INTEGRATING" "INTEGRATION_BLOCKED" "integrator" "candidate verification or fast-forward failed" || true
|
|
98
|
+
fi
|
|
99
|
+
automation_release_run_lock
|
|
100
|
+
exit "$exit_code"
|
|
101
|
+
}
|
|
102
|
+
trap integration_exit EXIT
|
|
103
|
+
|
|
104
|
+
jq -nc \
|
|
105
|
+
--arg taskId "$task_id" \
|
|
106
|
+
--arg kind "acceptance" \
|
|
107
|
+
--arg at "$(automation_now)" \
|
|
108
|
+
--arg originalBranch "$original_branch" \
|
|
109
|
+
--arg sealedDiffSha256 "$sealed_diff_sha" \
|
|
110
|
+
'{taskId: $taskId, kind: $kind, at: $at,
|
|
111
|
+
originalBranch: $originalBranch, sealedDiffSha256: $sealedDiffSha256}' \
|
|
112
|
+
| automation_append_json "$(automation_approvals_path "$task_id")"
|
|
113
|
+
jq -n \
|
|
114
|
+
--arg taskId "$task_id" \
|
|
115
|
+
--arg acceptedAt "$(automation_now)" \
|
|
116
|
+
--arg originalBranch "$original_branch" \
|
|
117
|
+
--arg sealedDiffSha256 "$sealed_diff_sha" \
|
|
118
|
+
'{taskId: $taskId, acceptedAt: $acceptedAt,
|
|
119
|
+
originalBranch: $originalBranch, sealedDiffSha256: $sealedDiffSha256}' \
|
|
120
|
+
| automation_record_json "$evidence_dir/acceptance.json"
|
|
121
|
+
|
|
122
|
+
automation_transition_state "$task_id" "AWAITING_HUMAN" "INTEGRATING" "integrator" "human accepted the sealed diff for the recorded original branch"
|
|
123
|
+
|
|
124
|
+
product_paths=()
|
|
125
|
+
while IFS= read -r path; do
|
|
126
|
+
[[ -n "$path" ]] && product_paths+=("$path")
|
|
127
|
+
done < <(automation_product_changed_paths_at "$task_id" "$task_root")
|
|
128
|
+
[[ "${#product_paths[@]}" -gt 0 ]] || automation_die "no product paths remain to commit with the planning artifacts"
|
|
129
|
+
|
|
130
|
+
commit_paths=()
|
|
131
|
+
while IFS= read -r path; do
|
|
132
|
+
[[ -n "$path" ]] && commit_paths+=("$path")
|
|
133
|
+
done < <(automation_changed_paths_at "$task_root")
|
|
134
|
+
[[ "${#commit_paths[@]}" -ge 3 ]] || automation_die "final commit must contain product changes and both planning artifacts"
|
|
135
|
+
git -C "$task_root" add -- "${commit_paths[@]}"
|
|
136
|
+
title="$(jq -er '.title' "$task_root/automation/tasks/$task_id.json")"
|
|
137
|
+
git -C "$task_root" commit -m "Implement $title ($task_id)"
|
|
138
|
+
product_commit="$(git -C "$task_root" rev-parse HEAD)"
|
|
139
|
+
automation_worktree_is_clean "$task_root" || automation_die "task root is dirty after the combined task commit"
|
|
140
|
+
git -C "$task_root" merge-base --is-ancestor "$baseline_head" "$product_commit" || automation_die "combined task commit is not based on the recorded pre-task baseline"
|
|
141
|
+
[[ "$(git -C "$task_root" rev-list --count "$baseline_head..$product_commit")" -eq 1 ]] || \
|
|
142
|
+
automation_die "task history must contain exactly one combined product-and-planning commit"
|
|
143
|
+
|
|
144
|
+
jq \
|
|
145
|
+
--arg contractCommit "$product_commit" \
|
|
146
|
+
--arg committedAt "$(automation_now)" \
|
|
147
|
+
'.contractCommit = $contractCommit |
|
|
148
|
+
.planningArtifactsCommittedWithProduct = true |
|
|
149
|
+
.planningArtifactsCommittedAt = $committedAt' \
|
|
150
|
+
"$origin_file" | automation_record_json "$origin_file"
|
|
151
|
+
|
|
152
|
+
jq \
|
|
153
|
+
--arg productCommit "$product_commit" \
|
|
154
|
+
--arg candidateHead "$product_commit" \
|
|
155
|
+
--arg sourceHeadAtIntegration "$source_head" \
|
|
156
|
+
--arg integrationMethod "$workspace_strategy-fast-forward" \
|
|
157
|
+
--arg updatedAt "$(automation_now)" \
|
|
158
|
+
'.productCommit = $productCommit |
|
|
159
|
+
.candidateHead = $candidateHead |
|
|
160
|
+
.sourceHeadAtIntegration = $sourceHeadAtIntegration |
|
|
161
|
+
.integrationMethod = $integrationMethod |
|
|
162
|
+
.updatedAt = $updatedAt' \
|
|
163
|
+
"$workspace_file" | automation_record_json "$workspace_file"
|
|
164
|
+
|
|
165
|
+
[[ "$source_head" == "$baseline_head" ]] || automation_die "original branch drifted from $baseline_head to $source_head; policy requires manual resolution"
|
|
166
|
+
[[ "$(automation_file_sha256 "$task_root/automation/tasks/$task_id.json")" == "$(jq -er '.contractSha256' "$origin_file")" ]] || \
|
|
167
|
+
automation_die "approved contract changed on the verified candidate"
|
|
168
|
+
|
|
169
|
+
set +e
|
|
170
|
+
(
|
|
171
|
+
cd "$task_root"
|
|
172
|
+
./scripts/automation/verify-integration.sh "$task_id" "$baseline_head" "$product_commit"
|
|
173
|
+
) 2>&1 | tee "$evidence_dir/integration-verification.log"
|
|
174
|
+
verification_status=${PIPESTATUS[0]}
|
|
175
|
+
set -e
|
|
176
|
+
[[ "$verification_status" -eq 0 ]] || automation_die "integration verification failed with exit $verification_status"
|
|
177
|
+
|
|
178
|
+
[[ "$(git -C "$source_root" rev-parse "$original_ref")" == "$baseline_head" ]] || automation_die "original branch advanced during candidate verification"
|
|
179
|
+
if [[ "$workspace_strategy" == "inPlaceExclusive" ]]; then
|
|
180
|
+
[[ "$(automation_current_branch "$source_root")" == "$task_branch" ]] || automation_die "in-place task branch changed during verification"
|
|
181
|
+
git -C "$source_root" switch "$original_branch"
|
|
182
|
+
else
|
|
183
|
+
[[ "$(automation_current_branch "$source_root")" == "$original_branch" ]] || automation_die "source branch changed during candidate verification"
|
|
184
|
+
[[ "$(git -C "$source_root" rev-parse HEAD)" == "$baseline_head" ]] || automation_die "source HEAD changed during candidate verification"
|
|
185
|
+
automation_worktree_is_clean "$source_root" || automation_die "source root became dirty during candidate verification"
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
git -C "$source_root" merge --ff-only "$product_commit"
|
|
189
|
+
integrated_head="$(git -C "$source_root" rev-parse HEAD)"
|
|
190
|
+
[[ "$integrated_head" == "$product_commit" ]] || automation_die "original branch did not reach the verified combined task commit"
|
|
191
|
+
integration_method="$workspace_strategy-fast-forward"
|
|
192
|
+
cleanup_log="$evidence_dir/cleanup.log"
|
|
193
|
+
task_worktree_disposition="source-root-reused"
|
|
194
|
+
|
|
195
|
+
if [[ "$workspace_strategy" == "isolatedWorktree" ]]; then
|
|
196
|
+
if [[ "$(automation_config_value '.autoCleanupWorktrees')" == "true" ]]; then
|
|
197
|
+
git -C "$source_root" worktree remove "$task_root" >> "$cleanup_log" 2>&1 || \
|
|
198
|
+
automation_die "could not remove isolated task worktree: $task_root"
|
|
199
|
+
printf '%s removed isolated task worktree %s\n' "$(automation_now)" "$task_root" >> "$cleanup_log"
|
|
200
|
+
task_worktree_disposition="removed"
|
|
201
|
+
else
|
|
202
|
+
git -C "$task_root" switch --detach "$product_commit" >> "$cleanup_log" 2>&1 || \
|
|
203
|
+
automation_die "could not detach retained isolated task worktree: $task_root"
|
|
204
|
+
printf '%s retained isolated task worktree in detached state at %s\n' \
|
|
205
|
+
"$(automation_now)" "$product_commit" >> "$cleanup_log"
|
|
206
|
+
task_worktree_disposition="retained-detached"
|
|
207
|
+
fi
|
|
208
|
+
fi
|
|
209
|
+
|
|
210
|
+
[[ "$(git -C "$source_root" rev-parse "$task_ref")" == "$product_commit" ]] || \
|
|
211
|
+
automation_die "task branch no longer points to the verified combined task commit"
|
|
212
|
+
[[ "$(automation_current_branch "$source_root")" == "$original_branch" ]] || \
|
|
213
|
+
automation_die "source root must be on the original branch before task branch deletion"
|
|
214
|
+
git -C "$source_root" branch -d -- "$task_branch" >> "$cleanup_log" 2>&1 || \
|
|
215
|
+
automation_die "could not safely delete integrated task branch: $task_branch"
|
|
216
|
+
if git -C "$source_root" show-ref --verify --quiet "$task_ref"; then
|
|
217
|
+
automation_die "integrated task branch still exists after deletion: $task_branch"
|
|
218
|
+
fi
|
|
219
|
+
task_branch_deleted_at="$(automation_now)"
|
|
220
|
+
printf '%s deleted integrated local task branch %s at %s\n' \
|
|
221
|
+
"$task_branch_deleted_at" "$task_branch" "$product_commit" >> "$cleanup_log"
|
|
222
|
+
|
|
223
|
+
jq \
|
|
224
|
+
--arg taskBranchDeletedAt "$task_branch_deleted_at" \
|
|
225
|
+
--arg taskWorktreeDisposition "$task_worktree_disposition" \
|
|
226
|
+
--arg updatedAt "$(automation_now)" \
|
|
227
|
+
'.taskBranchDeleted = true |
|
|
228
|
+
.taskBranchDeletedAt = $taskBranchDeletedAt |
|
|
229
|
+
.taskWorktreeDisposition = $taskWorktreeDisposition |
|
|
230
|
+
.updatedAt = $updatedAt' \
|
|
231
|
+
"$workspace_file" | automation_record_json "$workspace_file"
|
|
232
|
+
|
|
233
|
+
jq -n \
|
|
234
|
+
--arg taskId "$task_id" \
|
|
235
|
+
--arg integratedAt "$(automation_now)" \
|
|
236
|
+
--arg originalBranch "$original_branch" \
|
|
237
|
+
--arg sourceHeadBeforeIntegration "$source_head" \
|
|
238
|
+
--arg productCommit "$product_commit" \
|
|
239
|
+
--arg integratedHead "$integrated_head" \
|
|
240
|
+
--arg method "$integration_method" \
|
|
241
|
+
--arg workspaceStrategy "$workspace_strategy" \
|
|
242
|
+
--arg taskBranch "$task_branch" \
|
|
243
|
+
--arg taskBranchDeletedAt "$task_branch_deleted_at" \
|
|
244
|
+
--arg taskWorktreeDisposition "$task_worktree_disposition" \
|
|
245
|
+
--argjson verificationExitCode "$verification_status" \
|
|
246
|
+
'{taskId: $taskId, integratedAt: $integratedAt,
|
|
247
|
+
originalBranch: $originalBranch,
|
|
248
|
+
sourceHeadBeforeIntegration: $sourceHeadBeforeIntegration,
|
|
249
|
+
productCommit: $productCommit, integratedHead: $integratedHead,
|
|
250
|
+
method: $method, workspaceStrategy: $workspaceStrategy,
|
|
251
|
+
taskBranch: $taskBranch, taskBranchDeleted: true,
|
|
252
|
+
taskBranchDeletedAt: $taskBranchDeletedAt,
|
|
253
|
+
taskWorktreeDisposition: $taskWorktreeDisposition,
|
|
254
|
+
verificationExitCode: $verificationExitCode, pushed: false}' \
|
|
255
|
+
| automation_record_json "$evidence_dir/integration.json"
|
|
256
|
+
|
|
257
|
+
automation_transition_state "$task_id" "INTEGRATING" "COMPLETED" "integrator" "verified combined task commit fast-forwarded into the recorded original branch and the integrated local task branch was deleted; not pushed"
|
|
258
|
+
integration_complete=1
|
|
259
|
+
|
|
260
|
+
automation_release_repository_lease "$task_id"
|
|
261
|
+
automation_release_run_lock
|
|
262
|
+
trap - EXIT
|
|
263
|
+
jq . "$evidence_dir/integration.json"
|