fix(FN-679): sync task prompts when task metadata changes
- Regenerate PROMPT.md when task titles or descriptions are updated in the store - Add regression tests covering prompt regeneration for title and description changes - Tighten central activity cleanup coverage to preserve entries at the cutoff boundary - Add a changeset for the published CLI package fix
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
||||||
import { mkdtempSync, rmSync, mkdirSync } from "node:fs";
|
import { mkdtempSync, rmSync, mkdirSync } from "node:fs";
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
@@ -268,9 +268,15 @@ describe("CentralCore", () => {
|
|||||||
|
|
||||||
describe("project queries", () => {
|
describe("project queries", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
vi.useFakeTimers();
|
||||||
|
vi.setSystemTime(new Date("2026-04-01T12:00:00.000Z"));
|
||||||
await central.init();
|
await central.init();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
it("should get project by id", async () => {
|
it("should get project by id", async () => {
|
||||||
const projectPath = join(tempDir, "get-project");
|
const projectPath = join(tempDir, "get-project");
|
||||||
mkdirSync(projectPath);
|
mkdirSync(projectPath);
|
||||||
@@ -339,8 +345,7 @@ describe("CentralCore", () => {
|
|||||||
path: projectPath,
|
path: projectPath,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add small delay to ensure different timestamp
|
vi.setSystemTime(new Date("2026-04-01T12:00:01.000Z"));
|
||||||
await new Promise((r) => setTimeout(r, 10));
|
|
||||||
|
|
||||||
const updated = await central.updateProject(project.id, {
|
const updated = await central.updateProject(project.id, {
|
||||||
name: "Updated",
|
name: "Updated",
|
||||||
@@ -507,9 +512,15 @@ describe("CentralCore", () => {
|
|||||||
|
|
||||||
describe("unified activity feed", () => {
|
describe("unified activity feed", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
vi.useFakeTimers();
|
||||||
|
vi.setSystemTime(new Date("2026-04-01T12:00:00.000Z"));
|
||||||
await central.init();
|
await central.init();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
it("should log activity with auto-generated id", async () => {
|
it("should log activity with auto-generated id", async () => {
|
||||||
const projectPath = join(tempDir, "activity-project");
|
const projectPath = join(tempDir, "activity-project");
|
||||||
mkdirSync(projectPath);
|
mkdirSync(projectPath);
|
||||||
@@ -544,8 +555,7 @@ describe("CentralCore", () => {
|
|||||||
|
|
||||||
const beforeActivity = project.lastActivityAt;
|
const beforeActivity = project.lastActivityAt;
|
||||||
|
|
||||||
// Small delay
|
vi.setSystemTime(new Date("2026-04-01T12:00:01.000Z"));
|
||||||
await new Promise((r) => setTimeout(r, 10));
|
|
||||||
|
|
||||||
await central.logActivity({
|
await central.logActivity({
|
||||||
type: "task:moved",
|
type: "task:moved",
|
||||||
@@ -711,10 +721,8 @@ describe("CentralCore", () => {
|
|||||||
expect(projectCount).toBe(5);
|
expect(projectCount).toBe(5);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should cleanup old activity entries", async () => {
|
it("should cleanup only activity entries older than the cutoff", async () => {
|
||||||
vi.useFakeTimers();
|
vi.useFakeTimers();
|
||||||
const now = new Date("2026-01-15T12:00:00.000Z");
|
|
||||||
vi.setSystemTime(now);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const projectPath = join(tempDir, "cleanup-activity");
|
const projectPath = join(tempDir, "cleanup-activity");
|
||||||
@@ -726,19 +734,39 @@ describe("CentralCore", () => {
|
|||||||
path: projectPath,
|
path: projectPath,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
vi.setSystemTime(new Date("2026-04-01T12:00:00.000Z"));
|
||||||
await central.logActivity({
|
await central.logActivity({
|
||||||
type: "task:created",
|
type: "task:created",
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
projectName: project.name,
|
projectName: project.name,
|
||||||
timestamp: now.toISOString(),
|
timestamp: "2026-03-31T11:59:59.999Z",
|
||||||
details: "Recent",
|
details: "Older than cutoff",
|
||||||
});
|
});
|
||||||
|
|
||||||
const deleted = await central.cleanupOldActivity(-1);
|
await central.logActivity({
|
||||||
expect(deleted).toBe(0);
|
type: "task:created",
|
||||||
|
projectId: project.id,
|
||||||
|
projectName: project.name,
|
||||||
|
timestamp: "2026-03-31T12:00:00.000Z",
|
||||||
|
details: "Exactly at cutoff",
|
||||||
|
});
|
||||||
|
|
||||||
const countAfter = await central.getActivityCount();
|
await central.logActivity({
|
||||||
expect(countAfter).toBe(1);
|
type: "task:created",
|
||||||
|
projectId: project.id,
|
||||||
|
projectName: project.name,
|
||||||
|
timestamp: "2026-03-31T12:00:00.001Z",
|
||||||
|
details: "Newer than cutoff",
|
||||||
|
});
|
||||||
|
|
||||||
|
const deleted = await central.cleanupOldActivity(1);
|
||||||
|
expect(deleted).toBe(1);
|
||||||
|
|
||||||
|
const remaining = await central.getRecentActivity({ limit: 10, projectId: project.id });
|
||||||
|
expect(remaining.map((entry) => entry.details)).toEqual([
|
||||||
|
"Newer than cutoff",
|
||||||
|
"Exactly at cutoff",
|
||||||
|
]);
|
||||||
} finally {
|
} finally {
|
||||||
vi.useRealTimers();
|
vi.useRealTimers();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user