FN-5780: inherit source GitHub tracking when refining tasks

Preserve GitHub linking intent when creating refinement tasks from completed work.

- Set refinement `githubTracking` from the source task state during `refineTask`
- Opt out refinements when the source task is unlinked or explicitly disabled
- Carry forward `enabled` and optional `repoOverride` for linked sources without copying source issue metadata
- Add store-level tests covering unlinked, disabled, linked, and issue-only source tracking states
- Document refinement GitHub tracking inheritance behavior in task-management docs
- Add a patch changeset for `@runfusion/fusion`

Files changed:
 .changeset/fn-5780-refine-github-tracking.md  |  5 ++
 docs/task-management.md                       |  1 +
 packages/core/src/__tests__/store-ops.test.ts | 71 +++++++++++++++++++++++++++
 packages/core/src/store.ts                    | 12 +++++
 4 files changed, 89 insertions(+)

Fusion-Task-Id: FN-5780
Fusion-Task-Lineage: 88c1ce01-6efd-48c1-bbdd-704a821055d7
This commit is contained in:
gsxdsm
2026-05-31 09:41:04 -07:00
parent 9f29935525
commit 793da2c749
4 changed files with 89 additions and 0 deletions

View File

@@ -511,6 +511,77 @@ describe("TaskStore", () => {
expect(refined.status).toBeUndefined();
});
it("opts out github tracking when source has no githubTracking", async () => {
const task = await store.createTask({ description: "Original task" });
await store.moveTask(task.id, "todo");
await store.moveTask(task.id, "in-progress");
await store.moveTask(task.id, "in-review");
await store.moveTask(task.id, "done");
const refined = await store.refineTask(task.id, "Need improvements");
expect(refined.githubTracking?.enabled).toBe(false);
});
it("keeps github tracking disabled when source is explicitly disabled", async () => {
const task = await store.createTask({ description: "Original task" });
await store.updateGithubTracking(task.id, { enabled: false });
await store.moveTask(task.id, "todo");
await store.moveTask(task.id, "in-progress");
await store.moveTask(task.id, "in-review");
await store.moveTask(task.id, "done");
const refined = await store.refineTask(task.id, "Need improvements");
expect(refined.githubTracking?.enabled).toBe(false);
});
it("inherits enabled and repoOverride without copying linked issue", async () => {
const task = await store.createTask({ description: "Original task" });
await store.updateGithubTracking(task.id, {
enabled: true,
repoOverride: "owner/repo",
issue: {
number: 12,
url: "https://github.com/owner/repo/issues/12",
title: "Tracked",
state: "open",
updatedAt: new Date().toISOString(),
},
});
await store.moveTask(task.id, "todo");
await store.moveTask(task.id, "in-progress");
await store.moveTask(task.id, "in-review");
await store.moveTask(task.id, "done");
const refined = await store.refineTask(task.id, "Need improvements");
expect(refined.githubTracking).toEqual({ enabled: true, repoOverride: "owner/repo" });
expect(refined.githubTracking?.issue).toBeUndefined();
});
it("treats linked issue without enabled flag as linked for refinement", async () => {
const task = await store.createTask({ description: "Original task" });
await store.updateGithubTracking(task.id, {
issue: {
number: 99,
url: "https://github.com/owner/repo/issues/99",
title: "Tracked",
state: "open",
updatedAt: new Date().toISOString(),
},
});
await store.moveTask(task.id, "todo");
await store.moveTask(task.id, "in-progress");
await store.moveTask(task.id, "in-review");
await store.moveTask(task.id, "done");
const refined = await store.refineTask(task.id, "Need improvements");
expect(refined.githubTracking?.enabled).toBe(true);
expect(refined.githubTracking?.issue).toBeUndefined();
});
it("creates PROMPT.md for the refinement", async () => {
const task = await store.createTask({ description: "Original task" });
await store.moveTask(task.id, "todo");

View File

@@ -4210,6 +4210,17 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
const removed = extractTaskIdTokens(`Refinement: ${sourceLabel}`).filter((token) => token !== newId.toUpperCase());
storeLog.log(`[title-id-drift] normalized title for ${newId}: removed=[${removed.join(",")}]`);
}
const sourceGithubLinked = sourceTask.githubTracking?.enabled === true || Boolean(sourceTask.githubTracking?.issue);
// FN-5780: refinement should inherit source linking intent so unlinked tasks stay opted out from auto-create defaults.
const refinementGithubTracking = sourceGithubLinked
? {
enabled: true,
...(sourceTask.githubTracking?.repoOverride
? { repoOverride: sourceTask.githubTracking.repoOverride }
: {}),
}
: { enabled: false };
const newTask: Task = {
id: newId,
lineageId: generateTaskLineageId(),
@@ -4220,6 +4231,7 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
dependencies: [id],
sourceType: "task_refine",
sourceParentTaskId: id,
githubTracking: refinementGithubTracking,
steps: [],
currentStep: 0,
log: [{ timestamp: now, action: `Created as refinement of ${id}` }],