# Oikos ontology seed — the systems model of the homelab.
#
# Bootstraps entity_types / relationship_types / lifecycle_defs on first
# deploy (migration 001). After ingest the DB is authoritative; this file
# is regenerated by `GET /api/v1/export` for DR + version control.
#
# Conventions:
#   - entity type names are kebab-case
#   - `parent:` builds the is-a hierarchy; `abstract: true` types cannot be
#     instantiated (validation walks the hierarchy for relationship
#     endpoints and policy rules — plan R3-1)
#   - `layer:` one of meta | infrastructure | governance | cognition
#   - relationship `cardinality:` describes source→target multiplicity:
#     one-to-one | one-to-many | many-to-one | many-to-many
#   - relationship endpoints may name abstract types
#   - lifecycle transition `requires:` entries are NAMED CHECKS implemented
#     in Go (internal/ontology); the DB stores which checks gate a
#     transition, the code implements them
#   - mount details (mount_point, options) live as ATTRIBUTES on `mounts`
#     edges, not as a separate entity type
#
# Rule of completeness: if something can break, be changed, or hold data,
# it has an entity type here and edges to the things it touches.

version: 1

# ─── Lifecycles ────────────────────────────────────────────────────────

lifecycles:
  infrastructure:
    states: [planned, provisioning, active, migrating, failed, deprecated, destroyed]
    default_state: active          # legacy inventory entries without state are active
    terminal_states: [destroyed]
    transitions:
      planned:
        provisioning: {requires: [inventory-entry, ip-reserved, storage-pool-chosen, doc-page-stub]}
        destroyed:    {requires: [cancelled-note]}
      provisioning:
        active: {requires: [age-key-enrolled-if-needed, mesh-joined-if-needed,
                            ingress-live-if-public, health-check-answering,
                            doc-page-complete]}
        failed: {requires: []}
      active:
        migrating:  {requires: [preflight, backup-verified]}
        deprecated: {requires: [replacement-live-or-role-retired]}
        failed:     {requires: []}
      migrating:
        active: {requires: [post-verify, caddy-backends-checked, mounts-checked, docs-updated]}
        failed: {requires: []}
      failed:
        active:     {requires: [recovery-verified]}
        deprecated: {requires: [write-off-note]}
      deprecated:
        active:    {requires: [un-deprecate-note]}
        destroyed: {requires: [backups-verified, secrets-revoked-and-rekeyed,
                               ingress-and-dns-removed, no-inbound-edges,
                               archaeology-entry]}

  signal:
    states: [raised, acknowledged, acting, muted, resolved, failed]
    default_state: raised
    terminal_states: [resolved]
    transitions:
      raised:
        acknowledged: {requires: []}
        muted:        {requires: [mute-ttl-set]}
        resolved:     {requires: [condition-cleared]}
      acknowledged:
        acting:   {requires: [classification-exists]}
        resolved: {requires: []}
        muted:    {requires: [mute-ttl-set]}
      acting:
        resolved: {requires: [verification-passed]}
        raised:   {requires: [retry-budget-remaining]}
        failed:   {requires: []}
      failed:
        acknowledged: {requires: [operator-retry]}
      muted:
        raised: {requires: [mute-ttl-expired]}

  execution:
    states: [proposed, approved, auto_approved, denied, expired, executing,
             verifying, verified, failed, timed_out, cancelled, rolled_back,
             rollback_failed]
    default_state: proposed
    terminal_states: [verified, failed, denied, expired, cancelled,
                      rolled_back, rollback_failed]
    transitions:
      proposed:
        approved:      {requires: [operator-approval]}
        auto_approved: {requires: [autonomy-allows]}
        denied:        {requires: []}
      approved:
        executing: {requires: [approval-token-valid]}
        expired:   {requires: [approval-ttl-elapsed]}
      auto_approved:
        executing: {requires: []}
      executing:
        verified:  {requires: [verification-passed]}
        failed:    {requires: []}
        timed_out: {requires: []}
        cancelled: {requires: [operator-abort]}
      timed_out:
        verifying: {requires: []}          # check if the command completed anyway
      verifying:
        verified: {requires: [verification-passed]}
        failed:   {requires: []}
      failed:
        rolled_back:     {requires: [rollback-procedure-exists]}
        rollback_failed: {requires: []}

  approval:
    states: [pending, approved, denied, expired, revoked]
    default_state: pending
    terminal_states: [denied, expired, revoked]
    transitions:
      pending:
        approved: {requires: [token-verified]}
        denied:   {requires: []}
        expired:  {requires: [ttl-elapsed]}
      approved:
        revoked: {requires: [not-yet-executing]}

  pattern:
    states: [hypothesized, validated, active, deprecated, invalidated]
    default_state: hypothesized
    terminal_states: [deprecated, invalidated]
    transitions:
      hypothesized:
        validated:   {requires: [evidence-count-5plus, confidence-0.7plus]}
        invalidated: {requires: []}
      validated:
        active:      {requires: [operator-approval]}     # S4: never automatic
        invalidated: {requires: []}
      active:
        deprecated:  {requires: []}
        invalidated: {requires: [contradicting-evidence]}

  skill:
    states: [drafted, tested, active, refined, failed, deprecated]
    default_state: drafted
    terminal_states: [deprecated]
    transitions:
      drafted:
        tested:     {requires: [test-execution-recorded]}
        deprecated: {requires: []}
      tested:
        active: {requires: [operator-approval]}
        failed: {requires: []}
      failed:
        drafted: {requires: []}
      active:
        refined:    {requires: [new-version-created]}
        deprecated: {requires: []}
      refined:
        active: {requires: [operator-approval]}

