{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "skill-observation.schema.json",
  "title": "Skill observation",
  "description": "One noticed piece of friction in the pipeline's own skills, written the moment it is noticed. The store is a directory of markdown files whose frontmatter conforms to this schema; the directory listing IS the index, so there is no index file to keep in sync and no scan that reads a body. This schema describes that frontmatter. The learnings ledger holds what a run learned about a REPO; this holds what a run learned about the PIPELINE, which nothing captured before: /multi-agent:refactor derived its findings from scratch on every invocation, so a friction noticed on Tuesday was gone by Wednesday unless it was fixed the same hour.",
  "type": "object",
  "additionalProperties": false,
  "required": ["id", "title", "status", "target", "siblings_checked", "area", "date"],
  "properties": {
    "id": {
      "type": "string",
      "pattern": "^[0-9]{4}$",
      "description": "Zero-padded sequence number, matching the file's NNNN- prefix."
    },
    "title": {
      "type": "string",
      "minLength": 8,
      "description": "One line, stating the friction rather than the fix. 'refactor re-derives findings every run' is an observation; 'add a backlog mode' is a proposal, and belongs in the body."
    },
    "status": {
      "type": "string",
      "enum": ["open", "actioned", "declined", "superseded", "parked"],
      "description": "open = in the queue. actioned = a change shipped. declined = decided against, with the reason in resolution. superseded = another observation covers it. parked = decided, but blocked on something outside this repo; it leaves the queue without being archived, and parked_until is then required. Five values rather than open/closed because 'declined' and 'parked' are the two that get silently dropped when the vocabulary is too small - a parked item with no expiry is a deferral wearing a disguise."
    },
    "parked_until": {
      "type": "string",
      "description": "Required when status is parked: the concrete event that unblocks it (a version, a release, an upstream fix). 'more data' is not an event - if no observation could change the decision and no date is nameable, the honest status is declined."
    },
    "target": {
      "type": "array",
      "minItems": 1,
      "items": { "type": "string" },
      "description": "Repo-relative paths the observation is about. Always a list, even for one path: a scalar here means every consumer needs two code paths, and the one that handles the scalar is the one that gets forgotten.",
      "uniqueItems": true
    },
    "proposes_skill": {
      "type": "array",
      "items": { "type": "string" },
      "description": "Skills or commands that do not exist yet but should, if this observation implies one."
    },
    "siblings_checked": {
      "type": "string",
      "minLength": 3,
      "description": "What was found when the sibling surfaces were checked. This tree mirrors every command into skills/shared/core and again into the Copilot and Codex trees, so a fix applied to one copy drifts silently from the rest. 'checked, does not apply to the shared skill' is a valid answer; empty is not, which is why the field is required and why skill-siblings.mjs fills it mechanically rather than from memory."
    },
    "area": {
      "type": "string",
      "description": "Rough grouping for the weekly review: a phase name, a subsystem, 'gates', 'docs'."
    },
    "date": {
      "type": "string",
      "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$",
      "description": "ISO date the observation was written, absolute so it survives being read a year later."
    },
    "session_context": {
      "type": "string",
      "description": "What the session was doing when the friction appeared. Free text, kept short; it is what makes an old observation legible."
    },
    "resolved": {
      "type": "string",
      "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$",
      "description": "ISO date the status left 'open'."
    },
    "resolution": {
      "type": "string",
      "description": "One line saying what happened. Required in practice for declined and superseded - a decline with no reason is indistinguishable from neglect."
    },
    "reference": {
      "type": "string",
      "description": "A commit, PR, or issue that carried the change."
    }
  }
}
