fix(cli): fold non-ASCII case when matching names - #6079
Conversation
Four user-facing name matchers lowercased with to_ascii_lowercase, which leaves every non-ASCII letter untouched. So a channel named EQUIPE with an accented E was unfindable by the obvious query, and buzz notes ls --author with an accented display name failed outright with "no user found": - channels list --name (substring and --exact) - users search (display_name and name) - notes ls --author (name to pubkey resolution) - channel templates (lookup by name) Desktop matches the same names with JavaScript's toLowerCase, which is Unicode-aware, so this was also a disagreement between the CLI and the app about what a query finds. All four now go through validate::fold_name. Hex, pubkeys and UUIDs keep to_ascii_lowercase: they are ASCII by construction, and a Unicode fold there would only invite a dotless-i surprise. Signed-off-by: Taksh <takshkothari09@gmail.com>
Every expectation in fold_name_tests is node's toLowerCase output for the same input, because Desktop is the other implementation of this rule and the two have to agree about what a query finds. Covers Latin accents, Cyrillic, the German eszett, a titlecase digraph, and the Turkish dotted capital I, whose fold adds a combining dot. name_matches gains the substring and exact cases that were unreachable before, plus a non-match so the fold has not made matching sloppy. Reverting fold_name to to_ascii_lowercase turns both new tests red. Signed-off-by: Taksh <takshkothari09@gmail.com>
themiguelamador
left a comment
There was a problem hiding this comment.
I found three remaining user-facing name-resolution paths that still used ASCII-only case matching:
buzz messages search --authordid not match non-ASCII display names across case.- owned-agent name lookup did not match non-ASCII agent names across case.
- automatic
@Display Nameresolution still used ASCII-only keys in the CLI and ASCII-only known-name/profile matching inbuzz-sdk, so a member namedÉQUIPEwas not resolved from@équipe.
I fixed all three in commit 25233b618 on Complear:review/pr-6079-fix. The patch centralizes the Unicode lowercase behavior in buzz_sdk::mentions::fold_name, applies it across the remaining lookup paths, and handles lowercase mappings that expand in byte length (for example İ → i plus combining dot).
Verification:
cargo test -p buzz-sdk --lib --quiet— 264 passedcargo test -p buzz-cli --lib --quiet— 354 passedcargo clippy -p buzz-sdk --all-targets -- -D warningscargo clippy -p buzz-cli --all-targets -- -D warningscargo fmt --all -- --checkgit diff --check
… to the sdk Review finding (P1, themiguelamador on block#6079): three user-facing lookups still matched names ASCII-only. - `buzz messages search --author`: `match_profiles_by_name` folded with `to_ascii_lowercase`, so a non-ASCII display name was reachable only by reproducing its exact case. - Owned-agent lookup: `eq_ignore_ascii_case` is case-blind for ASCII letters alone, so an agent named `Équipe` was unreachable as `équipe`. - Automatic `@Display Name` resolution: buzz-cli keyed its `name → pubkey` map with `to_ascii_lowercase` while `extract_at_mentions_with_known` compared known names with `eq_ignore_ascii_case` and returned ASCII-folded keys — so a member named `ÉQUIPE` was not resolved from `@équipe` at either end. `fold_name` moves to `buzz_sdk::mentions`, which is where it has to live: the SDK returns the folded keys and the CLI looks them up, so one copy of the rule is the only way the two can agree. buzz-cli re-exports it, leaving existing call sites unchanged. Matching a known name also can no longer slice the content at the known name's byte length, because folding can change that length — `İ` is two bytes and folds to `i̇`, which is three. `folded_prefix_len` folds the content one char at a time and reports how many *source* bytes it consumed, and the word- boundary check runs at that offset, so `@équipement` still does not match a member named `ÉQUIPE`. Signed-off-by: Taksh <takshkothari09@gmail.com>
|
All three confirmed and fixed in Verified each: Agreed on the placement: The byte-length expansion you flagged needed a bit more than folding both sides: matching a known name could no longer slice the content at the known name's length, since Verification: |
Four user-facing name matchers in the CLI lowercase with
to_ascii_lowercase, which leaves every non-ASCII letter exactly as it was. So a name in any language with cased non-ASCII letters cannot be found by the obvious query:channels list --name(substring and--exact)commands/channels.rs:135,216users search(display_nameandname)commands/users.rs:151,164notes ls --author <name>commands/notes.rs:220,232no user found with display_name "josé"commands/channel_templates.rs:101,104NotFound, listing names that visibly include the one asked fornotes ls --authoris the sharp one — it is a resolution gate, so the command exits non-zero rather than just returning less.This is also a CLI-vs-app disagreement: Desktop matches the same names with JavaScript's
toLowerCase, which is Unicode-aware, so the two answer the same question differently.All four now go through
validate::fold_name. The expectations in its tests are node'stoLowerCaseoutput for the same inputs, since Desktop is the other implementation of this rule and they have to agree:Hex, pubkeys and UUIDs deliberately keep
to_ascii_lowercase. They are ASCII by construction, and a Unicode fold there is the kind of thing that gets surprised by a Turkish dotless i. The doc comment onfold_namesays so, so the next reader does not "fix" the ones that are already right.Reverting
fold_nametoto_ascii_lowercaseturns both new tests red.Verified locally:
cargo test -p buzz-cli --lib352 passed (349 before),cargo clippy -p buzz-cli --all-targets -- -D warningsclean,cargo fmt --all -- --check,git diff --check. Not run: a live relay — the profile and template inputs come from fixtures in the tests.