# ─── Entity types ──────────────────────────────────────────────────────
# domain: physical | compute | network | storage | software | external |
#         identity | cognition

entity_types:

  # Root
  entity:
    abstract: true
    domain: meta
    layer: meta
    description: Root abstract type. Relationship endpoints that accept any
      entity (documented-by, procedure-for, checks) reference this.

  # ── Infrastructure / physical ──
  site:
    parent: entity
    domain: physical
    layer: infrastructure
    lifecycle: infrastructure
    description: Physical location (home, VPS datacenter).
    attributes: {type: object, properties: {address: {type: string}}}
  ups:
    parent: entity
    domain: physical
    layer: infrastructure
    lifecycle: infrastructure
    description: Uninterruptible power supply.
    attributes: {type: object, properties: {vendor: {type: string}, va: {type: integer}}}
  sensor:
    parent: entity
    domain: physical
    layer: infrastructure
    lifecycle: infrastructure
    description: Environmental sensor.
  peripheral:
    parent: entity
    domain: physical
    layer: infrastructure
    lifecycle: infrastructure
    description: Attached hardware (GPU, e-ink display, dongle).

  # ── Infrastructure / compute ──
  compute-entity:
    parent: entity
    abstract: true
    domain: compute
    layer: infrastructure
    description: Anything that executes workloads (machine, VM, container).
  machine:
    parent: compute-entity
    abstract: true
    domain: compute
    layer: infrastructure
    description: Physical machine. Always instantiated as a subtype.
    attributes:
      type: object
      properties:
        cpu_arch: {type: string}
        ram_gb: {type: number}
        os: {type: string, enum: [linux, macos]}
        lan_ip: {type: string}
        mesh: {type: object}
        ssh: {type: object}
        age_pubkey: {type: string}
  proxmox-host:
    parent: machine
    domain: compute
    layer: infrastructure
    lifecycle: infrastructure
    description: Machine running Proxmox VE.
    attributes:
      type: object
      properties: {pve_version: {type: string}}
  standalone-server:
    parent: machine
    domain: compute
    layer: infrastructure
    lifecycle: infrastructure
    description: Machine outside PVE management (e.g. external VPS).
    attributes:
      type: object
      properties:
        hypervisor: {type: string}
        provider: {type: string}
        control_level: {type: string, enum: [full, partial, none]}
        public_ipv4: {type: string}
  workstation:
    parent: machine
    domain: compute
    layer: infrastructure
    lifecycle: infrastructure
    description: Operator machine (may also host services, e.g. mac-mini).
    attributes:
      type: object
      properties: {user: {type: string}}
  appliance:
    parent: machine
    domain: compute
    layer: infrastructure
    lifecycle: infrastructure
    description: Vendor appliance with limited management access.
    attributes:
      type: object
      properties: {vendor: {type: string}, model: {type: string}}
  vm:
    parent: compute-entity
    domain: compute
    layer: infrastructure
    lifecycle: infrastructure
    description: Virtual machine.
    attributes:
      type: object
      properties:
        pve_id: {type: integer}
        vcpus: {type: integer}
        memory_mb: {type: integer}
        lan_ip: {type: string}
        public_host: {type: string}
        role: {type: string}
  container:
    parent: compute-entity
    abstract: true
    domain: compute
    layer: infrastructure
    description: OS-level container (LXC or Docker).
    attributes:
      type: object
      properties: {runtime: {type: string}}
  lxc:
    parent: container
    domain: compute
    layer: infrastructure
    lifecycle: infrastructure
    description: Proxmox LXC container.
    attributes:
      type: object
      properties:
        pve_id: {type: integer}
        lan_ip: {type: string}
        public_host: {type: string}
        public_hosts: {type: array, items: {type: string}}
        role: {type: string}
        mesh: {type: object}
        age_pubkey: {type: string}
        destroyed: {type: string}
        reason: {type: string}
  docker-container:
    parent: container
    domain: compute
    layer: infrastructure
    lifecycle: infrastructure
    description: Docker container (the OS models its own stack with these).
    attributes:
      type: object
      properties: {image: {type: string}}
  hypervisor:
    parent: entity
    domain: compute
    layer: infrastructure
    lifecycle: infrastructure
    description: Hypervisor software running on a machine (PVE, KVM, OrbStack).
    attributes:
      type: object
      properties: {type: {type: string}, version: {type: string}}

  # ── Infrastructure / network ──
  network:
    parent: entity
    abstract: true
    domain: network
    layer: infrastructure
    description: A network things connect to.
  lan:
    parent: network
    domain: network
    layer: infrastructure
    lifecycle: infrastructure
    description: Local area network.
    attributes: {type: object, properties: {subnet: {type: string}}}
  mesh:
    parent: network
    domain: network
    layer: infrastructure
    lifecycle: infrastructure
    description: Overlay mesh network (NetBird, Tailscale).
    attributes:
      type: object
      properties:
        provider: {type: string}
        subnet: {type: string}
        domain: {type: string}
  vlan:
    parent: network
    domain: network
    layer: infrastructure
    lifecycle: infrastructure
    description: Tagged VLAN.
    attributes: {type: object, properties: {tag: {type: integer}}}
  network-interface:
    parent: entity
    domain: network
    layer: infrastructure
    description: Optional per-interface refinement (mac, ip). The seed uses
      coarse connects-via edges; interfaces can be backfilled later.
    attributes: {type: object, properties: {mac: {type: string}, ip: {type: string}}}
  dns-zone:
    parent: entity
    domain: network
    layer: infrastructure
    lifecycle: infrastructure
    description: DNS zone (e.g. split-horizon hubris.network).
    attributes: {type: object, properties: {zone: {type: string}, authority: {type: string}}}
  dns-record:
    parent: entity
    domain: network
    layer: infrastructure
    description: Individual DNS record.
    attributes:
      type: object
      properties: {name: {type: string}, record_type: {type: string}, value: {type: string}}
  ingress-route:
    parent: entity
    domain: network
    layer: infrastructure
    lifecycle: infrastructure
    description: Public hostname → upstream mapping (Caddy).
    attributes:
      type: object
      properties:
        pattern: {type: string}
        upstream: {type: string}
        forward_auth: {type: boolean}
  certificate:
    parent: entity
    domain: network
    layer: infrastructure
    description: TLS certificate.
    attributes: {type: object, properties: {issuer: {type: string}, expires: {type: string}}}
  firewall-rule:
    parent: entity
    domain: network
    layer: infrastructure
    description: Firewall / port-forward rule.

  # ── Infrastructure / storage ──
  storage-pool:
    parent: entity
    domain: storage
    layer: infrastructure
    lifecycle: infrastructure
    description: Storage pool (LVM, ZFS, NFS).
    attributes:
      type: object
      properties:
        type: {type: string}
        capacity_gb: {type: number}
  volume:
    parent: entity
    domain: storage
    layer: infrastructure
    lifecycle: infrastructure
    description: Named volume / dataset within a pool. Mount details live as
      attributes on `mounts` edges.
    attributes: {type: object, properties: {size_gb: {type: number}, path: {type: string}}}
  backup-target:
    parent: entity
    domain: storage
    layer: infrastructure
    lifecycle: infrastructure
    description: Where backups land (Proton Drive, PBS).
    attributes: {type: object, properties: {provider: {type: string}, encrypted: {type: boolean}}}
  dataset:
    parent: entity
    domain: storage
    layer: infrastructure
    description: Logical data collection worth tracking independently of its
      volume (e.g. paperless documents).

  # ── Infrastructure / software ──
  service:
    parent: entity
    domain: software
    layer: infrastructure
    lifecycle: infrastructure
    description: A running service with consumers.
    attributes:
      type: object
      properties:
        url: {type: string}
        port: {type: integer}
        health: {type: string}
        endpoint: {type: string}
        systemd_unit: {type: string}
        doc_page: {type: string}
        risk_notes: {type: string}
        note: {type: string}
  application:
    parent: entity
    domain: software
    layer: infrastructure
    description: Deployed application/package a service runs.
    attributes: {type: object, properties: {version: {type: string}}}
  config-repo:
    parent: entity
    domain: software
    layer: infrastructure
    description: Git repo holding tracked configuration.
    attributes:
      type: object
      properties: {url: {type: string}, branch: {type: string}}
  deploy-pipeline:
    parent: entity
    domain: software
    layer: infrastructure
    description: Automated deploy path (webhook → script).
    attributes:
      type: object
      properties: {trigger: {type: string}, target_path: {type: string}}
  package-set:
    parent: entity
    domain: software
    layer: infrastructure
    description: Managed package baseline for a host class.
  cluster:
    parent: entity
    domain: software
    layer: infrastructure
    lifecycle: infrastructure
    description: Proxmox cluster.
    attributes: {type: object, properties: {quorum: {type: string}}}
  compose-stack:
    parent: entity
    domain: software
    layer: infrastructure
    lifecycle: infrastructure
    description: Docker Compose stack (the Oikos OS itself is one).
    attributes: {type: object, properties: {path: {type: string}}}

  # ── Infrastructure / external ──
  domain-registration:
    parent: entity
    domain: external
    layer: infrastructure
    description: Registered public domain.
    attributes: {type: object, properties: {registrar: {type: string}, expires: {type: string}}}
  cloud-service:
    parent: entity
    domain: external
    layer: infrastructure
    description: External SaaS/cloud dependency.
  isp-link:
    parent: entity
    domain: external
    layer: infrastructure
    description: Internet uplink.
  vendor-dependency:
    parent: entity
    domain: external
    layer: infrastructure
    description: Vendor the lab depends on (registrar, IONOS, Proton).

  # ── Governance / identity ──
  person:
    parent: entity
    domain: identity
    layer: governance
    description: Human actor (operator).
    attributes:
      type: object
      properties: {matrix_id: {type: string}, oidc_sub: {type: string}, email: {type: string}}
  agent:
    parent: entity
    domain: identity
    layer: governance
    lifecycle: infrastructure   # agents are deployed/retired like infrastructure
    description: Software agent actor (Nomos, the Oikos control loop).
    attributes:
      type: object
      properties:
        provider: {type: string}
        model: {type: string}
        gateway_port: {type: integer}
  identity-provider:
    parent: entity
    domain: identity
    layer: governance
    description: OIDC / forward-auth provider (Authentik).
    attributes:
      type: object
      properties:
        issuer: {type: string}
        client_id: {type: string}
        auth_mode: {type: string, enum: [oidc, forward-auth, both]}
  account:
    parent: entity
    domain: identity
    layer: governance
    description: An account a person/agent holds on a service.
  secret:
    parent: entity
    domain: identity
    layer: governance
    description: Managed secret (Infisical path).
    attributes:
      type: object
      properties: {path: {type: string}, rotation_days: {type: integer}}
  key:
    parent: entity
    domain: identity
    layer: governance
    description: Cryptographic key (SSH, age).
  access-grant:
    parent: entity
    domain: identity
    layer: governance
    description: Grant of access to a secret/scope.
    attributes: {type: object, properties: {scope: {type: string}, expires: {type: string}}}

  # ── Cognition ──
  check:
    parent: entity
    domain: cognition
    layer: cognition
    description: Probe definition (checks-as-data, R3-7). Typed row in check_defs.
  signal:
    parent: entity
    domain: cognition
    layer: cognition
    lifecycle: signal
    description: Something needing attention. Typed row in signals.
  classification:
    parent: entity
    domain: cognition
    layer: cognition
    description: A classifier decision with full reasoning. Typed row in classifications.
  execution:
    parent: entity
    domain: cognition
    layer: cognition
    lifecycle: execution
    description: An action the OS performed. Typed row in executions.
  feedback:
    parent: entity
    domain: cognition
    layer: cognition
    description: What was learned from an execution. Typed row in feedback.
  pattern:
    parent: entity
    domain: cognition
    layer: cognition
    lifecycle: pattern
    description: Generalized rule extracted from feedback. Typed row in patterns.
  skill:
    parent: entity
    domain: cognition
    layer: cognition
    lifecycle: skill
    description: Codified, versioned procedure. Typed rows in skills.
  approval:
    parent: entity
    domain: cognition
    layer: cognition
    lifecycle: approval
    description: Operator approval request/decision. Typed row in approvals.
  document:
    parent: entity
    domain: cognition
    layer: cognition
    description: Knowledge document ingested from docs/.
    attributes:
      type: object
      properties: {title: {type: string}, source_path: {type: string}, content_hash: {type: string}}
  runbook:
    parent: entity
    domain: cognition
    layer: cognition
    description: Step-by-step procedure for an entity/action.
    attributes:
      type: object
      properties: {risk_class: {type: string}, source_path: {type: string}}
  investigation:
    parent: entity
    domain: cognition
    layer: cognition
    description: Recorded investigation/postmortem.
  task:
    parent: entity
    domain: cognition
    layer: cognition
    description: A goal-structured unit of agent work — one chat/session elevated
      to a task with a plan, lifecycle status, and outcome. Anchors the knowledge
      and involved-entity relationships for the task so future tasks can learn
      from it. Typed rows in agent_sessions.

