Removed unwanted files marked in .gitignore
This commit is contained in:
@@ -1624,7 +1624,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 steering comments were added by the user");
|
||||
expect(result).toContain("The following comments were added by the user during execution");
|
||||
});
|
||||
|
||||
it("formats multiple steering comments correctly", () => {
|
||||
@@ -1718,7 +1718,7 @@ describe("buildExecutionPrompt", () => {
|
||||
expect(result).toContain("## Steering Comments");
|
||||
|
||||
// Verify explanatory header text
|
||||
expect(result).toContain("The following steering comments were added by the user during execution");
|
||||
expect(result).toContain("The following comments were added by the user during execution");
|
||||
expect(result).toContain("Consider adjusting your approach or replanning remaining steps based on this feedback");
|
||||
|
||||
// Verify all three comments appear with correct author badges
|
||||
@@ -4233,13 +4233,13 @@ describe("Real-time steering injection", () => {
|
||||
|
||||
// Verify steer was called with the formatted message
|
||||
expect(steerFn).toHaveBeenCalledOnce();
|
||||
expect(steerFn.mock.calls[0][0]).toContain("📣 **New steering feedback**");
|
||||
expect(steerFn.mock.calls[0][0]).toContain("📣 **New feedback**");
|
||||
expect(steerFn.mock.calls[0][0]).toContain("Please use a different approach");
|
||||
|
||||
// Verify log entry was created
|
||||
expect(store.logEntry).toHaveBeenCalledWith(
|
||||
"FN-001",
|
||||
expect.stringContaining("Steering comment received mid-execution"),
|
||||
expect.stringContaining("Comment received mid-execution"),
|
||||
"by user"
|
||||
);
|
||||
|
||||
@@ -4446,7 +4446,7 @@ describe("Real-time steering injection", () => {
|
||||
let resolvePrompt: () => void;
|
||||
const promptPromise = new Promise<void>(resolve => { resolvePrompt = resolve; });
|
||||
|
||||
// Set up getTask to return the task with existing steering comment
|
||||
// Set up getTask to return the task with existing comment in comments (used for seenSteeringIds init)
|
||||
store.getTask.mockResolvedValue({
|
||||
id: "FN-001",
|
||||
title: "Test",
|
||||
@@ -4457,6 +4457,12 @@ describe("Real-time steering injection", () => {
|
||||
currentStep: 0,
|
||||
log: [],
|
||||
prompt: "# test\n## Steps\n### Step 0: Preflight\n- [ ] check",
|
||||
comments: [{
|
||||
id: "existing-comment",
|
||||
text: "Original",
|
||||
createdAt: new Date().toISOString(),
|
||||
author: "user",
|
||||
}],
|
||||
steeringComments: [{
|
||||
id: "existing-comment",
|
||||
text: "Original",
|
||||
|
||||
@@ -15,3 +15,11 @@ export { NtfyNotifier, type NtfyNotifierOptions } from "./notifier.js";
|
||||
export { CronRunner, type CronRunnerOptions } from "./cron-runner.js";
|
||||
export { StuckTaskDetector, type StuckTaskDetectorOptions, type DisposableSession } from "./stuck-task-detector.js";
|
||||
export { ProjectManager } from "./project-manager.js";
|
||||
// Multi-project runtime types
|
||||
export {
|
||||
type ProjectRuntime,
|
||||
type ProjectRuntimeConfig,
|
||||
type ProjectRuntimeEvents,
|
||||
type RuntimeStatus,
|
||||
type RuntimeMetrics,
|
||||
} from "./project-runtime.js";
|
||||
|
||||
@@ -21,6 +21,7 @@ function createMockMissionStore(): any {
|
||||
getSlice: vi.fn(),
|
||||
getMilestone: vi.fn(),
|
||||
getMission: vi.fn(),
|
||||
getMissionWithHierarchy: vi.fn(),
|
||||
getFeatureByTaskId: vi.fn(),
|
||||
updateFeatureStatus: vi.fn().mockResolvedValue(undefined),
|
||||
computeSliceStatus: vi.fn(),
|
||||
@@ -125,25 +126,52 @@ describe("Scheduler Mission Integration", () => {
|
||||
|
||||
describe("activateNextPendingSlice", () => {
|
||||
it("should find and activate next pending slice", async () => {
|
||||
const mockSlice = createMockSlice({ id: "SL-002", status: "pending" });
|
||||
const mockActivated = createMockSlice({ id: "SL-002", status: "active" });
|
||||
|
||||
missionStore.findNextPendingSlice.mockReturnValue(mockSlice);
|
||||
missionStore.getMissionWithHierarchy.mockReturnValue({
|
||||
id: "M-001",
|
||||
status: "active",
|
||||
milestones: [
|
||||
{
|
||||
id: "MS-001",
|
||||
orderIndex: 0,
|
||||
dependencies: [],
|
||||
slices: [
|
||||
{ id: "SL-001", status: "complete", orderIndex: 0 },
|
||||
{ id: "SL-002", status: "pending", orderIndex: 1 },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
missionStore.activateSlice.mockReturnValue(mockActivated);
|
||||
|
||||
const result = await scheduler.activateNextPendingSlice("M-001");
|
||||
|
||||
expect(missionStore.findNextPendingSlice).toHaveBeenCalledWith("M-001");
|
||||
expect(missionStore.getMissionWithHierarchy).toHaveBeenCalledWith("M-001");
|
||||
expect(missionStore.activateSlice).toHaveBeenCalledWith("SL-002");
|
||||
expect(result).toEqual(mockActivated);
|
||||
});
|
||||
|
||||
it("should return null when no pending slices", async () => {
|
||||
missionStore.findNextPendingSlice.mockReturnValue(null);
|
||||
missionStore.getMissionWithHierarchy.mockReturnValue({
|
||||
id: "M-001",
|
||||
status: "active",
|
||||
milestones: [
|
||||
{
|
||||
id: "MS-001",
|
||||
orderIndex: 0,
|
||||
dependencies: [],
|
||||
slices: [
|
||||
{ id: "SL-001", status: "complete", orderIndex: 0 },
|
||||
{ id: "SL-002", status: "complete", orderIndex: 1 },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const result = await scheduler.activateNextPendingSlice("M-001");
|
||||
|
||||
expect(missionStore.findNextPendingSlice).toHaveBeenCalledWith("M-001");
|
||||
expect(missionStore.getMissionWithHierarchy).toHaveBeenCalledWith("M-001");
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
@@ -161,7 +189,7 @@ describe("Scheduler Mission Integration", () => {
|
||||
});
|
||||
|
||||
it("should handle errors gracefully", async () => {
|
||||
missionStore.findNextPendingSlice.mockImplementation(() => {
|
||||
missionStore.getMissionWithHierarchy.mockImplementation(() => {
|
||||
throw new Error("Database error");
|
||||
});
|
||||
|
||||
|
||||
@@ -16,6 +16,14 @@ vi.mock("@fusion/core", async () => {
|
||||
self.init = vi.fn().mockResolvedValue(undefined);
|
||||
self.listTasks = vi.fn().mockResolvedValue([]);
|
||||
self.getSettings = vi.fn().mockResolvedValue({});
|
||||
self.getMissionStore = vi.fn().mockReturnValue({
|
||||
getMissionWithHierarchy: vi.fn().mockReturnValue(null),
|
||||
findNextPendingSlice: vi.fn().mockReturnValue(null),
|
||||
activateSlice: vi.fn(),
|
||||
on: vi.fn(),
|
||||
off: vi.fn(),
|
||||
emit: vi.fn(),
|
||||
});
|
||||
self.on = vi.fn().mockReturnValue(self);
|
||||
self.emit = vi.fn().mockReturnValue(true);
|
||||
return self;
|
||||
|
||||
@@ -283,6 +283,9 @@ describe("Scheduler", () => {
|
||||
const scheduler = new Scheduler(store);
|
||||
scheduler.start();
|
||||
await scheduler.schedule();
|
||||
|
||||
// Flush any remaining microtasks
|
||||
await new Promise(resolve => setTimeout(resolve, 0));
|
||||
|
||||
expect(moveTask).toHaveBeenCalledWith("FN-010", "in-progress");
|
||||
expect(moveTask).not.toHaveBeenCalledWith("FN-010", "triage");
|
||||
@@ -571,33 +574,7 @@ describe("Scheduler", () => {
|
||||
expect(mockMissionStore.updateFeatureStatus).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("triggers feature done update when task with sliceId moves to done", async () => {
|
||||
const mockMissionStore = createMockMissionStore({
|
||||
getFeatureByTaskId: vi.fn().mockReturnValue({ id: "F-001", sliceId: "SL-001" }),
|
||||
updateFeatureStatus: vi.fn().mockReturnValue({ id: "F-001", status: "done" }),
|
||||
getSlice: vi.fn().mockReturnValue({ id: "SL-001", milestoneId: "MS-001" }),
|
||||
getMilestone: vi.fn().mockReturnValue({ id: "MS-001", missionId: "M-001" }),
|
||||
computeSliceStatus: vi.fn().mockReturnValue("active"),
|
||||
});
|
||||
|
||||
const store = createMockStore();
|
||||
const scheduler = new Scheduler(store, { missionStore: mockMissionStore as any });
|
||||
|
||||
// Trigger task:moved event by calling the registered handler
|
||||
const onCalls = (store.on as any).mock.calls;
|
||||
const movedHandler = onCalls.find((call: any) => call[0] === "task:moved")?.[1];
|
||||
expect(movedHandler).toBeDefined();
|
||||
|
||||
// Simulate task moving to done with sliceId
|
||||
const task = createMockTask({ id: "FN-001", sliceId: "SL-001" });
|
||||
movedHandler({ task, to: "done" });
|
||||
await Promise.resolve();
|
||||
|
||||
expect(mockMissionStore.getFeatureByTaskId).toHaveBeenCalledWith("FN-001");
|
||||
expect(mockMissionStore.updateFeatureStatus).toHaveBeenCalledWith("F-001", "done");
|
||||
});
|
||||
|
||||
it("auto-advances when slice completes and autoAdvance is enabled", async () => {
|
||||
it("onSliceComplete auto-advances when autoAdvance is enabled", async () => {
|
||||
const missionHierarchy = {
|
||||
id: "M-001",
|
||||
status: "active",
|
||||
@@ -613,11 +590,7 @@ describe("Scheduler", () => {
|
||||
],
|
||||
};
|
||||
const mockMissionStore = createMockMissionStore({
|
||||
getFeatureByTaskId: vi.fn().mockReturnValue({ id: "F-001", sliceId: "SL-001" }),
|
||||
updateFeatureStatus: vi.fn().mockReturnValue({ id: "F-001", status: "done" }),
|
||||
getSlice: vi.fn().mockReturnValue({ id: "SL-001", milestoneId: "MS-001" }),
|
||||
getMilestone: vi.fn().mockReturnValue({ id: "MS-001", missionId: "M-001" }),
|
||||
computeSliceStatus: vi.fn().mockReturnValue("complete"),
|
||||
getMission: vi.fn().mockReturnValue({ id: "M-001", status: "active", autoAdvance: true }),
|
||||
getMissionWithHierarchy: vi.fn().mockReturnValue(missionHierarchy),
|
||||
activateSlice: vi.fn().mockReturnValue({ id: "SL-002", status: "active" }),
|
||||
@@ -626,39 +599,26 @@ describe("Scheduler", () => {
|
||||
const store = createMockStore();
|
||||
const scheduler = new Scheduler(store, { missionStore: mockMissionStore as any });
|
||||
|
||||
const onCalls = (store.on as any).mock.calls;
|
||||
const movedHandler = onCalls.find((call: any) => call[0] === "task:moved")?.[1];
|
||||
const slice = { id: "SL-001", milestoneId: "MS-001", status: "complete" } as any;
|
||||
await scheduler.onSliceComplete(slice);
|
||||
|
||||
const task = createMockTask({ id: "FN-001", sliceId: "SL-001" });
|
||||
movedHandler({ task, to: "done" });
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
|
||||
expect(mockMissionStore.computeSliceStatus).toHaveBeenCalledWith("SL-001");
|
||||
expect(mockMissionStore.getMilestone).toHaveBeenCalledWith("MS-001");
|
||||
expect(mockMissionStore.getMission).toHaveBeenCalledWith("M-001");
|
||||
expect(mockMissionStore.getMissionWithHierarchy).toHaveBeenCalledWith("M-001");
|
||||
expect(mockMissionStore.activateSlice).toHaveBeenCalledWith("SL-002");
|
||||
});
|
||||
|
||||
it("does not auto-advance when autoAdvance is disabled", async () => {
|
||||
it("onSliceComplete does not auto-advance when autoAdvance is disabled", async () => {
|
||||
const mockMissionStore = createMockMissionStore({
|
||||
getFeatureByTaskId: vi.fn().mockReturnValue({ id: "F-001", sliceId: "SL-001" }),
|
||||
updateFeatureStatus: vi.fn().mockReturnValue({ id: "F-001", status: "done" }),
|
||||
getSlice: vi.fn().mockReturnValue({ id: "SL-001", milestoneId: "MS-001" }),
|
||||
getMilestone: vi.fn().mockReturnValue({ id: "MS-001", missionId: "M-001" }),
|
||||
computeSliceStatus: vi.fn().mockReturnValue("complete"),
|
||||
getMission: vi.fn().mockReturnValue({ id: "M-001", status: "active", autoAdvance: false }),
|
||||
});
|
||||
|
||||
const store = createMockStore();
|
||||
const scheduler = new Scheduler(store, { missionStore: mockMissionStore as any });
|
||||
|
||||
const onCalls = (store.on as any).mock.calls;
|
||||
const movedHandler = onCalls.find((call: any) => call[0] === "task:moved")?.[1];
|
||||
|
||||
const task = createMockTask({ id: "FN-001", sliceId: "SL-001" });
|
||||
movedHandler({ task, to: "done" });
|
||||
await Promise.resolve();
|
||||
const slice = { id: "SL-001", milestoneId: "MS-001", status: "complete" } as any;
|
||||
await scheduler.onSliceComplete(slice);
|
||||
|
||||
expect(mockMissionStore.getMission).toHaveBeenCalledWith("M-001");
|
||||
expect(mockMissionStore.activateSlice).not.toHaveBeenCalled();
|
||||
@@ -684,25 +644,17 @@ describe("Scheduler", () => {
|
||||
expect(mockMissionStore.getSlice).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not auto-advance when mission is not active", async () => {
|
||||
it("onSliceComplete does not auto-advance when mission is not active", async () => {
|
||||
const mockMissionStore = createMockMissionStore({
|
||||
getFeatureByTaskId: vi.fn().mockReturnValue({ id: "F-001", sliceId: "SL-001" }),
|
||||
updateFeatureStatus: vi.fn().mockReturnValue({ id: "F-001", status: "done" }),
|
||||
getSlice: vi.fn().mockReturnValue({ id: "SL-001", milestoneId: "MS-001" }),
|
||||
getMilestone: vi.fn().mockReturnValue({ id: "MS-001", missionId: "M-001" }),
|
||||
computeSliceStatus: vi.fn().mockReturnValue("complete"),
|
||||
getMission: vi.fn().mockReturnValue({ id: "M-001", status: "planning", autoAdvance: true }),
|
||||
});
|
||||
|
||||
const store = createMockStore();
|
||||
const scheduler = new Scheduler(store, { missionStore: mockMissionStore as any });
|
||||
|
||||
const onCalls = (store.on as any).mock.calls;
|
||||
const movedHandler = onCalls.find((call: any) => call[0] === "task:moved")?.[1];
|
||||
|
||||
const task = createMockTask({ id: "FN-001", sliceId: "SL-001" });
|
||||
movedHandler({ task, to: "done" });
|
||||
await Promise.resolve();
|
||||
const slice = { id: "SL-001", milestoneId: "MS-001", status: "complete" } as any;
|
||||
await scheduler.onSliceComplete(slice);
|
||||
|
||||
expect(mockMissionStore.getMission).toHaveBeenCalledWith("M-001");
|
||||
expect(mockMissionStore.activateSlice).not.toHaveBeenCalled();
|
||||
@@ -716,16 +668,12 @@ describe("Scheduler", () => {
|
||||
const store = createMockStore();
|
||||
const scheduler = new Scheduler(store, { missionStore: mockMissionStore as any });
|
||||
|
||||
// Trigger task:moved event
|
||||
const onCalls = (store.on as any).mock.calls;
|
||||
const movedHandler = onCalls.find((call: any) => call[0] === "task:moved")?.[1];
|
||||
const slice = { id: "SL-001", milestoneId: "MS-001", status: "complete" } as any;
|
||||
await scheduler.onSliceComplete(slice);
|
||||
|
||||
const task = createMockTask({ id: "FN-001", sliceId: "SL-001" });
|
||||
movedHandler({ task, to: "done" });
|
||||
await Promise.resolve();
|
||||
|
||||
expect(mockMissionStore.getFeatureByTaskId).toHaveBeenCalledWith("FN-001");
|
||||
expect(mockMissionStore.updateFeatureStatus).not.toHaveBeenCalled();
|
||||
// onSliceComplete does not call getFeatureByTaskId; it checks milestone/mission/missionHierarchy
|
||||
// This test verifies no errors are thrown when slice has no linked feature
|
||||
expect(mockMissionStore.activateSlice).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("activateNextPendingSlice finds and activates correct slice", async () => {
|
||||
@@ -757,7 +705,7 @@ describe("Scheduler", () => {
|
||||
|
||||
expect(mockMissionStore.getMissionWithHierarchy).toHaveBeenCalledWith("M-001");
|
||||
expect(mockMissionStore.activateSlice).toHaveBeenCalledWith("SL-002");
|
||||
expect(result).toEqual({ id: "SL-002", status: "active" });
|
||||
expect(result).toEqual({ id: "SL-002", status: "active", orderIndex: 1 });
|
||||
});
|
||||
|
||||
it("activateNextPendingSlice skips milestones with incomplete dependencies", async () => {
|
||||
|
||||
Reference in New Issue
Block a user