ADR-016: UAT 證據來源分層與變更後複核
ADR 編號: 016 狀態: 已採納 (Accepted) 提案日期: 2026-09-29 決策日期: 2026-09-29 最後修訂: 2026-09-29 決策者: 團隊 peer review:設計方向三輪通過;P0、P1、P2 各三輪、P3 兩輪成果審閱通過 範圍:
/uat-script來源指紋、/uat驗收結果、/progress//work的 UAT 證據閘門、變更管理
摘要
UAT 指南的來源指紋目前對生成時讀過的每個檔案做整檔 SHA-256,任一 byte 改變就判定指南過期,並連帶讓已通過的人工驗收失效。框架升級、樣式調整或實作重構因此大量觸發重生與重驗,使用者習慣直接重蓋,真正需要重看的指南反而被淹沒。
本提案把一個指紋拆回兩個問題:「指南內容是否仍正確」與「先前的人工通過是否仍成立」。來源分為語意、脈絡與不列入三層;語意來源改變才判定 stale,脈絡來源改變則由真人複核,結論可以是保留、只重驗受影響場景,或回到重生。驗收結果改為逐場景綁定,讓規格的局部修改只作廢對應場景。
審閱結論
2026-09-29 經三輪 peer review 通過設計方向。兩輪共 8 條意見(6 major、2 minor)皆已修訂處置,無不採納項目;各輪修訂內容見「變更歷史」。審閱者明示確認四個待決點皆採作者建議,裁決見「審閱裁決」節。
通過範圍僅限本 ADR 與文件站索引。 本次通過不解鎖任何規則、skill 或工具的修改:P0–P3 各階段實作須另行送審;m-change-management.md 等現行規則在對應階段實作通過前維持原狀,ADR 狀態維持 Proposed,P3 成果審閱通過後才改為 Accepted。
採納(2026-09-29):P0–P3 成果審閱全部通過,狀態改為 Accepted。設計方向在實作中的修訂分別記於「P1 實作裁決」「P2 實作裁決」「P3 實作裁決」與「相容與遷移」;其中最大的偏離是不提供 migrate(依 florae-g1 模擬數據,見「相容與遷移」)。
背景
現行機制
/uat-script 以 .claude/skills/uat/scripts/evidence.py stamp-guide 建立 schema 5 sidecar,自動納入 US、完整 SBE 集合、design language 與 test-data-seed.md,並依 skill 指示以 --extra-input 納入 E2E Coverage/spec、環境來源檔及生成時讀取的其他權威來源(uat-script/SKILL.md Step「穩定場景 ID 與來源指紋」)。
| 機制 | 位置 | 行為 |
|---|---|---|
| 新鮮度比對 | check_guide | 整份 manifest 相等才算 fresh;錯誤訊息只有 guide sources changed,不指出哪個來源改變 |
| 結果綁定 | verify_result | 結果綁定整份指南 guideSha256 與 sidecar sourceFingerprintSha256;重新 stamp 即使內容不變,也使人工通過失效 |
| 共用單檔 | current_source_manifest | test-data-seed.md 與每個 mockup 的 m-design-language.md 各為單一檔案,任一 US 的修改讓所有引用它的指南過期 |
下游實測證據
下游專案 florae-g1 回報(2026-09):
- 41 份 P.UAT 指南中只有 1 份 fresh。
- 40 份過期指南中,31 份的 US 與 SBE 都沒有變更。
- 過期原因次數:實作檔 193、環境檔(package.json、vite.config、lock)70、E2E spec 61、設計語言 31、測試資料 26、翻譯 14。
- 一次頁級按鈕樣式調整讓 24 份指南需要重生,其中 21 份內容完全沒變,只是重蓋指紋。
最大一桶(實作檔)來自 generator 政策:skill 要求「生成時另讀原始碼也逐檔附加 --extra-input」,不是工具本身強制。
「回退」實際發生在哪裡
指南過期不會自動改寫共享 Sprint 階段,影響出現在下一次需要 UAT 證據時:
| 位置 | 行為 |
|---|---|
共享 /progress | 只在推進到某個 *.UAT 時呼叫 verify-result;已通過該階段的 US 不會被拉回 |
/work 分支進度 | 每次重驗連續證據鏈;任一 UAT stale,有效階段退回最後有效的一步 |
/work merge 整合重驗 | UAT stale 則維持 pending,須 /uat-script → /uat 重做才能推送 |
| 推進到 ✅ | 須重驗該軌道最後一次 UAT |
所以重做 UAT 的成本集中在合併與完成節點,並會一次爆發。
根因
同一個指紋同時回答兩個問題:
- 這份指南的內容還正確嗎?(文件問題)
- 先前的人工通過還適用於目前的產品嗎?(產品問題)
非規格變更通常不影響第一題,但可能影響第二題。UAT 只驗 E2E 證明不了的命題(操作手感、視覺辨識、真實外部依賴、非功能感受),而這些正是框架升級容易悄悄改變的地方。因此「指南仍適用」不等於「先前通過仍成立」;兩題需要不同的來源與不同的處置,不能用同一個整檔指紋判定。
決策
D1:兩個問題分開判定
| 問題 | 依據 | 不成立時 |
|---|---|---|
| 指南內容是否仍正確 | 語意來源 | stale:重生指南,受影響場景重驗 |
| 先前通過是否仍成立 | 脈絡來源 + 真人複核 | review-needed:由人依 D5 判斷 |
指南狀態從二元(fresh/stale)擴為三態:fresh、review-needed、stale。/uat 遇到 stale 阻擋;遇到 review-needed 先引導複核(D5),不直接要求重生。
D2:來源分三層
| 層級 | 來源 | 變更時的效果 |
|---|---|---|
| 語意來源 | US 中被場景引用的段落;UAT-only 的 SBE;Coverage Header 解析結果(場景 → E2E/UAT-only/Deferred);指南使用的測試資料段落;環境啟動命令及其解析定義;宣告為命題依據的權威來源(例如作為命題依據的 BR 原文、框架文件)的宣告段落,未宣告段落時為整檔 | stale |
| 脈絡來源 | 有效 Design Language;US 中未被引用的段落;E2E/UI 合約 spec 本體;旅程涉及的頁面與元件;旅程宣告的翻譯 key 值(或 D6 旅程的翻譯檔 fallback);框架版本(取自依賴宣告中框架套件的版本值);宣告為參考脈絡的其他來源;無法機械抽取定義的環境來源檔 | review-needed |
| 不列入 | lock 檔、建置設定全檔、未被任何旅程宣告的翻譯 key、與旅程無關的實作檔 | 無 |
兩條適用於所有來源的原則:
- 層級屬於片段,不屬於檔案。 同一個檔案可以同時提供多個片段、分屬不同層級,並分別比對。例如
package.json中scripts.{name}的值是語意來源,框架套件的依賴版本是脈絡來源,其餘內容不列入;只升級框架、scripts 不變時,指南變成 review-needed,而不是因整檔被排除而漏掉。指南狀態取所有已變片段中最嚴的一個。 - 權威來源依用途分類,不依檔案類型。
/uat-script以--extra-input納入的每個來源,都必須宣告用途:直接決定指南命題或操作內容者為「命題依據」(語意來源),只提供背景者為「參考脈絡」(脈絡來源)。未宣告用途者視為命題依據。作為命題依據的 BR 原文沿用 ADR-014 的邊界,以 bytes 摘要其宣告段落,不以 BR semantic digest 替代。
與原提案的差異及理由:
- E2E spec 本體列為脈絡,不是不列入。 UAT 會跳過「E2E 已證明的命題」;assertion 被刪除或放寬時,原本由 E2E 負責的命題會無人驗證。Coverage Header 的分類結果屬語意來源,spec 其餘內容由人複核是否仍證明相同命題。
- US 未被引用的段落列為脈絡,不是忽略。 段落綁定由 generator 宣告,漏宣告會造成漏報;未被引用段落的變更至少要讓人看過。
- 環境命令摘要的是解析定義,不是指南中的字串。 指南中的命令字串不會因
package.json改變而改變;應摘要實際解析到的定義,例如package.json的scripts.{name}值。無法機械抽取時,該來源檔整檔列為脈絡來源。 - 翻譯以 key 為單位追蹤,不整檔列入,也不整檔排除。 翻譯檔整檔列入會重現誤報(任何文案修改都讓所有指南過期);整檔排除則會漏掉「只改入口文字、頁面與元件檔案不變」的情況,而入口文字正是辨識性類旅程要判斷的東西。規則如下:
- 指南引用畫面文字(按鈕、選單、欄位標籤)時,generator 宣告對應的翻譯 key;工具摘要這些 key 在 UAT 使用語系下的值,列為該旅程的脈絡來源。
- D6 宣告 UI 入口依賴、但未宣告任何翻譯 key 的旅程,改以目標模組的翻譯檔整檔作為該旅程的脈絡來源(保守 fallback,只影響這類旅程)。
- 宣告的 key 在翻譯檔中不存在時 stamp 失敗,避免以錯誤 key 規避追蹤。
D3:語意來源以段落摘要,錨點缺失即 stale
- 單位:Markdown 標題區段(以標題路徑定位);需要更細的粒度時,於來源文件加入明確錨點標記。不以清單項目序號定位,避免插入一項就讓後續項目全部位移。
- 正規化:只做換行符統一、行尾空白去除、連續空行合併;不處理同義改寫,不宣稱能判斷語意等價。
- 錨點找不到(標題改名、段落刪除)時判 stale,寧可誤報不漏報。
- 綁定下限:UAT-only 的
sbe:場景由工具自動綁定其 SBE 檔;us:、ux:等補充場景必須至少宣告一個 US 錨點,否則 stamp 失敗。 - BR 已有語意 projection digest 前例(ADR-014),但兩者輸入語意不同;本決策只共用 bytes hash helper,不共用 projection 或 serializer,沿 ADR-014 的邊界。
D4:驗收結果逐場景綁定
每個 uat-scenario-id 的結果只綁定:
- 該場景在指南中的區塊摘要;
- 指南共用前言(使用前準備、身分與測試資料)的摘要;
- 該場景宣告的語意來源摘要;
- 取得通過時的脈絡來源摘要,以及可取回當時內容的引用(供 D5 複核比對,保存方式見 D5「差異證據的保存」)。
結果不再綁定整份指南與整份 sidecar 的 hash;只為一個場景重生指南時,其他場景的通過保留。verify-result 通過條件改為:目前指南的每個場景都有一筆有效的通過結果(區塊、前言、語意來源皆吻合),且其脈絡來源已吻合或已被複核為「保留」。
D5:變更後複核的三種結論
脈絡來源改變時,指南變為 review-needed。工具列出變更的來源、差異摘要,以及可能受影響的場景(依 D6 的宣告與涉及元件推導),由真人對每個受影響場景選擇:
| 結論 | 適用情況 | 處理 | 對進度的影響 |
|---|---|---|---|
| 保留 | 變更不影響該場景的人工判斷命題 | 寫入複核紀錄,保留原通過 | 無 |
| 部分重驗 | 指南仍正確,但該場景的體驗可能已改變 | 不重生指南;該場景結果標為待重驗,/uat 只走這些場景 | 只影響被標記的場景 |
| 轉為 stale | 複核發現指南本身已不正確 | 回到 /uat-script 重生,受影響場景重驗 | 依變更管理處理 |
複核紀錄的要求:
- 只能由真人做出:
/uat以明示確認取得結論,Agent 不得自行寫入「保留」。 - 內容:複核者、時間、檢視過的來源與差異、各場景結論與理由。
- 綁定所複核的脈絡摘要:同一批來源之後再改變,就要重新複核。
- 存放位置獨立:不寫入 sidecar 中會被結果綁定的部分,避免寫入複核紀錄反過來讓結果失效。
- 批次複核必須顯示差異:不得只列檔名讓人一鍵確認,避免複核退化成新的「習慣重蓋」。
- 複核不是驗收通過:「保留」只延續既有通過;沒有通過結果的場景不能經複核取得通過。
差異證據的保存
現行 sidecar 只記來源 hash,無法還原舊內容;驗收當時來源若尚未 commit,Git 也不一定能還原。沒有舊內容就無法顯示差異,「保留」就只剩人的記憶。因此在記錄通過結果與寫入複核紀錄時,同時保存每個脈絡來源的可取回引用:
| 當時來源狀態 | 保存方式 |
|---|---|
內容與記錄當下 HEAD 的同路徑 blob 相同 | 記錄該 commit 與路徑;結果檔之後提交在此 commit 之上,取得結果檔的 clone 通常也取得該 commit(若因 squash 或 rebase 而不可達,依下方「無法取得」處理) |
| 內容尚未 commit,或不符合上列條件 | 以內容 SHA-256 為名,存成隨 Git 追蹤的快照(位於 docs/testing/uat-results/ 下,同內容只存一份);翻譯 key 只存宣告的 key 值 |
取回時必須驗證取回內容的 SHA-256 等於結果記錄的摘要,不符視同無法取得。
舊內容無法取得時(快照遺失、commit 不可達、schema 5 遷移而來且 Git 歷史中找不到相符版本),該場景的複核結論只能是「部分重驗」或「轉為 stale」,不得選「保留」。
D6:依賴畫面體驗的旅程宣告 UI 入口
評估畫面體驗的旅程(例如「入口是否容易辨識」)可在指南中宣告所依賴的 UI 入口或元件,以及入口文字對應的翻譯 key(未宣告 key 時適用 D2 的翻譯檔 fallback)。這些入口相關的脈絡來源改變時,該旅程一定列入 D5 的受影響清單,並預設建議「部分重驗」,不預設「保留」。
D7:失效原因分兩類,回退時有條件保留
現行 m-change-management.md 規定 /progress --rollback 以 invalidate-result 作廢回退目標之後的整份 UAT 結果,重新推進時不得重用舊驗收結果。這與 D4「只作廢對應場景」衝突。
但只比對語意來源,不足以決定回退後保留哪些通過。因實作缺陷或先前驗收誤判而回退時,規格可能完全沒變,摘要全部吻合,舊通過卻已經不可信。因此失效原因分兩類,處理規則不同:
| 失效類別 | 觸發 | 範圍 | 能否因摘要吻合而恢復 |
|---|---|---|---|
| 來源變更 | 語意來源改變;/progress --rollback 且原因為規格變更 | 語意來源不吻合的場景 | 可以:摘要吻合的場景保留,重新推進時可重用 |
| 明示撤銷 | /progress --rollback 且原因為實作缺陷或驗收誤判;/uat 中發現先前通過有誤 | 由人選定的場景,預設為全部 | 不行:撤銷紀錄永久有效,只能以新的 /uat 驗收取代 |
/progress --rollback必須取得回退原因;原因未知或未回答時,視為明示撤銷全部場景(寧可多驗,不可誤留)。- 撤銷紀錄保存原因、撤銷者、時間與場景範圍;與來源變更造成的失效一樣,不刪除舊紀錄。
- Sprint 階段的回退語意不變。
此項推翻 m-change-management.md 現行明文規則,已由第 3 輪審閱裁決採用(見「審閱裁決」Q1);規則本身於 P3 實作通過後才修訂。
相容與遷移
P3 修訂(2026-09-29):不提供
migrate。 下方「原遷移設計」保留作為歷史,不實作。
現行做法:雙讀+重生升級。
- schema 5 指南與其結果持續以原本的整檔指紋判定與驗證(P1 起的雙讀),
/progress、/work不受影響。 - 指南重生時由
/uat-script產生 schema 6 版本;新指南的驗收結果為 schema 6 帳本,須重新驗收。schema 5 的通過紀錄不帶入帳本。 - 回退時 schema 5 結果整份失效(見 D7 與
m-change-management.md)。
不做 migrate 的理由:以 florae-g1 真實資料依下方遷移表唯讀模擬 58 份指南(其中 21 份有 passed 結果):
| 模擬結果 | 指南 | 其中有 passed |
|---|---|---|
| 無法遷移(schema 4 以前,工具本來就拒絕) | 23 | 5 |
| 規格已變,判 stale | 14 | 7 |
| 除非真人逐檔確認 extra 用途,否則 stale | 15 | 6 |
| 可自動轉為 review-needed | 5 | 3 |
| 須從 Git 歷史取回舊內容才能判定 | 1 | 0 |
migrate 能自動保住的通過只有 3 份,全部人工確認後最多再 6 份;實作卻須為「沒有來源宣告的指南」另建推導模式並取回 Git 歷史內容,成本與出錯風險都最高。這些指南多半已因來源變更過期、遲早要重生,重新驗收的代價小於維護一套遷移邏輯。
重生前須處理的資料問題(florae-g1 實測,追蹤於 FU-99):depends_on 中對不上實際標題的 test-data-seed 錨點(約 36 處)、UI 合約 spec 重複帶 Coverage Header(11 支);兩者都會讓 schema 6 stamp 拒收,須在重生前修正。
差異快照不做清理:快照以內容摘要為檔名、同內容只存一份,且被帳本中的通過與複核紀錄引用;刪除快照會讓之後的複核因舊內容取不回而無法選「保留」。若日後體積成為問題,另以「不被任何帳本引用」為條件設計清理。
原遷移設計(P3 決定不實作,保留歷史)
- schema 5 sidecar 視為舊版:
check-guide回報需升級,但不直接判定整份過期。 - 提供一次性
migrate指令,只遷移可證明未變的部分。原則:schema 5 只保存整檔 hash;整檔未變即可證明其中任何片段未變;整檔已變時,先嘗試從 Git 歷史取回舊內容(沿該路徑歷史逐版計算 SHA-256,找到與記錄相符的版本),取回後再依 D2 比對對應片段;取不回就無法證明,語意來源一律維持 stale。 - 依 schema 5 的來源角色逐類處理:
| schema 5 角色 | D2 歸屬 | 整檔未變 | 整檔已變,取回舊內容 | 整檔已變,取不回 |
|---|---|---|---|---|
us | 語意(遷移期間為整份,見下) | fresh | stale(schema 5 指南沒有段落宣告,無法判斷改變的段落是否被引用) | stale |
sbe | 語意 | fresh | 逐檔比對 hash(不需取回內容):SBE 集合增減,或 UAT-only 場景的 SBE 改變,則 stale;只有 E2E 或 Deferred 場景的 SBE 改變則 fresh。場景分類以 Coverage 為準,且 Coverage 必須已依下方 E2E spec 列證明未變,否則 stale | 同左 |
test-data-seed | 語意(遷移期間為整份,見下) | fresh | stale(理由同 us) | stale |
design-language | 脈絡 | fresh(以目前內容作為脈絡基線) | review-needed | review-needed,且不得「保留」(D5) |
extra:含本 US Coverage Header 的 E2E spec | Coverage 為語意,本體為脈絡 | fresh | 比對 Coverage:不同則 stale;只有本體不同則 review-needed | stale |
extra:環境來源檔 | 命令定義為語意、框架套件版本為脈絡,其餘不列入(同檔多片段) | fresh | 分別比對:命令定義不同則 stale;否則框架版本不同則 review-needed;兩者都相同則 fresh。命令定義無法解析時,整檔視為脈絡,review-needed | stale |
extra:翻譯檔 | 依 D2 以 key 追蹤 | fresh | review-needed(schema 5 指南未宣告 key,無法定位受影響場景,全部列入複核) | review-needed,且不得「保留」 |
extra:BR 原文(位於 requirements/business-requirements/) | 命題依據(語意) | fresh | stale(schema 5 未宣告段落,無法判斷改變的段落是否為依據) | stale |
extra:其他(實作檔、框架 README 等) | schema 5 未記錄用途,無法判定 | fresh | stale;migrate 可請真人逐檔確認用途,確認為「參考脈絡」者改為 review-needed,確認紀錄保存於遷移結果 | stale;真人確認為「參考脈絡」者改為 review-needed,且不得「保留」 |
- 用途只能由真人確認,Agent 不得自行判定為參考脈絡;未確認者一律依「命題依據」處理。這條避免遷移把規格變更降級為可複核保留。
- schema 5 指南沒有 US 錨點、測試資料段落與翻譯 key 的宣告。遷移後,每個場景暫以整份 US 與整份 test-data-seed 作為語意來源,並以原本納入的翻譯檔整檔作為所有場景的脈絡來源;下次以
/uat-script重生指南時,才依 D3 細分到段落。遷移只換 schema,不降低對規格的把關。 - 既有通過結果:只有結果綁定的
sourceFingerprintSha256等於被遷移的 sidecar,且guideSha256等於目前指南時,才轉為逐場景通過;否則不保留。 - 已經被重新 stamp 而作廢的舊結果,遷移無法恢復;如需恢復,須從 Git 歷史取回當時的 sidecar,並經真人確認。
- 遷移不得把目前的來源直接標為「已複核」。原提案的「依現有來源標為已複核」會把 US/SBE 真實變更的指南洗成 fresh(florae-g1 中有 9 份屬此情況),不採用。
考量的方案
| 方案 | 內容 | 不採用或保留的理由 |
|---|---|---|
| A. 維持現狀 | 整檔指紋,任一改變即 stale | 誤報淹沒真實過期,已造成習慣性重蓋,把關失效 |
| B. 只收緊 extra-input | 不再納入實作檔與翻譯檔 | 可消除最大一桶,但只是降低把關,沒有提供「產品已改變」時的複核路徑;可作為過渡,但不單獨採用 |
| C. 只改善診斷 | check-guide 回報具體變更來源 | 無 schema 變更、風險最低,但不減少重做;作為 P0 先行 |
| D. 完全排除非規格來源 | 只看 US/SBE/Coverage | 無法覆蓋「指南正確但體驗改變」的情況,框架升級後的 UAT-only 命題無人把關 |
| E. 三層來源 + 逐場景 + 三結論複核(本案) | 見 D1–D7 | 採用 |
影響範圍
| 類別 | 項目 |
|---|---|
| 工具 | uat/scripts/evidence.py(stamp-guide、check-guide、verify-result、invalidate-result 區分來源變更與明示撤銷、新增 review 相關指令、脈絡來源快照的寫入與取回驗證;P3 決定不做 migrate);uat/scripts/test_evidence.py;schema 5 → 6 |
| Skills | uat-script/SKILL.md(來源宣告、--extra-input 用途宣告(命題依據/參考脈絡)、場景錨點、翻譯 key 宣告);uat/SKILL.md(review-needed 流程、部分重驗、驗收中發現誤判時的撤銷);progress/SKILL.md(--rollback 取得回退原因;第 372 行仍寫 schema 2,已與實作不符);progress/scripts/update.py(證據 inventory);progress/execution-contract.json |
/work | work/references/progression.md、integration-revalidation.md(逐場景有效性、review-needed 在合併時的處理) |
| BR | m-business-requirements.md 第 226、233 行與 ADR-014 明定「保留 schema 5」「BR hash 不替代 UAT 全檔指紋」,須同步修訂;br/SKILL.md;br/scripts/test_boundaries.py(直接 import evidence) |
| Rules | m-change-management.md(stale 描述、只影響脈絡來源的變更類型、D7 回退語意);m-methodology.md(/uat-script、/uat 描述) |
| 發布 | 工具與 skills 同屬 methodology 命名空間,須同批 /methodology publish;下游不需遷移,既有 schema 5 指南以雙讀繼續運作,重生時升級 |
分階段實施
| 階段 | 內容 | schema |
|---|---|---|
| P0 | check-guide 回報變更來源(role/path);可獨立先行 | 5 |
| P1 | 三層來源與段落摘要;fresh/review-needed/stale 三態 | 6 |
| P2 | 逐場景結果、複核紀錄與三種結論;/uat 部分重驗 | 6 |
| P3 | /progress 回退原因(D7)、/work、BR consumers 與 rules 對齊;fleet publish(不做 migrate,見「相容與遷移」) | 6 |
驗收條件
- 框架版本升級、按鈕樣式調整、實作重構、翻譯修改,或未改變 Coverage Header 的 E2E spec 本體修改,都不會讓任何指南變成 stale。
- 上述變更若涉及旅程的脈絡來源,指南變成 review-needed;複核結論為「保留」時,寫入一筆紀錄即恢復 fresh,全程不改指南內容、不重驗。
- 只修改某旅程宣告的翻譯 key 值(頁面與元件檔案不變)時,該旅程變成 review-needed;D6 旅程未宣告 key 時,翻譯檔任何修改都讓它變成 review-needed。
- 脈絡來源的舊內容無法取得或取回內容的摘要不符時,複核不能選「保留」。
- 複核結論為「部分重驗」時,指南不重生,
/uat只要求重驗被標記的場景,其餘場景的通過保留。 - US 修改一條驗收標準時,只有引用該段落的場景變 stale、其結果失效。
- UAT-only 的 SBE 內容或 Coverage 分類改變時,指南變成 stale。
- E2E spec 刪除或放寬 assertion 時,指南至少變成 review-needed。
- 標題改名或段落刪除導致錨點找不到時,引用它的場景變成 stale。
- Agent 無法在沒有真人明示確認的情況下寫入複核結論。
- 只升級框架、
package.json的 scripts 不變時,指南變成 review-needed(一般情況與遷移皆同),不會 stale,也不會因為整檔排除而維持 fresh。 - 宣告為命題依據的權威來源(例如 BR 原文)的宣告段落改變時,指南變成 stale。
- (遷移未實作,下兩條改由雙讀保證:schema 5 判定不變)遷移不會讓下列指南變成 fresh:US 或 test-data-seed 已變更;SBE 集合增減;UAT-only 場景的 SBE 已變更。只有 E2E 或 Deferred 場景的 SBE 變更、且 Coverage 已證明未變時,才可 fresh。
- 遷移時,E2E spec 或環境來源檔已變更、且無法從 Git 歷史取回舊內容證明 Coverage 與命令定義未變,指南維持 stale;用途未經真人確認的其他 extra 已變更,指南維持 stale。
- 以實作缺陷或驗收誤判為原因的
/progress --rollback,即使所有摘要都吻合,被撤銷場景的舊通過也不會恢復;未提供回退原因時,全部場景視為撤銷。
非目標
- 不改變 E2E 與 UAT 的分工,也不改變 UAT-only 與延後場景的分類規則。
- 不把複核當成人工驗收通過;複核只延續既有通過。
- 不宣稱段落摘要能判斷同義改寫。
- 不改變
/progress的階段狀態機與--force不得略過 UAT 證據的規則。
審閱裁決
第 3 輪審閱者明示確認,以下四項皆採作者建議:
| 編號 | 問題 | 裁決 |
|---|---|---|
| Q1 | /progress --rollback 是否改為有條件的逐場景保留(D7) | 採有條件保留:規格變更造成的失效逐場景比對;實作缺陷或驗收誤判造成的明示撤銷,不因摘要吻合而恢復;未提供回退原因時視為全部撤銷;Sprint 階段回退語意不變 |
| Q2 | 「旅程涉及的頁面與元件」如何取得:由 generator 宣告,或從 E2E/UI 合約 spec 推導 | 先由 generator 宣告 UI 依賴,工具驗證路徑存在;推導留待後續 |
| Q3 | 框架版本升級是否一律觸發 review-needed,或僅在 CHANGELOG 有 UI 行為變更時觸發 | 一律觸發 review-needed,複核畫面附上 CHANGELOG 對應區段,降低判斷成本 |
| Q4 | 方案 B(收緊 extra-input)是否在 P1 完成前先行,作為過渡 | 不先行;只有 P0(回報變更來源)先行,避免在沒有複核路徑時降低把關 |
P1 實作裁決
P1(schema 6 來源分層與三態)於 2026-09-29 經三輪 peer review 通過。實作與 florae-g1 真實指南實測中定案的細節如下;權威操作說明見 uat-script/SKILL.md 的來源宣告表。
宣告方式:來源宣告以 HTML 註解寫在指南內。第一個 uat-scenario-id 之前為指南層級,之後為該場景層級。stamp 後修改指南(含宣告)即 stale。錨點採與 Docusaurus 相同的 heading slug,重複標題比照 github-slugger 遞增。
| 裁決 | 內容 | 理由 |
|---|---|---|
| 宣告來源消失一律 stale | 宣告的檔案、錨點、key 或 JSON 路徑消失時,不論語意或脈絡層都判 stale | 宣告已失效,重新 stamp 必被拒收;check 與 stamp 的有效性判定須一致 |
| Coverage 以 Header 數量+正規化分類判定 | 條目重排不影響;Header 增減、邊界變動或分類歧義為 stale;stamp 拒收同檔多個 Header 與分類歧義 | 語意來源是「場景到分類的解析結果」,不是文字排列 |
| 非 JSON 環境與框架來源逐行摘要 | 以宣告文字所在的行為片段(如 .env 的 port、Gradle 的 plugin 或依賴座標);寫 * 才整檔列為脈絡 | 整檔追蹤會讓建置設定的任何修改都觸發複核 |
| 依賴 UI 但未宣告翻譯 key 的旅程 | 必須宣告指南層級 uat-i18n-source(模組無翻譯時寫 none),其檔案整檔綁定到這些旅程 | 以明確宣告落實 D2/D6 的翻譯 fallback,不以檔名啟發式猜測 |
| design language 依目標模組定位 | web-mockup 用自身、web 用 {name}-mockup;server 目標不綁 design language | US 未寫 module 時仍能唯一定位;S.UAT 驗收 API 消費者旅程,畫面設計不是其判斷依據 |
拒絕 node_modules 路徑 | 框架行為依據改以 uat-framework 宣告版本 | P0 實測發現指南把套件內原始碼列為來源 |
| review-needed 暫時處置 | P2 複核紀錄上線前比照 stale 阻擋,但以 guideStatus 區分 | 沒有複核紀錄時不得放行 |
| schema 5/6 雙讀 | generator 6.0.0 以前產生 schema 5,判定邏輯不變;驗收結果仍為 result schema 5 | 既有驗收結果在 P3 遷移前須持續可驗證 |
| 發布時機 | P1、P2 各自審閱並 commit,不發布;P3 完成後一起 /methodology publish | 避免下游先看到沒有複核流程的 review-needed |
P1 審閱裁決的待決點(審閱者採作者建議):
| 編號 | 裁決 |
|---|---|
| Q1 重複 Coverage Header | 維持嚴格拒收;florae-g1 有 11 支 UI 合約 spec 違規,須以 /e2e-script 修正後才能重生指南 |
| Q2 失效的 seed 錨點 | 維持嚴格;重生時改正 depends_on。florae-g1 約 36 處舊英文錨點,P3 遷移須另定處理方式 |
| Q3 非 JSON 翻譯檔 | P1 維持整檔 fallback;P2 上線後依實際複核負擔再評估是否以 Node 抽取 key |
| Q4 server 目標的 design language | 採用:不綁 design language |
P2 實作裁決
P2(逐場景驗收帳本與變更後複核)於 2026-09-29 經三輪 peer review 通過(4 條 major 皆修正)。權威操作說明見 uat/SKILL.md Step 2.2a、2.4 與 5.x。
驗收帳本:結果檔升為 result schema 6,內容是只增不刪的 entries(場景紀錄、複核、撤銷、問題),以 sequence 定先後、不依賴時鐘;/progress、/work 仍只讀一個檔。場景紀錄綁定場景區塊、共用前言與適用於該場景的語意/脈絡片段摘要。場景區塊自 uat-scenario-id 起,涵蓋場景標題及其子段落;其餘內容(不含 frontmatter)為共用前言。所有寫入改由工具指令完成,不再由 Agent 手寫 JSON。
| 裁決 | 內容 | 理由 |
|---|---|---|
| 複核綁定呈現的版本 | review-status 回傳 reviewToken(綁 sidecar、帳本序號、受影響場景與每筆差異的前後摘要);record-review 必須帶回,不一致即拒寫並要求重新呈現差異;寫入沿用同一份已驗證狀態 | 結論必須對應使用者實際看過的差異;來源在呈現後又改變時不得套用舊結論 |
| 帳本寫入持跨程序鎖 | 所有寫入入口以 flock 鎖定 uat-results 目錄,涵蓋讀取、驗證、追加與保存;逾時報錯 | 檢查與寫入分離時,並行操作可互相覆蓋,已成功的撤銷可能遺失 |
| 差異快照一律以片段摘要存檔 | 取代 D5 表中「commit 引用或快照」的二擇一;快照位於 docs/testing/uat-results/snapshots/{sha256}.txt,stamp、記錄通過、複核時寫入,取回時驗證摘要 | 片段常只是檔案的一部分,commit 引用須重新推導片段;摘要快照可自我驗證、同內容只存一份。代價是 repo 多出快照檔 |
| 真人確認以流程保證+留痕 | /uat 以 AskUserQuestion 逐場景取得結論與理由;紀錄保存操作者(預設 git user.name)與理由 | 工具無法證明操作者身分,SKILL 如實寫明 |
| 首次驗收前也能複核 | review-needed 時 initialize-result 可建立不含通過紀錄的空帳本 | 避免初始化與複核互相阻擋;複核不產生通過 |
| 撤銷目標須為已知場景 | 目前指南的場景 ∪ 帳本曾記錄的場景;任一未知即整次拒寫 | 拼錯名稱不得回報成功而讓原驗收仍有效 |
| P3 前回退撤銷全部 | invalidate-result 對帳本寫入一筆撤銷全部場景的紀錄(rollback) | 與 m-change-management.md 現行規則一致;依回退原因逐場景保留屬 P3 |
| 重新 stamp 與複核 | 指南層級的複核只適用於當時的 sidecar;場景層級的「保留」針對場景的通過紀錄,重新 stamp 後延續 | 指南層級基準屬於特定 stamp;場景的保留判斷與 stamp 無關 |
P3 實作裁決
P3(方法論對齊與回退原因)於 2026-09-29 經兩輪 peer review 通過(1 條 major、1 條 minor 皆修正);Q1–Q4 採作者建議。
| 裁決 | 內容 | 理由 |
|---|---|---|
不提供 migrate | schema 5 指南與結果持續雙讀;重生時升為 schema 6 並重新驗收 | florae-g1 模擬:自動可保住的通過僅 3 份,人工確認後最多再 6 份;實作與出錯成本最高(見「相容與遷移」) |
| 回退原因(D7) | invalidate-result --cause:spec-change 只留紀錄、由來源摘要判定;defect/misjudgment 撤銷指定或全部場景;未提供原因撤銷全部;/progress --rollback 以 AskUserQuestion 取得原因 | 規格變更的影響由摘要精確判定;缺陷與誤判與摘要無關,不得因吻合而恢復;原因不明時寧可多驗 |
| 恢復方式依原因分流 | /progress 閘門、/work 整合重驗與 evidence.py 錯誤訊息皆區分:stale 才重生、review-needed 先複核、needs-uat 只重驗受影響場景 | 一律要求重生會讓逐場景保留失去作用,並重新引發「習慣重蓋」 |
| 快照不清理 | 見「相容與遷移」 | 快照被帳本引用,刪除會讓複核無法選「保留」 |
| 規則對齊範圍 | 已修訂 m-change-management、m-business-requirements、m-doc-version、m-work、m-methodology、/progress 與 /work 的說明、ADR-014(部分取代註記);以詞表搜尋確認其餘命中為無關或歷史保留 | 依 m-change-management.md「機制或架構前提變更的跨文件檢視」 |
相關文檔
- ADR-002: US 變更管理
- ADR-014: 選用 BR 業務需求與規格追溯
.claude/rules/m-change-management.md.claude/rules/m-work.md.claude/skills/uat-script/SKILL.md、.claude/skills/uat/SKILL.md
變更歷史
| 日期 | 內容 |
|---|---|
| 2026-09-29 | 初稿:依下游實測提案整理;補入變更後複核的「部分重驗」結論、E2E spec 列為脈絡來源、環境命令改摘要解析定義、遷移只保留可證明未變的部分 |
| 2026-09-29 | 第 1 輪審閱修訂:遷移改為依 schema 5 角色逐類定義並可從 Git 歷史取回舊內容;翻譯改以 key 追蹤並提供 D6 fallback;D7 區分來源變更與明示撤銷;D5 新增差異證據的保存與無法取得時的限制;修正驗收條件中 E2E spec 的矛盾 |
| 2026-09-29 | 第 2 輪審閱修訂:D2 明定層級屬於片段、同檔可多角色(命令定義與框架版本分別比對);權威來源依用途分為命題依據與參考脈絡,未宣告視為命題依據;遷移表新增 BR 原文列,其他 extra 未經真人確認用途即 stale;統一 SBE 遷移範圍與驗收條件 |
| 2026-09-29 | 第 3 輪審閱通過設計方向;Q1–Q4 依審閱裁決定案(「待決點」改為「審閱裁決」);狀態維持 Proposed,實作 P0–P3 另行送審 |
| 2026-09-29 | P0 實作經三輪 peer review 通過:check-guide 過期時逐筆回報變更來源(CLI 錯誤 JSON 附 sourceChanges,永不為空),schema 與判定不變;審閱決定 P0 單獨發布、錯誤訊息不截斷。狀態仍為 Proposed |
| 2026-09-29 | P1 實作經三輪 peer review 通過(5 條 major 皆修正):新增「P1 實作裁決」節記錄宣告方式與實作細節裁決;狀態仍為 Proposed,未發布 |
| 2026-09-29 | P2 實作經三輪 peer review 通過(4 條 major 皆修正):新增「P2 實作裁決」節;差異快照改為一律以片段摘要存檔(修訂 D5 保存方式);狀態仍為 Proposed,未發布 |
| 2026-09-29 | P3 實作經兩輪 peer review 通過:新增「P3 實作裁決」節;狀態改為 Accepted |