# at.kotoba.state

> Published by [kotoba.at](https://lexicon.garden/identity/did:plc:o5f3cgna5sku4p6iitld6mxs)

✓ This is the authoritative definition for this NSID.

## Links

- [View on Lexicon Garden](https://lexicon.garden/lexicon/did:plc:o5f3cgna5sku4p6iitld6mxs/at.kotoba.state)
- [Documentation](https://lexicon.garden/lexicon/did:plc:o5f3cgna5sku4p6iitld6mxs/at.kotoba.state/docs)
- [Examples](https://lexicon.garden/lexicon/did:plc:o5f3cgna5sku4p6iitld6mxs/at.kotoba.state/examples)

## Definitions

### `at.kotoba.state`

**Type**: `record`

Single overwritten record: per-glyph SRS state + the directed confusion map. Read once at startup; overwritten on flush. rkey is literally `self`.

**Key**: `literal:self`

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `glyphs` | `unknown` | Yes | Map keyed by glyph -> { box:int, lastSeen:str, modes:{ tap:{seen,missed,ewmaParseMs\|null}, type:{seen,missed,ewmaParseMs\|null} } }. Per-mode (tap vs type) success + latency; box is shared. All-integer. Legacy flat records ({box,ewmaParseMs,seen,missed}) are migrated to tap on read. `unknown` because lexicons have no dynamic-key map type (see README). |
| `confusion` | `unknown` | Yes | Directed, single-kana only: shown glyph -> { answered-as romaji: count }. Counts are decayed (x0.9/session) so the app stores them as fixed-point ×10000 INTEGERS (no float type). `unknown` map; the app owns the value scaling at the record edge. |
| `updatedAt` | `string` (datetime) | Yes |  |

## Raw Schema

```json
{
  "id": "at.kotoba.state",
  "defs": {
    "main": {
      "key": "literal:self",
      "type": "record",
      "record": {
        "type": "object",
        "required": [
          "updatedAt",
          "glyphs",
          "confusion"
        ],
        "properties": {
          "glyphs": {
            "type": "unknown",
            "description": "Map keyed by glyph -> { box:int, lastSeen:str, modes:{ tap:{seen,missed,ewmaParseMs|null}, type:{seen,missed,ewmaParseMs|null} } }. Per-mode (tap vs type) success + latency; box is shared. All-integer. Legacy flat records ({box,ewmaParseMs,seen,missed}) are migrated to tap on read. `unknown` because lexicons have no dynamic-key map type (see README)."
          },
          "confusion": {
            "type": "unknown",
            "description": "Directed, single-kana only: shown glyph -> { answered-as romaji: count }. Counts are decayed (x0.9/session) so the app stores them as fixed-point ×10000 INTEGERS (no float type). `unknown` map; the app owns the value scaling at the record edge."
          },
          "updatedAt": {
            "type": "string",
            "format": "datetime"
          }
        }
      },
      "description": "Single overwritten record: per-glyph SRS state + the directed confusion map. Read once at startup; overwritten on flush. rkey is literally `self`."
    }
  },
  "$type": "com.atproto.lexicon.schema",
  "lexicon": 1
}
```
