FN-5730: close linked GitHub issues on archived transitions

Ensure task archival updates linked GitHub issue state consistently across source columns.

- close linked issues as not_planned when tasks move to archived from non-done columns
- keep done -> archived using completed close reason
- reopen issues whenever tasks leave done, including done -> archived regression coverage updates

Files changed:
 .../src/__tests__/github-tracking-state.test.ts          | 16 +++++++++++++---
 packages/dashboard/src/github-tracking-state.ts          | 14 ++++++++++----
 2 files changed, 23 insertions(+), 7 deletions(-)

Fusion-Task-Id: FN-5730

Fusion-Task-Lineage: 0ce031be-be8c-49b8-a944-9b269fda4e00
This commit is contained in:
gsxdsm
2026-05-30 13:07:49 -07:00
parent 797ee8df5e
commit 50cdcbf06d
2 changed files with 23 additions and 7 deletions

View File

@@ -89,11 +89,11 @@ describe("decideIssueAction", () => {
});
it("closes on in-review -> archived", () => {
expect(decideIssueAction("in-review", "archived")).toEqual({ action: "close", stateReason: "completed" });
expect(decideIssueAction("in-review", "archived")).toEqual({ action: "close", stateReason: "not_planned" });
});
it.each(["todo", "triage", "in-progress"] as const)("returns null for %s -> archived", (from) => {
expect(decideIssueAction(from, "archived")).toBeNull();
it.each(["todo", "triage", "in-progress"] as const)("returns close not_planned for %s -> archived", (from) => {
expect(decideIssueAction(from, "archived")).toEqual({ action: "close", stateReason: "not_planned" });
});
it.each([
@@ -175,6 +175,16 @@ describe("GitHubTrackingStateService", () => {
expect(store.logEntry).toHaveBeenCalledWith("FN-1", "Closed linked GitHub tracking issue", "owner/repo#42");
});
it("closes triage -> archived with not_planned", async () => {
service.start();
store.emit("task:moved", { task: createTask(), from: "triage", to: "archived" });
await flushAsync();
expect(mockSetIssueState).toHaveBeenCalledWith("owner", "repo", 42, "closed", "not_planned");
expect(store.logEntry).toHaveBeenCalledWith("FN-1", "Closed linked GitHub tracking issue", "owner/repo#42");
});
it("does nothing for non-done transitions", async () => {
service.start();

View File

@@ -28,16 +28,22 @@ interface TaskMovedEvent {
export function decideIssueAction(
from: Column,
to: Column,
): { action: "close" | "reopen"; stateReason: "completed" | "reopened" } | null {
): { action: "close" | "reopen"; stateReason: "completed" | "not_planned" | "reopened" } | null {
if (to === "done" && from !== "done") {
return { action: "close", stateReason: "completed" };
}
if (to === "archived" && (from === "done" || from === "in-review")) {
return { action: "close", stateReason: "completed" };
if (to === "archived") {
if (from === "done") {
return { action: "close", stateReason: "completed" };
}
if (from !== "archived") {
return { action: "close", stateReason: "not_planned" };
}
return null;
}
if (from === "done" && to !== "done" && to !== "archived") {
if (from === "done" && to !== "done") {
return { action: "reopen", stateReason: "reopened" };
}