@moretea-labs/forge 1.7.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/AGENTS.md +2 -0
  2. package/CHANGELOG.md +13 -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 +85 -89
  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 +18 -25
  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 +96 -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 +805 -1009
  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 +1 -1
  166. package/src/runtime/root/package-runtime-service.ts +33 -2
  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,647 +1,4 @@
1
1
  #!/bin/bash
2
2
  set -euo pipefail
3
-
4
- usage() {
5
- cat <<'USAGE_EOF'
6
- Usage:
7
- scripts/architecture-queue.sh record --file <path>
8
- scripts/architecture-queue.sh status [--format text|json|summary] [--gate]
9
- scripts/architecture-queue.sh reindex [--check] [--quiet]
10
- scripts/architecture-queue.sh triage --before <YYYY-MM-DD>
11
- scripts/architecture-queue.sh check
12
- USAGE_EOF
13
- }
14
-
15
- repo="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
16
- repo="$(cd "$repo" && pwd)"
17
- cd "$repo"
18
-
19
- command_name="${1:-status}"
20
- shift || true
21
-
22
- file_path=""
23
- format="text"
24
- check_mode="false"
25
- quiet="false"
26
- gate="false"
27
- before_date=""
28
-
29
- while [[ $# -gt 0 ]]; do
30
- case "$1" in
31
- --file)
32
- [[ -n "${2:-}" ]] || { echo "architecture-queue: --file requires a value" >&2; exit 2; }
33
- file_path="$2"
34
- shift 2
35
- ;;
36
- --format)
37
- [[ -n "${2:-}" ]] || { echo "architecture-queue: --format requires a value" >&2; exit 2; }
38
- format="$2"
39
- shift 2
40
- ;;
41
- --before)
42
- [[ -n "${2:-}" ]] || { echo "architecture-queue: --before requires a value" >&2; exit 2; }
43
- before_date="$2"
44
- shift 2
45
- ;;
46
- --check)
47
- check_mode="true"
48
- shift
49
- ;;
50
- --quiet)
51
- quiet="true"
52
- shift
53
- ;;
54
- --gate)
55
- gate="true"
56
- shift
57
- ;;
58
- --help|-h)
59
- usage
60
- exit 0
61
- ;;
62
- *)
63
- echo "architecture-queue: unknown argument: $1" >&2
64
- usage
65
- exit 2
66
- ;;
67
- esac
68
- done
69
-
70
- index_file="docs/architecture/index.md"
71
- requests_dir="docs/architecture/requests"
72
- event_file=".ai/harness/architecture/events.jsonl"
73
-
74
- architecture_event() {
75
- if command -v bun >/dev/null 2>&1 && [[ -f "scripts/architecture-event.ts" ]]; then
76
- bun scripts/architecture-event.ts "$@"
77
- return $?
78
- fi
79
- return 127
80
- }
81
-
82
- architecture_event_required() {
83
- if ! command -v bun >/dev/null 2>&1; then
84
- echo "architecture-queue: bun is required for $command_name" >&2
85
- return 127
86
- fi
87
- if [[ ! -f "scripts/architecture-event.ts" ]]; then
88
- echo "architecture-queue: missing scripts/architecture-event.ts" >&2
89
- return 127
90
- fi
91
- return 0
92
- }
93
-
94
- json_escape() {
95
- local value="$1"
96
- value="${value//\\/\\\\}"
97
- value="${value//\"/\\\"}"
98
- value="${value//$'\n'/\\n}"
99
- value="${value//$'\r'/\\r}"
100
- value="${value//$'\t'/\\t}"
101
- printf '%s' "$value"
102
- }
103
-
104
- safe_token() {
105
- local value="$1"
106
- local parsed=""
107
-
108
- if parsed="$(architecture_event safe-token --value "$value" 2>/dev/null)"; then
109
- printf '%s' "$parsed"
110
- return 0
111
- fi
112
-
113
- value="$(printf '%s' "$value" | tr '[:upper:]' '[:lower:]')"
114
- value="$(printf '%s' "$value" | sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//; s/-{2,}/-/g')"
115
- printf '%s' "${value:-root}"
116
- }
117
-
118
- json_get() {
119
- local json_input="$1"
120
- local key="$2"
121
- local parsed=""
122
-
123
- if [[ -z "$json_input" ]]; then
124
- return 1
125
- fi
126
-
127
- if parsed="$(architecture_event json-get --key "$key" --json "$json_input" 2>/dev/null)"; then
128
- printf '%s' "$parsed"
129
- return 0
130
- fi
131
-
132
- if command -v jq >/dev/null 2>&1; then
133
- parsed="$(printf '%s' "$json_input" | jq -r ".$key // empty" 2>/dev/null || true)"
134
- fi
135
-
136
- if [[ -z "$parsed" ]] && command -v node >/dev/null 2>&1; then
137
- parsed="$(JSON_INPUT="$json_input" JSON_KEY="$key" node -e '
138
- const raw = process.env.JSON_INPUT || "";
139
- const key = process.env.JSON_KEY || "";
140
- try {
141
- const value = JSON.parse(raw)[key];
142
- if (value === undefined || value === null) process.exit(1);
143
- process.stdout.write(typeof value === "object" ? JSON.stringify(value) : String(value));
144
- } catch {
145
- process.exit(1);
146
- }
147
- ' 2>/dev/null || true)"
148
- fi
149
-
150
- [[ -n "$parsed" ]] || return 1
151
- printf '%s' "$parsed"
152
- }
153
-
154
- json_starts_with_object() {
155
- local json_input="$1"
156
- local first
157
- first="$(printf '%s' "$json_input" | sed -n 's/^[[:space:]]*//; /^$/d; s/^\(.\).*$/\1/p; q')"
158
- [[ "$first" == "{" ]]
159
- }
160
-
161
- repo_relative_path() {
162
- local value="$1"
163
-
164
- if architecture_event repo-path --repo "$repo" --path "$value" 2>/dev/null; then
165
- return 0
166
- fi
167
-
168
- value="${value#file://}"
169
- case "$value" in
170
- "$repo"/*)
171
- value="${value#$repo/}"
172
- ;;
173
- /*)
174
- return 1
175
- ;;
176
- ./*)
177
- value="${value#./}"
178
- ;;
179
- esac
180
-
181
- case "$value" in
182
- ""|.|..|../*|*/../*|*$'\n'*|*$'\r'*)
183
- return 1
184
- ;;
185
- esac
186
-
187
- printf '%s' "$value"
188
- }
189
-
190
- metadata_value() {
191
- local file="$1"
192
- local label="$2"
193
- [[ -f "$file" ]] || return 1
194
- awk -v label="> **${label}**:" '
195
- index($0, label) == 1 {
196
- value = substr($0, length(label) + 1)
197
- gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
198
- gsub(/^`|`$/, "", value)
199
- print value
200
- exit
201
- }
202
- ' "$file"
203
- }
204
-
205
- selected_blocks() {
206
- if [[ -x "scripts/select-agent-context-blocks.sh" ]]; then
207
- "scripts/select-agent-context-blocks.sh" "$repo" 2>/dev/null || true
208
- return 0
209
- fi
210
-
211
- find "$repo" \
212
- \( -path "$repo/.git" -o -path "$repo/node_modules" -o -path "$repo/.ai" -o -path "$repo/.claude" \) -prune -o \
213
- \( -type f \( -name 'CLAUDE.md' -o -name 'AGENTS.md' \) \) -print 2>/dev/null | while IFS= read -r context_file; do
214
- context_dir="$(dirname "$context_file")"
215
- rel_dir="${context_dir#$repo/}"
216
- [[ "$rel_dir" == "$context_dir" || "$rel_dir" == "." ]] && continue
217
- printf '%s\n' "$rel_dir"
218
- done | sort -u
219
- }
220
-
221
- match_functional_block() {
222
- local rel_path="$1"
223
- local best="root"
224
- local block
225
-
226
- while IFS= read -r block; do
227
- [[ -n "$block" ]] || continue
228
- block="${block#./}"
229
- block="${block%/}"
230
- [[ -z "$block" || "$block" == "." ]] && continue
231
- case "$block" in
232
- /*|../*|*/../*|*\"*)
233
- continue
234
- ;;
235
- esac
236
-
237
- if [[ "$rel_path" == "$block" || "$rel_path" == "$block/"* ]]; then
238
- if [[ "$best" == "root" || "${#block}" -gt "${#best}" ]]; then
239
- best="$block"
240
- fi
241
- fi
242
- done < <(selected_blocks)
243
-
244
- printf '%s' "$best"
245
- }
246
-
247
- classify_change() {
248
- local rel_path="$1"
249
- local base
250
- base="$(basename "$rel_path")"
251
-
252
- case "$rel_path" in
253
- .git/*|node_modules/*|.ai/harness/architecture/*|docs/architecture/*|.claude/.trace.jsonl)
254
- printf 'none internal\n'
255
- return
256
- ;;
257
- CLAUDE.md|AGENTS.md|*/CLAUDE.md|*/AGENTS.md)
258
- printf 'none agent-context\n'
259
- return
260
- ;;
261
- esac
262
-
263
- if [[ "$rel_path" =~ ^(\.ai/hooks/|assets/hooks/) ]] ||
264
- [[ "$rel_path" == ".ai/harness/policy.json" ]] ||
265
- [[ "$rel_path" == ".ai/harness/workflow-contract.json" ]] ||
266
- [[ "$rel_path" == "assets/workflow-contract.v1.json" ]] ||
267
- [[ "$rel_path" =~ ^scripts/(architecture-queue|context-contract-sync|workstream-sync|migrate-project-template|migrate-workflow-docs|inspect-project-state|check-skill-version|capability-resolver|capability-config|create-project-dirs|init-project|ensure-task-workflow|check-task-workflow|check-deploy-sql-order|refresh-current-status|workflow-contract|select-agent-context-blocks)\.(sh|ts)$ ]] ||
268
- [[ "$rel_path" == "scripts/lib/project-init-lib.sh" ]]; then
269
- printf 'high workflow-surface\n'
270
- return
271
- fi
272
-
273
- if [[ "$rel_path" =~ (^|/)(migrations|migration|schema|schemas|database|db|infra|terraform|k8s)(/|$) ]] ||
274
- [[ "$base" =~ ^wrangler.*\.toml$ ]] ||
275
- [[ "$base" =~ ^(Dockerfile|docker-compose\.ya?ml|schema\.prisma)$ ]]; then
276
- printf 'high data-or-deploy\n'
277
- return
278
- fi
279
-
280
- if [[ "$rel_path" =~ ^(apps|packages|services)/[^/]+/(package\.json|tsconfig\.json|metro\.config\.(js|ts)|vite\.config\.(js|ts)|next\.config\.(js|mjs|ts)|app\.json|app\.config\.(js|ts))$ ]] ||
281
- [[ "$rel_path" =~ ^(apps|packages|services)/[^/]+/src/(routes|api|server|app)(/|$) ]] ||
282
- [[ "$rel_path" =~ ^packages/[^/]+/src/[^/]+/index\.ts$ ]] ||
283
- [[ "$rel_path" =~ ^(package\.json|turbo\.json|tsconfig\.json|pnpm-workspace\.yaml|bunfig\.toml)$ ]]; then
284
- printf 'medium boundary-or-config\n'
285
- return
286
- fi
287
-
288
- if [[ "$rel_path" =~ ^(apps|packages|services)/[^/]+/src/ ]]; then
289
- printf 'low source-change\n'
290
- return
291
- fi
292
-
293
- printf 'none unrelated\n'
294
- }
295
-
296
- policy_arch_value() {
297
- local key="$1"
298
- local default_value="$2"
299
- local policy_file=".ai/harness/policy.json"
300
- local parsed=""
301
-
302
- if [[ -f "$policy_file" ]] && command -v node >/dev/null 2>&1; then
303
- parsed="$(POLICY_FILE="$policy_file" POLICY_KEY="$key" node -e '
304
- const fs = require("fs");
305
- const file = process.env.POLICY_FILE;
306
- const key = process.env.POLICY_KEY;
307
- try {
308
- const policy = JSON.parse(fs.readFileSync(file, "utf8"));
309
- const value = policy.architecture && policy.architecture[key];
310
- if (value === undefined || value === null || value === "") process.exit(1);
311
- process.stdout.write(String(value));
312
- } catch {
313
- process.exit(1);
314
- }
315
- ' 2>/dev/null || true)"
316
- fi
317
-
318
- printf '%s' "${parsed:-$default_value}"
319
- }
320
-
321
- severity_rank() {
322
- case "$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')" in
323
- critical) printf '4' ;;
324
- high) printf '3' ;;
325
- medium) printf '2' ;;
326
- low) printf '1' ;;
327
- *) printf '0' ;;
328
- esac
329
- }
330
-
331
- request_card_path() {
332
- local capability_id="$1"
333
- printf '%s/%s.md' "$requests_dir" "$(safe_token "$capability_id")"
334
- }
335
-
336
- pending_request_files() {
337
- [[ -d "$requests_dir" ]] || return 0
338
- find "$requests_dir" -maxdepth 1 -type f -name '*.md' | sort | while IFS= read -r request; do
339
- [[ "$(metadata_value "$request" "Status" || true)" == "Pending" ]] || continue
340
- printf '%s\n' "$request"
341
- done
342
- }
343
-
344
- pending_count() {
345
- pending_request_files | wc -l | tr -d ' '
346
- }
347
-
348
- pending_count_at_or_above() {
349
- local threshold="$1"
350
- local min_rank request severity count
351
- min_rank="$(severity_rank "$threshold")"
352
- count=0
353
- while IFS= read -r request; do
354
- severity="$(metadata_value "$request" "Severity" || true)"
355
- if [[ "$(severity_rank "$severity")" -ge "$min_rank" ]]; then
356
- count=$((count + 1))
357
- fi
358
- done < <(pending_request_files)
359
- printf '%s' "$count"
360
- }
361
-
362
- reindex_requests() {
363
- architecture_event_required
364
- if [[ "$check_mode" == "true" ]]; then
365
- architecture_event reindex-requests --index-file "$index_file" --requests-dir "$requests_dir" --check
366
- else
367
- architecture_event reindex-requests --index-file "$index_file" --requests-dir "$requests_dir"
368
- [[ "$quiet" == "true" ]] || echo "[ArchitectureQueue] Reindexed $index_file"
369
- fi
370
- }
371
-
372
- status_command() {
373
- local count mode threshold blocking
374
- count="$(pending_count)"
375
- mode="$(policy_arch_value "freshness_gate" "advisory")"
376
- threshold="$(policy_arch_value "gate_min_severity" "medium")"
377
- blocking="$(pending_count_at_or_above "$threshold")"
378
-
379
- case "$format" in
380
- json)
381
- printf '{"pending":%s,"gate_mode":"%s","gate_min_severity":"%s","blocking":%s}\n' "$count" "$mode" "$threshold" "$blocking"
382
- ;;
383
- summary)
384
- printf 'pending=%s gate_mode=%s gate_min_severity=%s blocking=%s\n' "$count" "$mode" "$threshold" "$blocking"
385
- ;;
386
- text)
387
- printf '[ArchitectureQueue] pending=%s gate_mode=%s gate_min_severity=%s blocking=%s\n' "$count" "$mode" "$threshold" "$blocking"
388
- ;;
389
- *)
390
- echo "architecture-queue: unsupported --format: $format" >&2
391
- exit 2
392
- ;;
393
- esac
394
-
395
- [[ "$gate" == "true" ]] || return 0
396
- case "$mode" in
397
- off)
398
- return 0
399
- ;;
400
- advisory)
401
- if [[ "$blocking" -gt 0 ]]; then
402
- echo "[ArchitectureQueue] WARN: pending architecture requests meet gate threshold ($blocking >= $threshold)" >&2
403
- fi
404
- return 0
405
- ;;
406
- strict)
407
- if ! command -v bun >/dev/null 2>&1 || [[ ! -f "scripts/architecture-event.ts" || ! -f "scripts/capability-resolver.ts" ]]; then
408
- echo "[ArchitectureQueue] strict gate failed: missing queue dependencies" >&2
409
- return 1
410
- fi
411
- if [[ "$blocking" -gt 0 ]]; then
412
- echo "[ArchitectureQueue] strict gate failed: pending architecture requests meet gate threshold ($blocking >= $threshold)" >&2
413
- return 1
414
- fi
415
- return 0
416
- ;;
417
- *)
418
- echo "[ArchitectureQueue] unknown freshness_gate: $mode" >&2
419
- return 1
420
- ;;
421
- esac
422
- }
423
-
424
- record_command() {
425
- if [[ -z "$file_path" ]]; then
426
- echo "architecture-queue: missing --file" >&2
427
- exit 2
428
- fi
429
-
430
- local rel_path severity change_type capability_match resolver_stderr
431
- rel_path="$(repo_relative_path "$file_path" || true)"
432
- if [[ -z "$rel_path" ]]; then
433
- echo "[ArchitectureDrift] Skipped unsafe path: $file_path"
434
- exit 0
435
- fi
436
-
437
- read -r severity change_type < <(classify_change "$rel_path")
438
- if [[ "$severity" == "none" ]]; then
439
- echo "[ArchitectureDrift] No architecture drift request for $rel_path ($change_type)."
440
- exit 0
441
- fi
442
-
443
- if ! command -v bun >/dev/null 2>&1 || [[ ! -f "scripts/architecture-event.ts" ]]; then
444
- echo "[ArchitectureQueue] WARN: bun and scripts/architecture-event.ts are required to record $rel_path; skipping advisory queue update"
445
- exit 0
446
- fi
447
-
448
- capability_match=""
449
- if [[ -f "scripts/capability-resolver.ts" ]]; then
450
- resolver_stderr="$(mktemp)"
451
- if ! capability_match="$(bun scripts/capability-resolver.ts match --path "$rel_path" --format json 2>"$resolver_stderr")"; then
452
- [[ -n "$capability_match" ]] && echo "$capability_match" >&2
453
- cat "$resolver_stderr" >&2
454
- rm -f "$resolver_stderr"
455
- exit 1
456
- fi
457
- if ! json_starts_with_object "$capability_match"; then
458
- cat "$resolver_stderr" >&2
459
- echo "[ArchitectureDrift] WARN: capability resolver returned non-JSON; using root fallback for $rel_path" >&2
460
- capability_match=""
461
- fi
462
- rm -f "$resolver_stderr"
463
- fi
464
-
465
- local functional_block matched_prefix capability_id contract_agents contract_claude capability_resolved
466
- local architecture_domain architecture_capability architecture_module workstream_dir
467
- functional_block="root"
468
- matched_prefix="root"
469
- capability_id="root"
470
- contract_agents=""
471
- contract_claude=""
472
- capability_resolved="false"
473
- if [[ -n "$capability_match" ]] && [[ "$(json_get "$capability_match" "matched" || true)" == "true" ]]; then
474
- functional_block="$(json_get "$capability_match" "functional_block")"
475
- matched_prefix="$(json_get "$capability_match" "matched_prefix")"
476
- capability_id="$(json_get "$capability_match" "capability_id")"
477
- capability_resolved="true"
478
- elif [[ ! -f "scripts/capability-resolver.ts" ]]; then
479
- functional_block="$(match_functional_block "$rel_path")"
480
- matched_prefix="$functional_block"
481
- capability_id="$(safe_token "$functional_block")"
482
- [[ "$functional_block" == "root" ]] && capability_id="root"
483
- fi
484
-
485
- if [[ "$capability_resolved" != "true" && "$severity" == "low" && "$change_type" == "source-change" ]]; then
486
- echo "[ArchitectureDrift] No architecture drift request for $rel_path (unmatched source-change)."
487
- exit 0
488
- fi
489
-
490
- architecture_domain="root"
491
- architecture_capability="_root"
492
- architecture_module="docs/architecture/index.md"
493
- workstream_dir="tasks/workstreams/root/_root"
494
- if [[ "$capability_resolved" == "true" && "$functional_block" != "root" ]]; then
495
- architecture_domain="$(json_get "$capability_match" "architecture_domain")"
496
- architecture_capability="$(json_get "$capability_match" "architecture_capability")"
497
- architecture_module="$(json_get "$capability_match" "architecture_module")"
498
- workstream_dir="$(json_get "$capability_match" "workstream_dir")"
499
- contract_agents="$(json_get "$capability_match" "contract_agents" || true)"
500
- contract_claude="$(json_get "$capability_match" "contract_claude" || true)"
501
- elif [[ "$functional_block" != "root" ]]; then
502
- read -r architecture_domain architecture_capability architecture_module workstream_dir < <(architecture_event derive-scope --block "$functional_block" --format lines)
503
- fi
504
-
505
- local iso_timestamp request_file spawn_recommended contract_sync_required request_event_json event_json
506
- iso_timestamp="$(date '+%Y-%m-%dT%H:%M:%S%z')"
507
- request_file="$(request_card_path "$capability_id")"
508
- spawn_recommended="false"
509
- contract_sync_required="false"
510
-
511
- [[ "$severity" == "high" ]] && spawn_recommended="true"
512
- if [[ "$functional_block" != "root" && ( "$severity" == "high" || "$severity" == "medium" ) ]]; then
513
- contract_sync_required="true"
514
- fi
515
-
516
- request_event_json="$(architecture_event event-json \
517
- --ts "$iso_timestamp" \
518
- --file-path "$rel_path" \
519
- --severity "$severity" \
520
- --functional-block "$functional_block" \
521
- --capability-id "$capability_id" \
522
- --matched-prefix "$matched_prefix" \
523
- --architecture-domain "$architecture_domain" \
524
- --architecture-capability "$architecture_capability" \
525
- --architecture-module "$architecture_module" \
526
- --workstream-dir "$workstream_dir" \
527
- --contract-agents "$contract_agents" \
528
- --contract-claude "$contract_claude" \
529
- --change-type "$change_type" \
530
- --request-file "$request_file" \
531
- --spawn-recommended "$spawn_recommended" \
532
- --contract-sync-required "$contract_sync_required" \
533
- --pretty)"
534
-
535
- mkdir -p "$requests_dir" "$(dirname "$event_file")" docs/architecture/snapshots docs/architecture/diagrams docs/architecture/domains docs/architecture/modules tasks/workstreams
536
- architecture_event upsert-request --request-file "$request_file" --event-json "$request_event_json"
537
-
538
- event_json="$(architecture_event event-json \
539
- --ts "$iso_timestamp" \
540
- --file-path "$rel_path" \
541
- --severity "$severity" \
542
- --functional-block "$functional_block" \
543
- --capability-id "$capability_id" \
544
- --matched-prefix "$matched_prefix" \
545
- --architecture-domain "$architecture_domain" \
546
- --architecture-capability "$architecture_capability" \
547
- --architecture-module "$architecture_module" \
548
- --workstream-dir "$workstream_dir" \
549
- --contract-agents "$contract_agents" \
550
- --contract-claude "$contract_claude" \
551
- --change-type "$change_type" \
552
- --request-file "$request_file" \
553
- --spawn-recommended "$spawn_recommended" \
554
- --contract-sync-required "$contract_sync_required")"
555
- printf '%s\n' "$event_json" >> "$event_file"
556
-
557
- quiet="true"
558
- check_mode="false"
559
- reindex_requests
560
-
561
- echo "[ArchitectureDrift] Request: $request_file"
562
- echo "[ArchitectureDrift] Event: $event_file"
563
- echo "[ArchitectureDrift] severity=$severity capability_id=$capability_id functional_block=$functional_block spawn_recommended=$spawn_recommended contract_sync_required=$contract_sync_required"
564
- }
565
-
566
- triage_command() {
567
- if [[ -z "$before_date" ]]; then
568
- echo "architecture-queue: triage requires --before <YYYY-MM-DD>" >&2
569
- exit 2
570
- fi
571
- if [[ ! "$before_date" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
572
- echo "architecture-queue: --before must be YYYY-MM-DD" >&2
573
- exit 2
574
- fi
575
- architecture_event_required
576
-
577
- local request detected detected_date capability_id card count
578
- count=0
579
- mkdir -p "$requests_dir"
580
- while IFS= read -r request; do
581
- [[ -f "$request" ]] || continue
582
- case "$(basename "$request")" in
583
- 20??????-??????-*.md)
584
- ;;
585
- *)
586
- continue
587
- ;;
588
- esac
589
- [[ "$(metadata_value "$request" "Status" || true)" == "Pending" ]] || continue
590
- detected="$(metadata_value "$request" "Detected" || true)"
591
- detected_date="${detected%%T*}"
592
- [[ "$detected_date" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] || continue
593
- if [[ "$detected_date" > "$before_date" || "$detected_date" == "$before_date" ]]; then
594
- continue
595
- fi
596
- capability_id="$(metadata_value "$request" "Capability ID" || true)"
597
- capability_id="${capability_id:-root}"
598
- card="$(request_card_path "$capability_id")"
599
- architecture_event upsert-from-request --source-request "$request" --request-file "$card"
600
- if [[ -x "scripts/archive-architecture-request.sh" ]]; then
601
- bash scripts/archive-architecture-request.sh \
602
- --request "$request" \
603
- --status superseded \
604
- --artifact "$card" \
605
- --note "Merged into architecture queue card by triage --before $before_date." >/dev/null
606
- else
607
- mkdir -p "docs/architecture/requests/archive/$(date '+%Y')"
608
- mv "$request" "docs/architecture/requests/archive/$(date '+%Y')/$(basename "$request")"
609
- fi
610
- count=$((count + 1))
611
- done < <(find "$requests_dir" -maxdepth 1 -type f -name '*.md' | sort)
612
-
613
- quiet="true"
614
- check_mode="false"
615
- reindex_requests
616
- echo "[ArchitectureQueue] triaged=$count before=$before_date pending=$(pending_count)"
617
- }
618
-
619
- check_command() {
620
- check_mode="true"
621
- reindex_requests
622
- gate="true"
623
- status_command
624
- }
625
-
626
- case "$command_name" in
627
- record)
628
- record_command
629
- ;;
630
- status)
631
- status_command
632
- ;;
633
- reindex)
634
- reindex_requests
635
- ;;
636
- triage)
637
- triage_command
638
- ;;
639
- check)
640
- check_command
641
- ;;
642
- *)
643
- echo "architecture-queue: unknown command: $command_name" >&2
644
- usage >&2
645
- exit 2
646
- ;;
647
- esac
3
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
4
+ exec "$SCRIPT_DIR/lib/run-forge-helper.sh" "architecture-queue.sh" "$@"