Files
prose-contracts/memory-audit-maintenance.prose.md
T
root 52566ff36e
PR Pipeline — Authorize → Validate → Review → Merge / auth (push) Successful in 2s
PR Pipeline — Authorize → Validate → Review → Merge / validate (push) Successful in 2s
PR Pipeline — Authorize → Validate → Review → Merge / lint (push) Successful in 3s
PR Pipeline — Authorize → Validate → Review → Merge / ai-review (push) Failing after 3s
PR Pipeline — Authorize → Validate → Review → Merge / gate (push) Has been skipped
fix: remove test file, add missing descriptions to zulip-health + memory-audit-maintenance
2026-07-04 23:06:40 +00:00

15 KiB

name, kind, description, id
name kind description id
memory-audit-maintenance responsibility Shared memory audit and maintenance contract for all Hermes agents (Mumuni, Tanko, Koby, Koonimo). Each agent runs it against its own isolated memory files — no cross-agent access, no shared state. Detects staleness, enforces writer registry, and rotates canary tokens. 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)

Scope

This contract is the Hermes Agent standard for memory maintenance. It is shared across all Hermes agents (Mumuni, Tanko, Koby, Koonimo). Each agent runs it against its own memory files only no cross-agent access, no shared state, no shared ledger, no shared canary. The contract is the standard; each agent enforces it independently with fully isolated data.

Agent Roster:

  • Mumuni
  • Tanko
  • Koby
  • Koonimo

Isolation Principle: Each agent has its own:

  • MEMORY.md and USER.md
  • MEMORY_AUDIT_LEDGER.md (audit trail)
  • MEMORY_WRITERS.md (writer registry)
  • Canary entries
  • No data crosses agent boundaries

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 the agent's own ~/.hermes/memories/MEMORY_AUDIT_LEDGER.md. Material: append-only, never modified or deleted. Each agent has its own ledger no shared ledger across agents.

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, and is isolated per agent
  • The canary entry survives every audit unmodified
  • No data crosses agent boundaries

Execution

# ============================================================
# 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

Per-Agent Notes

Each Hermes agent (Mumuni, Tanko, Koby, Koonimo) runs this contract against its own ~/.hermes/memories/ directory. The contract is identical across agents, but all data is fully isolated: separate ledgers, separate writer registries, separate canaries. If a new agent is added to the roster, it must be listed in ### Scope above and given its own isolated memory directory.