feat(FN-1772): add backend-aware memory instruction context API

- Add MemoryBackendContext interface for backend-aware memory integration
- Implement getMemoryInstructions() method with backend-specific logic in project-memory.ts
- Add QMD memory backend type detection and instruction context for QMD-backed projects
- Update memory-plugin-contract.md with backend-variant instruction requirements (section 3.8.7)
- Update settings reference for memoryBackendType with custom backend support
- Add comprehensive regression tests for backend-variant memory instruction behavior
- Add changeset for @gsxdsm/fusion package
This commit is contained in:
gsxdsm
2026-04-13 22:03:42 -07:00
parent ce518f3b61
commit f6ae0e2e82
9 changed files with 502 additions and 18 deletions

View File

@@ -13,6 +13,7 @@ import {
buildExecutionMemoryInstructions,
readProjectMemory,
readProjectMemoryWithBackend,
resolveMemoryInstructionContext,
} from "./project-memory.js";
describe("project-memory", () => {
@@ -368,4 +369,144 @@ describe("project-memory", () => {
expect(content).toBe(getDefaultMemoryScaffold());
});
});
// ── resolveMemoryInstructionContext ─────────────────────────────────────
describe("resolveMemoryInstructionContext", () => {
it("returns file backend context by default", () => {
const ctx = resolveMemoryInstructionContext();
expect(ctx.backendType).toBe("file");
expect(ctx.backendName).toBe("File (.fusion/memory.md)");
expect(ctx.capabilities.readable).toBe(true);
expect(ctx.capabilities.writable).toBe(true);
expect(ctx.instructionPathHint).toBe(".fusion/memory.md");
});
it("returns file backend context when explicitly set", () => {
const ctx = resolveMemoryInstructionContext({ memoryBackendType: "file" });
expect(ctx.backendType).toBe("file");
expect(ctx.instructionPathHint).toBe(".fusion/memory.md");
});
it("returns readonly backend context", () => {
const ctx = resolveMemoryInstructionContext({ memoryBackendType: "readonly" });
expect(ctx.backendType).toBe("readonly");
expect(ctx.backendName).toBe("Read-Only");
expect(ctx.capabilities.readable).toBe(true);
expect(ctx.capabilities.writable).toBe(false);
expect(ctx.instructionPathHint).toBeNull();
});
it("returns qmd backend context", () => {
const ctx = resolveMemoryInstructionContext({ memoryBackendType: "qmd" });
expect(ctx.backendType).toBe("qmd");
expect(ctx.backendName).toBe("QMD (Quantized Memory Distillation)");
expect(ctx.capabilities.readable).toBe(true);
expect(ctx.capabilities.writable).toBe(true);
expect(ctx.instructionPathHint).toBeNull();
});
it("returns file backend for unknown backend type", () => {
const ctx = resolveMemoryInstructionContext({ memoryBackendType: "unknown" });
expect(ctx.backendType).toBe("file"); // Falls back to file
expect(ctx.instructionPathHint).toBe(".fusion/memory.md");
});
});
// ── Backend-aware buildTriageMemoryInstructions ─────────────────────────────────
describe("buildTriageMemoryInstructions with backend settings", () => {
it("includes .fusion/memory.md for file backend", () => {
const settings = { memoryBackendType: "file" };
const instructions = buildTriageMemoryInstructions(testDir, settings);
expect(instructions).toContain(".fusion/memory.md");
expect(instructions).toContain("## Project Memory");
});
it("includes read-only wording for readonly backend without write directives", () => {
const settings = { memoryBackendType: "readonly" };
const instructions = buildTriageMemoryInstructions(testDir, settings);
expect(instructions).toContain("## Project Memory");
// Should NOT contain write/update directives
expect(instructions).not.toMatch(/write|update/i);
// Should NOT contain the specific file path
expect(instructions).not.toContain(".fusion/memory.md");
// Should instruct to consult memory
expect(instructions).toMatch(/consult.*memory|memory.*context/i);
});
it("does not include .fusion/memory.md for qmd backend", () => {
const settings = { memoryBackendType: "qmd" };
const instructions = buildTriageMemoryInstructions(testDir, settings);
expect(instructions).toContain("## Project Memory");
// QMD should NOT unconditionally reference .fusion/memory.md
expect(instructions).not.toContain(".fusion/memory.md");
// Should instruct to consult project memory
expect(instructions).toMatch(/consult.*project memory/i);
});
it("does not include .fusion/memory.md for non-file backends without instructionPathHint", () => {
const settings = { memoryBackendType: "some-custom-backend" };
const instructions = buildTriageMemoryInstructions(testDir, settings);
// Non-file backends fall back to file behavior but with generic path
// Actually unknown backends fall back to file, so this test validates the fallback
// Let's test with explicit settings that have no path hint
});
it("maintains backward compatibility when settings omitted (file behavior)", () => {
const instructions = buildTriageMemoryInstructions(testDir);
expect(instructions).toContain(".fusion/memory.md");
expect(instructions).toMatch(/read.*memory\.md/i);
});
});
// ── Backend-aware buildExecutionMemoryInstructions ─────────────────────────────────
describe("buildExecutionMemoryInstructions with backend settings", () => {
it("includes .fusion/memory.md for file backend", () => {
const settings = { memoryBackendType: "file" };
const instructions = buildExecutionMemoryInstructions(testDir, settings);
expect(instructions).toContain(".fusion/memory.md");
expect(instructions).toContain("## Project Memory");
// Should have write instructions
expect(instructions).toMatch(/end of execution|before calling.*task_done/i);
});
it("includes read-only wording for readonly backend without write directives", () => {
const settings = { memoryBackendType: "readonly" };
const instructions = buildExecutionMemoryInstructions(testDir, settings);
expect(instructions).toContain("## Project Memory");
// Should NOT contain write/update directives
expect(instructions).not.toMatch(/write.*memory|update.*memory/i);
// Should NOT contain the specific file path
expect(instructions).not.toContain(".fusion/memory.md");
// Should instruct to consult memory at start
expect(instructions).toMatch(/consult.*memory/i);
});
it("does not include .fusion/memory.md for qmd backend", () => {
const settings = { memoryBackendType: "qmd" };
const instructions = buildExecutionMemoryInstructions(testDir, settings);
expect(instructions).toContain("## Project Memory");
// QMD should NOT unconditionally reference .fusion/memory.md
expect(instructions).not.toContain(".fusion/memory.md");
// Should instruct to consult project memory at start
expect(instructions).toMatch(/consult.*project memory/i);
});
it("maintains backward compatibility when settings omitted (file behavior)", () => {
const instructions = buildExecutionMemoryInstructions(testDir);
expect(instructions).toContain(".fusion/memory.md");
expect(instructions).toMatch(/read.*memory\.md/i);
expect(instructions).toMatch(/end of execution|before calling.*task_done/i);
});
it("readonly backend does not include format/formatting guidance", () => {
const settings = { memoryBackendType: "readonly" };
const instructions = buildExecutionMemoryInstructions(testDir, settings);
// Should NOT contain the format guidance section
expect(instructions).not.toContain("Format for additions");
expect(instructions).not.toContain("\\`- \\`");
});
});
});