The hand-written is_alphanumeric neighbour rule in SourceSpan::locate got several boundary cases wrong: it accepted "I can" inside "I can't go" (an apostrophe is not alphanumeric, so it read as a boundary even though it binds the contraction into one word), accepted "the cafe" ending right before a combining acute mark, accepted "run" inside "run_fast", and falsely refused every CJK quote because every ideograph counts as alphanumeric and CJK prose carries no spaces. Replace the rule with real UAX #29 word segmentation from the unicode-segmentation crate: collect the boundary byte offsets split_word_bound_indices() reports for the source, plus source.len(), and accept an occurrence only when both its start and end land in that set. Delete starts_on_word_boundary/ends_on_word_boundary; the crate is the authority now, not a hand rule. unicode-segmentation is pinned to 1.x in the workspace [workspace.dependencies] table (Cargo.lock gains only that one package) and added to ghostlight-persona-projection's own Cargo.toml. Verified against UAX #29 with new tests: contractions (straight and curly apostrophe) are refused, a quote ending before a combining mark is refused, an underscore-joined word is refused, a leading-CJK two-character quote is accepted, a quote with its own leading space is accepted, a hyphen-bounded quote is accepted, and "Stop." in "Stop.Now" is refused (the crate glues a MidNumLet full stop between two letters into one word, so this is the crate's called shot, not a special case carved into the rule). Callers (Interpreter capture, Dungeon's span_is_exact) are unchanged; the lib and Dungeon bin suites both still pass in full (618 lib tests with the one known PA.f77 flake, 63 Dungeon bin tests). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
41 lines
1.5 KiB
TOML
41 lines
1.5 KiB
TOML
[workspace]
|
|
members = [
|
|
"crates/ghostlight",
|
|
"crates/ghostlight-dungeon",
|
|
"crates/ghostlight-persona-projection",
|
|
]
|
|
resolver = "2"
|
|
|
|
[workspace.package]
|
|
edition = "2024"
|
|
license = "MIT"
|
|
version = "0.1.0"
|
|
|
|
[workspace.dependencies]
|
|
anyhow = "1"
|
|
aes-gcm = "0.10"
|
|
async-trait = "0.1"
|
|
async-stream = "0.3"
|
|
axum = { version = "0.8", features = ["macros"] }
|
|
chrono = { version = "0.4", features = ["serde"] }
|
|
base64 = "0.22"
|
|
codex-connector = { git = "https://github.com/GameCult/CodexConnector.git", rev = "68fe94b9475823de92ed766a97ea962d48723708" }
|
|
cultcache-rs = { git = "https://github.com/GameCult/CultLib.git", rev = "85f7024a35ea42cf9f0a0199a064526427150191", package = "cultcache-rs" }
|
|
cultnet-rs = { git = "https://github.com/GameCult/CultLib.git", rev = "85f7024a35ea42cf9f0a0199a064526427150191", package = "cultnet-rs" }
|
|
cultmesh-rs = { git = "https://github.com/GameCult/CultLib.git", rev = "85f7024a35ea42cf9f0a0199a064526427150191", package = "cultmesh-rs" }
|
|
jsonwebtoken = "9"
|
|
hmac = "0.12"
|
|
rand = "0.9"
|
|
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls", "stream"] }
|
|
rmp-serde = "1.3"
|
|
serde = { version = "1", features = ["derive"] }
|
|
serde_json = "1"
|
|
sha2 = "0.10"
|
|
thiserror = "2"
|
|
tokio = { version = "1", features = ["full"] }
|
|
tower-http = { version = "0.6", features = ["fs", "trace"] }
|
|
tracing = "0.1"
|
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
|
unicode-segmentation = "1"
|
|
uuid = { version = "1", features = ["v4", "serde"] }
|
|
zeroize = "1.8"
|