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

> Discover how Reasonix uses YAML frontmatter in Markdown files for human-readable, versioned, and backward-compatible storage of auto-memory facts and persistent data.

- Repository: [YHH/DeepSeek-Reasonix](https://github.com/esengine/DeepSeek-Reasonix)
- Tags: internals
- Published: 2026-08-13

---

**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](https://github.com/esengine/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`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/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.

```go
// 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:

```go
// 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:

```yaml
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`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/memory/store.go) lines 806-810, the store implements:

```go
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`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/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:

```go
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

```markdown
---
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

```go
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

```go
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`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/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.