Skip to content

fix(fs): mark directory matches in glob results with a trailing slash - #4466

Merged
qin-ctx merged 1 commit into
mainfrom
fix/glob-dir-trailing-slash
Aug 29, 2026
Merged

fix(fs): mark directory matches in glob results with a trailing slash#4466
qin-ctx merged 1 commit into
mainfrom
fix/glob-dir-trailing-slash

Conversation

@ZaynJarvis

Copy link
Copy Markdown
Collaborator

起因

web-studio 的搜索面板加了 glob 模式后,glob 命中的目录全部被渲染成文件图标。查下来不是前端判断错了,而是前端根本拿不到这个信息。

问题性质

AGFS 的 glob entry 本身是带 is_dir 的(crates/ragfs/src/core/filesystem.rs:573),但 _ops.pyglob() 把结果拍平成 matches: list[str] 时把这个 flag 丢了。返回的目录 uri 和文件 uri 长得一模一样:

matches: ["viking://resources/folder", "viking://resources/a.md"]

调用方没有任何办法区分这两条。对比 fs/ls —— 它给每个 entry 一个显式的 isDir 字段,所以一直是对的;只有 glob 这条路把类型信息扔了。

这不是疏漏,test_glob_keeps_directory_matches 明确锁了「目录不带尾斜杠」这个行为,所以本 PR 一并更新该断言。

修改方式

matches 保持 list[str] 不变,只给目录 uri 补一个尾斜杠:

matches: ["viking://resources/folder/", "viking://resources/a.md"]

尾斜杠是 OV 里已有的目录约定 —— normalize_dir_uri 和 tree 渲染器(mcp_endpoint.py 里目录打印成 name/)都是这么表达的,这里只是让 glob 跟上。

实现上新增一个模块级 _glob_match_uri(entry_uri, is_dir),在 append 时决定要不要补斜杠。ACL 查询仍然用不带斜杠的原始 uripage_matches 改成 (acl_uri, match_uri) 二元组),所以权限判定逻辑逐字节不变。

待评审决策点

考虑过另一个方案:把 matches 改成 [{uri, isDir}],语义上和 fs/ls 彻底对齐、最干净。但那会破坏 MCP glob 工具、CLI、web-studio fetchGlob 和 e2e 测试的全部调用方,代价明显不成比例。选了破坏面最小的尾斜杠方案。如果 maintainer 更倾向结构化返回,可以在这个 PR 上改。

未改动

  • ACL / 可见性过滤逻辑,一行未动
  • matches 的类型(仍是 list[str])、count 字段
  • 文件类 uri,完全不受影响
  • 前端:web-studio 里 uri.endsWith('/') 的判断本来就写好了,服务端修正后自动生效,无需改动

验证

  • tests/storage/test_viking_fs_glob.py 11 passed。更新了 test_glob_keeps_directory_matches 的断言;新增 test_glob_marks_directories_but_not_files,覆盖 is_dir: True / is_dir: False / is_dir 字段缺失三种 entry 混排。
  • tests/storage/ 全量对照:clean main 是 33 failed / 392 passed,本 PR 是 33 failed / 393 passed —— 同样的 33 个预先存在的失败(volcengine_clients、vectordb_adaptor 等,与本改动无关),多出的 1 个 pass 是新增用例。零回归。
  • 内部 glob 调用方逐个查过:image_rewrite.pyparsers/markdown.pysemantic_processor.py 用的都是 **/*.md,只匹配文件,拿不到目录;resource_memory_link_service.pymcp_endpoint.py:1281 消费的是 grep 的 matches 不是 glob 的。均不受影响。

🤖 Generated with Claude Code

https://claude.ai/code/session_01C65Cb79Dvr5zLD6ib1T9Ew

`glob` returns a flat list of uri strings, dropping the `is_dir` flag that
AGFS already provides. Callers had no way to tell a directory match from a
file match, so web-studio rendered every glob hit with a file icon.

Keep `matches` as `list[str]` and append `/` to directory uris, matching the
convention `normalize_dir_uri` and the tree renderer already use. ACL lookups
still use the bare uri, so access decisions are unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C65Cb79Dvr5zLD6ib1T9Ew
@qin-ctx
qin-ctx merged commit e8cedae into main Aug 29, 2026
6 checks passed
@qin-ctx
qin-ctx deleted the fix/glob-dir-trailing-slash branch August 29, 2026 10:34
@github-project-automation github-project-automation Bot moved this from Backlog to Done in OpenViking project Aug 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants