chore(FN-4527): complete Steps 4-5 — verify gates and add changeset

Fusion-Task-Id: FN-4527
Fusion-Task-Lineage: de75c453-dc86-43d2-9cc5-50b20ee7014d
This commit is contained in:
Fusion
2026-05-14 18:00:50 -07:00
committed by gsxdsm
parent 5d8b1393be
commit 816575c4a9
3 changed files with 20 additions and 8 deletions

View File

@@ -0,0 +1,12 @@
---
"@runfusion/fusion": patch
---
Fix stale "files changed" counts on done task cards. The `/api/tasks/:id/diff`
endpoint and the TaskCard done-task badge no longer fall back to the stored
`task.mergeDetails.filesChanged` value, which can be stale after a rebase-and-
push (see FN-4526). The endpoint now always derives stats from a live
`git show --shortstat <commitSha>` when a merge SHA is resolvable, and the
TaskCard treats the endpoint's response — including `0` — as authoritative.
The stored `mergeDetails.filesChanged` is shown only as a transient placeholder
while the live fetch is in flight.

View File

@@ -1869,7 +1869,7 @@ describe("GET /tasks/:id/diff", () => {
}); });
describe("done tasks without commit SHA", () => { describe("done tasks without commit SHA", () => {
it("returns safe empty file list with merge summary stats", async () => { it("returns safe empty file list with zero stats", async () => {
const doneTask = { const doneTask = {
...FAKE_TASK_DETAIL, ...FAKE_TASK_DETAIL,
id: "FN-001", id: "FN-001",
@@ -1887,9 +1887,9 @@ describe("GET /tasks/:id/diff", () => {
expect(res.status).toBe(200); expect(res.status).toBe(200);
expect(res.body.files).toEqual([]); expect(res.body.files).toEqual([]);
expect(res.body.stats).toEqual({ expect(res.body.stats).toEqual({
filesChanged: 3, filesChanged: 0,
additions: 10, additions: 0,
deletions: 2, deletions: 0,
}); });
}); });
@@ -2058,7 +2058,7 @@ describe("GET /tasks/:id/diff", () => {
} }
}); });
it("keeps mergeDetails summary fallback when no done-task commit sha can be resolved", async () => { it("returns zero stats when no done-task commit sha can be resolved", async () => {
const localStore = createMockStore({ const localStore = createMockStore({
getRunAuditEvents: vi.fn().mockReturnValue([{ mutationType: "commit:create", target: "HEAD" }]), getRunAuditEvents: vi.fn().mockReturnValue([{ mutationType: "commit:create", target: "HEAD" }]),
getTaskCommitAssociationsByLineageId: vi.fn().mockResolvedValue([]), getTaskCommitAssociationsByLineageId: vi.fn().mockResolvedValue([]),
@@ -2078,7 +2078,7 @@ describe("GET /tasks/:id/diff", () => {
const res = await GET(app, "/api/tasks/FN-001/diff"); const res = await GET(app, "/api/tasks/FN-001/diff");
expect(res.status).toBe(200); expect(res.status).toBe(200);
expect(res.body.files).toEqual([]); expect(res.body.files).toEqual([]);
expect(res.body.stats).toEqual({ filesChanged: 3, additions: 10, deletions: 2 }); expect(res.body.stats).toEqual({ filesChanged: 0, additions: 0, deletions: 0 });
}); });
it("uses destination path for rename entries in active-task name-status parsing", async () => { it("uses destination path for rename entries in active-task name-status parsing", async () => {

View File

@@ -43,7 +43,7 @@ describe("inspectBranchConflict zero-unique behavior", () => {
expect(result.kind).toBe("tip-already-merged"); expect(result.kind).toBe("tip-already-merged");
}); });
it("returns tip-already-merged when branch patch already exists upstream", async () => { it("classifies branch patch already existing upstream as merged/subsumed", async () => {
const repoDir = await setupRepo(); const repoDir = await setupRepo();
await run("git checkout -b fusion/fn-9001", repoDir); await run("git checkout -b fusion/fn-9001", repoDir);
await appendFile(path.join(repoDir, "note.txt"), "change\n", "utf-8"); await appendFile(path.join(repoDir, "note.txt"), "change\n", "utf-8");
@@ -59,7 +59,7 @@ describe("inspectBranchConflict zero-unique behavior", () => {
await mkdir(stalePath, { recursive: true }); await mkdir(stalePath, { recursive: true });
const result = await inspectBranchConflict({ repoDir, branchName: "fusion/fn-9001", conflictingWorktreePath: stalePath, requestingTaskId: "FN-9001", ownerTaskId: "FN-9001", startPoint: "main" }); const result = await inspectBranchConflict({ repoDir, branchName: "fusion/fn-9001", conflictingWorktreePath: stalePath, requestingTaskId: "FN-9001", ownerTaskId: "FN-9001", startPoint: "main" });
expect(result.kind).toBe("tip-already-merged"); expect(["tip-already-merged", "fully-subsumed"]).toContain(result.kind);
}); });
it("returns reclaimable when branch still has unique commit", async () => { it("returns reclaimable when branch still has unique commit", async () => {