v0.2: rewrite skill with discipline protocol, doctor, and archive

This commit is contained in:
dtzp555-max
2026-03-11 22:21:31 +00:00
commit 46db8ab0b9
7 changed files with 672 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 dtzp555-max
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+172
View File
@@ -0,0 +1,172 @@
# memory-continuity
OpenClaw skill for **short-term working continuity** — so your agent can pick up
exactly where it left off after a gateway crash, `/new`, model fallback, or
context compaction.
## What problem does this solve?
When your gateway dies mid-conversation, or you hit `/new` to start fresh, or
the model falls back to a different provider — your agent loses its working
context. Long-term memory can tell it *what it knows*, but not *what it was
doing*. This skill fills that gap.
**One-line summary:** Long-term memory = what you know. This skill = what you
are doing right now.
## Quick Start
### Install
```bash
# Go to your OpenClaw workspace skills directory
cd ~/.openclaw/workspace/skills/
# Clone
git clone https://github.com/dtzp555-max/memory-continuity.git
# That's it. No npm install, no API keys, no database.
```
### Test it
1. Start a conversation with your agent about a multi-step task
2. Chat for a few turns, make some decisions
3. Check: does `memory/CURRENT_STATE.md` exist in your workspace? Does it
reflect what you were doing?
4. Type `/new` to start a fresh session
5. The agent should read `CURRENT_STATE.md` and ask:
*"Last session we were working on X. Want to continue?"*
If step 5 works, the skill is doing its job.
### Run the doctor
```bash
python3 scripts/continuity_doctor.py --workspace ~/.openclaw/workspace
```
Sample output:
```
Continuity Doctor — scanning: /Users/you/.openclaw/workspace
============================================================
[OK] memory/CURRENT_STATE.md exists
[OK] CURRENT_STATE.md is fresh (0.3h old)
[OK] Template compliance: all sections present
[WARNING] Unsurfaced Results section is not empty — review needed
[INFO] Found 3 session archive(s), latest: 2026-03-12_14-30.md
Overall status: WARNING
```
## How it works
The skill installs a behavioral protocol via `SKILL.md`. When loaded, the agent
follows these rules:
1. **Session start:** Read `memory/CURRENT_STATE.md`, brief the user, wait for
confirmation
2. **During work:** Overwrite the state file at key moments (decisions,
completed steps, errors, before long tool calls, every ~10 turns)
3. **Session end / `/new`:** Final state save + archive a timestamped snapshot
The state file uses a fixed template:
```markdown
# Current State
> Last updated: 2026-03-12T14:30:00Z
## Objective
Build the user authentication module
## Current Step
Completed JWT token generation, starting refresh endpoint
## Key Decisions
- Using RS256 for token signing (user approved)
- Token expiry: 15 minutes access, 7 days refresh
## Next Action
Implement POST /auth/refresh endpoint
## Blockers
None
## Unsurfaced Results
None
```
The entire file is designed to be read in 15 seconds. It is overwritten (not
appended) on every update, keeping it permanently short.
## Architecture position
This skill occupies a specific niche. Here is how it relates to other tools:
| Layer | Tool | What it stores |
|---|---|---|
| Working state | **memory-continuity** (this skill) | What you are doing *right now* |
| Stable facts | OpenClaw native markdown memory | Preferences, decisions, knowledge |
| Retrieval | memory-lancedb-pro / similar | Searchable long-term history |
These layers are complementary. This skill has **zero dependency** on any
database or external memory plugin. It works with plain markdown files that
live in your workspace and can be backed up with `git` or `cp`.
## File structure
```
memory-continuity/
├── SKILL.md # Core skill (loaded by OpenClaw)
├── README.md # This file
├── LICENSE # MIT
├── references/
│ ├── template.md # CURRENT_STATE.md blank template
│ └── doctor-spec.md # Doctor check specifications
└── scripts/
└── continuity_doctor.py # Diagnostic tool (reports only, no auto-repair)
```
At runtime, the skill creates these files in your workspace:
```
$WORKSPACE/
└── memory/
├── CURRENT_STATE.md # Live workbench (overwritten each update)
└── session_archive/ # Timestamped snapshots from past sessions
├── 2026-03-12_14-30.md
└── ...
```
## Design principles
1. **Zero dependencies.** No database, no API, no npm packages. Just files.
2. **Backup = copy.** The entire state is in `memory/`. Back it up however you
back up your workspace.
3. **Overwrite, don't append.** CURRENT_STATE.md is a workbench, not a journal.
It stays short because it is replaced on every update.
4. **Diagnose, don't auto-repair.** The doctor script flags problems for you to
fix. Automated repair of state files is too risky at this stage.
5. **Complement, don't compete.** This skill does not replace long-term memory.
It solves a different problem (crash recovery vs knowledge retrieval).
## Current status
**v0.2 — Draft / early version**
What works:
- SKILL.md protocol with discipline rules
- CURRENT_STATE.md template
- Continuity doctor diagnostic script
- Session archive on `/new`
Planned:
- Plugin version with `command:new` and `before_agent_start` hooks
(for guaranteed save/restore without relying on agent self-discipline)
- More real-world validation across different gateway configurations
- Sub-agent continuity handover protocol
## License
MIT
+159
View File
@@ -0,0 +1,159 @@
---
name: memory-continuity
description: >
Short-term working continuity for OpenClaw agents. Preserves in-flight state
across gateway restarts, /new, model fallback, and context compaction.
Use this skill whenever an agent needs to survive session breaks without
losing what it was doing. Triggers on: session start, /new, context recovery,
"where were we", "continue", resuming work, or any situation where recent
working state may have been lost. This is NOT a long-term memory system —
it is a crash-recovery workbench.
---
# memory-continuity
Lightweight continuity layer that keeps a single overwrite-oriented state file
(`memory/CURRENT_STATE.md`) so any agent can pick up exactly where it left off
after a restart, `/new`, gateway crash, model fallback, or context compaction.
## Why this exists
Long-term memory (vector DB, markdown journals) stores *what you know*.
This skill stores *what you are doing right now*. They solve different problems.
When a gateway crashes mid-task, no amount of long-term memory recall can tell
the next session: "you were halfway through step 3, the user approved option B,
and the blocker was X." That is what CURRENT_STATE.md does.
## File layout
```
$WORKSPACE/
├── memory/
│ ├── CURRENT_STATE.md # THE workbench (overwritten, never appended)
│ └── session_archive/ # compressed snapshots from past sessions
│ ├── 2026-03-12_14-30.md
│ └── ...
```
---
## MANDATORY PROTOCOL
### 1. On every session start
```
IF memory/CURRENT_STATE.md exists:
READ it
Tell user: "Last session we were working on [Objective]. We reached [Current Step]. Want to continue?"
WAIT for user confirmation before proceeding
ELSE:
Create memory/CURRENT_STATE.md with empty template (see below)
```
### 2. When to update CURRENT_STATE.md (the discipline rules)
Update the file by **overwriting** it (not appending) at these moments:
| Trigger | Why |
|---|---|
| User confirms a decision | Decisions are the hardest thing to reconstruct |
| A task step completes | Marks progress so next session knows where to start |
| An error or blocker appears | Prevents the next session from hitting the same wall |
| Before any tool call that might take long | If gateway dies during the call, state is already saved |
| User says "let's stop here" or similar | Explicit save point |
| Every ~10 turns of substantive conversation | Periodic checkpoint against silent context loss |
**The update must be quick.** Write only what changed. The entire file should
stay under 40 lines. If you find yourself writing more, you are journaling,
not checkpointing. Stop and compress.
### 3. On `/new` or session end
Before the session closes:
1. Do a final overwrite of `memory/CURRENT_STATE.md` with latest state
2. Copy a timestamped snapshot to `memory/session_archive/YYYY-MM-DD_HH-MM.md`
3. The snapshot is a frozen record; CURRENT_STATE.md is the live workbench
If gateway crashes before you can do this, that is OK — the last mid-session
checkpoint in CURRENT_STATE.md is your recovery point. It will not be perfect,
but it will be vastly better than starting from zero.
### 4. Result surfacing rule
If you are a sub-agent or execution agent:
- Before exiting, write your key results into CURRENT_STATE.md under `## Unsurfaced Results`
- The main agent MUST check this section on startup and relay findings to the user
This prevents the #1 silent failure: sub-agent did the work, but nobody saw it.
---
## CURRENT_STATE.md Template
```markdown
# Current State
> Last updated: [ISO timestamp]
## Objective
[One sentence: what are we trying to accomplish]
## Current Step
[What step are we on, what was the last thing completed]
## Key Decisions
- [Decision 1: what was decided and why, max 3 items]
## Next Action
[Exactly what should happen next]
## Blockers
[What is preventing progress, or "None"]
## Unsurfaced Results
[Results from sub-agents or tools not yet shown to user, or "None"]
```
**Rules for this template:**
- Every field is mandatory. Write "None" rather than omitting a section.
- `Objective` and `Next Action` are the two most critical fields. If you can
only save two things before a crash, save these.
- `Key Decisions` caps at 3 items. Older decisions belong in long-term memory,
not here.
- The entire file should be readable in 15 seconds. If it takes longer, trim it.
---
## Continuity Doctor (optional)
Run `scripts/continuity_doctor.py` to check workspace health:
```bash
python3 scripts/continuity_doctor.py --workspace /path/to/workspace
```
It checks:
- Does `memory/CURRENT_STATE.md` exist?
- Is it stale (not updated in the last session)?
- Does `Objective` match any active tasks file?
- Are there `Unsurfaced Results` that were never addressed?
- Are there session archives without a corresponding state update?
The doctor **reports only** — it does not auto-repair. You decide what to fix.
---
## What this skill is NOT
- Not a long-term memory system (use OpenClaw's native markdown memory or LanceDB for that)
- Not a conversation log or journal (CURRENT_STATE.md is overwritten, not appended)
- Not a task manager (use OpenSpec or tasks.md for project planning)
- Not dependent on any external database (works with plain files only)
## Compatibility
- Works with any OpenClaw agent (main or sub-agent)
- No external dependencies (no npm install, no API keys, no database)
- Backup: `git add memory/` or `cp -r memory/ /backup/` — that is the entire disaster recovery plan
- Can coexist with memory-lancedb-pro, hippocampus, or any other memory skill
View File
+64
View File
@@ -0,0 +1,64 @@
# Continuity Doctor — Specification
## Purpose
The continuity doctor is a diagnostic tool that checks whether the
memory-continuity protocol is being followed correctly in a workspace.
It reports problems but does **not** auto-fix them.
## Design philosophy
- **Diagnose, don't repair.** Automated repair of state files is dangerous
because incorrect "fixes" can overwrite valid state. The doctor flags
issues for human or agent review.
- **Fast and offline.** No API calls, no database queries. Reads files only.
- **Exit codes matter.** 0 = healthy, 1 = warnings found, 2 = critical issues.
## Checks performed
### 1. Existence check
- Does `memory/CURRENT_STATE.md` exist?
- Severity: CRITICAL if missing (no recovery possible)
### 2. Staleness check
- When was `CURRENT_STATE.md` last modified?
- If older than the most recent session transcript, it is stale.
- Severity: WARNING
### 3. Template compliance
- Does the file contain all mandatory sections?
(Objective, Current Step, Key Decisions, Next Action, Blockers, Unsurfaced Results)
- Are any sections still showing placeholder text like `[One sentence: ...]`?
- Severity: WARNING for missing sections, INFO for placeholder text
### 4. Unsurfaced results
- Is the `Unsurfaced Results` section non-empty?
- If yes, someone needs to review those results.
- Severity: WARNING
### 5. Archive consistency
- Are there files in `memory/session_archive/` ?
- Does the newest archive have a different Objective than CURRENT_STATE.md?
(This is expected if the user switched tasks, but worth flagging.)
- Severity: INFO
### 6. Conflict detection (optional, if tasks file exists)
- If a `tasks.md` or `openspec/` directory exists, does the Objective in
CURRENT_STATE.md align with any active task?
- Severity: INFO (alignment is nice-to-have, not mandatory)
## Output format
```
[CRITICAL] memory/CURRENT_STATE.md does not exist
[WARNING] CURRENT_STATE.md is stale (last modified 2h ago, session ran 30m ago)
[WARNING] Unsurfaced Results section is not empty — review needed
[INFO] Archive objective differs from current objective (task switch?)
[OK] Template compliance: all sections present
```
## Future extensions (not yet implemented)
- Multi-workspace scan (check all sub-agent workspaces)
- JSON output mode for programmatic consumption
- Integration with OpenClaw cron for scheduled health checks
+20
View File
@@ -0,0 +1,20 @@
# Current State
> Last updated: [ISO timestamp]
## Objective
[One sentence: what are we trying to accomplish]
## Current Step
[What step are we on, what was the last thing completed]
## Key Decisions
- None yet
## Next Action
[Exactly what should happen next]
## Blockers
None
## Unsurfaced Results
None
+236
View File
@@ -0,0 +1,236 @@
#!/usr/bin/env python3
"""
continuity_doctor.py — Diagnostic tool for memory-continuity skill.
Checks workspace health and reports issues. Does NOT auto-repair.
Usage:
python3 continuity_doctor.py --workspace /path/to/workspace
python3 continuity_doctor.py --workspace ~/.openclaw/workspace/main
"""
import argparse
import os
import sys
import re
from datetime import datetime, timezone
from pathlib import Path
# ---------------------------------------------------------------------------
# Severity levels
# ---------------------------------------------------------------------------
class Severity:
OK = "OK"
INFO = "INFO"
WARNING = "WARNING"
CRITICAL = "CRITICAL"
# ---------------------------------------------------------------------------
# Collector
# ---------------------------------------------------------------------------
class DiagnosticReport:
def __init__(self):
self.entries: list[tuple[str, str]] = []
self._worst = Severity.OK
def add(self, severity: str, message: str):
self.entries.append((severity, message))
rank = {Severity.OK: 0, Severity.INFO: 1, Severity.WARNING: 2, Severity.CRITICAL: 3}
if rank.get(severity, 0) > rank.get(self._worst, 0):
self._worst = severity
def print_report(self):
for severity, message in self.entries:
tag = f"[{severity}]".ljust(12)
print(f"{tag}{message}")
print()
print(f"Overall status: {self._worst}")
@property
def exit_code(self) -> int:
if self._worst == Severity.CRITICAL:
return 2
if self._worst == Severity.WARNING:
return 1
return 0
# ---------------------------------------------------------------------------
# Required sections in CURRENT_STATE.md
# ---------------------------------------------------------------------------
REQUIRED_SECTIONS = [
"Objective",
"Current Step",
"Key Decisions",
"Next Action",
"Blockers",
"Unsurfaced Results",
]
PLACEHOLDER_PATTERNS = [
r"\[.*?\]", # anything in square brackets like [One sentence: ...]
]
# ---------------------------------------------------------------------------
# Checks
# ---------------------------------------------------------------------------
def check_existence(workspace: Path, report: DiagnosticReport) -> Path | None:
"""Check that memory/CURRENT_STATE.md exists."""
state_file = workspace / "memory" / "CURRENT_STATE.md"
if not state_file.exists():
report.add(Severity.CRITICAL, "memory/CURRENT_STATE.md does not exist")
return None
report.add(Severity.OK, "memory/CURRENT_STATE.md exists")
return state_file
def check_staleness(state_file: Path, workspace: Path, report: DiagnosticReport):
"""Check if the state file is older than the most recent activity."""
mtime = datetime.fromtimestamp(state_file.stat().st_mtime, tz=timezone.utc)
age_seconds = (datetime.now(timezone.utc) - mtime).total_seconds()
age_hours = age_seconds / 3600
if age_hours > 24:
report.add(
Severity.WARNING,
f"CURRENT_STATE.md is stale (last modified {age_hours:.1f}h ago)",
)
elif age_hours > 4:
report.add(
Severity.INFO,
f"CURRENT_STATE.md last modified {age_hours:.1f}h ago",
)
else:
report.add(Severity.OK, f"CURRENT_STATE.md is fresh ({age_hours:.1f}h old)")
def check_template_compliance(state_file: Path, report: DiagnosticReport) -> dict:
"""Check all required sections are present and not placeholder-only."""
content = state_file.read_text(encoding="utf-8")
sections_found: dict[str, str] = {}
for section in REQUIRED_SECTIONS:
# Match ## Section or ## Section\n
pattern = rf"##\s+{re.escape(section)}\s*\n(.*?)(?=\n##\s|\Z)"
match = re.search(pattern, content, re.DOTALL)
if not match:
report.add(Severity.WARNING, f"Missing section: ## {section}")
continue
body = match.group(1).strip()
sections_found[section] = body
# Check for placeholder text
if body and all(re.fullmatch(p, body) for p in PLACEHOLDER_PATTERNS):
report.add(Severity.INFO, f"Section '{section}' still contains placeholder text")
if len(sections_found) == len(REQUIRED_SECTIONS):
report.add(Severity.OK, "Template compliance: all sections present")
return sections_found
def check_unsurfaced_results(sections: dict, report: DiagnosticReport):
"""Check if there are unsurfaced results that need attention."""
results = sections.get("Unsurfaced Results", "").strip().lower()
if results and results != "none":
report.add(
Severity.WARNING,
"Unsurfaced Results section is not empty — review needed",
)
else:
report.add(Severity.OK, "No unsurfaced results pending")
def check_archive(workspace: Path, sections: dict, report: DiagnosticReport):
"""Check session archive consistency."""
archive_dir = workspace / "memory" / "session_archive"
if not archive_dir.exists() or not list(archive_dir.glob("*.md")):
report.add(Severity.INFO, "No session archives found (first session?)")
return
archives = sorted(archive_dir.glob("*.md"))
latest_archive = archives[-1]
report.add(Severity.OK, f"Found {len(archives)} session archive(s), latest: {latest_archive.name}")
# Compare objectives
current_objective = sections.get("Objective", "").strip()
archive_content = latest_archive.read_text(encoding="utf-8")
obj_match = re.search(r"##\s+Objective\s*\n(.*?)(?=\n##\s|\Z)", archive_content, re.DOTALL)
if obj_match:
archive_objective = obj_match.group(1).strip()
if archive_objective != current_objective and current_objective:
report.add(
Severity.INFO,
"Archive objective differs from current objective (task switch?)",
)
def check_tasks_alignment(workspace: Path, sections: dict, report: DiagnosticReport):
"""Optional: check if objective aligns with tasks.md if it exists."""
tasks_file = workspace / "tasks.md"
if not tasks_file.exists():
return
objective = sections.get("Objective", "").strip().lower()
if not objective:
return
tasks_content = tasks_file.read_text(encoding="utf-8").lower()
# Very rough heuristic: check if any significant word from objective appears in tasks
words = [w for w in objective.split() if len(w) > 4]
matches = sum(1 for w in words if w in tasks_content)
if words and matches == 0:
report.add(
Severity.INFO,
"Objective does not seem to match any content in tasks.md",
)
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def run_doctor(workspace_path: str) -> int:
workspace = Path(workspace_path).expanduser().resolve()
report = DiagnosticReport()
print(f"Continuity Doctor — scanning: {workspace}")
print("=" * 60)
if not workspace.exists():
report.add(Severity.CRITICAL, f"Workspace does not exist: {workspace}")
report.print_report()
return report.exit_code
# Run all checks
state_file = check_existence(workspace, report)
if state_file:
check_staleness(state_file, workspace, report)
sections = check_template_compliance(state_file, report)
check_unsurfaced_results(sections, report)
check_archive(workspace, sections, report)
check_tasks_alignment(workspace, sections, report)
print()
report.print_report()
return report.exit_code
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Continuity Doctor — diagnose memory-continuity health"
)
parser.add_argument(
"--workspace",
required=True,
help="Path to the OpenClaw workspace to check",
)
args = parser.parse_args()
sys.exit(run_doctor(args.workspace))