@moretea-labs/forge 1.7.0 → 1.7.2

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.
Files changed (175) hide show
  1. package/AGENTS.md +2 -0
  2. package/CHANGELOG.md +26 -0
  3. package/CLAUDE.md +2 -0
  4. package/THIRD_PARTY_NOTICES.md +1 -1
  5. package/agents/openai.yaml +1 -1
  6. package/assets/partials-agents/02-operating-mode.partial.md +2 -0
  7. package/assets/plugin-registry.v1.json +3 -3
  8. package/assets/skill-version.json +2 -2
  9. package/assets/templates/helpers/check-architecture-sync.sh +17 -0
  10. package/assets/templates/helpers/inspect-project-state.ts +8 -6
  11. package/assets/templates/helpers/migrate-workflow-docs.ts +9 -7
  12. package/assets/templates/helpers/workflow-contract.ts +5 -1
  13. package/docs/architecture/CURRENT.md +12 -4
  14. package/docs/forge-plugin-management.md +1 -1
  15. package/docs/operations/controller-performance-and-502.md +19 -0
  16. package/docs/operations/homebrew.md +2 -2
  17. package/package.json +5 -2
  18. package/scripts/architecture-event.ts +2 -820
  19. package/scripts/architecture-queue.sh +2 -645
  20. package/scripts/archive-architecture-request.sh +2 -242
  21. package/scripts/archive-workflow.sh +1 -291
  22. package/scripts/benchmark-work-efficiency.ts +0 -0
  23. package/scripts/capability-config.ts +2 -392
  24. package/scripts/capability-resolver.ts +2 -503
  25. package/scripts/capture-plan.sh +1 -332
  26. package/scripts/check-agent-tooling.sh +2 -1531
  27. package/scripts/check-architecture-sync.sh +2 -302
  28. package/scripts/check-brain-manifest.sh +2 -253
  29. package/scripts/check-context-files.sh +2 -47
  30. package/scripts/check-deploy-sql-order.sh +2 -115
  31. package/scripts/check-runtime-architecture.mjs +243 -16
  32. package/scripts/check-skill-version.ts +3 -191
  33. package/scripts/check-source-duplication.mjs +61 -0
  34. package/scripts/check-stable-baseline.ts +0 -0
  35. package/scripts/check-task-sync.sh +2 -63
  36. package/scripts/check-task-workflow.sh +2 -1000
  37. package/scripts/codex-handoff-resume.sh +2 -267
  38. package/scripts/context-contract-sync.sh +2 -514
  39. package/scripts/contract-run.ts +2 -463
  40. package/scripts/contract-worktree.sh +1 -702
  41. package/scripts/ensure-task-workflow.sh +1 -1192
  42. package/scripts/inspect-project-state.ts +4 -279
  43. package/scripts/lib/project-init-lib.sh +7 -2
  44. package/scripts/lib/run-forge-helper.sh +19 -0
  45. package/scripts/lib/run-forge-helper.ts +26 -0
  46. package/scripts/migrate-project-template.sh +1 -33
  47. package/scripts/migrate-workflow-docs.ts +4 -412
  48. package/scripts/new-plan.sh +1 -180
  49. package/scripts/new-spec.sh +1 -47
  50. package/scripts/new-sprint.sh +1 -46
  51. package/scripts/plan-to-todo.sh +1 -771
  52. package/scripts/prepare-codex-handoff.sh +2 -201
  53. package/scripts/prepare-handoff.sh +1 -78
  54. package/scripts/refresh-current-status.sh +2 -467
  55. package/scripts/run-governed-gate.ts +3 -0
  56. package/scripts/select-agent-context-blocks.sh +2 -64
  57. package/scripts/ship-worktrees.sh +1 -531
  58. package/scripts/sprint-backlog.sh +1 -834
  59. package/scripts/stage-runtime-release.ts +2 -1
  60. package/scripts/summarize-failures.sh +2 -123
  61. package/scripts/switch-plan.sh +1 -150
  62. package/scripts/sync-brain-docs.sh +2 -379
  63. package/scripts/sync-generated-authority.mjs +175 -0
  64. package/scripts/verify-contract.sh +2 -593
  65. package/scripts/verify-sprint.sh +1 -500
  66. package/scripts/workflow-contract.ts +4 -201
  67. package/scripts/workstream-sync.sh +2 -261
  68. package/src/cli/agent-jobs/executor-health.ts +1 -1
  69. package/src/cli/commands/plugin.ts +12 -3
  70. package/src/cli/controller/check-runner.ts +7 -2
  71. package/src/cli/controller/context-pack.ts +46 -21
  72. package/src/cli/github/contracts.ts +16 -0
  73. package/src/cli/github/github.ts +2 -16
  74. package/src/cli/github/session.ts +2 -1
  75. package/src/cli/local-bridge/facade-api.ts +37 -47
  76. package/src/cli/local-bridge/server.ts +2 -2
  77. package/src/cli/mcp/legacy-tool-service.ts +10 -33
  78. package/src/cli/mcp/post-finalize-work-attribution.ts +150 -0
  79. package/src/cli/mcp/repository-tools.ts +125 -101
  80. package/src/cli/mcp/server.ts +57 -13
  81. package/src/cli/mcp/tool-contract.ts +14 -0
  82. package/src/cli/mcp/toolset.ts +2 -2
  83. package/src/cli/mcp/transports/http.ts +23 -0
  84. package/src/cli/repositories/command-classifier.ts +2 -2
  85. package/src/cli/repositories/command-executor.ts +24 -6
  86. package/src/cli/repositories/repository-snapshot.ts +5 -2
  87. package/src/cli/repositories/structured-git.ts +57 -3
  88. package/src/cli/repository/inspector.ts +11 -3
  89. package/src/runtime/context/generic-lsp-provider.ts +1 -1
  90. package/src/runtime/context/semantic-navigation-contract.ts +61 -0
  91. package/src/runtime/context/semantic-navigation.ts +9 -61
  92. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +69 -29
  93. package/src/runtime/control-plane/execution/repository-work-attribution.ts +106 -0
  94. package/src/runtime/control-plane/execution/retained-work-resume.ts +203 -0
  95. package/src/runtime/control-plane/execution/work-completion-authority.ts +58 -0
  96. package/src/runtime/control-plane/execution/work-evidence-policy.ts +200 -0
  97. package/src/runtime/control-plane/execution/work-execution-support.ts +386 -0
  98. package/src/runtime/control-plane/execution/work-finalization-service.ts +1913 -0
  99. package/src/runtime/control-plane/execution/work-handle-authority.ts +150 -0
  100. package/src/runtime/control-plane/execution/work-handle-store.ts +1 -1
  101. package/src/runtime/control-plane/execution/work-operation-service.ts +468 -0
  102. package/src/runtime/control-plane/execution/work-path-scope.ts +37 -0
  103. package/src/runtime/control-plane/execution/work-preparation-service.ts +479 -0
  104. package/src/runtime/control-plane/execution/work-process-evidence.ts +129 -0
  105. package/src/runtime/control-plane/execution/work-task-receipt.ts +4 -43
  106. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +13 -3
  107. package/src/runtime/control-plane/execution/work-verification-context.ts +56 -0
  108. package/src/runtime/control-plane/execution/work-verification-service.ts +388 -0
  109. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +5 -1
  110. package/src/runtime/control-plane/facade/controller-round-relay.ts +164 -37
  111. package/src/runtime/control-plane/facade/controller-session-store.ts +183 -24
  112. package/src/runtime/control-plane/facade/goal-workloop-access.ts +15 -1
  113. package/src/runtime/control-plane/facade/goal-workloop.ts +329 -148
  114. package/src/runtime/control-plane/facade/index.ts +2 -0
  115. package/src/runtime/control-plane/facade/plan-contract-store.ts +90 -22
  116. package/src/runtime/control-plane/facade/repository-work-admission.ts +147 -0
  117. package/src/runtime/control-plane/facade/requirement-authority.ts +83 -0
  118. package/src/runtime/control-plane/facade/types.ts +68 -5
  119. package/src/runtime/control-plane/facade/work-contract-store.ts +3 -209
  120. package/src/runtime/control-plane/facade/work-state-machine.ts +250 -0
  121. package/src/runtime/control-plane/global-scheduler/maintenance.ts +14 -7
  122. package/src/runtime/control-plane/global-scheduler/scheduler.ts +5 -1
  123. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +13 -0
  124. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +15 -6
  125. package/src/runtime/control-plane/launcher/launch-reservation-store.ts +54 -5
  126. package/src/runtime/control-plane/launcher/thin-launcher.ts +56 -7
  127. package/src/runtime/control-plane/persistence/requirement-store.ts +0 -12
  128. package/src/runtime/control-plane/repo-actor/registry.ts +4 -2
  129. package/src/runtime/control-plane/routing/route-policy.ts +18 -6
  130. package/src/runtime/control-plane/routing/workspace-admission.ts +70 -0
  131. package/src/runtime/evidence/process-check-execution.ts +18 -0
  132. package/src/runtime/execution/jobs/child-reference.ts +6 -2
  133. package/src/runtime/execution/process-runtime/check-result.ts +53 -18
  134. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +7 -1
  135. package/src/runtime/execution/process-runtime/command-facade.ts +39 -17
  136. package/src/runtime/execution/process-runtime/lightweight-managed.ts +20 -3
  137. package/src/runtime/execution/process-runtime/persisted-check.ts +4 -1
  138. package/src/runtime/execution/process-runtime/runtime.ts +10 -0
  139. package/src/runtime/execution/process-runtime/types.ts +2 -12
  140. package/src/runtime/execution/work-activity.ts +21 -0
  141. package/src/runtime/execution/workers/executor.ts +18 -13
  142. package/src/runtime/gateway/mcp/execution-tools.ts +35 -2930
  143. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +61 -0
  144. package/src/runtime/gateway/mcp/router.ts +2 -2
  145. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +21 -2
  146. package/src/runtime/gateway/mcp/runtime-tools.ts +815 -1015
  147. package/src/runtime/plugins/app-store-connect-adapter.ts +93 -28
  148. package/src/runtime/plugins/browser-adapter.ts +256 -1083
  149. package/src/runtime/plugins/browser-handoff-host.ts +2 -25
  150. package/src/runtime/plugins/browser-handoff.ts +18 -3
  151. package/src/runtime/plugins/browser-macos-bridge.ts +121 -16
  152. package/src/runtime/plugins/browser-manifest-surface.ts +603 -0
  153. package/src/runtime/plugins/browser-native-session-service.ts +222 -0
  154. package/src/runtime/plugins/browser-runtime-contract.ts +2 -0
  155. package/src/runtime/plugins/browser-runtime.ts +10 -0
  156. package/src/runtime/plugins/browser-session-store.ts +121 -0
  157. package/src/runtime/plugins/browser-session-types.ts +98 -0
  158. package/src/runtime/plugins/desktop-operator-registration.ts +4 -4
  159. package/src/runtime/plugins/external-adapter.ts +92 -58
  160. package/src/runtime/plugins/macos-capability-broker.ts +0 -8
  161. package/src/runtime/plugins/store.ts +66 -9
  162. package/src/runtime/plugins/types.ts +3 -1
  163. package/src/runtime/recovery/maintenance-executor.ts +61 -25
  164. package/src/runtime/root/package-connector-service.ts +46 -4
  165. package/src/runtime/root/package-runtime-release.ts +3 -2
  166. package/src/runtime/root/package-runtime-service.ts +48 -3
  167. package/src/runtime/root/release-manifest.ts +137 -16
  168. package/src/runtime/root/release-materialize.ts +109 -35
  169. package/src/runtime/root/types.ts +10 -0
  170. package/src/runtime/standalone-recovery/core.ts +425 -194
  171. package/src/runtime/standalone-recovery/entry.ts +1 -1
  172. package/src/runtime/standalone-recovery/installer.ts +1 -1
  173. package/src/runtime/workflow/schedules/engine.ts +113 -87
  174. package/src/runtime/workflow/schedules/settlement.ts +26 -23
  175. package/src/runtime/workflow/schedules/store.ts +79 -10
