Open-source State-of-the-art TTS model which runs on a CPU 😻
This report summarizes scanner evidence for KittenML/KittenTTS, a Python project with 15,793 GitHub stars. SynthScan examined 12,021 lines of code across 47 source files, recording 189 pattern matches distributed across 14 syntactic categories. The legacy adjusted pattern signal is 27.2 (Elevated pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Can maintainers explain intent, assumptions, and trade-offs? 87 finding(s).
Are imports declared and package/API names verified? 17 finding(s).
Are wrappers, guards, and repeated structures necessary? 140 finding(s).
Which changes need focused human review? 189 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 189 distinct pattern matches across 14 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM⚡ | kittenml/preprocess.py | 1264 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | kittenml/preprocess.py | 1266 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | kittenml/preprocess.py | 1272 | # ── Numbers ──────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | kittenml/preprocess.py | 1282 | # ── Scientific notation ───────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | kittenml/preprocess.py | 1286 | # ── Scale suffixes ───────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | kittenml/preprocess.py | 1290 | # ── Currency ─────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | kittenml/preprocess.py | 1294 | # ── Percentages ──────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | kittenml/preprocess.py | 1297 | # ── Ordinals ─────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | kittenml/preprocess.py | 1302 | # ── Fractions ────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | kittenml/preprocess.py | 1307 | # ── Time ─────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | kittenml/preprocess.py | 1312 | # ── Decades ──────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | kittenml/preprocess.py | 1318 | # ── Ranges ───────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | kittenml/preprocess.py | 1322 | # ── Model / version names ─────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | kittenml/preprocess.py | 1328 | # ── Units ────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | kittenml/preprocess.py | 1335 | # ── HTML / URLs / emails ─────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | kittenml/preprocess.py | 1339 | # ── Contractions ─────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | kittenml/preprocess.py | 1342 | # ── Edge / tricky cases ───────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | kittenml/preprocess.py | 1350 | # ── Mixed / real-world ────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/preprocess.py | 12 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/preprocess.py | 14 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/preprocess.py | 144 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/preprocess.py | 146 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/preprocess.py | 247 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/preprocess.py | 249 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/preprocess.py | 1011 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/preprocess.py | 1013 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/preprocess.py | 1136 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/preprocess.py | 1138 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/student_flow.py | 62 | # ── quantised layers, from bitnet.py ──────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/student_flow.py | 332 | # ── student flow architecture, from chatterbox_codec.py ───────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/student_flow.py | 1928 | # ── construction and the single-step sampler, from s3gen_flow_distill.py ──────── | COMMENT |
| MEDIUM | kittenml/kittentts2/model.py | 163 | # ── loading ─────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/model.py | 274 | # ── reference artifacts ─────────────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/model.py | 324 | # ── generation ──────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/model.py | 437 | # ── public API ──────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/text.py | 30 | # ── expression markup ───────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/text.py | 85 | # ── written -> spoken ───────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/text.py | 206 | # ── the written -> spoken backend ───────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/text.py | 270 | # ── chunking ────────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/text.py | 366 | # ── joining ─────────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | kittenml/kittentts2/s3gen.py | 72 | # ── from utils.py ────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | kittenml/kittentts2/s3gen.py | 81 | # ── from s3gen/const.py ──────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | kittenml/kittentts2/s3gen.py | 89 | # ── from s3gen/configs.py ────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | kittenml/kittentts2/s3gen.py | 280 | # ── from s3gen/utils/mel.py ──────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/s3gen.py | 102 | # ── from s3gen/utils/mask.py ─────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/s3gen.py | 364 | # ── from s3gen/utils/intmeanflow.py ──────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/s3gen.py | 401 | # ── from s3tokenizer/s3tokenizer.py ──────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/s3gen.py | 564 | # ── from s3gen/transformer/embedding.py ──────────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/s3gen.py | 679 | # ── from s3gen/transformer/subsampling.py ────────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/s3gen.py | 756 | # ── from s3gen/transformer/attention.py ──────────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/s3gen.py | 1082 | # ── from s3gen/transformer/convolution.py ────────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/s3gen.py | 1225 | # ── from s3gen/transformer/positionwise_feed_forward.py ──────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/s3gen.py | 1281 | # ── from s3gen/transformer/encoder_layer.py ──────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/s3gen.py | 1440 | # ── from s3gen/transformer/upsample_encoder.py ───────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/s3gen.py | 1742 | # ── from s3gen/matcha/transformer.py ─────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/s3gen.py | 3321 | # ── from s3gen/xvector.py ────────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/s3gen.py | 3728 | # ── from s3gen/flow.py ───────────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/s3gen.py | 3901 | # ── from s3gen/s3gen.py ──────────────────────────────────────────────── | COMMENT |
| MEDIUM | kittenml/kittentts2/s3gen.py | 2044 | # ── from s3gen/matcha/decoder.py ─────────────────────────────────────── | STRING |
| MEDIUM | kittenml/kittentts2/s3gen.py | 2189 | # ── from s3gen/decoder.py ────────────────────────────────────────────── | STRING |
| 4 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_vllm.py | 18 | def test_default_backend_keeps_native_loading(tmp_path): | CODE |
| LOW | tests/test_vllm.py | 30 | def test_selected_packed_weights_are_exact_and_full_ignores_packed_files(tmp_path): | CODE |
| LOW | tests/test_vllm.py | 50 | def test_native_full_loader_preserves_explicit_checkpoint_directory(tmp_path): | CODE |
| LOW | tests/test_vllm.py | 63 | def test_shared_speaker_head_matches_native_prefill_and_leaves_decode_unchanged(): | CODE |
| LOW | tests/test_vllm.py | 117 | def test_optional_backend_errors_before_model_download(): | CODE |
| LOW⚡ | tests/test_vllm.py | 130 | def test_vllm_options_require_backend(): | CODE |
| LOW⚡ | tests/test_vllm.py | 136 | def test_explicit_default_options_do_not_break_legacy_models(): | CODE |
| LOW⚡ | tests/test_vllm.py | 145 | def test_environment_rejects_unsupported_devices_before_engine_start(): | CODE |
| LOW | tests/test_vllm.py | 167 | def test_engine_environment_preserves_user_choice_and_restores_after_failure( | CODE |
| LOW | tests/test_vllm.py | 183 | def test_engine_receives_quiet_logging_config_without_changing_native_options(monkeypatch, tmp_path): | CODE |
| LOW | tests/test_vllm.py | 212 | def test_vllm_loader_filters_speaker_weights_and_rejects_missing_parameters(): | CODE |
| LOW | tests/test_vllm.py | 238 | def test_sampling_matches_native_when_requests_reorder_and_slots_are_reused(window): | CODE |
| LOW | tests/test_vllm.py | 274 | def test_sampler_is_neutral_and_speaker_replaces_only_first_embedding(): | CODE |
| LOW | tests/test_text_normalization.py | 8 | def test_issue_examples_normalize_for_tts(self): | CODE |
| LOW | tests/test_text_normalization.py | 21 | def test_normalizes_common_document_forms(self): | CODE |
| LOW⚡ | tests/test_text_normalization.py | 34 | def test_url_and_email_are_spoken_not_removed(self): | CODE |
| LOW⚡ | tests/test_text_normalization.py | 40 | def test_span_result_maps_replacements(self): | CODE |
| LOW⚡ | tests/test_text_normalization.py | 50 | def test_chunking_does_not_split_common_abbreviations(self): | CODE |
| LOW⚡ | tests/test_text_normalization.py | 60 | def test_unsupported_locale_fails_explicitly(self): | CODE |
| LOW | kittenml/preprocess.py | 479 | def normalize_leading_decimals(text: str) -> str: | CODE |
| LOW | kittenml/preprocess.py | 492 | def expand_scientific_notation(text: str) -> str: | CODE |
| LOW | kittenml/preprocess.py | 763 | def _replace_read_aloud_range(match: re.Match) -> str: | CODE |
| LOW | kittenml/preprocess.py | 771 | def _replace_read_aloud_number(match: re.Match) -> str: | CODE |
| LOW | kittenml/kittentts_legacy/model.py | 108 | def download_from_huggingface(repo_id="KittenML/kitten-tts-nano-0.1", cache_dir=None, backend=None): | CODE |
| LOW | kittenml/kittentts2/student_flow.py | 758 | def get_intmeanflow_time_mixer(dims: int) -> nn.Linear: | CODE |
| LOW | kittenml/kittentts2/tokens.py | 50 | def uses_reference_role_tokens(self) -> bool: | CODE |
| LOW | kittenml/kittentts2/speaker_embedding.py | 163 | def load_speaker_embedding_model(weights_path, device="cpu"): | CODE |
| LOW | kittenml/kittentts2/text.py | 157 | def _reformat_nontag_brackets(text): | CODE |
| LOW | kittenml/kittentts2/text.py | 183 | def _capitalize_and_terminate(text): | CODE |
| LOW⚡ | kittenml/kittentts2/s3gen.py | 292 | def dynamic_range_compression_torch(x, C=1, clip_val=1e-5): | CODE |
| LOW | kittenml/kittentts2/s3gen.py | 367 | def get_intmeanflow_time_mixer(dims): | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | kittenml/kittentts2/s3gen.py | 105 | def subsequent_mask( size: int, device: torch.device = torch.device("cpu"), ) -> torch.Tensor: """C | STRING |
| HIGH | kittenml/kittentts2/s3gen.py | 145 | Create mask for subsequent steps (size, size) with chunk size, this is for streaming encoder Args: s | STRING |
| HIGH | kittenml/kittentts2/s3gen.py | 251 | Make mask tensor containing indices of padded part. See description of make_non_pad_mask. Args: length | STRING |
| HIGH | kittenml/kittentts2/s3gen.py | 2406 | Forward pass of the UNet1DConditional model. Args: x: (B, 80, T) mask (_type_) | STRING |
| HIGH | kittenml/kittentts2/s3gen.py | 3325 | Perform padding for the list of tensors. Args: xs (List): List of Tensors [(T_1, `*`), (T_2, `*`), ..., (T_ | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | kittenml/preprocess.py | 1156 | CODE | |
| LOW | kittenml/get_model.py | 65 | CODE | |
| LOW | kittenml/kittentts2/student_flow.py | 696 | CODE | |
| LOW | kittenml/kittentts2/student_flow.py | 1992 | CODE | |
| LOW | kittenml/kittentts2/student_flow.py | 217 | CODE | |
| LOW | kittenml/kittentts2/student_flow.py | 1033 | CODE | |
| LOW | kittenml/kittentts2/student_flow.py | 1214 | CODE | |
| LOW | kittenml/kittentts2/student_flow.py | 1394 | CODE | |
| LOW | kittenml/kittentts2/student_flow.py | 1556 | CODE | |
| LOW | kittenml/kittentts2/student_flow.py | 1833 | CODE | |
| LOW | kittenml/kittentts2/model.py | 451 | CODE | |
| LOW | kittenml/kittentts2/model.py | 492 | CODE | |
| LOW | kittenml/kittentts2/s3gen.py | 171 | CODE | |
| LOW | kittenml/kittentts2/s3gen.py | 297 | CODE | |
| LOW | kittenml/kittentts2/s3gen.py | 312 | CODE | |
| LOW | kittenml/kittentts2/s3gen.py | 1522 | CODE | |
| LOW | kittenml/kittentts2/s3gen.py | 1884 | CODE | |
| LOW | kittenml/kittentts2/s3gen.py | 2092 | CODE | |
| LOW | kittenml/kittentts2/s3gen.py | 2262 | CODE | |
| LOW | kittenml/kittentts2/s3gen.py | 3135 | CODE | |
| LOW | kittenml/kittentts2/s3gen.py | 3398 | CODE | |
| LOW | kittenml/kittentts2/s3gen.py | 3433 | CODE | |
| LOW | kittenml/kittentts2/s3gen.py | 3638 | CODE | |
| LOW | kittenml/kittentts2/s3gen.py | 3744 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | kittenml/__init__.py | 1 | CODE | |
| LOW | kittenml/__init__.py | 1 | CODE | |
| LOW | kittenml/__init__.py | 1 | CODE | |
| LOW | kittenml/__init__.py | 1 | CODE | |
| LOW | kittenml/get_model.py | 11 | CODE | |
| LOW | kittenml/kittentts_legacy/__init__.py | 7 | CODE | |
| LOW | kittenml/kittentts_legacy/__init__.py | 7 | CODE | |
| LOW | kittenml/kittentts_legacy/__init__.py | 7 | CODE | |
| LOW | kittenml/kittentts2/__init__.py | 7 | CODE | |
| LOW | kittenml/kittentts2/__init__.py | 8 | CODE | |
| LOW | kittenml/kittentts2/__init__.py | 8 | CODE | |
| LOW | kittenml/kittentts2/__init__.py | 8 | CODE | |
| LOW | kittenml/kittentts2/__init__.py | 8 | CODE | |
| LOW | kittenml/kittentts2/__init__.py | 10 | CODE | |
| LOW | kittenml/kittentts2/student_flow.py | 35 | CODE | |
| LOW | kittenml/kittentts2/student_flow.py | 40 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | kittenml/preprocess.py | 689 | CODE | |
| LOW | kittenml/preprocess.py | 813 | CODE | |
| LOW | kittenml/kittentts_legacy/onnx_model.py | 44 | CODE | |
| LOW | kittenml/kittentts2/tl2_emb4.py | 141 | CODE | |
| LOW | kittenml/kittentts2/student_flow.py | 273 | CODE | |
| LOW | kittenml/kittentts2/student_flow.py | 699 | CODE | |
| LOW | kittenml/kittentts2/student_flow.py | 1185 | CODE | |
| LOW | kittenml/kittentts2/student_flow.py | 1658 | CODE | |
| LOW | kittenml/kittentts2/text.py | 319 | CODE | |
| LOW | kittenml/kittentts2/s3gen.py | 173 | CODE | |
| LOW | kittenml/kittentts2/s3gen.py | 3435 | CODE | |
| LOW | kittenml/kittentts2/s3gen.py | 1823 | CODE | |
| LOW | kittenml/kittentts2/s3gen.py | 2391 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | kittenml/kittentts2/s3gen.py | 2653 | CODE | |
| MEDIUM | kittenml/kittentts2/s3gen.py | 2654 | CODE | |
| MEDIUM | kittenml/kittentts2/s3gen.py | 2656 | CODE | |
| MEDIUM | kittenml/kittentts2/s3gen.py | 2659 | CODE | |
| MEDIUM | kittenml/kittentts2/s3gen.py | 2660 | CODE | |
| MEDIUM | kittenml/kittentts2/s3gen.py | 2661 | CODE | |
| MEDIUM | kittenml/kittentts2/s3gen.py | 2663 | CODE | |
| MEDIUM | kittenml/kittentts2/s3gen.py | 2664 | CODE | |
| MEDIUM | kittenml/kittentts2/s3gen.py | 2666 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | kittenml/kittentts2/model.py | 41 | # Knobs behind the `advanced=` argument. Everything here has a working default and | COMMENT |
| LOW | kittenml/kittentts2/text.py | 121 | COMMENT | |
| LOW | kittenml/kittentts2/s3gen.py | 561 | return log_spec | COMMENT |
| LOW | kittenml/kittentts2/s3gen.py | 681 | COMMENT | |
| LOW | kittenml/kittentts2/s3gen.py | 761 | # 2024 Alibaba Inc (Xiang Lyu) | COMMENT |
| LOW | kittenml/kittentts2/s3gen.py | 921 | # when export onnx model, for 1st chunk, we feed | COMMENT |
| LOW | kittenml/kittentts2/s3gen.py | 1021 | torch.Tensor: Cache tensor (1, head, cache_t + time1, d_k * 2) | COMMENT |
| LOW | kittenml/kittentts2/s3gen.py | 1081 | COMMENT | |
| LOW | kittenml/kittentts2/s3gen.py | 1221 | COMMENT | |
| LOW | kittenml/kittentts2/s3gen.py | 1281 | # ── from s3gen/transformer/encoder_layer.py ──────────────────────────── | COMMENT |
| LOW | kittenml/kittentts2/s3gen.py | 1441 | COMMENT | |
| LOW | kittenml/kittentts2/s3gen.py | 2701 | # Shapes: | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | kittenml/__init__.py | 12 | __all__ = [ | CODE |
| LOW | kittenml/kittentts_legacy/__init__.py | 9 | __all__ = [ | CODE |
| LOW | kittenml/kittentts2/__init__.py | 12 | __all__ = [ | CODE |
| LOW | kittenml/kittentts2/student_flow.py | 65 | __all__ = [ | CODE |
| LOW | kittenml/kittentts2/student_flow.py | 335 | logger = logging.getLogger(__name__) | CODE |
| LOW⚡ | kittenml/kittentts2/s3gen.py | 285 | logger = logging.getLogger(__name__) | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | kittenml/kittentts_legacy/onnx_model.py | 38 | except KeyError: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | kittenml/preprocess.py | 473 | except Exception: | CODE |
| LOW | kittenml/__init__.py | 7 | except Exception: # running from a source tree without an install | CODE |
| LOW | kittenml/kittentts2/model.py | 227 | except Exception: | CODE |
| LOW | kittenml/kittentts2/text.py | 261 | except Exception as exc: # a normalizer problem must not cost a generation | CODE |
| LOW | kittenml/kittentts2/speaker.py | 123 | except Exception: # formats libsndfile cannot open | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | kittenml/kittentts2/s3gen.py | 4206 | # hallucination prevention, drop special tokens | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | kittenml/kittentts2/s3gen.py | 1201 | # It's better we just return None if no cache is required, | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | kittenml/preprocess.py | 614 | "555-1234" → "five five five one two three four" | STRING |