fix(FN-4485): address orphan rescue review regressions
Fusion-Task-Id: FN-4485 Fusion-Task-Lineage: 034088dc-ebc4-4e12-8314-39419d41b23f
This commit is contained in:
@@ -96,9 +96,7 @@ describe("self-healing orphan branch rescue", () => {
|
||||
const result = await manager.cleanupOrphanedBranches();
|
||||
|
||||
expect(result).toBe(0);
|
||||
expect(store.updateTask).toHaveBeenCalledWith("FN-4470", expect.objectContaining({
|
||||
metadata: expect.objectContaining({ orphanRescueAcknowledged: true }),
|
||||
}));
|
||||
expect(store.updateTask).not.toHaveBeenCalled();
|
||||
expect(store.createTask).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -126,6 +126,7 @@ function createMockStore(overrides: Record<string, unknown> = {}): TaskStore & E
|
||||
archiveTaskAndCleanup: vi.fn().mockResolvedValue({} as Task),
|
||||
walCheckpoint: vi.fn().mockReturnValue({ busy: 0, log: 5, checkpointed: 5 }),
|
||||
listTasks: vi.fn().mockResolvedValue([]),
|
||||
createTask: vi.fn().mockResolvedValue({ id: "FN-RESCUE", lineageId: "lin-rescue" }),
|
||||
getRootDir: vi.fn().mockReturnValue("/tmp/test-project"),
|
||||
clearStaleExecutionStartBranchReferences: vi.fn().mockReturnValue([]),
|
||||
...overrides,
|
||||
@@ -1304,11 +1305,11 @@ describe("SelfHealingManager", () => {
|
||||
|
||||
expect(result).toBe(2);
|
||||
expect(mockedExecSync).toHaveBeenCalledWith(
|
||||
expect.stringContaining('git branch -d "fusion/fn-001"'),
|
||||
expect.stringContaining("git branch -d 'fusion/fn-001'"),
|
||||
expect.objectContaining({ cwd: "/tmp/test-project" }),
|
||||
);
|
||||
expect(mockedExecSync).toHaveBeenCalledWith(
|
||||
expect.stringContaining('git branch -d "fusion/fn-002"'),
|
||||
expect.stringContaining("git branch -d 'fusion/fn-002'"),
|
||||
expect.objectContaining({ cwd: "/tmp/test-project" }),
|
||||
);
|
||||
});
|
||||
@@ -1337,7 +1338,7 @@ describe("SelfHealingManager", () => {
|
||||
|
||||
const result = await manager.cleanupOrphanedBranches();
|
||||
|
||||
expect(result).toBe(0);
|
||||
expect(result).toBe(1);
|
||||
});
|
||||
|
||||
it("returns 0 when scanOrphanedBranches throws", async () => {
|
||||
|
||||
@@ -358,6 +358,7 @@ export class SelfHealingManager {
|
||||
// ── Per-task deadlock recovery cooldown ─────────────────────────────
|
||||
private deadlockRecoveryCooldown: Map<string, number> = new Map();
|
||||
private mergeStarvationDrops: Map<string, number> = new Map();
|
||||
private orphanArchivedAcknowledged = new Set<string>();
|
||||
|
||||
constructor(
|
||||
private store: TaskStore,
|
||||
@@ -3864,14 +3865,14 @@ export class SelfHealingManager {
|
||||
|
||||
private async inspectOrphanedBranch(branch: string): Promise<OrphanBranchInspection | null> {
|
||||
try {
|
||||
const tipSha = String(execSync(`git rev-parse --verify "${branch}"`, {
|
||||
const tipSha = String(execSync(`git rev-parse --verify ${shellQuote(branch)}`, {
|
||||
cwd: this.options.rootDir,
|
||||
encoding: "utf-8",
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
})).trim();
|
||||
if (!tipSha) return null;
|
||||
|
||||
const uniqueCommitCount = Number.parseInt(String(execSync(`git rev-list --count "${branch}" --not main`, {
|
||||
const uniqueCommitCount = Number.parseInt(String(execSync(`git rev-list --count ${shellQuote(branch)} --not ${shellQuote("main")}`, {
|
||||
cwd: this.options.rootDir,
|
||||
encoding: "utf-8",
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
@@ -3879,7 +3880,7 @@ export class SelfHealingManager {
|
||||
|
||||
let uniqueCommitSubjects: string[] = [];
|
||||
if (uniqueCommitCount > 0) {
|
||||
const subjectOutput = String(execSync(`git log --format=%s --max-count=${ORPHAN_RESCUE_SUBJECT_CAP} "${branch}" --not main`, {
|
||||
const subjectOutput = String(execSync(`git log --format=%s --max-count=${ORPHAN_RESCUE_SUBJECT_CAP} ${shellQuote(branch)} --not ${shellQuote("main")}`, {
|
||||
cwd: this.options.rootDir,
|
||||
encoding: "utf-8",
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
@@ -3923,7 +3924,7 @@ export class SelfHealingManager {
|
||||
|
||||
if (inspection.uniqueCommitCount <= 0) {
|
||||
try {
|
||||
execSync(`git branch -d "${branch}"`, {
|
||||
execSync(`git branch -d ${shellQuote(branch)}`, {
|
||||
cwd: this.options.rootDir,
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
});
|
||||
@@ -3958,16 +3959,8 @@ export class SelfHealingManager {
|
||||
const existingBranchTask = allTasks.find((task) => task.branch === branch);
|
||||
|
||||
if (matchedTask?.column === "archived") {
|
||||
const metadata = (matchedTask.metadata && typeof matchedTask.metadata === "object")
|
||||
? matchedTask.metadata as Record<string, unknown>
|
||||
: {};
|
||||
if (metadata.orphanRescueAcknowledged !== true) {
|
||||
await this.store.updateTask(matchedTask.id, {
|
||||
metadata: {
|
||||
...metadata,
|
||||
orphanRescueAcknowledged: true,
|
||||
},
|
||||
});
|
||||
if (!this.orphanArchivedAcknowledged.has(matchedTask.id)) {
|
||||
this.orphanArchivedAcknowledged.add(matchedTask.id);
|
||||
log.warn(`[recovery] orphan-rescue-archived-skip ${matchedTask.id} branch=${branch} tip=${inspection.tipSha.slice(0, 12)} unique=${inspection.uniqueCommitCount}`);
|
||||
}
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user