@@ -1,503 +1,4 @@
1
1
  #!/bin/bash
2
2
  set -euo pipefail
3
-
4
3
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
- if REPO_ROOT="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null)"; then
6
- cd "$REPO_ROOT"
7
- elif [[ "$SCRIPT_DIR" == */.ai/harness/scripts ]]; then
8
- cd "$SCRIPT_DIR/../../.."
9
- else
10
- cd "$SCRIPT_DIR/.."
11
- fi
12
-
13
- json_escape() {
14
- local value="$1"
15
- value="${value//\\/\\\\}"
16
- value="${value//\"/\\\"}"
17
- value="${value//$'\n'/\\n}"
18
- value="${value//$'\r'/\\r}"
19
- value="${value//$'\t'/\\t}"
20
- printf '%s' "$value"
21
- }
22
-
23
- read_contract_task_profile() {
24
- local file="$1"
25
- awk '/^> \*\*Task Profile\*\*:/ {sub(/^.*> \*\*Task Profile\*\*:[[:space:]]*/, ""); gsub(/\r/, ""); print; exit}' "$file" | xargs
26
- }
27
-
28
- contract_allowed_paths() {
29
- local file="$1"
30
- awk '
31
- BEGIN { in_block = 0; block = ""; found = 0 }
32
- /^```yaml[[:space:]]*$/ {
33
- in_block = 1
34
- block = ""
35
- next
36
- }
37
- /^```[[:space:]]*$/ && in_block == 1 {
38
- if (!found && block ~ /(^|[[:space:]])allowed_paths:/) {
39
- printf "%s", block
40
- found = 1
41
- }
42
- in_block = 0
43
- block = ""
44
- next
45
- }
46
- in_block == 1 {
47
- block = block $0 ORS
48
- }
49
- ' "$file" | awk '
50
- function trim(s) {
51
- gsub(/^[[:space:]]+/, "", s)
52
- gsub(/[[:space:]]+$/, "", s)
53
- return s
54
- }
55
- /^[[:space:]]*allowed_paths:[[:space:]]*$/ { in_paths = 1; next }
56
- in_paths && /^[^[:space:]]/ { exit }
57
- in_paths && /^[[:space:]]*-[[:space:]]*/ {
58
- line = $0
59
- sub(/^[[:space:]]*-[[:space:]]*/, "", line)
60
- gsub(/^["'\''`]+|["'\''`]+$/, "", line)
61
- print trim(line)
62
- }
63
- '
64
- }
65
-
66
- read_active_plan() {
67
- local marker plan
68
- if declare -F workflow_active_plan >/dev/null 2>&1; then
69
- workflow_active_plan || true
70
- return 0
71
- fi
72
- for marker in ".ai/harness/active-plan" ".claude/.active-plan"; do
73
- if [[ -f "$marker" ]]; then
74
- plan="$(cat "$marker" 2>/dev/null | xargs)"
75
- if [[ -n "$plan" ]]; then
76
- printf '%s' "$plan"
77
- return 0
78
- fi
79
- fi
80
- done
81
- }
82
-
83
- active_plan_declared_path() {
84
- local label="$1"
85
- local active_plan
86
- active_plan="$(read_active_plan || true)"
87
- [[ -n "$active_plan" && -f "$active_plan" ]] || return 1
88
- awk -v label="$label" '
89
- BEGIN { pattern = "^> \\*\\*" label "\\*\\*:" }
90
- $0 ~ pattern {
91
- sub(pattern "[[:space:]]*", "")
92
- gsub(/`/, "")
93
- gsub(/\r/, "")
94
- print
95
- exit
96
- }
97
- ' "$active_plan" | xargs
98
- }
99
-
100
- git_changed_files_json() {
101
- local changed_file
102
- if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1 || ! command -v jq >/dev/null 2>&1; then
103
- printf '[]'
104
- return 0
105
- fi
106
- while IFS= read -r changed_file; do
107
- if [[ -n "$changed_file" ]] && ! ignore_changed_file_for_scope "$changed_file"; then
108
- printf '%s\n' "$changed_file"
109
- fi
110
- done < <(git_changed_files_list | awk 'NF && !seen[$0]++') | jq -R -s 'split("\n") | map(select(length > 0))'
111
- }
112
-
113
- git_diff_base_ref() {
114
- local branch
115
- if [[ -n "${FORGE_DIFF_BASE:-}" ]]; then
116
- printf '%s' "$FORGE_DIFF_BASE"
117
- return 0
118
- fi
119
- if [[ -n "${HARNESS_DIFF_BASE:-}" ]]; then
120
- printf '%s' "$HARNESS_DIFF_BASE"
121
- return 0
122
- fi
123
- if [[ -n "${GITHUB_BASE_REF:-}" ]]; then
124
- if git rev-parse --verify "origin/${GITHUB_BASE_REF}^{commit}" >/dev/null 2>&1; then
125
- printf 'origin/%s' "$GITHUB_BASE_REF"
126
- else
127
- printf '%s' "$GITHUB_BASE_REF"
128
- fi
129
- return 0
130
- fi
131
-
132
- branch="$(git branch --show-current 2>/dev/null || true)"
133
- if [[ "$branch" != "main" ]] && git rev-parse --verify "origin/main^{commit}" >/dev/null 2>&1; then
134
- printf 'origin/main'
135
- return 0
136
- fi
137
- if [[ "$branch" != "main" ]] && git rev-parse --verify "main^{commit}" >/dev/null 2>&1; then
138
- printf 'main'
139
- return 0
140
- fi
141
-
142
- return 1
143
- }
144
-
145
- git_diff_merge_base() {
146
- local base_ref
147
- base_ref="$(git_diff_base_ref || true)"
148
- [[ -n "$base_ref" ]] || return 1
149
- git rev-parse --verify "$base_ref^{commit}" >/dev/null 2>&1 || return 1
150
- git merge-base HEAD "$base_ref" 2>/dev/null
151
- }
152
-
153
- git_changed_files_list() {
154
- local merge_base
155
- if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
156
- return 0
157
- fi
158
-
159
- merge_base="$(git_diff_merge_base || true)"
160
- if [[ -n "$merge_base" ]]; then
161
- git -c core.quotePath=false diff --name-only "$merge_base" HEAD 2>/dev/null || true
162
- fi
163
- git -c core.quotePath=false diff --name-only HEAD 2>/dev/null || true
164
- git -c core.quotePath=false ls-files --others --exclude-standard 2>/dev/null || true
165
- }
166
-
167
- allowed_paths_json() {
168
- local file="$1"
169
- if ! command -v jq >/dev/null 2>&1; then
170
- printf '[]'
171
- return 0
172
- fi
173
- contract_allowed_paths "$file" | jq -R -s 'split("\n") | map(select(length > 0))'
174
- }
175
-
176
- path_under_allowed_prefix() {
177
- local path="$1"
178
- local prefix="$2"
179
- prefix="${prefix%/}"
180
- [[ -n "$prefix" ]] || return 1
181
- [[ "$path" == "$prefix" || "$path" == "$prefix/"* ]]
182
- }
183
-
184
- ignore_changed_file_for_scope() {
185
- case "$1" in
186
- .ai/harness/active-plan|.ai/harness/active-worktree|.claude/.active-plan)
187
- return 0
188
- ;;
189
- esac
190
- return 1
191
- }
192
-
193
- allowed_paths_check_json() {
194
- local file="$1"
195
- shift
196
- local changed_file allowed_path outside=0 checked=0
197
- local outside_file=""
198
- local allowed_paths=()
199
- local changed_files=("$@")
200
-
201
- if ! command -v jq >/dev/null 2>&1; then
202
- printf '{"status":"unavailable","message":"jq unavailable"}'
203
- return 0
204
- fi
205
-
206
- while IFS= read -r allowed_path; do
207
- [[ -n "$allowed_path" ]] && allowed_paths+=("$allowed_path")
208
- done < <(contract_allowed_paths "$file")
209
-
210
- if ((${#allowed_paths[@]} == 0)); then
211
- if ((${#changed_files[@]} == 0)); then
212
- jq -n '{status:"unavailable", checked:false, message:"contract has no allowed_paths and no changed files were detected", allowed_paths: [], outside: []}'
213
- else
214
- jq -n \
215
- --argjson outside "$(printf '%s\n' "${changed_files[@]+"${changed_files[@]}"}" | jq -R -s 'split("\n") | map(select(length > 0))')" \
216
- '{status:"fail", checked:true, message:"contract has no allowed_paths", allowed_paths: [], outside:$outside}'
217
- fi
218
- return 0
219
- fi
220
-
221
- for changed_file in "${changed_files[@]+"${changed_files[@]}"}"; do
222
- [[ -n "$changed_file" ]] || continue
223
- if ignore_changed_file_for_scope "$changed_file"; then
224
- continue
225
- fi
226
- checked=1
227
- local matched=0
228
- for allowed_path in "${allowed_paths[@]+"${allowed_paths[@]}"}"; do
229
- if path_under_allowed_prefix "$changed_file" "$allowed_path"; then
230
- matched=1
231
- break
232
- fi
233
- done
234
- if [[ "$matched" -eq 0 ]]; then
235
- outside=1
236
- outside_file="${outside_file}${changed_file}"$'\n'
237
- fi
238
- done
239
-
240
- if [[ "$outside" -eq 0 ]]; then
241
- contract_allowed_paths "$file" | jq -R -s --arg checked "$checked" '{
242
- status: "pass",
243
- checked: ($checked == "1"),
244
- allowed_paths: (split("\n") | map(select(length > 0))),
245
- outside: []
246
- }'
247
- else
248
- jq -n \
249
- --argjson allowed "$(allowed_paths_json "$file")" \
250
- --argjson outside "$(printf '%s' "$outside_file" | jq -R -s 'split("\n") | map(select(length > 0))')" \
251
- '{status:"fail", checked:true, allowed_paths:$allowed, outside:$outside}'
252
- fi
253
- }
254
-
255
- if [[ -f ".ai/hooks/lib/workflow-state.sh" ]]; then
256
- # shellcheck source=/dev/null
257
- . ".ai/hooks/lib/workflow-state.sh"
258
- contract_file="$(workflow_active_contract || true)"
259
- checks_file="$(workflow_checks_file)"
260
- else
261
- contract_file="$(find tasks/contracts -maxdepth 1 -name '*.contract.md' -type f 2>/dev/null | sort | head -n 1)"
262
- checks_file=".ai/harness/checks/latest.json"
263
- fi
264
- if [[ -z "$contract_file" || ! -f "$contract_file" ]]; then
265
- contract_file="$(active_plan_declared_path "Task Contract" || active_plan_declared_path "Sprint Contract" || true)"
266
- fi
267
- [[ -n "$contract_file" && -f "$contract_file" ]] || { echo "No active sprint contract found" >&2; exit 1; }
268
-
269
- generated_at="$(date '+%Y-%m-%dT%H:%M:%S%z')"
270
- run_stamp="$(date '+%Y%m%dT%H%M%S')"
271
- run_id="${HOOK_RUN_ID:-${CLAUDE_RUN_ID:-${CODEX_RUN_ID:-run-${run_stamp}-$$}}}"
272
- safe_run_id="$(printf '%s' "$run_id" | sed -E 's/[^A-Za-z0-9._-]+/-/g')"
273
- contract_slug="$(basename "$contract_file" | sed -E 's/\.contract\.md$//')"
274
- safe_contract_slug="$(printf '%s' "$contract_slug" | sed -E 's/[^A-Za-z0-9._-]+/-/g')"
275
- runs_dir=".ai/harness/runs"
276
- if declare -F workflow_runs_dir >/dev/null 2>&1; then
277
- runs_dir="$(workflow_runs_dir)"
278
- fi
279
- run_file="${runs_dir}/${safe_run_id}-${safe_contract_slug}.json"
280
-
281
- mkdir -p "$(dirname "$checks_file")"
282
- mkdir -p "$runs_dir"
283
- contract_report="$(mktemp)"
284
- checks_report="$(mktemp)"
285
- trap 'rm -f "$contract_report" "$checks_report"' EXIT
286
- task_profile="$(read_contract_task_profile "$contract_file" || true)"
287
- active_plan="$(read_active_plan || true)"
288
- worktree_path="$(pwd -P)"
289
- branch_name="$(git branch --show-current 2>/dev/null || true)"
290
- diff_base_ref="$(git_diff_base_ref || true)"
291
- diff_base_commit="$(git_diff_merge_base || true)"
292
- changed_files=()
293
- if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
294
- while IFS= read -r changed_file; do
295
- if [[ -n "$changed_file" ]] && ! ignore_changed_file_for_scope "$changed_file"; then
296
- changed_files+=("$changed_file")
297
- fi
298
- done < <(git_changed_files_list | awk 'NF && !seen[$0]++')
299
- fi
300
- allowed_paths_check="$(allowed_paths_check_json "$contract_file" "${changed_files[@]+"${changed_files[@]}"}")"
301
- allowed_paths_status="unavailable"
302
- if command -v jq >/dev/null 2>&1; then
303
- allowed_paths_status="$(printf '%s' "$allowed_paths_check" | jq -r '.status // "unavailable"' 2>/dev/null || printf 'unavailable')"
304
- fi
305
-
306
- contract_command="bash scripts/verify-contract.sh --contract $contract_file --strict --report-file <temp>"
307
- if [[ -f "scripts/sync-brain-docs.sh" && -f ".ai/harness/brain-manifest.json" ]]; then
308
- bash scripts/sync-brain-docs.sh --all >/dev/null || true
309
- fi
310
- if [[ -f "scripts/prepare-codex-handoff.sh" && ( -f ".ai/harness/session/continuation.md" || -f ".ai/harness/session/resume.md" ) ]]; then
311
- bash scripts/prepare-codex-handoff.sh --reason "forge-verify-sprint" >/dev/null || true
312
- fi
313
- set +e
314
- contract_output="$(bash scripts/verify-contract.sh --contract "$contract_file" --strict --report-file "$contract_report" 2>&1)"
315
- contract_exit=$?
316
- set -e
317
-
318
- if [[ -n "$contract_output" ]]; then
319
- printf '%s\n' "$contract_output"
320
- fi
321
-
322
- status="fail"
323
- exit_code=1
324
- if [[ "$contract_exit" -eq 0 && "$allowed_paths_status" == "pass" ]]; then
325
- status="pass"
326
- exit_code=0
327
- fi
328
- failure_class=""
329
- if command -v jq >/dev/null 2>&1 && jq -e . "$contract_report" >/dev/null 2>&1; then
330
- failure_class="$(jq -r '.failure_class // empty' "$contract_report" 2>/dev/null || true)"
331
- fi
332
- if [[ -z "$failure_class" && "$status" != "pass" ]]; then
333
- if [[ "$contract_exit" -ne 0 ]]; then
334
- failure_class="contract"
335
- elif [[ "$allowed_paths_status" != "pass" ]]; then
336
- failure_class="allowed_paths"
337
- else
338
- failure_class="unknown"
339
- fi
340
- fi
341
- if [[ "$status" == "pass" ]]; then
342
- next_step="perform a fresh semantic impact review, then finish contract worktree or archive completed task"
343
- else
344
- next_step="resolve failing contract or allowed_paths evidence"
345
- fi
346
- handoff_current_exists=false
347
- handoff_resume_exists=false
348
- [[ -f ".ai/harness/session/continuation.md" ]] && handoff_current_exists=true
349
- [[ -f ".ai/harness/session/resume.md" ]] && handoff_resume_exists=true
350
-
351
- if command -v jq >/dev/null 2>&1 && jq -e . "$contract_report" >/dev/null 2>&1; then
352
- jq -n \
353
- --slurpfile contract_report "$contract_report" \
354
- --arg schema "forge-run-trace.v1" \
355
- --arg status "$status" \
356
- --arg source "verify-sprint" \
357
- --arg command "bash scripts/verify-sprint.sh" \
358
- --arg generated_at "$generated_at" \
359
- --arg run_id "$run_id" \
360
- --arg run_file "$run_file" \
361
- --arg task_profile "$task_profile" \
362
- --arg active_plan "$active_plan" \
363
- --arg contract_file "$contract_file" \
364
- --arg contract_status "$([[ "$contract_exit" -eq 0 ]] && printf pass || printf fail)" \
365
- --arg contract_command "$contract_command" \
366
- --argjson contract_exit "$contract_exit" \
367
- --arg worktree "$worktree_path" \
368
- --arg branch "$branch_name" \
369
- --arg diff_base_ref "$diff_base_ref" \
370
- --arg diff_base_commit "$diff_base_commit" \
371
- --argjson files_changed "$(git_changed_files_json)" \
372
- --argjson allowed_paths_check "$allowed_paths_check" \
373
- --argjson allowed_paths "$(allowed_paths_json "$contract_file")" \
374
- --argjson handoff_current_exists "$handoff_current_exists" \
375
- --argjson handoff_resume_exists "$handoff_resume_exists" \
376
- --arg failure_class "$failure_class" \
377
- --arg next_step "$next_step" \
378
- --argjson exit_code "$exit_code" \
379
- '{
380
- schema: $schema,
381
- status: $status,
382
- source: $source,
383
- command: $command,
384
- exit_code: $exit_code,
385
- generated_at: $generated_at,
386
- run_id: $run_id,
387
- run_file: $run_file,
388
- task_profile: $task_profile,
389
- active_plan: $active_plan,
390
- worktree: $worktree,
391
- branch: $branch,
392
- diff_base: {
393
- ref: $diff_base_ref,
394
- merge_base: $diff_base_commit
395
- },
396
- commands: [
397
- {name: "verify-sprint", command: $command, status: $status, exit_code: $exit_code},
398
- {name: "verify-contract", command: $contract_command, status: $contract_status, exit_code: $contract_exit}
399
- ],
400
- guards: [
401
- {name: "contract", status: $contract_status},
402
- {name: "allowed_paths", status: ($allowed_paths_check.status // "unavailable")}
403
- ],
404
- handoffs: [
405
- {file: ".ai/harness/session/continuation.md", exists: $handoff_current_exists},
406
- {file: ".ai/harness/session/resume.md", exists: $handoff_resume_exists}
407
- ],
408
- files_changed: $files_changed,
409
- allowed_paths_check: $allowed_paths_check,
410
- failure_class: $failure_class,
411
- next_step: $next_step,
412
- lifecycle: {
413
- latest: ".ai/harness/checks/latest.json",
414
- snapshot: $run_file,
415
- evidence_tier: "harness-trace-v1"
416
- },
417
- contract: {
418
- file: $contract_file,
419
- status: $contract_status,
420
- command: $contract_command,
421
- exit_code: $contract_exit,
422
- report: ($contract_report[0] // {}),
423
- task_profile: $task_profile,
424
- allowed_paths: $allowed_paths
425
- }
426
- }' > "$checks_report"
427
- else
428
- cat > "$checks_report" <<EOF_CHECKS
429
- {
430
- "schema": "forge-run-trace.v1",
431
- "status": "$(json_escape "$status")",
432
- "source": "verify-sprint",
433
- "command": "bash scripts/verify-sprint.sh",
434
- "exit_code": $exit_code,
435
- "generated_at": "$(json_escape "$generated_at")",
436
- "run_id": "$(json_escape "$run_id")",
437
- "run_file": "$(json_escape "$run_file")",
438
- "task_profile": "$(json_escape "$task_profile")",
439
- "active_plan": "$(json_escape "$active_plan")",
440
- "worktree": "$(json_escape "$worktree_path")",
441
- "branch": "$(json_escape "$branch_name")",
442
- "diff_base": {
443
- "ref": "$(json_escape "$diff_base_ref")",
444
- "merge_base": "$(json_escape "$diff_base_commit")"
445
- },
446
- "commands": [
447
- {
448
- "name": "verify-sprint",
449
- "command": "bash scripts/verify-sprint.sh",
450
- "status": "$(json_escape "$status")",
451
- "exit_code": $exit_code
452
- },
453
- {
454
- "name": "verify-contract",
455
- "command": "$(json_escape "$contract_command")",
456
- "status": "$([[ "$contract_exit" -eq 0 ]] && printf pass || printf fail)",
457
- "exit_code": $contract_exit
458
- }
459
- ],
460
- "guards": [
461
- {"name": "contract", "status": "$([[ "$contract_exit" -eq 0 ]] && printf pass || printf fail)"},
462
- {"name": "allowed_paths", "status": "$(json_escape "$allowed_paths_status")"}
463
- ],
464
- "handoffs": [
465
- {"file": ".ai/harness/session/continuation.md", "exists": $handoff_current_exists},
466
- {"file": ".ai/harness/session/resume.md", "exists": $handoff_resume_exists}
467
- ],
468
- "files_changed": [],
469
- "allowed_paths_check": {
470
- "status": "unavailable",
471
- "message": "jq unavailable"
472
- },
473
- "failure_class": "$(json_escape "$failure_class")",
474
- "next_step": "$(json_escape "$next_step")",
475
- "lifecycle": {
476
- "latest": ".ai/harness/checks/latest.json",
477
- "snapshot": "$(json_escape "$run_file")",
478
- "evidence_tier": "harness-trace-v1"
479
- },
480
- "contract": {
481
- "file": "$(json_escape "$contract_file")",
482
- "status": "$([[ "$contract_exit" -eq 0 ]] && printf pass || printf fail)",
483
- "command": "$(json_escape "$contract_command")",
484
- "exit_code": $contract_exit,
485
- "task_profile": "$(json_escape "$task_profile")",
486
- "allowed_paths": []
487
- }
488
- }
489
- EOF_CHECKS
490
- fi
491
-
492
- cp "$checks_report" "$checks_file"
493
- cp "$checks_report" "$run_file"
494
-
495
- if [[ "$exit_code" -eq 0 ]]; then
496
- echo "Sprint verification passed"
497
- echo "Run snapshot: $run_file"
498
- else
499
- echo "Sprint verification failed" >&2
500
- echo "Run snapshot: $run_file" >&2
501
- fi
502
-
503
- exit "$exit_code"
4
+ exec "$SCRIPT_DIR/lib/run-forge-helper.sh" "verify-sprint.sh" "$@"