{
  "$type": "com.atproto.lexicon.schema",
  "defs": {
    "defaults": {
      "description": "Pack-level drill defaults (SPEC §4). Open additional keys round-trip via the pack `meta`.",
      "properties": {
        "katakanaMultiplier": {
          "description": "Queue weight multiplier for katakana. Fixed-point ×10000 (e.g. 1.5 -> 15000); no float type.",
          "type": "integer"
        },
        "tapToTypeBox": {
          "description": "Leitner box at/above which a glyph may graduate tap -> type.",
          "maximum": 5,
          "minimum": 1,
          "type": "integer"
        }
      },
      "type": "object"
    },
    "forms": {
      "description": "Authored conjugation surfaces, keyed by the form names in src/drill/conjugate.ts. Kana only — the kanji surface is derived from the item's `kanji` form. A key this build does not know is ignored, so a form added later needs no lexicon change.",
      "properties": {
        "chau": {
          "type": "string"
        },
        "imperative": {
          "type": "string"
        },
        "masu": {
          "type": "string"
        },
        "nai": {
          "type": "string"
        },
        "nakatta": {
          "type": "string"
        },
        "nakya": {
          "type": "string"
        },
        "potential": {
          "type": "string"
        },
        "ta": {
          "type": "string"
        },
        "te": {
          "type": "string"
        },
        "teru": {
          "type": "string"
        },
        "toku": {
          "type": "string"
        },
        "volitional": {
          "type": "string"
        }
      },
      "type": "object"
    },
    "item": {
      "description": "A card. A discriminated union on `type` (#1), flattened into one object because lexicons have no oneOf: a RECOGNITION item (hiragana | katakana | kanji | word) requires glyph + romaji, a GRAMMAR item (particle | contraction | usage | sentence) requires id + prompt + answer. `conjugation` is a RECOGNITION type (#146): a derived verb surface, so it carries glyph + romaji like any word. Only `type` can be required here; the per-branch requiredness is enforced by lessons/pack.schema.json (JSON Schema if/then) and src/packs/validate.ts, which is what every reader of this record already routes through.",
      "properties": {
        "accept": {
          "description": "GRAMMAR branch, optional. Alternative answers graded correct beside `answer` — casual forms routinely have two natural ones. Exact-match grading is unchanged; the accepted set just has more members.",
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        "answer": {
          "description": "GRAMMAR branch, required there. The one canonical answer; graded by exact compare after trim.",
          "type": "string"
        },
        "choices": {
          "description": "GRAMMAR branch, optional. Authored options for the choice grid (#145); contains `answer` when present. Never empty — the app drops an empty or wrong-typed list at the upload boundary rather than writing one.",
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        "confusableWith": {
          "description": "ROMAJI (not glyphs) of same-script confusers; cold-start distractor seed.",
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        "forms": {
          "description": "Authored kana surfaces for the forms no rule derives (#136) — an irregular's whole paradigm, or one form of an otherwise regular verb (iku's itte). A form with no entry falls back to the derivation.",
          "ref": "#forms",
          "type": "ref"
        },
        "gloss": {
          "description": "English meaning; recommended for words/kanji, and for a grammar item it is the meaning of the whole line.",
          "type": "string"
        },
        "glyph": {
          "description": "RECOGNITION branch, required there. 1+ chars: single kana, word, or kanji.",
          "type": "string"
        },
        "group": {
          "description": "Verb conjugation class (#136): 1 godan, 2 ichidan, 3 irregular. Word items only. The app derives every form from it (src/drill/conjugate.ts) instead of the pack authoring them.",
          "maximum": 3,
          "minimum": 1,
          "type": "integer"
        },
        "id": {
          "description": "GRAMMAR branch, required there and unique within the pack. The SRS state map is glyph-keyed and a grammar item has no glyph, so it is keyed `grammar:<id>` (#145). Uniqueness is enforced by validate.ts, not here.",
          "type": "string"
        },
        "kanji": {
          "description": "Written form in kanji (or kanji + okurigana) of a kana word — にほんじん -> 日本人. The below-hint path is passive (#125); a usable ruby meaning prompt records the separate kanji-recognition lane (#171). Omit where there is no kanji form (gairaigo) or the word is normally written in kana.",
          "type": "string"
        },
        "meta": {
          "description": "Item-level open extension; UI ignores, round-trips.",
          "type": "unknown"
        },
        "prompt": {
          "description": "GRAMMAR branch, required there. The sentence, with the blank written by the author (U+FF3F). Opaque to this layer: it may carry `{kanji|kana}` ruby markup (#139).",
          "type": "string"
        },
        "romaji": {
          "description": "RECOGNITION branch, required there. Expected reading; lowercase Hepburn, exact case-insensitive match.",
          "type": "string"
        },
        "row": {
          "description": "Gojuon row label, single kana and combination kana (yoon + extended) only.",
          "type": "string"
        },
        "type": {
          "description": "Open. Recognition: hiragana | katakana | kanji | word | conjugation (#146, a derived verb surface). Grammar (#1): particle | contraction | usage | sentence.",
          "type": "string"
        },
        "weight": {
          "description": "Optional author priority override. Fixed-point ×10000 (e.g. 1.8 -> 18000); lexicons have no float type. App scales at the record edge (see README).",
          "type": "integer"
        }
      },
      "required": [
        "type"
      ],
      "type": "object"
    },
    "main": {
      "description": "Lesson content the app drills. Append-only stack; latest = active. Free-form by design: a known core the UI reads + open `meta`/`defaults` round-tripped untouched (SPEC §3.1a). This record body IS the upload interchange shape (lessons/pack.schema.json).",
      "key": "tid",
      "record": {
        "properties": {
          "createdAt": {
            "description": "ISO 8601. NOTE: the app currently stamps a date-only string (YYYY-MM-DD), not a datetime — left unformatted here pending reconciliation (see README).",
            "type": "string"
          },
          "defaults": {
            "ref": "#defaults",
            "type": "ref"
          },
          "items": {
            "items": {
              "ref": "#item",
              "type": "ref"
            },
            "type": "array"
          },
          "meta": {
            "description": "Pack-level open extension; UI ignores, round-trips.",
            "type": "unknown"
          },
          "phase": {
            "description": "Open. kana | vocab | grammar.",
            "type": "string"
          },
          "rationale": {
            "type": "string"
          },
          "script": {
            "description": "Declared scripts; drives chips + Focus. The scripts the glyphs are WRITTEN in — for a `word` item that is its kana script, not \"word\" (#99). Derived the same way if omitted; a word that is unsegmentable or mixes both kana falls back to \"word\". A declared script is cleaned by the app (#99): \"word\", junk and duplicates are dropped and the rest kept; \"word\" returns only if derivation finds an item with no derivable script.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "title": {
            "type": "string"
          }
        },
        "required": [
          "title",
          "items"
        ],
        "type": "object"
      },
      "type": "record"
    }
  },
  "id": "at.kotoba.lesson.pack",
  "lexicon": 1
}
