tools/sage-claude-plugin/skills/sage-ontology/SKILL.md
Typed knowledge graph stored in sage-memory. Use when creating or querying structured entities (Person, Project, Task, Event, Document), linking related objects, checking dependencies, planning multi-step actions as graph transformations, or when skills need to share structured state. Trigger on "remember that X is Y", "what do I know about", "link X to Y", "show dependencies", "what blocks X", entity CRUD, cross-skill data access, or any request involving structured relationships between things.
npx skillsauth add xoai/sage sage-ontologyInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Typed knowledge graph. Entities stored as memories, relationships stored as edges — searchable, traversable, persistent.
Part of the unified knowledge system. Ontology entries surface alongside regular knowledge and self-learning entries during recall.
| Capability | MCP | Files |
|------------|-----|-------|
| Create entities | ✅ sage_memory_store | ✅ .sage-memory/ont-*.md files |
| Search entities | ✅ BM25 + tag filter | ⚠️ filename scan + read |
| Update entities | ✅ sage_memory_update | ✅ edit file |
| Delete entities | ✅ CASCADE removes edges | ✅ delete file + clean relations |
| Create relations | ✅ sage_memory_link | ✅ relations: in entity file |
| Multi-hop traversal | ✅ sage_memory_graph | ❌ not available |
| Cycle detection | ✅ built into graph tool | ⚠️ manual trace |
| Browse by type | ✅ sage_memory_list | ✅ scan ont- files |
How to detect backend: At session start, call sage_memory_set_project
with the project root path. If it responds, use MCP. If not, use
.sage-memory/ files.
Agent-driven extraction (0.9+). Ontology entities are populated
primarily by passing entities and relations to sage_memory_store
from the calling agent:
sage_memory_store(
content: "...",
entities: [{name: "PaymentOrchestrator", type: "CONCEPT"}],
relations: [{from: "PaymentOrchestrator", to: "Stripe", rel: "depends_on"}]
)
The background worker is a fallback that runs only when an LLM API key is configured AND the agent didn't pass an extraction payload. Prefer the agent-driven path: your model has the full conversational context, the worker only sees the stored content.
Entities hold properties. Relations connect two entities with a typed, directed edge.
Entity: [Task:task_a1b2] "Fix payment timeout"
Relation: task_a1b2 —blocks→ task_f3a4
With MCP:
sage_memory_store(
title: "[Task:task_a1b2] Fix payment timeout in checkout flow",
content: '{"id":"task_a1b2","type":"Task","properties":{"title":"Fix payment timeout","status":"open","priority":"high"}}',
tags: ["ontology", "entity", "task", "billing", "checkout"],
scope: "project"
)
With files:
File: .sage-memory/ont-task-a1b2-fix-payment-timeout.md
---
tags: [ontology, entity, task, billing, checkout]
type: ontology
scope: project
entity_id: task_a1b2
entity_type: Task
created: 2026-03-20
---
{"id":"task_a1b2","type":"Task","properties":{"title":"Fix payment timeout","status":"open","priority":"high"}}
Filename convention: ont-{type}-{id}-{short-description}.md
ID format: {type_prefix}_{8_hex} — e.g., task_a1b2c3d4,
pers_e5f6a7b8, proj_c9d0e1f2
With MCP:
sage_memory_search(query: "task_a1b2", filter_tags: ["ontology", "entity"])
sage_memory_search(query: "open tasks billing", filter_tags: ["ontology", "entity", "task"])
FTS5 safety: Search by bare entity ID, never by bracket title.
With files: Scan .sage-memory/ for ont- prefixed files. Filter
by type: ont-task-* for tasks, ont-pers-* for persons. Read matching
files to check properties.
With MCP: sage_memory_list(tags: ["ontology", "entity", "task"])
With files: List all ont-task-*.md files in .sage-memory/.
With MCP: sage_memory_delete(id: "<entity_memory_id>") — CASCADE
automatically removes all edges.
With files: Delete the entity file. Then scan other ont-*.md files
for relations: entries referencing this entity's ID and remove them.
With MCP:
sage_memory_link(
source_id: "<source_memory_id>",
target_id: "<target_memory_id>",
relation: "blocks",
properties: {"reason": "payment flow must complete first"}
)
One call. Validation before creating:
blocks/depends_on → cycle check via sage_memory_graphWith files: Add a relations: section to the source entity's file:
File: .sage-memory/ont-task-a1b2-fix-payment-timeout.md
---
tags: [ontology, entity, task, billing, checkout]
type: ontology
scope: project
entity_id: task_a1b2
entity_type: Task
created: 2026-03-20
relations:
- rel: blocks
target: task_f3a4
target_type: Task
- rel: assigned_to
target: pers_e5f6
target_type: Person
---
{"id":"task_a1b2","type":"Task","properties":{"title":"Fix payment timeout","status":"open","priority":"high"}}
Relations live inside the source entity's frontmatter. This is
denormalized but avoids separate relation files and works without a
database. To find what blocks task_f3a4, scan all ont-task-*.md files
for relations: entries with target: task_f3a4.
With MCP:
sage_memory_link(
source_id: "<source_id>", target_id: "<target_id>",
relation: "blocks", delete: true
)
With files: Edit the source entity file — remove the relation entry
from the relations: list.
With MCP:
sage_memory_graph(
id: "<target_memory_id>",
relation: "blocks",
direction: "outbound",
depth: 5
)
If source_id appears in results → cycle → reject.
With files: Manually trace the chain. Read target's file → check its
relations: for outbound blocks → follow to next entity → repeat.
If you return to the source, it's a cycle. Practical for graphs under
20 edges of that type.
sage_memory_graph(id: "<project_id>", relation: "has_task", direction: "outbound", depth: 1)
sage_memory_graph(id: "<task_id>", relation: "blocks", direction: "inbound", depth: 2)
sage_memory_graph(id: "<entity_id>", direction: "both", depth: 1)
With files: Multi-hop traversal is not available. Use single-hop
lookups by scanning relations: in entity files. For tasks in a project,
scan all ont-task-*.md files for relations: entries like
{rel: part_of, target: <project_id>}.
Plan: "Set up feature project with tasks"
1. CREATE Project → sage_memory_store (or file)
2. CREATE Task1 → sage_memory_store (or file)
3. CREATE Task2 → sage_memory_store (or file)
4. RELATE has_task: Project → Task1 → sage_memory_link (or add to file)
5. RELATE has_task: Project → Task2 → sage_memory_link (or add to file)
6. RELATE blocks: Task1 → Task2 → sage_memory_link (after cycle check)
7. VALIDATE: no cycles ✓, cardinality ✓
graph_check.pyscripts/graph_check.py is a zero-dependency Python validator. Pipe
it a JSON document describing the entities and proposed relations to
get a structural OK / FAIL plus a list of violations. Use it before
the agent calls sage_memory_link for a non-trivial plan.
python3 scripts/graph_check.py <<'EOF'
{
"entities": [
{"id": "proj_c9d0", "type": "Project"},
{"id": "task_a1b2", "type": "Task"},
{"id": "task_f3a4", "type": "Task"}
],
"relations": [
{"from": "proj_c9d0", "to": "task_a1b2", "rel": "has_task"},
{"from": "proj_c9d0", "to": "task_f3a4", "rel": "has_task"},
{"from": "task_a1b2", "to": "task_f3a4", "rel": "blocks"}
]
}
EOF
Exit code 0 = valid; 1 = violations (printed as JSON to stdout).
Checks type compatibility, cardinality, and acyclicity for blocks
and depends_on chains. See the script's docstring for the full
input schema.
| Type | Required | |------|----------| | Task | title, status | | Person | name | | Project | name | | Event | title, start | | Document | title |
The full controlled vocabulary lives in
references/schema.md. All 11 built-in
relations:
| Relation | From → To | Cardinality | Acyclic | |----------|-----------|-------------|---------| | has_owner | Project, Task → Person | many_to_one | no | | assigned_to | Task → Person | many_to_one | no | | has_task | Project → Task | one_to_many | no | | has_goal | Project → Goal | one_to_many | no | | part_of | Task, Document, Event → Project | many_to_one | no | | blocks | Task → Task | many_to_many | yes | | depends_on | Task, Project → Task, Project, Event | many_to_many | yes | | member_of | Person → Organization | many_to_many | no | | mentions | Document, Message, Note → Person, Project, Task, Event | many_to_many | no | | follows_up | Task, Event → Event, Message | many_to_one | no | | attendee_of | Person → Event | many_to_many | no |
Custom relation strings are accepted by sage_memory_link but only
the 11 above are validated by graph_check.py. Stick to the
controlled vocabulary unless the project clearly needs an extension.
Credential safety: Never store password, secret, token,
api_key as properties.
Store a schema extension for custom types (works with both backends):
With MCP:
sage_memory_store(
title: "[Schema:Sprint] Custom entity type",
content: '{"type":"Sprint","required":["name","start","end"]}',
tags: ["ontology", "schema"]
)
With files:
File: .sage-memory/ont-schema-sprint.md
---
tags: [ontology, schema]
type: ontology
---
{"type":"Sprint","required":["name","start","end"],"enums":{"status":["planning","active","closed"]}}
For projects where code symbols (functions, classes, modules) are a recurring discovery target, sage-memory can pre-index the source tree via tree-sitter.
If sage-memory scan-codebase --help exits 0: the [codebase]
extra is installed. Run sage-memory scan-codebase (default: project
root) at session start or after notable code changes. Then use:
sage_memory_search(query: "...", filter_tags: ["codebase"]) —
surface files matching the query.sage_memory_scan_codebase(...) MCP tool — same as the CLI.If the extra is NOT installed: the CLI / MCP tool errors with an
install hint. Fall back to extracting code entities yourself via
your existing AST tools and the 0.9.0 entities / relations
payload on sage_memory_store.
The pre-indexed codebase saves 10-50× tokens on cross-file queries ("where is X called?", "what imports Y?", "what functions exist in this module?"). For line-level edits, you still need to read source.
10 languages supported: Python, TypeScript (incl. TSX), JavaScript
(incl. JSX), Go, Rust, Java, Ruby, PHP, C, C++. Symbol kinds
captured: FUNCTION, CLASS, METHOD, INTERFACE, STRUCT,
ENUM. Relations captured: imports, calls — with
confidence: resolved | unresolved so you can filter for
high-signal cross-file links.
Safety guards (T10b): scans are project-scoped (different
projects can scan in parallel), refuse to run against $HOME,
respect --limit (default 5000) BEFORE writes happen, and hold a
600s-recovery advisory lock that prevents two scans from racing on
the same project.
references/encoding.md — Full format, examples, search patternsreferences/schema.md — All types, relations, constraintsscripts/graph_check.py — Structural validatordevelopment
Branch-per-initiative git discipline for all delivery workflows. Defines branch naming by workflow, the propose-confirm creation protocol, dirty-tree and detached-HEAD handling, the always user-gated merge protocol, worktree support for parallel sessions, and abandonment cleanup. Activates only in git repositories — silently inactive everywhere else. Use when starting /build, /fix, /architect, or /build-x at Standard+ scope, when resuming an initiative, when offering a merge at a completion checkpoint, or when the user wants a second concurrent initiative.
development
Drives task-by-task execution from an approved plan with quality gates between each task. Reads the plan, finds the next incomplete task, dispatches implementation, validates, updates progress, and continues. Use after a plan is approved and the user says "go", "start building", "execute the plan", or "implement the feature".
testing
Preserves and restores context across agent sessions using plan file checkboxes as source of truth. Use when starting a new session, resuming previous work, ending a session, or when the user says "continue from last time", "what was I doing", or "save progress".
tools
Captures agent mistakes, corrections, and discovered gotchas so they are not repeated. Use when: (1) a command or operation fails unexpectedly, (2) the user corrects the agent, (3) the agent discovers non-obvious behavior through debugging, (4) an API or tool behaves differently than expected, (5) a better approach is found for a recurring task. Also searches past learnings before starting tasks to avoid known pitfalls. Activate alongside the sage-memory skill — they share the same MCP backend but serve different purposes (sage-memory = codebase knowledge, sage-self-learning = agent mistakes and gotchas).