Day 24 最後留了一個很明確的方向:
Day 25 我想給它補 focused unit coverage:每個條件單獨壞一次,看它自己能不能讓整個判斷不通過。
這個「它」,就是決定 產生 Project Specification 能不能按的函式:
isSpecificationSetupComplete()
所以今天不再補 browser test,往下一層補它的 focused unit coverage。
ClarifyBuild 是我這系列正在做的小工具:它會把一個模糊的產品 idea,透過幾站釐清流程,整理成 Project Specification,最後再組成 AI Coding Prompt。
今天新增的是一條 unit test,裡面用迴圈跑 17 個 case。
結果是:npm test 31 條全過,30 條舊的,加今天這 1 條。
產品程式沒有改。
今天做的事,是把既有完成條件釘得更清楚。
Day 23 和 Day 24 其實已經守住了使用者看得到的結果。
| 起點 | 動作 | 守住的結果 | |
|---|---|---|---|
| Day 23 | 四個 setup 區塊都沒做 | 一塊一塊儲存 | 四塊都完成前,產生 Project Specification 保持 disabled |
| Day 24 | 四塊都完成 | 把主要使用者改成「研究生與助教」 | 主要流程、技術設定變成「需要重新檢查」,功能規格掉到 0 / 1,功能規格標題顯示「需要重新檢查」,按鈕重新鎖住 |
這兩條 browser test 的起點 draft 是預先放進 localStorage 的。
但起點之後,每一步都是點按鈕、填欄位、讀畫面。
所以它們守的是 main.js、scopeEngine.js、render.js 接起來之後的結果。
這很重要。
因為使用者不會看到某個函式回傳 true 或 false。
使用者只會看到:
我現在可不可以產生 Project Specification?
Day 23 和 Day 24 的答案是:
不完整不能產生,資料過期也不能產生。
Browser test 很適合守「最後有沒有被擋住」。
但它不適合回答:
到底是哪個條件在擋?
Day 24 的 browser test 裡,需求一改,主要流程、功能規格、技術設定會一起變成需要重新檢查。
按鈕 disabled。
但那條測試只能說明「至少有一個條件讓它不能產生」。
例如這幾個問題,browser test 不適合一個一個回答:
needsReview,會不會擋?basedOnRevision 過期,會不會擋?needsReview,會不會擋?這些條件如果只靠一條 browser flow,很難知道是哪個條件真的有被獨立守住。
這就是 Day 25 要補的空洞。
不是再走一次畫面流程。
而是直接問核心判斷:
isSpecificationSetupComplete(draft)
只要 draft 裡某個必要條件壞掉,這個函式就必須回傳 false。
今天新增的測試叫:
specification setup completeness fails when any required setup condition is missing
它的測法很單純。
先建立一份完整的 Specification Setup draft。
這份 draft 有:
ai_delegated 的 tech stack,也就是交給 Coding Agent 選技術needsReview 都是 false測試先確認它真的完整:
isSpecificationSetupComplete(completeSpecificationDraft()) === true
接著每個 case 都重新建立一份乾淨的完整 draft。
然後只破壞一個條件。
最後確認:
isSpecificationSetupComplete(draft) === false
程式大概長這樣:
const cases = [
{
name: "flow needsReview",
breakDraft(draft) {
draft.targetProjectUserFlow.needsReview = true;
}
},
{
name: "tech needsReview",
breakDraft(draft) {
draft.buildConfig.techStack.needsReview = true;
}
}
// ...共 17 個
];
cases.forEach(({ name, breakDraft }) => {
const draft = completeSpecificationDraft();
breakDraft(draft);
assert.equal(isSpecificationSetupComplete(draft), false, name);
});
這樣做的好處是:測試訊息會指向第一個壞掉的 case 名稱。
它仍然是一條 test,不是 17 條 test。
但當某個條件被意外放寬時,失敗訊息至少不會只剩「整個流程怪怪的」。
它會告訴我,是哪個 focused case 沒守住。
isSpecificationSetupComplete() 現在要求四個 setup section 都完整,而且不能過期。
Day 25 把這些條件拆成 17 個 focused cases。
| 區塊 | 單獨壞掉的條件 |
|---|---|
| 前提 | 一個 Must Have 都沒有 |
| 主要流程 | 沒有步驟;needsReview 為 true |
| 功能規格 | 缺 actor;缺 userValue;第二個 Must Have 沒有 spec;沒有 behaviors;behavior 缺 trigger、systemBehavior 或 result;第二個 behavior 缺 result;basedOnRevision 過期;needsReview 為 true |
| 專案限制 | 還沒 reviewed |
| 技術設定 | mode 未設定;指定技術但沒有 items;needsReview 為 true |
這裡有三個小細節。
第一,缺 actor、缺 userValue、缺 trigger、缺 systemBehavior、缺 result 都用空白字串測。
這是為了確認 .trim() 真的有被算進去。
第二,今天也補了「第二個 Must Have 沒有 spec」和「第二個 behavior 缺 result」。
因為程式不是只看第一個 Must Have,也不是只看第一個 behavior。
它用的是:
mustHave.every(...)
spec.behaviors.every(...)
所以測試也要守住「每一個 Must Have」和「每一個 behavior」。
第三,basedOnRevision 那一項則是在守規格是否仍對得上目前功能定義。
目前功能改名時,feature 的 revision 會加一。
如果 feature spec 記的 basedOnRevision 還停在舊 revision,就代表它是依舊定義寫的,不應該繼續算完成。
到這裡,兩層各自守的東西就清楚了:Day 23、Day 24 的 browser test 守「結果」,也就是按鈕有沒有鎖住;今天的 unit test 守「條件」,也就是 17 個完成條件,每一個都能單獨讓判斷不通過。
Day 24 文章裡有記一件事:
目前程式的 needs-review rules,和 Day 11 的 Project_Spec_Schema_v0.1.md〈十四、失效與 Needs Review 規則〉有差異。
例如現在程式比較保守:Target User、Problem、Goal 只要有變,已填過的 tech setting 也會被標成需要重新檢查。
但那張規則表裡,Target User、Problem、Goal 對 Tech Stack 是「不影響」。
Platform 這一格兩邊一致:規則表和目前程式都認為 Platform 變更會影響 Tech Stack。
反過來,也有一些規則表說應該影響、但目前程式還沒標記的情況。
這件事值得處理。
但不該混在今天。
因為 Day 25 的目標是:
先補
isSpecificationSetupComplete()的現有行為 coverage。
今天這條測試直接把 needsReview 設成 true。
它不經過「哪些變更會觸發 needsReview」的規則。
所以之後不管要不要對齊 Schema,只要完成條件本身不變,今天這些 case 都不用動。
如果之後是把程式改成對齊 Schema,至少 Day 24 browser test 裡期待「技術設定:需要重新檢查」的那一條斷言要跟著改。
今天先跑 focused test:
node --test test/mvpFlow.test.js
tests 2
pass 2
fail 0
再跑完整測試:
npm test
tests 31
pass 31
fail 0
Day 24 是 30 條,今天多了這 1 條。
跟 Day 24 一樣,我的執行環境第一次跑完整測試時,browser tests 會因為 local server 不能 bind 到 127.0.0.1 失敗。
放寬本機執行權限後,同一組測試通過。
Day 25 把 isSpecificationSetupComplete() 的條件釘住了。
下一步我想補另一道防線:
generateSpecFromState()
現在 UI 按鈕會依照 isSpecificationSetupComplete() 決定能不能按。
但真正產生 spec 的 state function 裡,也有第二道 guard:
if (!isSpecificationSetupComplete(state.draft)) {
return {
ok: false,
message: "請先完成主要流程、Must Have 功能規格、專案限制與技術設定。"
};
}
如果把這段 guard 拿掉,目前 31 條測試仍然會全過。
這代表 Day 26 有一個很清楚的小洞可以補。
UI 是第一道門。
state function 是第二道門。
即使有一天畫面按鈕狀態不小心壞了,底層也不該產生一份不完整的 Project Specification。
所以 Day 26 可以補一條 focused unit test:
setup 不完整時,
generateSpecFromState()應該拒絕產生 spec,stage 不應前進,generatedSpec 不應被填入。
這會讓 Day 23 的 browser test 和 Day 26 的 state guard test 形成上下層防線。
Day 25 完成。
明天見。