Install
openclaw skills install openclaw-memory-canonicalLightweight file-based memory system for single-user AI agents. Uses markdown files only: HOT/WARM/COLD/BUFFER plus SCRATCH learnings, with tiered loading, WAL-lite writes, compaction recovery, weekly review, TTL archival, health checks, and a built-in self-improvement loop. Use when setting up or improving agent memory, preventing memory bloat or repeated mistakes, promoting recurring lessons into durable files, validating memory health, or when users mention memory systems, working buffers, episodic/semantic/procedural memory, learnings, weekly review, self-improvement, or how an AI agent should remember and improve between sessions.
openclaw skills install openclaw-memory-canonicalFile-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:
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:
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:
MEMORY.md does not exist, create it with the canonical 4-line header before proceeding.MEMORY.md.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.memory/working-buffer.md does not exist, create it with the standard working-buffer header and current timestamps before proceeding.memory/working-buffer.md. Immediately update its last_active: line to the current ISO-8601 timestamp.memory/working-buffer.md.pending:
memory/working-buffer.md.lock exists, delete it unconditionally..pending does not exist, go to step 7..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..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..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.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):
bash memory/scripts/health-check.sh. If it FAILs, fix the reported invariant before continuing..learnings/* only if one of these is true:
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.
| Type | What | Lives |
|---|---|---|
| Episodic | What happened | memory/episodic/YYYY-MM-DD.md |
| Semantic | What I know | memory/semantic/*.md |
| Procedural | How to do things | memory/procedural/*.md |
| Tier | Files | When to load |
|---|---|---|
| HOT | MEMORY.md (≤60 lines) | Every session |
| WARM | semantic/*.md, procedural/*.md | By context (grep tags + metadata freshness) |
| COLD | episodic/, archive/ | On query |
| BUFFER | working-buffer.md | During active task |
| SCRATCH | .learnings/*.md | Session start (scan recent relevant lines) |
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
# 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:
# <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+):
# 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:
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 type | Lives in |
|---|---|
| IP, ports, services | semantic/infrastructure.md |
| Preferences, style, rules | semantic/preferences.md |
| Commands, how-to | procedural/*.md |
| Events, decisions, timeline | episodic/YYYY-MM-DD.md |
| Post-mortems, anti-patterns | procedural/lessons-learned.md |
| Final decisions with rationale | semantic/decisions.md |
Prevent corrupted files on crash. Prefer the bundled script:
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:
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 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 passworking-buffer.md.lock exists, abort rotation. Recovery first.working-buffer.md.lock.# 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.memory/working-buffer.md with a fresh header.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 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:
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):
[PROMOTE-CANDIDATE] when one of these is true:
remember this, make this permanent, make permanent, or add to knowledge.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:
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.