@moretea-labs/forge 1.5.0-rc.1 → 1.5.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 (264) hide show
  1. package/AGENTS.md +9 -11
  2. package/CHANGELOG.md +27 -1
  3. package/CLAUDE.md +8 -10
  4. package/DESIGN.md +278 -0
  5. package/README.md +70 -90
  6. package/README.zh-CN.md +52 -55
  7. package/SKILL.md +8 -11
  8. package/THIRD_PARTY_NOTICES.md +9 -3
  9. package/assets/branding/README.md +4 -2
  10. package/assets/hooks/lib/workflow-state.sh +19 -18
  11. package/assets/hooks/post-edit-guard.sh +6 -75
  12. package/assets/hooks/prompt-guard.sh +24 -12
  13. package/assets/hooks/session-start-context.sh +17 -17
  14. package/assets/hooks/stop-orchestrator.sh +2 -2
  15. package/assets/partials/04-project-structure.partial.md +6 -6
  16. package/assets/partials/05-workflow.partial.md +6 -6
  17. package/assets/partials/07-footer.partial.md +1 -1
  18. package/assets/partials/08-orchestration.partial.md +2 -2
  19. package/assets/partials-agents/02-operating-mode.partial.md +5 -5
  20. package/assets/partials-agents/03-orchestration.partial.md +3 -3
  21. package/assets/partials-agents/04-task-protocol.partial.md +3 -3
  22. package/assets/partials-agents/08-deep-docs.partial.md +1 -1
  23. package/assets/reference-configs/agentic-development-flow.md +49 -86
  24. package/assets/reference-configs/external-tooling.md +21 -61
  25. package/assets/reference-configs/handoff-protocol.md +25 -20
  26. package/assets/reference-configs/harness-overview.md +8 -8
  27. package/assets/skill-version.json +2 -2
  28. package/assets/templates/helpers/archive-workflow.sh +0 -1
  29. package/assets/templates/helpers/capture-plan.sh +3 -3
  30. package/assets/templates/helpers/check-task-workflow.sh +2 -2
  31. package/assets/templates/helpers/codex-handoff-resume.sh +5 -5
  32. package/assets/templates/helpers/ensure-task-workflow.sh +37 -35
  33. package/assets/templates/helpers/new-plan.sh +1 -1
  34. package/assets/templates/helpers/plan-to-todo.sh +2 -2
  35. package/assets/templates/helpers/prepare-codex-handoff.sh +2 -2
  36. package/assets/templates/helpers/prepare-handoff.sh +9 -9
  37. package/assets/templates/helpers/refresh-current-status.sh +2 -2
  38. package/assets/templates/helpers/ship-worktrees.sh +1 -1
  39. package/assets/templates/helpers/sprint-backlog.sh +1 -1
  40. package/assets/templates/helpers/verify-sprint.sh +7 -7
  41. package/assets/templates/helpers/workstream-sync.sh +1 -1
  42. package/assets/templates/plan.template.md +1 -1
  43. package/assets/workflow-contract.v1.json +6 -3
  44. package/docs/architecture/current/README.md +10 -9
  45. package/docs/architecture/current/approved-target-architecture.zh-CN.md +2 -2
  46. package/docs/architecture/current/architecture-invariants.md +2 -2
  47. package/docs/architecture/current/automation-and-schedule-engine.md +15 -15
  48. package/docs/architecture/current/codegraph-context-provider.md +2 -2
  49. package/docs/architecture/current/control-plane-authority-inventory.md +12 -0
  50. package/docs/architecture/current/dispatch-and-agent-strategy.md +11 -11
  51. package/docs/architecture/current/entity-model.md +0 -27
  52. package/docs/architecture/current/failure-recovery.md +0 -24
  53. package/docs/architecture/current/governance.md +1 -1
  54. package/docs/architecture/current/implementation-status.md +1 -1
  55. package/docs/architecture/current/legacy-governance.md +58 -0
  56. package/docs/architecture/current/multi-repository-execution.md +1 -1
  57. package/docs/architecture/current/peer-supercontroller-runtime.md +1 -1
  58. package/docs/architecture/current/personal-assistant-plugin-baseline.md +4 -5
  59. package/docs/architecture/current/scheduler-and-resource-claims.md +0 -10
  60. package/docs/architecture/current/target-requirements-traceability.md +2 -2
  61. package/docs/architecture/current/thin-harness-governance-policy.md +1 -1
  62. package/docs/architecture/current/thin-harness-v1.md +9 -11
  63. package/docs/architecture/domains/runtime-harness.md +1 -1
  64. package/docs/forge-autonomous-goal-loop.md +3 -1
  65. package/docs/forge-chatgpt-mcp-setup.md +67 -176
  66. package/docs/images/forge-banner-cn.svg +26 -1
  67. package/docs/images/forge-banner.svg +26 -1
  68. package/docs/operations/features.md +1 -1
  69. package/docs/operations/homebrew.md +3 -3
  70. package/docs/operations/mcp-tool-exposure.md +5 -5
  71. package/docs/operations/platform-support.md +45 -37
  72. package/docs/operations/platform-support.zh-CN.md +40 -38
  73. package/docs/operations/releasing.md +3 -3
  74. package/docs/operations/releasing.zh-CN.md +3 -3
  75. package/docs/operations/troubleshooting.md +11 -6
  76. package/docs/operations/troubleshooting.zh-CN.md +11 -6
  77. package/docs/public-usage-guide.md +28 -21
  78. package/docs/public-usage-guide.zh-CN.md +28 -21
  79. package/docs/tutorials/01-install-and-start.md +58 -35
  80. package/docs/tutorials/01-install-and-start.zh-CN.md +58 -35
  81. package/docs/tutorials/02-connect-chatgpt.md +118 -46
  82. package/docs/tutorials/02-connect-chatgpt.zh-CN.md +119 -45
  83. package/docs/wiki/Architecture.md +1 -1
  84. package/docs/wiki/Installation.md +2 -2
  85. package/docs/wiki/Quick-Start.md +1 -1
  86. package/docs/wiki/Work-Lifecycle.md +1 -1
  87. package/install.ps1 +4 -5
  88. package/install.sh +4 -6
  89. package/package.json +15 -5
  90. package/scripts/archive-workflow.sh +0 -1
  91. package/scripts/benchmark-thin-harness-mcp-e2e.ts +27 -14
  92. package/scripts/build-controller-ui.ts +28 -0
  93. package/scripts/capture-plan.sh +4 -4
  94. package/scripts/check-agent-tooling.sh +12 -1
  95. package/scripts/check-platform-support.mjs +2 -2
  96. package/scripts/check-public-docs.sh +16 -5
  97. package/scripts/check-release-readiness.sh +5 -11
  98. package/scripts/check-runtime-architecture.mjs +14 -22
  99. package/scripts/check-tarball-install-smoke.sh +3 -2
  100. package/scripts/check-task-workflow.sh +2 -2
  101. package/scripts/codex-handoff-resume.sh +5 -5
  102. package/scripts/create-project-dirs.sh +3 -3
  103. package/scripts/ensure-task-workflow.sh +45 -35
  104. package/scripts/init-project.sh +4 -4
  105. package/scripts/install-desktop-operator-registration.ts +2 -2
  106. package/scripts/lib/project-init-lib.sh +46 -44
  107. package/scripts/migrate-project-template.sh +7 -4
  108. package/scripts/new-plan.sh +1 -1
  109. package/scripts/plan-to-todo.sh +2 -2
  110. package/scripts/prepare-codex-handoff.sh +2 -2
  111. package/scripts/prepare-handoff.sh +9 -9
  112. package/scripts/refresh-current-status.sh +2 -2
  113. package/scripts/setup-plugins.sh +3 -3
  114. package/scripts/ship-worktrees.sh +1 -1
  115. package/scripts/sprint-backlog.sh +5 -5
  116. package/scripts/sync-codex-installed-copies.sh +2 -3
  117. package/scripts/verify-sprint.sh +7 -7
  118. package/scripts/workstream-sync.sh +1 -1
  119. package/src/cli/chatgpt-browser/bridge-extension.ts +1 -1
  120. package/src/cli/chatgpt-browser/bridge-provider.ts +29 -0
  121. package/src/cli/chatgpt-browser/file-policy.ts +3 -1
  122. package/src/cli/commands/chatgpt.ts +51 -0
  123. package/src/cli/commands/doctor.ts +1 -1
  124. package/src/cli/commands/init-hook.ts +213 -70
  125. package/src/cli/commands/init.ts +5 -6
  126. package/src/cli/commands/mcp.ts +31 -5
  127. package/src/cli/commands/runtime.ts +61 -8
  128. package/src/cli/commands/setup-profile.ts +472 -0
  129. package/src/cli/controller/check-runner.ts +20 -3
  130. package/src/cli/controller/codex-continuation.ts +4 -7
  131. package/src/cli/controller/context-pack.ts +118 -14
  132. package/src/cli/controller/operational-plan.ts +4 -4
  133. package/src/cli/controller/task-ledger.ts +10 -10
  134. package/src/cli/controller/work-mode.ts +6 -13
  135. package/src/cli/index.ts +14 -35
  136. package/src/cli/local-bridge/connector-freshness.ts +1 -1
  137. package/src/cli/local-bridge/console-assets.ts +17 -0
  138. package/src/cli/local-bridge/console-automations.ts +160 -0
  139. package/src/cli/local-bridge/dashboard.ts +8 -1303
  140. package/src/cli/local-bridge/facade-api.ts +16 -7
  141. package/src/cli/local-bridge/frontend/src/App.tsx +10 -0
  142. package/src/cli/local-bridge/frontend/src/components/Button.tsx +1 -0
  143. package/src/cli/local-bridge/frontend/src/components/DetailPane.tsx +3 -0
  144. package/src/cli/local-bridge/frontend/src/components/SearchField.tsx +1 -0
  145. package/src/cli/local-bridge/frontend/src/components/SectionHeader.tsx +1 -0
  146. package/src/cli/local-bridge/frontend/src/components/Segmented.tsx +1 -0
  147. package/src/cli/local-bridge/frontend/src/components/StatusText.tsx +3 -0
  148. package/src/cli/local-bridge/frontend/src/design/icons.tsx +11 -0
  149. package/src/cli/local-bridge/frontend/src/design/tokens.css +8 -0
  150. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +3 -0
  151. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +4 -0
  152. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +3 -0
  153. package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +2 -0
  154. package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +2 -0
  155. package/src/cli/local-bridge/frontend/src/features/system/SystemView.tsx +2 -0
  156. package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +3 -0
  157. package/src/cli/local-bridge/frontend/src/lib/api.ts +23 -0
  158. package/src/cli/local-bridge/frontend/src/lib/format.ts +4 -0
  159. package/src/cli/local-bridge/frontend/src/lib/routes.ts +7 -0
  160. package/src/cli/local-bridge/frontend/src/main.tsx +1 -0
  161. package/src/cli/local-bridge/frontend/src/shell/AppShell.tsx +1 -0
  162. package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +2 -0
  163. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +3 -0
  164. package/src/cli/local-bridge/frontend/src/styles.css +12 -0
  165. package/src/cli/local-bridge/frontend/src/types.ts +14 -0
  166. package/src/cli/local-bridge/frontend/src/vite-env.d.ts +1 -0
  167. package/src/cli/local-bridge/frontend/vite.config.ts +27 -0
  168. package/src/cli/local-bridge/server.ts +45 -0
  169. package/src/cli/local-bridge/ui-dist/app.css +1 -0
  170. package/src/cli/local-bridge/ui-dist/app.js +11094 -0
  171. package/src/cli/mcp/auth.ts +2 -0
  172. package/src/cli/mcp/instructions.ts +3 -3
  173. package/src/cli/mcp/legacy-tool-service.ts +27 -23
  174. package/src/cli/mcp/multi-repository.ts +2 -0
  175. package/src/cli/mcp/policy.ts +7 -5
  176. package/src/cli/mcp/repository-tools.ts +53 -14
  177. package/src/cli/mcp/server.ts +5 -10
  178. package/src/cli/mcp/setup.ts +220 -276
  179. package/src/cli/mcp/tool-capability-cache.ts +1 -1
  180. package/src/cli/mcp/toolset-names.ts +1 -1
  181. package/src/cli/mcp/toolset.ts +0 -1
  182. package/src/cli/mcp/transports/http.ts +51 -4
  183. package/src/cli/repositories/branch-name-policy.ts +0 -7
  184. package/src/cli/repositories/controller-home.ts +110 -3
  185. package/src/cli/repositories/safe-patch.ts +1 -1
  186. package/src/cli/repositories/selected-path-actions.ts +26 -24
  187. package/src/core/adoption/gitignore-plan.ts +2 -3
  188. package/src/core/adoption/plan.ts +4 -1
  189. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +556 -0
  190. package/src/runtime/control-plane/execution/work-handle-store.ts +128 -1
  191. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +277 -0
  192. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +169 -0
  193. package/src/runtime/control-plane/facade/capability-registry.ts +0 -10
  194. package/src/runtime/control-plane/facade/handoff-inbox-store.ts +44 -25
  195. package/src/runtime/control-plane/facade/suggested-actions.ts +7 -1
  196. package/src/runtime/control-plane/facade/types.ts +0 -1
  197. package/src/runtime/control-plane/global-scheduler/scheduler.ts +30 -8
  198. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +114 -0
  199. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +175 -0
  200. package/src/runtime/control-plane/launcher/launch-reservation-store.ts +161 -0
  201. package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +64 -0
  202. package/src/runtime/control-plane/launcher/thin-launcher.ts +117 -24
  203. package/src/runtime/control-plane/routing/route-policy.ts +14 -14
  204. package/src/runtime/control-plane/runtime-cleanup.ts +0 -1
  205. package/src/runtime/evidence/event-ledger.ts +1 -1
  206. package/src/runtime/{workflow/campaigns/workspace.ts → execution/managed-workspace.ts} +41 -43
  207. package/src/runtime/execution/process-runtime/check-facade.ts +6 -0
  208. package/src/runtime/execution/process-runtime/check-result.ts +45 -0
  209. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +42 -16
  210. package/src/runtime/execution/process-runtime/command-facade.ts +4 -2
  211. package/src/runtime/execution/process-runtime/gc.ts +1 -1
  212. package/src/runtime/execution/process-runtime/index.ts +3 -0
  213. package/src/runtime/execution/process-runtime/interactive-admission.ts +14 -5
  214. package/src/runtime/execution/process-runtime/persisted-check.ts +285 -0
  215. package/src/runtime/execution/process-runtime/resource-claims.ts +30 -0
  216. package/src/runtime/execution/process-runtime/runtime.ts +18 -1
  217. package/src/runtime/execution/process-runtime/store.ts +1 -19
  218. package/src/runtime/execution/process-runtime/types.ts +4 -0
  219. package/src/runtime/execution/thin-harness/execution-router.ts +0 -1
  220. package/src/runtime/execution/thin-harness/fast-executor.ts +1 -1
  221. package/src/runtime/execution/thin-harness/lightweight-lanes.ts +1 -2
  222. package/src/runtime/execution/thin-harness/types.ts +0 -1
  223. package/src/runtime/execution/workers/executor.ts +1 -1
  224. package/src/runtime/gateway/mcp/execution-tools.ts +1 -1
  225. package/src/runtime/gateway/mcp/persisted-check-process.ts +3 -255
  226. package/src/runtime/gateway/mcp/process-tools.ts +16 -3
  227. package/src/runtime/gateway/mcp/resource-policy.ts +0 -9
  228. package/src/runtime/gateway/mcp/router.ts +117 -182
  229. package/src/runtime/gateway/mcp/runtime-tools.ts +331 -409
  230. package/src/runtime/gateway/mcp/work-validation-reconciler.ts +1 -219
  231. package/src/runtime/plugins/browser-adapter.ts +5 -0
  232. package/src/runtime/plugins/desktop-operator-registration.ts +1 -1
  233. package/src/runtime/plugins/store.ts +14 -6
  234. package/src/runtime/plugins/types.ts +1 -1
  235. package/src/runtime/projections/materialized-view.ts +1 -23
  236. package/src/runtime/recovery/maintenance-executor.ts +9 -0
  237. package/src/runtime/resources/ownership.ts +0 -1
  238. package/src/runtime/root/gateway-adapter.ts +2 -0
  239. package/src/runtime/root/mcp-transport.ts +10 -2
  240. package/src/runtime/root/package-connector-service.ts +136 -0
  241. package/src/runtime/root/package-runtime-release.ts +161 -0
  242. package/src/runtime/root/package-runtime-service.ts +185 -0
  243. package/src/runtime/root/release-materialize.ts +19 -0
  244. package/src/runtime/root/runtime.ts +2 -1
  245. package/src/runtime/root/scheduler.ts +5 -0
  246. package/src/runtime/root/service.ts +18 -5
  247. package/src/runtime/standalone-recovery/core.ts +65 -16
  248. package/src/runtime/workflow/findings/store.ts +145 -106
  249. package/src/runtime/workflow/findings/types.ts +22 -0
  250. package/src/runtime/workflow/schedules/engine.ts +132 -2
  251. package/src/runtime/workflow/schedules/pending-activation.ts +139 -0
  252. package/src/runtime/workflow/schedules/work-continuation.ts +218 -0
  253. package/docs/CHANGELOG.md +0 -978
  254. package/docs/README.zh-CN.md +0 -58
  255. package/scripts/smoke-chatgpt-supervised-automation.ts +0 -208
  256. package/src/cli/commands/controller.ts +0 -680
  257. package/src/runtime/workflow/campaigns/cleanup.ts +0 -263
  258. package/src/runtime/workflow/campaigns/engine.ts +0 -112
  259. package/src/runtime/workflow/campaigns/normalize.ts +0 -100
  260. package/src/runtime/workflow/campaigns/review.ts +0 -315
  261. package/src/runtime/workflow/campaigns/store.ts +0 -526
  262. package/src/runtime/workflow/campaigns/supervisor.ts +0 -112
  263. package/src/runtime/workflow/campaigns/types.ts +0 -290
  264. /package/src/runtime/{workflow/campaigns → execution}/workspace-agent.ts +0 -0
