feat(FN-1022): add project memory bootstrap with agent integration
- Add project-memory module in @fusion/core with read/write/resolve helpers and upsert-on-store hook - Bootstrap project memory during task creation (store) with structured task context - Inject resolved project memory into executor and triage agent system prompts - Add comprehensive tests for project-memory module, store integration, and agent prompt changes - Document project memory architecture and usage in README
This commit is contained in:
@@ -2163,27 +2163,48 @@ describe("buildExecutionPrompt", () => {
|
||||
});
|
||||
|
||||
describe("memoryEnabled setting", () => {
|
||||
it("accepts memoryEnabled: true without error", () => {
|
||||
it("includes memory instructions when memoryEnabled: true", () => {
|
||||
const task = createMockTaskDetail();
|
||||
const result = buildExecutionPrompt(task, "/project", {
|
||||
memoryEnabled: true,
|
||||
} as any);
|
||||
// Memory instructions are a placeholder until FN-810; just verify no crash
|
||||
expect(result).toContain("Execute this task.");
|
||||
expect(result).toContain("## Project Memory");
|
||||
expect(result).toContain(".fusion/memory.md");
|
||||
});
|
||||
|
||||
it("accepts memoryEnabled: false without error", () => {
|
||||
it("excludes memory instructions when memoryEnabled: false", () => {
|
||||
const task = createMockTaskDetail();
|
||||
const result = buildExecutionPrompt(task, "/project", {
|
||||
memoryEnabled: false,
|
||||
} as any);
|
||||
expect(result).toContain("Execute this task.");
|
||||
expect(result).not.toContain("## Project Memory");
|
||||
});
|
||||
|
||||
it("accepts undefined memoryEnabled (default enabled) without error", () => {
|
||||
it("includes memory instructions when memoryEnabled is undefined (default enabled)", () => {
|
||||
const task = createMockTaskDetail();
|
||||
const result = buildExecutionPrompt(task, "/project", {} as any);
|
||||
expect(result).toContain("Execute this task.");
|
||||
expect(result).toContain("## Project Memory");
|
||||
expect(result).toContain(".fusion/memory.md");
|
||||
});
|
||||
|
||||
it("includes append instruction for updating memory at end of execution", () => {
|
||||
const task = createMockTaskDetail();
|
||||
const result = buildExecutionPrompt(task, "/project", {
|
||||
memoryEnabled: true,
|
||||
} as any);
|
||||
expect(result).toContain("append");
|
||||
expect(result).toMatch(/end of execution|before calling.*task_done/i);
|
||||
});
|
||||
|
||||
it("uses project-root memory path not worktree-local path", () => {
|
||||
const task = createMockTaskDetail();
|
||||
const result = buildExecutionPrompt(task, "/project", {
|
||||
memoryEnabled: true,
|
||||
} as any);
|
||||
expect(result).toContain("`.fusion/memory.md`");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ import { join } from "node:path";
|
||||
import { existsSync } from "node:fs";
|
||||
import type { TaskStore, Task, TaskDetail, StepStatus, Settings, WorkflowStep, MissionStore, Slice, AgentState, AgentCapability } from "@fusion/core";
|
||||
import type { AgentStore } from "@fusion/core";
|
||||
import { buildExecutionMemoryInstructions } from "@fusion/core";
|
||||
import { findWorktreeUser } from "./merger.js";
|
||||
import { generateWorktreeName, slugify } from "./worktree-names.js";
|
||||
import { Type, type Static } from "@mariozechner/pi-ai";
|
||||
@@ -3024,13 +3025,10 @@ git log --oneline
|
||||
|
||||
// Build project memory section from settings
|
||||
// When enabled, agents consult and update .fusion/memory.md for durable project learnings.
|
||||
// Actual memory instructions will be injected by FN-810; this placeholder establishes
|
||||
// the conditional integration point.
|
||||
const memoryEnabled = settings?.memoryEnabled !== false;
|
||||
let memorySection = "";
|
||||
if (memoryEnabled && rootDir) {
|
||||
// TODO(FN-810): Call buildMemoryInstructions(rootDir) to populate memory context
|
||||
memorySection = "";
|
||||
memorySection = "\n" + buildExecutionMemoryInstructions(rootDir);
|
||||
}
|
||||
|
||||
// Build steering comments section (last 10 comments only to avoid context bloat)
|
||||
|
||||
@@ -217,7 +217,7 @@ describe("buildSpecificationPrompt", () => {
|
||||
});
|
||||
|
||||
describe("memoryEnabled setting", () => {
|
||||
it("accepts memoryEnabled: true without error", () => {
|
||||
it("includes memory instructions when memoryEnabled: true", () => {
|
||||
const settings: Settings = {
|
||||
maxConcurrent: 2,
|
||||
maxWorktrees: 4,
|
||||
@@ -231,11 +231,12 @@ describe("buildSpecificationPrompt", () => {
|
||||
".fusion/tasks/KB-001/PROMPT.md",
|
||||
settings,
|
||||
);
|
||||
// Memory instructions are a placeholder until FN-810; just verify no crash
|
||||
expect(prompt).toContain("Specify this task");
|
||||
expect(prompt).toContain("## Project Memory");
|
||||
expect(prompt).toContain(".fusion/memory.md");
|
||||
});
|
||||
|
||||
it("accepts memoryEnabled: false without error", () => {
|
||||
it("excludes memory instructions when memoryEnabled: false", () => {
|
||||
const settings: Settings = {
|
||||
maxConcurrent: 2,
|
||||
maxWorktrees: 4,
|
||||
@@ -250,15 +251,18 @@ describe("buildSpecificationPrompt", () => {
|
||||
settings,
|
||||
);
|
||||
expect(prompt).toContain("Specify this task");
|
||||
expect(prompt).not.toContain("## Project Memory");
|
||||
});
|
||||
|
||||
it("accepts undefined memoryEnabled (default enabled) without error", () => {
|
||||
it("includes memory instructions when memoryEnabled is undefined (default enabled)", () => {
|
||||
const prompt = buildSpecificationPrompt(
|
||||
baseTask,
|
||||
".fusion/tasks/KB-001/PROMPT.md",
|
||||
undefined,
|
||||
);
|
||||
expect(prompt).toContain("Specify this task");
|
||||
expect(prompt).toContain("## Project Memory");
|
||||
expect(prompt).toContain(".fusion/memory.md");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import type {
|
||||
TaskAttachment,
|
||||
Settings,
|
||||
} from "@fusion/core";
|
||||
import { buildTriageMemoryInstructions } from "@fusion/core";
|
||||
import type { ImageContent } from "@mariozechner/pi-ai";
|
||||
import { Type, type Static } from "@mariozechner/pi-ai";
|
||||
import type {
|
||||
@@ -1252,13 +1253,10 @@ export function buildSpecificationPrompt(
|
||||
|
||||
// Build project memory section from settings.
|
||||
// When enabled, agents consult .fusion/memory.md for durable project learnings.
|
||||
// Actual memory instructions will be injected by FN-810; this placeholder
|
||||
// establishes the conditional integration point.
|
||||
const memoryEnabled = settings?.memoryEnabled !== false;
|
||||
let memorySection = "";
|
||||
if (memoryEnabled) {
|
||||
// TODO(FN-810): Call buildMemoryInstructions(rootDir) to populate memory context
|
||||
memorySection = "";
|
||||
memorySection = "\n\n" + buildTriageMemoryInstructions("");
|
||||
}
|
||||
|
||||
let attachmentsSection = "";
|
||||
|
||||
Reference in New Issue
Block a user