轻量级、基于文件的单用户AI代理记忆系统,仅使用Markdown文件(HOT/WARM/COLD/BUFFER 加上 SCRATCH 学习),采用分层加载,W...
File-based memory system for single-user AI agents: HOT/WARM/COLD/BUFFER + SCRATCH. Small, grep-first, zero external service dependencies (assumes standard Unix tools).
Run runtime commands from the workspace root so both memory/ and skills/ paths resolve correctly.
In a real workspace, runtime scripts live under memory/scripts/. Never use bash scripts/. Always prefix with memory/.
This skill package also ships reference scripts under skills/openclaw-memory-canonical/scripts/ for review, copying, and local testing. The bundled scripts resolve the workspace root dynamically so they can be tested from the skill folder before being copied into memory/scripts/.
Treat skills/openclaw-memory-canonical/ and memory/scripts/ as two distinct surfaces:
skills/openclaw-memory-canonical/ = packaged reference version of the skillmemory/scripts/ = deployed runtime scripts actually used by the workspaceA ClawHub install or update refreshes the packaged skill tree, but it does not automatically overwrite already-deployed memory/scripts/ files.
After every install or update of this skill, explicitly re-sync the deployed runtime scripts before trusting runtime behavior changes:
bash
mkdir -p memory/scripts
cp skills/openclaw-memory-canonical/scripts/*.sh memory/scripts/
chmod +x memory/scripts/*.sh
bash memory/scripts/health-check.sh
Until that re-sync + validation step is complete, trust memory/scripts/ for actual file operations and host-side runtime behavior, but treat this skill's Markdown instructions (Runtime Decision Tree, Mechanisms, thresholds, and routing rules) as immediately authoritative for AI planning and decision logic. A ClawHub update alone is not enough to claim deployed runtime tool behavior changed.
If you need to compare packaged vs deployed runtime before overwriting, diff them first:
bash
diff -u skills/openclaw-memory-canonical/scripts/health-check.sh memory/scripts/health-check.sh || true
If the packaged scripts changed but memory/scripts/ was not re-synced yet, trust the deployed runtime behavior until re-sync + validation are complete.
Runtime Decision Tree first for the active session.Mechanisms next for implementation rules.Core Design & Tables for routing and structure.References last for rationale, edge cases, and script internals.
If text disagrees, follow #1. Do not scan backward.On session start:
If MEMORY.md does not exist, create it with the canonical 4-line header before proceeding.
Read MEMORY.md.
If memory/scripts/ does not exist or is missing .sh files, bootstrap them explicitly: mkdir -p memory/scripts && cp skills/openclaw-memory-canonical/scripts/*.sh memory/scripts/ && chmod +x memory/scripts/*.sh.
bash memory/scripts/health-check.sh before relying on the updated runtime contract.bash memory/scripts/health-check.sh preflight cannot run because the script is missing or not executable, do not substitute an informal manual pass/fail judgment. First bootstrap or re-sync memory/scripts/ from skills/openclaw-memory-canonical/scripts/, then rerun bash memory/scripts/health-check.sh and trust that real result.If memory/working-buffer.md does not exist, create it with the standard working-buffer header and current timestamps before proceeding.
Read memory/working-buffer.md. Immediately update its last_active: line to the current ISO-8601 timestamp.
Clear stale locks, then handle memory/working-buffer.md.pending:
If memory/working-buffer.md.lock exists, delete it unconditionally.
If .pending does not exist, go to step 7.
If .pending exists:
.pending.# Working Buffer; line 2 created: followed by a valid ISO-8601 timestamp; line 3 last_active: followed by a valid ISO-8601 timestamp; line 4 blank. Any deviation in field names, order, timestamp validity, or the required blank line fails recovery; otherwise continue to the recovery action..learnings/ERRORS.md, notify the user, discard .pending, overwrite memory/working-buffer.md with a fresh header, and go to step 7.memory/working-buffer.md from .pending, then discard .pending.- after ## Active Task: that contain non-whitespace content.last_active is more than 2 hours old) or there are no active task lines, append its body (skip the 4-line header) to today's episodic file with a [RECOVERED: STALE] marker, ensuring the 4-line closure block remains the absolute final lines of the episodic file, then overwrite memory/working-buffer.md with a fresh header.If .pending did not exist and memory/working-buffer.md last_active is more than 2 hours old, append its body (skip the 4-line header) to today's episodic file with a [RECOVERED: STALE] marker, ensuring the 4-line closure block remains the absolute final lines of the episodic file, then overwrite memory/working-buffer.md with a fresh header.
Read episodic notes for today and yesterday, then continue with the recent-context scan.
Scan the last 20 lines of .learnings/ERRORS.md, .learnings/LEARNINGS.md, and .learnings/FEATURE_REQUESTS.md if those files exist. Stop after either: (a) applying one relevant lesson that matches the current task, or (b) scanning all three files once. If nothing relevant exists, emit nothing.
Steps 1-9 define the mandatory base context. For any additional memory needed beyond this base, use targeted grep searches on demand. Do not proactively load other memory files. If a targeted grep returns zero matches, stop searching and proceed with the available context.
At session end:
memory/working-buffer.md contains active task lines (bullet-list lines starting with - after ## Active Task: that contain non-whitespace content), run the Buffer Rotation Procedure, then finish session cleanup. Skip the health-check pre-flight here, because rotation is the designated remediation for the buffer-length invariant.Before lesson promotion or MEMORY.md rewrite (pre-commit gate):
Run bash memory/scripts/health-check.sh. If it FAILs, fix the reported invariant before continuing.
Re-scan .learnings/* only if one of these is true:
Do not re-scan for small follow-up questions, clarifications, or minor edits on the same artifact; otherwise continue with the current context.
If a prior lesson clearly matches the current task, apply it, then continue with the adjusted plan.
If nothing matches, continue silently. Stop after either applying one relevant lesson or confirming that nothing relevant matched.
Before writing any important fact:
memory/working-buffer.md first.After a failure, correction, or missing capability:
.learnings/ file.nothing found or other no-op activity.When working-buffer.md grows too large mid-session:
## Active Task: OR more than 80 lines.grep -c '^-[[:space:]]' memory/working-buffer.md and wc -l memory/working-buffer.md instead of estimating manually.Buffer oversized; deferring weekly review until session-end rotation to today's episodic note. Do not interrupt active work.If health-check.sh fails:
Memory integrity check failed. Run bash memory/scripts/health-check.sh.When searching memory:
MEMORY.md is always in context.memory/semantic/ and memory/procedural/ first.memory/episodic/ only when needed.3 types, 4 primary tiers + SCRATCH, 10 mechanisms that fit in a terminal.
TypeWhatLivesEpisodicWhat happenedmemory/episodic/YYYY-MM-DD.mdSemanticWhat I knowmemory/semantic/*.mdProceduralHow to do thingsmemory/procedural/*.md
TierFilesWhen to loadHOTMEMORY.md (≤60 lines)Every sessionWARMsemantic/*.md, procedural/*.mdBy context (grep tags + metadata freshness)COLDepisodic/, archive/On queryBUFFERworking-buffer.mdDuring active taskSCRATCH.learnings/*.mdSession start (scan recent relevant lines)
text
MEMORY.md ← HOT, ≤60 lines, always loaded
├── .learnings/ ← SCRATCH, recent failures/corrections/capability gaps
│ ├── ERRORS.md
│ ├── LEARNINGS.md
│ └── FEATURE_REQUESTS.md
├── memory/
│ ├── episodic/ ← COLD, one per day
│ │ └── YYYY-MM-DD.md ← events, decisions, signals
│ ├── semantic/ ← WARM, one per domain
│ │ ├── infrastructure.md ← IP, ports, services
│ │ ├── preferences.md ← style, rules, habits
│ │ ├── projects.md ← active projects
│ │ └── decisions.md ← final decisions with why
│ ├── procedural/ ← WARM, how-to + lessons
│ │ ├── lessons-learned.md ← post-mortems, anti-patterns
│ │ └── (other how-to files)
│ ├── working-buffer.md ← BUFFER, active task scratchpad
│ ├── archive/YYYY/MM/ ← episodic >30 days, auto-moved
│ ├── archive/learnings/YYYY/MM/ ← SCRATCH entries >30 days without promotion
│ └── scripts/
│ ├── archive-old-episodic.sh ← episodic TTL archiver
│ ├── archive-old-learnings.sh← legacy SCRATCH TTL archiver
│ ├── atomic-write.sh ← crash-safe file writes
│ └── health-check.sh ← invariant validation
markdown
# Agent Memory
#tags: <frozen-vocabulary>
last_verified: YYYY-MM-DD
last_updated: YYYY-MM-DD
Only these 4 lines are mandatory. Header validation is exact: line 1 MUST be # Agent Memory; line 2 MUST start with #tags: followed by frozen-vocabulary tags; lines 3-4 MUST be last_verified: and last_updated: with ISO dates. The body contains ≤56 lines of durable context. Never exceed 60 lines total.
Every file in memory/semantic/*.md and memory/procedural/*.md should include this minimal header shape:
markdown
# <title>
#tags: <frozen-vocabulary>
> last_verified: YYYY-MM-DD
The leading > blockquote marker on last_verified is the documented reference format. The shipped health-check.sh also accepts plain last_verified: for backward compatibility. If you intentionally remove that compatibility path later, update the script and this skill text together in the same change.
Before answering with important info (decisions, corrections, facts), write to working-buffer.md first. Then reply. Files survive restarts.
Working buffer header format (v4.1+):
markdown
# Working Buffer
created: 2026-04-05T14:31:00+08:00
last_active: 2026-04-05T14:31:00+08:00
## Active Task:
- ...
Header enables:
last_active > 2 hours and no current session working on it → abandoned bufferlast_active to the current ISO timestamp on every buffer append or session resumeHeartbeat invariant: After loading or modifying memory/working-buffer.md, immediately update the last_active: line to the current ISO-8601 timestamp. Do not treat it as static metadata. The 2-hour stale checks and weekly-review session gate depend on this heartbeat.
How to update last_active safely: Use an in-place line replacement and verify the result:
bash
sed -i "s/^last_active:.*/last_active: $(date -Iseconds)/" memory/working-buffer.md
grep '^last_active:' memory/working-buffer.md
If sed -i is unavailable or you prefer a full rewrite, use atomic-write.sh with the complete updated file content.
This is a design rule, not a runtime branch table. Use it to decide where a fact belongs. Do not treat it as a step-by-step workflow. When a fact could plausibly fit multiple homes, choose the most specific home based on its primary nature; if it is still ambiguous, default to today's episodic note and add a #routing-note: that explains the ambiguity for weekly review.
Each class of fact has exactly one home. Never duplicate:
Fact typeLives inIP, ports, servicessemantic/infrastructure.mdPreferences, style, rulessemantic/preferences.mdCommands, how-toprocedural/*.mdEvents, decisions, timelineepisodic/YYYY-MM-DD.mdPost-mortems, anti-patternsprocedural/lessons-learned.mdFinal decisions with rationalesemantic/decisions.md
Prevent corrupted files on crash. Prefer the bundled script:
bash
echo "content" | bash memory/scripts/atomic-write.sh /path/to/file.md
Rule: write the complete temp file first. Replace the target file only after the temp file is complete. See references for full fallback details.
End every episodic note with 4 lines:
text
Updated: YYYY-MM-DD
Decisions: what was changed
Signal: what I learned about the system
Open: remaining items (or "none")
Decision = what I changed. Signal = what I learned.
Archive files older than N days into archive/YYYY/MM/:
bash
bash memory/scripts/archive-old-episodic.sh 30 # default 30
.learnings/)#Auto-archive only legacy or non-canonical scratch note files older than 30 days:
memory/archive/learnings/YYYY/MM/trash > rm)#pinned are exempt until manually unpinnedERRORS.md, LEARNINGS.md, FEATURE_REQUESTS.mdbash memory/scripts/archive-old-learnings.sh 30 for the legacy/non-canonical archival passIf working-buffer.md.lock exists, abort rotation. Recovery first.
Write working-buffer.md.lock.
Append the buffer body (skip the exact 4-line header: # Working Buffer, created: ..., last_active: ..., blank line) to today's episodic note without displacing the closure block.
tail -n 1 memory/episodic/YYYY-MM-DD.md | grep -q '^Open:'.atomic-write.sh: reassemble (a) the pre-closure body of today's episodic file, (b) the appended working-buffer body, and (c) the original 4-line closure block, in that order, then write the rebuilt full file atomically before continuing.Overwrite memory/working-buffer.md with a fresh header.
Remove memory/working-buffer.md.lock unconditionally.
Use wc -l memory/working-buffer.md when you need a deterministic line count before deciding whether rotation is required.
Recovery: If working-buffer.md.pending exists, recover it before any other operation. If working-buffer.md.lock exists at session start, delete it unconditionally before proceeding with .pending recovery or staleness evaluation.
Trigger contract: weekly review is a semi-autonomous maintenance flow scheduled for Monday 9:00. A human may also invoke the same checklist manually when needed. It performs deterministic maintenance automatically but requires manual confirmation for promotions. Treat both paths as the same review procedure; the cron schedule is the default trigger, not a separate behavior.
Minimal safe v1 weekly review should automate only deterministic maintenance plus candidate detection.
Checklist (run at the scheduled Monday 9:00 review or when manually prompted):
working-buffer.md.lock exists, delete it unconditionally before evaluating the session gate.working-buffer.md has last_active less than 2 hours ago, skip/defer review, log Active session detected, and stopBuffer oversized, and stop until rotation happens firstmemory/scripts/health-check.sh; if the script is missing or not executable, first bootstrap/re-sync memory/scripts/ from skills/openclaw-memory-canonical/scripts/, then rerun bash memory/scripts/health-check.sh. If the rerun FAILs, stop review and fix health first.bash memory/scripts/archive-old-episodic.sh 30.learnings/ archival (>30 days): bash memory/scripts/archive-old-learnings.sh 30.learnings/ERRORS.md and .learnings/LEARNINGS.md as [PROMOTE-CANDIDATE]. Normalize by lowercasing and collapsing whitespace before comparing. If no helper script automates this check, skip this step entirely; do not attempt manual line-by-line comparison.Updated: YYYY-MM-DD, Decisions: weekly review performed (or the real maintenance action), Signal: <what the review learned>, Open: none (or the real remaining item). If a valid closure block already exists at the file end, overwrite those final 4 lines in place with the weekly-review state; otherwise append a fresh 4-line closure block.Do not do these automatically in v1:
last_verified refreshMEMORY.md rewriteIf nothing meaningful changed → skip with "No meaningful changes".
Before lesson promotion or MEMORY.md rewrite, run:
bash
bash memory/scripts/health-check.sh
If the script is missing or not executable, first bootstrap/re-sync memory/scripts/ from skills/openclaw-memory-canonical/scripts/, then rerun bash memory/scripts/health-check.sh before proceeding.
Health check should cover at least: MEMORY.md header validity, tags on line 2, frozen vocabulary, WARM-file > last_verified: freshness, exact working-buffer 4-line header validity ( # Working Buffer, created:, last_active:, blank line), buffer <80 lines, closure blocks, and pending/rotating files absent.
Returns exit code 0 if all required checks pass, 1 if any fail.
Treat health-check.sh as a separate script in the reference implementation, not as implicit model behavior.
If health-check.sh fails:
bash memory/scripts/health-check.sh.bash memory/scripts/health-check.sh --verbose before making changes.Buffer rotation is exempt from this pre-flight check, because buffer rotation is the designated remediation for the buffer-length invariant.
Example of a relevant lesson match: if the current task is fixing a Docker build and .learnings/ERRORS.md contains a lesson about a Docker build flag that fixes the same failure mode, apply it. If the lesson is unrelated to the current task, ignore it.
Every WARM file has #tags: on line 2:
bash
grep -rl "qdrant" memory/ # all files mentioning qdrant
grep -rl "#tags:.*networking" memory/ # files tagged networking
head -2 memory/**/*.md | grep "#tags:" # discover all tags
Capture only lessons that should change future behavior.
Write immediately after:
Use these files:
.learnings/ERRORS.md → what failed, why, and the fix attempt.learnings/LEARNINGS.md → what was wrong and the better way.learnings/FEATURE_REQUESTS.md → the missing capability, impact, and desired behaviorPromotion workflow (zero auto-promotion):
Flag as [PROMOTE-CANDIDATE] when one of these is true:
remember this, make this permanent, make permanent, or add to knowledgePromote to durable memory only after explicit user approval or manual weekly review confirmation.
If nothing meets the criteria, keep it in .learnings/.
Target mapping for promotion:
memory/procedural/<topic>.md only after 3 or more promotions on the same topicmemory/semantic/*.mdmemory/procedural/lessons-learned.md (always, not generic procedural)memory/semantic/decisions.mdDo not promote:
infrastructure: tailscale, tailnet, docker, synapse, matrix, coturn, qdrant, ubuntu, systemd, ufw, fail2ban, samba, ssh, networking, gateway, pm2 preferences: rules, style, workflow, memory, habits projects: audit, skills, security, review procedural: deepseek, puppeteer, benchmark, troubleshooting, tasks, checklist, backup, diagnostics lessons: postmortem, errors, fixes, anti-patterns
Do not invent new tags.
If a task takes >5 minutes of waiting/googling → note it inline with #friction: tag in the working buffer. Weekly review may record that friction exists, but in the minimal safe v1 it should not auto-promote friction into lessons-learned.md without manual review.
health-check.sh validates tags against the frozen vocabulary after writes, but it does not prevent a bad tag from being written in the first placehealth-check.sh> last_verified:, while health-check.sh also accepts plain last_verified: to avoid breaking older fileshealth-check.sh currently uses date -d, so non-GNU environments may need a synchronized portability patch before relying on the script as-istrash or archive/. Never rm..learnings/ files.nothing found, no-op, or scan metadata.wc -l for line-count checks instead of manual estimates when a threshold matters.atomic-write.sh for full-file rewrites).When the asked scope includes packaging or publication, complete all accepted version-metadata, documentation, and release-evidence updates that belong inside the hashed package tree first, then run one deterministic package-validation pass after the final accepted patch:
bash
cd skills/openclaw-memory-canonical
find . -type f \
-not -path './.clawhub/*' \
-not -path './.logs/*' \
-not -path './.sessions/*' \
-not -path './.profile/*' \
-not -path './node_modules/*' \
-not -path './dist/*' \
-not -path './references/package-tree.sha256' \
-not -name '*.tmp' \
-not -name '*.pending' \
-not -name '*.lock' \
| sort | xargs sha256sum > references/package-tree.sha256
Record that command and its observed result in references/verification-evidence.md and references/reference-test-log.md before claiming the artifact is ready to publish.
Chronology rule: finalize every hashed package file first, generate references/package-tree.sha256 last among in-package content updates, and do not edit any hashed package files after that generation before publish.
See references/design-rationale.md for the full decision trail from the 6-round multi-orchestrator pass that shaped the current publish-ready system.
See UPGRADE.md for the installed-runtime re-sync contract after ClawHub installs or updates.
See references/verification-evidence.md, references/reference-test-log.md, and references/package-tree.sha256 for the current release evidence.