feat(FN-5064): merge fusion/fn-5064
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import { mkdir } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { activeSessionRegistry } from "../../active-session-registry.js";
|
||||
import { git, makeReliabilityFixture } from "./_helpers.js";
|
||||
|
||||
describe("reliability interactions: meta archive guard composition", () => {
|
||||
it("FN-5064: meta-archive guards refuse to destroy substantive work across composition with branch, executor retry, and active session", async () => {
|
||||
const fixture = await makeReliabilityFixture({
|
||||
taskId: "FN-5064-COMPOSITION",
|
||||
task: { id: "FN-5064-COMPOSITION", title: "anchor", column: "todo" },
|
||||
settings: {
|
||||
pausedScopeDecayMs: 1,
|
||||
metaTaskStallAutoCloseMs: 2 * 60 * 60_000,
|
||||
metaTaskActiveExecutionGraceMs: 30 * 60_000,
|
||||
boardStallSweepWindowMs: 2 * 60 * 60_000,
|
||||
},
|
||||
});
|
||||
|
||||
const target = await fixture.store.createTask({
|
||||
id: "FN-5064-TARGET-DONE",
|
||||
title: "target done",
|
||||
description: "target",
|
||||
column: "done",
|
||||
steps: [],
|
||||
} as any);
|
||||
|
||||
const mkMeta = async (id: string, title: string) => fixture.store.createTask({
|
||||
id,
|
||||
title,
|
||||
description: `meta guard test for ${target.id}`,
|
||||
sourceParentTaskId: target.id,
|
||||
column: "todo",
|
||||
noCommitsExpected: true,
|
||||
steps: [],
|
||||
} as any);
|
||||
|
||||
const branchMeta = await mkMeta("FN-5064-META-BRANCH", `Recover ${target.id}`);
|
||||
const recentMeta = await mkMeta("FN-5064-META-RECENT", `Recover ${target.id}`);
|
||||
const retryMeta = await mkMeta("FN-5064-META-RETRY", `Recover ${target.id}`);
|
||||
const activeWorktreePath = join(fixture.rootDir, "meta-active-worktree");
|
||||
await mkdir(activeWorktreePath, { recursive: true });
|
||||
const activeMeta = await fixture.store.createTask({
|
||||
id: "FN-5064-META-ACTIVE",
|
||||
title: `Recover ${target.id}`,
|
||||
description: `meta guard test for ${target.id}`,
|
||||
sourceParentTaskId: target.id,
|
||||
column: "todo",
|
||||
noCommitsExpected: true,
|
||||
steps: [],
|
||||
worktree: activeWorktreePath,
|
||||
} as any);
|
||||
await fixture.store.updateTask(activeMeta.id, { worktree: activeWorktreePath } as any);
|
||||
const controlMeta = await mkMeta("FN-5064-META-CONTROL", `Recover ${target.id}`);
|
||||
|
||||
await fixture.store.updateTask(recentMeta.id, {
|
||||
column: "in-progress",
|
||||
executionStartedAt: new Date(Date.now() - 5 * 60_000).toISOString(),
|
||||
} as any);
|
||||
await fixture.store.updateTask(retryMeta.id, { taskDoneRetryCount: 1 } as any);
|
||||
activeSessionRegistry.registerPath(activeWorktreePath, { taskId: activeMeta.id, kind: "executor", ownerKey: activeMeta.id });
|
||||
|
||||
const branchName = `fusion/${branchMeta.id.toLowerCase()}`;
|
||||
git(fixture.rootDir, `git checkout -b ${branchName}`);
|
||||
git(fixture.rootDir, "git commit --allow-empty -m \"feat: ahead branch meta\"");
|
||||
git(fixture.rootDir, "git checkout main");
|
||||
await fixture.store.updateTask(branchMeta.id, { branch: branchName } as any);
|
||||
|
||||
try {
|
||||
await (fixture.manager as any).runMaintenance();
|
||||
|
||||
const byId = new Map((await fixture.store.listTasks({ includeArchived: true })).map((task) => [task.id, task]));
|
||||
expect(byId.get(branchMeta.id)?.column).not.toBe("archived");
|
||||
expect(byId.get(recentMeta.id)?.column).not.toBe("archived");
|
||||
expect(byId.get(retryMeta.id)?.column).not.toBe("archived");
|
||||
expect(byId.get(activeMeta.id)?.column).not.toBe("archived");
|
||||
expect(byId.get(controlMeta.id)?.column).toBe("archived");
|
||||
|
||||
const events = fixture.store.getRunAuditEvents({ limit: 400 });
|
||||
const skipped = events.filter((event) => event.mutationType === "task:auto-archive-meta-resolved-skipped");
|
||||
const archived = events.filter((event) => event.mutationType === "task:auto-archived-meta-resolved");
|
||||
expect(skipped).toHaveLength(4);
|
||||
const blockedByByTask = new Map(skipped.map((event) => [(event.metadata as any)?.taskId, (event.metadata as any)?.blockedBy ?? []]));
|
||||
expect(blockedByByTask.get(branchMeta.id)).toEqual(expect.arrayContaining(["branch-has-unique-commits"]));
|
||||
expect(blockedByByTask.get(recentMeta.id)).toEqual(expect.arrayContaining(["recent-executor-activity"]));
|
||||
expect(blockedByByTask.get(retryMeta.id)).toEqual(expect.arrayContaining(["task-done-retry-pending"]));
|
||||
expect(blockedByByTask.get(activeMeta.id)).toEqual(expect.arrayContaining(["active-session"]));
|
||||
expect(archived).toHaveLength(1);
|
||||
expect((archived[0]?.metadata as any)?.taskId).toBe(controlMeta.id);
|
||||
} finally {
|
||||
activeSessionRegistry.unregisterPath(activeWorktreePath);
|
||||
await fixture.cleanup();
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user