Files
fusion/docs/memory-plugin-contract.md
2026-04-19 10:13:33 -07:00

14 KiB

Fusion Memory Plugin Contract

← Docs index

This document is the source-of-truth contract for Fusion memory backend behavior after the FN-2087 migration series.


Table of Contents

  1. Current Fusion Memory Baseline
  2. OpenClaw Research Findings
  3. Fusion Memory Plugin Contract
  4. Migration Strategy + Compatibility Guardrails
  5. Downstream Task Alignment

1. Current Fusion Memory Baseline

1.1 Two Memory Systems (Distinct Responsibilities)

Fusion currently has two related but distinct memory systems:

  1. Layered memory backend system (memory-backend.ts + project-memory.ts)
    • Handles agent-facing read/write/get/search for project memory
    • Canonical layered workspace under .fusion/memory/
    • Uses pluggable backends (file, qmd, readonly, custom)
  2. Insight extraction system (memory-insights.ts)
    • Runs scheduled extraction and pruning workflows
    • Reads working memory and writes extracted insights/audit artifacts
    • Independent automation lifecycle and settings

1.2 Canonical Constants and Paths

Constant Value Source Module
MEMORY_WORKSPACE_PATH .fusion/memory memory-backend.ts
MEMORY_LONG_TERM_FILENAME MEMORY.md memory-backend.ts
MEMORY_DREAMS_FILENAME DREAMS.md memory-backend.ts
LEGACY_MEMORY_FILE_PATH .fusion/memory.md memory-backend.ts
DEFAULT_MEMORY_BACKEND qmd memory-backend.ts
MEMORY_FILE_PATH .fusion/memory/MEMORY.md project-memory.ts
MEMORY_WORKING_PATH .fusion/memory/MEMORY.md memory-insights.ts
MEMORY_INSIGHTS_PATH .fusion/memory-insights.md memory-insights.ts
MEMORY_AUDIT_PATH .fusion/memory-audit.md memory-insights.ts

MEMORY_FILE_PATH and MEMORY_WORKING_PATH currently resolve to the same canonical long-term file (.fusion/memory/MEMORY.md) but are owned by different modules for different concerns (backend/prompt plumbing vs insight extraction workflow).

1.3 Exported Surface (Post-Migration)

project-memory.ts exports

Export Purpose
MEMORY_FILE_PATH, memoryFilePath() Canonical long-term path helpers
getDefaultMemoryScaffold() Default long-term scaffold content
ensureMemoryFile() Filesystem bootstrap for canonical memory
ensureMemoryFileWithBackend() Backend-aware bootstrap
readProjectMemoryWithBackend() Backend-aware read helper
searchProjectMemory(), getProjectMemory() Backend-aware search/get wrappers
resolveMemoryInstructionContext() Backend-aware instruction context
buildTriageMemoryInstructions() Triage prompt memory instructions
buildExecutionMemoryInstructions() Executor prompt memory instructions
buildReviewerMemoryInstructions() Reviewer prompt memory instructions
readProjectMemory() Direct canonical long-term file read

memory-backend.ts key exports

Export Purpose
MemoryBackend, MemoryBackendCapabilities Runtime backend contract
MemoryBackendError, MemoryBackendErrorCode Typed backend error model
FileMemoryBackend, QmdMemoryBackend, ReadOnlyMemoryBackend Built-in backend implementations
registerMemoryBackend(), getMemoryBackend(), listMemoryBackendTypes() Function-based registry API
resolveMemoryBackend() Backend resolution from settings
memoryWorkspacePath(), memoryLongTermPath(), dailyMemoryPath(), memoryDreamsPath() Canonical layered path helpers
ensureOpenClawMemoryFiles() Layered file bootstrap + legacy seeding
listProjectMemoryFiles(), readProjectMemoryFile(), writeProjectMemoryFile() Validated layered file operations

1.4 Settings Baseline

ProjectSettings fields that govern memory behavior:

  • memoryEnabled?: boolean (default true)
  • memoryBackendType?: string (default "qmd")
  • insightExtractionEnabled?: boolean (default false)
  • insightExtractionSchedule?: string (default "0 2 * * *")
  • insightExtractionMinIntervalMs?: number (default 86400000)

Runtime backend resolution uses an internal MemorySettings shape with memoryBackendType and optional additional keys.

1.5 Key Invariants

  1. Canonical layered long-term memory file is .fusion/memory/MEMORY.md.
  2. Legacy .fusion/memory.md is compatibility-only (migration seed/legacy alias), not canonical storage.
  3. Default backend is qmd (DEFAULT_MEMORY_BACKEND).
  4. Backend selection key is memoryBackendType.
  5. Prompt instruction context is backend-aware (file path hint vs qmd/readonly behavior).
  6. Dashboard /api/memory routes are backend-aware; layered file routes validate requests against allowed memory workspace files.

