Skip to content

feat: repo 地圖 + global config(~/.claude/.idd/global.json)— 讓 target resolution 知道哪些層級實際存在,上收停在當前 git repo 邊界 #302

Description

@kiki830621

Problem

#301 提議在 target resolution 加入 residual clause——未列舉事項依「事務性質」歸屬。但那條判準要能執行,需要先知道哪些層級實際存在:判準說「這件事屬 project 層」時,若該 project 根本沒有自己的 tracker,往下歸就會落空。

目前 IDD 完全沒有 global 層,也沒有任何跨 repo 的階層知識。每次 target resolution 只看得到 cwd 往上第一個 config,看不到「這台機器上有哪些 repo、誰是誰的上層」。

Original text(使用者,2026-08-12 對話逐字):

「我在想可能如果要考慮idd repo的話要考慮有沒有地方政府,如果最下層的repo已經沒了的話就不要往下,所以可能要有一個repo的地圖,是不是存在~/.idd (當idd的global setting如何? 你可以參考其他的repo)」

「好,照你的建議好,上收其實通常會到我現在正在開起的專案的git,要跨過的話通常會需要特別指定」

「地方政府在不在」是核心比喻:階層可以缺席,事務要歸給實際存在的最近層級,而不是歸給一個不存在的層。


Type

feature

Priority

P1(本週)— #301 的前置基礎設施


現況調查

1. walk-up 其實已經會經過 $HOME/.claude/.idd/

references/config-protocol.md 的 walk-up 終止條件寫著「don't go above $HOME」,但檢查在 break 之前

while [ "$dir" != "/" ] && [ "$dir" != "$HOME/.." ]; do
  if [ -f "$dir/.claude/.idd/local.json" ]; then ... return 0; fi   # ← 先檢查
  [ "$dir" = "$HOME" ] && break                                      # ← 才 break
  dir=$(dirname "$dir")
done

所以 $HOME/.claude/.idd/ 這個路徑已經在讀取路徑上,只是:

  • 沒有 global 語意——它會被當成「$HOME 這個目錄的 local config」,而 $HOME 不是 repo
  • 實測該目錄存在但為空(0 檔案),是某次在 home 執行留下的空殼

2. 沒有任何 global / registry / 階層概念

grep -niE 'global|XDG|user-level|machine-wide'config-protocol.md 只命中 walk-up 的 $HOME 邊界描述。六機制全部是單點解析(解出一個 repo),沒有一個機制看得到 repo 之間的關係。

3. 兩種 config 格式並存(掃描器必須兩種都認)

實測一台機器上的分布:

格式 數量
.claude/.idd/local.json(新,#195 規定新裝一律用此) 20+
.claude/issue-driven-dev.local.json(legacy,walk-up 仍 fallback 解析) 17

任何掃描實作若只認新格式,會漏掉近半數 repo——而漏掉的那些會被判成「該層不存在」,觸發錯誤的上收。


Expected

A. Global config:~/.claude/.idd/global.json

位置決定(考慮過 ~/.idd/,不採用):

  1. walk-up 已經會經過 $HOME/.claude/.idd/ — 零新增掃描邏輯,只要多認一個檔名
  2. 與 project 層 .claude/.idd/local.json 完全對稱,只差 local / global
  3. IDD 是 Claude Code plugin,~/.claude/ 是其生態家(settings.json / rules/ / plugins/ 皆在此);~/.idd/ 等於在 home 再開一個 dotdir
  4. 檔名用 global.json 而非 local.json 是必要的——後者會讓 $HOME 被誤讀成「一個 repo」,語意汙染既有的 repo-boundary 判定

生態先例:同一套工具鏈中,codex-pro 已採 ~/.codex-pro/profile.yaml(global)+ <project>/.codex-pro/profile.yaml(project)雙層 resolve。本案是同一模式,但寄生在 ~/.claude/ 之下而非另開 dotdir。

B. 地圖:自動推導,不手寫

手寫 registry 會 stale,而 stale 正是本問題的病因——#301 記錄的那條分散案例,成因就是某 project 中途被 extract 成獨立 repo 而沒有任何地方被更新。手寫地圖只是把同一種病從 walk-up 搬到 registry。

地圖可以完全自動推導:每個 repo 的 .claude/.idd/local.json(或 legacy)已經自我宣告了它的 tracker。掃描即得圖。因此 global.json 存的不是 repo 清單,而是掃描參數與快取控制:

{
  "scan_roots": ["~/<work-root-a>", "~/<work-root-b>"],
  "cache_ttl_seconds": 86400
}

single source of truth 仍在各 repo 自己的 config,global 只是索引 + 快取。實測規模:一個工作根 7 個 repo、另一個 30+ 個,全掃成本可接受(配 TTL 快取後更低)。

C. 缺層上收:階層可以缺席

判準與地圖必須分開——「判準說該歸哪一層」和「那一層是否存在」是兩種不同的失敗,混在一起會 debug 不出來。

D. 上收天花板:當前 git repo boundary,跨越需明示

這是安全約束,不是最佳化。 上收預設停在 cwd 所在的 git repo 邊界;要把 issue 開到該 repo 之外的上層,必須明示(--target,或 config 中顯式宣告的上層關係)。

理由:repo 邊界即「轄區」。事務移出轄區若可以默默發生,就會出現「以為開在自己專案、其實開到上層」的靜默錯誤——而這種錯誤事後極難察覺(issue 就在別的 tracker 躺著,沒有任何訊號)。

實作上可直接沿用既有的邊界語彙,不必新造:

E. 上收到頂仍無解 → 問,不要猜

一路上收會停在 global,但 global 不是 tracker,不能開 issue。兩條路:

  1. 規定「最上層必須指定一個 catch-all general repo」(憲政類比:中央永遠存在)
  2. 上收到頂仍無解 → AskUserQuestion 交使用者裁決

建議取 (2)。取 (1) 會要求使用者指定一個 catch-all,而那個 repo 在實務上會變成垃圾桶——所有判不出來的東西都往那裡倒,久了失去分類意義。(2) 也與 #301 引用的憲法第 111 條後段「遇有爭議時,由立法院解決之」一致:判不出來時交裁決,而非塞給預設。


Actual

  • 無 global config 層(~/.claude/.idd/ 目錄存在但空)
  • 無 repo 地圖、無階層知識;六機制皆為單點解析
  • 缺層無偵測:判準若指向一個沒有 tracker 的層,行為未定義
  • 上收無邊界概念(因為根本沒有上收機制)

Impact

Out-of-scope

相關


Current Status

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions