A 5e storytelling engine with an LLM DM

Fix diff performance, stale dead-code allows, and tar symlink escape master

The fidelity diff built a full Levenshtein matrix even when the two word streams were identical, and again when they differed by one word in a 10,000-word file: 424 MB for one comparison, 385 MB peak RSS and 13.9 of 14.0 seconds across the whole test suite. first_divergence now returns early on equal streams and trims the common prefix and suffix off both streams before aligning the rest, so the matrix only covers the words that actually differ. cargo test drops from 13.90 s to 0.59 s; srd verify drops from 14.23 s to 0.48 s and 385 MB to 18 MB peak RSS. MonsterFields and SpellFields carried #[allow(dead_code)] with doc comments claiming their fields were validated but never read again. subtitle.rs reads every one of them, checking them against the source's subtitle line; removing the attributes and clippy still passes clean. unpack_stripped called Entry::unpack, which does not validate a symlink entry's target before writing through it. is_safe_entry_path checks an entry's own path but not its type, so a tarball could carry a symlink entry followed by a file entry whose path walks through it, writing outside the destination once the pinned tarball hash changes. Vendored entries are now required to be a regular file or a directory.


+193 -19
4 changed files