2. OpenClaw Research Findings

Fusion adopted OpenClaw-style layered memory while keeping a concrete TypeScript runtime contract.

2.1 What Was Adopted

  • Layered workspace (MEMORY.md, daily files, DREAMS.md)
  • Backend abstraction with explicit capability declarations
  • Search over layered files with bounded snippets
  • Migration safety via compatibility seeding from legacy file

2.2 Implications Table

OpenClaw Concept Fusion Current State Contract Implication
Layered memory files Implemented under .fusion/memory/ Canonical source-of-truth is workspace-based, not single-file
Path abstraction Implemented via backend methods that receive rootDir (read(rootDir), write(rootDir, ...), etc.) Backends must resolve paths relative to project root; no global hardcoded absolute paths
Pluggable backends Implemented (file, qmd, readonly, custom) Contract must document MemoryBackend exactly as shipped
Capability negotiation Implemented via boolean-struct capabilities No enum or lifecycle-based capability API
Search Implemented through optional search(rootDir, options) hooks Results are bounded snippets, not full-document dumps
Migration compatibility Implemented via ensureOpenClawMemoryFiles() and legacy constants Legacy .fusion/memory.md remains compatibility-only

3. Fusion Memory Plugin Contract

3.1 Runtime Interface (Canonical)

export interface MemoryBackendCapabilities {
  readable: boolean;
  writable: boolean;
  supportsAtomicWrite: boolean;
  hasConflictResolution: boolean;
  persistent: boolean;
}

export interface MemoryBackend {
  readonly type: string;
  readonly name: string;
  readonly capabilities: MemoryBackendCapabilities;
  read(rootDir: string): Promise<MemoryReadResult>;
  write(rootDir: string, content: string): Promise<MemoryWriteResult>;
  get?(rootDir: string, options: MemoryGetOptions): Promise<MemoryGetResult>;
  search?(rootDir: string, options: MemorySearchOptions): Promise<MemorySearchResult[]>;
  exists?(rootDir: string): Promise<boolean>;
}

export interface MemoryBackendConfig {
  type: string;
  options?: Record<string, unknown>;
}

This is the active contract. The following are not part of current runtime semantics: initialize(), hasCapability(), flush(), shutdown(), class-based registry APIs, or lifecycle interfaces.

3.2 Error Contract

export type MemoryBackendErrorCode =
  | "NOT_FOUND"
  | "READ_ONLY"
  | "READ_FAILED"
  | "WRITE_FAILED"
  | "UNSUPPORTED"
  | "CONFLICT"
  | "QUOTA_EXCEEDED"
  | "BACKEND_UNAVAILABLE";

export class MemoryBackendError extends Error {
  readonly code: MemoryBackendErrorCode;
  readonly backend: string;
}

3.3 Built-In Backends

Backend Class Type Current Behavior
FileMemoryBackend file Reads/writes canonical .fusion/memory/MEMORY.md; supports exists/get/search; atomic writes via temp file rename
QmdMemoryBackend qmd Delegates read/write to file backend; schedules qmd refresh; uses qmd search first, local layered search fallback
ReadOnlyMemoryBackend readonly Read-only; write() throws MemoryBackendError("READ_ONLY", ...); search() returns empty

3.4 Registry Contract (Function-Based)

registerMemoryBackend(backend: MemoryBackend): void
getMemoryBackend(type: string): MemoryBackend | undefined
listMemoryBackendTypes(): string[]

Backends are stored in a module-level Map<string, MemoryBackend>. Built-ins are registered at module load.

3.5 Resolution Contract

export const MEMORY_BACKEND_SETTINGS_KEYS = {
  MEMORY_BACKEND_TYPE: "memoryBackendType",
} as const;

export const DEFAULT_MEMORY_BACKEND = "qmd";

export function resolveMemoryBackend(settings?: { memoryBackendType?: string }): MemoryBackend

Resolution chain:

  1. Configured memoryBackendType (if registered)
  2. DEFAULT_MEMORY_BACKEND ("qmd")

3.6 Layered Memory Operations

The layered memory workspace helpers in memory-backend.ts provide:

  • Workspace bootstrap: ensureOpenClawMemoryFiles()
  • File listing: listProjectMemoryFiles()
  • Validated file access: readProjectMemoryFile(), writeProjectMemoryFile()
  • Path helpers: memoryLongTermPath(), dailyMemoryPath(), memoryDreamsPath()

Allowed workspace files are constrained to:

  • .fusion/memory/MEMORY.md
  • .fusion/memory/DREAMS.md
  • .fusion/memory/YYYY-MM-DD.md

3.7 Prompt Instruction Context Contract

resolveMemoryInstructionContext(settings?) in project-memory.ts currently resolves as:

