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 7cab64e346
commit 656df5de29
9 changed files with 174 additions and 14 deletions

View File

@@ -18,6 +18,7 @@ const {
mockResumeOrphaned,
mockTaskStoreSettings,
mockMessageStoreSetHook,
mockSchedulerConfigurePrMonitoring,
} = vi.hoisted(() => ({
mockSelfHealingStart: vi.fn(),
mockSelfHealingStop: vi.fn(),
@@ -28,6 +29,7 @@ const {
mockResumeOrphaned: vi.fn().mockResolvedValue(undefined),
mockTaskStoreSettings: {} as Record<string, unknown>,
mockMessageStoreSetHook: vi.fn(),
mockSchedulerConfigurePrMonitoring: vi.fn(),
}));
// Mock the TaskStore class
@@ -108,6 +110,7 @@ vi.mock("../../scheduler.js", async () => {
self.start = vi.fn();
self.stop = vi.fn();
self.reconcileAllMissionFeatures = vi.fn().mockResolvedValue(0);
self.configurePrMonitoring = mockSchedulerConfigurePrMonitoring;
return self;
}),
};
@@ -473,6 +476,19 @@ describe("InProcessRuntime", () => {
it("should return undefined TriggerScheduler before start", () => {
expect(runtime.getTriggerScheduler()).toBeUndefined();
});
it("configures scheduler PR monitoring after start", async () => {
await runtime.start();
runtime.configurePrMonitoring({
prMonitor: {} as never,
onClosedPrFeedback: vi.fn(),
});
expect(mockSchedulerConfigurePrMonitoring).toHaveBeenCalledTimes(1);
expect(mockSchedulerConfigurePrMonitoring).toHaveBeenCalledWith(expect.objectContaining({
prMonitor: expect.any(Object),
}));
});
});
describe("trigger scheduler wiring", () => {

View File

@@ -13,6 +13,8 @@ import type {
} from "@fusion/core";
import { isEphemeralAgent } from "@fusion/core";
import { Scheduler } from "../scheduler.js";
import type { PrMonitor, PrComment } from "../pr-monitor.js";
import type { PrInfo } from "@fusion/core";
import { TaskExecutor, type TaskExecutorOptions } from "../executor.js";
import { WorktreePool, isGitRepository } from "../worktree-pool.js";
import { AgentSemaphore } from "../concurrency.js";
@@ -1004,6 +1006,17 @@ export class InProcessRuntime
return this.scheduler;
}
configurePrMonitoring(options: {
prMonitor: PrMonitor;
onClosedPrFeedback?: (taskId: string, prInfo: PrInfo, comments: PrComment[]) => void | Promise<void>;
}): void {
if (!this.scheduler) {
throw new Error("Scheduler not initialized. Call start() first.");
}
this.scheduler.configurePrMonitoring(options);
}
/**
* Get current runtime metrics.
*/