test(FN-783): cover poisoned write-scope metadata

Fusion-Task-Id: FN-783

Co-authored-by: Fusion <noreply@runfusion.ai>
This commit is contained in:
Phil Larson
2026-06-25 02:27:17 -07:00
parent 5cd795a3f2
commit c8be19f9e6
2 changed files with 168 additions and 0 deletions

View File

@@ -419,6 +419,96 @@ describe("TaskStore", () => {
const paths = await store.parseFileScopeFromPrompt(task.id);
expect(paths).toEqual(["packages/dashboard/app/components/TaskDetailModal.tsx"]);
});
it("deduplicates effective write scope while preserving broad mixed-case source globs", async () => {
const task = await store.createTask({ description: "Duplicate effective scope" });
const dir = join(rootDir, ".fusion", "tasks", task.id);
await writeFile(
join(dir, "PROMPT.md"),
`# ${task.id}: Duplicate effective scope
## File Scope
- \`packages/core/**\`
- \`packages/core/**\`
- \`Packages/MobileApp/**\`
- \`Tests/AtlasNotesMobileUITests/**\`
`,
);
const paths = await store.parseFileScopeFromPrompt(task.id);
expect(paths).toEqual([
"packages/core/**",
"Packages/MobileApp/**",
"Tests/AtlasNotesMobileUITests/**",
]);
});
it("excludes poisoned FN-779/FN-756 context-only paths from effective write scope", async () => {
const task = await store.createTask({ description: "Poisoned Fusion prompt" });
const dir = join(rootDir, ".fusion", "tasks", task.id);
await writeFile(
join(dir, "PROMPT.md"),
`# ${task.id}: Poisoned Fusion prompt
## File Scope
Expected touched paths in \`/Users/plarson/src/Fusion-local-runtime\`:
- \`packages/core/src/store.ts\`
- \`packages/engine/src/scheduler.ts\`
- \`packages/dashboard/**\`
- \`packages/cli/**\`
- \`packages/core/src/__tests__/store-parsing.test.ts\`
Forbidden paths / non-goals:
- Do not edit Atlas Notes Swift/mobile files: \`project.yml\`, \`AtlasNotes.xcodeproj/**\`, \`Tests/AtlasNotesMobileUITests/**\`, \`Packages/MobileApp/**\`, \`Sources/**\`.
- Do not hand-edit \`.fusion/fusion.db\` or \`.fusion/tasks/*/task.json\`.
- Generated locks such as \`Packages/*/Package.resolved\` are evidence only.
- \`.changeset/*.md\` is required only if published behavior changes.
- Operator routes/actions: \`/tasks/:id\`, \`fn_task_update\`, \`review\`, \`merge\`, \`retry\`, \`archive\`.
`,
);
const paths = await store.parseFileScopeFromPrompt(task.id);
expect(paths).toEqual([
"packages/core/src/store.ts",
"packages/engine/src/scheduler.ts",
"packages/dashboard/**",
"packages/cli/**",
"packages/core/src/__tests__/store-parsing.test.ts",
]);
});
it("keeps true Atlas mobile hot-file family writes when declared as implementation scope", async () => {
const task = await store.createTask({ description: "Atlas mobile scope" });
const dir = join(rootDir, ".fusion", "tasks", task.id);
await writeFile(
join(dir, "PROMPT.md"),
`# ${task.id}: Atlas mobile scope
## File Scope
Expected touched paths:
- \`project.yml\`
- \`AtlasNotes.xcodeproj/**\`
- \`Tests/AtlasNotesMobileUITests/**\`
- \`Packages/MobileApp/**\`
- \`Sources/AtlasNotesMobileApp/**\`
`,
);
const paths = await store.parseFileScopeFromPrompt(task.id);
expect(paths).toEqual([
"project.yml",
"AtlasNotes.xcodeproj/**",
"Tests/AtlasNotesMobileUITests/**",
"Packages/MobileApp/**",
"Sources/AtlasNotesMobileApp/**",
]);
});
});
describe("FN-5216 File Scope sanitization on copy paths", () => {

View File

@@ -1813,6 +1813,84 @@ describe("approved triage recovery", () => {
);
});
it("stamps source metadata from sanitized effective write scope during recovery", async () => {
await writeFile(
join(rootDir, ".fusion", "tasks", "FN-001", "PROMPT.md"),
`# Task: FN-001 - Fix poisoned scope
**Size:** L
## File Scope
Expected touched paths:
- \`packages/core/src/store.ts\`
- \`packages/engine/src/scheduler.ts\`
- \`packages/dashboard/**\`
Forbidden paths / non-goals:
- Do not edit Atlas files: \`AtlasNotes.xcodeproj/**\`, \`Tests/AtlasNotesMobileUITests/**\`, \`Packages/MobileApp/**\`.
- Evidence only: \`.fusion/fusion.db\`, \`.fusion/tasks/*/task.json\`, \`Packages/*/Package.resolved\`.
- Conditional only: \`.changeset/*.md\`.
`,
);
const store = createMockStore({
getSettings: vi.fn().mockResolvedValue({
maxConcurrent: 2,
maxWorktrees: 4,
pollIntervalMs: 10000,
groupOverlappingFiles: false,
autoMerge: true,
requirePlanApproval: false,
} as Settings),
parseFileScopeFromPrompt: vi.fn().mockResolvedValue([]),
});
const processor = new TriageProcessor(store, rootDir);
const recovered = await processor.recoverApprovedTask({
id: "FN-001",
description: "Recovered triage task",
column: "triage",
status: "planning",
dependencies: [],
steps: [],
currentStep: 0,
log: [{ timestamp: "2026-01-01T00:00:00.000Z", action: "Spec review: APPROVE" }],
createdAt: "2026-01-01T00:00:00.000Z",
updatedAt: "2026-01-01T00:02:00.000Z",
});
expect(recovered).toBe(true);
expect(store.updateTask).toHaveBeenCalledWith(
"FN-001",
expect.objectContaining({
sourceMetadataPatch: expect.objectContaining({
fileScope: [
"packages/core/src/store.ts",
"packages/engine/src/scheduler.ts",
"packages/dashboard/**",
],
intentSignature: expect.objectContaining({
filePaths: [
"packages/core/src/store.ts",
"packages/engine/src/scheduler.ts",
],
}),
}),
}),
);
const metadataPatch = (store.updateTask as ReturnType<typeof vi.fn>).mock.calls
.map(([, patch]) => patch?.sourceMetadataPatch)
.find(Boolean);
expect(metadataPatch.fileScope).not.toContain("AtlasNotes.xcodeproj/**");
expect(metadataPatch.fileScope).not.toContain(".fusion/fusion.db");
expect(metadataPatch.fileScope).not.toContain("Packages/*/Package.resolved");
expect(metadataPatch.fileScope).not.toContain(".changeset/*.md");
expect(metadataPatch.intentSignature.filePaths).not.toContain("AtlasNotes.xcodeproj/**");
});
it("updates malformed metadata title from prompt heading when task ID matches", async () => {
await writeFile(
join(rootDir, ".fusion", "tasks", "FN-001", "PROMPT.md"),