Skip to content

fix(api-validation): dot-gated extension extraction so .bash_history sorts as empty - #73

Merged
githubrobbi merged 1 commit into
mainfrom
fix/api-validation-extension-dot-gated
Apr 26, 2026
Merged

fix(api-validation): dot-gated extension extraction so .bash_history sorts as empty#73
githubrobbi merged 1 commit into
mainfrom
fix/api-validation-extension-dot-gated

Conversation

@githubrobbi

Copy link
Copy Markdown
Collaborator

Symptom

T62 --sort extension asc fails on Windows live MFT with:

Error: Not asc: bash_history > 2008
RPC: search({"limit":10,"pattern":"*.*","sort":"extension"})

CLI lane (uffs.exe "*.*" --sort extension --limit 10) returns the correct order — .bash_history first with empty Extension "", then digit-prefixed "2008", "BIN", "bin", "csv", "dat". MCP lane is also green (fixed in #69 / v0.5.74). Only the pure-RPC API validation lane fails.

Root cause

scripts/windows/api-validation.rs derives the Extension column locally from the daemon's name JSON field via rpc_field_computed("_ext"):

"_ext" => {
    let name = field_str(row, "name");
    let ext = name.rsplit('.').next().unwrap_or("").to_lowercase();
    Some(ext)
}

".bash_history".rsplit('.').next() returns "bash_history" (post-dot suffix when the dot is at index 0), instead of the empty string the sort engine uses. Same naive extractor at line 2002 of the type_* allowlist validator: dotless names get their full lowercased name as a phantom extension.

This was the third copy of the bug. CLI/format and MCP lanes were fixed in v0.5.74 (#69) by routing through extension_from_name / extract_extension_after_dot. The API validation lane was missed because it has its own validator-side extraction (no shared helper with the workspace).

Fix

Single helper, two call sites:

fn extract_ext_dot_gated(name: &str) -> String {
    let Some(dot) = name.rfind('.') else { return String::new(); };
    if dot == 0 || dot + 1 >= name.len() {
        return String::new();
    }
    name.get(dot + 1..).unwrap_or("").to_ascii_lowercase()
}

Mirrors extract_extension_after_dot in crates/uffs-core/src/search/filters/ext_match.rs. Returns "" for: dotless names (README), leading-dot hidden files (.bash_history), trailing-dot names (foo.).

Used in:

  • _ext computed column → fixes T62 sort + Extension column display in API validator.
  • type_* allowlist (type_code, type_document, type_executable, type_picture, type_system) → no longer mis-classifies dotless rows by their full name.

Verification

  • rust-script --package + cargo check on the generated package: clean compile, no new warnings.
  • lint-fast (file-size, fmt-check, lint-prod, lint-tests, lint-ci, typos, reuse): all green.
  • lint-pre-push (incl. smoke + check-windows): all green.
  • Equivalent dot-gated semantics already pinned by regression tests in crates/uffs-format/src/writer.rs, crates/uffs-core/src/output/tests.rs, and crates/uffs-mcp/src/tools/search.rs (fix(mcp): dot-gated extension extraction so .bash_history sorts as empty #69).

Out of scope

scripts/windows/api-validation.rs is a rust-script (not a workspace crate), so it is not gated by just lint-ci. Pre-existing clippy nits in unrelated parts of the file (unnecessary_filter_map, unnecessary_map_or, etc., lines 2689 / 2862 / …) are left untouched.

Affects

  • ✅ T62 --sort extension asc (the originally reported failure)
  • type_code / type_document / type_executable / type_picture / type_system validators

Closes the third copy of the dot-gated extension bug; CLI / MCP / API lanes now share semantics.

…sorts as empty

Bug:

  T62 `--sort extension asc` fails on Windows live MFT with

  "Not asc: bash_history > 2008" because `scripts/windows/api-validation.rs`

  computes the per-row Extension column with a naive

  `name.rsplit('.').next()`. For `.bash_history` (leading-dot hidden

  file) that yields `"bash_history"` instead of `""`, mis-ordering it

  ahead of the digit-prefixed names.

Why CLI + MCP already work, but API didn't:

  v0.5.74 fixed the MCP lane (`crates/uffs-mcp/src/tools/search.rs`)

  and v0.5.75 (PR #71) shipped alongside, but the same dot-gated fix

  for `extension_from_name` in `uffs-format`/`uffs-core` is what the

  CLI uses. The pure-RPC validation lane in `api-validation.rs` derives

  its own Extension column from the daemon's `name` JSON field via

  `rpc_field_computed("_ext")` and never picked up the fix.

Fix:

  Add `extract_ext_dot_gated` helper mirroring

  `extract_extension_after_dot` in

  `crates/uffs-core/src/search/filters/ext_match.rs`. Returns empty

  string for: dotless names, leading-dot hidden files, trailing-dot

  names. Use it in (a) `_ext` computed column (sort + display) and

  (b) the `type_*` allowlist validator (so dotless rows are not

  classified by their full name as a phantom extension).

Verification:

  - `rust-script -p` + `cargo check` on generated package: clean.

  - Existing dot-gated regression tests in `uffs-format` /

    `uffs-core` / `uffs-mcp` already pin equivalent semantics for

    the workspace lanes.

Affects: T62 (sort extension asc) + type_code/document/executable/picture/system.
@githubrobbi
githubrobbi enabled auto-merge (squash) April 26, 2026 16:42
@githubrobbi
githubrobbi merged commit 17cdab0 into main Apr 26, 2026
16 checks passed
@githubrobbi
githubrobbi deleted the fix/api-validation-extension-dot-gated branch April 26, 2026 16:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant