16 entity types✦15 relation types✦5 constraint classes✦Two local files only✦No network calls✦Path traversal blocked in testing✦MIT-0✦ 16 entity types✦15 relation types✦5 constraint classes✦Two local files only✦No network calls✦Path traversal blocked in testing✦MIT-0✦
ONTOLOGY

A skill for AI agents

It remembers the text.
It forgets the relationships.

Ontology gives your agent's memory a skeleton. Use create and relate to store people, projects, and tasks as 16 typed entities joined by 15 named relations — then run one validate pass to check required fields, enums, forbidden properties, cardinality, and cycles. You do not get another Markdown file. You get a graph you can traverse, diff, and hand to another skill as-is.

16 entity types 15 relation types 5 constraint classes MIT-0

One command to install · Open source · Requires Python 3.10+

TERMINAL · REAL COMMANDS, REAL OUTPUT
$ ontology create --type Person --props '{"name":"Alice Chen"}'
→ pers_b5ab78eb
$ ontology create --type Project --props '{"name":"Website Redesign","status":"active"}'
→ proj_04932cc7
$ ontology create --type Task --props '{"title":"Rewrite homepage copy","status":"open"}'
→ task_6a18211f
$ ontology relate --from proj_04932cc7 --rel has_task --to task_6a18211f
$ ontology relate --from proj_04932cc7 --rel has_owner --to pers_b5ab78eb
$ ontology relate --from task_6a18211f --rel assigned_to --to pers_b5ab78eb
$ ontology validate
✓ Graph is valid.
$  
KNOWLEDGE GRAPH · BUILT LINE BY LINE
has_owner has_task assigned_to PROJECT Website Redesign PERSON Alice Chen TASK Rewrite homepage copy
Path traversal blocked
0 violations
✓ Graph is valid.   3 entities · 3 relations · 0 violations
02

Before / After

From searching prose for a relationship
to walking one named edge to the answer

The prompt and output on the right were produced while this page was being made. Reproduced verbatim, unedited.

BEFOREBefore the skill
  • ✕Memory is prose; relationships are implied. The note says "Alice owns the website redesign" — owner, assignee, or merely mentioned? The machine cannot tell.
  • ✕Status fields are free text. One state gets written as "in progress", "doing", and "wip". The downstream skill finds out when it already broke.
  • ✕Every skill keeps its own files. A commitment written by the email skill is invisible to the task skill — so you write a converter, or copy the data by hand.
  • ✕Answering a relationship question means re-reading history. "Who owns this project?" turns into a keyword search across old transcripts.
  • ✕Close the context, lose the structure. Every new session starts with re-explaining who owns what and what is blocked by what.
AFTERAfter running the skill
PROMPT · WHAT YOU TYPE
ontology create --type Person --props '{"name":"Alice Chen"}' ontology create --type Project --props '{"name":"Website Redesign","status":"active"}' ontology create --type Task --props '{"title":"Rewrite homepage copy","status":"open"}' ontology relate --from proj_04932cc7 --rel has_task --to task_6a18211f ontology relate --from proj_04932cc7 --rel has_owner --to pers_b5ab78eb ontology relate --from task_6a18211f --rel assigned_to --to pers_b5ab78eb ontology validate
OUTPUT · VERBATIM
✓ Graph is valid.

3 entities written:
  pers_b5ab78eb  Person   Alice Chen
  proj_04932cc7  Project  Website Redesign
  task_6a18211f  Task     Rewrite homepage copy

3 relations created:
  proj_04932cc7 --has_task-->    task_6a18211f
  proj_04932cc7 --has_owner-->   pers_b5ab78eb
  task_6a18211f --assigned_to--> pers_b5ab78eb
RESULT
0 violations 3 entities 3 relations 2 local files
01 Searching dozens of notes for "who owns this project" → related --rel has_owner returns the owner entity in one call
02 Discovering missing fields and bad enum values only when something reads them → validate lists every violation with entity id and field name
03 Writing a format converter for every pair of skills → One shared graph; each skill reads and writes only the types it declares
03

Core Capabilities

Six things it does properly

Entity and relation counts come from the skill's own schema reference, checked one by one.

01

Typed entities

Person, Project, Task, Event, Document, Credential and ten more. Each type declares its required fields and property types, so a record is a record — not a paragraph.

16 typesrequired fieldsref links
02

Named relations

15 relation types, each declaring what it may connect to what. has_owner, has_task, and blocks are not string labels — they are traversable, checkable edges.

15 typesforward walkreverse walk
03

Five constraint classes

Required, enum, forbidden, cardinality, acyclic. Credential entities are forced to store a secret reference only — a plaintext password is flagged by name.

requiredenumforbiddencardinalityacyclic
04

One pass, every violation

validate reports the entity id, the field name, and the cause — not a generic "something is wrong". In testing, all five classes were reported in a single call.

validateid-level detail
05

Shared across skills

Each skill declares which types it reads and which it writes, and all of them share one graph. Add a skill without touching the others or breaking their data format.

readswritesno glue code
06

Two plain text files

Everything lands in graph.jsonl and schema.yaml inside your workspace. One line per operation: diffable, revertible, version-controllable. The script makes no network calls and reads no credential files.

graph.jsonlschema.yamldiffable
WHERE THE LIMITS ARE

create and relate do not validate on write. Invalid data is committed first and caught afterwards by validate — confirmed in testing: a record missing a required field and one holding an out-of-enum value were both written successfully, then reported. If you need validation at write time, wrap the calls.

