Add continuity-guard skill skeleton and doctor script

This commit is contained in:
2026-03-08 11:19:28 +10:00
parent 9f2cd76891
commit 01ca393531
4 changed files with 320 additions and 0 deletions
+92
View File
@@ -0,0 +1,92 @@
---
name: continuity-guard
description: Maintain short-term continuity and anti-silence discipline for OpenClaw agents using memory/CURRENT_STATE.md plus dual reporting rules. Use when creating/updating agent workspaces, after OpenClaw upgrades/restarts, when continuity seems broken, when a session may have lost context, or when you need to doctor/repair CURRENT_STATE coverage and reporting protocol drift across agents.
---
# Continuity Guard
Use `memory/CURRENT_STATE.md` as a small overwrite-oriented workbench, not as a journal.
## Core rules
- Ensure every agent workspace has `memory/CURRENT_STATE.md`
- Keep `CURRENT_STATE.md` small
- main: target 25-40 lines, hard cap 50
- other agents: target 15-25 lines, hard cap 30
- Required sections:
- `In Flight`
- `Blocked / Waiting`
- `Recently Finished`
- `Next`
- `Reset Summary`
- Update only on state changes, not on a timer
- Remove stale items instead of endlessly appending
## Dual reporting protocol
### Worker → main
Execution agents must report to main at:
- accepted
- blocked
- milestone
- done
- model/environment abnormal
Preferred reply format:
- `status`
- `summary`
- `evidence`
- `risk`
- `next`
### main → Tao
Main must report to Tao at:
- task accepted / formally started
- worker dispatched
- blocked
- milestone reached
- task/phase completed
Preferred Tao update format:
- who
- status
- output
- next
Ordering rule:
- When a worker reports milestone/completion/blocker, first update `CURRENT_STATE.md`, then update Tao, then continue with review/commit/next dispatch.
- If no evidence point exists yet (sessionKey / commit / branch / PR / log), do not claim work has already started.
## When to use the doctor
Run `scripts/continuity_doctor.py` when:
- OpenClaw was upgraded
- gateway restarted and continuity feels suspicious
- an agent seems to have lost short-term context
- you need to confirm `CURRENT_STATE.md` coverage across workspaces
## How to use the doctor
From the main workspace:
```bash
python3 skills/continuity-guard/scripts/continuity_doctor.py \
--main-workspace /Users/taodeng/.openclaw/workspace/main \
--agents-root /Users/taodeng/.openclaw/workspaces
```
The doctor checks:
- main `memory/CURRENT_STATE.md` exists
- all agent workspaces have `memory/CURRENT_STATE.md`
- required sections exist
- line-count caps are respected
- `AGENTS.md` still contains continuity + dual reporting rules
## Repair strategy
If drift is found:
1. Restore/create missing `memory/CURRENT_STATE.md`
2. Restore continuity guidance in `AGENTS.md`
3. Re-run doctor
4. Only then investigate deeper behavioral failures
## References
- For template and limits: read `references/template.md`
- For doctor semantics and PASS/WARN/FAIL meanings: read `references/doctor-spec.md`
@@ -0,0 +1,34 @@
# continuity doctor spec
## PASS
The checked item exists and matches the required structure.
## WARN
The item exists but is drifting:
- file too long
- required text partly missing
- structure present but not ideal
## FAIL
The item is missing or materially broken:
- missing CURRENT_STATE.md
- missing required sections
- missing continuity rules in AGENTS.md
## Minimal checks
1. `main/memory/CURRENT_STATE.md` exists
2. each agent workspace has `memory/CURRENT_STATE.md`
3. every `CURRENT_STATE.md` contains:
- `## In Flight`
- `## Blocked / Waiting`
- `## Recently Finished`
- `## Next`
- `## Reset Summary`
4. main `AGENTS.md` contains continuity guidance and dual reporting protocol markers
5. line-count caps are respected
## Suggested actions on failure
- create missing files from template
- restore missing AGENTS continuity section
- trim oversized CURRENT_STATE files
- rerun doctor after repair
@@ -0,0 +1,51 @@
# CURRENT_STATE template
## main agent (expanded)
```md
# CURRENT_STATE
_Last updated: YYYY-MM-DD HH:MM Australia/Brisbane_
## In Flight
- [status] task — owner — latest milestone
## Blocked / Waiting
- item — blocker / waiting on
## Recently Finished
- result — why it still matters now
## Next
- next action
## Reset Summary
- one short paragraph explaining what matters if a new session starts now
```
Capacity:
- target 25-40 lines
- hard cap 50 lines
- In Flight max 5
- Blocked / Waiting max 5
- Recently Finished max 3
- Next max 5
## other agents (standard)
Use the same template, but keep it smaller.
Capacity:
- target 15-25 lines
- hard cap 30 lines
- In Flight max 3
- Blocked / Waiting max 3
- Recently Finished max 2
- Next max 3
## Allowed status values
- planned
- dispatched
- in_progress
- blocked
- reviewing
- done
+143
View File
@@ -0,0 +1,143 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
from pathlib import Path
REQUIRED_SECTIONS = [
'## In Flight',
'## Blocked / Waiting',
'## Recently Finished',
'## Next',
'## Reset Summary',
]
AGENTS_MARKERS = [
'CURRENT_STATE.md - Your Short-Term Workbench',
'Dual reporting protocol',
'Execution agent → main',
'main → Tao',
]
def result(level: str, msg: str) -> tuple[str, str]:
print(f'{level:<5} {msg}')
return level, msg
def line_count(path: Path) -> int:
try:
return len(path.read_text().splitlines())
except Exception:
return 0
def has_sections(path: Path) -> list[str]:
text = path.read_text()
missing = [s for s in REQUIRED_SECTIONS if s not in text]
return missing
def main() -> int:
ap = argparse.ArgumentParser(description='Check continuity/CURRENT_STATE coverage and drift.')
ap.add_argument('--main-workspace', required=True)
ap.add_argument('--agents-root', required=True)
args = ap.parse_args()
main_ws = Path(args.main_workspace)
agents_root = Path(args.agents_root)
failures = 0
warns = 0
print('continuity doctor\n')
# main AGENTS.md
agents_md = main_ws / 'AGENTS.md'
if not agents_md.exists():
failures += 1
result('FAIL', f'missing {agents_md}')
else:
text = agents_md.read_text()
missing = [m for m in AGENTS_MARKERS if m not in text]
if missing:
failures += 1
result('FAIL', f'AGENTS continuity markers missing: {", ".join(missing)}')
else:
result('PASS', 'AGENTS continuity rules present')
# main CURRENT_STATE
main_cs = main_ws / 'memory' / 'CURRENT_STATE.md'
if not main_cs.exists():
failures += 1
result('FAIL', 'main CURRENT_STATE missing')
else:
missing = has_sections(main_cs)
if missing:
failures += 1
result('FAIL', f'main CURRENT_STATE missing sections: {", ".join(missing)}')
else:
result('PASS', 'main CURRENT_STATE sections present')
n = line_count(main_cs)
if n > 50:
warns += 1
result('WARN', f'main CURRENT_STATE too long: {n} lines (cap 50)')
else:
result('PASS', f'main CURRENT_STATE length ok: {n} lines')
# agent workspaces
checked = 0
missing_files = []
missing_sections = []
oversize = []
if agents_root.exists():
for ws in sorted([p for p in agents_root.iterdir() if p.is_dir()]):
checked += 1
cs = ws / 'memory' / 'CURRENT_STATE.md'
if not cs.exists():
missing_files.append(ws.name)
continue
missing = has_sections(cs)
if missing:
missing_sections.append((ws.name, missing))
n = line_count(cs)
if n > 30:
oversize.append((ws.name, n))
if missing_files:
failures += 1
result('FAIL', f'agent CURRENT_STATE missing: {", ".join(missing_files)}')
else:
result('PASS', f'agent CURRENT_STATE files present: {checked}/{checked}')
if missing_sections:
failures += 1
details = '; '.join(f'{name}: {", ".join(m)}' for name, m in missing_sections)
result('FAIL', f'agent CURRENT_STATE missing sections: {details}')
else:
result('PASS', 'agent CURRENT_STATE sections present')
if oversize:
warns += 1
details = '; '.join(f'{name}: {n} lines' for name, n in oversize)
result('WARN', f'agent CURRENT_STATE oversize: {details}')
else:
result('PASS', 'agent CURRENT_STATE length caps respected')
print('\nSummary')
print(f'- failures: {failures}')
print(f'- warnings: {warns}')
if failures:
print('\nSuggested actions:')
print('1. create/restore missing CURRENT_STATE files')
print('2. restore AGENTS continuity section if missing')
print('3. trim oversized CURRENT_STATE files')
print('4. rerun continuity doctor')
return 2
return 0
if __name__ == '__main__':
raise SystemExit(main())