feat(dashboard): extended integration-branch status in Git Manager
Repository Status panel now answers "what is the actual state of my
project root vs the integration branch?" so operators can be sure of
the picture even when the Merge Advance Notice banner is dismissed.
GET /api/git/status accepts ?extended=1 and returns additional optional
fields:
- integrationBranch + integrationBranchSource (settings|origin-head|fallback)
- integrationTipSha / originIntegrationTipSha
- aheadOfIntegration / behindIntegration (HEAD vs local integration tip)
- aheadOfOriginIntegration / behindOriginIntegration (local tip vs origin)
- dirtyDetails {staged, modified, untracked, conflicted, sample}
- indexStaleVsHead (surfaces the FN-INDEX-DESYNC scenario)
- stashCount
- recentMergeAdvances: up to 5 merge:integration-ref-advance events
joined with merge:auto-sync outcomes; needsAction flag flips when
auto-sync didn't successfully bring this worktree forward
GitManagerModal renders all of it:
- Existing cards get sub-text: branch shows "not on <integration>",
Working Tree shows staged/modified/untracked/conflicted breakdown
- Second row: Integration branch + source, HEAD-vs-integration,
local-vs-origin, stash count
- Yellow warning panel when indexStaleVsHead surfaces the merger's
stale-index situation with a recovery hint
- Recent integration-branch advances list, color-coded by needsAction,
shows the per-advance auto-sync outcome so operators can audit
even after dismissing the banner
All fetchGitStatus calls in GitManagerModal switched to extended:true.
Other callers unaffected — extra fields are optional.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2578,6 +2578,34 @@ export interface GitStatus {
|
||||
isDirty: boolean;
|
||||
ahead: number;
|
||||
behind: number;
|
||||
// Returned only when `?extended=1` is passed to GET /api/git/status.
|
||||
headSha?: string;
|
||||
integrationBranch?: string;
|
||||
integrationBranchSource?: "settings" | "origin-head" | "fallback";
|
||||
isOnIntegrationBranch?: boolean;
|
||||
integrationTipSha?: string | null;
|
||||
originIntegrationTipSha?: string | null;
|
||||
aheadOfIntegration?: number;
|
||||
behindIntegration?: number;
|
||||
aheadOfOriginIntegration?: number;
|
||||
behindOriginIntegration?: number;
|
||||
dirtyDetails?: {
|
||||
staged: number;
|
||||
modified: number;
|
||||
untracked: number;
|
||||
conflicted: number;
|
||||
sample: string[];
|
||||
};
|
||||
indexStaleVsHead?: boolean;
|
||||
stashCount?: number;
|
||||
recentMergeAdvances?: Array<{
|
||||
taskId: string;
|
||||
fromSha: string | null;
|
||||
toSha: string;
|
||||
advancedAt: string;
|
||||
autoSyncOutcome?: string;
|
||||
needsAction: boolean;
|
||||
}>;
|
||||
}
|
||||
|
||||
/** Git commit info */
|
||||
@@ -2630,9 +2658,15 @@ export interface GitPushResult {
|
||||
message: string;
|
||||
}
|
||||
|
||||
/** Fetch current git status */
|
||||
export function fetchGitStatus(projectId?: string): Promise<GitStatus> {
|
||||
return api<GitStatus>(withProjectId("/git/status", projectId));
|
||||
/** Fetch current git status. Pass `extended` to also get integration-branch
|
||||
* resolution, ahead/behind vs both local and origin integration tip, dirty
|
||||
* breakdown, stash count, index-stale detection, and recent merge-advance
|
||||
* audit events for the project-root worktree. */
|
||||
export function fetchGitStatus(projectId?: string, opts?: { extended?: boolean }): Promise<GitStatus> {
|
||||
const base = withProjectId("/git/status", projectId);
|
||||
if (!opts?.extended) return api<GitStatus>(base);
|
||||
const sep = base.includes("?") ? "&" : "?";
|
||||
return api<GitStatus>(`${base}${sep}extended=1`);
|
||||
}
|
||||
|
||||
/** Fetch recent commits */
|
||||
|
||||
Reference in New Issue
Block a user