feat(FN-3202): handle manual PR linking and feedback follow-ups

- Add scheduler logic to create dependency-linked follow-up tasks when actionable PR feedback remains after a PR is merged or closed
- Update engine runtime/project wiring to support manual PR create flows and branch publish behavior for fusion/<task-id>
- Add dashboard route coverage for manual PR creation/linking behavior and corresponding engine/runtime tests
- Document manual PR branch conventions and follow-up behavior in task management and dashboard docs

Fusion-Task-Id: FN-3202
This commit is contained in:
Fusion
2026-05-02 10:59:51 -07:00
committed by gsxdsm
parent bed7f3d325
commit 4eacff7a9e
9 changed files with 174 additions and 14 deletions

View File

@@ -24,6 +24,8 @@ const mocks = vi.hoisted(() => ({
notifierNotifyGridlock: vi.fn(),
notificationServiceStart: vi.fn(async () => undefined),
notificationServiceStop: vi.fn(),
runtimeConfigurePrMonitoring: vi.fn(),
prHandlerCreateFollowUpTask: vi.fn(async () => undefined),
}));
vi.mock("@fusion/core", async (importOriginal) => {
@@ -71,6 +73,7 @@ vi.mock("../pr-monitor.js", () => ({
vi.mock("../pr-comment-handler.js", () => ({
PrCommentHandler: vi.fn().mockImplementation(() => ({
handleNewComments: vi.fn(),
createFollowUpTask: mocks.prHandlerCreateFollowUpTask,
})),
}));
@@ -101,6 +104,7 @@ vi.mock("../runtimes/in-process-runtime.js", () => ({
getRoutineRunner: vi.fn(),
getHeartbeatMonitor: vi.fn(),
getTriggerScheduler: vi.fn(),
configurePrMonitoring: mocks.runtimeConfigurePrMonitoring,
})),
}));
@@ -284,6 +288,36 @@ describe("ProjectEngine notification ownership wiring", () => {
});
});
describe("ProjectEngine PR monitoring wiring", () => {
it("wires runtime scheduler PR monitoring with closed-PR follow-up handler", async () => {
const { store } = createMockStore(baseSettings);
mocks.currentStore = store;
const engine = createEngine();
await engine.start();
expect(mocks.runtimeConfigurePrMonitoring).toHaveBeenCalled();
const configArg = mocks.runtimeConfigurePrMonitoring.mock.calls.at(-1)?.[0] as {
onClosedPrFeedback?: (taskId: string, prInfo: Record<string, unknown>, comments: unknown[]) => Promise<void> | void;
};
expect(typeof configArg.onClosedPrFeedback).toBe("function");
await configArg.onClosedPrFeedback?.(
"FN-3202",
{ number: 12, status: "merged", url: "https://example/pr/12" } as never,
[{ id: 1, body: "please fix", user: { login: "reviewer" } }] as never,
);
expect(mocks.prHandlerCreateFollowUpTask).toHaveBeenCalledWith(
"FN-3202",
expect.objectContaining({ number: 12 }),
expect.any(Array),
);
await engine.stop();
});
});
describe("ProjectEngine auto-summarize wiring", () => {
beforeEach(() => {
vi.clearAllMocks();