Condition backendType instructionPathHint Behavior
memoryEnabled === false disabled null No memory instructions
memoryBackendType === "file" file .fusion/memory/MEMORY.md Explicit path-aware read/write instructions
memoryBackendType === "readonly" readonly null Read-only instruction set
memoryBackendType === "qmd" or unknown qmd null Backend-aware generic instructions

3.8 File Compatibility & Source-of-Truth Semantics

3.8.1 Canonical source-of-truth

  • Canonical layered workspace: .fusion/memory/
  • Canonical long-term file: .fusion/memory/MEMORY.md
  • Legacy .fusion/memory.md: compatibility-only for migration/legacy alias handling

3.8.2 Legacy compatibility scope

Legacy path preservation is limited to compatibility behavior:

  • LEGACY_MEMORY_FILE_PATH constant
  • Migration seeding in ensureOpenClawMemoryFiles() when legacy exists and canonical long-term file is missing
  • Transition/alias handling in layered-path validation

No legacy dual-write mirror is part of the current contract.

3.8.3 Dashboard and API path constraints

Dashboard memory routes must remain rooted in project-scoped memory APIs:

  • /api/memory uses backend-aware read/write (readMemory, writeMemory)
  • /api/memory/file uses validated layered file operations (MEMORY.md, DREAMS.md, daily files)
  • Read-only backends must reject writes with READ_ONLY

3.8.4 Prompt compatibility matrix

Backend Read behavior Write behavior Instruction style
qmd (default) Delegated file read Delegated file write + qmd refresh schedule Generic backend-aware instructions, no fixed path hint
file Direct canonical file read Direct canonical file write Explicit .fusion/memory/MEMORY.md path hint
readonly Empty/non-throw read semantics Throws READ_ONLY Read-only instructions only

3.8.5 Insight extraction relationship

Insight extraction is a separate subsystem that currently uses:

  • Working source: .fusion/memory/MEMORY.md
  • Insight output: .fusion/memory-insights.md
  • Audit output: .fusion/memory-audit.md

It is related to, but not equivalent to, backend selection and prompt instruction logic.


4. Migration Strategy + Compatibility Guardrails

4.1 Migration Phase Status

Phase Task(s) Status
Core backend contract + built-ins FN-1418 Complete
Engine backend-aware prompt integration FN-1419 Complete
Dashboard backend-aware memory integration FN-1420 Complete
Path/backend reconciliation umbrella FN-2087 Complete
Reconciliation implementation slices FN-2131, FN-2132, FN-2133, FN-2134 Complete

4.2 Must-Not-Break Invariants

  1. Canonical long-term layered memory remains .fusion/memory/MEMORY.md.
  2. Legacy .fusion/memory.md remains compatibility-only and must not become canonical again.
  3. Runtime backend selection remains keyed by memoryBackendType.
  4. DEFAULT_MEMORY_BACKEND remains "qmd" unless explicitly changed in code + docs.
  5. Prompt instruction behavior remains backend-dependent via resolveMemoryInstructionContext().
  6. Missing working-memory reads degrade gracefully to empty string where contract requires.

4.3 Resolution Fallback Chain

  1. Use configured backend type if registered.
  2. Otherwise fall back to DEFAULT_MEMORY_BACKEND ("qmd").
  3. For qmd search failures/unavailability, fall back to local layered file search.

4.4 Compatibility Matrix

Concern Required Behavior
Settings persistence Unknown memoryBackendType may be persisted, but runtime still falls back safely
Read-only backend Writes fail with typed READ_ONLY error
Legacy upgrades Existing .fusion/memory.md can seed canonical .fusion/memory/MEMORY.md once during migration
Memory file APIs Must enforce workspace-relative path validation and project root boundaries
Prompt generation Must honor memoryEnabled toggle and backend-aware instruction context

4.5 Test Coverage Verification Checklist

Contract-critical behavior is covered by:

  • packages/core/src/memory-backend.test.ts
    • built-in backends (file, qmd, readonly)
    • registry helpers and resolveMemoryBackend() default/fallback behavior
    • layered workspace helpers (ensureOpenClawMemoryFiles, list/get/write, path validation)
  • packages/core/src/project-memory.test.ts
    • canonical MEMORY_FILE_PATH
    • resolveMemoryInstructionContext() branching
    • triage/execution/reviewer instruction generation per backend
  • packages/core/src/store.test.ts
    • memory bootstrap behavior when memory is enabled/disabled and toggled
  • packages/engine/src/triage.test.ts and packages/engine/src/executor.test.ts
    • memory instruction injection behavior by settings/backend

5. Downstream Task Alignment

This contract should remain aligned with:

If backend contract behavior changes in source, update these docs in the same change.


Last updated: 2026-04-19