@@ -42,8 +42,10 @@ export interface RepositoryCommandProcessInput {
42
42
  timeoutMs?: number;
43
43
  interactiveWaitMs?: number;
44
44
  maxOutputBytes?: number;
45
- /** Force durable workflow (async apply, release, remote). */
45
+ /** Force an explicit external/durable workflow boundary. */
46
46
  forceDurable?: boolean;
47
+ /** Return a Process handle immediately without changing execution architecture. */
48
+ returnHandleImmediately?: boolean;
47
49
  requestId?: string;
48
50
  workId?: string;
49
51
  commandId?: string;
@@ -297,7 +299,7 @@ export async function executeRepositoryCommandViaProcessRuntime(
297
299
  correlationId: input.workId,
298
300
  },
299
301
  signal: input.signal,
300
- returnHandleImmediately: input.forceDurable === true,
302
+ returnHandleImmediately: input.returnHandleImmediately === true,
301
303
  });
302
304
 
303
305
  const route: RepositoryCommandRoute = handle.completed ? 'process_direct' : 'process_managed';
@@ -134,7 +134,7 @@ export function gcTerminalProcesses(options: ProcessGcOptions): ProcessGcResult
134
134
  for (const victim of victims) {
135
135
  if (safeUnlink(victim.path)) removedRecords += 1;
136
136
  if (options.deleteLogs !== false) {
137
- for (const suffix of ['.stdout.log', '.stderr.log', '.exit.json']) {
137
+ for (const suffix of ['.stdout.log', '.stderr.log', '.exit.json', '.exit.json.started.json']) {
138
138
  if (safeUnlink(join(logs, `${victim.processId}${suffix}`))) removedLogs += 1;
139
139
  }
140
140
  }
@@ -7,3 +7,6 @@ export * from './check-receipt';
7
7
  export * from './command-facade';
8
8
  export * from './gc';
9
9
  export type { ProcessCommandDescriptor, ProcessRunnerExitReceipt } from './process-runner-entry';
10
+ export * from './persisted-check';
11
+
12
+ export * from './check-result';
@@ -4,11 +4,20 @@ const SHORT_COMMANDS = new Set([
4
4
  'true', 'false', 'echo', 'printf', 'pwd', 'basename', 'dirname', 'test', '[', 'which',
5
5
  ]);
6
6
 
7
+ // ChatGPT reaches Process Runtime through a remote MCP hop. Returning a handle
8
+ // immediately for a process that finishes a few milliseconds later forces an
9
+ // additional caller round trip that is much more expensive than a small local
10
+ // completion grace. Keep this far below the retired 800ms-2s waits: genuinely
11
+ // long commands still become managed almost immediately.
12
+ const DEFAULT_COMPLETION_GRACE_MS = 100;
13
+
7
14
  /**
8
15
  * Admission is deliberately category-based rather than a second scheduler.
9
- * Only commands that are predictably sub-second get a synchronous window;
10
- * builds, tests, shells, and unknown programs return their existing handle
11
- * immediately. Callers may always explicitly override the recommendation.
16
+ * Predictable primitives retain the larger short-command window. Other
17
+ * synchronous commands receive only a tiny completion grace so near-immediate
18
+ * processes can finish in the originating MCP call instead of forcing a second
19
+ * remote process_wait round trip. Explicit async/zero-wait callers still return
20
+ * handles immediately.
12
21
  */
13
22
  export function durationAwareInteractiveWaitMs(
14
23
  command: string | readonly string[],
@@ -17,7 +26,7 @@ export function durationAwareInteractiveWaitMs(
17
26
  ): number {
18
27
  if (requestedWaitMs !== undefined) return Math.max(0, Math.trunc(requestedWaitMs));
19
28
  const normalized = normalizeRepositoryCommand(command);
20
- if (normalized.kind !== 'argv') return 0;
29
+ if (normalized.kind !== 'argv') return DEFAULT_COMPLETION_GRACE_MS;
21
30
  const executable = normalized.executable?.split(/[\\/]/).at(-1)?.toLowerCase();
22
- return executable && SHORT_COMMANDS.has(executable) ? shortCommandWaitMs : 0;
31
+ return executable && SHORT_COMMANDS.has(executable) ? shortCommandWaitMs : DEFAULT_COMPLETION_GRACE_MS;
23
32
  }
@@ -0,0 +1,285 @@
1
+ import { createHash } from 'crypto';
2
+ import { existsSync } from 'fs';
3
+ import { dirname, resolve } from 'path';
4
+ import {
5
+ controllerCheckExecutionIdentity,
6
+ listControllerChecks,
7
+ snapshotControllerCheck,
8
+ } from '../../../cli/controller/check-runner';
9
+ import {
10
+ currentCliRuntimeTarget,
11
+ resolveCliChildInvocation,
12
+ type CliChildInvocation,
13
+ type CliChildInvocationOptions,
14
+ type CliRuntimeTarget,
15
+ } from '../../../cli/runtime-invocation';
16
+ import { readRuntimeGeneration, resolveControllerRuntimeSourceRoot } from '../../control-plane/runtime-generation';
17
+ import { cleanupWorkVerificationSnapshot, materializeWorkVerificationSnapshot } from '../../control-plane/execution/work-verification-snapshot';
18
+ import {
19
+ checkRequiresDurableWorkflow,
20
+ processCheckSemanticScopeKey,
21
+ type CheckExecutionMode,
22
+ type RunCheckFacadeInput,
23
+ type RunCheckFacadeResult,
24
+ } from './check-facade';
25
+ import { claimsForCheck, toProcessClaims } from './resource-claims';
26
+ import { spawnManagedProcess } from './runtime';
27
+ import { allocatePersistedCheckResultReceiptPath } from './check-result';
28
+ import { durationAwareInteractiveWaitMs } from './interactive-admission';
29
+ const PERSISTED_CHECK_SOURCE_ENTRY = 'src/runtime/execution/process-runtime/check-runner-sidecar.ts';
30
+
31
+ export function resolvePersistedCheckCliInvocation(
32
+ cliEntry: string,
33
+ args: string[],
34
+ options: CliChildInvocationOptions = {},
35
+ ): CliChildInvocation {
36
+ return resolveCliChildInvocation(cliEntry, args, options);
37
+ }
38
+
39
+ export function resolveRuntimeCliTarget(controllerHome?: string): CliRuntimeTarget {
40
+ const moduleSourceRoot = resolve(import.meta.dir, '..', '..', '..', '..');
41
+ const sourceEntry = resolve(moduleSourceRoot, 'src', 'cli', 'index.ts');
42
+ const source = resolveControllerRuntimeSourceRoot({
43
+ explicitRoot: existsSync(sourceEntry) ? moduleSourceRoot : undefined,
44
+ });
45
+ const sourceMode = Boolean(source.root && existsSync(resolve(source.root, 'src', 'cli', 'index.ts')));
46
+ const runtimeEnv = { ...process.env };
47
+ if (sourceMode) {
48
+ delete runtimeEnv.FORGE_RUNTIME_EXECUTION;
49
+ delete runtimeEnv.FORGE_RUNTIME_CLI_ENTRY;
50
+ }
51
+ const generation = controllerHome ? readRuntimeGeneration(controllerHome) : undefined;
52
+ return currentCliRuntimeTarget({
53
+ env: runtimeEnv,
54
+ argv: sourceMode ? [] : process.argv,
55
+ moduleUrl: sourceMode ? undefined : import.meta.url,
56
+ sourceRoot: source.root,
57
+ cwd: source.root ?? moduleSourceRoot,
58
+ sourceRevision: generation?.source.releaseRevision
59
+ ?? generation?.source.commit
60
+ ?? process.env.FORGE_ACTIVE_RUNTIME_REVISION,
61
+ });
62
+ }
63
+
64
+ export function resolveRuntimeCliEntry(controllerHome?: string): string {
65
+ return resolveRuntimeCliTarget(controllerHome).entry;
66
+ }
67
+
68
+ export function persistedCheckSemanticScopeKey(
69
+ input: Pick<RunCheckFacadeInput, 'workId' | 'verificationBinding' | 'checkoutId'>,
70
+ reuseScope: 'repository' | 'checkout',
71
+ ): string {
72
+ return processCheckSemanticScopeKey(input, reuseScope);
73
+ }
74
+
75
+ export function resolvePersistedCheckRuntimeExecutable(
76
+ cliTarget: CliRuntimeTarget,
77
+ runtimeExecutable = process.execPath,
78
+ entryExists: (path: string) => boolean = existsSync,
79
+ ): string {
80
+ if (cliTarget.runtimeKind !== 'compiled_bun_release') return runtimeExecutable;
81
+ const checkRunner = resolve(dirname(runtimeExecutable), 'forge-check-runner');
82
+ if (!entryExists(checkRunner)) {
83
+ throw new Error(`PERSISTED_CHECK_RUNNER_MISSING: ${checkRunner}`);
84
+ }
85
+ return checkRunner;
86
+ }
87
+
88
+ export function resolvePersistedCheckProcessInvocation(
89
+ cliTarget: CliRuntimeTarget,
90
+ args: string[],
91
+ options: { runtimeExecutable?: string; entryExists?: (path: string) => boolean } = {},
92
+ ): CliChildInvocation {
93
+ const runtimeExecutable = options.runtimeExecutable ?? process.execPath;
94
+ const executable = resolvePersistedCheckRuntimeExecutable(cliTarget, runtimeExecutable, options.entryExists ?? existsSync);
95
+ if (cliTarget.runtimeKind === 'compiled_bun_release') {
96
+ return { executable, args };
97
+ }
98
+ const sourceEntry = resolve(cliTarget.cwd, PERSISTED_CHECK_SOURCE_ENTRY);
99
+ if (!(options.entryExists ?? existsSync)(sourceEntry)) {
100
+ throw new Error(`PERSISTED_CHECK_SOURCE_RUNNER_MISSING: ${sourceEntry}`);
101
+ }
102
+ return resolvePersistedCheckCliInvocation(sourceEntry, args, {
103
+ runtimeExecutable: executable,
104
+ runtimeKind: cliTarget.runtimeKind,
105
+ sourceRevision: cliTarget.sourceRevision,
106
+ immutable: false,
107
+ ...(cliTarget.runtimeKind === 'package_launcher' ? { launcherEntry: cliTarget.entry } : {}),
108
+ });
109
+ }
110
+
111
+ /**
112
+ * Run a registered check through Process Runtime while keeping check-runner
113
+ * evidence authoritative. The managed OS process invokes the same immutable
114
+ * forge CLI bundle with a hidden internal subcommand; that subcommand
115
+ * executes runControllerCheck, writes the Artifact atomically, mirrors bounded
116
+ * stdout/stderr, and exits with the exact check status.
117
+ */
118
+ export async function runPersistedCheckViaProcessRuntime(
119
+ input: RunCheckFacadeInput,
120
+ ): Promise<RunCheckFacadeResult> {
121
+ const emptyEffects = {
122
+ executionJobCount: 0,
123
+ localJobCount: 0,
124
+ workerSpawnCount: 0,
125
+ projectionUpdateCount: 0,
126
+ };
127
+ const verificationSnapshot = input.verificationSnapshot
128
+ ? materializeWorkVerificationSnapshot({
129
+ controllerHome: input.controllerHome,
130
+ repoId: input.repoId,
131
+ sourceRoot: input.repoRoot,
132
+ scope: input.verificationSnapshot,
133
+ })
134
+ : undefined;
135
+ const executionRoot = verificationSnapshot?.root ?? input.repoRoot;
136
+ const cleanupVerificationSnapshot = () => {
137
+ if (verificationSnapshot) cleanupWorkVerificationSnapshot(verificationSnapshot.root);
138
+ };
139
+ const check = listControllerChecks(executionRoot).find((entry) => entry.id === input.checkId);
140
+ if (!check) {
141
+ cleanupVerificationSnapshot();
142
+ return {
143
+ mode: 'durable',
144
+ checkId: input.checkId,
145
+ durable: {
146
+ reason: 'check_not_found_or_requires_registry_lookup',
147
+ suggestedOperation: 'list_checks then run_check with a known check_id',
148
+ },
149
+ durableSideEffects: emptyEffects,
150
+ };
151
+ }
152
+ if (input.forceDurable || checkRequiresDurableWorkflow(input.checkId, check)) {
153
+ cleanupVerificationSnapshot();
154
+ return {
155
+ mode: 'durable',
156
+ checkId: input.checkId,
157
+ check,
158
+ durable: {
159
+ reason: 'multi_phase_or_release_check_requires_durable_workflow',
160
+ suggestedOperation: 'Claim the related WorkContract and run this check through an external Controller.',
161
+ },
162
+ durableSideEffects: emptyEffects,
163
+ };
164
+ }
165
+
166
+ const interactiveWaitMs = durationAwareInteractiveWaitMs(check.command, input.interactiveWaitMs);
167
+ const leaseWaitMs = input.leaseWaitMs;
168
+ const timeoutMs = Math.min(
169
+ check.timeoutMs,
170
+ typeof input.timeoutMs === 'number' && Number.isFinite(input.timeoutMs)
171
+ ? Math.max(1_000, Math.trunc(input.timeoutMs))
172
+ : check.timeoutMs,
173
+ );
174
+ if (!input.executionIdentity) {
175
+ throw new Error('EXECUTION_IDENTITY_REQUIRED: persisted run_check requires an immutable executionIdentity');
176
+ }
177
+ const executionIdentity = input.executionIdentity;
178
+ if (executionIdentity.repositoryId !== input.repoId) {
179
+ throw new Error(
180
+ `EXECUTION_IDENTITY_MISMATCH: check repo ${input.repoId} differs from identity ${executionIdentity.repositoryId}`,
181
+ );
182
+ }
183
+ if (input.checkoutId?.trim() && executionIdentity.checkoutId !== input.checkoutId.trim()) {
184
+ throw new Error(
185
+ `CHECKOUT_ROUTE_MISMATCH: check checkout ${input.checkoutId} differs from identity ${executionIdentity.checkoutId}`,
186
+ );
187
+ }
188
+ const claims = claimsForCheck(
189
+ input.checkId,
190
+ check.command,
191
+ executionIdentity.repositoryId,
192
+ executionIdentity.checkoutId,
193
+ check.effects,
194
+ );
195
+ const checkSnapshot = snapshotControllerCheck(executionRoot, input.checkId);
196
+ const checkFingerprint = createHash('sha256')
197
+ .update(JSON.stringify(checkSnapshot))
198
+ .digest('hex');
199
+ const semanticCheck = controllerCheckExecutionIdentity(
200
+ executionRoot,
201
+ input.checkId,
202
+ timeoutMs,
203
+ checkSnapshot,
204
+ );
205
+ const processCheckExecution = {
206
+ schemaVersion: 1 as const,
207
+ checkId: semanticCheck.checkId,
208
+ cacheKey: semanticCheck.cacheKey,
209
+ revision: semanticCheck.revision,
210
+ definitionDigest: semanticCheck.definitionDigest,
211
+ environmentFingerprint: semanticCheck.environmentFingerprint,
212
+ timeoutMs: semanticCheck.timeoutMs,
213
+ reuseScope: semanticCheck.reuseScope,
214
+ scopeKey: persistedCheckSemanticScopeKey(input, semanticCheck.reuseScope),
215
+ };
216
+ const cliTarget = resolveRuntimeCliTarget(input.controllerHome);
217
+ const checkResultReceiptPath = allocatePersistedCheckResultReceiptPath(input.controllerHome, input.repoId);
218
+ const checkArgs = [
219
+ '--repo',
220
+ executionRoot,
221
+ '--check-id',
222
+ input.checkId,
223
+ '--timeout-ms',
224
+ String(timeoutMs),
225
+ '--expected-check-fingerprint',
226
+ checkFingerprint,
227
+ '--result-receipt',
228
+ checkResultReceiptPath,
229
+ ...(verificationSnapshot ? ['--cleanup-root', verificationSnapshot.root] : []),
230
+ ];
231
+ const invocation = resolvePersistedCheckProcessInvocation(cliTarget, checkArgs);
232
+ let handle;
233
+ try {
234
+ handle = await spawnManagedProcess({
235
+ controllerHome: input.controllerHome,
236
+ repoId: executionIdentity.repositoryId,
237
+ checkoutId: executionIdentity.checkoutId,
238
+ executionIdentity,
239
+ workId: input.workId,
240
+ commandId: input.commandId,
241
+ command: {
242
+ kind: 'argv',
243
+ executable: invocation.executable,
244
+ args: invocation.args,
245
+ cwd: executionIdentity.canonicalRoot,
246
+ },
247
+ interactiveWaitMs,
248
+ leaseWaitMs,
249
+ timeoutMs: timeoutMs + 5_000,
250
+ resourceClaims: toProcessClaims(claims),
251
+ checkExecution: processCheckExecution,
252
+ origin: {
253
+ surface: 'check',
254
+ toolName: 'run_check',
255
+ checkId: input.checkId,
256
+ requestId: input.requestId,
257
+ correlationId: input.workId,
258
+ executionSessionId: input.verificationBinding?.executionSessionId,
259
+ editSessionId: input.verificationBinding?.editSessionId,
260
+ editRevision: input.verificationBinding?.editRevision,
261
+ issueId: input.verificationBinding?.issueId,
262
+ taskId: input.verificationBinding?.taskId,
263
+ checkResultReceiptPath,
264
+ workVerificationSnapshot: Boolean(verificationSnapshot),
265
+ },
266
+ signal: input.signal,
267
+ });
268
+ } catch (error) {
269
+ cleanupVerificationSnapshot();
270
+ throw error;
271
+ }
272
+ if (verificationSnapshot && (handle.deduplicated === true || handle.semanticDeduplicated === true)) {
273
+ cleanupVerificationSnapshot();
274
+ }
275
+
276
+ const mode: CheckExecutionMode = handle.completed ? 'direct' : 'managed';
277
+ return {
278
+ mode,
279
+ checkId: input.checkId,
280
+ check,
281
+ process: handle,
282
+ ok: handle.completed ? handle.ok : undefined,
283
+ durableSideEffects: handle.durableSideEffects,
284
+ };
285
+ }
@@ -130,6 +130,33 @@ function browserAppleScriptHeredocClaims(script: string): ResourceClaimSpec[] |
130
130
  return [claimHostService(`osascript:${uniqueTargets[0]}`, 'write')];
131
131
  }
132
132
 
133
+ function iosSimulatorTestCommandClaims(
134
+ command: string | readonly string[],
135
+ repoId: string,
136
+ checkoutId?: string,
137
+ ): ResourceClaimSpec[] | undefined {
138
+ const canonical = normalizeRepositoryCommand(command);
139
+ const isTestAction = (text: string): boolean => /(?:^|\s)(?:test|test-without-building)(?:\s|$)/i.test(text);
140
+ let matches = false;
141
+ if (canonical.kind === 'argv') {
142
+ const program = (canonical.executable ?? '').split(/[\\/]/).at(-1)?.toLowerCase();
143
+ if (program === 'xcodebuild') matches = (canonical.args ?? []).some((arg) => /^(?:test|test-without-building)$/i.test(arg));
144
+ if (!matches) {
145
+ const wrapped = fixedShellWrapperCommand([canonical.executable ?? '', ...(canonical.args ?? [])]);
146
+ matches = Boolean(wrapped && /\bxcodebuild\b/i.test(wrapped) && isTestAction(wrapped));
147
+ }
148
+ } else {
149
+ const shell = canonical.shellCommand ?? '';
150
+ matches = /\bxcodebuild\b/i.test(shell) && isTestAction(shell);
151
+ }
152
+ if (!matches) return undefined;
153
+ return normalizeClaims([
154
+ claimWorkspaceRead(checkoutId),
155
+ claimBuildCacheWrite(repoId),
156
+ claimHostService('ios-simulator-test', 'write'),
157
+ ]);
158
+ }
159
+
133
160
  function hostOnlyCommandClaims(command: string | readonly string[]): ResourceClaimSpec[] | undefined {
134
161
  const canonical = normalizeRepositoryCommand(command);
135
162
  if (canonical.kind === 'shell') {
@@ -208,6 +235,7 @@ function claimsForDeclaredCheckEffects(
208
235
  if (effects.git === 'refs') claims.push(claimGitRefs(repoId));
209
236
  if (effects.git === 'write') claims.push(claimWorkspaceWrite(checkoutId), claimGitIndex(checkoutId), claimGitRefs(repoId));
210
237
  if (effects.network) claims.push(claimNetwork(repoId, effects.network));
238
+ for (const service of effects.hostServices ?? []) claims.push(claimHostService(service, 'write'));
211
239
 
212
240
  return normalizeClaims(claims, { readOnly: claims.every((claim) => claim.mode === 'read') });
213
241
  }
@@ -323,6 +351,8 @@ export function claimsForRepositoryCommand(
323
351
  const classification = classifyRepositoryCommand(command, defaultBranch);
324
352
  const canonical = normalizeRepositoryCommand(command);
325
353
  const focused = isFocusedCheckCommand(command);
354
+ const iosSimulatorClaims = iosSimulatorTestCommandClaims(command, repoId, checkoutId);
355
+ if (iosSimulatorClaims) return iosSimulatorClaims;
326
356
  const hostClaims = hostOnlyCommandClaims(command);
327
357
  if (hostClaims) return hostClaims;
328
358
 
@@ -121,6 +121,21 @@ function safeProcessText(value: string): string {
121
121
  return redactSensitiveText(value).text;
122
122
  }
123
123
 
124
+ function cleanupTerminalRunnerReceipts(record: ManagedProcessRecord): void {
125
+ if (!isManagedProcessTerminal(record) || record.terminalWritten !== true || record.leasesReleased !== true) return;
126
+ const exitReceiptPath = record.exitReceiptPath
127
+ ?? receiptPathFor(record.controllerHome, record.repoId, record.processId);
128
+ for (const path of [exitReceiptPath, `${exitReceiptPath}.started.json`]) {
129
+ if (!path || !existsSync(path)) continue;
130
+ try {
131
+ rmSync(path, { force: true });
132
+ } catch {
133
+ // Best-effort only. The durable terminal record is authoritative and
134
+ // fallback Process GC reconciles stale runner artifacts later.
135
+ }
136
+ }
137
+ }
138
+
124
139
  function sanitizeTerminalProcessArtifacts(record: ManagedProcessRecord): ManagedProcessRecord {
125
140
  if (!isManagedProcessTerminal(record)) return record;
126
141
  let filesExamined = 0;
@@ -555,7 +570,9 @@ export function completeProcessFromEvidence(
555
570
  // already-terminal races (second caller still needs to clear leases once).
556
571
  const afterTerminal = completion.record ?? getProcessRecord(controllerHome, repoId, processId);
557
572
  if (afterTerminal && (completion.ok || completion.reason === 'already_terminal' || afterTerminal.terminalWritten === true)) {
558
- return releaseProcessLeasesOnce(controllerHome, repoId, processId) ?? afterTerminal;
573
+ const released = releaseProcessLeasesOnce(controllerHome, repoId, processId) ?? afterTerminal;
574
+ cleanupTerminalRunnerReceipts(released);
575
+ return released;
559
576
  }
560
577
  return afterTerminal;
561
578
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Durable Managed Process records under controller-home repositories.
3
3
  * Layout: repositories/<repoId>/processes/<processId>.json
4
- * Index: repositories/<repoId>/processes/active-index.json
4
+ * Recovery membership authority: SQLite process_recovery_index/v2.
5
5
  */
6
6
 
7
7
  import { createHash } from 'crypto';
@@ -87,10 +87,6 @@ function checkExecutionBindingPath(controllerHome: string, repoId: string, scope
87
87
  return join(checkExecutionBindingsRoot(controllerHome, repoId), `${checkExecutionBindingKey(repoId, scopeKey, cacheKey)}.json`);
88
88
  }
89
89
 
90
- function indexPath(controllerHome: string, repoId: string): string {
91
- return join(processesRoot(controllerHome, repoId), 'active-index.json');
92
- }
93
-
94
90
  interface ProcessRecoveryIndex {
95
91
  schemaVersion: 2;
96
92
  updatedAt: string;
@@ -118,18 +114,6 @@ function processRecoveryMembership(record: ManagedProcessRecord): { active: bool
118
114
  };
119
115
  }
120
116
 
121
- function projectRecoveryIndex(controllerHome: string, repoId: string, index: ProcessRecoveryIndex): void {
122
- // Compatibility/readability projection only. SQLite is authoritative; the
123
- // projection is never read by Process Runtime after v2 migration.
124
- atomicWrite(indexPath(controllerHome, repoId), {
125
- schemaVersion: 2,
126
- source: 'sqlite_projection',
127
- updatedAt: index.updatedAt,
128
- processIds: index.activeProcessIds,
129
- pendingLeaseReleaseIds: index.pendingLeaseReleaseIds,
130
- });
131
- }
132
-
133
117
  function readRecoveryIndex(controllerHome: string, repoId: string): ProcessRecoveryIndex | undefined {
134
118
  return readOrImportControlPlaneRecord<ProcessRecoveryIndex>(controllerHome, {
135
119
  namespace: PROCESS_RECOVERY_INDEX_NAMESPACE,
@@ -163,7 +147,6 @@ function replaceRecoveryIndex(
163
147
  pendingLeaseReleaseIds: normalizedProcessIds(pendingLeaseReleaseIds),
164
148
  }),
165
149
  }).value;
166
- projectRecoveryIndex(controllerHome, repoId, value);
167
150
  return value;
168
151
  }
169
152
 
@@ -224,7 +207,6 @@ function updateRecoveryIndexMembership(
224
207
  };
225
208
  },
226
209
  }).value;
227
- projectRecoveryIndex(controllerHome, repoId, value);
228
210
  return value;
229
211
  }
230
212
 
@@ -248,6 +248,10 @@ export interface ManagedProcessRecord {
248
248
  /** Optional durable task identity consuming the same receipt. */
249
249
  issueId?: string;
250
250
  taskId?: string;
251
+ /** Internal structured result written by the persisted Check sidecar. */
252
+ checkResultReceiptPath?: string;
253
+ /** True when this Check executed in a Work-owned verification snapshot. */
254
+ workVerificationSnapshot?: boolean;
251
255
  };
252
256
  error?: { code: string; message: string };
253
257
  /** Sidecar exit receipt path written by wrapper (survives controller restart). */
@@ -675,7 +675,6 @@ function durableSuggestion(operation: string): string {
675
675
  if (operation.includes('patch')) return 'repository_safe_patch_apply with apply_mode=async or Durable Work';
676
676
  if (operation.includes('command')) return 'repository_command_execute through Durable Work / Local Job';
677
677
  if (operation.includes('agent') || operation === 'quick_agent_session') return 'quick_agent_session / dispatch_task';
678
- if (operation.includes('campaign')) return 'Campaign (opt-in long orchestration only)';
679
678
  return 'Durable Work (ExecutionJob path)';
680
679
  }
681
680
 
@@ -419,7 +419,7 @@ function ownershipOptionsFor(
419
419
  }
420
420
 
421
421
  /**
422
- * Execute one Fast Path operation without ExecutionJob / Local Job / Worker / Campaign.
422
+ * Execute one Fast Path operation without ExecutionJob / Local Job / Worker.
423
423
  */
424
424
  export async function executeFast(
425
425
  ctx: FastExecutorContext,
@@ -157,7 +157,7 @@ function digest(value: unknown): string {
157
157
  }
158
158
 
159
159
  /**
160
- * Lightweight concurrent lanes without Campaign/Issue/worktree.
160
+ * Lightweight concurrent lanes without Issue/worktree.
161
161
  * Read lanes use async Fast primitives. Proposal lanes only validate conflicts (no Agent analysis).
162
162
  */
163
163
  export async function executeLightweightLanes(
@@ -429,7 +429,6 @@ export async function executeLightweightLanes(
429
429
  latency,
430
430
  appliedByIntegrator: false,
431
431
  createdIssue: false,
432
- createdCampaign: false,
433
432
  createdWorktree: false,
434
433
  concurrent,
435
434
  };
@@ -295,7 +295,6 @@ export interface LightweightLanesResult {
295
295
  latency: LatencyBreakdown;
296
296
  appliedByIntegrator: false;
297
297
  createdIssue: false;
298
- createdCampaign: false;
299
298
  createdWorktree: false;
300
299
  concurrent: boolean;
301
300
  }
@@ -22,7 +22,7 @@ import {
22
22
  import { markOperationDelegated } from '../jobs/receipt-store';
23
23
  import { assertAutomatedOperationAllowed } from '../../control-plane/governance/external-effects';
24
24
  import { recordCandidateFinding, updateCandidateFinding } from '../../workflow/findings/store';
25
- import { triggerWorkspaceAgent } from '../../workflow/campaigns/workspace-agent';
25
+ import { triggerWorkspaceAgent } from '../workspace-agent';
26
26
  import { executeAssistantPluginAction } from '../../plugins/store';
27
27
  import { CONTROLLER_SCOPE_REPO_ID, controllerSystemRoot, repositoryControllerRoot } from '../../../cli/repositories/controller-home';
28
28
  import { writeExecutionArtifact } from '../../evidence/artifact-store';
@@ -18,7 +18,7 @@ import { repositoryGitCommit, repositoryGitDeleteBranch, repositoryGitFinishWork
18
18
  import { previewRepositoryCommandExecution } from '../../../cli/repositories/command-executor';
19
19
  import { classifyRepositoryCommand } from '../../../cli/repositories/command-classifier';
20
20
  import { globMatches } from '../../../cli/mcp/paths';
21
- import { ensureManagedWorkspace } from '../../workflow/campaigns/workspace';
21
+ import { ensureManagedWorkspace } from '../../execution/managed-workspace';
22
22
  import { listControllerChecks } from '../../../cli/controller/check-runner';
23
23
  import { readRepositoryAccessPolicy } from '../../control-plane/governance/access-policy';
24
24
  import { appendWorkEvidence, createWorkContract, getWorkContract, recordWorkCompletionReceipt, updateWorkContract, appendVerificationRecord } from '../../control-plane/facade/work-contract-store';