Day 24|新同事找到八個問題,只有六個能開單
前言
Hunter 說是 bug、Verifier 說重現得了,還要再看一次開單需要的資料有沒有齊。今天請一位同事守在開單前,照六項清單逐一檢查,全部通過才放行。這就是 Issue Quality Gate。

當時的紀錄是 6 筆 pass、2 筆 hold。不過,這兩筆 hold 的分法有問題,下面會一起說明。
開始之前
前面五天立了一堆規矩:要有證據、要命中 oracle、要算分、要查誤報名單、要去重、要獨立重現。每一條當時都同意,問題是它們現在都只是好習慣。
好習慣會被跳過。趕著收班的時候、只差一條的時候、覺得「這個一看就是 bug」的時候,人會自己放行。放行一次沒事,第三次之後前面五天等於白做。
閘門的作用是把這六條從「最好有」變成「沒有就開不了」。它自己不判斷是非,只檢查該有的東西在不在,然後決定放行、擋下、或送人判。
還有一件事同樣重要:不放行不等於丟掉。 被擋的每一筆都要寫清楚卡在哪一條、下一步找誰,讓「要人來判」變成看得到的佇列。沒有這一欄,擋下就等於默默消失,那比放行還糟。
動手試試
把 Day 22 那輪的 8 個候選,連同判斷依據、信心分數、證據,以及昨天的盲驗結果,一起交給 issue-quality-gate。其中 6 筆有盲驗結果,另外 2 筆尚未送驗,不能當成已經驗過。
請它逐筆檢查六項條件,每一項都記下 pass 或 fail。不通過的要交代卡在哪裡、接下來要補什麼,當時的輸出寫在 output/gate.yaml。
跑完之後
放行的長這樣:
- candidate: "product-detail|quantity-field-not-reset-across-products|route-product-to-product"
checks: { reproducible: pass, has_evidence: pass, oracle_passed: pass,
confidence_ok: pass, not_false_pos: pass, not_duplicate: pass }
result: pass
detail: "verifier confirmed(V-C-05,附 verifier-run/C-05-qty-leak.png);confidence 0.80 ≥ 0.7;issues-index 為空無重複"沒有放行的兩筆才是重點。C-07 是 UI 拼字,當時留下的紀錄長這樣:
checks: { reproducible: fail, has_evidence: pass, oracle_passed: fail,
confidence_ok: fail, not_false_pos: pass, not_duplicate: pass }
result: hold
blocked_on: >
confidence 0.45 < min_to_file 0.7,因此未送盲驗(reproducible 無 verdict 可依);
oracle 只能靠「使用者期待」,無拼字/文案規格可比對。下一步:人決定要不要開一張文案單,
或提供文案規格讓 test-oracle 有判準。六條裡它只過了三條,當然不能放行。不過,這裡的 hold 也分得不夠準:它沒有送盲驗,連拼字要對照哪份規格都還沒釐清。依現在的規則,應先記為 block,把缺的判準和驗證補齊,再重新過閘門。
blocked_on 那段仍然有用,它記著卡在哪裡、接下來該做什麼。C-08 也一樣:當時缺少跨解析度的佐證、沒有送盲驗,卻也記成 hold。兩筆舊紀錄先保留,讓我們看得到當時怎麼判;它們都不能拿來示範「缺盲驗也可以只是分數不足」。
還有一筆值得看,C-02 差一點被誤壓:
detail: "known-FP 第 1 條(/users/me 401)scope=anonymous-only,本候選在登入後且以 API 200 為判準,不在該條範圍"誤報名單有一條講 /users/me 回 401,而這個候選也牽涉到 /users/me。scope 那欄擋下了一次錯誤的壓制。閘門把判斷過程寫進 detail,不是只寫一個 pass。
所以,當時記下的是 6 筆 pass、2 筆 hold、0 筆 block;若依現在的規則重新檢查,後兩筆應先 block。這裡是在檢討原判定,沒有補做盲驗,也不把舊檔改成好看的版本。
把決定寫進 gate.yaml
- candidate: "<fingerprint>"
checks: { reproducible: pass, has_evidence: pass, oracle_passed: pass,
confidence_ok: pass, not_false_pos: pass, not_duplicate: pass }
result: pass | hold | block
blocked_on: "<卡在哪、下一步找誰>" # hold 與 block 必填checks 六個欄位一個都不能少,即使全 pass 也要逐條寫出來。沒有逐條紀錄,你分不出它是全過,還是根本沒檢查。
舊實驗把結果放在 output/gate.yaml,現在改放在每輪的 output/sessions/<date>_<slug>/gate.yaml,免得不同輪次混在一起。閘門結果要回填 Day 20 的校準表,之後收到人的判斷再補上;不能把「暫時不能開單」直接填成「不是 bug」。
背後怎麼做
| # | 條件 | 判準 | 來源 |
|---|---|---|---|
| 1 | reproducible |
Verifier 的 verdict == confirmed |
Day 23 |
| 2 | has_evidence |
manifest、重現步驟、佐證檔齊全且可攜 | Day 11 |
| 3 | oracle_passed |
verdict == bug,非 needs-spec 或 inconclusive |
Day 18 |
| 4 | confidence_ok |
分數 ≥ confidence.min_to_file |
Day 20 |
| 5 | not_false_pos |
未命中誤報名單 | Day 21 |
| 6 | not_duplicate |
同專案的指紋不在索引;已併入舊單仍不能另開新單 | Day 21 |
六條逐條記 pass 或 fail,順序固定,不准挑著跑。這是 AND,不是加權平均,任何一條 fail 就不放行。
同時有好幾項沒過時,先看有沒有必須擋下的條件,再決定是不是只需要等人判斷:
| 結果 | 什麼時候用 | 接下來做什麼 |
|---|---|---|
pass |
六項全部通過 | 交給開單流程,執行前仍要確認授權 |
block |
缺規格、沒有通過獨立重現、證據不全、命中誤報,或已經有單 | 記下原因;可補的退回補齊,重複的連到舊單 |
hold |
沒有上述 block 條件,但信心分數不足,或仍有需要人工複核的疑慮 | 列出要請人判斷的事,暫不開單 |
「還沒送驗」和「送驗後沒重現」原因不同,必須分別寫清楚;但兩者都還沒取得開單資格。也不能因為另一項只是分數低,就把整筆從 block 改成 hold。
閘門本身不開單、不修、不改候選內容。它只做決定、留紀錄。
有人要求例外處理時,override_gate 也要經過授權,留下誰、何時、為什麼。這不代表一句「先放行」就能把缺少的盲驗或證據變出來,也不能把重複問題變成新單。該補的仍要補,補完再跑六項檢查。
今天學到什麼
今天把開單前的條件列成六項清單,全部通過才放行。有項目沒過,就把缺什麼、接下來找誰寫清楚,讓問題有地方繼續處理。
這次最值得記住的是兩筆舊 hold 紀錄。它們沒有送盲驗,卻被當成只是分數不足;依現在的規則,應先 block。自己訂的規則也要拿實例回頭檢查,否則表格寫得再完整,執行時還是會分錯。
明天輪到新同事動手修。拿到通過閘門、範圍也清楚的問題,先重現、再用測試抓住它,然後才改產品碼。修完可以在授權範圍內開 PR,合併仍留給人。
參考資料
- Michael E. Fagan(1976)《Design and Code Inspections to Reduce Errors in Program Development》,IBM Systems Journal 15(3) - 正式檢查要有明確的 entry 與 exit criteria,軟體業「閘門」概念的源頭
- Watts S. Humphrey(1989)《Managing the Software Process》 - 把 entry/exit criteria 制度化,說明為什麼判準要寫死而不是靠當下判斷
- James Reason(1990)《Human Error》 - 瑞士起司模型:單一防線一定有洞,六條 AND 是多層防護而不是一道大門
- Marilyn Strathern(1997)《'Improving Ratings': Audit in the British University System》,European Review 5(3) - Goodhart 定律的常見表述來源,說明為什麼放行率本身不能當成績效指標
- 本專案
skills/agents/issue-quality-gate/SKILL.md- 六條判準與分流 - 本專案
output/gate.yaml- 實跑輸出 - 本專案
config/governance.yaml-override_gate的留痕規則