@moretea-labs/forge 1.7.0 → 1.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/AGENTS.md +2 -0
  2. package/CHANGELOG.md +26 -0
  3. package/CLAUDE.md +2 -0
  4. package/THIRD_PARTY_NOTICES.md +1 -1
  5. package/agents/openai.yaml +1 -1
  6. package/assets/partials-agents/02-operating-mode.partial.md +2 -0
  7. package/assets/plugin-registry.v1.json +3 -3
  8. package/assets/skill-version.json +2 -2
  9. package/assets/templates/helpers/check-architecture-sync.sh +17 -0
  10. package/assets/templates/helpers/inspect-project-state.ts +8 -6
  11. package/assets/templates/helpers/migrate-workflow-docs.ts +9 -7
  12. package/assets/templates/helpers/workflow-contract.ts +5 -1
  13. package/docs/architecture/CURRENT.md +12 -4
  14. package/docs/forge-plugin-management.md +1 -1
  15. package/docs/operations/controller-performance-and-502.md +19 -0
  16. package/docs/operations/homebrew.md +2 -2
  17. package/package.json +5 -2
  18. package/scripts/architecture-event.ts +2 -820
  19. package/scripts/architecture-queue.sh +2 -645
  20. package/scripts/archive-architecture-request.sh +2 -242
  21. package/scripts/archive-workflow.sh +1 -291
  22. package/scripts/benchmark-work-efficiency.ts +0 -0
  23. package/scripts/capability-config.ts +2 -392
  24. package/scripts/capability-resolver.ts +2 -503
  25. package/scripts/capture-plan.sh +1 -332
  26. package/scripts/check-agent-tooling.sh +2 -1531
  27. package/scripts/check-architecture-sync.sh +2 -302
  28. package/scripts/check-brain-manifest.sh +2 -253
  29. package/scripts/check-context-files.sh +2 -47
  30. package/scripts/check-deploy-sql-order.sh +2 -115
  31. package/scripts/check-runtime-architecture.mjs +243 -16
  32. package/scripts/check-skill-version.ts +3 -191
  33. package/scripts/check-source-duplication.mjs +61 -0
  34. package/scripts/check-stable-baseline.ts +0 -0
  35. package/scripts/check-task-sync.sh +2 -63
  36. package/scripts/check-task-workflow.sh +2 -1000
  37. package/scripts/codex-handoff-resume.sh +2 -267
  38. package/scripts/context-contract-sync.sh +2 -514
  39. package/scripts/contract-run.ts +2 -463
  40. package/scripts/contract-worktree.sh +1 -702
  41. package/scripts/ensure-task-workflow.sh +1 -1192
  42. package/scripts/inspect-project-state.ts +4 -279
  43. package/scripts/lib/project-init-lib.sh +7 -2
  44. package/scripts/lib/run-forge-helper.sh +19 -0
  45. package/scripts/lib/run-forge-helper.ts +26 -0
  46. package/scripts/migrate-project-template.sh +1 -33
  47. package/scripts/migrate-workflow-docs.ts +4 -412
  48. package/scripts/new-plan.sh +1 -180
  49. package/scripts/new-spec.sh +1 -47
  50. package/scripts/new-sprint.sh +1 -46
  51. package/scripts/plan-to-todo.sh +1 -771
  52. package/scripts/prepare-codex-handoff.sh +2 -201
  53. package/scripts/prepare-handoff.sh +1 -78
  54. package/scripts/refresh-current-status.sh +2 -467
  55. package/scripts/run-governed-gate.ts +3 -0
  56. package/scripts/select-agent-context-blocks.sh +2 -64
  57. package/scripts/ship-worktrees.sh +1 -531
  58. package/scripts/sprint-backlog.sh +1 -834
  59. package/scripts/stage-runtime-release.ts +2 -1
  60. package/scripts/summarize-failures.sh +2 -123
  61. package/scripts/switch-plan.sh +1 -150
  62. package/scripts/sync-brain-docs.sh +2 -379
  63. package/scripts/sync-generated-authority.mjs +175 -0
  64. package/scripts/verify-contract.sh +2 -593
  65. package/scripts/verify-sprint.sh +1 -500
  66. package/scripts/workflow-contract.ts +4 -201
  67. package/scripts/workstream-sync.sh +2 -261
  68. package/src/cli/agent-jobs/executor-health.ts +1 -1
  69. package/src/cli/commands/plugin.ts +12 -3
  70. package/src/cli/controller/check-runner.ts +7 -2
  71. package/src/cli/controller/context-pack.ts +46 -21
  72. package/src/cli/github/contracts.ts +16 -0
  73. package/src/cli/github/github.ts +2 -16
  74. package/src/cli/github/session.ts +2 -1
  75. package/src/cli/local-bridge/facade-api.ts +37 -47
  76. package/src/cli/local-bridge/server.ts +2 -2
  77. package/src/cli/mcp/legacy-tool-service.ts +10 -33
  78. package/src/cli/mcp/post-finalize-work-attribution.ts +150 -0
  79. package/src/cli/mcp/repository-tools.ts +125 -101
  80. package/src/cli/mcp/server.ts +57 -13
  81. package/src/cli/mcp/tool-contract.ts +14 -0
  82. package/src/cli/mcp/toolset.ts +2 -2
  83. package/src/cli/mcp/transports/http.ts +23 -0
  84. package/src/cli/repositories/command-classifier.ts +2 -2
  85. package/src/cli/repositories/command-executor.ts +24 -6
  86. package/src/cli/repositories/repository-snapshot.ts +5 -2
  87. package/src/cli/repositories/structured-git.ts +57 -3
  88. package/src/cli/repository/inspector.ts +11 -3
  89. package/src/runtime/context/generic-lsp-provider.ts +1 -1
  90. package/src/runtime/context/semantic-navigation-contract.ts +61 -0
  91. package/src/runtime/context/semantic-navigation.ts +9 -61
  92. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +69 -29
  93. package/src/runtime/control-plane/execution/repository-work-attribution.ts +106 -0
  94. package/src/runtime/control-plane/execution/retained-work-resume.ts +203 -0
  95. package/src/runtime/control-plane/execution/work-completion-authority.ts +58 -0
  96. package/src/runtime/control-plane/execution/work-evidence-policy.ts +200 -0
  97. package/src/runtime/control-plane/execution/work-execution-support.ts +386 -0
  98. package/src/runtime/control-plane/execution/work-finalization-service.ts +1913 -0
  99. package/src/runtime/control-plane/execution/work-handle-authority.ts +150 -0
  100. package/src/runtime/control-plane/execution/work-handle-store.ts +1 -1
  101. package/src/runtime/control-plane/execution/work-operation-service.ts +468 -0
  102. package/src/runtime/control-plane/execution/work-path-scope.ts +37 -0
  103. package/src/runtime/control-plane/execution/work-preparation-service.ts +479 -0
  104. package/src/runtime/control-plane/execution/work-process-evidence.ts +129 -0
  105. package/src/runtime/control-plane/execution/work-task-receipt.ts +4 -43
  106. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +13 -3
  107. package/src/runtime/control-plane/execution/work-verification-context.ts +56 -0
  108. package/src/runtime/control-plane/execution/work-verification-service.ts +388 -0
  109. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +5 -1
  110. package/src/runtime/control-plane/facade/controller-round-relay.ts +164 -37
  111. package/src/runtime/control-plane/facade/controller-session-store.ts +183 -24
  112. package/src/runtime/control-plane/facade/goal-workloop-access.ts +15 -1
  113. package/src/runtime/control-plane/facade/goal-workloop.ts +329 -148
  114. package/src/runtime/control-plane/facade/index.ts +2 -0
  115. package/src/runtime/control-plane/facade/plan-contract-store.ts +90 -22
  116. package/src/runtime/control-plane/facade/repository-work-admission.ts +147 -0
  117. package/src/runtime/control-plane/facade/requirement-authority.ts +83 -0
  118. package/src/runtime/control-plane/facade/types.ts +68 -5
  119. package/src/runtime/control-plane/facade/work-contract-store.ts +3 -209
  120. package/src/runtime/control-plane/facade/work-state-machine.ts +250 -0
  121. package/src/runtime/control-plane/global-scheduler/maintenance.ts +14 -7
  122. package/src/runtime/control-plane/global-scheduler/scheduler.ts +5 -1
  123. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +13 -0
  124. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +15 -6
  125. package/src/runtime/control-plane/launcher/launch-reservation-store.ts +54 -5
  126. package/src/runtime/control-plane/launcher/thin-launcher.ts +56 -7
  127. package/src/runtime/control-plane/persistence/requirement-store.ts +0 -12
  128. package/src/runtime/control-plane/repo-actor/registry.ts +4 -2
  129. package/src/runtime/control-plane/routing/route-policy.ts +18 -6
  130. package/src/runtime/control-plane/routing/workspace-admission.ts +70 -0
  131. package/src/runtime/evidence/process-check-execution.ts +18 -0
  132. package/src/runtime/execution/jobs/child-reference.ts +6 -2
  133. package/src/runtime/execution/process-runtime/check-result.ts +53 -18
  134. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +7 -1
  135. package/src/runtime/execution/process-runtime/command-facade.ts +39 -17
  136. package/src/runtime/execution/process-runtime/lightweight-managed.ts +20 -3
  137. package/src/runtime/execution/process-runtime/persisted-check.ts +4 -1
  138. package/src/runtime/execution/process-runtime/runtime.ts +10 -0
  139. package/src/runtime/execution/process-runtime/types.ts +2 -12
  140. package/src/runtime/execution/work-activity.ts +21 -0
  141. package/src/runtime/execution/workers/executor.ts +18 -13
  142. package/src/runtime/gateway/mcp/execution-tools.ts +35 -2930
  143. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +61 -0
  144. package/src/runtime/gateway/mcp/router.ts +2 -2
  145. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +21 -2
  146. package/src/runtime/gateway/mcp/runtime-tools.ts +815 -1015
  147. package/src/runtime/plugins/app-store-connect-adapter.ts +93 -28
  148. package/src/runtime/plugins/browser-adapter.ts +256 -1083
  149. package/src/runtime/plugins/browser-handoff-host.ts +2 -25
  150. package/src/runtime/plugins/browser-handoff.ts +18 -3
  151. package/src/runtime/plugins/browser-macos-bridge.ts +121 -16
  152. package/src/runtime/plugins/browser-manifest-surface.ts +603 -0
  153. package/src/runtime/plugins/browser-native-session-service.ts +222 -0
  154. package/src/runtime/plugins/browser-runtime-contract.ts +2 -0
  155. package/src/runtime/plugins/browser-runtime.ts +10 -0
  156. package/src/runtime/plugins/browser-session-store.ts +121 -0
  157. package/src/runtime/plugins/browser-session-types.ts +98 -0
  158. package/src/runtime/plugins/desktop-operator-registration.ts +4 -4
  159. package/src/runtime/plugins/external-adapter.ts +92 -58
  160. package/src/runtime/plugins/macos-capability-broker.ts +0 -8
  161. package/src/runtime/plugins/store.ts +66 -9
  162. package/src/runtime/plugins/types.ts +3 -1
  163. package/src/runtime/recovery/maintenance-executor.ts +61 -25
  164. package/src/runtime/root/package-connector-service.ts +46 -4
  165. package/src/runtime/root/package-runtime-release.ts +3 -2
  166. package/src/runtime/root/package-runtime-service.ts +48 -3
  167. package/src/runtime/root/release-manifest.ts +137 -16
  168. package/src/runtime/root/release-materialize.ts +109 -35
  169. package/src/runtime/root/types.ts +10 -0
  170. package/src/runtime/standalone-recovery/core.ts +425 -194
  171. package/src/runtime/standalone-recovery/entry.ts +1 -1
  172. package/src/runtime/standalone-recovery/installer.ts +1 -1
  173. package/src/runtime/workflow/schedules/engine.ts +113 -87
  174. package/src/runtime/workflow/schedules/settlement.ts +26 -23
  175. package/src/runtime/workflow/schedules/store.ts +79 -10
