feat(KB-341): unify steeringComments and comments into single comments system
- Consolidate steeringComments and comments into unified comments field using TaskComment type - Remove SteeringComment type, migrate all comments to TaskComment format - Update addComment to delegate to addTaskComment with auto-refinement for done tasks - Merge legacy steeringComments data into comments during database migration - Update dashboard API endpoint from /steer to /comments - Update executor to use unified comments field for real-time injection - Update PR comment handler to use addTaskComment method
This commit is contained in:
@@ -1622,7 +1622,7 @@ describe("buildExecutionPrompt", () => {
|
||||
id: "1",
|
||||
text: "Please handle the edge case",
|
||||
createdAt: new Date().toISOString(),
|
||||
author: "user" as const,
|
||||
author: "user",
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -1631,7 +1631,7 @@ describe("buildExecutionPrompt", () => {
|
||||
expect(result).toContain("## Steering Comments");
|
||||
expect(result).toContain("**user**");
|
||||
expect(result).toContain("> Please handle the edge case");
|
||||
expect(result).toContain("The following comments were added during execution");
|
||||
expect(result).toContain("The following comments were added");
|
||||
});
|
||||
|
||||
it("formats multiple comments correctly", () => {
|
||||
@@ -1642,13 +1642,13 @@ describe("buildExecutionPrompt", () => {
|
||||
id: "1",
|
||||
text: "First comment",
|
||||
createdAt: new Date(now.getTime() - 60000).toISOString(), // 1 minute ago
|
||||
author: "user" as const,
|
||||
author: "user",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
text: "Second comment",
|
||||
createdAt: now.toISOString(),
|
||||
author: "agent" as const,
|
||||
author: "agent",
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -1660,8 +1660,8 @@ describe("buildExecutionPrompt", () => {
|
||||
expect(result).toContain("> Second comment");
|
||||
});
|
||||
|
||||
it("omits Comments section when steeringComments is empty", () => {
|
||||
const task = createMockTaskDetail({ steeringComments: [] });
|
||||
it("omits Comments section when comments is empty", () => {
|
||||
const task = createMockTaskDetail({ comments: [] });
|
||||
const result = buildExecutionPrompt(task);
|
||||
|
||||
expect(result).not.toContain("## Steering Comments");
|
||||
@@ -1675,14 +1675,14 @@ describe("buildExecutionPrompt", () => {
|
||||
});
|
||||
|
||||
it("includes only the 10 most recent comments", () => {
|
||||
const comments = Array.from({ length: 15 }, (_, i) => ({
|
||||
const allComments = Array.from({ length: 15 }, (_, i) => ({
|
||||
id: `${i}`,
|
||||
text: `Comment ${i}`,
|
||||
createdAt: new Date().toISOString(),
|
||||
author: "user" as const,
|
||||
author: "user",
|
||||
}));
|
||||
|
||||
const task = createMockTaskDetail({ comments });
|
||||
const task = createMockTaskDetail({ comments: allComments });
|
||||
const result = buildExecutionPrompt(task);
|
||||
|
||||
// Should include comments 5-14 (the 10 most recent), not 0-4
|
||||
|
||||
@@ -3,7 +3,7 @@ import { PrCommentHandler } from "./pr-comment-handler.js";
|
||||
import type { TaskStore, Task } from "@fusion/core";
|
||||
|
||||
const mockStore = {
|
||||
addComment: vi.fn<(id: string, text: string, author?: "user" | "agent") => Promise<Task>>(),
|
||||
addTaskComment: vi.fn<(id: string, text: string, author?: string) => Promise<Task>>(),
|
||||
createTask: vi.fn<(input: Parameters<TaskStore["createTask"]>[0]) => Promise<Task>>().mockResolvedValue({ id: "FN-123" } as Task),
|
||||
} as unknown as TaskStore;
|
||||
|
||||
@@ -49,7 +49,7 @@ describe("PrCommentHandler", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
expect(mockStore.addComment).not.toHaveBeenCalled();
|
||||
expect(mockStore.addTaskComment).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -65,7 +65,7 @@ describe("PrCommentHandler", () => {
|
||||
{ body: "Consider using a different approach", keyword: "consider" },
|
||||
{ body: "I suggest renaming this", keyword: "suggest" },
|
||||
{ body: "Recommend adding tests", keyword: "recommend" },
|
||||
])("creates steering comment for actionable feedback containing '$keyword': $body", async ({ body }) => {
|
||||
])("creates comment for actionable feedback containing '$keyword': $body", async ({ body }) => {
|
||||
await handler.handleNewComments("FN-001", mockPrInfo, [
|
||||
{
|
||||
id: 1,
|
||||
@@ -77,12 +77,12 @@ describe("PrCommentHandler", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
expect(mockStore.addComment).toHaveBeenCalled();
|
||||
expect(mockStore.addTaskComment).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("code suggestions", () => {
|
||||
it("creates steering comment for comments with code blocks", async () => {
|
||||
it("creates comment for comments with code blocks", async () => {
|
||||
await handler.handleNewComments("FN-001", mockPrInfo, [
|
||||
{
|
||||
id: 1,
|
||||
@@ -94,10 +94,10 @@ describe("PrCommentHandler", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
expect(mockStore.addComment).toHaveBeenCalled();
|
||||
expect(mockStore.addTaskComment).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("creates steering comment for inline code suggestions", async () => {
|
||||
it("creates comment for inline code suggestions", async () => {
|
||||
await handler.handleNewComments("FN-001", mockPrInfo, [
|
||||
{
|
||||
id: 1,
|
||||
@@ -109,11 +109,11 @@ describe("PrCommentHandler", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
expect(mockStore.addComment).toHaveBeenCalled();
|
||||
expect(mockStore.addTaskComment).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("steering comment content", () => {
|
||||
describe("comment content", () => {
|
||||
it("includes PR info and comment details", async () => {
|
||||
await handler.handleNewComments("FN-001", mockPrInfo, [
|
||||
{
|
||||
@@ -126,7 +126,7 @@ describe("PrCommentHandler", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
const call = (mockStore.addComment as ReturnType<typeof vi.fn>).mock.calls[0];
|
||||
const call = (mockStore.addTaskComment as ReturnType<typeof vi.fn>).mock.calls[0];
|
||||
const text = call[1] as string;
|
||||
|
||||
expect(text).toContain("PR Review Feedback");
|
||||
@@ -151,7 +151,7 @@ describe("PrCommentHandler", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
const call = (mockStore.addComment as ReturnType<typeof vi.fn>).mock.calls[0];
|
||||
const call = (mockStore.addTaskComment as ReturnType<typeof vi.fn>).mock.calls[0];
|
||||
const text = call[1] as string;
|
||||
|
||||
expect(text.length).toBeLessThan(longBody.length);
|
||||
@@ -170,7 +170,7 @@ describe("PrCommentHandler", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
expect(mockStore.addComment).toHaveBeenCalledWith(
|
||||
expect(mockStore.addTaskComment).toHaveBeenCalledWith(
|
||||
"FN-001",
|
||||
expect.any(String),
|
||||
"agent"
|
||||
@@ -189,7 +189,7 @@ describe("PrCommentHandler", () => {
|
||||
},
|
||||
]);
|
||||
|
||||
const text = (mockStore.addComment as ReturnType<typeof vi.fn>).mock.calls[0][1] as string;
|
||||
const text = (mockStore.addTaskComment as ReturnType<typeof vi.fn>).mock.calls[0][1] as string;
|
||||
expect(text).toContain("This PR is already merged");
|
||||
expect(text).toContain("follow-up work");
|
||||
});
|
||||
|
||||
@@ -81,11 +81,11 @@ export class PrCommentHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build steering comment text
|
||||
const text = this.buildSteeringText(prInfo, comment, hasCodeSuggestions);
|
||||
// Build comment text
|
||||
const text = this.buildCommentText(prInfo, comment, hasCodeSuggestions);
|
||||
|
||||
try {
|
||||
await this.store.addComment(taskId, text, "agent");
|
||||
await this.store.addTaskComment(taskId, text, "agent");
|
||||
prMonitorLog.log(`Added comment for PR review #${comment.id}`);
|
||||
} catch (err) {
|
||||
prMonitorLog.error(`Failed to add comment for ${taskId}:`, err);
|
||||
@@ -117,9 +117,9 @@ export class PrCommentHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Build steering comment text from PR review comment.
|
||||
* Build comment text from PR review comment.
|
||||
*/
|
||||
private buildSteeringText(
|
||||
private buildCommentText(
|
||||
prInfo: PrInfo,
|
||||
comment: PrComment,
|
||||
hasCodeSuggestions: boolean
|
||||
|
||||
Reference in New Issue
Block a user