文件解析遷移:設計類
狀態:P2-3 設計已通過審閱(20261017-docs-resolve-p2-3,第 2 輪,受審 revision r2-ed0145d21ad1c955;2 條意見均處置);P2-3 實作已通過審閱(20261017-docs-resolve-p2-3-impl,第 2 輪,受審 revision r2-e0da1ddeec030484,2026-10-19;2 條 minor 均處置),實作中確認的修訂見 §7 依據:ADR-023 的 D4 與實作分期 P2;文件解析與索引規格(P2-1);規格類遷移(P2-2,已實作);追蹤項目 FU-142 本頁定義:設計類 4 個 skill(
/domain-model、/api-spec、/system-context、/persona)改用docs.py resolve/register時的類型欄位與 skill 改寫方式。共用契約沿用 P2-2 的/docs-sitereferences/doc-locations.md,本頁只寫設計類特有的部分。
1. 範圍
| 做 | 不做 |
|---|---|
為 domain-model、api-spec、persona、system-context 加上範本欄位 | data-model(目前沒有產出者,P2-5 一併盤點) |
把 domain-model 拆成單檔與多檔兩個類型(§2.2);新增 api-spec-draft(§2.3) | Bruno 測試集(位於 server 模組,不在 docs 模組的 registry 範圍;/api-spec 維持現行) |
4 個 skill 改用 resolve/--list 取得路徑,建立或搬移頁面後執行 register;/domain-model 舊路徑遷移不再直接改 llms.txt | 改寫各 skill 的文件內容、提問、source 維度與 Domain Model 的拆分門檻 |
doc-locations.md 的「已遷移的類型」表加入本頁的類型 | /domain-model 模式 E 的程式碼 retarget、project.json 的 description 同步、module-inventory.md 的生成(不是 docs 模組文件) |
/persona 的文檔模組偵測方式(與 m-skill-execution.md 的偵測規則不同,屬另一議題) |
2. 類型欄位
2.1 範本
{G} 代表 {docsRoot}/docs/design。kebab 為 ^[a-z0-9]+(-[a-z0-9]+)*$。
| 類型 | template | 其他 locate | params | key | example |
|---|---|---|---|---|---|
domain-model(單檔) | {G}/domain-model.md | {docsRoot}/docs/specs/domain-model.md(legacy) | — | — | — |
domain-model-part(多檔,新) | {G}/domain-model/{part}.md | {docsRoot}/docs/specs/domain-model/{part}.md(legacy) | part ^(overview|epic-[0-9]+|domain-[a-z0-9]+(-[a-z0-9]+)*)$ | part | epic-2 |
api-spec | {G}/api-specs/{resource}.md | — | resource kebab | resource | orders |
api-spec-draft(新) | {G}/api-specs/{resource}.generated.md | — | resource kebab | resource | orders |
persona | {G}/personas/{persona}.md | — | persona kebab | persona | shop-owner |
system-context | {G}/system-context.md | — | — | — | — |
api-specs/README.md、personas/README.md、design/README.md 是目錄頁(section-index),屬內容,不加範本。
2.2 拆分 domain-model
現行 domain-model 類型同時涵蓋單檔(domain-model.md)與多檔目錄(domain-model/*.md)。兩者識別不同:單檔是唯一一份文件(key 為空),多檔以分檔名稱識別(overview、epic-{N}、domain-{D})。一個類型只能有一個範本,且 key 必須出現在每條 locate(R12),因此拆成兩個類型:
| 類型 | paths | legacy |
|---|---|---|
domain-model | {G}/domain-model.md | {docsRoot}/docs/specs/domain-model.md |
domain-model-part | {G}/domain-model/*.md | {docsRoot}/docs/specs/domain-model/*.md |
其他特性(persistent、page、writer /domain-model)與現行相同。拆分也讓 /domain-model 現行的「完整盤點四個候選、兩個以上並存即停止」直接對應到 resolve 的結果(§3.1)。
2.3 新增 api-spec-draft
/api-spec 在既有規格存在時,可讓使用者選擇「產生為 {resource}.generated.md 草稿」。草稿與正式規格並存是正常狀態,若同屬 api-spec 類型、以 resource 為 key,就會被判定為重複。因此另設類型(priority 5,比 api-spec 的 *.md 更精確),以同一個 resource 識別。
2.4 以 fleet 實際檔案驗證
以 P2-1 的 resolve.Locator 對 fleet 9 個 workspace 的全部檔案驗證:R12 全部通過,沒有無法解析的檔案,也沒有重複 key。
| workspace | 可解析 |
|---|---|
| 參考實作、florae-g1 | domain-model-part 8、api-spec 26~27、api-spec-draft 1(僅參考實作)、persona 6、system-context 1 |
| nbsos | domain-model-part 4(含 domain-{D})、api-spec 33、system-context 1 |
| almanac-g1、ict、tms、tts-v1 | domain-model(單檔)各 1、api-spec、persona、system-context 依專案 |
| tps-g1、crm-v2 | 依專案(crm-v2 尚無設計文件) |
所有下游都不需要專案層設定或改名。
3. skill 改寫
各 skill 依 doc-locations.md 的共用契約(只對已遷移類型呼叫、讀取來源與建立成果分開、直接路徑、部分參數、篩選結果的完整性、類型目錄、連結、站台索引),以下只列各 skill 特有的部分。查無時的處置一律維持現行。
3.1 /domain-model
位置偵測(現行「完整盤點四個候選」):
| 候選 | 以 resolve 判定 |
|---|---|
| 新單檔/舊單檔 | resolve --type domain-model:exists: true 時依 legacy 區分新舊;RS-AMBIGUOUS 即新舊單檔並存 |
| 新多檔/舊多檔 | resolve --type domain-model-part --list:items 中 legacy: false 者為新多檔、legacy: true 者為舊多檔 |
候選數為 0 → 無模型;恰 1 → 依類型決定單檔或多檔模式;2 個以上(含 RS-AMBIGUOUS,以及同位置單檔與多檔並存)→ 停止,列出各候選由人決定(現行規則)。恰 1 且在舊位置時,依現行分兩種讀取方:
/domain-model:先建議遷移,拒絕則停止,不在舊路徑寫入。- 其他讀取方(
/api-spec、/us的depends_on、/assess、/analyze等):照常讀取並提示執行/domain-model遷移,不阻擋;產生引用時指向resolve回傳的舊位置檔案(它就是當下實際存在的檔案),由遷移統一改寫。新多檔以「有任何分檔」判定,比現行「有overview.md」更嚴:只有分檔、沒有overview.md的目錄也算候選,不會被當成沒有模型而另建單檔。
其他:
- 新檔路徑:單檔
resolve --type domain-model;多檔各分檔resolve --type domain-model-part --param part={overview|epic-N|domain-D}。自動拆分(單檔 → 多檔)刪除單檔、建立分檔後執行register。 - 舊路徑遷移:
git mv、改寫docs/內引用、等價紀錄照現行;手動側欄(sidebars*.ts)與design/README.md是內容,照現行更新。llms.txt分兩部分處理:- 受管區塊:不手改,搬移後以
register重建(新位置的頁面列入、舊位置的頁面移除)。 - 區塊外的手寫條目:
register只接管現存頁面的條目,指向舊網址的手寫條目會留在區塊外,且register --check仍為 exit 0,因此須定向清理。對象只限- {permalink} - {描述}形式、permalink 在舊模型路由下(/specs/domain-model、/specs/domain-model/…,含 baseUrl)的條目:以引用改寫的同一路由對應算出新 permalink,新 permalink 已在register後的受管區塊中才算確認指向該模型,刪除該行;手寫描述與區塊內自動描述不同時,改寫區塊內該條目的描述以保留原文(區塊內允許改描述)。新 permalink 不在區塊中的條目、以及在說明文字中提到舊網址的行,列入未完成清單,不猜測。其他內容一律不動。
- 完成條件在現行條件之外再加兩項:
llms.txt(區塊內外)沒有指向舊模型路由的條目;register --checkexit 0。
- 受管區塊:不手改,搬移後以
- 模式 A、D 讀取 Epic、US、SBE、Roadmap 改用 P2-2 的
resolve/--list(取代epics/epic-{N}-*.md、user-stories/epic-{N}/US-*.md、sbe/epic-{N}/US-*/等固定樣式)。 - 其他 skill 的
depends_on錨點(design/domain-model.md#{Entity}、design/domain-model/epic-{N}.md#{Entity})由對應類型的resolve結果轉為相對於docs/的路徑。
3.2 /api-spec
- 既有規格判定(寫入前讀
source):resolve --type api-spec --param resource={resource}。選擇產生草稿時:resolve --type api-spec-draft --param resource={resource}。 - 補建(backfill)列出既有規格:
resolve --type api-spec --list,取代 Globapi-specs/*.md。 - 正向模式讀 US、SBE:改用 P2-2 的
resolve --type us/sbe --list(現行樣式**/user-stories/...未錨定{docsRoot});讀 Domain Model 依 §3.1 的偵測。 - 建立新規格或草稿後執行
register。 - Bruno(
{serverModule}/bruno/**)、deploy.json、application.yml等 server 模組檔案維持現行。
3.3 /system-context
- 文件:
resolve --type system-context(exists決定建立或修訂,現行)。 - 來源:
persona --list、resolve --type roadmap、feature-list、api-spec --list。 depends_on的 Persona 路徑由persona的resolve結果計算。- 新建時執行
register。project.json與module-inventory.md維持現行。
3.4 /persona
- 列出:
resolve --type persona --list(README.md屬目錄頁,不在結果中)。 - 檔案 ID 與名稱分開:
persona參數是檔名的{personaId},只接受 kebab-case(^[a-z0-9]+(-[a-z0-9]+)*$);使用者輸入的多半是顯示名稱(可能是中文或含空白)。輸入不符合 ID 格式時不傳給resolve,否則得到的是RS-PARAM(參數錯誤),不是「查無」,也到不了現行的建立流程。 - 以名稱查詢(模式 B,現行「檔名或 frontmatter 匹配」):
- 以
resolve --type persona --list取得全部 Persona,逐份讀 frontmatter,以輸入比對name(即{personaId})與display_name;輸入符合 ID 格式時另比對檔名的params.persona。 - 命中 1 份 → 顯示摘要(現行);命中多份 → 列出由使用者選擇(現行);0 份 → 照現行詢問是否建立。
- 輸入符合 ID 格式時,比對與
resolve --type persona --param persona={輸入}的結果一致;兩者不同時列出差異,不猜。
- 以
- 新建:
{personaId}由名稱轉 kebab-case(現行);轉換結果不符合 ID 格式(例如中文名稱無法直接轉換)時,向使用者確認{personaId},不以拼音或編號自行猜測。以resolve --type persona --param persona={personaId}取得新路徑;已存在時照現行詢問覆寫或取消。personas/README.md為 Persona 類型目錄的README.md(doc-locations.md第 5 節),內容照現行更新。新建後執行register。
3.5 查無處置(維持現行)
| skill/情境 | 現行處置 |
|---|---|
/domain-model 無 Roadmap/Epic | 進入模式 B,不報錯 |
/domain-model 指定 Epic 不存在 | 報錯並列出可用 Epic(resolve --type epic --list) |
/domain-model 舊路徑、使用者拒絕遷移 | 停止,不寫入 |
/api-spec 指定 Epic 無目錄或無 US | 報錯 |
/api-spec 部分 US 沒有 SBE | 詢問;沒有任何 SBE 時只用 US |
/api-spec 評估摘要不存在 | 繼續 |
/system-context --sync-only 沒有文件 | 提示先執行 /system-context |
/persona 目錄不存在或為空 | 提示 --suggest/--create |
/persona --suggest 沒有 Roadmap | 報錯 |
4. 測試
| 類別 | 測試 |
|---|---|
| 範本 | 六個類型(含兩個新類型)R12 通過;以實際路徑 resolve 既有文件與新路徑 |
| 拆分 | domain-model.md 歸 domain-model、domain-model/epic-2.md 歸 domain-model-part;新舊單檔並存時 RS-AMBIGUOUS;多檔 --list 區分 legacy |
| 草稿 | orders.md 歸 api-spec、orders.generated.md 歸 api-spec-draft,兩者並存時都不報重複 key |
| 走查 | 以可重跑的腳本(記錄命令、exit code、斷言)對參考實作與隔離 workspace 執行:四候選偵測的 0/1/2 個情境、草稿路徑 |
| 舊路徑遷移 | 隔離 workspace 的 llms.txt 區塊外放一筆指向 /specs/domain-model 的手寫條目與一行提到舊網址的說明文字:遷移後區塊內有新位置、區塊內外都沒有舊模型路由的條目、手寫描述保留在區塊內、說明文字列入未完成、其他區塊外內容逐位元組不變、register --check exit 0;另以「區塊外條目的新 permalink 不存在」驗證該條目不被刪除且列入未完成 |
| Persona | 中文顯示名稱與含空白名稱:不呼叫 resolve --param(不出現 RS-PARAM),以 frontmatter display_name 命中;以 ID 查詢命中;兩份同名時列為多筆;查無時進入建立流程;建立時中文名稱無法轉為 ID 而要求確認 ID,確認後 resolve 回傳新路徑 |
| fleet | §2.4 的驗證腳本納入證據,送審時以 canonical registry 重跑 |
5. 會修改的檔案
| 檔案 | 變更 |
|---|---|
/docs-site 的 docs-registry.json、測試 | 四類型範本;拆分 domain-model;新增 api-spec-draft |
/docs-site 的 references/doc-locations.md | 「已遷移的類型」表加入本頁類型 |
/domain-model、/api-spec、/system-context、/persona 的 SKILL.md | §3 |
m-depends-on.md | 已遷移類型清單加入設計類(行為不變) |
| 參考實作 app-docs | 投影更新 |
6. 待決與審閱重點
待決:
domain-model拆成單檔與多檔兩個類型(§2.2),或維持一個類型、改以其他方式表達兩種佈局。.generated.md草稿另設api-spec-draft類型(§2.3)。- 新多檔以「有任何分檔」判定候選(§3.1),比現行「有
overview.md」更嚴。
請特別看:
- §3.1 的四候選偵測是否與
/domain-model現行規則完全對應(特別是新舊並存、拒絕遷移時的處置)。 /domain-model舊路徑遷移:受管區塊由register重建,區塊外指向舊模型路由的手寫條目定向清理(§3.1)。- Bruno 維持在 registry 之外是否合適。
7. 實作修訂
| 項目 | 原設計 | 修訂 | 理由 |
|---|---|---|---|
| 多檔目錄中有無法解析的檔案(§3.1) | 未明訂 | resolve --type domain-model-part --list 回 RS-UNPARSED(例如 domain-model/notes.md)時,/domain-model 停止並列出該檔,依 DR-UNPARSED-LOCATION 處理,不略過 | 略過會讓候選盤點少算一個分檔,等於靜默擇一 |
/api-spec 其他讀取點(§3.2) | 只列既有規格判定、補建列舉、US/SBE | 另改:Step 0.5 的 Domain Model 評估摘要改用 resolve --type assess-summary --param target=domain-model;--adopt-legacy 的候選盤點改用 api-spec --list;反向萃取的規格路徑改用 resolve,建立新規格後 register | 同一 skill 中殘留固定路徑會與已遷移的類型不一致 |
/system-context 來源查無(§3.3) | 「來源」只列 resolve 呼叫 | 補一句:來源 exists: false 或清單為空時照現行略過該來源,不把新路徑當成要建立的文件 | 對應共用契約第 2 節「讀取來源」 |
/persona 列出與目錄頁(§3.4) | 未明訂 | 模式 A 與「目錄為空」的提示都以 --list 的 items 判定;personas/README.md 位置為 Persona 類型目錄(尚無 Persona 時為新路徑的目錄) | 對應共用契約第 5 節「類型目錄」 |
| 共用契約與索引(§5) | 只列 doc-locations.md 的類型表 | doc-locations.md 另補:參數值須符合類型格式,使用者輸入的名稱不是參數值;第 1 節「其他類型」的舉例移除已遷移的 Domain Model、API Spec。m-docs-site.md、m-methodology.md 的已遷移類型清單同步;/docs-site 1.6.0 | R1-01 的教訓適用所有以名稱查詢的情境,不只 /persona |
| skill 版本 | — | /domain-model 3.7.0、/api-spec 5.12.0、/system-context 1.1.0、/persona 1.2.0 | 路徑取得方式改變;/persona 名稱比對另加 display_name |
| 讀取設計類文件的其他 skill | 未列入範圍 | 本輪不改:/analyze、/assess、/us、/impl 等讀取 Domain Model 時依 /domain-model「位置、解析與舊路徑遷移」,該節已改用 resolve;讀 API Spec、System Context、Persona 的固定路徑與範本路徑相同且沒有舊位置,行為不變。位置字樣更新已明列在文件解析與索引規格 §1 的 P2-5 範圍,包含 /assess 的「Domain Model 沿用現行規則」 | 實作審閱 R1-01:維持本輪 4 個產出 skill 的範圍,延後工作有明確落點 |
多檔缺少 overview.md | 以任何分檔判定已有多檔模型,未明訂概覽缺檔的讀寫 | 讀取時略過缺檔、載入既有分檔並維持 multi;/domain-model 在產出階段依已載入分檔及本輪確認內容建立概覽,報告註明新建與來源,不足處列待補;唯讀讀取方只提示,不建立文件。多檔寫入表改為「建立或更新」 | 實作審閱 R1-02:補齊新狀態的處置,避免讀不存在的來源或另建單檔模型 |