feat(FN-5175): thread soft-delete audit context and add reconciliation scri

Adds a reconcile script to recover leaked soft-deleted tasks, threads delete audit context through all callers, and records soft-delete audit events with archive column tracking across core/engine/cli/dashboard, with reliability backstop tests covering caller alignment.

Fusion-Task-Id: FN-5175
This commit is contained in:
Fusion (runfusion.ai)
2026-05-20 02:38:58 -07:00
committed by gsxdsm
parent 6ecef44ab3
commit c9fd41ef26
17 changed files with 656 additions and 21 deletions

View File

@@ -1154,7 +1154,12 @@ describe("project-aware task command behavior", () => {
await runTaskDelete("FN-123", true, "demo-project");
expect(getTask).toHaveBeenCalledWith("FN-123");
expect(deleteTask).toHaveBeenCalledWith("FN-123");
expect(deleteTask).toHaveBeenCalledWith("FN-123", expect.objectContaining({
auditContext: expect.objectContaining({
agentId: "cli",
runId: expect.stringMatching(/^synthetic-cli-delete-FN-123-/),
}),
}));
});
it("runTaskComment, runTaskComments, and runTaskSteer use resolved project store", async () => {
@@ -2224,7 +2229,12 @@ describe("runTaskDelete", () => {
expect(mockGetTask).toHaveBeenCalledWith("FN-001");
expect(mockRlQuestion).not.toHaveBeenCalled();
expect(mockDeleteTask).toHaveBeenCalledOnce();
expect(mockDeleteTask).toHaveBeenCalledWith("FN-001");
expect(mockDeleteTask).toHaveBeenCalledWith("FN-001", expect.objectContaining({
auditContext: expect.objectContaining({
agentId: "cli",
runId: expect.stringMatching(/^synthetic-cli-delete-FN-001-/),
}),
}));
const successLine = logSpy.mock.calls.find(
(call) => typeof call[0] === "string" && call[0].includes("✓ Deleted"),
@@ -2242,7 +2252,12 @@ describe("runTaskDelete", () => {
expect(mockRlQuestion).toHaveBeenCalledWith("Are you sure you want to delete FN-001? [y/N] ");
expect(mockRlClose).toHaveBeenCalled();
expect(mockDeleteTask).toHaveBeenCalledOnce();
expect(mockDeleteTask).toHaveBeenCalledWith("FN-001");
expect(mockDeleteTask).toHaveBeenCalledWith("FN-001", expect.objectContaining({
auditContext: expect.objectContaining({
agentId: "cli",
runId: expect.stringMatching(/^synthetic-cli-delete-FN-001-/),
}),
}));
const successLine = logSpy.mock.calls.find(
(call) => typeof call[0] === "string" && call[0].includes("✓ Deleted"),

View File

@@ -1207,7 +1207,12 @@ export async function runTaskDelete(id: string, force?: boolean, projectName?: s
}
try {
await store.deleteTask(id);
await store.deleteTask(id, {
auditContext: {
agentId: "cli",
runId: `synthetic-cli-delete-${id}-${Date.now()}`,
},
});
console.log();
console.log(` ✓ Deleted ${id}`);
console.log();

View File

@@ -1179,7 +1179,12 @@ export default function kbExtension(pi: ExtensionAPI) {
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
const store = await getStore(ctx.cwd);
const task = await store.deleteTask(params.id);
const task = await store.deleteTask(params.id, {
auditContext: {
agentId: "pi-extension",
runId: `synthetic-pi-delete-${params.id}-${Date.now()}`,
},
});
return {
content: [{ type: "text", text: `Deleted ${task.id}` }],