{
  "schema": "snappy-card/1",
  "skill": "snappy-knowledge",
  "tier": "public",
  "origin_tier": "base",
  "purpose": "You are operating as the CRM brain for Snappy. This file is the operational contract. The full SKILL.md exists for reference but the rules below are load-bearing -- if you deviate from them, the graph is corrupted and downstream skills (sales, clients, ops) read bad data.",
  "verbs": [
    {
      "name": "birthdays",
      "args": [],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-knowledge/api.ts birthdays"
    },
    {
      "name": "bulk-create",
      "args": [
        "contacts-json?"
      ],
      "effect": "write",
      "firstCall": "npx tsx ~/.claude/skills/snappy-knowledge/api.ts bulk-create"
    },
    {
      "name": "bulk-update",
      "args": [
        "updates-json?"
      ],
      "effect": "write",
      "firstCall": "npx tsx ~/.claude/skills/snappy-knowledge/api.ts bulk-update"
    },
    {
      "name": "contacts",
      "args": [
        "filter?"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-knowledge/api.ts contacts"
    },
    {
      "name": "create",
      "args": [
        "contact-json?"
      ],
      "effect": "write",
      "firstCall": "npx tsx ~/.claude/skills/snappy-knowledge/api.ts create"
    },
    {
      "name": "dormant",
      "args": [
        "days?"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-knowledge/api.ts dormant"
    },
    {
      "name": "entities",
      "args": [
        "page?"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-knowledge/api.ts entities"
    },
    {
      "name": "entity",
      "args": [
        "entity-id"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-knowledge/api.ts entity <entity-id>"
    },
    {
      "name": "get",
      "args": [
        "contact-id"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-knowledge/api.ts get <contact-id>"
    },
    {
      "name": "interaction",
      "args": [
        "interaction-id"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-knowledge/api.ts interaction <interaction-id>"
    },
    {
      "name": "interactions",
      "args": [
        "page?"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-knowledge/api.ts interactions"
    },
    {
      "name": "link",
      "args": [
        "entity-id",
        "person-id"
      ],
      "effect": "write",
      "firstCall": "npx tsx ~/.claude/skills/snappy-knowledge/api.ts link <entity-id> <person-id>"
    },
    {
      "name": "log-interaction",
      "args": [
        "interaction-json?"
      ],
      "effect": "write",
      "firstCall": "npx tsx ~/.claude/skills/snappy-knowledge/api.ts log-interaction"
    },
    {
      "name": "metrics",
      "args": [
        "name"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-knowledge/api.ts metrics \"<name>\""
    },
    {
      "name": "resolve",
      "args": [
        "identity-json?"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-knowledge/api.ts resolve"
    },
    {
      "name": "search",
      "args": [
        "query"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-knowledge/api.ts search \"<query>\""
    },
    {
      "name": "sensor",
      "args": [
        "name"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-knowledge/api.ts sensor \"<name>\""
    },
    {
      "name": "update",
      "args": [
        "contact-id"
      ],
      "effect": "write",
      "firstCall": "npx tsx ~/.claude/skills/snappy-knowledge/api.ts update <contact-id>"
    }
  ],
  "faces": [],
  "agents": [],
  "uses": [],
  "used_by": [
    "snappy-calendar",
    "snappy-client-template",
    "snappy-course",
    "snappy-imessage"
  ],
  "checks": {
    "pass": 43,
    "fail": 8,
    "defer": 10,
    "line": "43 of 51 checks pass",
    "failing": [
      {
        "id": 4,
        "rule": "Near-neighbour descriptions cross-reference each other",
        "says": "near neighbours not named: snappy-client-feisst"
      },
      {
        "id": 5,
        "rule": "Description is valid, third-person, and says what plus when",
        "says": "first/second person"
      },
      {
        "id": 8,
        "rule": "Skill body and reference graph stay within disclosure budgets",
        "says": "225 lines; ~3224 tokens; deep reference ../snappy-sales/SKILL.md; ../snappy-sales/SKILL.md >100 lines without ToC; deep reference ../snappy-clients/SKILL.md; ../snappy-clients/SKILL.md >100 lines without ToC; deep reference ../snappy-testimonials/SKILL.md; ../snappy-testimonials/SKILL.md >100 lines without ToC; deep reference ../snappy-ops/SKILL.md; ../snappy-ops/SKILL.md >100 lines without ToC; deep reference ../snappy-transcripts/SKILL.md; ../snappy-transcripts/SKILL.md >100 lines without ToC"
      },
      {
        "id": 10,
        "rule": "One term names each concept",
        "says": "mixed synonym sets: endpoint/url/route/path"
      },
      {
        "id": 40,
        "rule": "Metadata, skill body, and resources follow three-level budgets",
        "says": "metadata ~159 tokens; body ~3224 tokens"
      },
      {
        "id": 54,
        "rule": "At least three eval scenarios precede ready status",
        "says": "0 eval scenario(s); ready=true"
      },
      {
        "id": 55,
        "rule": "Eval scenarios are multi-call with ground truth",
        "says": "eval manifest missing"
      },
      {
        "id": 57,
        "rule": "Eval manifest contains a disjoint holdout",
        "says": "0 disjoint holdout scenario(s)"
      }
    ]
  },
  "coverage": null,
  "spec": null,
  "evals": null,
  "files": [
    "AGENTS.md",
    "SKILL.md",
    "api.ts",
    "contract.test.ts",
    "data/last-contact-backfill-2026-04-11.md",
    "data/tune-up-outreach-queue-2026-04-11.md",
    "endpoints.md",
    "entities.json",
    "faces/components/knowledge-faces.css",
    "faces/components/knowledge-faces.tsx",
    "faces/family.tsx",
    "faces/fixtures/knowledge-company.json",
    "faces/fixtures/knowledge-contact.json",
    "faces/fixtures/knowledge-hits.json",
    "metrics.json",
    "schemas.md",
    "sensors.ts",
    "workflows.md"
  ],
  "total_size": 163005,
  "checksum": "sha256:cd8fdf3bd971332a24acc7ed3ead504efd009dce40d38fde135557176565b835",
  "built_at": "2026-09-09T22:09:43.106Z",
  "openapi": {
    "openapi": "3.1.0",
    "info": {
      "title": "snappy-knowledge",
      "version": "0.0.0",
      "description": "Snappy Knowledge Graph -- contact management, company profiles, relationship mapping, interaction history, meeting and call prep, post-call capture, dormant outreach. The CRM brain layer. Triggers on: who is, contact info, company info, knowledge graph, relationship, meeting prep, call prep, add contact, update contact, log interaction, what do we know about, prep for meeting, prep for call, after the call, debrief, relationship map, pre-call brief, post-call capture, new contact, log call, email context, relationship maintenance, stale contacts, re-engage, met someone, follow-up reminder, VIP, advisor, prospect, client lookup."
    },
    "x-snappy-transport": "cli",
    "servers": [
      {
        "url": "npx tsx ~/.claude/skills/snappy-knowledge/api.ts",
        "description": "The hand itself. Every path is a verb word, every query or body key an argument."
      }
    ],
    "paths": {
      "/birthdays": {
        "get": {
          "operationId": "snappy_knowledge_birthdays",
          "summary": "snappy-knowledge birthdays",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/bulk-create": {
        "post": {
          "operationId": "snappy_knowledge_bulk_create",
          "summary": "snappy-knowledge bulk-create",
          "x-snappy-effect": "write",
          "x-snappy-annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "requestBody": {
            "required": false,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "contacts-json": {
                      "type": "string",
                      "description": "JSON array of contact objects to create",
                      "x-snappy-position": 0
                    }
                  }
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/bulk-update": {
        "post": {
          "operationId": "snappy_knowledge_bulk_update",
          "summary": "snappy-knowledge bulk-update",
          "x-snappy-effect": "write",
          "x-snappy-annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "requestBody": {
            "required": false,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "updates-json": {
                      "type": "string",
                      "description": "JSON array of {id, data} objects to apply",
                      "x-snappy-position": 0
                    }
                  }
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/contacts": {
        "get": {
          "operationId": "snappy_knowledge_contacts",
          "summary": "snappy-knowledge contacts",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "filter",
              "in": "query",
              "required": false,
              "schema": {
                "type": "string",
                "description": "Narrow the list to contacts matching this text; omit for all",
                "x-snappy-position": 0
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/create": {
        "post": {
          "operationId": "snappy_knowledge_create",
          "summary": "snappy-knowledge create",
          "x-snappy-effect": "write",
          "x-snappy-annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "requestBody": {
            "required": false,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "contact-json": {
                      "type": "string",
                      "description": "JSON object of contact fields",
                      "x-snappy-position": 0
                    }
                  }
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/dormant": {
        "get": {
          "operationId": "snappy_knowledge_dormant",
          "summary": "snappy-knowledge dormant",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "days",
              "in": "query",
              "required": false,
              "schema": {
                "type": "integer",
                "description": "How many days of silence make a contact dormant",
                "default": 30,
                "maximum": 3650,
                "x-snappy-position": 0
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/entities": {
        "get": {
          "operationId": "snappy_knowledge_entities",
          "summary": "snappy-knowledge entities",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "page",
              "in": "query",
              "required": false,
              "schema": {
                "type": "integer",
                "description": "Page of the entity list, one-based",
                "default": 1,
                "x-snappy-position": 0
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/entity": {
        "get": {
          "operationId": "snappy_knowledge_entity",
          "summary": "snappy-knowledge entity",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "entity-id",
              "in": "query",
              "required": true,
              "schema": {
                "type": "string",
                "description": "Entity identifier",
                "x-snappy-position": 0
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/get": {
        "get": {
          "operationId": "snappy_knowledge_get",
          "summary": "snappy-knowledge get",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "contact-id",
              "in": "query",
              "required": true,
              "schema": {
                "type": "string",
                "description": "Contact identifier",
                "x-snappy-position": 0
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/interaction": {
        "get": {
          "operationId": "snappy_knowledge_interaction",
          "summary": "snappy-knowledge interaction",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "interaction-id",
              "in": "query",
              "required": true,
              "schema": {
                "type": "string",
                "description": "Interaction identifier",
                "x-snappy-position": 0
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/interactions": {
        "get": {
          "operationId": "snappy_knowledge_interactions",
          "summary": "snappy-knowledge interactions",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "page",
              "in": "query",
              "required": false,
              "schema": {
                "type": "integer",
                "description": "Page of the interaction list, one-based",
                "default": 1,
                "x-snappy-position": 0
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/link": {
        "post": {
          "operationId": "snappy_knowledge_link",
          "summary": "snappy-knowledge link",
          "x-snappy-effect": "write",
          "x-snappy-annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "requestBody": {
            "required": true,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "entity-id": {
                      "type": "string",
                      "description": "Entity being linked",
                      "x-snappy-position": 0
                    },
                    "person-id": {
                      "type": "string",
                      "description": "Person the entity is linked to",
                      "x-snappy-position": 1
                    }
                  },
                  "required": [
                    "entity-id",
                    "person-id"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/log-interaction": {
        "post": {
          "operationId": "snappy_knowledge_log_interaction",
          "summary": "snappy-knowledge log-interaction",
          "x-snappy-effect": "write",
          "x-snappy-annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "requestBody": {
            "required": false,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "interaction-json": {
                      "type": "string",
                      "description": "JSON object describing the interaction to record",
                      "x-snappy-position": 0
                    }
                  }
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/metrics": {
        "get": {
          "operationId": "snappy_knowledge_metrics",
          "summary": "snappy-knowledge metrics",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "name",
              "in": "query",
              "required": true,
              "schema": {
                "type": "string",
                "description": "Name of the knowledge metric to compute",
                "x-snappy-position": 0
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/resolve": {
        "get": {
          "operationId": "snappy_knowledge_resolve",
          "summary": "snappy-knowledge resolve",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "identity-json",
              "in": "query",
              "required": false,
              "schema": {
                "type": "string",
                "description": "JSON object of identifying fields, such as an email address",
                "x-snappy-position": 0
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/search": {
        "get": {
          "operationId": "snappy_knowledge_search",
          "summary": "snappy-knowledge search",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "query",
              "in": "query",
              "required": true,
              "schema": {
                "type": "string",
                "description": "Text searched for across contacts",
                "x-snappy-position": 0
              }
            },
            {
              "name": "limit",
              "in": "query",
              "required": false,
              "schema": {
                "type": "integer",
                "description": "How many matches to return. The count is the FLAG --limit, never a positional word; default 20, ceiling 200",
                "default": 20,
                "minimum": 1,
                "maximum": 200,
                "x-snappy-flag": "--limit"
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/sensor": {
        "get": {
          "operationId": "snappy_knowledge_sensor",
          "summary": "snappy-knowledge sensor",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "name",
              "in": "query",
              "required": true,
              "schema": {
                "type": "string",
                "description": "Sensor whose current reading is returned",
                "x-snappy-position": 0
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/update": {
        "post": {
          "operationId": "snappy_knowledge_update",
          "summary": "snappy-knowledge update",
          "x-snappy-effect": "write",
          "x-snappy-annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "requestBody": {
            "required": true,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "contact-id": {
                      "type": "string",
                      "description": "Contact identifier to update",
                      "x-snappy-position": 0
                    }
                  },
                  "required": [
                    "contact-id"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      }
    },
    "components": {
      "schemas": {
        "SnappyRefusal": {
          "type": "object",
          "required": [
            "outcome",
            "code",
            "message"
          ],
          "properties": {
            "outcome": {
              "type": "string",
              "const": "refused"
            },
            "code": {
              "type": "string",
              "description": "A row of the closed refusal table."
            },
            "message": {
              "type": "string"
            },
            "fix": {
              "type": "string"
            }
          }
        }
      }
    },
    "x-snappy-requires": [
      "XANO_METADATA_TOKEN"
    ]
  }
}