Files
fusion/packages/engine/src/executor/prepare-graph-node-execution.ts
Fusion Agent bfaa0f42da FN-158: enforce workspace multi-repo merge boundaries
Support workspace tasks across a shared root with scope-driven repositories and verifiable merge gates.

- Route task work through one workspace directory with per-repository acquisition and isolation.
- Add sandbox session policies and per-repository verification command handling.
- Enforce required pre-merge checks and honest merge blocking for workspace changes.
- Update workspace, workflow, and sandbox documentation and release metadata.

Files changed:
 .changeset/fn-158-workspace-single-root.md         |   7 +
 docs/sandbox.md                                    |   6 +-
 docs/workflow-steps.md                             |   6 +-
 docs/workspaces.md                                 |  12 +-
 .../core/src/__tests__/legacy-adoption.test.ts     |  15 +-
 .../src/__tests__/required-pre-merge-steps.test.ts |  25 ++++
 .../core/src/__tests__/store-bypass-review.test.ts |  24 ++-
 packages/core/src/__tests__/task-merge.test.ts     |  24 +++
 .../core/src/__tests__/worktree-layout.test.ts     |  29 ++++
 packages/core/src/db/legacy-adoption.ts            |  20 ++-
 packages/core/src/index.gate.ts                    |   4 +
 packages/core/src/index.ts                         |   4 +
 .../core/src/merge/required-pre-merge-steps.ts     |  26 ++++
 packages/core/src/merge/task-merge.ts              |  35 ++++-
 packages/core/src/store.ts                         |  59 ++++++--
 packages/core/src/task-store/lifecycle-ops.ts      |   1 +
 packages/core/src/task-store/merge-queue-ops.ts    |   5 +-
 packages/core/src/task-store/moves.ts              |  13 +-
 packages/core/src/task-store/task-artifacts-ops.ts |  11 +-
 packages/core/src/tasks/worktree-layout.ts         |  43 +++++-
 packages/core/src/types/workflow/workflow-steps.ts |   3 +-
 .../executor-workspace-session-cwd.test.ts         |  42 ++++--
 .../src/__tests__/node-worktree-isolation.test.ts  |  13 +-
 .../src/__tests__/pi-create-fn-agent.test.ts       |  16 ++
 .../engine/src/__tests__/project-engine.test.ts    |  20 ++-
 .../src/__tests__/reviewer-workspace.test.ts       |  30 +++-
 .../src/__tests__/run-verification-command.test.ts |  90 +++++++++++-
 .../__tests__/sandbox/sandbox-exec-policy.test.ts  |  16 +-
 .../src/__tests__/sandbox/session-policy.test.ts   |  45 ++++++
 .../__tests__/workspace-add-repo-midflight.test.ts |   9 ++
 .../engine/src/__tests__/workspace-e2e.test.ts     |  13 +-
 .../workspace-root-worktree-routing.test.ts        |  18 +--
 packages/engine/src/agent-tools.ts                 |  15 +-
 packages/engine/src/agents/agent-runtime.ts        |  19 +++
 .../engine/src/agents/agent-session-helpers.ts     |  15 ++
 packages/engine/src/execution/hold-release.ts      |  29 ++++
 .../engine/src/execution/run-verification-tool.ts  | 114 ++++++++++++++-
 .../create-authoritative-workflow-seams.ts         |  20 +--
 packages/engine/src/executor/deps-bags.ts          |   5 +-
 .../executor/ensure-graph-custom-node-worktree.ts  |  24 ++-
 .../executor/ensure-task-worktree-for-planning.ts  |  36 ++---
 .../engine/src/executor/execute-workflow-step.ts   |   4 +-
 .../src/executor/finalize-already-reviewed-task.ts |   6 +-
 .../src/executor/prepare-graph-node-execution.ts   |  14 +-
 .../engine/src/executor/run-graph-custom-node.ts   | 161 +++++++++++++--------
 packages/engine/src/executor/run-implementation.ts | 117 ++++++++++++---
 packages/engine/src/merge/merger-ai.ts             |  16 +-
 packages/engine/src/merger.ts                      |  20 ++-
 packages/engine/src/pi.ts                          | 150 ++++++++++++++++---
 packages/engine/src/project-engine.ts              |  10 +-
 packages/engine/src/runtimes/in-process-runtime.ts |   4 +-
 packages/engine/src/sandbox/bubblewrap-backend.ts  |  55 ++++++-
 packages/engine/src/sandbox/bubblewrap-policy.ts   |  10 +-
 packages/engine/src/sandbox/index.ts               |   1 +
 .../engine/src/sandbox/sandbox-exec-backend.ts     |  45 +++++-
 packages/engine/src/sandbox/sandbox-exec-policy.ts |  18 ++-
 packages/engine/src/sandbox/session-policy.ts      |  41 ++++++
 packages/engine/src/sandbox/types.ts               |  11 ++
 packages/engine/src/self-healing.ts                |   1 +
 packages/engine/src/triage.ts                      |  10 ++
 .../engine/src/worktree/worktree-acquisition.ts    |  53 ++++---
 61 files changed, 1393 insertions(+), 315 deletions(-)