Two behaviours described in the documentation are not implemented in the current code: task status transition checks and orphan-task warnings. The acyclic constraint only takes effect once the relation is declared in the schema.

04

Real Output

Three runs, verbatim

The first two were executed while this page was being made. The third follows the skill's documentation and has not been tested here; it is labelled as such.

01
EXECUTED · VERBATIM

Three entities and three relations in one pass

Three creates and three relates, and people, projects, and tasks are joined by traversable edges.

INPUT
create --type Person --props '{"name":"Alice Chen"}' create --type Project --props '{"name":"Website Redesign","status":"active"}' create --type Task --props '{"title":"Rewrite homepage copy","status":"open"}' relate proj → has_task → task relate proj → has_owner → person relate task → assigned_to → person
OUTPUT
$ ontology validate
Graph is valid.

$ ontology related --id proj_04932cc7 --rel has_task
[
  {
    "relation": "has_task",
    "entity": {
      "id": "task_6a18211f",
      "type": "Task",
      "properties": {
        "title": "Rewrite homepage copy",
        "status": "open"
      },
      "created": "2026-09-16T07:42:56.082351+00:00",
      "updated": "2026-09-16T07:42:56.082351+00:00"
    }
  }
]
Why it matters: "Who owns this project?" is one edge away rather than one search away. Traversal works in both directions, from either endpoint.
02
EXECUTED · VERBATIM

Five constraint violations, caught in a single validate

Dirty data written on purpose: a missing field, an out-of-enum value, a plaintext password, a dependency cycle, and a cardinality breach.

INPUT · DIRTY DATA, WRITTEN ON PURPOSE
create Task # status deliberately omitted create Task # status set to "almost done" create Credential # carries a plaintext password relate A blocks B, B blocks A # cycle relate a second Project to the same Task
OUTPUT · VERBATIM
$ ontology validate
Validation errors:
  - task_5bf72652: missing required property 'status'
  - task_61ec4b8e: 'status' must be one of
    ['open', 'in_progress', 'blocked', 'done'],
    got 'almost done'
  - cred_e549e590: contains forbidden property 'password'
  - blocks: cyclic dependency detected
  - has_task: to entity task_6a18211f
    violates cardinality one_to_many
Why it matters: All five classes reported their own location and cause in one pass instead of a single generic failure. The plaintext password was named explicitly, and the dependency cycle was detected.
03
DOCUMENTED · NOT TESTED

One graph, shared across skills

A commitment written by the email skill is picked up by the task skill through a typed query.

INPUT
# email skill writes commitment = create("Commitment", { "source_message": msg_id, "description": "Send the report by Friday", "due": "2026-01-31" })
OUTPUT
# task skill picks it up tasks = query("Commitment", {"status": "pending"}) for c in tasks: create("Task", { "title": c.description, "due": c.due })
Why it matters: Neither skill needs to know the other exists, and no converter is written for this specific pair. Each one only declares which entity types it reads and writes. This example comes from the skill documentation and was not executed here.
05

How It Works

Five steps from empty to a validated graph

You supply the entities and the relations. The skill writes them into your workspace, one line per operation — appending, never overwriting.

1

Declare the rules

schema-append writes required fields, enum values, forbidden fields, relation cardinality, and whether a relation must stay acyclic.

schema-append --data '{...}'
2

Create entities

Give a type and its properties; get back an entity with an id and a timestamp.

create --type Person --props '{"name":"Alice Chen"}'
3

Connect them

Relation types come from the 15 predefined kinds, each restricting what may connect to what.

relate --from proj_xxx --rel has_owner --to pers_xxx
4

Ask questions

query filters by type and condition, get fetches by id, and related walks a named relation.

related --id proj_xxx --rel has_task
5

Run the check

One pass lists every violation with entity id, field name, and cause.

validate
graph.jsonl → schema.yaml → diffable · revertible · version-controllable
06

Who & When

Who should use it, and for what

Developers building multi-agent systems

PRIMARY USER

You are wiring several skills into one pipeline: what the announcement-fetching skill produces has to be usable by the analysis skill, and what the analysis skill writes has to be readable by the report skill. Today every one of those handoffs is hand-built.

  • Fetch announcement → create entities → trigger analysis → generate report
  • Skills exchange state through the graph instead of passing arguments
  • Adding a skill does not require changing the existing ones

Power users tired of repeating themselves

SECONDARY USER

Your agent remembers isolated facts but cannot answer "who owns this". Every new session means re-explaining the relationships and the context, and the explanations pile up across dozens of notes until nothing is findable.

  • Long-lived people and project records that survive across sessions
  • Multi-step planning, with dependencies you can check for cycles
  • Commitment tracking from email, with a chain you can trace back
USE CASE Long-lived people & project records → People, organisations, and projects interlinked — "who owns this" is one hop
USE CASE Multi-step task planning → A plan modelled as graph operations; each step checkable, dependencies provably acyclic
USE CASE Cross-skill collaboration → Skills share state through the graph, not through arguments
USE CASE Contacts & scheduling → Event, Location, and attendee relations fit the model natively

ONTOLOGY

Give your agent's memory a skeleton. Store the relationships, not just the sentences.

clawhub.ai/oswalpalash/skills/ontology

What you provide: the entities you want your agent to remember, and the relationships between them. Optionally a rule set — which fields are required, which values a status may take.

What you get back: a persistent, validated knowledge graph that other skills can read and write directly, plus a rule file you can audit at any time.