fix(dashboard): clear "needs action" on recent advances after manual sync
The Git Manager's recent integration-advances list derived `needsAction` purely from the original `merge:auto-sync` audit-event outcome, so it kept showing "(N need action)" after the operator clicked "Sync working tree" or fixed up the worktree by hand. `collectRecentMergeAdvances` now also checks whether each advance's `toSha` is reachable from HEAD — if it is, the worktree already contains that advance and `needsAction` is false regardless of what the audit trail recorded. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
9
.changeset/fix-recent-merge-advances-needsaction.md
Normal file
9
.changeset/fix-recent-merge-advances-needsaction.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
"@fusion/dashboard": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix(dashboard): clear `needs action` on recent integration-branch advances after manual sync
|
||||||
|
|
||||||
|
The Git Manager's "Recent integration-branch advances" list derived `needsAction` purely from the original `merge:auto-sync` audit-event outcome. When the operator clicked "Sync working tree" — or fixed up the worktree by hand — the worktree caught up to the integration tip, but the list kept showing "(N need action)" because the historical audit events still recorded the original failure/disabled state.
|
||||||
|
|
||||||
|
`collectRecentMergeAdvances` now also checks whether each advance's `toSha` is reachable from the current HEAD. If it is, the worktree already contains that advance and `needsAction` is false regardless of what the audit trail recorded.
|
||||||
@@ -586,6 +586,7 @@ async function collectRecentMergeAdvances(
|
|||||||
}) => RunAuditEvent[];
|
}) => RunAuditEvent[];
|
||||||
},
|
},
|
||||||
worktreePath: string,
|
worktreePath: string,
|
||||||
|
headSha: string | undefined,
|
||||||
): Promise<ExtendedGitStatus["recentMergeAdvances"]> {
|
): Promise<ExtendedGitStatus["recentMergeAdvances"]> {
|
||||||
if (typeof scopedStore.getRunAuditEvents !== "function") return [];
|
if (typeof scopedStore.getRunAuditEvents !== "function") return [];
|
||||||
const advances = scopedStore.getRunAuditEvents({
|
const advances = scopedStore.getRunAuditEvents({
|
||||||
@@ -642,13 +643,34 @@ async function collectRecentMergeAdvances(
|
|||||||
const autoSyncOutcome =
|
const autoSyncOutcome =
|
||||||
autoSyncByAdvance.get(pairKey(tid, md.toSha))
|
autoSyncByAdvance.get(pairKey(tid, md.toSha))
|
||||||
?? autoSyncByTaskFallback.get(tid);
|
?? autoSyncByTaskFallback.get(tid);
|
||||||
|
// The worktree may already contain `toSha` — either because auto-sync
|
||||||
|
// succeeded, the operator manually ran "Sync working tree" / pulled, or
|
||||||
|
// they checked out a later commit by hand. In all those cases there's
|
||||||
|
// nothing left to do, regardless of what the original auto-sync audit
|
||||||
|
// event recorded. Treat reachability from HEAD as authoritative.
|
||||||
|
let alreadyInHead = false;
|
||||||
|
if (headSha) {
|
||||||
|
if (headSha === md.toSha) {
|
||||||
|
alreadyInHead = true;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
await runGitCommand(["merge-base", "--is-ancestor", md.toSha, headSha], worktreePath, 5_000);
|
||||||
|
alreadyInHead = true;
|
||||||
|
} catch {
|
||||||
|
alreadyInHead = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const needsAction = alreadyInHead
|
||||||
|
? false
|
||||||
|
: (autoSyncOutcome === undefined || !successOutcomes.has(autoSyncOutcome));
|
||||||
out.push({
|
out.push({
|
||||||
taskId: tid,
|
taskId: tid,
|
||||||
fromSha: typeof md.fromSha === "string" ? md.fromSha : null,
|
fromSha: typeof md.fromSha === "string" ? md.fromSha : null,
|
||||||
toSha: md.toSha,
|
toSha: md.toSha,
|
||||||
advancedAt: ev.timestamp,
|
advancedAt: ev.timestamp,
|
||||||
autoSyncOutcome,
|
autoSyncOutcome,
|
||||||
needsAction: autoSyncOutcome === undefined || !successOutcomes.has(autoSyncOutcome),
|
needsAction,
|
||||||
});
|
});
|
||||||
if (out.length >= 5) break;
|
if (out.length >= 5) break;
|
||||||
}
|
}
|
||||||
@@ -731,6 +753,7 @@ export async function computeExtendedGitStatus(rootDir: string, scopedStore: Tas
|
|||||||
getRunAuditEvents?: (filters: { taskId?: string; domain?: "database" | "git" | "filesystem" | "sandbox"; mutationType?: string; limit?: number }) => RunAuditEvent[];
|
getRunAuditEvents?: (filters: { taskId?: string; domain?: "database" | "git" | "filesystem" | "sandbox"; mutationType?: string; limit?: number }) => RunAuditEvent[];
|
||||||
},
|
},
|
||||||
rootDir,
|
rootDir,
|
||||||
|
headSha,
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user