@moretea-labs/forge 1.6.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 (469) hide show
  1. package/AGENTS.md +4 -0
  2. package/CHANGELOG.md +39 -0
  3. package/CLAUDE.md +4 -0
  4. package/README.md +2 -2
  5. package/README.zh-CN.md +1 -1
  6. package/SKILL.md +2 -2
  7. package/THIRD_PARTY_NOTICES.md +1 -1
  8. package/agents/openai.yaml +1 -1
  9. package/assets/hooks/hook-input.sh +11 -8
  10. package/assets/hooks/lib/session-state.sh +10 -7
  11. package/assets/hooks/lib/workflow-state.sh +13 -177
  12. package/assets/hooks/post-tool-observer.sh +8 -2
  13. package/assets/hooks/prompt-guard.sh +5 -81
  14. package/assets/hooks/session-start-context.sh +56 -1
  15. package/assets/partials/05-workflow.partial.md +1 -1
  16. package/assets/partials-agents/02-operating-mode.partial.md +2 -0
  17. package/assets/partials-agents/03-orchestration.partial.md +1 -1
  18. package/assets/plugin-registry.v1.json +3 -3
  19. package/assets/reference-configs/document-generation.md +1 -1
  20. package/assets/reference-configs/harness-overview.md +1 -1
  21. package/assets/reference-configs/sprint-contracts.md +3 -3
  22. package/assets/skill-version.json +2 -2
  23. package/assets/templates/contract.template.md +2 -7
  24. package/assets/templates/helpers/capture-plan.sh +2 -2
  25. package/assets/templates/helpers/check-architecture-sync.sh +17 -0
  26. package/assets/templates/helpers/check-task-workflow.sh +4 -4
  27. package/assets/templates/helpers/contract-worktree.sh +1 -21
  28. package/assets/templates/helpers/ensure-task-workflow.sh +11 -36
  29. package/assets/templates/helpers/inspect-project-state.ts +8 -6
  30. package/assets/templates/helpers/migrate-project-template.sh +22 -45
  31. package/assets/templates/helpers/migrate-workflow-docs.ts +9 -7
  32. package/assets/templates/helpers/new-plan.sh +1 -1
  33. package/assets/templates/helpers/plan-to-todo.sh +12 -37
  34. package/assets/templates/helpers/ship-worktrees.sh +2 -29
  35. package/assets/templates/helpers/verify-contract.sh +1 -111
  36. package/assets/templates/helpers/verify-sprint.sh +3 -180
  37. package/assets/templates/helpers/workflow-contract.ts +5 -1
  38. package/assets/templates/plan.template.md +1 -1
  39. package/assets/templates/review.template.md +9 -34
  40. package/assets/workflow-contract.v1.json +10 -19
  41. package/docs/README.md +10 -10
  42. package/docs/architecture/CURRENT.md +178 -0
  43. package/docs/architecture/EVOLUTION.md +42 -0
  44. package/docs/architecture/current/README.md +4 -156
  45. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
  46. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
  47. package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
  48. package/docs/architecture/history/README.md +7 -0
  49. package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
  50. package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
  51. package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
  52. package/docs/architecture/history.md +3 -34
  53. package/docs/architecture/index.md +15 -98
  54. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
  55. package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
  56. package/docs/architecture/versions/1.6.md +38 -0
  57. package/docs/architecture/versions/1.7.md +41 -0
  58. package/docs/forge-chatgpt-browser-engine.md +2 -2
  59. package/docs/forge-chatgpt-mcp-setup.md +4 -28
  60. package/docs/forge-chatgpt-supervised-automation.md +3 -3
  61. package/docs/forge-github-issue-launcher.md +1 -1
  62. package/docs/forge-plugin-management.md +1 -1
  63. package/docs/forge-runtime-self-healing-loop.md +1 -1
  64. package/docs/operations/controller-browser-plugin.md +26 -6
  65. package/docs/operations/controller-performance-and-502.md +143 -0
  66. package/docs/operations/controller-reliability-runbook.md +1 -1
  67. package/docs/operations/features.md +2 -2
  68. package/docs/operations/github-repository.md +1 -1
  69. package/docs/operations/homebrew.md +2 -2
  70. package/docs/operations/mcp-tool-exposure.md +5 -3
  71. package/docs/wiki/Architecture.md +6 -6
  72. package/docs/wiki/Implementation.md +2 -2
  73. package/docs/wiki/Integrations.md +2 -2
  74. package/docs/wiki/Work-Lifecycle.md +1 -1
  75. package/package.json +33 -11
  76. package/scripts/activate-source-baseline.ts +17 -13
  77. package/scripts/architecture-event.ts +2 -820
  78. package/scripts/architecture-queue.sh +2 -645
  79. package/scripts/archive-architecture-request.sh +2 -242
  80. package/scripts/archive-workflow.sh +1 -291
  81. package/scripts/benchmark-background-check-overlap.ts +228 -0
  82. package/scripts/benchmark-mcp-host.sh +117 -0
  83. package/scripts/benchmark-mcp-transport.ts +170 -0
  84. package/scripts/benchmark-quality-harness.ts +146 -0
  85. package/scripts/benchmark-route-session-concurrency.ts +207 -6
  86. package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
  87. package/scripts/benchmark-typescript-navigation.ts +186 -0
  88. package/scripts/benchmark-work-efficiency.ts +334 -0
  89. package/scripts/capability-config.ts +2 -392
  90. package/scripts/capability-resolver.ts +2 -503
  91. package/scripts/capture-plan.sh +1 -332
  92. package/scripts/check-agent-tooling.sh +2 -1558
  93. package/scripts/check-architecture-sync.sh +2 -371
  94. package/scripts/check-brain-manifest.sh +2 -253
  95. package/scripts/check-context-files.sh +2 -47
  96. package/scripts/check-deploy-sql-order.sh +2 -115
  97. package/scripts/check-mcp-compatibility.ts +14 -0
  98. package/scripts/check-runtime-architecture.mjs +349 -79
  99. package/scripts/check-skill-version.ts +3 -191
  100. package/scripts/check-source-duplication.mjs +61 -0
  101. package/scripts/check-stable-baseline.ts +92 -0
  102. package/scripts/check-task-sync.sh +2 -63
  103. package/scripts/check-task-workflow.sh +2 -1000
  104. package/scripts/codex-handoff-resume.sh +2 -267
  105. package/scripts/context-contract-sync.sh +2 -514
  106. package/scripts/contract-run.ts +2 -463
  107. package/scripts/contract-worktree.sh +1 -722
  108. package/scripts/ensure-task-workflow.sh +1 -1225
  109. package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
  110. package/scripts/inspect-project-state.ts +4 -279
  111. package/scripts/install-standalone-recovery.ts +24 -0
  112. package/scripts/lib/project-init-lib.sh +22 -46
  113. package/scripts/lib/run-forge-helper.sh +19 -0
  114. package/scripts/lib/run-forge-helper.ts +26 -0
  115. package/scripts/migrate-project-template.sh +1 -1189
  116. package/scripts/migrate-workflow-docs.ts +4 -412
  117. package/scripts/new-plan.sh +1 -180
  118. package/scripts/new-spec.sh +1 -47
  119. package/scripts/new-sprint.sh +1 -46
  120. package/scripts/plan-to-todo.sh +1 -796
  121. package/scripts/prepare-codex-handoff.sh +2 -201
  122. package/scripts/prepare-handoff.sh +1 -78
  123. package/scripts/refresh-current-status.sh +2 -467
  124. package/scripts/run-governed-gate.ts +5 -1
  125. package/scripts/select-agent-context-blocks.sh +2 -64
  126. package/scripts/ship-worktrees.sh +1 -558
  127. package/scripts/smoke-ios-physical-device.ts +4 -2
  128. package/scripts/smoke-runtime-recovery.ts +1 -35
  129. package/scripts/smoke-schedule-engine.ts +1 -20
  130. package/scripts/sprint-backlog.sh +1 -834
  131. package/scripts/stage-runtime-release.ts +2 -1
  132. package/scripts/summarize-failures.sh +2 -123
  133. package/scripts/switch-plan.sh +1 -150
  134. package/scripts/sync-brain-docs.sh +2 -379
  135. package/scripts/sync-generated-authority.mjs +175 -0
  136. package/scripts/verify-contract.sh +2 -703
  137. package/scripts/verify-forge-runtime.sh +0 -1
  138. package/scripts/verify-sprint.sh +1 -677
  139. package/scripts/workflow-contract.ts +4 -205
  140. package/scripts/workstream-sync.sh +2 -261
  141. package/src/cli/agent-jobs/executor-health.ts +1 -1
  142. package/src/cli/commands/init-hook.ts +3 -3
  143. package/src/cli/commands/mcp.ts +9 -5
  144. package/src/cli/commands/plugin.ts +12 -3
  145. package/src/cli/commands/recovery.ts +25 -3
  146. package/src/cli/commands/runtime.ts +55 -3
  147. package/src/cli/commands/security.ts +12 -1
  148. package/src/cli/commands/setup-profile.ts +31 -21
  149. package/src/cli/controller/check-runner.ts +113 -5
  150. package/src/cli/controller/composite-result.ts +1 -1
  151. package/src/cli/controller/context/focus.ts +51 -0
  152. package/src/cli/controller/context/instruction-context.ts +114 -0
  153. package/src/cli/controller/context/known-paths.ts +76 -0
  154. package/src/cli/controller/context/query-planning.ts +110 -0
  155. package/src/cli/controller/context/rule-resolution.ts +532 -0
  156. package/src/cli/controller/context/source-materializer.ts +278 -0
  157. package/src/cli/controller/context/structural.ts +16 -0
  158. package/src/cli/controller/context/types.ts +286 -0
  159. package/src/cli/controller/context-pack.ts +828 -487
  160. package/src/cli/controller/launch-agents.ts +77 -2
  161. package/src/cli/controller/operational-plan.ts +2 -2
  162. package/src/cli/controller/work-mode.ts +6 -4
  163. package/src/cli/editing/edit-session.ts +60 -7
  164. package/src/cli/github/contracts.ts +16 -0
  165. package/src/cli/github/github.ts +135 -16
  166. package/src/cli/github/session.ts +2 -1
  167. package/src/cli/installer/managed-entries.ts +15 -1
  168. package/src/cli/local-bridge/console-automations.ts +54 -78
  169. package/src/cli/local-bridge/console-view-models.ts +1 -35
  170. package/src/cli/local-bridge/facade-api.ts +37 -264
  171. package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
  172. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +23 -13
  173. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
  174. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +6 -4
  175. package/src/cli/local-bridge/frontend/src/lib/api.ts +1 -6
  176. package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
  177. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +2 -2
  178. package/src/cli/local-bridge/frontend/src/styles.css +1 -1
  179. package/src/cli/local-bridge/frontend/src/types.ts +5 -8
  180. package/src/cli/local-bridge/job-store.ts +1 -490
  181. package/src/cli/local-bridge/server.ts +2 -515
  182. package/src/cli/local-bridge/ui-dist/app.css +1 -1
  183. package/src/cli/local-bridge/ui-dist/app.js +3703 -3757
  184. package/src/cli/mcp/auth.ts +5 -5
  185. package/src/cli/mcp/instructions.ts +3 -2
  186. package/src/cli/mcp/legacy-tool-service.ts +10 -222
  187. package/src/cli/mcp/multi-repository.ts +16 -5
  188. package/src/cli/mcp/oauth.ts +175 -26
  189. package/src/cli/mcp/post-finalize-work-attribution.ts +150 -0
  190. package/src/cli/mcp/repository-tools.ts +282 -146
  191. package/src/cli/mcp/server.ts +586 -116
  192. package/src/cli/mcp/tool-capability-cache.ts +1 -1
  193. package/src/cli/mcp/tool-contract.ts +14 -0
  194. package/src/cli/mcp/toolset-selection.ts +1 -1
  195. package/src/cli/mcp/toolset.ts +44 -7
  196. package/src/cli/mcp/transports/http.ts +124 -14
  197. package/src/cli/mcp/transports/session-registry.ts +43 -5
  198. package/src/cli/mcp/types.ts +3 -3
  199. package/src/cli/repositories/command-classifier.ts +103 -21
  200. package/src/cli/repositories/command-executor.ts +78 -526
  201. package/src/cli/repositories/command-process.ts +108 -0
  202. package/src/cli/repositories/command-scope.ts +99 -41
  203. package/src/cli/repositories/controller-home.ts +7 -8
  204. package/src/cli/repositories/git-command-executor.ts +2 -0
  205. package/src/cli/repositories/locks.ts +53 -16
  206. package/src/cli/repositories/repository-snapshot.ts +124 -0
  207. package/src/cli/repositories/selected-path-actions.ts +17 -9
  208. package/src/cli/repositories/structured-git.ts +118 -6
  209. package/src/cli/repository/inspector.ts +563 -65
  210. package/src/cli/repository/session-cache.ts +58 -17
  211. package/src/cli/v81-entry.ts +1 -4
  212. package/src/effects/fs-transaction.ts +85 -17
  213. package/src/runtime/context/codegraph-read-provider.ts +233 -2
  214. package/src/runtime/context/codegraph-sidecar.cjs +74 -40
  215. package/src/runtime/context/generic-lsp-provider.ts +355 -0
  216. package/src/runtime/context/lsp-client.ts +283 -0
  217. package/src/runtime/context/semantic-navigation-contract.ts +61 -0
  218. package/src/runtime/context/semantic-navigation.ts +223 -0
  219. package/src/runtime/context/swift-navigation.ts +565 -0
  220. package/src/runtime/context/typescript-navigation.ts +226 -0
  221. package/src/runtime/control-plane/dispatch-priority.ts +28 -0
  222. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +48 -16
  223. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
  224. package/src/runtime/control-plane/execution/repository-work-attribution.ts +106 -0
  225. package/src/runtime/control-plane/execution/retained-work-resume.ts +203 -0
  226. package/src/runtime/control-plane/execution/session-store.ts +18 -0
  227. package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
  228. package/src/runtime/control-plane/execution/work-completion-authority.ts +58 -0
  229. package/src/runtime/control-plane/execution/work-evidence-policy.ts +200 -0
  230. package/src/runtime/control-plane/execution/work-execution-support.ts +386 -0
  231. package/src/runtime/control-plane/execution/work-finalization-service.ts +1913 -0
  232. package/src/runtime/control-plane/execution/work-handle-authority.ts +96 -0
  233. package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
  234. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
  235. package/src/runtime/control-plane/execution/work-operation-service.ts +468 -0
  236. package/src/runtime/control-plane/execution/work-path-scope.ts +37 -0
  237. package/src/runtime/control-plane/execution/work-preparation-service.ts +479 -0
  238. package/src/runtime/control-plane/execution/work-process-evidence.ts +129 -0
  239. package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
  240. package/src/runtime/control-plane/execution/work-task-receipt.ts +22 -18
  241. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +156 -11
  242. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +22 -14
  243. package/src/runtime/control-plane/execution/work-verification-context.ts +56 -0
  244. package/src/runtime/control-plane/execution/work-verification-service.ts +388 -0
  245. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +90 -9
  246. package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
  247. package/src/runtime/control-plane/facade/controller-round-relay.ts +1118 -0
  248. package/src/runtime/control-plane/facade/controller-session-store.ts +302 -12
  249. package/src/runtime/control-plane/facade/goal-workloop-access.ts +17 -1
  250. package/src/runtime/control-plane/facade/goal-workloop.ts +928 -190
  251. package/src/runtime/control-plane/facade/index.ts +3 -0
  252. package/src/runtime/control-plane/facade/plan-contract-store.ts +275 -42
  253. package/src/runtime/control-plane/facade/repository-work-admission.ts +147 -0
  254. package/src/runtime/control-plane/facade/requirement-authority.ts +83 -0
  255. package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
  256. package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
  257. package/src/runtime/control-plane/facade/types.ts +90 -10
  258. package/src/runtime/control-plane/facade/work-contract-store.ts +157 -261
  259. package/src/runtime/control-plane/facade/work-state-machine.ts +250 -0
  260. package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
  261. package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
  262. package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
  263. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
  264. package/src/runtime/control-plane/global-scheduler/maintenance.ts +152 -0
  265. package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
  266. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
  267. package/src/runtime/control-plane/global-scheduler/scheduler.ts +278 -543
  268. package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
  269. package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
  270. package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
  271. package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
  272. package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
  273. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
  274. package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
  275. package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
  276. package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
  277. package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
  278. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +63 -0
  279. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +365 -117
  280. package/src/runtime/control-plane/launcher/launch-reservation-store.ts +89 -0
  281. package/src/runtime/control-plane/launcher/thin-launcher.ts +244 -11
  282. package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
  283. package/src/runtime/control-plane/persistence/requirement-store.ts +0 -12
  284. package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
  285. package/src/runtime/control-plane/release-retention.ts +62 -2
  286. package/src/runtime/control-plane/repo-actor/registry.ts +4 -2
  287. package/src/runtime/control-plane/routing/route-policy.ts +24 -21
  288. package/src/runtime/control-plane/routing/workspace-admission.ts +70 -0
  289. package/src/runtime/control-plane/runtime-status-client.ts +0 -2
  290. package/src/runtime/diagnostics/mcp-timing.ts +13 -0
  291. package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
  292. package/src/runtime/diagnostics/performance.ts +24 -1
  293. package/src/runtime/evidence/event-ledger.ts +1 -1
  294. package/src/runtime/evidence/process-check-execution.ts +18 -0
  295. package/src/runtime/execution/jobs/child-reference.ts +6 -2
  296. package/src/runtime/execution/jobs/store.ts +58 -4
  297. package/src/runtime/execution/jobs/types.ts +1 -1
  298. package/src/runtime/execution/managed-workspace.ts +203 -1
  299. package/src/runtime/execution/process-runtime/check-facade.ts +51 -4
  300. package/src/runtime/execution/process-runtime/check-result.ts +88 -0
  301. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +7 -1
  302. package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
  303. package/src/runtime/execution/process-runtime/command-facade.ts +367 -40
  304. package/src/runtime/execution/process-runtime/gc.ts +38 -6
  305. package/src/runtime/execution/process-runtime/lightweight-managed.ts +1201 -0
  306. package/src/runtime/execution/process-runtime/persisted-check.ts +51 -5
  307. package/src/runtime/execution/process-runtime/resource-claims.ts +10 -6
  308. package/src/runtime/execution/process-runtime/runtime.ts +53 -3
  309. package/src/runtime/execution/process-runtime/store.ts +1 -0
  310. package/src/runtime/execution/process-runtime/types.ts +4 -12
  311. package/src/runtime/execution/thin-harness/async-process.ts +12 -2
  312. package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
  313. package/src/runtime/execution/work-activity.ts +21 -0
  314. package/src/runtime/execution/workers/executor.ts +18 -61
  315. package/src/runtime/gateway/mcp/execution-tools.ts +35 -1852
  316. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +61 -0
  317. package/src/runtime/gateway/mcp/process-tools.ts +83 -37
  318. package/src/runtime/gateway/mcp/router.ts +186 -84
  319. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +565 -0
  320. package/src/runtime/gateway/mcp/runtime-tools.ts +2378 -1947
  321. package/src/runtime/plugins/app-store-connect-adapter.ts +216 -47
  322. package/src/runtime/plugins/browser-adapter.ts +2662 -829
  323. package/src/runtime/plugins/browser-automation-service.ts +10 -0
  324. package/src/runtime/plugins/browser-handoff-host.ts +2 -25
  325. package/src/runtime/plugins/browser-handoff.ts +110 -5
  326. package/src/runtime/plugins/browser-macos-bridge.ts +701 -63
  327. package/src/runtime/plugins/browser-manifest-surface.ts +603 -0
  328. package/src/runtime/plugins/browser-native-session-service.ts +222 -0
  329. package/src/runtime/plugins/browser-node-bridge.ts +6 -34
  330. package/src/runtime/plugins/browser-provider-registry.ts +109 -0
  331. package/src/runtime/plugins/browser-runtime-contract.ts +154 -0
  332. package/src/runtime/plugins/browser-runtime.ts +193 -0
  333. package/src/runtime/plugins/browser-session-authority.ts +332 -0
  334. package/src/runtime/plugins/browser-session-store.ts +121 -0
  335. package/src/runtime/plugins/browser-session-types.ts +98 -0
  336. package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
  337. package/src/runtime/plugins/desktop-operator-registration.ts +10 -4
  338. package/src/runtime/plugins/external-adapter.ts +358 -3
  339. package/src/runtime/plugins/external-unix-socket.ts +7 -1
  340. package/src/runtime/plugins/first-party-registry.ts +2 -0
  341. package/src/runtime/plugins/github-adapter.ts +125 -0
  342. package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
  343. package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
  344. package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
  345. package/src/runtime/plugins/ios-adapter.ts +106 -16
  346. package/src/runtime/plugins/ios-agent-device.ts +234 -64
  347. package/src/runtime/plugins/ios-physical-device.ts +1143 -392
  348. package/src/runtime/plugins/lightweight-action.ts +153 -0
  349. package/src/runtime/plugins/local-system-adapter.ts +123 -6
  350. package/src/runtime/plugins/macos-capability-broker.ts +47 -9
  351. package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
  352. package/src/runtime/plugins/plugin-management-adapter.ts +49 -8
  353. package/src/runtime/plugins/resend-adapter.ts +1 -1
  354. package/src/runtime/plugins/store.ts +330 -33
  355. package/src/runtime/plugins/types.ts +38 -3
  356. package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
  357. package/src/runtime/projections/git-status-sampler.ts +16 -16
  358. package/src/runtime/projections/materialized-view.ts +28 -10
  359. package/src/runtime/recovery/maintenance-executor.ts +426 -25
  360. package/src/runtime/root/mcp-transport.ts +180 -10
  361. package/src/runtime/root/package-connector-service.ts +230 -10
  362. package/src/runtime/root/package-runtime-release.ts +143 -17
  363. package/src/runtime/root/package-runtime-service.ts +461 -22
  364. package/src/runtime/root/release-manifest.ts +144 -18
  365. package/src/runtime/root/release-materialize.ts +125 -32
  366. package/src/runtime/root/release-store.ts +48 -11
  367. package/src/runtime/root/runtime.ts +23 -5
  368. package/src/runtime/root/service.ts +100 -29
  369. package/src/runtime/root/types.ts +12 -0
  370. package/src/runtime/safe-tooling/ios-development.ts +321 -63
  371. package/src/runtime/shared/trusted-executable.ts +62 -0
  372. package/src/runtime/shared/trusted-node-executable.ts +48 -0
  373. package/src/runtime/standalone-recovery/core.ts +1079 -240
  374. package/src/runtime/standalone-recovery/entry.ts +100 -24
  375. package/src/runtime/standalone-recovery/installer.ts +20 -4
  376. package/src/runtime/standalone-recovery/service-labels.ts +2 -0
  377. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
  378. package/src/runtime/workflow/schedules/browser-probe.ts +81 -3
  379. package/src/runtime/workflow/schedules/engine.ts +364 -202
  380. package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
  381. package/src/runtime/workflow/schedules/settlement.ts +64 -17
  382. package/src/runtime/workflow/schedules/store.ts +209 -19
  383. package/src/runtime/workflow/schedules/types.ts +2 -4
  384. package/src/runtime/workflow/schedules/work-continuation.ts +26 -13
  385. package/src/testing/test-governance.ts +8 -40
  386. package/assets/reference-configs/heartbeat-triage.md +0 -57
  387. package/assets/templates/helpers/harness-trace-grade.sh +0 -150
  388. package/assets/templates/helpers/heartbeat-triage.sh +0 -361
  389. package/assets/templates/helpers/maintenance-triage.sh +0 -167
  390. package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
  391. package/docs/architecture/current/architecture-invariants.md +0 -331
  392. package/docs/architecture/current/automation-and-schedule-engine.md +0 -459
  393. package/docs/architecture/current/codegraph-context-provider.md +0 -238
  394. package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
  395. package/docs/architecture/current/controller-repository-decoupling.md +0 -148
  396. package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -373
  397. package/docs/architecture/current/entity-model.md +0 -612
  398. package/docs/architecture/current/external-plugin-governance.md +0 -396
  399. package/docs/architecture/current/failure-recovery.md +0 -447
  400. package/docs/architecture/current/governance.md +0 -213
  401. package/docs/architecture/current/human-interaction-plane.md +0 -87
  402. package/docs/architecture/current/implementation-status.md +0 -128
  403. package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
  404. package/docs/architecture/current/legacy-governance.md +0 -58
  405. package/docs/architecture/current/local-workspace-target-grants.md +0 -70
  406. package/docs/architecture/current/migration-roadmap.md +0 -105
  407. package/docs/architecture/current/multi-repository-execution.md +0 -371
  408. package/docs/architecture/current/operations-runbook.md +0 -45
  409. package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
  410. package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
  411. package/docs/architecture/current/repository-access-modes.md +0 -102
  412. package/docs/architecture/current/route-policy.md +0 -15
  413. package/docs/architecture/current/route-session-concurrency.md +0 -85
  414. package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
  415. package/docs/architecture/current/runtime-directory-map.md +0 -49
  416. package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
  417. package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
  418. package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
  419. package/docs/architecture/current/system-overview.md +0 -169
  420. package/docs/architecture/current/target-requirements-traceability.md +0 -202
  421. package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
  422. package/docs/architecture/current/thin-harness-v1.md +0 -321
  423. package/docs/architecture/current/verification-and-release-gates.md +0 -391
  424. package/docs/forge-autonomous-goal-loop.md +0 -9
  425. package/docs/forge-provider-configuration.md +0 -7
  426. package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -93
  427. package/docs/operations/gmail-assistant-routines.md +0 -186
  428. package/docs/operations/personal-assistant-operator-guide.md +0 -61
  429. package/docs/operations/provider-configuration.md +0 -31
  430. package/scripts/harness-trace-grade.sh +0 -150
  431. package/scripts/heartbeat-triage.sh +0 -361
  432. package/scripts/maintenance-triage.sh +0 -167
  433. package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
  434. package/src/cli/repositories/goal-registry.ts +0 -239
  435. package/src/runtime/assistant/action-proposals.ts +0 -279
  436. package/src/runtime/assistant/intent.ts +0 -395
  437. package/src/runtime/assistant/model-provider.ts +0 -460
  438. package/src/runtime/assistant/openapi.ts +0 -223
  439. package/src/runtime/assistant/policy.ts +0 -89
  440. package/src/runtime/assistant/readiness.ts +0 -258
  441. package/src/runtime/assistant/routine-runtime.ts +0 -477
  442. package/src/runtime/assistant/schedule-binding.ts +0 -171
  443. package/src/runtime/assistant/standing-grants.ts +0 -375
  444. package/src/runtime/assistant/store.ts +0 -243
  445. package/src/runtime/assistant/types.ts +0 -135
  446. package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
  447. package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
  448. package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
  449. package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
  450. package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
  451. package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
  452. package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
  453. package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
  454. package/src/runtime/control-plane/goal-loop/index.ts +0 -13
  455. package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
  456. package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
  457. package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
  458. package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
  459. package/src/runtime/control-plane/goal-loop/types.ts +0 -378
  460. package/src/runtime/control-plane/startup-recovery.ts +0 -198
  461. package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
  462. package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
  463. package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
  464. package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
  465. package/src/runtime/workflow/findings/store.ts +0 -204
  466. package/src/runtime/workflow/findings/types.ts +0 -58
  467. package/src/runtime/workflow/portfolio/engine.ts +0 -20
  468. package/src/runtime/workflow/portfolio/store.ts +0 -185
  469. package/src/runtime/workflow/portfolio/types.ts +0 -29