# ─── Relationship types ────────────────────────────────────────────────
# cardinality is source→target: e.g. `hosts` one-to-many = one machine
# hosts many compute entities; each hosted entity has one hosting machine.

relationship_types:

  # Infrastructure topology
  hosts:
    inverse: runs-on
    source: machine
    target: compute-entity
    cardinality: one-to-many
    description: Machine hosts a VM/container (hubris hosts lxc:apps).
  runs-hypervisor:
    inverse: hypervisor-on
    source: machine
    target: hypervisor
    cardinality: one-to-one
    description: Machine runs hypervisor software.
  member-of:
    inverse: has-member
    source: proxmox-host
    target: cluster
    cardinality: many-to-one
    description: PVE host belongs to a cluster.
  part-of:
    inverse: comprises
    source: docker-container
    target: compose-stack
    cardinality: many-to-one
    description: Docker container belongs to a compose stack.
  provides:
    inverse: provided-by
    source: compute-entity
    target: service
    cardinality: one-to-many
    description: Compute entity provides a service (lxc:gitea provides service:gitea).
  runs:
    inverse: run-by
    source: service
    target: application
    cardinality: one-to-many
    description: Service runs an application.
  configured-by:
    inverse: configures
    source: entity
    target: config-repo
    cardinality: many-to-one
    description: Entity's config is tracked in a repo (mutations = commit+push).
  deploys-to:
    inverse: deployed-by
    source: deploy-pipeline
    target: entity
    cardinality: many-to-one
    description: Pipeline deploys to a service/host.
  routes-to:
    inverse: routed-via
    source: ingress-route
    target: service
    cardinality: many-to-one
    description: Public hostname routes to a service.
  secured-by:
    inverse: secures
    source: ingress-route
    target: identity-provider
    cardinality: many-to-one
    description: Route gated by forward-auth.
  uses-certificate:
    inverse: certifies
    source: ingress-route
    target: certificate
    cardinality: many-to-one
    description: Route served with this certificate.
  authenticates-via:
    inverse: authenticates-service
    source: service
    target: identity-provider
    cardinality: many-to-one
    description: Service uses native OIDC (jellyfin authenticates-via authentik).
  in-zone:
    inverse: contains-record
    source: dns-record
    target: dns-zone
    cardinality: many-to-one
    description: Record belongs to a zone.
  resolves-to:
    inverse: resolved-from
    source: dns-record
    target: entity
    cardinality: many-to-one
    description: Record points at an ingress route or host.
  depends-on:
    inverse: dependency-of
    source: service
    target: service
    cardinality: many-to-many
    description: Runtime dependency (blast-radius edge).
  connects-via:
    inverse: connects
    source: compute-entity
    target: network
    cardinality: many-to-many
    description: Coarse network membership (host on LAN / mesh).
  has-interface:
    inverse: interface-of
    source: compute-entity
    target: network-interface
    cardinality: one-to-many
    description: Optional per-interface refinement.
  interface-on:
    inverse: has-endpoint
    source: network-interface
    target: network
    cardinality: many-to-one
    description: Interface attaches to a network.

  # Storage
  mounts:
    inverse: mounted-by
    source: compute-entity
    target: volume
    cardinality: many-to-many
    description: Compute entity mounts a volume. Edge attributes carry
      mount_point and options.
  stores-on:
    inverse: stores-for
    source: compute-entity
    target: storage-pool
    cardinality: many-to-many
    description: Rootfs/data lives on a pool.
  contains:
    inverse: contained-in
    source: storage-pool
    target: volume
    cardinality: one-to-many
    description: Pool contains a volume.
  holds-dataset:
    inverse: dataset-on
    source: volume
    target: dataset
    cardinality: one-to-many
    description: Volume holds a tracked dataset.
  backs-up-to:
    inverse: backup-of
    source: entity
    target: backup-target
    cardinality: many-to-many
    description: Entity's data is backed up to a target.

  # Physical / external
  powered-by:
    inverse: powers
    source: machine
    target: ups
    cardinality: many-to-one
    description: Machine on UPS power.
  located-at:
    inverse: location-of
    source: machine
    target: site
    cardinality: many-to-one
    description: Machine's physical site.
  registered-with:
    inverse: registrar-of
    source: domain-registration
    target: vendor-dependency
    cardinality: many-to-one
    description: Domain registered with a registrar.

  # Governance
  owns:
    inverse: owned-by
    source: person
    target: agent
    cardinality: one-to-many
    description: Person owns/controls an agent.
  authenticates:
    inverse: authenticated-by
    source: identity-provider
    target: person
    cardinality: one-to-many
    description: IdP authenticates a person.
  holds-grant:
    inverse: granted-to
    source: agent
    target: access-grant
    cardinality: one-to-many
    description: Agent holds an access grant.
  grants:
    inverse: granted-by
    source: access-grant
    target: secret
    cardinality: many-to-one
    description: Grant covers a secret.
  can-decrypt:
    inverse: readable-by
    source: compute-entity
    target: secret
    cardinality: many-to-many
    description: Host can decrypt a secret (legacy SOPS; Infisical grants later).

  # Cognition
  checks:
    inverse: checked-by
    source: check
    target: entity
    cardinality: many-to-one
    description: Check probes an entity.
  raises:
    inverse: raised-by
    source: check
    target: signal
    cardinality: one-to-many
    description: Check raised a signal.
  about:
    inverse: subject-of
    source: entity
    target: entity
    cardinality: many-to-many
    description: Entity concerns (or is about) another entity.
  classifies:
    inverse: classified-as
    source: classification
    target: signal
    cardinality: many-to-one
    description: Classification of a signal.
  precedes:
    inverse: follows
    source: classification
    target: execution
    cardinality: one-to-one
    description: Classification that led to an execution.
  targets:
    inverse: targeted-by
    source: execution
    target: entity
    cardinality: many-to-one
    description: Execution acts on an entity.
  requires-approval:
    inverse: approves
    source: execution
    target: approval
    cardinality: one-to-one
    description: Execution gated by an approval.
  performs:
    inverse: performed-by
    source: agent
    target: execution
    cardinality: one-to-many
    description: Agent performed an execution.
  decides:
    inverse: decided-by
    source: person
    target: approval
    cardinality: one-to-many
    description: Person decided an approval.
  produces:
    inverse: produced-by
    source: execution
    target: feedback
    cardinality: one-to-one
    description: Execution produced feedback.
  contributes-to:
    inverse: built-from
    source: feedback
    target: pattern
    cardinality: many-to-many
    description: Feedback supports a pattern.
  informs:
    inverse: informed-by
    source: pattern
    target: skill
    cardinality: many-to-one
    description: Pattern informs a skill.
  guides:
    inverse: guided-by
    source: skill
    target: classification
    cardinality: one-to-many
    description: Skill guided a classification.
  documents:
    inverse: documented-by
    source: document
    target: entity
    cardinality: many-to-one
    description: Document describes an entity.
  involves:
    inverse: involved-in
    source: task
    target: entity
    cardinality: many-to-many
    description: Task explored or acted on an entity (captured from its tool
      calls). A task's involved-entity set is its graph neighborhood, so future
      tasks on the same entities can surface this task's knowledge and outcome.
  procedure-for:
    inverse: has-procedure
    source: runbook
    target: entity
    cardinality: many-to-many
    description: Runbook applies to an entity.
