323 lines
14 KiB
Markdown
323 lines
14 KiB
Markdown
---
|
|
name: memory-audit-maintenance
|
|
kind: responsibility
|
|
id: 067NC4KG01RG50R40M30E20918
|
|
---
|
|
|
|
### Goal
|
|
|
|
Autonomously audit and reorganize an agent's native memory (MEMORY.md, USER.md, SOUL.md), categorize entries, redistribute them to correct locations, verify live configurations, and free character budget all without user intervention unless a truly unresolvable ambiguity exists. Every agent runs this contract for **its own** memory files only no cross-agent access, no shared state.
|
|
|
|
### Requires
|
|
|
|
(none this is a self-driven, autonomous node)
|
|
|
|
### Maintains
|
|
|
|
Memory health state current utilization, last audit timestamp, drift alerts, and any unresolved ambiguities. Postcondition: no entry is duplicated, no session note or stale snapshot survives, no config is unverified, and the audit ledger is up-to-date.
|
|
|
|
#### utilization
|
|
Current character utilization of MEMORY.md and USER.md. Material: the percentages.
|
|
|
|
#### last_audit
|
|
Timestamp of the most recent successful audit. Material: the timestamp.
|
|
|
|
#### unresolved
|
|
Any entries that could not be autonomously categorized. Material: the entry text and the reason for ambiguity.
|
|
|
|
#### drift_alerts
|
|
Active alerts from drift detection (e.g., deletions accelerating, unverified configs growing). Material: the alert list.
|
|
|
|
#### ledger
|
|
Persistent audit trail stored at `~/.hermes/memories/MEMORY_AUDIT_LEDGER.md`. Material: append-only, never modified or deleted.
|
|
|
|
### Continuity
|
|
|
|
- self-driven: re-audit every 14 days (2 weeks)
|
|
- input-driven: triggered by explicit user request ("memory audit", "memory maintenance", "clean up memory", "memory is full")
|
|
- threshold: trigger immediately if either store exceeds 85% utilization
|
|
|
|
### Parameters
|
|
|
|
- `memory_threshold`: percentage that triggers an immediate audit (default: 85)
|
|
- `max_memory_chars`: maximum allowed characters for MEMORY.md (default: 2200)
|
|
- `max_user_chars`: maximum allowed characters for USER.md (default: 1375)
|
|
- `compress_entries`: whether to rewrite entries for brevity (default: true)
|
|
- `dry_run`: whether to only report changes without applying them (default: false)
|
|
|
|
### Strategies
|
|
|
|
- **Autonomous first:** Never ask the user about entries that clearly belong in a deletion or move category. Only escalate on true ambiguity.
|
|
- **Incremental over nuclear:** Always use patch-level edits (find-and-replace) rather than wholesale file rewrites. If the patch tool fails, fall back to rewrite only as a last resort.
|
|
- **Verify before trusting:** Every technical config in MEMORY.md must be verified against the live system. If a service is unreachable, mark it as `UNVERIFIED` rather than deleting it.
|
|
- **Stale state has a shelf life:** Any entry marked as "Temporary State" (e.g., "verbal offer", "deal pending", "negotiation phase") is deleted if older than 14 days.
|
|
- **Work around tool failures:** If the `memory` tool fails after 2 retries, add a summary marker and report the stale state in the unresolved list.
|
|
- **Privacy isolation:** Each agent only reads and writes its own memory files. No cross-agent memory access, no shared ledger, no shared state. The contract is the standard; each agent runs it independently.
|
|
|
|
### Invariants
|
|
|
|
- No entry is deleted without being categorized first
|
|
- No duplicate entries survive across MEMORY.md and USER.md
|
|
- No session note or temporary state survives past 14 days
|
|
- No config is trusted without verification (or marked UNVERIFIED)
|
|
- The audit ledger is append-only never modified or deleted
|
|
- The canary entry survives every audit unmodified
|
|
|
|
### Execution
|
|
|
|
```prose
|
|
# ============================================================
|
|
# PHASE 0: Canaries & Privacy
|
|
# ============================================================
|
|
|
|
# Check canaries if either is missing or modified, raise CRITICAL alert
|
|
memory_canary = read_file_line("~/.hermes/memories/MEMORY.md", containing="CANARY: Do not modify this line")
|
|
user_canary = read_file_line("~/.hermes/memories/USER.md", containing="CANARY: Do not modify this line")
|
|
|
|
if memory_canary is null or user_canary is null:
|
|
return {
|
|
status: "CRITICAL",
|
|
alert: "CANARY BREACH memory integrity compromised",
|
|
details: {
|
|
memory_canary_intact: memory_canary is not null,
|
|
user_canary_intact: user_canary is not null
|
|
}
|
|
}
|
|
|
|
# This agent only touches its own files no cross-agent access
|
|
# Confirm we are operating on this agent's own memory directory
|
|
if not directory_exists("~/.hermes/memories"):
|
|
return {status: "ERROR", reason: "Memory directory not found for this agent"}
|
|
|
|
# ============================================================
|
|
# PHASE 1: Pre-flight & Ledger
|
|
# ============================================================
|
|
|
|
# Read the audit ledger for drift detection
|
|
ledger = read_file("~/.hermes/memories/MEMORY_AUDIT_LEDGER.md")
|
|
last_audit_entry = parse_last_entry(ledger)
|
|
|
|
# Verify the memory tool works
|
|
try:
|
|
memory_status = call check_memory_tool
|
|
catch:
|
|
memory_status = "BROKEN"
|
|
|
|
# Read current state
|
|
memory_content = read_file("~/.hermes/memories/MEMORY.md")
|
|
user_content = read_file("~/.hermes/memories/USER.md")
|
|
|
|
# Calculate current utilization
|
|
memory_util = len(memory_content) / max_memory_chars * 100
|
|
user_util = len(user_content) / max_user_chars * 100
|
|
|
|
# Check if audit is needed
|
|
if last_audit_entry.timestamp > 14 days and memory_util < 75 and user_util < 75:
|
|
return {status: "SKIP", reason: "No audit needed"}
|
|
|
|
# ============================================================
|
|
# PHASE 1b: Drift Detection (Compare to Last Audit)
|
|
# ============================================================
|
|
|
|
drift_alerts = []
|
|
|
|
if last_audit_entry exists:
|
|
# Deletions accelerating? (50% increase from last audit)
|
|
if last_audit_entry.deletions > 0:
|
|
deletion_increase = (memory_util - last_audit_entry.memory_util_after) / last_audit_entry.deletions * 100
|
|
if deletion_increase > 50:
|
|
drift_alerts.append("ALERT: Memory bloat accelerating deletions increasing by " + deletion_increase + "% vs last audit")
|
|
|
|
# Unverified configs growing?
|
|
if last_audit_entry.unverified > 0:
|
|
unverified_increase = count_entries(memory_content, "[UNVERIFIED]") - last_audit_entry.unverified
|
|
if unverified_increase > 2:
|
|
drift_alerts.append("ALERT: Unverified configs growing " + unverified_increase + " new unverified entries since last audit")
|
|
|
|
# Audit missed? (No audit for >21 days)
|
|
if last_audit_entry.timestamp > 21 days:
|
|
drift_alerts.append("ALERT: Audit gap no audit completed in 21+ days")
|
|
|
|
# Utilization not improving? (After audit, still >70%)
|
|
if last_audit_entry.memory_util_after > 70:
|
|
drift_alerts.append("ALERT: Previous audit failed to reduce MEMORY.md below 70% capacity issue")
|
|
|
|
# ============================================================
|
|
# PHASE 2: Categorize every entry
|
|
# ============================================================
|
|
|
|
categorized = []
|
|
for entry in split_entries(memory_content):
|
|
cat = call categorize_entry(entry)
|
|
categorized.append({entry, category: cat, source: "MEMORY.md"})
|
|
|
|
for entry in split_entries(user_content):
|
|
cat = call categorize_entry(entry)
|
|
categorized.append({entry, category: cat, source: "USER.md"})
|
|
|
|
# ============================================================
|
|
# PHASE 3: Identify actions
|
|
# ============================================================
|
|
|
|
actions = []
|
|
for item in categorized:
|
|
if item.category in ["Session Note", "Temporary State"]:
|
|
# Check age of temporary state
|
|
if item.category == "Temporary State" and item.age < 14 days:
|
|
continue # Too young to delete
|
|
actions.append({action: "DELETE", entry: item.entry, reason: item.category})
|
|
elif item.category == "Historical Snapshot":
|
|
actions.append({action: "DELETE", entry: item.entry, reason: "Stale"})
|
|
elif item.category == "Technical Config" and item.source == "USER.md":
|
|
actions.append({action: "MOVE", from: "USER.md", to: "MEMORY.md", entry: item.entry})
|
|
elif item.category == "Identity" or item.category == "Preference" and item.source == "MEMORY.md":
|
|
actions.append({action: "MOVE", from: "MEMORY.md", to: "USER.md", entry: item.entry})
|
|
elif item.category == "Operating Rule":
|
|
actions.append({action: "MOVE", from: "MEMORY.md", to: "Skill file", entry: item.entry})
|
|
|
|
# Check for duplicates
|
|
for entry_a, entry_b in find_duplicates(categorized):
|
|
actions.append({action: "DELETE", entry: entry_b.entry, reason: "Duplicate of " + entry_a.entry})
|
|
|
|
# ============================================================
|
|
# PHASE 4: Verify live configs
|
|
# ============================================================
|
|
|
|
for item in categorized:
|
|
if item.category == "Technical Config" and item.entry contains URL:
|
|
result = call verify_endpoint(item.entry)
|
|
if result == "UNREACHABLE":
|
|
# Mark as UNVERIFIED, do not delete
|
|
actions.append({action: "MARK_UNVERIFIED", entry: item.entry})
|
|
elif result == "VERIFIED":
|
|
pass # Good, nothing to do
|
|
|
|
for item in categorized:
|
|
if item.category == "Technical Config" and item.entry contains "model:":
|
|
result = call verify_model(item.entry)
|
|
if result == "MISMATCH":
|
|
actions.append({action: "CORRECT", entry: item.entry, corrected: result.new_value})
|
|
|
|
# ============================================================
|
|
# PHASE 5: Apply changes
|
|
# ============================================================
|
|
|
|
if dry_run:
|
|
return {status: "DRY_RUN", actions: actions, drift_alerts: drift_alerts, summary: generate_summary(actions)}
|
|
|
|
# Apply deletions
|
|
for action in actions:
|
|
if action.action == "DELETE":
|
|
try:
|
|
memory action=remove target=action.source old_text=action.entry
|
|
catch after 2 retries:
|
|
patch path=action.source old_string=action.entry new_string=""
|
|
# Add summary marker since memory tool is broken
|
|
memory action=add target=action.source content="Entry deleted via patch: " + action.entry
|
|
|
|
# Apply moves
|
|
for action in actions:
|
|
if action.action == "MOVE":
|
|
# Delete from source
|
|
patch path=action.from old_string=action.entry new_string=""
|
|
# Add to destination
|
|
append_file path=action.to content=action.entry + "\n"
|
|
|
|
# Apply corrections
|
|
for action in actions:
|
|
if action.action == "CORRECT":
|
|
patch path="~/.hermes/memories/MEMORY.md" old_string=action.entry new_string=action.corrected
|
|
|
|
# Apply UNVERIFIED marks
|
|
for action in actions:
|
|
if action.action == "MARK_UNVERIFIED":
|
|
patch path="~/.hermes/memories/MEMORY.md" old_string=action.entry new_string=action.entry + " [UNVERIFIED]"
|
|
|
|
# ============================================================
|
|
# PHASE 6: Post-audit verification
|
|
# ============================================================
|
|
|
|
new_memory_content = read_file("~/.hermes/memories/MEMORY.md")
|
|
new_user_content = read_file("~/.hermes/memories/USER.md")
|
|
|
|
new_memory_util = len(new_memory_content) / max_memory_chars * 100
|
|
new_user_util = len(new_user_content) / max_user_chars * 100
|
|
|
|
# Verify canaries survived the audit
|
|
post_canary_check = read_file_line("~/.hermes/memories/MEMORY.md", containing="CANARY: Do not modify this line")
|
|
if post_canary_check is null:
|
|
drift_alerts.append("CRITICAL: Canary destroyed during audit memory integrity compromised")
|
|
|
|
# Add summary markers to in-memory stores
|
|
memory action=add target=memory content="MEMORY.md rewritten. Utilization: " + new_memory_util + "%."
|
|
memory action=add target=user content="USER.md rewritten. Utilization: " + new_user_util + "%."
|
|
|
|
# ============================================================
|
|
# PHASE 7: Append to Ledger
|
|
# ============================================================
|
|
|
|
ledger_entry = """
|
|
## """ + now() + """
|
|
- memory_util_before: """ + memory_util + """%
|
|
- memory_util_after: """ + new_memory_util + """%
|
|
- user_util_before: """ + user_util + """%
|
|
- user_util_after: """ + new_user_util + """%
|
|
- deletions: """ + count(actions, "DELETE") + """
|
|
- moves: """ + count(actions, "MOVE") + """
|
|
- corrections: """ + count(actions, "CORRECT") + """
|
|
- unverified: """ + count(actions, "MARK_UNVERIFIED") + """
|
|
- unresolved: """ + len(unresolved_entries) + """
|
|
- drift_alerts: """ + len(drift_alerts) + """
|
|
- canary_intact: """ + (post_canary_check is not null) + """
|
|
- status: COMPLETE
|
|
"""
|
|
|
|
append_file path="~/.hermes/memories/MEMORY_AUDIT_LEDGER.md" content=ledger_entry
|
|
|
|
# ============================================================
|
|
# PHASE 8: Report
|
|
# ============================================================
|
|
|
|
return {
|
|
status: "COMPLETE",
|
|
memory_utilization: new_memory_util,
|
|
user_utilization: new_user_util,
|
|
actions_applied: len(actions),
|
|
deletions: count(actions, "DELETE"),
|
|
moves: count(actions, "MOVE"),
|
|
corrections: count(actions, "CORRECT"),
|
|
unverified: count(actions, "MARK_UNVERIFIED"),
|
|
unresolved: unresolved_entries,
|
|
drift_alerts: drift_alerts,
|
|
last_audit: now(),
|
|
ledger_updated: true,
|
|
summary: generate_summary(actions)
|
|
}
|
|
```
|
|
|
|
### Shape
|
|
|
|
- `self`: categorize, verify configs, apply incremental patches, generate reports, drift detection, ledger management, canary checks
|
|
- `delegates`:
|
|
- `categorize_entry`: determine the category of a memory entry
|
|
- `check_memory_tool`: verify the memory tool is functional
|
|
- `verify_endpoint`: check if a URL is reachable
|
|
- `verify_model`: check if a model config matches live config.yaml
|
|
- `find_duplicates`: detect duplicate entries across files
|
|
- `generate_summary`: produce a human-readable summary of actions
|
|
|
|
### Tools
|
|
|
|
- `file:read`: read MEMORY.md and USER.md
|
|
- `file:patch`: incremental edits via find-and-replace
|
|
- `file:append`: append entries when moving between files
|
|
- `memory:action`: add/remove/replace entries in the in-memory store
|
|
- `network:curl`: verify endpoint reachability
|
|
- `cli:grep`: verify model configs against config.yaml
|
|
|
|
### Environment
|
|
|
|
- `MEMORY_PATH`: ~/.hermes/memories/MEMORY.md
|
|
- `USER_PATH`: ~/.hermes/memories/USER.md
|
|
- `CONFIG_PATH`: ~/.hermes/config.yaml
|
|
- `LEDGER_PATH`: ~/.hermes/memories/MEMORY_AUDIT_LEDGER.md |