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[];
|
||||
},
|
||||
worktreePath: string,
|
||||
headSha: string | undefined,
|
||||
): Promise<ExtendedGitStatus["recentMergeAdvances"]> {
|
||||
if (typeof scopedStore.getRunAuditEvents !== "function") return [];
|
||||
const advances = scopedStore.getRunAuditEvents({
|
||||
@@ -642,13 +643,34 @@ async function collectRecentMergeAdvances(
|
||||
const autoSyncOutcome =
|
||||
autoSyncByAdvance.get(pairKey(tid, md.toSha))
|
||||
?? 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({
|
||||
taskId: tid,
|
||||
fromSha: typeof md.fromSha === "string" ? md.fromSha : null,
|
||||
toSha: md.toSha,
|
||||
advancedAt: ev.timestamp,
|
||||
autoSyncOutcome,
|
||||
needsAction: autoSyncOutcome === undefined || !successOutcomes.has(autoSyncOutcome),
|
||||
needsAction,
|
||||
});
|
||||
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[];
|
||||
},
|
||||
rootDir,
|
||||
headSha,
|
||||
),
|
||||
]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user