feat(FN-4523): complete Step 6 — docs, audit type, and verification fixes

Fusion-Task-Id: FN-4523
Fusion-Task-Lineage: c7a02071-f911-4c42-a174-cdcf2453fd23
This commit is contained in:
Fusion
2026-05-14 13:58:37 -07:00
committed by gsxdsm
parent cc18c4f780
commit 211fd0817e
7 changed files with 21 additions and 15 deletions

View File

@@ -127,7 +127,7 @@ describeIfGit("SelfHealingManager recoverAlreadyMergedReviewTasks (real git)", (
expect(task.mergeDetails?.mergeConfirmed).toBe(true);
expect(existsSync(worktreePath)).toBe(false);
expect(git(repo, "git worktree list")).not.toContain(worktreePath);
expect((store as any).recordRunAuditEvent).toHaveBeenCalledTimes(1);
expect((store as any).recordRunAuditEvent).toHaveBeenCalledTimes(2);
expect((store as any).recordRunAuditEvent).toHaveBeenCalledWith(
expect.objectContaining({
domain: "database",

View File

@@ -106,7 +106,7 @@ describe("self-healing completion fan-out", () => {
});
it("removes worktree from hint and is idempotent when missing", async () => {
existsSyncMock.mockImplementation((p: string) => p === "/wt/fn-b");
(existsSyncMock as any).mockImplementation((p: string) => p === "/wt/fn-b");
const blocker = makeTask("FN-B", { column: "done", branch: "fusion/fn-b" });
const store = createStore([blocker]);
const mgr = new SelfHealingManager(store, { rootDir: "/repo" });
@@ -127,8 +127,8 @@ describe("self-healing completion fan-out", () => {
});
it("derives worktree from worktree list and skips branch delete when unique commits exist", async () => {
existsSyncMock.mockImplementation((p: string) => String(p).includes("/wt/fn-c"));
uniqueCommitsMock.mockResolvedValue({ commits: [{ sha: "abc", subject: "x" }], mainRef: "main", degraded: false });
(existsSyncMock as any).mockImplementation((p: string) => String(p).includes("/wt/fn-c"));
uniqueCommitsMock.mockResolvedValue({ commits: [{ sha: "abc", subject: "x" }] as any, mainRef: "main", degraded: false });
execMock.mockImplementation((cmd: string, _opts: unknown, cb: (err: unknown, stdout: string, stderr: string) => void) => {
cb(null, "", "");
});

View File

@@ -3847,7 +3847,7 @@ describe("SelfHealingManager", () => {
const recovered = await managerWithRecovery.recoverAlreadyMergedReviewTasks();
expect(recovered).toBe(1);
expect(recordRunAuditEvent).toHaveBeenCalledTimes(1);
expect(recordRunAuditEvent).toHaveBeenCalledTimes(2);
expect(recordRunAuditEvent).toHaveBeenCalledWith(
expect.objectContaining({
domain: "database",
@@ -3923,7 +3923,7 @@ describe("SelfHealingManager", () => {
expect(recovered).toBe(1);
expect(storeWithAudit.moveTask).toHaveBeenCalledWith("FN-audit-throw", "done");
expect(recordRunAuditEvent).toHaveBeenCalledTimes(1);
expect(recordRunAuditEvent).toHaveBeenCalledTimes(2);
managerWithRecovery.stop();
});

View File

@@ -99,6 +99,7 @@ export type DatabaseMutationType =
| "task:unpause"
| "task:dependency:add"
| "task:auto-recover-already-merged"
| "task:auto-recover-completion-fanout"
| "document:write"
| "workflow-step:result"
| "agent:create:requested"

View File

@@ -1680,21 +1680,21 @@ export class SelfHealingManager {
*
* @returns Number of tasks unblocked
*/
private async findWorktreePathForBranch(branchName: string): Promise<string | null> {
private async findWorktreePathForBranch(branchName: string): Promise<string | undefined> {
try {
const { stdout } = await execAsync("git worktree list --porcelain", {
cwd: this.options.rootDir,
timeout: 30_000,
});
const lines = stdout.split("\n");
let currentWorktree: string | null = null;
let currentBranch: string | null = null;
let currentWorktree: string | undefined;
let currentBranch: string | undefined;
for (const rawLine of lines) {
const line = rawLine.trim();
if (!line) {
if (currentWorktree && currentBranch === branchName) return currentWorktree;
currentWorktree = null;
currentBranch = null;
currentWorktree = undefined;
currentBranch = undefined;
continue;
}
if (line.startsWith("worktree ")) {
@@ -1706,11 +1706,11 @@ export class SelfHealingManager {
}
}
if (currentWorktree && currentBranch === branchName) return currentWorktree;
return null;
return undefined;
} catch (err: unknown) {
const errorMessage = err instanceof Error ? err.message : String(err);
log.warn(`[self-healing] reconcileCompletedTask: failed to read worktree list for ${branchName}: ${errorMessage}`);
return null;
return undefined;
}
}
@@ -1829,7 +1829,7 @@ export class SelfHealingManager {
runId: generateSyntheticRunId("self-heal", taskId),
agentId: "self-healing",
taskId,
taskLineageId: task?.lineageId,
taskLineageId: task?.lineageId ?? undefined,
phase: "completion-fanout",
});
await auditor.database({