Address PR review feedback (#1461)

- pr-nodes: forward post-increment entity to deps.respond (responseRounds off-by-one); branch-group entity fallback now keys on task.branchContext.groupId, not task.id
- core: hoist shared autoMergeGateReason into @fusion/core pr-entity.ts; CLI + dashboard import the single definition (R13)
- cli: fn pr create also writes the unified PR entity (ensure → open), keeping legacy prInfo; resolveBranchHeadOid uses argv-safe execFileAsync (injection fix)
- changeset: fn-pr-commands bumped minor → major (breaking CLI removal per AGENTS.md)
- tests: bin.test.ts daemon fixture off reserved port 4040; lazy-loaded-views title 19 → 20 views

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-06-06 00:09:53 -07:00
parent 1922b5efc2
commit 841ff9d838
12 changed files with 196 additions and 49 deletions

View File

@@ -1,5 +1,6 @@
import { describe, expect, it } from "vitest";
import {
autoMergeGateReason,
isPrBacked,
isPrEntityActionable,
isPrEntityActive,
@@ -71,4 +72,19 @@ describe("PR entity predicates", () => {
expect(isPrEntityAutoMergeReady({ ...base, mergeable: "unknown" })).toBe(false);
expect(isPrEntityAutoMergeReady({ ...base, unverified: true })).toBe(false);
});
it("autoMergeGateReason is the single R13-shared status string for both surfaces", () => {
const ready = entity({
autoMerge: true,
reviewDecision: "APPROVED",
checksRollup: "success",
mergeable: "clean",
});
expect(autoMergeGateReason(ready)).toBe("Ready to merge");
expect(autoMergeGateReason({ ...ready, autoMerge: false })).toBe("Auto-merge off");
expect(autoMergeGateReason({ ...ready, mergeable: "conflicting" })).toBe("Blocked: conflict");
expect(autoMergeGateReason({ ...ready, reviewDecision: "CHANGES_REQUESTED" })).toBe("Waiting for approval");
expect(autoMergeGateReason({ ...ready, checksRollup: "pending" })).toBe("Waiting for checks");
expect(autoMergeGateReason({ ...ready, mergeable: "unknown" })).toBe("Waiting for checks");
});
});

View File

@@ -611,6 +611,7 @@ export {
isPrBacked,
isPrEntityActionable,
isPrEntityAutoMergeReady,
autoMergeGateReason,
} from "./pr-entity.js";
export {
findVitestProcessIds,

View File

@@ -63,3 +63,21 @@ export function isPrEntityAutoMergeReady(
if (entity.mergeable !== "clean") return false;
return true;
}
/**
* The live auto-merge gate reason shown next to the toggle (R11). Mirrors the
* auto-merge-ready predicate ordering so every surface (the dashboard route and
* the `fn pr` CLI) reports the same status and never disagrees with what the gate
* will actually do. Shared in @fusion/core (R13) so the two surfaces cannot drift.
*/
export function autoMergeGateReason(
entity: Pick<PrEntity, "state" | "unverified" | "autoMerge" | "reviewDecision" | "checksRollup" | "mergeable">,
): string {
if (!entity.autoMerge) return "Auto-merge off";
if (entity.mergeable === "conflicting") return "Blocked: conflict";
if (entity.reviewDecision !== "APPROVED") return "Waiting for approval";
if (entity.checksRollup !== "success") return "Waiting for checks";
if (entity.mergeable !== "clean") return "Waiting for checks";
if (isPrEntityAutoMergeReady(entity)) return "Ready to merge";
return "Waiting for checks";
}