Files
fusion/packages/engine/src/worktree-base-refresh.ts
gsxdsm cb57093d03 refactor: domain folder layout (types, API, core, engine) (#2398)
## Summary

Wave 17 organizes Fusion into **domain folders** (stacks on #2397).

### Layout
- **core/types/** — board, task, agents, settings, merge, workflow,
mesh, …
- **core/src/** — agents, ai, async-stores, workflows, tasks, config,
db, …
- **dashboard/app/api/** — client, tasks, agents, git, missions,
planning, …
- **engine/src/** — agents, auth, execution, merge, missions, overseer,
worktree, …

Root keepers retained for large entrypoints (`store.ts`, `executor.ts`,
`merger.ts`, …).

Public barrels (`@fusion/core`, `@fusion/engine`, `app/api.ts` → legacy)
stay stable.

## Test plan
- [x] `@fusion/core` typecheck
- [x] `@fusion/engine` typecheck (pre-existing playwright-core noise
only)
- [ ] CI merge gate

**Stack:** #2394 → #2397 → **this PR**
2026-08-03 00:20:53 -07:00

159 lines
7.1 KiB
TypeScript

import { exec } from "node:child_process";
import { promisify } from "node:util";
import type { Settings, Task, TaskStore } from "@fusion/core";
import { resolveIntegrationBranch } from "./merge/integration-branch.js";
import type { GitAuditInput } from "./util/run-audit.js";
const execAsync = promisify(exec);
const GIT_TIMEOUT_MS = 120_000;
export type WorktreeBaseRefreshKind =
| "up-to-date"
| "reset-to-base"
| "rebased"
| "stale-base-conflict"
| "dirty-worktree"
| "base-unresolvable"
| "worktrunk-refresh-unsupported"
| "git-refresh-failed"
| "base-persistence-failed-compensated"
| "base-reconciliation-required";
export interface WorktreeBaseRefreshResult {
kind: WorktreeBaseRefreshKind;
executionSafe: boolean;
integrationBranch?: string;
baseSha?: string;
originalHead?: string;
observedHead?: string;
durableBaseSha?: string | null;
detail?: string;
}
export interface RefreshReusedWorktreeBaseInput {
task: Task;
rootDir: string;
worktreePath: string;
store: TaskStore;
settings: Partial<Settings>;
/** Audit is intentionally structural so acquisition can use its run-scoped auditor. */
audit?: { git?: (event: GitAuditInput) => Promise<void> };
logger?: { warn: (message: string) => void };
}
function quote(value: string): string {
return `'${value.replace(/'/g, "'\\''")}'`;
}
async function git(cwd: string, command: string): Promise<string> {
const { stdout } = await execAsync(`git ${command}`, { cwd, encoding: "utf8", timeout: GIT_TIMEOUT_MS, maxBuffer: 10 * 1024 * 1024 });
return stdout.trim();
}
async function isAncestor(cwd: string, ancestor: string, descendant: string): Promise<boolean> {
try {
await git(cwd, `merge-base --is-ancestor ${quote(ancestor)} ${quote(descendant)}`);
return true;
} catch {
return false;
}
}
async function compensate(cwd: string, originalHead: string): Promise<boolean> {
try {
await git(cwd, `rebase --abort`);
} catch {
// No rebase may be in progress; reset below is the proof-bearing restoration.
}
try {
await git(cwd, `reset --hard ${quote(originalHead)}`);
const [head, dirty] = await Promise.all([git(cwd, "rev-parse HEAD"), git(cwd, "status --porcelain")]);
return head === originalHead && !dirty;
} catch {
return false;
}
}
/*
FNXC:WorktreeBaseRefresh 2026-08-01-16:04:
Planning deliberately creates an isolated task worktree, but execution may begin after dependencies land.
Only refresh-enabled execution reuse reaches this primitive: planning, review, gates, and merge retain their
existing worktrees. The primitive refuses dirty, unresolved, worktrunk, conflict, git, persistence, and
reconciliation-unknown states with typed non-execution outcomes. It resolves integration C1 independently
on every reuse and aligns durable baseCommitSha to C1; a rebased own-commit HEAD C2 is preserved and is never
stored as the baseline. Git mutations are compensated back to C0 when baseline persistence fails; a later
process can statelessly reconcile C0 versus clean C1/C2 from durable metadata and git proof alone.
*/
export async function refreshReusedWorktreeBase(input: RefreshReusedWorktreeBaseInput): Promise<WorktreeBaseRefreshResult> {
const { task, rootDir, worktreePath, store, settings, audit, logger } = input;
if (settings.worktrunk?.enabled) {
return { kind: "worktrunk-refresh-unsupported", executionSafe: false, durableBaseSha: task.baseCommitSha ?? null };
}
let integrationBranch: string;
let baseSha: string;
let originalHead: string;
let dirty: string;
try {
integrationBranch = await resolveIntegrationBranch(rootDir, settings, { logger: logger ?? console });
[baseSha, originalHead, dirty] = await Promise.all([
git(rootDir, `rev-parse --verify ${quote(`${integrationBranch}^{commit}`)}`),
git(worktreePath, "rev-parse HEAD"),
git(worktreePath, "status --porcelain"),
]);
} catch (error) {
return { kind: "base-unresolvable", executionSafe: false, durableBaseSha: task.baseCommitSha ?? null, detail: error instanceof Error ? error.message : String(error) };
}
const common = { integrationBranch, baseSha, originalHead, durableBaseSha: task.baseCommitSha ?? null };
if (dirty) return { kind: "dirty-worktree", executionSafe: false, observedHead: originalHead, ...common };
const baselineMatches = task.baseCommitSha === baseSha;
const headCurrent = await isAncestor(worktreePath, baseSha, originalHead);
if (headCurrent) {
if (baselineMatches) return { kind: "up-to-date", executionSafe: true, observedHead: originalHead, ...common };
try {
await store.updateTask(task.id, { baseCommitSha: baseSha });
await audit?.git?.({ type: "worktree:base-refresh-reconciled", target: worktreePath, metadata: { taskId: task.id, outcome: "reconciled" } });
return { kind: "up-to-date", executionSafe: true, observedHead: originalHead, ...common };
} catch (error) {
return { kind: "base-reconciliation-required", executionSafe: false, observedHead: originalHead, ...common, detail: error instanceof Error ? error.message : String(error) };
}
}
let mergeBase: string;
try {
mergeBase = await git(worktreePath, `merge-base HEAD ${quote(baseSha)}`);
} catch (error) {
return { kind: "git-refresh-failed", executionSafe: false, observedHead: originalHead, ...common, detail: error instanceof Error ? error.message : String(error) };
}
let kind: "reset-to-base" | "rebased";
try {
const ownCommits = await git(worktreePath, `rev-list --count ${quote(`${mergeBase}..HEAD`)}`);
if (Number(ownCommits) === 0) {
await git(worktreePath, `reset --hard ${quote(baseSha)}`);
kind = "reset-to-base";
} else {
try {
await git(worktreePath, `rebase ${quote(baseSha)}`);
kind = "rebased";
} catch (error) {
const restored = await compensate(worktreePath, originalHead);
return { kind: restored ? "stale-base-conflict" : "base-reconciliation-required", executionSafe: false, observedHead: originalHead, ...common, detail: error instanceof Error ? error.message : String(error) };
}
}
} catch (error) {
return { kind: "git-refresh-failed", executionSafe: false, observedHead: originalHead, ...common, detail: error instanceof Error ? error.message : String(error) };
}
const observedHead = await git(worktreePath, "rev-parse HEAD").catch(() => undefined);
try {
await store.updateTask(task.id, { baseCommitSha: baseSha });
await audit?.git?.({ type: "worktree:base-refreshed", target: worktreePath, metadata: { taskId: task.id, outcome: kind } });
return { kind, executionSafe: true, observedHead, ...common };
} catch (error) {
const restored = await compensate(worktreePath, originalHead);
await audit?.git?.({ type: restored ? "worktree:base-refresh-persistence-failed-compensated" : "worktree:base-refresh-blocked", target: worktreePath, metadata: { taskId: task.id, outcome: restored ? "compensated" : "reconciliation-required" } }).catch(() => undefined);
return { kind: restored ? "base-persistence-failed-compensated" : "base-reconciliation-required", executionSafe: false, observedHead, ...common, detail: error instanceof Error ? error.message : String(error) };
}
}