Files
fusion/packages/engine/src/__tests__/reliability-interactions/self-defeating-dep-reconcile.test.ts
Fusion (runfusion.ai) 10455b7992 feat(FN-4898): reconcile task title-id drift in storage migrations
Adds a title-id drift detection and reconciliation system to prevent duplicate tasks when task titles change after initial creation. Core adds the `task-title-id-drift.ts` module with `markTitleIdStable` and `resolveTitleIdDrift` logic, wired through `db.ts`, `store.ts`, and `archive-db.ts`, with co

Fusion-Task-Id: FN-4898
2026-05-17 09:51:10 -07:00

88 lines
2.8 KiB
TypeScript

import { afterEach, describe, expect, it } from "vitest";
import { hasGit, makeReliabilityFixture } from "./_helpers.js";
const describeIfGit = hasGit ? describe : describe.skip;
describeIfGit("reliability interactions: self-defeating dep reconciliation", () => {
const fixtures: Array<Awaited<ReturnType<typeof makeReliabilityFixture>>> = [];
afterEach(async () => {
while (fixtures.length) await fixtures.pop()!.cleanup();
});
it("reconciles pre-existing offender in todo and records audit + task log", async () => {
const fx = await makeReliabilityFixture({
taskId: "FN-4891-A",
task: {
column: "todo",
title: "safe title",
dependencies: ["FN-100", "FN-200"],
} as any,
});
fixtures.push(fx);
fx.store.getDatabase().prepare("UPDATE tasks SET title = ? WHERE id = ?").run("Finalize FN-100: close loop", fx.task.id);
const recovered = await fx.manager.reconcileSelfDefeatingDependencies();
expect(recovered).toBe(1);
const updated = await fx.store.getTask(fx.task.id);
expect(updated?.dependencies).toEqual(["FN-200"]);
expect(
updated?.log.some((entry) => JSON.stringify(entry).includes("Auto-reconciled self-defeating dependency")),
).toBe(true);
const events = fx.store.getRunAuditEvents({
taskId: fx.task.id,
domain: "database",
mutationType: "task:auto-reconciled-self-defeating-dep",
});
expect(events).toHaveLength(1);
expect(events[0]?.target).toBe(fx.task.id);
expect(events[0]?.metadata).toMatchObject({
matchedVerb: "finalize",
operandTaskId: "FN-100",
originalDependencies: ["FN-100", "FN-200"],
nextDependencies: ["FN-200"],
});
});
it("does not reconcile non-operational test title", async () => {
const fx = await makeReliabilityFixture({
taskId: "FN-4891-B",
task: {
column: "todo",
title: "Test FN-100",
dependencies: ["FN-100"],
} as any,
});
fixtures.push(fx);
const recovered = await fx.manager.reconcileSelfDefeatingDependencies();
expect(recovered).toBe(0);
const updated = await fx.store.getTask(fx.task.id);
expect(updated?.dependencies).toEqual(["FN-100"]);
});
it("does not touch in-progress offenders", async () => {
const fx = await makeReliabilityFixture({
taskId: "FN-4891-C",
task: {
column: "in-progress",
title: "safe title",
dependencies: ["FN-100"],
} as any,
});
fixtures.push(fx);
fx.store.getDatabase().prepare("UPDATE tasks SET title = ? WHERE id = ?").run("Finalize FN-100", fx.task.id);
const recovered = await fx.manager.reconcileSelfDefeatingDependencies();
expect(recovered).toBe(0);
const updated = await fx.store.getTask(fx.task.id);
expect(updated?.dependencies).toEqual(["FN-100"]);
});
});