@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,117 +1,4 @@
1
1
  #!/bin/bash
2
2
  set -euo pipefail
3
-
4
- usage() {
5
- cat <<'USAGE_EOF'
6
- Usage: scripts/check-deploy-sql-order.sh [--quiet] [--root deploy/sql]
7
-
8
- Validates deployment SQL assets:
9
- - deployment SQL files must live directly under deploy/sql/
10
- - filenames must start with a 4-digit numeric prefix
11
- - prefixes must be strictly ascending in filename order
12
- - if tests/sql/control_plane_invariants.sql exists, it must reference every deployment SQL file
13
-
14
- Example: deploy/sql/0001_create_tables.sql
15
- USAGE_EOF
16
- }
17
-
18
- quiet=0
19
- sql_root="deploy/sql"
20
- invariant_file="tests/sql/control_plane_invariants.sql"
21
-
22
- while [[ $# -gt 0 ]]; do
23
- case "$1" in
24
- --quiet)
25
- quiet=1
26
- shift
27
- ;;
28
- --root)
29
- sql_root="${2:-}"
30
- if [[ -z "$sql_root" ]]; then
31
- echo "--root requires a path" >&2
32
- exit 1
33
- fi
34
- shift 2
35
- ;;
36
- --help|-h)
37
- usage
38
- exit 0
39
- ;;
40
- *)
41
- echo "Unknown argument: $1" >&2
42
- usage
43
- exit 1
44
- ;;
45
- esac
46
- done
47
-
48
- sql_root="${sql_root%/}"
49
- deploy_root="${sql_root%%/sql}"
50
- issues=0
51
-
52
- report_issue() {
53
- echo "[deploy-sql] $1"
54
- issues=$((issues + 1))
55
- }
56
-
57
- if [[ "$sql_root" != "deploy/sql" ]]; then
58
- report_issue "SQL root must be deploy/sql; got: $sql_root"
59
- fi
60
-
61
- if [[ ! -d "$sql_root" ]]; then
62
- report_issue "Missing SQL directory: $sql_root"
63
- else
64
- if [[ -d "$deploy_root" ]]; then
65
- while IFS= read -r file; do
66
- [[ -z "$file" ]] && continue
67
- if [[ "$file" != "$sql_root/"* ]]; then
68
- report_issue "Deploy SQL file must live under $sql_root/: $file"
69
- continue
70
- fi
71
-
72
- rel="${file#"$sql_root"/}"
73
- if [[ "$rel" == */* ]]; then
74
- report_issue "SQL file must be a direct child of $sql_root/: $file"
75
- fi
76
- done < <(find "$deploy_root" -type f -name '*.sql' | sort)
77
- fi
78
-
79
- previous_number=-1
80
- previous_file=""
81
- while IFS= read -r file; do
82
- [[ -z "$file" ]] && continue
83
- base="$(basename "$file")"
84
-
85
- if [[ ! "$base" =~ ^([0-9]{4})[_-].+\.sql$ ]]; then
86
- report_issue "SQL filename must start with a 4-digit prefix: $file (example: deploy/sql/0001_create_tables.sql)"
87
- continue
88
- fi
89
-
90
- number="${BASH_REMATCH[1]}"
91
- number_value=$((10#$number))
92
- if (( previous_number >= 0 && number_value <= previous_number )); then
93
- report_issue "SQL prefixes must be strictly ascending: $previous_file before $file"
94
- fi
95
-
96
- previous_number="$number_value"
97
- previous_file="$file"
98
- done < <(find "$sql_root" -maxdepth 1 -type f -name '*.sql' | sort)
99
-
100
- if [[ -f "$invariant_file" ]]; then
101
- while IFS= read -r file; do
102
- [[ -z "$file" ]] && continue
103
- base="$(basename "$file")"
104
- if ! grep -Fq "$file" "$invariant_file" && ! grep -Fq "$base" "$invariant_file"; then
105
- report_issue "SQL migration must be referenced by $invariant_file: $file"
106
- fi
107
- done < <(find "$sql_root" -maxdepth 1 -type f -name '*.sql' | sort)
108
- fi
109
- fi
110
-
111
- if (( issues > 0 )); then
112
- exit 1
113
- fi
114
-
115
- if (( quiet == 0 )); then
116
- echo "[deploy-sql] OK"
117
- fi
3
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
4
+ exec "$SCRIPT_DIR/lib/run-forge-helper.sh" "check-deploy-sql-order.sh" "$@"
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { readFileSync, existsSync, lstatSync, readdirSync } from 'node:fs';
3
- import { resolve } from 'node:path';
3
+ import { createRequire } from 'node:module';
4
+ import { dirname, extname, normalize, resolve } from 'node:path';
4
5
 
5
6
  const root = resolve(new URL('..', import.meta.url).pathname);
6
7
  const failures = [];
@@ -52,6 +53,105 @@ function sourceFiles(directory) {
52
53
  return files;
53
54
  }
54
55
 
56
+ function relativeStaticTypeScriptDependencyGraph(directory = 'src') {
57
+ let ts;
58
+ try {
59
+ ts = createRequire(import.meta.url)('typescript');
60
+ } catch (error) {
61
+ failures.push(`TypeScript dependency is required for architecture import-cycle analysis: ${error instanceof Error ? error.message : String(error)}`);
62
+ return new Map();
63
+ }
64
+
65
+ const files = sourceFiles(directory).sort();
66
+ const known = new Set(files);
67
+ const graph = new Map(files.map((path) => [path, new Set()]));
68
+
69
+ function resolveTypeScriptImport(fromPath, specifier) {
70
+ if (!specifier.startsWith('.')) return undefined;
71
+ const base = normalize(`${dirname(fromPath)}/${specifier}`).replaceAll('\\', '/');
72
+ const extension = extname(base);
73
+ const withoutRuntimeExtension = ['.js', '.mjs', '.cjs'].includes(extension)
74
+ ? base.slice(0, -extension.length)
75
+ : base;
76
+ const candidates = extension === '.ts'
77
+ ? [base]
78
+ : [withoutRuntimeExtension, `${withoutRuntimeExtension}.ts`, `${withoutRuntimeExtension}/index.ts`];
79
+ return candidates.find((candidate) => known.has(candidate));
80
+ }
81
+
82
+ for (const path of files) {
83
+ const sourceFile = ts.createSourceFile(path, text(path), ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
84
+ for (const statement of sourceFile.statements) {
85
+ let specifier;
86
+ if ((ts.isImportDeclaration(statement) || ts.isExportDeclaration(statement))
87
+ && statement.moduleSpecifier
88
+ && ts.isStringLiteralLike(statement.moduleSpecifier)) {
89
+ specifier = statement.moduleSpecifier.text;
90
+ } else if (ts.isImportEqualsDeclaration(statement)
91
+ && ts.isExternalModuleReference(statement.moduleReference)
92
+ && statement.moduleReference.expression
93
+ && ts.isStringLiteralLike(statement.moduleReference.expression)) {
94
+ specifier = statement.moduleReference.expression.text;
95
+ }
96
+ if (!specifier) continue;
97
+ const target = resolveTypeScriptImport(path, specifier);
98
+ if (target) graph.get(path).add(target);
99
+ }
100
+ }
101
+ return graph;
102
+ }
103
+
104
+ function stronglyConnectedComponents(graph) {
105
+ let index = 0;
106
+ const indices = new Map();
107
+ const lowLinks = new Map();
108
+ const stack = [];
109
+ const onStack = new Set();
110
+ const components = [];
111
+
112
+ function visit(node) {
113
+ indices.set(node, index);
114
+ lowLinks.set(node, index);
115
+ index += 1;
116
+ stack.push(node);
117
+ onStack.add(node);
118
+
119
+ for (const target of graph.get(node) ?? []) {
120
+ if (!indices.has(target)) {
121
+ visit(target);
122
+ lowLinks.set(node, Math.min(lowLinks.get(node), lowLinks.get(target)));
123
+ } else if (onStack.has(target)) {
124
+ lowLinks.set(node, Math.min(lowLinks.get(node), indices.get(target)));
125
+ }
126
+ }
127
+
128
+ if (lowLinks.get(node) !== indices.get(node)) return;
129
+ const component = [];
130
+ while (stack.length) {
131
+ const member = stack.pop();
132
+ onStack.delete(member);
133
+ component.push(member);
134
+ if (member === node) break;
135
+ }
136
+ components.push(component.sort());
137
+ }
138
+
139
+ for (const node of [...graph.keys()].sort()) {
140
+ if (!indices.has(node)) visit(node);
141
+ }
142
+ return components;
143
+ }
144
+
145
+ function requireAcyclicProductionTypeScript() {
146
+ const graph = relativeStaticTypeScriptDependencyGraph('src');
147
+ const cyclic = stronglyConnectedComponents(graph).filter((component) =>
148
+ component.length > 1 || (component.length === 1 && graph.get(component[0])?.has(component[0])),
149
+ );
150
+ for (const component of cyclic) {
151
+ failures.push(`production TypeScript import cycle: ${component.join(' -> ')}`);
152
+ }
153
+ }
154
+
55
155
  const allowedArchitectureRootMarkdown = new Set(['CURRENT.md', 'EVOLUTION.md', 'history.md', 'index.md']);
56
156
  const architectureRootMarkdown = readdirSync(resolve(root, 'docs/architecture'), { withFileTypes: true })
57
157
  .filter((entry) => entry.isFile() && entry.name.endsWith('.md'))
@@ -96,6 +196,26 @@ const required = [
96
196
  'src/runtime/execution/thin-harness/index.ts',
97
197
  'src/runtime/execution/thin-harness/execution-router.ts',
98
198
  'src/runtime/control-plane/routing/route-policy.ts',
199
+ 'src/runtime/control-plane/routing/workspace-admission.ts',
200
+ 'src/runtime/control-plane/facade/requirement-authority.ts',
201
+ 'src/runtime/control-plane/facade/repository-work-admission.ts',
202
+ 'src/runtime/control-plane/execution/retained-work-resume.ts',
203
+ 'src/runtime/control-plane/execution/work-handle-authority.ts',
204
+ 'src/runtime/control-plane/execution/work-verification-context.ts',
205
+ 'src/runtime/control-plane/execution/work-verification-service.ts',
206
+ 'src/runtime/control-plane/execution/repository-work-attribution.ts',
207
+ 'src/runtime/control-plane/execution/work-completion-authority.ts',
208
+ 'src/runtime/control-plane/execution/work-evidence-policy.ts',
209
+ 'src/runtime/control-plane/execution/work-execution-support.ts',
210
+ 'src/runtime/control-plane/execution/work-finalization-service.ts',
211
+ 'src/runtime/control-plane/execution/work-preparation-service.ts',
212
+ 'src/runtime/control-plane/execution/work-operation-service.ts',
213
+ 'src/runtime/control-plane/facade/work-state-machine.ts',
214
+ 'src/runtime/evidence/process-check-execution.ts',
215
+ 'src/runtime/context/semantic-navigation-contract.ts',
216
+ 'src/cli/mcp/tool-contract.ts',
217
+ 'src/cli/github/contracts.ts',
218
+ 'src/runtime/gateway/mcp/runtime-tool-definitions.ts',
99
219
  'docs/architecture/CURRENT.md',
100
220
  'src/runtime/resources/leases/store.ts',
101
221
  'src/runtime/evidence/event-ledger.ts',
@@ -124,19 +244,33 @@ const required = [
124
244
  'src/cli/index.ts',
125
245
  ];
126
246
  for (const path of required) text(path);
247
+ requireAcyclicProductionTypeScript();
248
+ for (const path of sourceFiles('src/runtime/control-plane')) {
249
+ forbid(path, /(?:from\s+['"]|import\s*\(\s*['"])(?:\.\.\/)+gateway\//, 'control-plane domain/application code must not depend on Gateway transport');
250
+ }
127
251
  requireText('src/runtime/control-plane/routing/route-policy.ts', "export function decideRoute");
128
252
  requireText('src/runtime/control-plane/routing/route-policy.ts', 'inputFingerprint');
129
253
  requireText('src/runtime/control-plane/routing/route-policy.ts', 'policyVersion');
130
- requireText('src/runtime/gateway/mcp/runtime-tools.ts', 'runPersistedCheckViaProcessRuntime({');
131
- requireText('src/runtime/gateway/mcp/runtime-tools.ts', 'interactiveWaitMs: 0');
132
- requireText('src/runtime/gateway/mcp/runtime-tools.ts', 'checkContentRevision');
133
- requireText('src/runtime/gateway/mcp/runtime-tools.ts', 'observedGitHead');
134
- forbidBetween(
254
+ requireText('src/runtime/control-plane/execution/work-verification-service.ts', 'runPersistedCheckViaProcessRuntime({');
255
+ requireText('src/runtime/control-plane/execution/work-verification-service.ts', 'interactiveWaitMs: input.interactiveWaitMs ?? 0');
256
+ requireText('src/runtime/control-plane/execution/work-verification-service.ts', 'checkContentRevision');
257
+ requireText('src/runtime/control-plane/execution/work-verification-service.ts', 'observedGitHead');
258
+ requireText('src/runtime/gateway/mcp/runtime-tools.ts', 'executeWorkVerification({');
259
+ requireText('src/cli/local-bridge/facade-api.ts', 'executeWorkVerification({');
260
+ forbid(
261
+ 'src/cli/local-bridge/facade-api.ts',
262
+ /runControllerCheck\s*\(/,
263
+ 'Local Bridge Work verification must use the canonical persisted Work verification service',
264
+ );
265
+ forbid(
135
266
  'src/runtime/gateway/mcp/runtime-tools.ts',
136
- 'async function runFacadeVerify(',
137
- 'export async function callRuntimeTool',
267
+ /function\s+classifyTerminalCheckEvidence\s*\(/,
268
+ 'terminal Check evidence classification belongs to Process Runtime, not the Gateway transport',
269
+ );
270
+ forbid(
271
+ 'src/runtime/control-plane/execution/work-verification-service.ts',
138
272
  /runControllerCheck\s*\(/,
139
- 'route rh_work verify through persisted Process Runtime instead of synchronous check execution',
273
+ 'route Work verification through persisted Process Runtime instead of synchronous check execution',
140
274
  );
141
275
  for (const adapter of [
142
276
  'src/cli/controller/work-mode.ts',
@@ -152,6 +286,52 @@ for (const path of sourceFiles('src')) {
152
286
  forbid(path, /requirePlanForGoalWorkloop\s*:\s*true/, 'never restore mandatory Plan gating in production');
153
287
  }
154
288
  if (routeAuthorityCount !== 1) failures.push(`exactly one decideRoute authority is required; found ${routeAuthorityCount}`);
289
+ forbid(
290
+ 'src/runtime/control-plane/facade/goal-workloop.ts',
291
+ /function\s+(?:evaluateWorkCompletionEvidence|evaluateWorkImplementationEvidence|verificationRecordAppliesToCurrentWorkspace)\s*\(/,
292
+ 'keep Work evidence policy in the canonical work-evidence-policy module instead of reimplementing it in the facade',
293
+ );
294
+ requireText('src/runtime/control-plane/facade/goal-workloop.ts', "from '../execution/work-evidence-policy'");
295
+ for (const path of sourceFiles('src/runtime/control-plane')) {
296
+ if (path === 'src/runtime/control-plane/facade/work-contract-store.ts' || path === 'src/runtime/control-plane/execution/work-completion-authority.ts') continue;
297
+ forbid(
298
+ path,
299
+ /\brecordWorkCompletionReceipt\s*\(/,
300
+ 'route Work completion through the canonical work-completion-authority instead of writing terminal receipts directly',
301
+ );
302
+ }
303
+ requireText('src/runtime/control-plane/execution/work-finalization-service.ts', 'completeWorkWithReceipt(');
304
+ forbid(
305
+ 'src/runtime/plugins/browser-handoff-host.ts',
306
+ /browser\/sessions|saveBrowserSession|writeBrowserSession|sessionPath/,
307
+ 'Browser handoff sidecars must return interaction results and never write BrowserSession authority directly',
308
+ );
309
+ forbid(
310
+ 'src/runtime/plugins/browser-adapter.ts',
311
+ /BROWSER_POST_DISPATCH_REPLAY_SAFE_ACTIONS|POST_DISPATCH_REPLAY_SAFE_ACTIONS/,
312
+ 'Browser replay safety must come from Browser Runtime transaction policy, not an adapter-local allowlist',
313
+ );
314
+ requireText('src/runtime/plugins/browser-runtime.ts', 'browserActionCanReplayAfterDispatch');
315
+ forbid(
316
+ 'src/runtime/gateway/mcp/runtime-tools.ts',
317
+ /\b(?:createRequirement|resumeRetainedCancelledWorkContract)\s*\(/,
318
+ 'keep Requirement admission and retained-cancelled Work lifecycle authority out of the MCP transport',
319
+ );
320
+ forbid(
321
+ 'src/runtime/gateway/mcp/runtime-tools.ts',
322
+ /\b(?:updateWorkContract|writeWorkHandle)\s*\(/,
323
+ 'keep WorkContract/WorkHandle persistence policy out of the MCP transport',
324
+ );
325
+ forbid(
326
+ 'src/runtime/gateway/mcp/execution-tools.ts',
327
+ /\bcreateWorkContract\s*\(/,
328
+ 'route compatibility work preparation through canonical Work admission authority',
329
+ );
330
+ forbid(
331
+ 'src/cli/mcp/legacy-tool-service.ts',
332
+ /\bcreateWorkContract\s*\(/,
333
+ 'keep the legacy MCP surface as translation over canonical Work admission authority',
334
+ );
155
335
  requireMissing('src/runtime/control-plane/daemon-entry.ts');
156
336
  requireMissing('scripts/smoke-runtime-control-plane.ts');
157
337
  requireMissing('src/cli/controller/lifecycle.ts');
@@ -386,7 +566,14 @@ requireText('src/runtime/gateway/mcp/execution-tools.ts', 'isDurableWorkOperatio
386
566
  requireText('src/runtime/gateway/mcp/execution-tools.ts', "'work_execute'");
387
567
  requireText('src/runtime/gateway/mcp/execution-tools.ts', "'work_validate'");
388
568
  requireText('src/runtime/gateway/mcp/execution-tools.ts', "'work_finalize'");
389
- requireText('src/runtime/execution/workers/executor.ts', 'callExecutionTool(runtimeContext');
569
+ requireText('src/runtime/execution/workers/executor.ts', 'executeWork(runtimeContext');
570
+ requireText('src/runtime/execution/workers/executor.ts', 'validateWork(runtimeContext');
571
+ requireText('src/runtime/execution/workers/executor.ts', 'finalizeWork(runtimeContext');
572
+ forbid(
573
+ 'src/runtime/execution/workers/executor.ts',
574
+ /gateway\/mcp\/execution-tools|callExecutionTool\s*\(/,
575
+ 'Execution Worker must invoke control-plane Work application services directly, never MCP transport',
576
+ );
390
577
  requireText('src/runtime/execution/workers/executor.ts', '__from_durable_worker');
391
578
  requireText('src/runtime/gateway/mcp/runtime-tools.ts', 'managedProcessOperationDigest');
392
579
  forbid(
@@ -582,14 +769,50 @@ forbidBetween(
582
769
  requireText('src/runtime/control-plane/execution/session-store.ts', 'lastValidatedAt: now');
583
770
  requireText('src/runtime/control-plane/execution/validation.ts', 'warnings.push');
584
771
  requireText('src/runtime/control-plane/execution/work-handle-store.ts', "failed: ['validating', 'editing', 'committed', 'merged', 'cleaned', 'failed_terminal_cleanup']");
585
- requireText('src/runtime/gateway/mcp/execution-tools.ts', 'resetFinalizationStagesForRequest');
586
- requireText('src/runtime/gateway/mcp/execution-tools.ts', 'finalizationComplete');
587
- forbidBetween(
772
+ requireText('src/runtime/gateway/mcp/execution-tools.ts', "from '../../control-plane/execution/work-finalization-service'");
773
+ requireText('src/runtime/gateway/mcp/execution-tools.ts', "from '../../control-plane/execution/work-preparation-service'");
774
+ requireText('src/runtime/gateway/mcp/execution-tools.ts', "from '../../control-plane/execution/work-operation-service'");
775
+ requireText('src/runtime/gateway/mcp/execution-tools.ts', 'Compatibility exports: implementation authority lives in control-plane execution.');
776
+ requireText('src/runtime/gateway/mcp/execution-tools.ts', 'resetFinalizationStagesForRequest,');
777
+ requireText('src/runtime/gateway/mcp/execution-tools.ts', 'selectDefaultWorkValidationChecks');
778
+ requireText('src/runtime/gateway/mcp/runtime-tools.ts', "callExecutionTool(ctx, 'work_finalize'");
779
+ forbid(
780
+ 'src/runtime/gateway/mcp/runtime-tools.ts',
781
+ /repositoryGit(?:Commit|FinishWorkflow|MergeBranch|DeleteBranch|RebaseOnto)\s*\(/,
782
+ 'rh_work facade finalization must delegate to the canonical Work finalization application service instead of performing Git delivery itself',
783
+ );
784
+ forbid(
588
785
  'src/runtime/gateway/mcp/execution-tools.ts',
589
- 'function finalizeWork(',
590
- 'export async function callExecutionTool',
786
+ /function\s+finalizeWork\s*\(/,
787
+ 'MCP execution transport must delegate Work finalization to the control-plane application service',
788
+ );
789
+ forbid(
790
+ 'src/runtime/gateway/mcp/execution-tools.ts',
791
+ /function\s+(?:prepareWork|adoptExistingWorkHead)\s*\(/,
792
+ 'MCP execution transport must delegate Work preparation/adoption to the control-plane application service',
793
+ );
794
+ forbid(
795
+ 'src/runtime/gateway/mcp/execution-tools.ts',
796
+ /function\s+(?:executeWork|validateWork)\s*\(/,
797
+ 'MCP execution transport must delegate Work execute/validate operations to the control-plane application service',
798
+ );
799
+ forbid(
800
+ 'src/runtime/gateway/mcp/execution-tools.ts',
801
+ /(?:ensureManagedWorkspace|admitPreparedRepositoryWorkContract)\s*\(/,
802
+ 'MCP execution transport must not own managed-workspace or WorkContract preparation admission',
803
+ );
804
+ requireText('src/runtime/control-plane/execution/work-preparation-service.ts', 'export function prepareWork(');
805
+ requireText('src/runtime/control-plane/execution/work-preparation-service.ts', 'function adoptExistingWorkHead(');
806
+ requireText('src/runtime/control-plane/execution/work-operation-service.ts', 'export async function executeWork(');
807
+ requireText('src/runtime/control-plane/execution/work-operation-service.ts', 'export async function validateWork(');
808
+ requireText('src/runtime/control-plane/execution/work-finalization-service.ts', 'resetFinalizationStagesForRequest');
809
+ requireText('src/runtime/control-plane/execution/work-finalization-service.ts', 'finalizationComplete');
810
+ forbidBetween(
811
+ 'src/runtime/control-plane/execution/work-finalization-service.ts',
812
+ 'export async function finalizeWork(',
813
+ '// WORK_FINALIZATION_SERVICE_END',
591
814
  /withControllerLock\([\s\S]{0,900}?(?:repositoryGitCommit|repositoryGitFinishWorkflow|runCleanup|repositoryGitDeleteBranch)/,
592
- 'work_finalize must not hold the controller lock while committing, merging, deleting branches, or removing worktrees',
815
+ 'Work finalization must not hold the controller lock while committing, merging, deleting branches, or removing worktrees',
593
816
  );
594
817
  requireText('src/runtime/workflow/schedules/types.ts', "'repository-event'");
595
818
  requireText('src/runtime/workflow/schedules/types.ts', "'dependency-checkpoint'");
@@ -614,6 +837,10 @@ for (const path of [
614
837
  forbid(path, /\b(?:spawnSync|execSync|execFileSync)\s*\(/, 'the non-blocking Gateway/Controller hot-path rule');
615
838
  }
616
839
 
840
+ requireText('scripts/run-governed-gate.ts', "label: 'source duplication'");
841
+ requireText('scripts/run-governed-gate.ts', "args: ['scripts/check-source-duplication.mjs']");
842
+ requireText('scripts/run-governed-gate.ts', "label: 'controller UI bundle'");
843
+ requireText('scripts/run-governed-gate.ts', "args: ['run', 'check:controller-ui']");
617
844
  requireText('docs/architecture/CURRENT.md', '## State ownership');
618
845
  requireText('docs/architecture/CURRENT.md', '## Runtime and MCP boundary');
619
846
  requireText('docs/architecture/CURRENT.md', '## Testing and verification');
@@ -1,193 +1,5 @@
1
1
  #!/usr/bin/env bun
2
- /**
3
- * Skill Version Consistency Checker
4
- *
5
- * Validates that version numbers are consistent across:
6
- * - package.json
7
- * - assets/skill-version.json
8
- *
9
- * Also checks if a generated project needs migration.
10
- */
2
+ export * from "../assets/templates/helpers/check-skill-version";
3
+ import { runTrackedForgeHelper } from "./lib/run-forge-helper";
11
4
 
12
- import { readFileSync, existsSync } from "fs";
13
- import { join, dirname } from "path";
14
- import { fileURLToPath } from "url";
15
- import { resolveAgenticDevRoot } from "./workflow-contract.ts";
16
-
17
- const __filename = fileURLToPath(import.meta.url);
18
- const __dirname = dirname(__filename);
19
- const REPO_ROOT = __dirname.endsWith("/.ai/harness/scripts")
20
- ? join(__dirname, "../../..")
21
- : __dirname.endsWith("/assets/templates/helpers")
22
- ? join(__dirname, "../../..")
23
- : join(__dirname, "..");
24
-
25
- export interface ConsistencyResult {
26
- consistent: boolean;
27
- packageJsonVersion: string | null;
28
- skillVersionJsonVersion: string;
29
- templateVersionJsonVersion: string;
30
- skillVersionSource: string;
31
- warnings: string[];
32
- errors: string[];
33
- }
34
-
35
- export interface MigrationCheckResult {
36
- needsMigration: boolean;
37
- currentSkillVersion: string;
38
- projectSkillVersion: string | null;
39
- projectTemplatVersion: string | null;
40
- }
41
-
42
- /**
43
- * Check version consistency across the version sources this repo owns.
44
- */
45
- export function checkConsistency(repoRoot: string = REPO_ROOT): ConsistencyResult {
46
- const errors: string[] = [];
47
- const warnings: string[] = [];
48
-
49
- // Read package.json
50
- const pkgPath = join(repoRoot, "package.json");
51
- if (!existsSync(pkgPath)) {
52
- throw new Error(`package.json not found at ${pkgPath}`);
53
- }
54
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
55
- const packageJsonVersion =
56
- typeof pkg.version === "string" && pkg.version.length > 0
57
- ? (pkg.version as string)
58
- : null;
59
-
60
- // Read the local source manifest when this script runs inside forge;
61
- // generated projects resolve the upstream skill root instead.
62
- const localSvPath = join(repoRoot, "assets", "skill-version.json");
63
- const svPath = existsSync(localSvPath)
64
- ? localSvPath
65
- : join(resolveAgenticDevRoot(repoRoot), "assets", "skill-version.json");
66
- if (!existsSync(svPath)) {
67
- throw new Error(`skill-version.json not found at ${svPath}`);
68
- }
69
- const sv = JSON.parse(readFileSync(svPath, "utf-8"));
70
- const skillVersionJsonVersion = sv.version as string;
71
- const templateVersionJsonVersion = sv.templateVersion as string;
72
- const usingLocalSkillManifest = svPath === localSvPath;
73
-
74
- const packageCoreVersion = packageJsonVersion?.split("-", 1)[0] ?? null;
75
- const isForgePackage =
76
- pkg.name === "forge" ||
77
- pkg.name === "@moretea-labs/forge-controller" ||
78
- pkg.name === "@moretea-labs/forge";
79
-
80
- if (
81
- usingLocalSkillManifest &&
82
- isForgePackage &&
83
- packageCoreVersion !== null &&
84
- packageCoreVersion !== skillVersionJsonVersion
85
- ) {
86
- errors.push(
87
- `package.json core version (${packageCoreVersion}) must match assets/skill-version.json version (${skillVersionJsonVersion})`
88
- );
89
- }
90
-
91
- if (usingLocalSkillManifest && templateVersionJsonVersion !== skillVersionJsonVersion) {
92
- errors.push(
93
- `assets/skill-version.json templateVersion (${templateVersionJsonVersion}) must match version (${skillVersionJsonVersion})`
94
- );
95
- }
96
-
97
- if (!usingLocalSkillManifest && packageJsonVersion !== null) {
98
- warnings.push(
99
- `package.json.version (${packageJsonVersion}) belongs to the target repo; workflow stamp uses upstream forge skill-version.json`
100
- );
101
- }
102
-
103
- return {
104
- consistent: errors.length === 0,
105
- packageJsonVersion,
106
- skillVersionJsonVersion,
107
- templateVersionJsonVersion,
108
- skillVersionSource: svPath,
109
- warnings,
110
- errors,
111
- };
112
- }
113
-
114
- /**
115
- * Check if a generated project needs migration.
116
- */
117
- export function checkProjectNeedsMigration(
118
- projectPath: string,
119
- repoRoot: string = REPO_ROOT
120
- ): MigrationCheckResult {
121
- const localSvPath = join(repoRoot, "assets", "skill-version.json");
122
- const svPath = existsSync(localSvPath)
123
- ? localSvPath
124
- : join(resolveAgenticDevRoot(repoRoot), "assets", "skill-version.json");
125
- const sv = JSON.parse(readFileSync(svPath, "utf-8"));
126
- const currentSkillVersion = sv.version as string;
127
-
128
- const stampPath = join(projectPath, ".claude", ".skill-version");
129
- if (!existsSync(stampPath)) {
130
- return {
131
- needsMigration: true,
132
- currentSkillVersion,
133
- projectSkillVersion: null,
134
- projectTemplatVersion: null,
135
- };
136
- }
137
-
138
- const stampContent = readFileSync(stampPath, "utf-8");
139
- const skillVersionMatch = stampContent.match(/^skill_version=(.+)$/m);
140
- const templateVersionMatch = stampContent.match(/^template_version=(.+)$/m);
141
-
142
- const projectSkillVersion = skillVersionMatch?.[1] ?? null;
143
- const projectTemplatVersion = templateVersionMatch?.[1] ?? null;
144
-
145
- return {
146
- needsMigration: projectSkillVersion !== currentSkillVersion,
147
- currentSkillVersion,
148
- projectSkillVersion,
149
- projectTemplatVersion,
150
- };
151
- }
152
-
153
- // CLI entry point
154
- if (import.meta.main) {
155
- const args = process.argv.slice(2);
156
- const projectIdx = args.indexOf("--project");
157
- const projectPath = projectIdx !== -1 ? args[projectIdx + 1] : null;
158
-
159
- try {
160
- const result = checkConsistency();
161
-
162
- if (result.consistent) {
163
- console.log(
164
- `Workflow version check passed: forge=${result.skillVersionJsonVersion}, template=${result.templateVersionJsonVersion}`
165
- );
166
- console.log(`Workflow version source: ${result.skillVersionSource}`);
167
- for (const warning of result.warnings) {
168
- console.log(`Warning: ${warning}`);
169
- }
170
- } else {
171
- console.error("Version consistency check FAILED:");
172
- for (const error of result.errors) {
173
- console.error(` - ${error}`);
174
- }
175
- process.exit(1);
176
- }
177
-
178
- if (projectPath) {
179
- const migration = checkProjectNeedsMigration(projectPath);
180
- if (migration.needsMigration) {
181
- console.log(
182
- `Project at ${projectPath} needs migration: ` +
183
- `${migration.projectSkillVersion ?? "(no stamp)"} → ${migration.currentSkillVersion}`
184
- );
185
- } else {
186
- console.log(`Project at ${projectPath} is up to date (${migration.currentSkillVersion})`);
187
- }
188
- }
189
- } catch (error) {
190
- console.error("Error:", error instanceof Error ? error.message : error);
191
- process.exit(1);
192
- }
193
- }
5
+ if (import.meta.main) runTrackedForgeHelper("check-skill-version.ts");