Ghostlight/Cargo.toml
Meta Vens 4fea8fdd36 Use UAX #29 word boundaries in SourceSpan::locate (PA.f78)
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>
2026-09-22 16:25:32 +02:00

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"