拷問開放式決策
持續面試使用者關於計畫、票證或設計的細節,直到達成共識。目標是在開始工作前達成一致:基於錯誤假設的變更,會耗費整個實作成本,加上後續的審查、瀏覽器走查和合併。一個事前提出的問題只花費一次點擊;同一個問題在實作中提出,則會打斷使用者的工作流程,並讓執行停滯直到使用者恢復狀態。
在使用者確認達成共識之前,不對拷問的主題採取任何行動。
流程
- 自行查證所有事實(參見 事實 vs 決策。在面試開始前就了解程式碼。
- 繪製決策樹。 每個開放式決策都會分支成其相關的決策。
- 釐清票證和證據能解決的事項。 每個開放式決策都透過 決策簡報 的五個部分進行處理。票證清楚說明的內容直接記錄在文字中;證據只留下一個選項的缺口,則記錄其
Beleg:;其餘事項則對使用者保持開放(釐清或提問)。 - 在提出問題前,再次對照來源進行檢查(參見 提問前再次檢查)。一個來源已經回答的問題,會讓使用者做出一個他們從不需要做的決策。
- 一次提出一個「前緣」回合。 「前緣」是所有其前提條件已解決的開放式決策:你可以「現在」提出的問題,而不需要猜測尚未聽到的答案。依賴於本回合中其他仍開放的問題,則屬於「後續」回合。
- 每個問題都附上其簡報。 簡報以你的建議做結尾,使用者可以在看到事實的情況下,一次點擊做出決策,無需先閱讀程式碼或從頭設計。
- 每回合後重新計算前緣: 已解決的決策會推動前緣向外擴展,解除依賴於它們的問題的阻塞,並經常淘汰某些問題,或使其足夠清晰而能從證據中解決。再次檢查並提出下一回合。
- 當前緣為空時停止: 每個分支都已被造訪,沒有任何東西被默默假設。撰寫 決策記錄 並讓使用者確認。
釐清或提問
票證(或被拷問的計畫或規格)是指令,並且具有優先權:它清楚說明的內容將被嚴格按原樣實作。票證通常不精確、自相矛盾或遺漏事項,而這些就是需要決策的點。使用者需要做的決策越少越好,每一個不清楚的點都是他們的責任。決策簡報劃清了界線:在向任何人提問之前,為每個開放式決策撰寫簡報,並讓其結果將該點歸入三種情況之一。
1. 票證清楚回答了:按原樣實作。 該點已解決,並標記為 [Ticket <ID>],即使鄰近功能做法不同或你原本會選擇其他方式。無需向任何人提問。
2. 票證不精確、自相矛盾或遺漏了後果:由使用者決定。 當用語可以理解為兩種方式,或對「完成」的定義不明確時,提出簡報提問;當描述、評論、驗收標準和設計不一致時;當其對當前程式碼的描述與實際程式碼不符時;或當嚴格遵循它會破壞其未提及的內容時(其他功能依賴的行為、安全性、現有資料)。票證的含義由使用者決定,因此即使你傾向某個方向,這些問題也應交由使用者處理。
3. 票證未說明:當證據明確無誤時自行解決缺口,否則提問。 當恰好一個選項成立時,自行解決:
- 另一個來源(連結的文件、設計、專案文件)提供了答案,或程式碼以一種已確立的方式處理了這個確切情況 (
path:line), - 來源或程式碼中沒有任何內容指向其他方向,
- 沒有其他選項具有客觀評分更高的優勢,且
- 沒有持久後果(資料模型、遷移、權限預設值、其他模組或團隊消費的合約)。每當票證未說明時,這些事項都應交由使用者處理,無論你自己的推理多麼自信。
將已解決的缺口記錄為 Annahme,並在其 Beleg: 行記錄證據(決策記錄);使用者會在確認記錄時看到它,並可以在那裡推翻它。所有其他缺口都連同簡報一起交由使用者處理,你無法確定的事項也是如此:對某點是否明確的懷疑,本身就意味著它不明確。
提問前再次檢查
一個最終到達使用者面前的問題,是要求他們做出決定,因此在一個回合送出之前,帶著該回合的問題回到來源進行檢查。第一次閱讀是為了尋找需求;這次閱讀是為了尋找一個特定問題的答案,並且能發現一般閱讀會忽略的內容:評論串深處的回覆、設計稿中的第二個畫面、一個只能以一種方式運作的驗收標準。
- 重新完整閱讀票證,牢記本回合的問題:描述、驗收標準、每個評論和回覆(不僅是最新的),以及其中的圖片。
- 搜尋其他來源中每個問題的術語,使用英文和德文以及同義詞(它所涉及的實體、欄位、角色、畫面或狀態):父級、子級和連結的問題、附帶文件、先前的決策評論和問題文件、設計稿的每個畫面和狀態、流程圖、專案文件。
- 尋找從已固定事項中推導出的答案:另一個只能與一個選項配合使用的驗收標準、本次拷問中先前所做的決策、一個已經鎖定了票證未更改行為的測試或驗證。
然後根據釐清或提問 再次對每個問題進行分類:一個由票證清楚陳述的答案會解決它並按原樣實作;另一個來源中的答案可以解決一個缺口,並帶有其 Beleg:;再次檢查發現的矛盾仍然是一個問題,現在附帶了雙方引述。只有存活下來的問題才會進入該回合,其簡報的第 3 部分會說明再次檢查涵蓋了哪些內容,以便使用者看到這個問題是真實存在的。
決策簡報
按順序的五個部分,每個部分都來自你在本次會話中收集的證據:分支上的程式碼、當今的來源。
- Ist-Stand im Code:程式碼在決策落地處的現狀,附帶
path:line。對於尚未存在的東西,請說明並指出你在哪裡查找的。如果票證對當前行為的描述與程式碼所示不同,則兩者都說明:該差異通常是真正的問題所在。 - Geplante Änderung:更改、添加或刪除了什麼,如何更改以及為什麼更改(要求這樣做的驗收標準或來源)。將已固定的內容與選項要解決的分歧點分開。
- Ticket und Quellen:票證、其評論、連結的票證和文件、設計稿以及專案文件中關於此點的說明,首先是票證,用「…」逐字引用,並附上來源(票證和章節、評論日期、文件標題、Figma 節點、
CLAUDE.md:<line>)。引用關鍵句子,而不是段落。當來源不一致時,引用雙方。當沒有來源涉及該點時,請說明並列出再次檢查 涵蓋了哪些內容:這種沉默正是它成為一個問題的原因。 - Optionen:二到四個,與問題提供的選項相同,每個選項都要說明其具體含義、優點、缺點及其影響:觸及的文件和模組、現有資料、其他角色或消費者、測試、工作量,以及未來是否難以撤銷。框架已提供的選項應作為其中之一。
- Empfehlung:你會選擇的選項及其原因,基於第 1 到第 3 部分進行論證。
將每個部分控制在 1 到 3 行:簡報是單一決策的上下文,而不是一份報告。撰寫簡報也是 釐清或提問 的測試:當第 3 部分找到回答該點的引述,或第 1 部分顯示程式碼已經決定了,那麼該點就解決了並從回合中移除。
**F1 發票刪除行為**
1. **Ist-Stand im Code:** `InvoiceService.delete()` 使用 `deleteOne` 進行硬刪除 (`projects/api/src/server/modules/invoice/invoice.service.ts:88`),但目前未被調用。`Payment.invoice` 引用發票 (`payment.model.ts:31`)。專案中有兩種模式:`Order` 透過 `deletedAt` 進行軟刪除 (`order.service.ts:54`),`Customer` 進行硬刪除 (`customer.service.ts:71`)。
2. **Geplante Änderung:** 管理員在發票清單中獲得一個刪除操作 (AK 3):新的端點 `DELETE /invoices/:id`,`InvoiceList.vue` 中的按鈕。誰可以刪除已確定;尚未確定的是記錄是否會被移除。
3. **Ticket und Quellen:**
- 「管理員應能刪除有問題的發票。」 (DEV-1234, 描述)
- 「僅管理員可以刪除。」 (DEV-1234, 2026-10-02 的評論)
- 是否保留已刪除的發票,來源中未說明(已檢查:描述、3 則評論、Figma 節點 12:345、`docs/`)。
4. **Optionen:**
- **A 軟刪除** 透過 `deletedAt`,類似於 `Order`。優點:歷史記錄和 `Payment` 引用保持有效。缺點:每個查詢都需要過濾器。影響:模型、服務和三個清單查詢會更改;無需遷移,該欄位是可選的。
- **B 硬刪除**,類似於 `Customer`。優點:沒有額外欄位,沒有過濾器。缺點:發票將永久消失。影響:`Payment` 引用會失效,需要單獨處理。
5. **Empfehlung:** A,因為付款會引用發票,否則已刪除的發票將無法追溯。
票證未說明,專案中兩者皆有先例,且選擇會影響資料模型,因此此缺口交由使用者決定。如果描述說「已刪除的發票將保持儲存並僅被隱藏」,則票證會解決它:軟刪除,按原樣實作,標記為 [Ticket DEV-1234],無需提問。如果評論提出了相反的要求,則矛盾會交由使用者決定,在第 3 部分下列出雙方引述。
回合格式
使用 AskUserQuestion:每個回合一個調用,最多四個問題,每個問題首先列出其推薦選項並加上後綴 (Recommended),每個選項的描述攜帶其影響,一行。自動提供的「其他」條目攜帶自由文字答案。當前緣包含超過四個問題時,先詢問最上游的問題;它們的答案通常會淘汰掉一些後續問題。
將本回合的簡報放在與調用相同的訊息中,就在其前面,編號以匹配 (F1, F2, …),以便使用者在看到問題的同時閱讀它們。
一個僅收集使用者獨有的事實(他們觀察到的內容、他們使用的帳戶)或工作流程選擇(執行審查、如何合併)的問題不需要簡報:其選項標籤已經包含了重要訊息。
如果沒有 AskUserQuestion(純聊天、非互動式執行器),則以相同格式印出該回合並等待答案。
事實 vs 決策
查找事實是你的工作。詢問使用者你可以自行查閱的資訊,會讓面試感覺像審訊,而且你從記憶中得到的答案比存儲庫中的答案更不可靠。
| 自行查閱 | 決定(從證據中解決,或提問) |
|---|---|
服務已允許哪些角色(@Restricted, @Roles, securityCheck) | 應允許哪些角色 |
實體、DTO 或生成類型的當前形狀 (types.gen.ts, sdk.gen.ts) | 是否添加、重新命名或替換某個欄位 |
| 端點、頁面或 composable 是否已存在 | 是擴展它還是從頭構建 |
| 票證、其評論和連結的 Figma 節點實際上說了什麼 | 票證在模糊之處的含義 |
| 鄰近功能如何解決相同問題 | 此功能是否應遵循該先例 |
| 哪些測試目前涵蓋受影響的路徑 | 新測試應位於何處 |
| 所描述的缺陷是否仍然可重現 | 正確的行為是什麼 |
在提出問題之前,先使用 Read、Grep、Glob、Linear MCP 和 Figma MCP。如果查找範圍很廣,則為其分派一個子代理,不要阻塞:正在進行的查找是一個未解決的前提條件,因此只有依賴於它的下游問題會等待,而其餘的前緣則立即提出。
問題品質
- 探測邊緣情況,而不是快樂路徑。 空值、null、極大值、併發、未授權、離線。快樂路徑是每個人都已達成共識的部分。
- 揭示會比票證活得更久的後果: 未來難以遷移的資料模型、成為先例的權限預設值、其他團隊將消費的合約。
- 一個新的 nest-server 專案應在此回合中決定其資料庫。 除非票證或根目錄的
CLAUDE.md中的## Database章節指定了資料庫和 SQL 引擎,否則它們都是一個問題(它們會比任何票證都活得久),其簡報應遵循generating-nest-servers→ reference/choosing-a-database.md 中的程序:第 3 部分來自來源的信號,第 5 部分的決定步驟,以及對於 MySQL / MariaDB 來說,專案放棄了什麼。一個擁有自己資料的新模組應保留在專案的資料庫中,無需提問;只有當來源指向另一個資料庫時,它才成為一個問題(混合模式)。確認的答案應進入決策記錄,並作為## Database放入根目錄的CLAUDE.md。 - 涵蓋實作過程中國內會停止的事項。 在結束之前,在你的腦中走一次計畫中的工作(模型、端點、權限、UI 狀態、測試),並將每個你會停下來思考的點,都透過 釐清或提問 進行處理。這使得之後的執行過程可以暢通無阻,不再需要提問。
當使用者無法聯繫時
一個不明確的決策屬於使用者,因此一個阻塞性的決策永遠不應代為回答
…