{
  "experiment": "ci-run",
  "generated_at": "2026-04-30 19:43 UTC",
  "workload_docs": {
    "unicode-normalization": [
      {
        "mutations": [
          "composition_table_astral_99d6e46_1"
        ],
        "tasks": [
          {
            "property": "ComposeTable",
            "witnesses": [
              {
                "test_fn": "witness_compose_table_case_astral_105d2"
              },
              {
                "test_fn": "witness_compose_table_case_astral_105da"
              }
            ]
          }
        ],
        "source": {
          "repo": "https://github.com/unicode-rs/unicode-normalization",
          "commits": [
            "99d6e46cfbe1b9bc84249a84f9e58b7619c106e0"
          ],
          "commit_subjects": [
            "Include astral-plane compositions in the generated table"
          ],
          "summary": "The composition-table generator emitted `compose(c1, c2) -> c3` entries only when all three codepoints were astral, so astral+BMP canonical compositions silently fell through to `None`. The fix widens the generator to emit the mixed-plane entries as well."
        },
        "injection": {
          "kind": "marauders",
          "files": [
            "src/tables.rs"
          ]
        },
        "bug": {
          "short_name": "composition_table_astral",
          "invariant": "`compose(c1, c2)` returns the expected canonical composition whenever the Unicode data defines one, including across the BMP/astral-plane boundary.",
          "how_triggered": "The generator's astral-gating predicate suppressed mixed-plane canonical compositions; the mutation reinstates the over-strict guard so astral+BMP pairs that ought to compose return `None`."
        }
      },
      {
        "mutations": [
          "hangul_compose_lvt_e4fd0e1_1"
        ],
        "tasks": [
          {
            "property": "HangulComposeLvt",
            "witnesses": [
              {
                "test_fn": "witness_hangul_compose_lvt_case_ac01_11a8"
              },
              {
                "test_fn": "witness_hangul_compose_lvt_case_ac02_11a9"
              }
            ]
          }
        ],
        "source": {
          "repo": "https://github.com/unicode-rs/unicode-normalization",
          "commits": [
            "e4fd0e1b21f39e7d8ff35da53eb11d0624bbf6cc"
          ],
          "commit_subjects": [
            "Do not compose LVT syllables with trailing jamo"
          ],
          "summary": "`char::compose` had a match arm that accepted any `S_BASE..=S_LAST` LV or LVT syllable and unconditionally offset into another LVT cell. For LVT syllables (which already carry a trailing consonant), composing with another T-jamo silently produced a different LVT syllable instead of `None`. The fix restricts the arm to actual LV syllables."
        },
        "injection": {
          "kind": "patch",
          "files": [
            "src/normalize.rs"
          ],
          "patch": "patches/hangul_compose_lvt_e4fd0e1_1.patch"
        },
        "bug": {
          "short_name": "hangul_compose_lvt",
          "invariant": "Composing an LVT syllable with any T-jamo yields `None` — LVT syllables already carry a trailing consonant and cannot absorb another.",
          "how_triggered": "The mutation widens the Hangul-compose match arm back to `S_BASE..=S_LAST`, so LVT syllables are treated as composable and offset into a different LVT cell instead of rejected."
        }
      },
      {
        "mutations": [
          "hangul_compose_tbase_bd3770c_1"
        ],
        "tasks": [
          {
            "property": "HangulComposeTbase",
            "witnesses": [
              {
                "test_fn": "witness_hangul_compose_tbase_case_ac00_11a7"
              },
              {
                "test_fn": "witness_hangul_compose_tbase_case_ac1c_11a7"
              }
            ]
          }
        ],
        "source": {
          "repo": "https://github.com/unicode-rs/unicode-normalization",
          "commits": [
            "bd3770c5852d7e01fa74635c52100f0e4f4c34f4"
          ],
          "commit_subjects": [
            "Reject T_BASE sentinel in Hangul compose"
          ],
          "summary": "`char::compose` did not guard against the T-base sentinel codepoint `U+11A7` — the slot between T-jamos, not an actual trailing consonant. Composing any LV syllable with `U+11A7` incorrectly produced an LVT syllable. The fix rejects the T-base sentinel and requires the T-jamo to be strictly `> T_BASE`."
        },
        "injection": {
          "kind": "patch",
          "files": [
            "src/normalize.rs"
          ],
          "patch": "patches/hangul_compose_tbase_bd3770c_1.patch"
        },
        "bug": {
          "short_name": "hangul_compose_tbase",
          "invariant": "Composing an LV syllable with the T-base sentinel codepoint `U+11A7` yields `None` — the slot between T-jamos is not a real trailing consonant.",
          "how_triggered": "The mutation restores the `>= T_BASE` bound on the trailing jamo, accepting `U+11A7` and offsetting into the LVT syllable space instead of rejecting the sentinel."
        }
      },
      {
        "mutations": [
          "streamsafe_drop_buffer_47906fa_1"
        ],
        "tasks": [
          {
            "property": "StreamsafePreservesInput",
            "witnesses": [
              {
                "test_fn": "witness_streamsafe_preserves_input_case_31_nonstarters"
              },
              {
                "test_fn": "witness_streamsafe_preserves_input_case_40_nonstarters"
              }
            ]
          }
        ],
        "source": {
          "repo": "https://github.com/unicode-rs/unicode-normalization",
          "commits": [
            "47906fa3ab0baea6baacec756c77e9a94c1a7675"
          ],
          "commit_subjects": [
            "Preserve all input characters in StreamSafe"
          ],
          "summary": "`StreamSafe::next` dropped the internal pending-non-starter buffer when the run crossed the 30-non-starter boundary, so input characters were silently lost from the output even though the stream-safe transform is required to be a CGJ-insertion (never a deletion)."
        },
        "injection": {
          "kind": "patch",
          "files": [
            "src/stream_safe.rs"
          ],
          "patch": "patches/streamsafe_drop_buffer_47906fa_1.patch"
        },
        "bug": {
          "short_name": "streamsafe_drop_buffer",
          "invariant": "The stream-safe transform may only insert CGJ (U+034F); the multiset of non-CGJ input characters equals the multiset of non-CGJ output characters.",
          "how_triggered": "The mutation reinstates the pre-fix logic that clears the pending-non-starter buffer on a 30-non-starter overflow, so non-starters that should have been flushed to the output after the inserted CGJ are silently discarded."
        }
      }
    ]
  },
  "metrics": []
}