feat(FN-5419): add stash conflict modal gating and smart pull routing for m
Implements a pull-based merge workflow by wiring the merger pull helpers from the engine, extending the git pull and stash routes, and aligning the `MergeAdvanceNotice` and `StashConflictModal` components to gate dismissal on stash drop. The `run-audit` module is updated with pull mutation documenta Fusion-Task-Id: FN-5419 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> Fusion-Task-Id: FN-5419
This commit is contained in:
@@ -88,16 +88,51 @@ describe("run-audit provisioning mutation types", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("accepts smart-pull git mutation types", async () => {
|
||||
it("accepts pull:fast-forward metadata shape", async () => {
|
||||
const store = new AuditStoreStub();
|
||||
const auditor = createRunAuditor(store as unknown as TaskStore, { runId: "r1", agentId: "a1", taskId: "FN-5358" });
|
||||
const auditor = createRunAuditor(store as unknown as TaskStore, { runId: "r1", agentId: "a1", taskId: "FN-5419" });
|
||||
|
||||
const types: GitMutationType[] = ["pull:fast-forward", "stash:pop-conflict", "push:origin"];
|
||||
for (const type of types) {
|
||||
await auditor.git({ type, target: "feature/worktree", metadata: { taskId: "FN-5358" } });
|
||||
}
|
||||
const type: GitMutationType = "pull:fast-forward";
|
||||
await auditor.git({
|
||||
type,
|
||||
target: "/repo/.worktrees/integration",
|
||||
metadata: {
|
||||
taskId: "FN-5419",
|
||||
worktreePath: "/repo/.worktrees/integration",
|
||||
integrationBranch: "main",
|
||||
remote: "origin",
|
||||
fromSha: "1111111",
|
||||
toSha: "2222222",
|
||||
durationMs: 12,
|
||||
succeeded: true,
|
||||
behind: 0,
|
||||
ahead: 0,
|
||||
},
|
||||
});
|
||||
|
||||
expect(store.events.map((event) => event.mutationType)).toEqual(types);
|
||||
expect(store.events[0]?.mutationType).toBe(type);
|
||||
});
|
||||
|
||||
it("accepts stash:pop-conflict metadata shape", async () => {
|
||||
const store = new AuditStoreStub();
|
||||
const auditor = createRunAuditor(store as unknown as TaskStore, { runId: "r1", agentId: "a1", taskId: "FN-5419" });
|
||||
|
||||
const type: GitMutationType = "stash:pop-conflict";
|
||||
await auditor.git({
|
||||
type,
|
||||
target: "/repo/.worktrees/integration",
|
||||
metadata: {
|
||||
taskId: "FN-5419",
|
||||
worktreePath: "/repo/.worktrees/integration",
|
||||
stashSha: "abc123",
|
||||
stashLabel: "fusion-autostash-FN-5419",
|
||||
conflictedFiles: ["README.md"],
|
||||
autostashOutcome: "conflict-needs-manual",
|
||||
advice: "Resolve conflicts and drop stash when complete",
|
||||
},
|
||||
});
|
||||
|
||||
expect(store.events[0]?.mutationType).toBe(type);
|
||||
});
|
||||
|
||||
it("records merge:scope:auto-widen git events", async () => {
|
||||
|
||||
@@ -33,6 +33,12 @@ export {
|
||||
SquashAuditError,
|
||||
type MergerOptions,
|
||||
type AutostashOrphanRecord,
|
||||
stashUnrelatedRootDirChanges,
|
||||
dropAutostashHandle,
|
||||
restoreUnrelatedRootDirChanges,
|
||||
tryFastForwardFromOrigin,
|
||||
getConflictedFiles,
|
||||
type AutostashHandle,
|
||||
} from "./merger.js";
|
||||
export {
|
||||
resolveIntegrationBranch,
|
||||
@@ -48,6 +54,9 @@ export {
|
||||
type HandoffResult,
|
||||
type MergeIntegrationRootResolution,
|
||||
} from "./merger-integration-worktree.js";
|
||||
export {
|
||||
generateSyntheticRunId,
|
||||
} from "./run-audit.js";
|
||||
export {
|
||||
auditSquashMerge,
|
||||
formatSquashAuditReport,
|
||||
|
||||
@@ -1896,7 +1896,7 @@ function resetMergeWithWarn(rootDir: string, taskId: string, label: string): voi
|
||||
* `rescueShas` lists any race-rescue stashes the autostash captured for
|
||||
* late-dirty paths (concurrent dev edits during the merger run). They are
|
||||
* surfaced separately so the caller can log them to the task feed. */
|
||||
interface AutostashHandle {
|
||||
export interface AutostashHandle {
|
||||
sha: string;
|
||||
label: string;
|
||||
rescueShas?: { sha: string; label: string }[];
|
||||
@@ -2461,7 +2461,7 @@ export const __test__ = {
|
||||
notifyAutostashOrphans,
|
||||
};
|
||||
|
||||
async function stashUnrelatedRootDirChanges(
|
||||
export async function stashUnrelatedRootDirChanges(
|
||||
rootDir: string,
|
||||
taskId: string,
|
||||
): Promise<AutostashHandle | null> {
|
||||
@@ -2688,7 +2688,7 @@ async function isAutostashLive(rootDir: string, sha: string): Promise<boolean> {
|
||||
}
|
||||
}
|
||||
|
||||
async function dropAutostashHandle(
|
||||
export async function dropAutostashHandle(
|
||||
rootDir: string,
|
||||
taskId: string,
|
||||
handle: AutostashHandle,
|
||||
@@ -3384,7 +3384,7 @@ async function restoreRescueAutostashes(
|
||||
return { unresolvedCount };
|
||||
}
|
||||
|
||||
async function restoreUnrelatedRootDirChanges(
|
||||
export async function restoreUnrelatedRootDirChanges(
|
||||
rootDir: string,
|
||||
taskId: string,
|
||||
handle: AutostashHandle,
|
||||
@@ -9926,7 +9926,7 @@ export async function aiMergeTask(
|
||||
* NOTE: This is NOT FN-5350's integration-branch ref advance path. FN-5350
|
||||
* advances refs/heads/<integration-branch> via compare-and-swap `git update-ref`.
|
||||
*/
|
||||
async function tryFastForwardFromOrigin(
|
||||
export async function tryFastForwardFromOrigin(
|
||||
rootDir: string,
|
||||
taskId: string,
|
||||
integrationBranch: string,
|
||||
|
||||
@@ -288,11 +288,14 @@ export type GitMutationType =
|
||||
* taskId?: string;
|
||||
* worktreePath: string;
|
||||
* integrationBranch: string;
|
||||
* remote?: string;
|
||||
* fromSha: string;
|
||||
* toSha: string;
|
||||
* durationMs: number;
|
||||
* succeeded: boolean;
|
||||
* error?: string;
|
||||
* behind?: number;
|
||||
* ahead?: number;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
@@ -324,7 +327,8 @@ export type GitMutationType =
|
||||
* stashSha: string;
|
||||
* stashLabel: string;
|
||||
* conflictedFiles: string[];
|
||||
* advice: string;
|
||||
* autostashOutcome: "conflict-needs-manual" | "failed";
|
||||
* advice?: string;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user