Fusion-Task-Id: FN-158

Fusion-Task-Lineage: ba57f5a2-fa69-4210-8ea7-3d124be3deb2

Co-authored-by: Fusion <noreply@runfusion.ai>
2026-08-23 01:37:58 +00:00

67 lines
2.9 KiB
TypeScript

/**
* FNXC:CodeOrganization 2026-08-03-11:45:
* prepareGraphNodeExecution peeled from TaskExecutor (U4).
*
* FNXC:WorktreeBaseRefresh 2026-08-01-16:32:
* An existing code-node checkout must remain attached so it takes the guarded reuse/refresh path.
* Only a missing recorded path is cleared to permit fresh creation.
*
* FNXC:WorkflowExecution 2026-06-29-15:28 / 09:50:
* Graph declares worktree requirement; this adapter fulfills it. Stale paths are reacquired before write-capable nodes.
*
* FNXC:WorktreeBaseRefresh 2026-08-01-16:04:
* Code nodes reacquire with refresh enabled; planning/review keep C0 checkout.
*/
import { existsSync } from "node:fs";
import type { Settings, TaskDetail, TaskStore, WorkflowIrNode, WorkspaceConfig } from "@fusion/core";
import type { WorkflowNodePreparationRequirement } from "../workflows/workflow-graph-executor.js";
import type { EngineRunContext } from "../util/run-audit.js";
import { workflowNodeRequiresWorktree } from "../workflows/workflow-node-execution-needs.js";
import { resolveWorkspaceConfigOnce } from "./workspace-config-resolver.js";
export type PrepareGraphNodeExecutionDeps = {
store: TaskStore;
rootDir: string;
workspaceConfigOwner: object;
getWorkspaceConfig: () => WorkspaceConfig | null | undefined;
setWorkspaceConfig: (config: WorkspaceConfig | null) => void;
getRunContextFor: (taskId: string) => EngineRunContext | undefined;
ensureGraphCustomNodeWorktree: (
task: TaskDetail,
settings: Settings,
nodeId: string,
refreshStaleBase?: boolean,
) => Promise<TaskDetail>;
};
export async function prepareGraphNodeExecution(
deps: PrepareGraphNodeExecutionDeps,
node: WorkflowIrNode,
nodeTask: TaskDetail,
settings: Settings,
requirement: WorkflowNodePreparationRequirement,
): Promise<void> {
if (!requirement.requiresWorktree) return;
const live = await deps.store.getTask(nodeTask.id);
const workspaceConfig = await resolveWorkspaceConfigOnce(deps);
const writeCapable = workflowNodeRequiresWorktree(node);
// FNXC:WorkspaceBoundary 2026-08-22-23:05: workspace read-only graph
// nodes plan against the root before a repository scope exists; acquisition
// belongs exclusively to write-capable nodes after confirmation.
if (workspaceConfig && !writeCapable) return;
const executionCodeNode = node.kind === "code";
if (live.worktree && existsSync(live.worktree) && !executionCodeNode) return;
const taskForAcquisition = live.worktree && !existsSync(live.worktree)
? ({ ...live, worktree: undefined, sessionFile: undefined } as TaskDetail)
: live;
if (live.worktree) {
await deps.store.logEntry(
live.id,
`Workflow node '${node.id}' assigned worktree is missing — reacquiring before node execution`,
live.worktree,
deps.getRunContextFor(live.id),
);
}
await deps.ensureGraphCustomNodeWorktree(taskForAcquisition, settings, node.id, executionCodeNode);
}