Skip to content

Angelo — LLM Agent Guide

This file is for AI agents that interact with the angelo codebase. It describes the current storage model, upgrade procedure, and key conventions.

Storage Model (v1.0 — git-canonical)

Memory entries are stored as markdown files with YAML frontmatter inside .memory/:

.memory/
    config.yaml          # format version, migration metadata
    project.md           # root project node
    entries/<id>.md      # research tree entries
    skills/<name>.md     # procedural/pattern knowledge
    sessions/<id>.md     # session handoff records
    active-sessions/     # live session sidecars, gitignored
    documents/<id>.md    # registered document references
    archive/entries/     # vacuumed (archived) entries

Runtime state and derived caches live in the gitignored .angelo/ directory (.angelo/memory.kgl, .angelo/memory.index.json, .angelo/memory.embeddings.json, coordinator state, zettelkasten notes). The KGLite cache is disposable and rebuilt from .memory/ files on startup. The .memory/ directory is the source of truth. Legacy root-level cache files (pre-.angelo layout) are auto-migrated into .angelo/ on server startup.

Live sessions are tracked separately from finalized handoffs. While an MCP process is running, it writes .memory/active-sessions/<session-id>.json with heartbeat/process metadata and lists of entries created, accessed, and modified. The dashboard reads these sidecars for live session cards. A daemon heartbeat keeps idle but healthy MCP sessions fresh; stale sidecars from crashed processes are shown as stale and are not counted as live.

Commit Policy

Entry file writes are committed to git in debounced batches (5-second window) by the memory-mcp server. These commits are authored as memory-mcp <memory@local>.

Auto-push rule: the server will only push if all unpushed commits on the current branch are authored by memory-mcp. It never pushes user WIP commits.

File-pin commits (for the files parameter in record()) still happen synchronously and immediately, but the server first classifies the dirty working tree as clean, memory_only, relevant, unrelated, or risky.

Use the files parameter only when you intentionally want reproducible pinned file references. Unrelated unstaged work is tolerated and reported as a warning; unrelated staged work blocks auto-committing dirty requested files with a DirtyWorkingTreeError. If a pin is refused, commit or unstage the unrelated work, or omit files and mention paths in the entry body.

After git pull

The server auto-syncs: before each tool call it hash-diffs .memory/ against the last-seen index (at most once per AUTO_SYNC_INTERVAL_SECONDS, default 10s) and rebuilds the cache when another process or a git pull changed files. Set MEMORY_AUTO_SYNC=0 to disable.

Manual sync() is still available as a forced, un-throttled rebuild — useful right after a pull when you don't want to wait out the check interval, or when auto-sync is disabled.

If the dashboard shows stale or missing memory data after a pull, run sync() first, then health() to confirm the cache, counts, and git state.

Upgrade Procedure

From .kgl-only to .memory/ files

  1. Update the package:
pip install --upgrade git+https://github.com/natashahirt/angelo
  1. Update each target repo (every repo where you use Angelo memory):
cd /path/to/your/repo
angelo update

This copies the latest rule files (.cursor/rules/memory.mdc) and ensures the MCP server config is current.

  1. Restart Cursor (or restart the MCP server) so the new server code loads.

  2. On first startup, the server auto-detects the old .memory.kgl file and migrates it to .memory/ files. Migration is verified automatically. Legacy root-level cache files are moved into .angelo/ on startup, so the repo root stays clean.

  3. Commit the new .memory/ directory:

git add .memory/
git commit -m "chore: migrate memory to file-based storage"

Verifying the migration

After restarting, call the health() MCP tool. Check that: - cache_stale is false - counts match your expected entry/skill/session counts - format_version is "1.0"

Maintenance tools

Use these tools for file-backed memory maintenance:

Tool Purpose
sync() Force-rebuild cache from .memory/ files (the server also auto-syncs external changes before tool calls)
health() Report system health: cache freshness, counts, unpushed commits, install info
vacuum(days_old?, dry_run?) Archive discarded entries older than N days and rebuild the cache

Live session troubleshooting

If the Sessions dashboard does not show the current chat:

  1. Restart Cursor or the MCP server so the latest memory server code is running.
  2. Confirm .memory/active-sessions/ contains a sidecar for the live process.
  3. Call health() to check memory system state.
  4. If the card is stale, the MCP process likely stopped or is old enough that it does not emit heartbeats. Restart the MCP server.

Experiment Artifacts & Cloud Reruns (per-repo)

Every repo that uses Angelo gets its own, fully isolated artifact setup. Nothing is shared between repos: each has its own DVC remote, manifests, and job queue.

.memory/artifacts/
    manifests/<run-id>.yaml             # provenance per run (code SHA, hashes, command, env)
    experiments/<family>/runs/<stamp>/  # per-run output files (DVC-tracked, never overwritten)
    catalogs/                           # human-readable S3 catalog exports
.dvc/config                             # tracked: remote URL, region, profile name
.dvc/config.local                       # gitignored: machine-local overrides/secrets

Key facts for agents:

  • Per-repo queue: the rerun job queue is derived from that repo's DVC remote (<remote URL>/jobs), overridable with ANGELO_JOBS_URI. Repos never see each other's jobs.
  • Runs are a series: runs sharing an experiment family id group into one dashboard card; rerun manifests carry rerun_of, executor, run_stamp, and a replication verdict (exact when output hashes match the original).
  • Sandbox reruns: experiment(action='rerun') checks out the original commit into a git worktree under .angelo/sandboxes/, pulls inputs by content hash, runs the manifest command, and writes a new dated run manifest. where="cloud" enqueues for a runner; where="local" executes in-process.
  • Runner: angelo-runner --repo <path> polls the queue on any machine with bucket credentials and git push access (one runner process per repo). Installed with the [artifacts] extra (pip install "angelo[artifacts]").
  • Backups: artifact(action='backup', path) is the one-step pattern for "keep my local file but make it restorable anywhere" (dvc add + push + verify).
  • Setup in a new repo: configure the remote once via the configure_s3_remote MCP tool (or dvc remote add), commit .dvc/config, and everything above works.

Key Conventions

  • One project per repo: create_project only runs once. Use subproject plan nodes for separate workstreams.
  • Tree hierarchy: Project → Subproject (plan) → Phase (plan) → Entries.
  • Entry types: plan, checkpoint, experiment, decision, note, annotation, todo.
  • File pinning: pass files in record() to auto-commit and pin references to a git SHA.
  • Vacuum: archives discarded entries older than N days, then rebuilds the cache.
  • Live sessions: .memory/active-sessions/ is ephemeral runtime state and should stay gitignored. Final session records live in .memory/sessions/.