@@ -10,7 +10,7 @@
10
10
 
11
11
  import type { RepositoryRecord } from '../../../cli/repositories/types';
12
12
  import { repositoryGitStatus } from '../../../cli/repositories/structured-git';
13
- import { classifyRepositoryCommand, fixedShellWrapperCommand } from '../../../cli/repositories/command-classifier';
13
+ import { classifyRepositoryCommand, fixedShellWrapperCommand, shellSegments, shellWordsPreservingQuotes } from '../../../cli/repositories/command-classifier';
14
14
  import {
15
15
  executeRepositoryCommandAsync,
16
16
  executeRepositoryReadOnlyCommandDirect,
@@ -237,29 +237,40 @@ function repositoryCommandRecoveryLifecycle(command: string | readonly string[])
237
237
  return wrapped ? recoveryLifecycleFromShell(wrapped) : undefined;
238
238
  }
239
239
 
240
- function rawGitCommitRequiresExplicitPathScope(command: string | readonly string[]): boolean {
241
- const normalized = normalizeRepositoryCommand(command);
242
- if (normalized.kind === 'shell') {
243
- return /(?:^|[;&|]\s*)\s*git\s+commit\b/i.test(normalized.shellCommand ?? '');
244
- }
245
- const executable = commandBasename(normalized.executable ?? '');
246
- const args = [...(normalized.args ?? [])];
247
- if (executable !== 'git') {
248
- const wrapped = fixedShellWrapperCommand(normalized.value as string[]);
249
- return wrapped ? /(?:^|[;&|]\s*)\s*git\s+commit\b/i.test(wrapped) : false;
250
- }
240
+ function gitCommitWordsRequireExplicitPathScope(words: readonly string[]): boolean | undefined {
241
+ const executable = commandBasename(words[0] ?? '');
242
+ if (executable !== 'git') return undefined;
243
+ const args = words.slice(1);
251
244
  let index = 0;
252
245
  while (index < args.length && args[index]?.startsWith('-')) {
253
246
  const option = args[index]!;
254
247
  if (['-c', '-C', '--git-dir', '--work-tree', '--namespace'].includes(option)) index += 2;
248
+ else if (['--git-dir=', '--work-tree=', '--namespace='].some((prefix) => option.startsWith(prefix))) index += 1;
255
249
  else index += 1;
256
250
  }
257
- if (args[index]?.toLowerCase() !== 'commit') return false;
251
+ if (args[index]?.toLowerCase() !== 'commit') return undefined;
258
252
  const commitArgs = args.slice(index + 1);
259
253
  const pathSeparator = commitArgs.indexOf('--');
260
- const hasExplicitOnly = commitArgs.includes('--only') || commitArgs.includes('-o');
261
254
  const paths = pathSeparator >= 0 ? commitArgs.slice(pathSeparator + 1).filter(Boolean) : [];
262
- return !(hasExplicitOnly && paths.length > 0);
255
+ const widensScope = commitArgs.some((arg) => ['-a', '--all', '-i', '--include', '--interactive', '-p', '--patch'].includes(arg));
256
+ return paths.length === 0 || widensScope;
257
+ }
258
+
259
+ function rawGitCommitRequiresExplicitPathScope(command: string | readonly string[]): boolean {
260
+ const normalized = normalizeRepositoryCommand(command);
261
+ if (normalized.kind === 'argv') {
262
+ const direct = gitCommitWordsRequireExplicitPathScope([
263
+ normalized.executable ?? '',
264
+ ...(normalized.args ?? []),
265
+ ]);
266
+ if (direct !== undefined) return direct;
267
+ const wrapped = fixedShellWrapperCommand(normalized.value as string[]);
268
+ if (!wrapped) return false;
269
+ return shellSegments(wrapped)
270
+ .some((segment) => gitCommitWordsRequireExplicitPathScope(shellWordsPreservingQuotes(segment)) === true);
271
+ }
272
+ return shellSegments(normalized.shellCommand ?? '')
273
+ .some((segment) => gitCommitWordsRequireExplicitPathScope(shellWordsPreservingQuotes(segment)) === true);
263
274
  }
264
275
 
265
276
  function toProcessCommand(command: string | readonly string[], cwd: string): ProcessCommandSpec {
@@ -286,6 +297,7 @@ export function classifyRepositoryCommandRoute(
286
297
  command: string | readonly string[],
287
298
  options: {
288
299
  forceDurable?: boolean;
300
+ workId?: string;
289
301
  defaultBranch?: string;
290
302
  timeoutMs?: number;
291
303
  } = {},
@@ -300,6 +312,12 @@ export function classifyRepositoryCommandRoute(
300
312
  return { route: 'durable', reason: 'force_durable_or_async' };
301
313
  }
302
314
  const classification = classifyRepositoryCommand(command, options.defaultBranch);
315
+ // Work-bound effects must survive Controller/Runtime rotation with one durable
316
+ // Process identity. Read-only observations can remain ephemeral because they
317
+ // have no ambiguous side effect to reconcile after a restart.
318
+ if (options.workId?.trim() && classification.risk !== 'readonly') {
319
+ return { route: 'process_managed', reason: 'work_attributed_effectful_durable' };
320
+ }
303
321
  const text = Array.isArray(command) ? command.join(' ') : String(command);
304
322
  // Classification chooses scheduling/receipt granularity only; it does not
305
323
  // decide whether a scoped repository command is allowed to run. Known remote
@@ -354,8 +372,7 @@ export async function executeRepositoryCommandViaProcessRuntime(
354
372
  assertRepositoryCommandNoPluginExecutionBypass(input.command);
355
373
  const decision = classifyRepositoryCommandRoute(input.command, {
356
374
  forceDurable: input.forceDurable,
357
- // Work identity is continuity/audit metadata only. It must not change the
358
- // execution lane of an otherwise ordinary local repository command.
375
+ workId: input.workId,
359
376
  defaultBranch: input.repository.defaultBranch,
360
377
  timeoutMs: input.timeoutMs,
361
378
  });
@@ -454,6 +471,11 @@ export async function executeRepositoryCommandViaProcessRuntime(
454
471
  deferStart: deferLongPreparation,
455
472
  reuseActiveEquivalent: deferLongPreparation,
456
473
  onCompleted: (result) => {
474
+ // A terminal child exit is sufficient to close the Process, but not to
475
+ // advance Work mutation authority when post-execution repository
476
+ // evidence is unavailable. Finalization must keep failing closed until
477
+ // a later authoritative repository inspection proves the new HEAD.
478
+ if (result.evidenceError) return;
457
479
  settleWorkHandleExpectedHeadAfterRepositoryCommand({
458
480
  controllerHome: input.controllerHome,
459
481
  repository: input.repository,
@@ -52,6 +52,8 @@ interface LightweightExecutionResult {
52
52
  cancelled?: boolean;
53
53
  stdout?: string;
54
54
  stderr?: string;
55
+ /** Child execution completed, but repository mutation evidence could not be proven. */
56
+ evidenceError?: { code: string; message: string };
55
57
  }
56
58
 
57
59
  interface LightweightExitObservation {
@@ -714,16 +716,31 @@ export async function startLightweightRepositoryCommand(
714
716
  timedOut: result.timedOut,
715
717
  cancelled: result.cancelled,
716
718
  };
717
- persistRunningReceipt(entry, true);
719
+ // Child exit is the authoritative Process terminal boundary. Repository
720
+ // post-processing may continue, but it must never keep this Process
721
+ // logically running or make process_get/process_wait lose the exit fact.
722
+ entry.result = result;
723
+ entry.finishedAtMs = Date.now();
724
+ input.execution.signal?.removeEventListener('abort', onCallerAbort);
725
+ persistTerminalReceipt(input.controllerHome, entry);
718
726
  },
719
727
  },
720
728
  ).then((result) => {
729
+ // Enriched execution evidence may replace the compact child-exit result in
730
+ // memory. The already-persisted terminal Process identity remains stable.
731
+ // Work lifecycle settlement belongs here, after repository evidence has
732
+ // either been proven or explicitly marked unavailable.
721
733
  entry.result = result;
722
- entry.finishedAtMs = Date.now();
723
- input.execution.signal?.removeEventListener('abort', onCallerAbort);
724
734
  input.onCompleted?.(result);
725
735
  persistTerminalReceipt(input.controllerHome, entry);
726
736
  return result;
737
+ }).catch((error) => {
738
+ // Once child exit has been observed, later bookkeeping failure cannot revoke
739
+ // the terminal Process fact or trigger command replay. Expected snapshot
740
+ // degradation is represented by RepositoryCommandExecution.evidenceError;
741
+ // this catch is the final fail-safe for any later post-processing exception.
742
+ if (entry.exitObservation && entry.result) return entry.result;
743
+ throw error;
727
744
  });
728
745
  entry.promise = input.deferStart
729
746
  ? new Promise<LightweightExecutionResult>((resolve, reject) => {
@@ -276,7 +276,10 @@ export async function runPersistedCheckViaProcessRuntime(
276
276
  checkFingerprint,
277
277
  '--result-receipt',
278
278
  checkResultReceiptPath,
279
- ...(verificationSnapshot ? ['--cleanup-root', verificationSnapshot.root] : []),
279
+ ...(verificationSnapshot ? [
280
+ '--isolated-controller-home', verificationSnapshot.isolatedControllerHome,
281
+ '--cleanup-root', verificationSnapshot.root,
282
+ ] : []),
280
283
  ];
281
284
  const invocation = resolvePersistedCheckProcessInvocation(cliTarget, checkArgs);
282
285
  let handle;
@@ -694,6 +694,16 @@ export function resolveProcessRunnerEntryPath(
694
694
  const configured = env.FORGE_PROCESS_RUNNER_ENTRY?.trim();
695
695
  if (configured && existsSync(configured)) return configured;
696
696
 
697
+ // macOS launchd runs the immutable Runtime through a stable mirrored
698
+ // executable path for TCC identity. In that mode process.execPath is not a
699
+ // sibling of the active release sidecars, so use the release authority path
700
+ // exported by the launch contract before any executable/source fallback.
701
+ const releaseRoot = env.FORGE_RELEASE_PATH?.trim();
702
+ if (releaseRoot) {
703
+ const releaseRunner = join(releaseRoot, 'process-runner.js');
704
+ if (existsSync(releaseRunner)) return releaseRunner;
705
+ }
706
+
697
707
  // Compiled daemon/Gateway entrypoints and process-runner.js are siblings in
698
708
  // every immutable release. Prefer that closed release surface before looking
699
709
  // at source paths recorded only for diagnostics and development fallback.
@@ -140,18 +140,8 @@ export interface ProcessRequestBinding {
140
140
  createdAt: string;
141
141
  }
142
142
 
143
- /** Content-bound Check identity used only to index/reuse Process Runtime evidence. */
144
- export interface ProcessCheckExecutionIdentity {
145
- schemaVersion: 1;
146
- checkId: string;
147
- cacheKey: string;
148
- revision: string;
149
- definitionDigest: string;
150
- environmentFingerprint: string;
151
- timeoutMs: number;
152
- reuseScope: 'repository' | 'checkout';
153
- scopeKey: string;
154
- }
143
+ export type { ProcessCheckExecutionIdentity } from '../../evidence/process-check-execution';
144
+ import type { ProcessCheckExecutionIdentity } from '../../evidence/process-check-execution';
155
145
 
156
146
  /** Persistent semantic Check binding to one physical Process record. */
157
147
  export interface ProcessCheckExecutionBinding {
@@ -0,0 +1,21 @@
1
+ import { listActiveExecutionJobs } from './jobs/store';
2
+ import type { ExecutionJob } from './jobs/types';
3
+ import { listRecoverableProcessRecords } from './process-runtime/store';
4
+ import { isManagedProcessActive } from './process-runtime/types';
5
+
6
+ export function executionJobBelongsToWork(job: ExecutionJob, workId: string): boolean {
7
+ if (job.resourceClaims.some((claim) => claim.workId === workId)) return true;
8
+ const payload = job.payload as Record<string, unknown>;
9
+ const args = payload.arguments && typeof payload.arguments === 'object' && !Array.isArray(payload.arguments)
10
+ ? payload.arguments as Record<string, unknown>
11
+ : undefined;
12
+ return [payload.workId, payload.work_id, args?.workId, args?.work_id].some((value) => value === workId);
13
+ }
14
+
15
+ export function workHasActiveExecution(controllerHome: string, repoId: string, workId: string): boolean {
16
+ const activeProcess = listRecoverableProcessRecords(controllerHome, repoId)
17
+ .some((record) => record.workId === workId && isManagedProcessActive(record));
18
+ if (activeProcess) return true;
19
+ return listActiveExecutionJobs(controllerHome, repoId)
20
+ .some((job) => executionJobBelongsToWork(job, workId));
21
+ }
@@ -28,6 +28,8 @@ import { writeExecutionArtifact } from '../../evidence/artifact-store';
28
28
  import { existsSync } from 'fs';
29
29
  import { isAbsolute, relative, resolve, sep } from 'path';
30
30
  import { isAssistantPluginError } from '../../plugins/errors';
31
+ import { executeWork, validateWork } from '../../control-plane/execution/work-operation-service';
32
+ import { finalizeWork } from '../../control-plane/execution/work-finalization-service';
31
33
 
32
34
 
33
35
  function childReferenceFromLocalJob(
@@ -524,23 +526,26 @@ export async function executeExecutionJob(controllerHome: string, job: Execution
524
526
  toolset: job.payload.toolset === 'core' || job.payload.toolset === 'full' ? job.payload.toolset : 'advanced',
525
527
  toolsetLocked: true,
526
528
  };
527
- // Public Work mutations are admitted by the Gateway, then executed exactly
528
- // once here. Calling the implementation directly avoids recursive Job
529
- // creation while preserving the existing Work validation/authorization path.
529
+ // Durable Work mutations execute through control-plane application services.
530
+ // The Worker must never import MCP transport to obtain Work semantics.
530
531
  const durableWorkOperation = job.payload.operation === 'work_execute'
531
532
  || job.payload.operation === 'work_validate'
532
533
  || job.payload.operation === 'work_finalize';
533
- // Load the Work implementation only for Work Jobs. A static import here
534
- // creates an initialization cycle through repository command and Local Bridge
535
- // modules, changing unrelated execution behavior.
536
534
  const toolArguments = { ...(job.payload.arguments ?? {}) };
537
- const executionResult = durableWorkOperation
538
- ? await import('../../gateway/mcp/execution-tools').then(({ callExecutionTool }) =>
539
- callExecutionTool(runtimeContext, job.payload.operation, {
540
- ...toolArguments,
541
- __from_durable_worker: true,
542
- __execution_job_id: job.jobId,
543
- }))
535
+ const workArguments = {
536
+ ...toolArguments,
537
+ __from_durable_worker: true,
538
+ __execution_job_id: job.jobId,
539
+ };
540
+ const executionRecord = job.payload.operation === 'work_execute'
541
+ ? await executeWork(runtimeContext, workArguments)
542
+ : job.payload.operation === 'work_validate'
543
+ ? await validateWork(runtimeContext, workArguments)
544
+ : job.payload.operation === 'work_finalize'
545
+ ? await finalizeWork(runtimeContext, workArguments)
546
+ : undefined;
547
+ const executionResult: CallToolResult | undefined = durableWorkOperation && executionRecord
548
+ ? { structuredContent: executionRecord, content: [] }
544
549
  : undefined;
545
550
  const runtimeResult = executionResult ?? (policy.profile === 'controller'
546
551
  ? await callRuntimeTool(runtimeContext, job.payload.operation, toolArguments)