Memory compositionality

Shipped in v3-alpha (Beta). Third zero-LLM feature — pure graph traversal + rule application. Interfaces may still change before 3.0 stable.

The philosophy

If Fact A says “Service X calls Service Y” and Fact B says “Service Y has 5s timeout”, we can infer “Service X waits up to 5s for Y” without ever storing or generating that with an LLM. Pure pattern matching on the graph.

COMPOSITION ENGINE
Forward-chaining rule engine over knowledge graph.
Zero LLM calls. Deterministic. Auditable.

Input:  Recalled facts + entity graph + relation verbs
Output: Inferred statements (confidence < 1.0) with provenance
ROI:    Every inference saves 1 LLM reasoning step

Declarative rules

interface CompositionRule {
  id: string;
  name: string;
  pattern: {
    facts:     FactPattern[];
    relations: RelationPattern[];
  };
  inference: {
    template:   string;       // "{{A.subject}} depends on {{B.subject}}"
    confidence: number;
    type:       "fact" | "warning";
  };
}

Built-in rules (excerpt)

const COMPOSITION_RULES: CompositionRule[] = [
  {
    id: "transitive_dependency",
    name: "A depends on B, B depends on C → A transitively depends on C",
    inference: {
      template: "{{A}} transitively depends on {{C}} (via {{B}})",
      confidence: 0.6,
      type: "fact",
    },
  },
  {
    id: "timeout_conflict",
    name: "Caller timeout < callee processing time → potential failure",
    inference: {
      template: "⚠️ {{A}} may timeout calling {{B}} ({{A.timeout}} < {{B.processing_time}})",
      confidence: 0.8,
      type: "warning",
    },
  },
  {
    id: "supersession_chain",
    name: "A supersedes B, B supersedes C → only A is current",
    inference: {
      template: "{{C}} is doubly superseded — only {{A}} is current truth",
      confidence: 0.95,
      type: "fact",
    },
  },
  {
    id: "shared_dependency_risk",
    name: "Multiple services depend on same entity → SPOF",
    inference: {
      template: "⚠️ {{X}} is a SPOF — {{A}}, {{B}}, {{C}} all depend on it",
      confidence: 0.75,
      type: "warning",
    },
  },
];

Custom rules per workspace

await mnemo.createCompositionRule({
  name: "team_on_call",
  pattern: {
    facts: [
      { subject: "?person",  predicate: "is_on_call", value: "true" },
      { subject: "?service", predicate: "owned_by",   value: "?person" },
    ],
  },
  inference: {
    template: "{{person}} is on-call AND owns {{service}} — they handle incidents for it",
    confidence: 0.9,
    type: "fact",
  },
});

Execution (post-recall hook)

function composeInferences(hits: RecallHit[], graph: SubGraph): ComposedInference[] {
  const inferences: ComposedInference[] = [];
  for (const rule of COMPOSITION_RULES) {
    for (const match of matchPattern(rule.pattern, hits, graph)) {
      inferences.push({
        content:    interpolate(rule.inference.template, match.bindings),
        confidence: rule.inference.confidence * avgConfidence(match.facts),
        type:       rule.inference.type,
        provenance: match.facts.map(f => f.id),
        ruleId:     rule.id,
      });
    }
  }
  return inferences.filter(i => i.confidence > 0.5);
}

ROI

Without composition:
  Agent recalls "Service X timeout = 3s" + "Service Y processing = 5s"
  → reasoning tokens to figure out "X will timeout calling Y"
  Cost: ~50–200 reasoning tokens per inference

With composition:
  Engine appends "⚠️ Service X may timeout calling Y (3s < 5s)"
  Agent reads it — no reasoning needed
  Cost: 0 tokens

At scale: 10 inferences/session × 100 tokens saved each = 1000 tokens/session

See also