GCWing / GCWing/OpenBitFun

[Feature]: 多 git 子仓库的大型 workspace(如 vllm + 插件)下 Flashgrep 无法正常工作:根目录无 git 基线导致索引不可用,进入子仓库则丢失关联仓库搜索范围

Abierto
#2,306 0 comentarios 0 reacciones 1 asignado Reclamado por @wgqqqqq Ver en GitHub
accept feature
Lenguaje dominante
Rust
Estrellas
2.3k
Forks
231
Merge medio
2 h 49 min
PR fusionados (30 d)
589

Descripción

### Summary

当一个 workspace 是**大型聚合目录**——本身不是 git 仓库,内部包含多个独立 git 仓库(例如 vllm 主仓库 + 若干插件/关联仓库,统一在一个 workspace 下开发):

```
workspace/ ← 非 git 仓库(聚合根)
├── git仓库1/ ← 独立 repo(如 vllm)
├── git仓库2/ ← 独立 repo(如 vllm-engine 插件)
└── git仓库3/ ← 独立 repo(如推理插件)
```

此时 Flashgrep 索引搜索两条路都走不通:

1. **以整个 workspace 为 repo 打开**:flashgrep 的索引模型依赖 git 基线(`base-snapshot` 绑定 commit),而聚合根不是 git 仓库 → 无法建立基线 → 索引不可用(Grep 退化为全量扫描,Glob 无 fallback 直接受影响)。
2. **进入某个子仓库打开**:搜索 scope 被强制限定在该 repo 内(越界直接报 "Search path is outside workspace root")→ 搜不到同 workspace 下其他关联仓库 → 丢失跨仓库搜索能力。

根因是 flashgrep 的集成模型是**单仓库**的(一次搜索请求只带一个 `repo_id`,scope 必须落在该 repo 内),而产品需要的是**多仓库聚合 workspace** 场景。

- **类型:** Bug / 能力缺失(对多 git 子仓库 workspace 布局的支持)
- **严重度:** 中~高(影响 monorepo/多仓库布局用户的 Grep/Glob,表现为搜不到或降级)
- **影响面:** 桌面端 Glob/Grep 工具(本地 workspace),多 git 子仓库布局用户

### Area

Desktop app

### Reproduction or evidence

复现步骤:

1. 建立如上目录结构:聚合根 `workspace/` 不是 git 仓库,内部 `git仓库1/`、`git仓库2/` 各自 `git init`。
2. 开启 `app.ai_experience.enable_workspace_search`,以 `workspace/` 为工作目录打开。
3. 调用 Grep/Glob 搜索跨仓库内容(如同时分布在两个子仓库的符号)。
4. 观察:
- 以 workspace 根为 repo 打开时,索引停留在 `MissingBaseSnapshot`/扫描降级(Grep 慢或结果不完整;Glob 依赖索引目录,行为异常,与 #TODO 关联的超时问题互相放大)。
- 把 workspace 切到 `git仓库1/` 打开时,能搜到仓库 1 内部,但搜索 `git仓库2/` 路径直接报越界/无结果——丢失关联仓库权限。

### 代码证据(单仓库模型的绑定点)

| 位置 | 事实 |
| --- | --- |
| `flashgrep/protocol.rs:105-119` | `SearchParams { repo_id, ... }`、`GlobParams { repo_id, scope }`——一次搜索请求只携带**单个** `repo_id`,无跨仓库聚合字段 |
| `flashgrep/protocol.rs:357-374` | `RepoStatus` 的索引状态是单仓库语义:`base_snapshot_root` / `snapshot_key` / `dirty_files{modified,deleted,new}` / `rebuild_recommended`——基线绑定 git commit |
| `flashgrep/protocol.rs:378-386` | `RepoPhase::MissingBaseSnapshot`——无可用 git 基线时索引不可用,是显式状态 |
| `workspace_search/service.rs:78,435` | `sessions: HashMap`、`get_or_open_session(repo_root)`——session 以**单个 repo_root 为键**,一次搜索只打开/查询一个 repo |
| `workspace_search/service.rs:906-921` | `normalize_scope_path`:`normalized.starts_with(repo_root)` 否则报 **"Search path is outside workspace root"**——scope 硬性限定在单 repo 内,跨仓库路径被拒绝 |
| `workspace_search/service.rs:226` | Grep 搜索带 `with_scan_fallback(true)`——可降级扫描;`GlobParams`(protocol.rs:115-119)**无 fallback 字段**,Glob 无降级路径 |
| `grep_tool.rs:270-274` | 工具层把 `context.workspace.root_path_string()` 整体当作 repo_root 传参——**整个 workspace 被当成一个 repo**,多子仓库结构被压平 |
| `glob_tool.rs:419-426` | `GlobSearchRequest { repo_root: workspace_root, ... }`——同上,整个 workspace 当单个 repo |
| `flashgrep/rpc_client.rs:340` | daemon 能力声明 `"workspace_list": false`——daemon 不支持列出 workspace 内多仓库并聚合 |
| `resources/flashgrep/README.md:5` | daemon 为外部项目 `wgqqqqq/flashgrep` v0.2.10,BitFun 侧为集成方;多仓库能力需在集成层解决 |

### 根因调用链

```
工具层 Grep/Glob
→ repo_root = workspace 聚合根(非 git 仓库)
→ WorkspaceSearchService::search_content/glob
→ get_or_open_session(workspace_root) // 单 repo session
→ normalize_scope_path:scope 必须 starts_with(repo_root) // 跨子仓库路径被拒
→ daemon:无 git 基线 → MissingBaseSnapshot → 索引不可用/降级
```

两个方向的矛盾:

1. **把 workspace 根当 repo**:flashgrep 索引模型要求 git 基线(base-snapshot 绑定 commit、dirty 分类来自 git status、语料遵循 .gitignore),聚合根不是 git 仓库时基线缺失;即便强行索引,也会重复扫描内部子仓库并撞上子仓库各自的 .gitignore 边界。
2. **把子仓库当 repo**:scope 被 `starts_with(repo_root)` 强制锁死在单仓库内,同 workspace 的其他关联仓库(插件等)不可搜索——正是「进入某个仓库就失去外部其他关联仓库的权限」。

工具层没有任何**跨仓库聚合**逻辑:不枚举 workspace 下的子仓库、不开多个 repo session 并发查询、不合并结果。一次 Grep/Glob 只能命中一个 repo_root。

## Expected behavior

多 git 子仓库的聚合 workspace 应被当作**仓库集合**处理,而不是单个 repo:

1. **仓库集合抽象**:workspace 是多个 git 仓库的组合。搜索请求支持 `repo_ids: Vec`(或聚合根 + 子仓库发现),daemon 或服务层按子仓库建索引并聚合结果,去重后返回。
2. **子仓库自动发现**:服务层识别 workspace 内的 git 子仓库边界(`git rev-parse --show-toplevel` 或 `.git` 目录发现),每个子仓库一个 repo session;跨仓库 Grep/Glob 并发查询各 session 后合并。
3. **scope 语义扩展**:搜索路径允许跨多个子仓库,越界检查从「单 repo 内」放宽为「任一子仓库内」,并保留权限边界(子仓库外仍拒绝)。
4. **无 git 基线的降级路径**:聚合根非 git 时,Grep 的 scan fallback 需显式告知用户「索引不可用、正在全量扫描」;Glob 需要补齐 fallback(当前无)。
5. **避免重复扫描**:子仓库边界识别后,聚合根扫描应跳过嵌套 `.git` 目录与子仓库内部,防止重复索引。

### 建议方案

- **短期(集成层)**:`WorkspaceSearchService` 增加 workspace → 子仓库集合的映射;Grep/Glob 工具对跨仓库请求并发调用各子仓库 session 并合并结果;scope 越界检查按仓库集合判定。
- **中期(协议层)**:与外部 `wgqqqqq/flashgrep` 上游对齐多仓库能力(`workspace_list`、多 repo 搜索参数),或引入聚合根索引模式(非 git 目录也可建不依赖 commit 的文件快照基线)。
- **配套**:与本仓库 #TODO(Flashgrep RPC 调用链无超时)联动——多仓库并发查询放大了 daemon 单请求挂起的影响,修复时须一并加上超时与降级。

### Environment, if relevant

```
BitFun commit: 4d3869859 (2026-08-14)
OS: Windows x64
FLASHGREP_DAEMON_BIN: C:\Users\wfwt\AppData\Local\BitFun\flashgrep\flashgrep-x86_64-pc-windows-msvc.exe
flashgrep daemon: wgqqqqq/flashgrep v0.2.10(外部项目,本仓库集成)
布局: workspace 聚合根(非 git)+ 多个独立 git 子仓库(主项目 + 插件)
```

## Related files

- `src/crates/services/services-integrations/src/workspace_search/flashgrep/protocol.rs`
- `src/crates/services/services-integrations/src/workspace_search/service.rs`
- `src/crates/services/services-integrations/src/workspace_search/flashgrep/client.rs`
- `src/crates/services/services-integrations/src/workspace_search/flashgrep/rpc_client.rs`
- `src/crates/assembly/core/src/agentic/tools/implementations/grep_tool.rs`
- `src/crates/assembly/core/src/agentic/tools/implementations/glob_tool.rs`
- `src/crates/assembly/core/src/service/search/service.rs`
- `resources/flashgrep/README.md`

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.