@@ -0,0 +1,228 @@
1
+ #!/usr/bin/env bun
2
+
3
+ /**
4
+ * Executable goal-drift guard for the managed-check controller path.
5
+ *
6
+ * It deliberately performs useful rh_context work after run_check returns and
7
+ * before its one final dependency join. A future change that turns this into
8
+ * periodic process_wait polling, re-executes the check, or blocks the initial
9
+ * handle return fails the benchmark rather than only a local Process test.
10
+ */
11
+
12
+ import { execFileSync } from 'child_process';
13
+ import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'fs';
14
+ import { tmpdir } from 'os';
15
+ import { join } from 'path';
16
+ import { performance } from 'perf_hooks';
17
+ import { createMcpToolContext } from '../src/cli/mcp/server';
18
+ import { registerRepository } from '../src/cli/repositories/registry';
19
+ import { callRuntimeTool } from '../src/runtime/gateway/mcp/runtime-tools';
20
+ import { callProcessTool } from '../src/runtime/gateway/mcp/process-tools';
21
+ import { routeDurableMcpCall } from '../src/runtime/gateway/mcp/router';
22
+ import { getProcessRecord } from '../src/runtime/execution/process-runtime/store';
23
+ import {
24
+ clearGitIdentityCacheForTest,
25
+ clearGitSnapshotCacheForTest,
26
+ gitSnapshotPerformanceSnapshot,
27
+ } from '../src/cli/repository/inspector';
28
+
29
+ const CHECK_DURATION_MS = 1_200;
30
+ const MAX_INITIAL_HANDLE_LATENCY_MS = 700;
31
+ const MAX_TERMINAL_OBSERVATION_LATENCY_MS = 1_000;
32
+ const root = mkdtempSync(join(tmpdir(), 'forge-background-check-overlap-'));
33
+ const controllerHome = join(root, 'controller');
34
+ const repoRoot = join(root, 'repo');
35
+
36
+ const behavioralContract = {
37
+ originalUserFacingGoal: 'A long focused check must run in the background while Forge continues useful independent work, then synchronize once at the true dependency boundary.',
38
+ invariants: {
39
+ backgroundCheck: 'long_check_returns_a_managed_handle_promptly',
40
+ noPollingWhileUsefulWorkExists: 'zero_process_wait_calls_before_independent_work_finishes',
41
+ dependencyDrivenJoin: 'one_final_process_wait_after_the_dependency_boundary',
42
+ exactlyOnce: 'one_physical_check_execution',
43
+ resultCorrectness: 'terminal_result_matches_the_returned_process_id',
44
+ noAdditionalOrchestration: 'no_execution_job_created_for_the_fast_check_route',
45
+ sourceRetrievalStaysFocused: 'ordinary_rh_context_search_skips_execution_preflight',
46
+ summaryStaysCheap: 'rh_status_summary_avoids_detail_diagnostics',
47
+ },
48
+ antiGoals: [
49
+ 'periodic_process_wait_polling',
50
+ 'synchronous_wait_before_independent_work',
51
+ 'duplicate_check_execution',
52
+ 'source_retrieval_building_unrequested_check_readiness',
53
+ 'summary_status_triggering_detail_diagnostics',
54
+ ],
55
+ validation: 'Runs a real managed check, performs rh_context work before the sole join, and separately asserts the rh_status summary hot path.',
56
+ };
57
+
58
+ function percentile(values: number[], ratio: number): number {
59
+ const sorted = [...values].sort((left, right) => left - right);
60
+ return Math.round((sorted[Math.min(sorted.length - 1, Math.floor(sorted.length * ratio))] ?? 0) * 100) / 100;
61
+ }
62
+
63
+ try {
64
+ mkdirSync(join(repoRoot, 'src'), { recursive: true });
65
+ mkdirSync(join(repoRoot, '.forge'), { recursive: true });
66
+ execFileSync('git', ['init', '-q', '-b', 'main'], { cwd: repoRoot });
67
+ execFileSync('git', ['config', 'user.email', 'benchmark@example.test'], { cwd: repoRoot });
68
+ execFileSync('git', ['config', 'user.name', 'Forge Benchmark'], { cwd: repoRoot });
69
+ writeFileSync(join(repoRoot, 'src', 'fixture.ts'), 'export const backgroundFixture = true;\n');
70
+ const executionMarker = join(root, 'physical-check-executions.txt');
71
+ writeFileSync(join(repoRoot, '.forge', 'checks.json'), JSON.stringify({
72
+ version: 1,
73
+ checks: {
74
+ background: {
75
+ description: 'Synthetic long check for controller overlap behavior.',
76
+ command: [
77
+ process.execPath,
78
+ '-e',
79
+ [
80
+ `require('fs').appendFileSync(${JSON.stringify(executionMarker)}, '1');`,
81
+ `setTimeout(() => { process.stdout.write('BACKGROUND_CHECK_DONE\\n'); process.exit(0); }, ${CHECK_DURATION_MS});`,
82
+ ].join(' '),
83
+ ],
84
+ timeoutMs: 10_000,
85
+ },
86
+ },
87
+ }, null, 2));
88
+ execFileSync('git', ['add', '.'], { cwd: repoRoot });
89
+ execFileSync('git', ['commit', '-qm', 'fixture'], { cwd: repoRoot });
90
+
91
+ const repository = registerRepository({ path: repoRoot, controllerHome, displayName: 'background-check-overlap' });
92
+ const ctx = createMcpToolContext({ controllerHome, profile: 'controller', repo: repoRoot });
93
+
94
+ const startedAt = performance.now();
95
+ const started = await routeDurableMcpCall(ctx, 'run_check', {
96
+ repo_id: repository.repoId,
97
+ check_id: 'background',
98
+ interactive_wait_ms: 0,
99
+ });
100
+ const initialHandleLatencyMs = performance.now() - startedAt;
101
+ const startPayload = started?.structuredContent as Record<string, unknown> | undefined;
102
+ const processId = typeof startPayload?.processId === 'string' ? startPayload.processId : '';
103
+
104
+ const independentStartedAt = performance.now();
105
+ const independent = await callRuntimeTool(ctx, 'rh_context', {
106
+ repo_id: repository.repoId,
107
+ operation: 'search',
108
+ query: 'backgroundFixture',
109
+ known_paths: ['src/fixture.ts'],
110
+ structural_context: 'off',
111
+ });
112
+ const independentOperationLatencyMs = performance.now() - independentStartedAt;
113
+ const independentPayload = independent?.structuredContent as { data?: Record<string, unknown> } | undefined;
114
+ const independentData = independentPayload?.data;
115
+ const independentRetrievalPolicy = independentData?.retrievalPolicy as Record<string, unknown> | undefined;
116
+ const sourceRetrievalSkippedExecutionPreflight = independentData?.executionReadiness === undefined
117
+ && independentData?.registeredChecks === undefined
118
+ && independentRetrievalPolicy?.executionReadiness === 'requested_check_ids_only';
119
+ const processWaitCallsBeforeDependencyBoundary = 0;
120
+
121
+ const joinStartedAtMs = Date.now();
122
+ const joined = processId
123
+ ? await callProcessTool(ctx, 'process_wait', {
124
+ repo_id: repository.repoId,
125
+ process_id: processId,
126
+ timeout_ms: 10_000,
127
+ })
128
+ : undefined;
129
+ const resultObservedAtMs = Date.now();
130
+ const finalSynchronizationCount = processId ? 1 : 0;
131
+ const joinedPayload = joined?.structuredContent as { process?: { processId?: string; completed?: boolean; ok?: boolean } } | undefined;
132
+ const record = processId ? getProcessRecord(controllerHome, repository.repoId, processId) : undefined;
133
+ const finishedAtMs = record?.finishedAt ? Date.parse(record.finishedAt) : NaN;
134
+ const completionToObservationLatencyMs = Number.isFinite(finishedAtMs)
135
+ ? Math.max(0, resultObservedAtMs - finishedAtMs)
136
+ : undefined;
137
+ const physicalExecutionCount = readFileSync(executionMarker, 'utf8').length;
138
+
139
+ // Summary is a separate hot path: it must answer readiness without falling
140
+ // into the full Git status/diff-stat or maintenance/detail projection.
141
+ clearGitIdentityCacheForTest();
142
+ clearGitSnapshotCacheForTest();
143
+ const statusSummaryDurations: number[] = [];
144
+ let summaryPayload: Record<string, unknown> | undefined;
145
+ for (let iteration = 0; iteration < 7; iteration += 1) {
146
+ const statusStartedAt = performance.now();
147
+ const status = await callRuntimeTool(ctx, 'rh_status', { repo_id: repository.repoId, detail_level: 'summary' });
148
+ statusSummaryDurations.push(performance.now() - statusStartedAt);
149
+ summaryPayload = status?.structuredContent as Record<string, unknown> | undefined;
150
+ }
151
+ // Snapshot this before the intentional detail call below: detail is allowed
152
+ // to build the full Git projection, while summary is not.
153
+ const summaryGitSnapshot = gitSnapshotPerformanceSnapshot();
154
+ const detailStartedAt = performance.now();
155
+ const detail = await callRuntimeTool(ctx, 'rh_status', { repo_id: repository.repoId, detail_level: 'detail' });
156
+ const detailElapsedMs = performance.now() - detailStartedAt;
157
+ const detailPayload = detail?.structuredContent as Record<string, unknown> | undefined;
158
+ const detailMeta = detailPayload?.responseMeta as Record<string, unknown> | undefined;
159
+ const summaryData = summaryPayload?.data as Record<string, unknown> | undefined;
160
+ const summaryRepositoryState = summaryData?.repositoryState as Record<string, unknown> | undefined;
161
+ const summaryUsesCompactPayload = Boolean(summaryData)
162
+ && summaryData?.access === undefined
163
+ && summaryData?.pendingHandoffCount === undefined
164
+ && summaryData?.activeWorkCount === undefined
165
+ && summaryData?.activeProcessCount === undefined
166
+ && summaryRepositoryState?.status === undefined
167
+ && summaryRepositoryState?.diffStat === undefined;
168
+
169
+ const assertions = {
170
+ initialManagedHandle: Boolean(processId) && startPayload?.path === 'process_managed' && initialHandleLatencyMs <= MAX_INITIAL_HANDLE_LATENCY_MS,
171
+ independentUsefulOperation: independent?.isError !== true,
172
+ sourceRetrievalSkipsExecutionPreflight: sourceRetrievalSkippedExecutionPreflight,
173
+ noPollingBeforeDependencyBoundary: processWaitCallsBeforeDependencyBoundary === 0,
174
+ oneDependencyJoin: finalSynchronizationCount === 1,
175
+ exactlyOnce: physicalExecutionCount === 1,
176
+ terminalResultCorrect: joinedPayload?.process?.processId === processId && joinedPayload?.process?.completed === true && joinedPayload?.process?.ok === true,
177
+ promptResultObservation: completionToObservationLatencyMs === undefined || completionToObservationLatencyMs <= MAX_TERMINAL_OBSERVATION_LATENCY_MS,
178
+ statusSummaryUsesCompactProjection: Boolean(summaryPayload?.data)
179
+ && summaryGitSnapshot.refreshes === 0
180
+ && summaryGitSnapshot.subprocesses === 0
181
+ && summaryUsesCompactPayload,
182
+ };
183
+ const output = {
184
+ schemaVersion: 1,
185
+ behavioralContract,
186
+ check: {
187
+ configuredDurationMs: CHECK_DURATION_MS,
188
+ initialManagedHandleLatencyMs: Number(initialHandleLatencyMs.toFixed(2)),
189
+ processWaitCallsBeforeDependencyBoundary,
190
+ independentOperation: {
191
+ tool: 'rh_context.search',
192
+ latencyMs: Number(independentOperationLatencyMs.toFixed(2)),
193
+ completed: independent?.isError !== true,
194
+ executionPreflight: sourceRetrievalSkippedExecutionPreflight ? 'not_requested' : 'unexpected',
195
+ },
196
+ finalSynchronizationCount,
197
+ finalJoinDurationMs: resultObservedAtMs - joinStartedAtMs,
198
+ physicalExecutionCount,
199
+ terminalCompletionToObservationLatencyMs: completionToObservationLatencyMs,
200
+ processId,
201
+ },
202
+ status: {
203
+ summaryP50Ms: percentile(statusSummaryDurations, 0.5),
204
+ summaryP95Ms: percentile(statusSummaryDurations, 0.95),
205
+ detailElapsedMs: Number(detailElapsedMs.toFixed(2)),
206
+ detailServerDurationMs: detailMeta?.serverDurationMs,
207
+ detailPhaseTimingsMs: detailMeta?.phaseTimingsMs,
208
+ fullGitSnapshot: summaryGitSnapshot,
209
+ summaryOmissions: {
210
+ maintenance: true,
211
+ plugins: true,
212
+ workState: true,
213
+ accessPolicy: true,
214
+ gitStatusAndDiffStat: true,
215
+ },
216
+ },
217
+ thresholds: {
218
+ initialManagedHandleLatencyMs: MAX_INITIAL_HANDLE_LATENCY_MS,
219
+ terminalCompletionToObservationLatencyMs: MAX_TERMINAL_OBSERVATION_LATENCY_MS,
220
+ },
221
+ assertions,
222
+ passed: Object.values(assertions).every(Boolean),
223
+ };
224
+ process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
225
+ if (!output.passed) process.exitCode = 1;
226
+ } finally {
227
+ rmSync(root, { recursive: true, force: true });
228
+ }
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ BUN_BIN="${BUN_BIN:-bun}"
6
+ LABEL="${FORGE_MCP_BENCH_LABEL:-host-runtime-direct}"
7
+ PORT="${FORGE_MCP_BENCH_PORT:-18765}"
8
+ ITERATIONS="${FORGE_MCP_BENCH_ITERATIONS:-50}"
9
+ CONNECT_ITERATIONS="${FORGE_MCP_BENCH_CONNECT_ITERATIONS:-30}"
10
+
11
+ for value in "$PORT" "$ITERATIONS" "$CONNECT_ITERATIONS"; do
12
+ if ! [[ "$value" =~ ^[0-9]+$ ]] || [[ "$value" == "0" ]]; then
13
+ echo "benchmark:mcp-host requires positive integer port/iteration values" >&2
14
+ exit 2
15
+ fi
16
+ done
17
+ if (( PORT < 1024 || PORT > 65535 )); then
18
+ echo "FORGE_MCP_BENCH_PORT must be between 1024 and 65535" >&2
19
+ exit 2
20
+ fi
21
+
22
+ TMP_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/forge-mcp-host-bench.XXXXXX")"
23
+ CONTROLLER_HOME="$TMP_ROOT/controller-home"
24
+ REGISTER_JSON="$TMP_ROOT/repository.json"
25
+ INSTALL_JSON="$TMP_ROOT/runtime-install.json"
26
+ READY_JSON="$TMP_ROOT/runtime-ready.json"
27
+ STEADY_JSON="$TMP_ROOT/steady.json"
28
+ CONNECT_JSON="$TMP_ROOT/connect.json"
29
+ RUNTIME_PID=""
30
+
31
+ cleanup() {
32
+ if [[ -n "$RUNTIME_PID" ]] && kill -0 "$RUNTIME_PID" 2>/dev/null; then
33
+ kill "$RUNTIME_PID" 2>/dev/null || true
34
+ for _ in $(seq 1 20); do
35
+ kill -0 "$RUNTIME_PID" 2>/dev/null || break
36
+ sleep 0.1
37
+ done
38
+ kill -9 "$RUNTIME_PID" 2>/dev/null || true
39
+ fi
40
+ for _ in $(seq 1 5); do
41
+ rm -rf "$TMP_ROOT" 2>/dev/null && return 0
42
+ sleep 0.1
43
+ done
44
+ rm -rf "$TMP_ROOT" 2>/dev/null || true
45
+ }
46
+ trap cleanup EXIT INT TERM
47
+
48
+ TOKEN="$(node -e "process.stdout.write(require('crypto').randomBytes(32).toString('hex'))")"
49
+ export FORGE_MCP_TOKEN="$TOKEN"
50
+ export FORGE_MCP_BENCH_TOKEN="$TOKEN"
51
+
52
+ cd "$ROOT"
53
+ "$BUN_BIN" bin/forge.mjs repo register "$ROOT" \
54
+ --controller-home "$CONTROLLER_HOME" \
55
+ --json > "$REGISTER_JSON"
56
+ REPO_ID="$(node -e "const x=require(process.argv[1]); const id=x?.repository?.repoId; if(!id) process.exit(2); process.stdout.write(id)" "$REGISTER_JSON")"
57
+
58
+ "$BUN_BIN" bin/forge.mjs runtime service install-package \
59
+ --controller-home "$CONTROLLER_HOME" \
60
+ --host 127.0.0.1 \
61
+ --port "$PORT" \
62
+ --portable > "$INSTALL_JSON"
63
+ RUNTIME_PID="$(node -e "const x=require(process.argv[1]); const pid=x?.pid; if(!Number.isInteger(pid)||pid<=0) process.exit(2); process.stdout.write(String(pid))" "$INSTALL_JSON")"
64
+
65
+ curl --fail --silent --show-error \
66
+ --retry 30 --retry-delay 1 --retry-connrefused \
67
+ "http://127.0.0.1:${PORT}/ready" > "$READY_JSON"
68
+
69
+ "$BUN_BIN" run benchmark:mcp-transport -- \
70
+ --endpoint "http://127.0.0.1:${PORT}/mcp" \
71
+ --label "$LABEL" \
72
+ --repo-id "$REPO_ID" \
73
+ --tool rh_status \
74
+ --iterations "$ITERATIONS" \
75
+ --warmup 3 \
76
+ --timing-log "$CONTROLLER_HOME/audit/mcp-timings.jsonl" > "$STEADY_JSON"
77
+
78
+ "$BUN_BIN" run benchmark:mcp-transport -- \
79
+ --endpoint "http://127.0.0.1:${PORT}/mcp" \
80
+ --label "$LABEL" \
81
+ --repo-id "$REPO_ID" \
82
+ --tool rh_status \
83
+ --iterations "$CONNECT_ITERATIONS" \
84
+ --warmup 0 \
85
+ --include-connect \
86
+ --timing-log "$CONTROLLER_HOME/audit/mcp-timings.jsonl" > "$CONNECT_JSON"
87
+
88
+ node - "$STEADY_JSON" "$CONNECT_JSON" "$READY_JSON" <<'NODE'
89
+ const fs = require('fs');
90
+ const os = require('os');
91
+ const path = require('path');
92
+ const [steadyPath, connectPath, readyPath] = process.argv.slice(2);
93
+ const steady = JSON.parse(fs.readFileSync(steadyPath, 'utf8'));
94
+ const connect = JSON.parse(fs.readFileSync(connectPath, 'utf8'));
95
+ const ready = JSON.parse(fs.readFileSync(readyPath, 'utf8'));
96
+ const report = {
97
+ schemaVersion: 1,
98
+ generatedAt: new Date().toISOString(),
99
+ host: {
100
+ platform: process.platform,
101
+ arch: process.arch,
102
+ cpuCount: os.cpus().length,
103
+ totalMemoryBytes: os.totalmem(),
104
+ },
105
+ ready,
106
+ steady,
107
+ connectInclusive: connect,
108
+ };
109
+ const text = `${JSON.stringify(report, null, 2)}\n`;
110
+ const output = process.env.FORGE_MCP_BENCH_OUTPUT?.trim();
111
+ if (output) {
112
+ const target = path.resolve(output);
113
+ fs.mkdirSync(path.dirname(target), { recursive: true });
114
+ fs.writeFileSync(target, text, 'utf8');
115
+ }
116
+ process.stdout.write(text);
117
+ NODE
@@ -0,0 +1,170 @@
1
+ #!/usr/bin/env bun
2
+ import { readFileSync } from 'fs';
3
+ import { performance } from 'perf_hooks';
4
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
5
+ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
6
+ import { readMcpServiceBearerToken } from '../src/cli/mcp/auth';
7
+ import { resolveRepoPreferredControllerHome } from '../src/cli/repositories/controller-home';
8
+ import {
9
+ aggregateMcpTransportLatencySamples,
10
+ extractForgeServerDuration,
11
+ normalizeMcpTransportLatencySample,
12
+ type McpTransportLatencySample,
13
+ } from '../src/runtime/diagnostics/mcp-transport-benchmark';
14
+
15
+ function option(name: string): string | undefined {
16
+ const index = process.argv.indexOf(name);
17
+ return index >= 0 ? process.argv[index + 1] : undefined;
18
+ }
19
+
20
+ function integerAtLeast(name: string, fallback: number, minimum: number): number {
21
+ const raw = option(name);
22
+ if (!raw) return fallback;
23
+ const value = Number(raw);
24
+ if (!Number.isInteger(value) || value < minimum) {
25
+ const requirement = minimum === 0 ? 'a non-negative integer' : `an integer >= ${minimum}`;
26
+ throw new Error(`${name} must be ${requirement}`);
27
+ }
28
+ return value;
29
+ }
30
+
31
+ function positiveInteger(name: string, fallback: number): number {
32
+ return integerAtLeast(name, fallback, 1);
33
+ }
34
+
35
+ function readSamples(path: string): McpTransportLatencySample[] {
36
+ const text = readFileSync(path, 'utf8').trim();
37
+ if (!text) return [];
38
+ if (text.startsWith('[')) {
39
+ const values = JSON.parse(text);
40
+ if (!Array.isArray(values)) throw new Error('--input JSON must be an array or JSONL');
41
+ return values.map(normalizeMcpTransportLatencySample);
42
+ }
43
+ return text.split(/\r?\n/).filter(Boolean).map((line) => normalizeMcpTransportLatencySample(JSON.parse(line)));
44
+ }
45
+
46
+ export function timingLedgerServerDuration(
47
+ path: string | undefined,
48
+ tool: string,
49
+ meta: Record<string, unknown>,
50
+ ): number | undefined {
51
+ if (!path) return undefined;
52
+ const traceId = typeof meta.traceId === 'string' ? meta.traceId : undefined;
53
+ const requestId = typeof meta.requestId === 'string' ? meta.requestId : undefined;
54
+ if (!traceId && !requestId) return undefined;
55
+ try {
56
+ const text = readFileSync(path, 'utf8').trim();
57
+ if (!text) return undefined;
58
+ const lines = text.split(/\r?\n/).filter(Boolean);
59
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
60
+ let value: Record<string, unknown>;
61
+ try {
62
+ value = JSON.parse(lines[index]!) as Record<string, unknown>;
63
+ } catch {
64
+ continue;
65
+ }
66
+ if (value.tool !== tool) continue;
67
+ if (traceId && value.traceId !== traceId) continue;
68
+ if (requestId && value.requestId !== requestId) continue;
69
+ const duration = value.totalToolDurationMs;
70
+ if (typeof duration === 'number' && Number.isFinite(duration) && duration >= 0) return duration;
71
+ }
72
+ } catch {
73
+ return undefined;
74
+ }
75
+ return undefined;
76
+ }
77
+
78
+ async function probe(): Promise<McpTransportLatencySample[]> {
79
+ const endpointRaw = option('--endpoint');
80
+ if (!endpointRaw) throw new Error('--endpoint is required unless --input is used');
81
+ const endpoint = new URL(endpointRaw);
82
+ const label = option('--label')?.trim() || endpoint.hostname;
83
+ const tool = option('--tool')?.trim() || 'rh_status';
84
+ const iterations = positiveInteger('--iterations', 30);
85
+ const warmup = integerAtLeast('--warmup', 3, 0);
86
+ const timeoutMs = positiveInteger('--timeout-ms', 120_000);
87
+ const timingLog = option('--timing-log')?.trim();
88
+ const includeConnect = process.argv.includes('--include-connect');
89
+ const timingScope = includeConnect ? 'connect_and_tool_call' as const : 'tool_call' as const;
90
+ const tokenEnv = option('--token-env')?.trim() || 'FORGE_MCP_BENCH_TOKEN';
91
+ const localServiceAuth = process.argv.includes('--local-service-auth');
92
+ const token = process.env[tokenEnv]?.trim()
93
+ || (localServiceAuth
94
+ ? readMcpServiceBearerToken(resolveRepoPreferredControllerHome(process.cwd()))?.trim()
95
+ : undefined);
96
+ if (!token) {
97
+ throw new Error(localServiceAuth
98
+ ? 'MCP_BENCH_LOCAL_SERVICE_TOKEN_UNAVAILABLE: no local Forge service bearer credential was found'
99
+ : `${tokenEnv} is not set; use --local-service-auth on the Forge host or keep benchmark bearer credentials in the environment, never CLI args`);
100
+ }
101
+ const argumentsRaw = option('--arguments');
102
+ const args = argumentsRaw ? JSON.parse(argumentsRaw) as Record<string, unknown> : {};
103
+ const repoId = option('--repo-id')?.trim();
104
+ if (repoId && args.repo_id === undefined) args.repo_id = repoId;
105
+
106
+ const createClient = () => {
107
+ const transport = new StreamableHTTPClientTransport(endpoint, {
108
+ requestInit: { headers: { Authorization: `Bearer ${token}` } },
109
+ });
110
+ const client = new Client({ name: 'forge-mcp-transport-benchmark', version: '1.0.0' });
111
+ return { client, transport };
112
+ };
113
+ const steadyState = includeConnect ? undefined : createClient();
114
+ const call = async (): Promise<{ response: unknown; totalMs: number }> => {
115
+ const connection = steadyState ?? createClient();
116
+ const started = performance.now();
117
+ try {
118
+ if (includeConnect) await connection.client.connect(connection.transport);
119
+ const response = await connection.client.callTool({ name: tool, arguments: args }, undefined, { timeout: timeoutMs });
120
+ return { response, totalMs: performance.now() - started };
121
+ } finally {
122
+ if (includeConnect) await connection.client.close().catch(() => undefined);
123
+ }
124
+ };
125
+ const samples: McpTransportLatencySample[] = [];
126
+ try {
127
+ if (steadyState) await steadyState.client.connect(steadyState.transport);
128
+ for (let index = 0; index < warmup; index += 1) await call();
129
+ for (let index = 0; index < iterations; index += 1) {
130
+ const { response, totalMs } = await call();
131
+ const structured = response && typeof response === 'object' && !Array.isArray(response)
132
+ ? (response as Record<string, unknown>).structuredContent
133
+ : undefined;
134
+ const responseMeta = structured && typeof structured === 'object' && !Array.isArray(structured)
135
+ ? (structured as Record<string, unknown>).responseMeta
136
+ : undefined;
137
+ const meta = responseMeta && typeof responseMeta === 'object' && !Array.isArray(responseMeta)
138
+ ? responseMeta as Record<string, unknown>
139
+ : {};
140
+ const serverDurationMs = extractForgeServerDuration(response)
141
+ ?? timingLedgerServerDuration(timingLog, tool, meta);
142
+ if (serverDurationMs === undefined) {
143
+ throw new Error('MCP_SERVER_DURATION_MISSING: target must return Forge responseMeta.serverDurationMs or expose a matching --timing-log ledger entry');
144
+ }
145
+ samples.push({
146
+ label,
147
+ tool,
148
+ timingScope,
149
+ clientTotalMs: totalMs,
150
+ serverDurationMs,
151
+ ...(typeof meta.traceId === 'string' ? { traceId: meta.traceId } : {}),
152
+ ...(typeof meta.requestId === 'string' ? { requestId: meta.requestId } : {}),
153
+ observedAt: new Date().toISOString(),
154
+ });
155
+ }
156
+ } finally {
157
+ if (steadyState) await steadyState.client.close().catch(() => undefined);
158
+ }
159
+ return samples;
160
+ }
161
+
162
+ if (import.meta.main) {
163
+ const input = option('--input');
164
+ const samples = input ? readSamples(input) : await probe();
165
+ const report = aggregateMcpTransportLatencySamples(samples);
166
+ console.log(JSON.stringify({
167
+ generatedAt: new Date().toISOString(),
168
+ ...report,
169
+ }, null, 2));
170
+ }
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { execFileSync, spawn } from 'child_process';
4
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'fs';
5
+ import { tmpdir } from 'os';
6
+ import { join } from 'path';
7
+ import { performance } from 'perf_hooks';
8
+ import { buildControllerContextPackAsync } from '../src/cli/controller/context-pack';
9
+ import { getMcpPolicy } from '../src/cli/mcp/policy';
10
+ import { closeCodeGraphReadProviderSessions } from '../src/runtime/context/codegraph-read-provider';
11
+ import { clearAllSessionCachesForTest } from '../src/cli/repository/session-cache';
12
+ import { startLightweightRepositoryCommand } from '../src/runtime/execution/process-runtime/lightweight-managed';
13
+ import { registerRepository } from '../src/cli/repositories/registry';
14
+
15
+ function percentile(values: number[], ratio: number): number {
16
+ const sorted = [...values].sort((a, b) => a - b);
17
+ return Math.round((sorted[Math.min(sorted.length - 1, Math.floor(sorted.length * ratio))] ?? 0) * 100) / 100;
18
+ }
19
+ async function rawGitStatus(root: string): Promise<number> {
20
+ const started = performance.now();
21
+ await new Promise<void>((resolve, reject) => {
22
+ const child = spawn('git', ['status', '--short'], { cwd: root, stdio: 'ignore' });
23
+ child.on('error', reject); child.on('close', () => resolve());
24
+ });
25
+ return performance.now() - started;
26
+ }
27
+
28
+ const iterationsIndex = process.argv.indexOf('--iterations');
29
+ const iterations = Math.max(2, Number(iterationsIndex >= 0 ? process.argv[iterationsIndex + 1] : 7) || 7);
30
+ const structuralIndex = process.argv.indexOf('--structural');
31
+ const structuralContext = process.argv[structuralIndex + 1] === 'off' ? 'off' as const : 'auto' as const;
32
+ const sandboxRoot = mkdtempSync(join(tmpdir(), 'forge-quality-harness-benchmark-'));
33
+ const root = join(sandboxRoot, 'repo');
34
+ const controllerHome = join(sandboxRoot, 'controller');
35
+ try {
36
+ mkdirSync(root, { recursive: true });
37
+ execFileSync('git', ['init', '-q', '-b', 'main'], { cwd: root });
38
+ execFileSync('git', ['config', 'user.email', 'benchmark@example.test'], { cwd: root });
39
+ execFileSync('git', ['config', 'user.name', 'Forge Benchmark'], { cwd: root });
40
+ mkdirSync(join(root, 'src'), { recursive: true });
41
+ for (let index = 0; index < 120; index += 1) {
42
+ writeFileSync(join(root, 'src', `module-${index}.ts`), `export function symbol${index}(value: number) {\n return value + ${index};\n}\n`);
43
+ }
44
+ writeFileSync(join(root, 'src', 'target.ts'), 'export function benchmarkTarget(value: number) {\n const next = value + 1;\n return next;\n}\n');
45
+ writeFileSync(join(root, 'src', 'consumer.ts'), "import { benchmarkTarget } from './target';\nexport function consumeBenchmarkTarget(value: number) {\n return benchmarkTarget(value) * 2;\n}\n");
46
+ execFileSync('git', ['add', '.'], { cwd: root }); execFileSync('git', ['commit', '-qm', 'fixture'], { cwd: root });
47
+ const repository = registerRepository({ path: root, controllerHome, displayName: 'quality-harness-benchmark' });
48
+
49
+ const contextCold: number[] = [];
50
+ const contextHot: number[] = [];
51
+ const structuralProcess: number[] = [];
52
+ const structuralOpen: number[] = [];
53
+ const structuralQuery: number[] = [];
54
+ let hotCacheEvidence: unknown;
55
+ let contextAccuracyEvidence: { selected: string[]; required: string[]; recall: number; precision: number } | undefined;
56
+ const requiredContextPaths = ['src/target.ts', 'src/consumer.ts'];
57
+ for (let index = 0; index < iterations; index += 1) {
58
+ clearAllSessionCachesForTest();
59
+ const session = { sessionId: `benchmark-${index}`, repoId: repository.repoId, checkoutId: repository.activeCheckoutId };
60
+ let started = performance.now();
61
+ const cold = await buildControllerContextPackAsync(root, getMcpPolicy('controller'), { description: 'benchmarkTarget implementation and impact', searchTerms: ['benchmarkTarget'], knownPaths: ['src/target.ts'], structuralContext, session });
62
+ contextCold.push(performance.now() - started);
63
+ const structuralTimings = cold.structuralContext?.timingsMs;
64
+ if (typeof structuralTimings?.process === 'number') structuralProcess.push(structuralTimings.process);
65
+ if (typeof structuralTimings?.open === 'number') structuralOpen.push(structuralTimings.open);
66
+ if (typeof structuralTimings?.query === 'number') structuralQuery.push(structuralTimings.query);
67
+ started = performance.now();
68
+ const hot = await buildControllerContextPackAsync(root, getMcpPolicy('controller'), { description: 'benchmarkTarget implementation and impact', searchTerms: ['benchmarkTarget'], knownPaths: ['src/target.ts'], structuralContext, session });
69
+ contextHot.push(performance.now() - started);
70
+ hotCacheEvidence = hot.cache;
71
+ const selected = [...new Set(hot.files.map((file) => file.path))].sort();
72
+ const requiredHits = requiredContextPaths.filter((path) => selected.includes(path)).length;
73
+ contextAccuracyEvidence = {
74
+ selected,
75
+ required: requiredContextPaths,
76
+ recall: requiredHits / requiredContextPaths.length,
77
+ precision: selected.length === 0 ? 0 : requiredHits / selected.length,
78
+ };
79
+ }
80
+
81
+ const rawChild: number[] = [];
82
+ const harnessTotal: number[] = [];
83
+ const preSpawnHarness: number[] = [];
84
+ for (let index = 0; index < iterations; index += 1) {
85
+ rawChild.push(await rawGitStatus(root));
86
+ const started = performance.now();
87
+ const run = await startLightweightRepositoryCommand({
88
+ controllerHome, repository, interactiveWaitMs: 5_000, timeoutMs: 30_000,
89
+ execution: {
90
+ command: ['git', 'status', '--short'],
91
+ authorizationDecision: { decision: 'allow', source: 'full_access', reason: 'benchmark fixture' },
92
+ },
93
+ });
94
+ harnessTotal.push(performance.now() - started);
95
+ if (run.metrics.preSpawnHarnessMs !== undefined) preSpawnHarness.push(run.metrics.preSpawnHarnessMs);
96
+ }
97
+
98
+ const steadyContextCold = contextCold.slice(1);
99
+ const output = {
100
+ schemaVersion: 1,
101
+ iterations,
102
+ context: {
103
+ structuralContext,
104
+ coldP50Ms: percentile(contextCold, 0.5), coldP95Ms: percentile(contextCold, 0.95),
105
+ startupColdMs: Math.round((contextCold[0] ?? 0) * 100) / 100,
106
+ steadyColdP50Ms: percentile(steadyContextCold, 0.5),
107
+ steadyColdP95Ms: percentile(steadyContextCold, 0.95),
108
+ coldSamplesMs: contextCold.map((value) => Math.round(value * 100) / 100),
109
+ structuralProcessP50Ms: percentile(structuralProcess, 0.5),
110
+ structuralProcessP95Ms: percentile(structuralProcess, 0.95),
111
+ structuralOpenP50Ms: percentile(structuralOpen, 0.5),
112
+ structuralQueryP50Ms: percentile(structuralQuery, 0.5),
113
+ hotP50Ms: percentile(contextHot, 0.5), hotP95Ms: percentile(contextHot, 0.95),
114
+ hotToColdRatio: Math.round((percentile(contextHot, 0.5) / Math.max(0.01, percentile(contextCold, 0.5))) * 1_000) / 1_000,
115
+ cacheEvidence: hotCacheEvidence,
116
+ accuracy: contextAccuracyEvidence,
117
+ },
118
+ command: {
119
+ rawChildP50Ms: percentile(rawChild, 0.5), lightweightTotalP50Ms: percentile(harnessTotal, 0.5),
120
+ preSpawnHarnessP50Ms: percentile(preSpawnHarness, 0.5),
121
+ preSpawnHarnessP95Ms: percentile(preSpawnHarness, 0.95),
122
+ durableProcessWrites: 0, leaseOperations: 0,
123
+ },
124
+ thresholds: {
125
+ hotContextToColdP50MaxRatio: 0.9,
126
+ contextRecallMin: 1,
127
+ contextPrecisionMin: 1,
128
+ lightweightPreSpawnHarnessP95MaxMs: 750,
129
+ durableProcessWritesMax: 0,
130
+ leaseOperationsMax: 0,
131
+ },
132
+ };
133
+ const assertions = {
134
+ hotContextReuse: output.context.hotToColdRatio <= output.thresholds.hotContextToColdP50MaxRatio,
135
+ completeRequiredContext: (output.context.accuracy?.recall ?? 0) >= output.thresholds.contextRecallMin,
136
+ boundedContextNoise: (output.context.accuracy?.precision ?? 0) >= output.thresholds.contextPrecisionMin,
137
+ boundedPreSpawnHarness: output.command.preSpawnHarnessP95Ms <= output.thresholds.lightweightPreSpawnHarnessP95MaxMs,
138
+ zeroDurableWrites: output.command.durableProcessWrites <= output.thresholds.durableProcessWritesMax,
139
+ zeroLeaseOperations: output.command.leaseOperations <= output.thresholds.leaseOperationsMax,
140
+ };
141
+ process.stdout.write(`${JSON.stringify({ ...output, assertions, passed: Object.values(assertions).every(Boolean) }, null, 2)}\n`);
142
+ if (!Object.values(assertions).every(Boolean)) process.exitCode = 1;
143
+ } finally {
144
+ await closeCodeGraphReadProviderSessions();
145
+ rmSync(sandboxRoot, { recursive: true, force: true });
146
+ }