How the reflection pass works
ThereflectAndCleanMemory function in src/nuggets/rewrite.ts is the entry point for the daily pass:
ReflectionResult with counts of what changed:
What it does
The pass runs five operations in sequence:Select notes to inspect
Notes are sorted by a quality score that prioritizes the most in-need notes first: notes that are older, lack tags, or lack links score higher. The pass inspects at most
MEMORY_REFLECTION_MAX_NOTES notes (default: 10) per run.Archive low-value notes
A note is considered low-value if:
- Its content is empty
- Its content is 2 characters or fewer and it has never been recalled
- Its title starts with
tmp,temp, orscratchand it has zero hits
hidden=true, archivedAt timestamp recorded). They remain in the graph file and can be recovered by setting hidden=false.Normalize stale content
For each surviving note, the content is normalized: trailing whitespace is removed, consecutive blank lines are collapsed, and duplicate lines are deduplicated. If the normalized content differs from the stored content, the note is updated and its
lastRewrittenAt timestamp is set.Improve tags
Tags are derived from the note’s scope, type, title, and content tokens. The derivation adds structured tags like
scope:user and type:fact, and content-derived tags for notes about files, preferences, or reflections. If the derived tag set differs from the current tags, the note is updated.Merge near-duplicates
Notes within the same nugget are compared pairwise using Jaccard similarity on their content tokens. If two notes share 90% or more of their tokens and have the same
subject, scope, and type, the shorter one is merged into the longer one. The merged-away note is soft-deleted with a link back to the surviving note.When it runs
The reflection pass is triggered in two ways:| Trigger | Timing | Source |
|---|---|---|
| Scheduled cron job | MEMORY_REFLECTION_HOUR:MEMORY_REFLECTION_MINUTE (default: 09:00) | src/gateway/cron.ts |
| Heartbeat fallback | During waking hours if the scheduled job was missed | src/gateway/heartbeat.ts |
Configuration env vars
| Variable | Default | Description |
|---|---|---|
MEMORY_REFLECTION_HOUR | 9 | Hour (0–23) when the daily pass runs |
MEMORY_REFLECTION_MINUTE | 0 | Minute within the hour |
MEMORY_REFLECTION_MAX_NOTES | 10 | Maximum notes inspected per run |
Promotion to MEMORY.md
ThepromoteFacts function in src/nuggets/promote.ts runs alongside the reflection pass and handles promotion of high-value notes to MEMORY.md:
- A note must have
hidden=false - A note must have
hits >= 3(recalled in at least 3 separate sessions) - The note’s title and content are written to
MEMORY.mdunder a section determined by its subject and scope
promote.ts:
MEMORY.md is structured with ## sections and bullet entries:
learnings and preferences first, then alphabetically. The file is written atomically to prevent corruption.
Triggering manually
You can invoke the reflection pass directly from code or from a Pi extension:.pi/extensions/proactive.ts), reflectAndCleanMemory is exposed as the reflectAndCleanMemory tool and can be called by the agent during the daily maintenance window.
The ReflectionChange record
Every action taken by the pass is recorded as a ReflectionChange:
changes array in ReflectionResult gives you an audit trail of every transformation made during a run.