{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://anakai.dev/schemas/behaviour-graph/v1.0.json",
  "title": "Anakai Kai Coaching Mode — Behaviour Graph v1.0",
  "description": "Canonical schema for chapter artifacts. Versioned. Baseline derived from ch.0 + ch.1. Minor bumps on field additions. Do not hand-edit — regenerate from pipeline amendment process when a new field is introduced.",
  "version": "1.0.0",
  "$defs": {
    "ArtifactId": {
      "type": "string",
      "description": "Global unique artifact identifier. Prefix encodes node type.",
      "pattern": "^(S-[a-z][a-z0-9-]*|L-[0-9]{2}-[0-9]{2}|P-[0-9]{2}-[0-9]{2}|AP-[0-9]{2}-[0-9]{2}|Sc-[0-9]{2}-[0-9]{2}(-[a-z]+)?|ext-[a-z][a-z0-9-]*)$",
      "examples": ["S-skill-committed", "L-01-01", "P-00-01", "AP-01-02", "Sc-01-01", "Sc-01-01-kr"]
    },
    "StepId": {
      "type": "string",
      "description": "Step identifier within a loop. Format: step-N-<kebab-name>.",
      "pattern": "^step-[0-9]+-[a-z][a-z0-9-]*$",
      "examples": ["step-1-set-capture-context", "step-2-dump-without-filter"]
    },
    "FullStepRef": {
      "type": "string",
      "description": "Fully-qualified step reference used in script.linked_implements_step. Format: <loop-id>.<step-id>.",
      "pattern": "^L-[0-9]{2}-[0-9]{2}\\.step-[0-9]+-[a-z][a-z0-9-]*$",
      "examples": ["L-01-05.step-2-find-light-stay-in-touch-reason"]
    },
    "NodeType": {
      "type": "string",
      "enum": ["user_state", "loop", "principle", "anti_pattern", "script"],
      "description": "The five node types in the Behaviour Graph."
    },
    "EdgeType": {
      "type": "string",
      "enum": [
        "triggers",
        "transitions_to",
        "backed_by",
        "corrects",
        "vulnerable_to",
        "precedes",
        "depends_on",
        "implements_step",
        "paraphrase_of",
        "edifies"
      ],
      "description": "All 10 canonical edge types. See protocols/edge-cheatsheet.md for direction rules and disambiguation."
    },
    "GapTag": {
      "type": "string",
      "description": "Canonical reason tag for gap-list entries. The prefix 'restatement-of-' must be followed by a valid artifact id.",
      "oneOf": [
        {
          "pattern": "^restatement-of-(S-[a-z][a-z0-9-]*|L-[0-9]{2}-[0-9]{2}|P-[0-9]{2}-[0-9]{2}|AP-[0-9]{2}-[0-9]{2}|Sc-[0-9]{2}-[0-9]{2}(-[a-z]+)?)$"
        },
        {
          "enum": ["anecdote", "transitional", "recap", "meta-commentary", "forward-reference"]
        }
      ]
    },
    "SourceRef": {
      "type": "string",
      "description": "Internal audit anchor only — never user-facing, never rendered in FigJam. Format: 'ch.NN p.NN para.N | \"<≤30-word verbatim snippet>\"'. The verbatim snippet is the only location in the artifact where source text is permitted verbatim.",
      "examples": [
        "ch.01 p.30 para.11 | \"Step one: Make your list as comprehensive as possible. Include every person you can think of.\""
      ]
    },
    "VoiceContextTag": {
      "type": "string",
      "enum": [
        "warm-market",
        "cold-market",
        "family",
        "friend",
        "professional",
        "newcomer",
        "experienced",
        "onboarding",
        "low-awareness",
        "diagnostic"
      ],
      "description": "Context tags describing when a node is activated. See spec §3.3 for full vocabulary."
    },
    "LanguageCode": {
      "type": "string",
      "enum": ["en", "ru", "kr", "jp", "vn"],
      "description": "Language code for script nodes. Canon scripts are en; variants are separate nodes linked via paraphrase_of."
    },
    "LoopStep": {
      "type": "object",
      "description": "A single step within a loop. Steps serve dual purpose: audit structure and script-attachment anchor.",
      "properties": {
        "id": {
          "$ref": "#/$defs/StepId",
          "description": "Step identifier. Used as the step-side of a FullStepRef in script.linked_implements_step."
        },
        "kai_intent": {
          "type": "string",
          "description": "Required. One-sentence statement of what Kai aims to achieve in this step — not what the user does; what Kai's role is. Procedural, not voiced. The voiced response lives in kai_voice_draft or the script body. Closes the 'opaque labels' gap (Terry feedback 2026-06-10).",
          "minLength": 10,
          "maxLength": 200
        },
        "script_ref": {
          "oneOf": [
            { "$ref": "#/$defs/ArtifactId" },
            { "type": "null" }
          ],
          "description": "Optional. The Sc-XX-YY id of a script node that implements this exact step. Omit (or null) if no script implements this step."
        }
      },
      "required": ["id", "kai_intent"],
      "additionalProperties": false
    },
    "UserState": {
      "type": "object",
      "description": "A discrete stage a user occupies on the journey map. States are places, not actions — they receive triggers and are entered by loop transitions.",
      "properties": {
        "id": { "$ref": "#/$defs/ArtifactId" },
        "type": { "const": "user_state" },
        "title": {
          "type": "string",
          "description": "Short descriptive label for the state.",
          "minLength": 3,
          "maxLength": 80
        },
        "body": {
          "type": "string",
          "description": "Paraphrased description of this stage in our language. 1–3 sentences. Must not reproduce source text verbatim (see IP rule in protocols/extraction.md §7).",
          "minLength": 20
        },
        "source_ref": { "$ref": "#/$defs/SourceRef" },
        "kai_voice_draft": {
          "type": "string",
          "description": "How a seasoned Anakai top-KOL would describe or address this state to the user. 1–3 sentences. Subject to voice persona constraints (voice/kai-voice-persona.md).",
          "minLength": 10
        },
        "voice_context_tags": {
          "type": "array",
          "items": { "$ref": "#/$defs/VoiceContextTag" },
          "minItems": 1,
          "description": "Contexts in which this node is typically activated."
        },
        "entry_criteria": {
          "type": "string",
          "description": "One line — what observation or signal places the user in this state.",
          "minLength": 5
        },
        "exit_criteria": {
          "type": "string",
          "description": "One line — what indicates the user has moved on from this state.",
          "minLength": 5
        },
        "linked_triggers": {
          "type": "array",
          "items": { "$ref": "#/$defs/ArtifactId" },
          "description": "Loop(s) that fire when the user is in this state. May be empty for entry-only states (e.g., ch.0 states whose loops belong to later chapters)."
        },
        "linked_vulnerable_to": {
          "type": "array",
          "items": { "$ref": "#/$defs/ArtifactId" },
          "description": "Anti-pattern(s) this state makes tempting. May be empty."
        },
        "notes": {
          "type": "string",
          "description": "Reviewer notes. Optional."
        }
      },
      "required": [
        "id",
        "title",
        "body",
        "source_ref",
        "kai_voice_draft",
        "voice_context_tags",
        "entry_criteria",
        "exit_criteria",
        "linked_triggers",
        "linked_vulnerable_to"
      ],
      "additionalProperties": false
    },
    "Loop": {
      "type": "object",
      "description": "A repeatable behavioural unit: trigger → user action → Kai response → outcome. Loops can span multiple sessions.",
      "properties": {
        "id": { "$ref": "#/$defs/ArtifactId" },
        "type": { "const": "loop" },
        "title": {
          "type": "string",
          "description": "Short descriptive label.",
          "minLength": 3,
          "maxLength": 80
        },
        "body": {
          "type": "string",
          "description": "Paraphrased description of this behavioural cycle: trigger → user action → Kai response → outcome. 2–4 sentences.",
          "minLength": 40
        },
        "source_ref": { "$ref": "#/$defs/SourceRef" },
        "kai_voice_draft": {
          "type": "string",
          "description": "How a seasoned Anakai top-KOL would coach a user through this loop. 1–3 sentences.",
          "minLength": 10
        },
        "voice_context_tags": {
          "type": "array",
          "items": { "$ref": "#/$defs/VoiceContextTag" },
          "minItems": 1
        },
        "steps": {
          "type": "array",
          "description": "Ordered list of steps in the loop. Each step carries a kai_intent (required) and optional script_ref. Step ids become FullStepRef targets in script.linked_implements_step.",
          "items": { "$ref": "#/$defs/LoopStep" },
          "minItems": 2
        },
        "linked_transitions_to": {
          "type": "array",
          "items": { "$ref": "#/$defs/ArtifactId" },
          "description": "User state(s) the user lands in after this loop completes."
        },
        "linked_backed_by": {
          "type": "array",
          "items": { "$ref": "#/$defs/ArtifactId" },
          "description": "Principle(s) that explain why this loop works."
        },
        "linked_precedes": {
          "type": "array",
          "items": { "$ref": "#/$defs/ArtifactId" },
          "description": "Loop(s) that naturally follow this one in the journey. May reference unextracted forward-ref ids (noted in cross_chapter_links)."
        },
        "linked_edifies": {
          "type": "array",
          "items": { "type": "string" },
          "description": "External actors edified by this loop, if any. Named as strings (not artifact ids)."
        },
        "notes": {
          "type": "string",
          "description": "Reviewer notes. Optional."
        }
      },
      "required": [
        "id",
        "title",
        "body",
        "source_ref",
        "kai_voice_draft",
        "voice_context_tags",
        "steps",
        "linked_transitions_to",
        "linked_backed_by",
        "linked_precedes",
        "linked_edifies"
      ],
      "additionalProperties": false
    },
    "Principle": {
      "type": "object",
      "description": "A framework truth or named lesson that explains why a loop works. Principles have no trigger — they are backing logic referenced via backed_by edges.",
      "properties": {
        "id": { "$ref": "#/$defs/ArtifactId" },
        "type": { "const": "principle" },
        "title": {
          "type": "string",
          "description": "Short descriptive label.",
          "minLength": 3,
          "maxLength": 80
        },
        "body": {
          "type": "string",
          "description": "Paraphrased substance in our wording. 1–3 sentences.",
          "minLength": 20
        },
        "source_ref": { "$ref": "#/$defs/SourceRef" },
        "kai_voice_draft": {
          "type": "string",
          "description": "How a seasoned Anakai top-KOL would express this principle to the user. 1–3 sentences.",
          "minLength": 10
        },
        "voice_context_tags": {
          "type": "array",
          "items": { "$ref": "#/$defs/VoiceContextTag" },
          "minItems": 1
        },
        "linked_corrects": {
          "type": "array",
          "items": { "$ref": "#/$defs/ArtifactId" },
          "description": "Anti-pattern(s) this principle corrects."
        },
        "linked_depends_on": {
          "type": "array",
          "items": { "$ref": "#/$defs/ArtifactId" },
          "description": "Principle(s) that must be internalised first. May be empty."
        },
        "notes": {
          "type": "string",
          "description": "Reviewer notes. Optional."
        }
      },
      "required": [
        "id",
        "title",
        "body",
        "source_ref",
        "kai_voice_draft",
        "voice_context_tags",
        "linked_corrects",
        "linked_depends_on"
      ],
      "additionalProperties": false
    },
    "AntiPattern": {
      "type": "object",
      "description": "The instinctive wrong behaviour that a principle corrects. Anti-patterns have NO outgoing linked_* edges — all edges are inbound (principle.linked_corrects → here; user_state.linked_vulnerable_to → here).",
      "properties": {
        "id": { "$ref": "#/$defs/ArtifactId" },
        "type": { "const": "anti_pattern" },
        "title": {
          "type": "string",
          "description": "Short descriptive label.",
          "minLength": 3,
          "maxLength": 80
        },
        "body": {
          "type": "string",
          "description": "Paraphrased description of the wrong behaviour and why it feels instinctive. 1–3 sentences.",
          "minLength": 20
        },
        "source_ref": { "$ref": "#/$defs/SourceRef" },
        "kai_voice_draft": {
          "type": "string",
          "description": "Brief reframe in Kai's voice: here is what usually happens and why it costs the user. 1–3 sentences.",
          "minLength": 10
        },
        "voice_context_tags": {
          "type": "array",
          "items": { "$ref": "#/$defs/VoiceContextTag" },
          "minItems": 1
        },
        "trigger_pattern": {
          "type": "string",
          "description": "One line — the instinct or impulse that drives users into this behaviour.",
          "minLength": 5
        },
        "cost": {
          "type": "string",
          "description": "One line — what concretely goes wrong when the user falls into this anti-pattern.",
          "minLength": 5
        },
        "notes": {
          "type": "string",
          "description": "Reviewer notes. Optional."
        }
      },
      "required": [
        "id",
        "title",
        "body",
        "source_ref",
        "kai_voice_draft",
        "voice_context_tags",
        "trigger_pattern",
        "cost"
      ],
      "additionalProperties": false
    },
    "Script": {
      "type": "object",
      "description": "An operational language unit — concrete words the user can actually use. Canon scripts are EN; variants are separate nodes linked via paraphrase_of.",
      "properties": {
        "id": { "$ref": "#/$defs/ArtifactId" },
        "type": { "const": "script" },
        "title": {
          "type": "string",
          "description": "Short descriptive label.",
          "minLength": 3,
          "maxLength": 80
        },
        "body": {
          "type": "string",
          "description": "Full script draft in Kai's voice — operational language the user can actually use.",
          "minLength": 20
        },
        "source_ref": { "$ref": "#/$defs/SourceRef" },
        "kai_voice_draft": {
          "type": "string",
          "description": "How the script sounds when voiced naturally — may overlap body for scripts.",
          "minLength": 10
        },
        "voice_context_tags": {
          "type": "array",
          "items": { "$ref": "#/$defs/VoiceContextTag" },
          "minItems": 1
        },
        "situation": {
          "type": "string",
          "description": "One line — when this script is used: what context or user state triggers it.",
          "minLength": 5
        },
        "language": {
          "$ref": "#/$defs/LanguageCode",
          "description": "Language of this script node. Canon = en; variants = kr/jp/ru/vn."
        },
        "requires_variants": {
          "type": "array",
          "items": { "$ref": "#/$defs/LanguageCode" },
          "description": "Language codes that need native-authored variant nodes for this canon script. Default [kr, jp, ru, vn] for v0 canon scripts. Empty array if no variants planned. Variants are batched after all 7 EN chapters land (Dmitry decision 2026-06-10).",
          "examples": [["kr", "jp", "ru", "vn"], []]
        },
        "linked_backed_by": {
          "type": "array",
          "items": { "$ref": "#/$defs/ArtifactId" },
          "description": "Principle(s) that ground this script's design."
        },
        "linked_implements_step": {
          "type": "array",
          "items": { "$ref": "#/$defs/FullStepRef" },
          "description": "Loop step(s) this script fills. Format: L-XX-YY.step-N-<name>."
        },
        "linked_paraphrase_of": {
          "type": "array",
          "items": { "$ref": "#/$defs/ArtifactId" },
          "description": "Canon script id if this is a variant node. Empty array for canon scripts."
        },
        "linked_edifies": {
          "type": "array",
          "items": { "type": "string" },
          "description": "External actors edified by this script, if any. Named as strings."
        },
        "notes": {
          "type": "string",
          "description": "Reviewer notes. Optional."
        }
      },
      "required": [
        "id",
        "title",
        "body",
        "source_ref",
        "kai_voice_draft",
        "voice_context_tags",
        "situation",
        "language",
        "requires_variants",
        "linked_backed_by",
        "linked_implements_step",
        "linked_paraphrase_of",
        "linked_edifies"
      ],
      "additionalProperties": false
    },
    "GapListEntry": {
      "type": "object",
      "description": "A paragraph-level accounting entry explaining why a source paragraph was not emitted as an artifact.",
      "properties": {
        "para_ref": {
          "type": "string",
          "description": "Paragraph reference in format 'ch.NN p.NN para.N' or 'ch.NN p.NN para.Nb' for sub-paragraph beats.",
          "pattern": "^ch\\.[0-9]{2} p\\.[0-9]+ para\\.[0-9]+[a-z]?$"
        },
        "tag": { "$ref": "#/$defs/GapTag" },
        "note": {
          "type": "string",
          "description": "Optional one-sentence explanation."
        }
      },
      "required": ["para_ref", "tag"],
      "additionalProperties": false
    },
    "ValidationScenarioStep": {
      "type": "object",
      "description": "A single numbered step in a validation scenario.",
      "properties": {
        "step_number": { "type": "integer", "minimum": 1 },
        "text": {
          "type": "string",
          "description": "Step description. May reference node ids (S-*, L-*, P-*, AP-*, Sc-*). All cited node ids must exist in this chapter or a locked prior chapter."
        }
      },
      "required": ["step_number", "text"],
      "additionalProperties": false
    },
    "ValidationScenario": {
      "type": "object",
      "description": "A walkthrough scenario for validator review. Per spec §8.1: 5–10 per chapter.",
      "properties": {
        "title": { "type": "string", "minLength": 3 },
        "steps": {
          "type": "array",
          "items": { "$ref": "#/$defs/ValidationScenarioStep" },
          "minItems": 3
        }
      },
      "required": ["title", "steps"],
      "additionalProperties": false
    },
    "CrossChapterLink": {
      "type": "object",
      "description": "An outgoing edge from this chapter's nodes targeting nodes in prior chapters (or forward slots for later chapters).",
      "properties": {
        "source_id": { "$ref": "#/$defs/ArtifactId" },
        "edge_type": { "$ref": "#/$defs/EdgeType" },
        "target_id": {
          "type": "string",
          "description": "Target artifact id. May be a placeholder (e.g. L-02-XX) for forward-references to unextracted chapters."
        },
        "note": {
          "type": "string",
          "description": "Brief explanation of why this cross-chapter edge exists."
        }
      },
      "required": ["source_id", "edge_type", "target_id"],
      "additionalProperties": false
    },
    "ValidatorsSignoff": {
      "type": "object",
      "description": "Signoff state from the four required reviewers.",
      "properties": {
        "dmitry": {
          "type": "string",
          "enum": ["pending", "passed", "revisions-requested"]
        },
        "adversarial_reviewer": {
          "type": "string",
          "description": "Format: '<N> flags' or '<N> flags — [flag log](../protocols/adversarial-flags-ch.XX.md)'. Green when '0 flags'."
        },
        "roma": {
          "type": "string",
          "enum": ["pending", "signed", "revisions-requested"]
        },
        "maks": {
          "type": "string",
          "enum": ["pending", "signed", "revisions-requested"]
        }
      },
      "required": ["dmitry", "adversarial_reviewer", "roma", "maks"],
      "additionalProperties": false
    },
    "ChapterFrontmatter": {
      "type": "object",
      "description": "YAML frontmatter block at the top of every chapter file.",
      "properties": {
        "id": {
          "type": "string",
          "description": "Chapter id. Format: ch.NN (e.g. ch.00, ch.01, ch.07).",
          "pattern": "^ch\\.[0-9]{2}$"
        },
        "title": {
          "type": "string",
          "description": "Chapter title string.",
          "minLength": 3
        },
        "source_pages": {
          "type": "string",
          "description": "Page range in source PDF. Format: 'pp.NN–NN'.",
          "pattern": "^pp\\.[0-9]+–[0-9]+$"
        },
        "status": {
          "type": "string",
          "enum": ["draft", "in-review", "locked"]
        },
        "validators_signoff": { "$ref": "#/$defs/ValidatorsSignoff" },
        "locked": {
          "type": "boolean",
          "description": "True only when all four validator gate conditions are met. Must stay in sync with the 'Locked:' field in the review_state section."
        }
      },
      "required": ["id", "title", "source_pages", "status", "validators_signoff", "locked"],
      "additionalProperties": false
    },
    "Chapter": {
      "type": "object",
      "description": "Full chapter artifact. Rendered as a markdown file; this schema describes the logical structure.",
      "properties": {
        "frontmatter": { "$ref": "#/$defs/ChapterFrontmatter" },
        "user_states": {
          "type": "array",
          "items": { "$ref": "#/$defs/UserState" },
          "description": "All user_state nodes emitted for this chapter. May include carry-throughs from prior chapters."
        },
        "loops": {
          "type": "array",
          "items": { "$ref": "#/$defs/Loop" },
          "description": "All loop nodes emitted for this chapter. May be empty (e.g. ch.0 intro has no loops)."
        },
        "principles": {
          "type": "array",
          "items": { "$ref": "#/$defs/Principle" }
        },
        "anti_patterns": {
          "type": "array",
          "items": { "$ref": "#/$defs/AntiPattern" }
        },
        "scripts": {
          "type": "array",
          "items": { "$ref": "#/$defs/Script" },
          "description": "All script nodes emitted for this chapter. May be empty (e.g. ch.0 intro has no scripts)."
        },
        "gap_list": {
          "type": "array",
          "items": { "$ref": "#/$defs/GapListEntry" },
          "description": "Coverage proof: every source paragraph is either an artifact or a gap-list entry. Stopping condition from extraction.md §6."
        },
        "validation_scenarios": {
          "type": "array",
          "items": { "$ref": "#/$defs/ValidationScenario" },
          "minItems": 5,
          "maxItems": 10,
          "description": "Per spec §8.1: 5–10 scenarios per chapter."
        },
        "cross_chapter_links": {
          "type": "array",
          "items": { "$ref": "#/$defs/CrossChapterLink" },
          "description": "Outgoing edges from this chapter's nodes targeting nodes in prior (or forward) chapters."
        }
      },
      "required": [
        "frontmatter",
        "user_states",
        "loops",
        "principles",
        "anti_patterns",
        "scripts",
        "gap_list",
        "validation_scenarios",
        "cross_chapter_links"
      ],
      "additionalProperties": false
    }
  },
  "type": "object",
  "$ref": "#/$defs/Chapter",
  "notes": [
    "This schema is EN-only. The requires_variants field on Script is the signal for the KR/JP/RU/VN variant workstream.",
    "Variant script nodes are separate artifacts linked via paraphrase_of. They are NOT encoded as language sub-keys in this schema.",
    "LoopStep.kai_intent is required (not optional). Closes the 'opaque labels' gap from Terry's 2026-06-10 analysis.",
    "Minor version bump (v1.0 → v1.1) required when any chapter introduces a new field. Regenerate this file and update the Phase 6 schema-versioning checklist item.",
    "Baseline: ch.0 (2 states, 0 loops, 4 principles, 3 anti-patterns, 0 scripts) + ch.1 (4 states, 5 loops, 4 principles, 5 anti-patterns, 1 script). Total v1.0 baseline: 6 states, 5 loops, 8 principles, 8 anti-patterns, 1 script."
  ]
}
