feat(FN-3097): wire plugin prompt contributions into execution surfaces

- Add plugin prompt contribution support across executor, triage, reviewer, and heartbeat instruction builders
- Thread plugin runner context into execution prompt assembly and preserve source issue commit reference hints
- Expand engine tests to cover prompt contribution injection behavior and regression scenarios
- Update architecture and plugin authoring docs with prompt surface integration details

Fusion-Task-Id: FN-3097
This commit is contained in:
Fusion
2026-05-05 23:31:06 -07:00
committed by gsxdsm
parent b312ca4122
commit ef1ae3eac3
10 changed files with 335 additions and 20 deletions

View File

@@ -697,6 +697,103 @@ describe("fast-mode triage", () => {
expect(capturedSystemPrompt).toContain("## Review Level");
});
it("includes triage plugin contributions when provided", async () => {
const task = createTriageTask({ id: "FN-FAST-PLUGIN-001", executionMode: "standard" });
const store = createMockStore({
getTask: vi.fn().mockResolvedValue({ ...mockTaskDetail, id: task.id, attachments: [], comments: [] }),
});
const pluginRunner = {
getPromptContributionsForSurface: vi.fn().mockImplementation((surface: string) => {
if (surface !== "triage") return [];
return [{ pluginId: "plugin-triage", contribution: { surface: "triage", content: "Use plugin triage policy." }, config: {} }];
}),
getPluginSkills: vi.fn().mockReturnValue([]),
};
let capturedSystemPrompt = "";
mockCreateFnAgent.mockImplementationOnce(async (opts: any) => {
capturedSystemPrompt = opts.systemPrompt;
return {
session: {
state: {},
sessionManager: { getLeafId: vi.fn().mockReturnValue(null) },
prompt: vi.fn().mockResolvedValue(undefined),
dispose: vi.fn(),
navigateTree: vi.fn(),
},
};
});
const processor = new TriageProcessor(store, "/tmp/root", { pluginRunner: pluginRunner as any });
await processor.specifyTask(task);
expect(capturedSystemPrompt).toContain("## Plugin: plugin-triage");
expect(capturedSystemPrompt).toContain("Use plugin triage policy.");
});
it("keeps triage prompt unchanged when no triage plugin contributions exist", async () => {
const task = createTriageTask({ id: "FN-FAST-PLUGIN-002", executionMode: "standard" });
const store = createMockStore({
getTask: vi.fn().mockResolvedValue({ ...mockTaskDetail, id: task.id, attachments: [], comments: [] }),
});
const pluginRunner = {
getPromptContributionsForSurface: vi.fn().mockReturnValue([]),
getPluginSkills: vi.fn().mockReturnValue([]),
};
let capturedSystemPrompt = "";
mockCreateFnAgent.mockImplementationOnce(async (opts: any) => {
capturedSystemPrompt = opts.systemPrompt;
return {
session: {
state: {},
sessionManager: { getLeafId: vi.fn().mockReturnValue(null) },
prompt: vi.fn().mockResolvedValue(undefined),
dispose: vi.fn(),
navigateTree: vi.fn(),
},
};
});
const processor = new TriageProcessor(store, "/tmp/root", { pluginRunner: pluginRunner as any });
await processor.specifyTask(task);
expect(capturedSystemPrompt).not.toContain("## Plugin:");
});
it("applies triage plugin contributions in fast mode too", async () => {
const task = createTriageTask({ id: "FN-FAST-PLUGIN-003", executionMode: "fast" });
const store = createMockStore({
getTask: vi.fn().mockResolvedValue({ ...mockTaskDetail, id: task.id, attachments: [], comments: [] }),
});
const pluginRunner = {
getPromptContributionsForSurface: vi.fn().mockReturnValue([
{ pluginId: "plugin-fast", contribution: { surface: "triage", content: "Fast mode plugin note." }, config: {} },
]),
getPluginSkills: vi.fn().mockReturnValue([]),
};
let capturedSystemPrompt = "";
mockCreateFnAgent.mockImplementationOnce(async (opts: any) => {
capturedSystemPrompt = opts.systemPrompt;
return {
session: {
state: {},
sessionManager: { getLeafId: vi.fn().mockReturnValue(null) },
prompt: vi.fn().mockResolvedValue(undefined),
dispose: vi.fn(),
navigateTree: vi.fn(),
},
};
});
const processor = new TriageProcessor(store, "/tmp/root", { pluginRunner: pluginRunner as any });
await processor.specifyTask(task);
expect(capturedSystemPrompt).toContain("This task is running in **fast mode**");
expect(capturedSystemPrompt).toContain("## Plugin: plugin-fast");
});
it("auto-approves fn_review_spec in fast mode without calling reviewer", async () => {
const rootDir = await createTriageFixtureRoot("fusion-triage-fast-review-");
try {