How the Frontmatter System Functions for Memory and Fact Storage in Reasonix

Reasonix stores "facts" as plain Markdown files with YAML frontmatter metadata blocks, enabling human-readable, versioned, and backward-compatible persistent storage for auto-memory entries.

The DeepSeek-Reasonix repository implements a specialized frontmatter system that bridges structured data and free-form content. This design allows the AI assistant to save, retrieve, and migrate memories as simple files while preserving rich metadata like revision history, timestamps, and access scopes.

Core Frontmatter Parser Architecture

The frontmatter handling is centralized in internal/frontmatter/frontmatter.go. This package provides a robust, fault-tolerant parser that treats Markdown files as first-class data objects.

Split: Fence Detection and Body Extraction

The Split function detects and extracts the optional leading ----fenced YAML block. It returns a map[string]string of lower-cased keys together with the remaining body content.

// Implementation: internal/frontmatter/frontmatter.go#L27-L34
func Split(src string) (map[string]string, string)

The parser deliberately tolerates malformed input:

  • Missing opening fence → entire input treated as body
  • Unclosed fence → entire input treated as body (no partial parses)
  • Empty frontmatter → empty map, full body preserved

Decode: Typed Schema Validation

For callers requiring strict validation, Decode performs the same fence detection but unmarshals YAML into a typed struct via yaml.Node traversal:

// Implementation: internal/frontmatter/frontmatter.go#L37-L50
func Decode(src string, out interface{}) (string, error)

Low-Level Fence Handling

The helper splitRaw (lines 53-64) implements the core fence-searching logic. It locates opening --- and closing --- delimiters, with safety checks for unterminated blocks to prevent data corruption on malformed files.

YAML Flattening for Legacy Compatibility

Once raw YAML is isolated, parseYAMLFrontmatter (lines 67-86) walks the yaml.Node tree with a specific flattening strategy:

  • Nested maps: flattened to dot-notation keys (or joined paths)
  • Sequences: joined into comma-separated strings

This preserves legacy "flat" frontmatter expectations while supporting richer YAML structures. For example:

allowed-tools: ["read_file", "grep"]

becomes allowed-tools: read_file, grep in the parsed map.

Memory Subsystem Integration

The memory store (internal/memory) wraps the frontmatter parser in a thin abstraction for fact persistence.

Loading Facts from Storage

In internal/memory/store.go lines 806-810, the store implements:

func splitFrontmatter(s string) (map[string]string, string) {
    return frontmatter.Split(s)
}

When loading, the store maps normalized keys to Memory struct fields:

Frontmatter Key Memory Field
id ID
revision Revision
created_at CreatedAt
updated_at UpdatedAt
name Name
title Title
description Description
type Type
scope Scope

Because keys are lower-cased and structures flattened, the loading code uses simple map[string]string lookups without complex YAML handling.

Saving Facts to Storage

The render function in internal/memory/render.go (lines 38-77) handles serialization. It builds a memoryFrontmatter struct from the Memory object, then uses yaml.NewEncoder to generate properly escaped frontmatter:

type memoryFrontmatter struct {
    ID          string    `yaml:"id"`
    Revision    int       `yaml:"revision"`
    CreatedAt   time.Time `yaml:"created_at"`
    UpdatedAt   time.Time `yaml:"updated_at"`
    Name        string    `yaml:"name"`
    Title       string    `yaml:"title"`
    Description string    `yaml:"description"`
    Type        string    `yaml:"type"`
    Scope       string    `yaml:"scope"`
}

The rendered frontmatter block is concatenated with the body and written to disk as a complete Markdown file.

Version Compatibility Routing

The previousReleaseRoutingType function (lines 79-94) maintains backward compatibility with legacy directory layouts by inspecting type and scope fields. This ensures older Reasonix binaries correctly locate fact files after schema evolution.

Practical Code Examples

Creating a Memory File Manually

---
id: fact-123
revision: 1
created_at: 2024-01-01T12:00:00Z
updated_at: 2024-01-01T12:00:00Z
name: example-fact
title: Example Fact
description: This fact explains the frontmatter system.
type: user
scope: project
---
The body of the fact can contain any markdown you like.

Parsing an Existing Memory File

import (
    "os"
    "path/to/reasonix/internal/memory"
)

func loadMemory(path string) (*memory.Memory, error) {
    data, err := os.ReadFile(path)
    if err != nil {
        return nil, err
    }
    
    fm, body := memory.SplitFrontmatter(string(data))
    
    mem := memory.Memory{
        ID:          fm["id"],
        Revision:    parseInt(fm["revision"]),
        CreatedAt:   parseTime(fm["created_at"]),
        UpdatedAt:   parseTime(fm["updated_at"]),
        Name:        fm["name"],
        Title:       fm["title"],
        Description: fm["description"],
        Type:        memory.Type(fm["type"]),
        Scope:       memory.FactScope(fm["scope"]),
        Body:        body,
    }
    return &mem, nil
}

Rendering and Saving a Memory

mem := memory.Memory{
    ID:          "fact-123",
    Revision:    2,
    Name:        "example-fact",
    Title:       "Example Fact (updated)",
    Description: "Updated description of the frontmatter system.",
    Type:        memory.TypeUser,
    Scope:       memory.FactScopeProject,
    Body:        "New body content with **markdown**.",
}

content := memory.Render(mem, mem.Name)
err := os.WriteFile(
    store.Path(mem.Name),
    []byte(content),
    0o644,
)

Key Design Decisions

  • Human-readable format: Facts are plain Markdown files editable in any editor
  • Graceful degradation: Malformed frontmatter never corrupts fact content
  • Schema evolution: Typed serialization via yaml.v3 handles special characters safely
  • Legacy support: Flattened key structure accommodates both old and new data shapes

Summary

  • Reasonix stores memories as Markdown files with YAML frontmatter, separating metadata from content
  • The frontmatter.Split and frontmatter.Decode functions in internal/frontmatter/frontmatter.go provide fault-tolerant parsing with backward-compatible flattening
  • memory.SplitFrontmatter wraps the parser for loading, mapping normalized keys to struct fields
  • memory.Render uses yaml.NewEncoder for safe, typed serialization when saving
  • The system tolerates missing fences, unclosed blocks, and nested YAML structures without data loss

Frequently Asked Questions

What happens if a memory file has no frontmatter delimiter?

The parser treats the entire file as the body content. As implemented in splitRaw (lines 53-64), missing opening --- fences cause the full input to pass through unmodified, with an empty metadata map returned.

How does Reasonix handle special characters in metadata values?

The render function uses yaml.NewEncoder from yaml.v3, which properly escapes colons, quotes, newlines, and other YAML-significant characters. This prevents injection attacks and syntax errors in generated files.

Can I manually edit fact files without breaking Reasonix?

Yes. The frontmatter system is designed for human editability. The Split function tolerates minor formatting variations, and the flattened key structure ensures that standard YAML edits remain compatible with the parser's expectations.

What fields are required in a valid memory frontmatter?

The parser itself enforces no required fields—it returns whatever keys are present. However, the memory subsystem expects id, revision, created_at, updated_at, name, type, and scope for proper operation. Missing fields receive zero values during loading.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →