jiantw83 and Claude Opus 5
42f5b86299
merge(pr-留言): 把議題支援套到 pr-threads 的共用結構上
...
#48 把留言讀取抽成 pr-threads 給 pr-watch 與 pr-comments 共用,本分支則在修「純議題讀不了」
與「已處理的判定有兩份」。兩邊動到同一塊,但要的其實是同一件事——#48 的檔頭就寫著
「規則寫兩份遲早會各自演化,一邊認自己打的 +1、另一邊認任何人的」,而那正是本分支在
lib 與 pr-comments 之間發現的那個 bug。
合併的方向是保留 pr-threads 的結構,把修正套進去:
- readGeneral 改名 readGeneralComments 並導出,pr-comments 判斷出是純議題時只叫它。
先讀 /issues/{index} 再決定要不要翻 review——每個 PR 都是議題,反過來不成立。
- pr-threads 自己那份 markedByMe 拿掉,改用 lib 的 mergedByMe。抽取契約數未整併則數
用的是同一條規則,現在三處共用一份,#48 擔心的分歧不會再發生。
- pr-comments 的試跑改印 commonRequests:它收得下兩種輸入,而試跑階段還沒讀過議題、
不知道是哪一種。與其假設是 PR 而列出五個(對純議題有三個根本不會發),不如只列一定
會發的,其餘交給 note。pr-watch 的輸入一定是 PR,繼續用 plannedRequests。
lib.js 與 sdlc-feat.md 兩邊改的是不同區域,三方合併無衝突。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 09:16:34 +00:00
jiantw83
767ca69c12
test(流程正本): 新增 sdlc-sync,並讓另外兩份正本整併完自動接回
...
挑出哪些留言真的是決策、寫回去之前讓使用者點頭、略過的保持未標記——三件都只有正本做得到。
analyze 與 feat 原本寫「建議先執行 /sdlc-sync,再回來」,那等於要使用者重打指令。改成直接走
sync 的流程、做完自動接回,並在接回前重新抽取一次——接著用舊的那一份做事,這一整段就白做了。
略過的留言會讓未處理留言數停在大於 0,於是 analyze 與 feat 每次都會再停一次。這是驗收標準
本身的兩條放在一起的結果,正本把它講明白,讓使用者分得出「沒整併乾淨」與「我選了略過」。
2026-09-17 09:08:09 +00:00
jiantw83
82bda1109b
test(comments-merge): 把留言裡的決策整併回議題描述並標記
...
判斷「哪幾則有決策、該併進哪一段」是讀得懂內容的人的事;這一支只負責把結果安全地寫回去。
先寫描述再標記,描述寫失敗就不標記:反過來的話,那幾則已經被標成處理過,再也不會被提出來。
--merged 只收真的併進去的那幾則,略過的保持未標記。標記之前先核對那幾則確實在這顆議題上——
打錯 id 的 reaction 會落在別顆議題的留言上,而那幾乎不會有人發現。
2026-09-17 09:08:08 +00:00
jiantw83
97e556a72e
test(pr-comments): 議題也讀得了,不再先打 PR 端點
...
每個 PR 都是議題,議題不一定是 PR——原本的註解把這句話講反了,程式也照著反過來寫:
先打 /pulls/{index},對純議題回 404,於是 /sdlc-sync 在讀到第一則留言之前就斷了。
改成先讀 /issues/{index}(兩種都有),看它有沒有 pull_request 才決定要不要去翻 review。
輸出加上「類型」讓下游知道拿到的是議題還是 PR。
2026-09-17 09:08:06 +00:00
jiantw83
c62bff571e
test(pr-create): 讀檔與列留言收進 lib,並讓已整併只認自己打的 +1
...
「讀一個 --xxx-file 或直接失敗」原本在四支腳本各寫一份,錯誤碼還有三種拼法
(BODY_FILE_NOT_FOUND/BODY_FILE_MISSING/CONTENT_FILE_NOT_FOUND)。同一種情況要有同一個
碼,呼叫端才分辨得出是哪一步壞了。留言分頁的那段咒語也是第三份,一併收成 listIssueComments。
countUnmergedComments 原本接受任何人的 +1,而 pr-comments 只認自己的——兩端對「已整併」的
定義不一致。後果是隊友對決策留言按個讚,未處理留言數就掉到 0,analyze 與 feat 再也不提示,
那則決策永遠不會被收進描述。統一成只認自己打的:別人按讚是「我同意」,不是「已經收進去了」。
2026-09-17 09:08:04 +00:00
jiantw83
a1bbd78cb4
test(sdlc-fix-assets): 新增 sdlc-fix 流程正本
...
分類必改/建議、不確定時停下來問、最後那則摘要——這三件腳本擋不住,只有正本做得到。
標不了的那幾則要在摘要裡單獨點出來,否則 reviewer 掃 reaction 與 resolve 時會以為它們
被跳過了。留言指向的程式碼已被改掉時不要硬試,列進「無法處理」讓使用者自己回。
2026-09-17 08:46:35 +00:00
jiantw83
8d9bcd65e3
test(pr-reply): 回覆一則 PR 留言並標記已處理
...
「回在原本那一串底下」對三類是三件不同的事。行內留言沒有「回覆某一則」的端點,
把新留言指向同一個檔案與同一行,Gitea 才會把它排在原留言底下——位置是串的識別。位置不由
呼叫端給而是拿 id 查出來:手抄行號是這一段最容易錯的地方。回覆還帶上原留言的 commit_id,
否則 PR 之後又推了新 commit 時,同一個行號指的是別的程式碼。
標記排在回覆之後,回覆沒成功就不標記:沒回就標記等於謊稱處理過。
輸出三類同形:用不到的欄位填 null 而不是讓它消失,下游不必為了少一欄多寫一種分支。
2026-09-17 08:46:34 +00:00
jiantw83
de1fd08efa
test(pr-comments): 讀 PR 上的三類留言
...
一般留言、review 總評、行內留言分散在三個端點,漏掉任何一類就會有意見沒被處理——
而那正是 /sdlc-fix 存在的理由。只讀不寫,必改/建議的分類由讀到內容的人判斷。
三類的「已處理」機制不同:一般留言與總評看自己打的 +1,行內留言看有沒有被 resolve。
總評的 reaction 掛在它在 issue comment 表裡那一份的 id 上,由 timeline 對應得出來;
拿 review 自己的 id 去打會 404,兩個 id 不同命名空間。
reaction 要是自己打的才算已處理:reviewer 對留言按讚是「我同意」,不是「這則處理過了」,
當成已處理會讓那一則被靜靜跳過。
行內留言的位置分兩側:新檔那側在 position,被刪掉的那行在 original_position,Gitea 只填
其中一個。輸出把兩者收斂成「行」與「側」,回覆時才知道該送哪個欄位。也帶出 commit,
讓回覆落在原留言的那個 commit 上。
三種清單都逐頁讀完:這個站台的預設頁大小是 30,沒分頁的話第 31 個 review 之後整批消失。
2026-09-17 08:46:33 +00:00
jiantw83 and Claude Opus 5
0154cf59d4
fix(claim): 被碼錶擋下時明說停錶不會動到既有的工作樹
...
議題 #38 的使用者故事第 30 條:使用者常以為停錶等於放棄那顆工作包,於是寧可
不停——工時就記到別顆議題去了。碼錶只管時間、工作樹只管檔案,兩者互不相干,
這件事要在擋下來的當下就講,不能指望使用者自己推論。
領取與起錶會撞到同一個擋路理由,訊息收進 lib 只寫一份。順手收掉 review 指出的
三處:planWorktree 沒用到的 repo 參數、與 path.resolve 同名而誤導的區域函式、
以及只有 lib 自己用得到卻對外 export 的兩支路徑函式。
回滾補上最後一道:git 清不掉時把目錄本身也刪掉。那條路徑在這次執行之前不存在
(不存在正是建立的前提),裡面不可能有使用者的東西,而留著它下一次重跑會直接
撞上 WORKTREE_PATH_TAKEN——一次失敗的建立不該讓人從此開不了工。
議題 #40
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 16:29:12 +08:00
jiantw83 and Claude Opus 5
4583a5f210
docs(sdlc-feat): 第一段改成領取、備妥工作樹、最後起錶
...
正本跟著實作走:分支那一步改成工作樹,新增起錶那一步排在它後面,
並把三處「不要自己決定」寫明——來源分支不存在時不自己換一支、路徑被佔住時
不自己刪、工作樹建不起來時不退回原地切分支。工作區不乾淨的處置整段拿掉:
工作樹本來就是為了讓未提交的變更不再擋路。
AGENTS.md 補上兩條邊界。git worktree add 一定會在目標 repo 的 .git/worktrees/
底下寫中繼資料,這是 git 的機制,無法避免——「不改目標專案」指的是專案的內容檔,
把這件事明說,免得下一個人以為實作違規。
議題 #40
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 16:29:12 +08:00
jiantw83 and Claude Opus 5
74b4ca130e
feat(timer): 碼錶移出領取,等工作樹建成之後才起
...
原本的順序是「放行 → 設 assignee 與標籤 → 起錶 → 處理分支」,而工作樹建立
失敗會中止整個領取——錶已經起了才失敗,使用者會被計一段什麼都沒做的時間,
而工時要準正是工時報表的立足點。
claim 只留領取鎖的兩件事(assignee 與標籤),起錶交給新的 timer.js,由流程
正本排在 branch-prep 之後。timer 已經跑在這顆議題上時什麼都不做:中斷後重跑
是它最常見的處境,重新起錶會把已經累積的時間切成兩段;跑在別顆上則照舊擋下,
不代勞停錶。
三支腳本讀碼錶的那段各留一份,趁這次收進 lib。
議題 #40
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 16:27:43 +08:00
jiantw83 and Claude Opus 5
5b740c236b
feat(branch-prep): 一律在獨立的工作樹上開工,不在原地切換分支
...
同一份 clone 上同時持有多顆工作包時,原地切分支有三種損耗,一種比一種難查:
未提交的變更擋路、建置產物跨分支混淆,以及 agent 讀到不屬於它那顆工作包的
程式碼——agent 是非同步的,它可能在分支已經被切走之後才去讀檔,而且不會察覺,
產出看起來完全合理,只是接錯了上下文。前兩種人會當場發現,第三種不會,
所以工作樹一律建立,不是「有衝突才用」。
建不起來就中止,不退回原地切分支:靜默降級會讓使用者以為自己在隔離環境裡,
其實在原地改。
分支與工作樹合併為一個原子動作(fetch 後一次 worktree add),並補上回滾——
git 在 worktree add 失敗時仍會把分支留下來,那是最難查的半成品:下一次重跑
會走到「目標分支已存在」那條路,起點從此不再是遠端的來源分支。
起點一律取自 origin/{來源分支},遠端沒有就中止,不退回本機同名分支;
本機分支可能落後好幾天,而這件事從輸出上完全看不出來。原「來源分支在遠端
已存在時 pull 而非重建」那條,用更強的方式達成同一個目的:根本不碰本機分支,
就沒有覆蓋他人進度的可能。
不設 upstream:此刻遠端還沒有這個新分支,--track 會把 upstream 指到來源分支,
之後 git pull 會把來源分支的提交拉進來。留給第一次 push -u 自然建立。
路徑由 owner/repo/分支名 正規化後取雜湊推導(lib 的 worktreePath),不查表、
不寫狀態檔,換機器算出來一樣。取雜湊而不是把斜線攤平成 -,是因為攤平會讓
feat/a-b/main 與 feat/a/b/main 撞成同一個目錄,而現行的分支命名規則恰好讓
這種形狀有機會出現。
議題 #40
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 16:27:43 +08:00
jiantw83
013947630f
test(sdlc-feat-assets): 第三段補上兩個 repo 的區別與重跑的行為
...
說明 --repo 與 --issue-repo 的差別、--base 要明講不讓腳本猜、重跑不會開出第二顆 PR,
以及提交中途失敗時不要自己回捲歷史。
2026-09-17 08:23:28 +00:00
jiantw83
da5b670f29
test(script-contract): 讓 --key=value 的值可以本身以 -- 開頭
...
commit 訊息與 PR 描述裡出現 --flag 是常態,而原本的檢查對兩種寫法一視同仁:值只要以
-- 開頭就報「需要一個值」。空格分隔的寫法確實分不出「值」與「打錯的 flag」,但等號寫法
沒有這個歧義,不該一起擋掉。
這是實際撞到的:拿 commit-split 提交它自己時,--body 的內容第一行就是「--repo 與
--issue-repo 的差別」,整個 commit 因此做不出來。空格寫法的錯誤訊息現在會指路到等號寫法。
2026-09-17 08:23:27 +00:00
jiantw83
fbc80f286f
test(pr-create): 錶停在議題的 repo,並讓重跑不會開出第二顆 PR
...
claim 在工作包議題上起錶,而 PR 開在目標專案上——議題在需求的 repo,程式碼在 repos 列的
那幾個,兩者常常不是同一個。先前用同一個 --repo 同時指 PR 與停錶,停到的會是別人的議題
(或 404 而在 PR 已建立之後才拋錯),而自己的錶還在跑。新增 --issue-repo,預設與 --repo 相同。
--index 與 --base 改為必填:停錶是這一步的一部分,忘了給會讓工時算不準;而目標專案的開發
分支可能叫 master、main 或 develop,猜錯會開到不存在的 base。
重跑先查同一個 head 有沒有開著的 PR,有就回傳它並把 created 設為 false,然後照樣停錶——
那一步可能正是上次中斷的地方。先前重跑會撞上 Gitea 的 422,而那個錯誤看不出 PR 其實已經開好。
停錶的 500 改為只在訊息確實提到 stopwatch 時才視為「本來就沒在跑」,免得把真的伺服器錯誤
吞掉;未經證實的 409 那一支拿掉。測試結果的空話檢查改成整段每一行都是空話才擋,段落也改用
行首標題切,描述裡引用到「## 測試結果」這幾個字不會再讓檢查看錯地方。
2026-09-17 08:23:25 +00:00
jiantw83
8a6145ea14
test(commit-split): 改名時別漏掉舊檔的刪除,失敗時說出做到哪裡
...
git diff --name-only 預設偵測改名,只印出目的地那一個路徑。來源的刪除因此被漏掉——
留在 index 裡沒被提交,而腳本還回報成功,要等下一次跑才會發現工作區不乾淨。加 --no-renames。
某一批提交失敗時,錯誤現在會列出前面已經建立的那幾顆 commit。不回捲它們:那會動到使用者的
歷史,而那幾顆本身是好的;但一定要說出做到哪裡,否則重跑前得自己去翻 git log。
類型對照表補上目標專案常見的測試擺法:tests/、spec/、__tests__/,以及放在被測檔案旁邊的
user.test.js。先前只認 test/,目標專案的測試會被併進 feat 那一批。
2026-09-17 08:23:24 +00:00
jiantw83
513dad7f6a
test(sdlc-feat-assets): 加入第三段「提交與開立 PR」
...
PR 描述的八個段落與順序寫在正本裡,由 pr-create 擋;正本負責的是腳本擋不住的事:
測試結果要貼實際輸出而不是改寫成一句話、被擋下來時補真的內容而不是為了通過而拼湊、
以及跨兩個功能時用 --files 分兩次跑。
先開 PR 再停錶的理由也寫進去了:工時要記在真的有做事的那段時間上。
2026-09-17 08:23:23 +00:00
jiantw83
958b1f85e8
test(pr-create): 開立 PR 並停錶
...
標題等同分支名:reviewer 在列表上看到的就是分支,兩者對不上會找錯 PR。
描述的段落固定且順序固定,缺一段或順序不對就擋下,不自動補——補出來的段落是編的,
而 reviewer 會把它當成真的。
「測試結果」另外驗一次它不是空話。那一段是 reviewer 唯一能判斷「這東西真的跑過嗎」的
依據,寫「已測試通過」等於沒寫。判斷刻意很窄,只擋「整段只有一行,而那一行是已知的
偷懶寫法」——這一關要擋的是明顯沒跑過就交差,不是去評價別人的測試寫得夠不夠好。
沒有自動化測試時,寫得出可重現的手動驗證步驟就放行。
停錶排在 PR 開出去之後,而且只在 PR 真的建立了才停:工時要記在真的有做事的那段時間上。
錶本來就沒在跑不算失敗(Gitea 對此回 500)——PR 已經開出去了,把整件事報成失敗只會讓人
以為 PR 沒開成而重跑一次。
2026-09-17 08:23:22 +00:00
jiantw83
d6b44e0ba8
test(分批提交): 把變更依類型分批 commit
...
一個 commit 只裝一種類型:程式碼、測試、文件、雜項各自成批,reviewer 一次只看一件事,
日後 git log 也讀得懂。全部混成一顆「完成工作包」的巨大 commit,等於沒有歷史。
類型多半看得出來——測試檔就是 test、README 就是 docs——但 scripts/ 底下的改動是新功能
還是修 bug,只有做的人知道,所以那一批由 --type 指定。這張對照表是純字串規則,表格驅動。
scope 單檔用檔名(claim.test.js 的 scope 是 claim,不是 claim.test),多檔用 --scope 的
功能名。描述要有中文:日後回顧時看得懂的是中文,而夾雜英文的專有名詞本來就該保留原文。
--files 讓一次變更橫跨兩個功能時能分兩次跑;--body 讓工具產出的歷史與本 repo 既有的
commit 一樣說明得出「為什麼」。
列變更檔案刻意不用 git status --porcelain:它的前兩欄是狀態碼,而 runGit 會 trim 掉
輸出的前導空白,未 staged 的修改會少掉檔名的第一個字元。
2026-09-17 08:23:21 +00:00
jiantw83 and Claude Opus 5
8ea6a2a8b3
test(逐項實作): 覆蓋勾選的五種危險、兩份規則正本與正本第二段
...
勾選的測試全部繞著「會不會改錯行」打轉,五種都是 code review 抓出來的實際缺陷:
圍欄裡長得像 checkbox 的那一行不會被改到、不同段落的同一句話靠 --section 分得開、
大寫 [X] 重跑是 no-op、方框後沒有空白照樣勾得到、沒有方框的項目給的是指路的錯誤
而不是謊報已勾過。
另外釘住抽取端與勾選端的一致性:wp-extract 交得出來的每一種 raw,--tick 都要收得下。
patchOf 收進 helpers——它先前在三個測試檔裡各有一份一模一樣的定義。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 07:39:54 +00:00
jiantw83 and Claude Opus 5
67528dde5f
fix(README): 安裝指令補上 git+ 前綴,否則 npm 會把它當壓縮檔
...
npm 只有看到 git/git+ssh/git+http/git+https/git+file 才會當成 git repo。
寫成 https://….git 會被歸類成遠端 tarball,下載回來解壓失敗,錯誤是
TAR_BAD_ARCHIVE: Unrecognized archive format——看起來完全不像「網址寫法錯了」,
使用者只會以為套件壞了。
先前那條「README 指令逐字執行得動」的測試沒抓到,因為它只跑 tea-sdlc 開頭的
行,npm 那行從來沒被執行過。補一條規則把 .git 網址必須帶 git+ 前綴釘住:
真的去跑一次 npm 全域安裝要開網路、要三秒,不適合放進這套向來密封的測試裡,
但「前綴在不在」這條規則本身就足以擋掉這個錯。
議題 #25
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 15:30:27 +08:00
jiantw83 and Claude Opus 5
cbf4c3df36
docs(README): 前置需求分開講安裝與跑流程,並把指令是否真的跑得動釘住
...
前置需求表原本一句「缺少時腳本印出指引並中止」套在所有需求上,但 install 只
警告不中止——文件描述的是一個不存在的行為。改成逐項寫「缺了會怎樣」,並把
「安裝只需要 Node」提到表前面:使用者不該為了裝 plugin 先去裝 tea。
另外補上三條把 #30 驗收標準真的驗起來的測試。其中一條把 README bash 區塊裡的
tea-sdlc 指令逐行拿去真的執行(家目錄指向空的暫存,所以一個檔都不會寫),
失敗理由若是「這個指令/參數我不認得」就算 README 寫錯——這比用眼睛核對可靠。
議題 #30
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 07:11:18 +00:00
jiantw83 and Claude Opus 5
002511ce45
test(sdlc-feat): 覆蓋領取鎖決策表、分支命名規則與三處不覆蓋他人進度
...
領取鎖的四種狀態各一例,而且每一種擋的情況都驗「一個字都沒寫進 Gitea」——擋下來卻已經
改了一半,比直接放行更難收拾。
分支命名是純字串規則,表格驅動:開發分支三種寫法、功能分支兩層,加上中文、超長、大寫、
底線與連續連字號的輸入驗證。
git 的部分在臨時 repo 上跑真的 git,釘住三件事後來由 code review 抓出來的實際缺陷:
遠端分支的比對必須用全名(否則 feat/x/main 會冒名頂替 main)、工作區不乾淨要在動手前
就擋、來源分支與遠端分歧要回可區分的錯誤碼而不是 git 的原始訊息。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 07:07:02 +00:00
jiantw83 and Claude Opus 5
e1897f2d33
test(helpers): 加上「有遠端」的臨時 repo,並收掉三份重複的 git 執行器
...
branch-prep 的重點行為都繞著遠端打轉——來源分支在遠端已存在時要 pull 而不是重建、
目標分支已存在時不能覆蓋他人進度。這些事沒有遠端就驗不出來,所以補一個 bare origin
加工作用 clone,並附 pushFromElsewhere 模擬「別人推了東西上去」。
順手把 makeTempRepo 與新 helper 各自重寫一次的 git 執行器與 seed 收成共用的兩支。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 07:07:01 +00:00
jiantw83 and Claude Opus 5
17178f0c4d
feat(佈署): 以 npm 裝出 tea-sdlc 指令並產生各平台轉接檔
...
單一入口 bin/tea-sdlc.js 認四個子指令。第一個位置參數是子指令,其餘 argv 原樣
交出去——既有的 flag 解析拒絕位置參數,所以子指令必須在那之前就被取走。
轉接檔裡沒有路徑,只有一句 tea-sdlc prompt --name <指令名>。正本在哪由 PATH 上
的 tea-sdlc 自己回推:fnm 把 Node 版號寫進全域安裝路徑,寫死路徑的話升一次
Node,七個平台的轉接檔會同時指向不存在的檔案,而且不會有任何錯誤訊息。
prompt 是全專案唯一輸出非 JSON 的路徑,理由只有一個:它的輸出要餵給模型讀。
失敗仍走 envelope——成功是內容,失敗才需要結構。
status 的 ok 不兼差表達環境好壞,健康與否放在 data.healthy:呼叫端要分得出
「status 掛了」與「status 成功查到你環境有問題」。
install 只寫進偵測得到的平台;缺 git/tea 只警告不中止,因為那兩個完全不影響
轉接檔產生,硬擋等於逼使用者為了裝 plugin 先去裝 tea。uninstall 只刪帶產生標記
的檔案,使用者自己寫的同名檔案一律留著並在輸出裡交代。裝哪些指令以 prompts/ 裡
實際存在的正本為準,不是寫死的六個名字——裝出指向不存在正本的轉接檔,使用者只會
看到 PROMPT_NOT_FOUND。
流程正本的 description 前綴在抄進轉接檔之前就檢查:有三個平台關不掉自動觸發,
全靠那句話把 description 窄到不會被誤判,不能等使用者發現誤觸才知道漏了。
議題 #26 #27 #28 #29 #17
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 06:59:37 +00:00
jiantw83 and Claude Opus 5
b56216e0f0
test(工時報表): 釘住週次歸屬的跨月、跨年與五個週五
...
時區在測試裡固定為 Asia/Taipei:週界是以人在的時區切的,不釘住時區就等於沒釘住答案。
--today 讓「本週」在 CLI 接縫上釘得住,否則預設期間會跟著系統時鐘漂走。
寫入請求那則斷言誠實列出唯一的非 GET——四層前置檢查的寫入權探針,
而不是先把它濾掉再宣稱整趟沒有寫入。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 06:50:35 +00:00
jiantw83 and Claude Opus 5
74930b0571
test(wp-extract): 覆蓋契約欄位、模板變體、活狀態與 CRLF
...
解析是整條鏈的上游,解析錯則下游全錯,所以模板變體餵得雜一些:缺段落、巢狀驗收為空、
checkbox 已勾、中英混排、圍欄裡的假待辦、只寫一格的表格列。
另外釘住三件容易在日後鬆掉的事:
- `raw` 逐行出現在原始 body 裡,包括 CRLF 的 body 連行尾的 \r 都留著,否則下游替換
時對不上原文。
- 相依與留言都逐頁讀完,不是只讀第一頁。
- --dry-run 預告的請求順序與實跑一致,且完全不碰 Gitea。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 06:30:24 +00:00
jiantw83 and Claude Opus 5
a460d7e661
test(總覽網頁): 把三條驗不出東西的斷言改成驗得出來的
...
- mermaid 那條原本只比對「有出現 mermaid 字樣」,而 CDN 網址裡就有這個字,
整段渲染腳本刪掉也照樣通過。改成驗 mermaid.initialize 真的被呼叫。
- 新增一條守住例外的範圍:模板裡只能有那一段腳本,且不得夾帶網路或儲存操作。
- sdlc-plan 的七條斷言原本拿整份正本比對,任何一處撞到就算過。改成只看第 6 步,
與 sdlc-analyze 那邊的做法一致。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 06:22:31 +00:00
jiantw83 and Claude Opus 5
0aa0439061
test(總覽網頁): 釘住模板結構與兩份正本的規則
...
模板的檢查重點是「樣式與內容分離」與「全景段落能整段消失」,兩者壞掉時規劃版會
留下空標題或行內樣式散落各處,肉眼不容易發現。
順帶把 work-package 測試的 phase2 切法收斂到第三段之前——原本切到檔尾,第三段
新增的編號清單會混進段落順序的斷言裡。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 06:22:29 +00:00
jiantw83 and Claude Opus 5
47529d33c2
test(issue-update): 覆蓋總覽網址的寫回與就地更新
...
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 06:22:29 +00:00
jiantw83 and Claude Opus 5
97d0afac38
test(project-add): 把「不建立專案」改成驗得出東西的斷言
...
原本的條件是「沒有非 GET 請求打到路徑含 project 的端點」,但 Gitea 的專案根本
沒有 API 路徑,這個條件恆真,壞掉的實作也會通過。改成斷言真正的寫入只有議題本身
那一次 PATCH、而且只帶 projects 欄位。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 06:06:12 +00:00
jiantw83 and Claude Opus 5
15da38a460
test(issue-update): 封住估算那一行的三種污染
...
三條測試都先確認過會在舊版實作上失敗。第一版寫出來時有兩條其實是陪跑的——
情境裡沒有後續段落,正確與錯誤的結果剛好都落在 body 結尾,分不出來。加上一個
真正的後續段落之後才有鑑別力。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 06:06:11 +00:00
jiantw83 and Claude Opus 5
b09746a309
test(排程): 覆蓋重複 index
...
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 06:06:11 +00:00
jiantw83 and Claude Opus 5
257642cb1c
test(sdlc-analyze): 釘住第三段的腳本順序與已知限制那一節
...
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 06:06:08 +00:00
jiantw83 and Claude Opus 5
5327f5882e
test(project-add): 覆蓋反查、貼網址、既有看板不被覆蓋
...
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 06:06:07 +00:00
jiantw83 and Claude Opus 5
3f97fe0901
test(issue-update): 覆蓋 Milestone 解析、日期格式與估算的就地更新
...
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 06:06:06 +00:00
jiantw83 and Claude Opus 5
b45437e527
test(issue-link): 覆蓋兩種相依、冪等與自我相依
...
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 06:06:05 +00:00
jiantw83 and Claude Opus 5
be477a4391
test(排程): 覆蓋拓撲順序、多重先決與成環
...
除了逐例比對日期,另有一條測試直接驗「所有先決關係都滿足」這個不變式,
而不是只比對硬編的日期字串——不變式壞掉時它會指出是哪一對。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 06:06:04 +00:00
jiantw83 and Claude Opus 5
6457d68874
refactor(測試): 模板結構的三項檢查抽到共用斷言
...
段落順序、正本的編號清單、圖表段落不寫死圍欄——這三件事需求議題與工作包議題
都要驗,第二份模板出現時就該抽出來。兩份測試原本各抄一份,其中佔位數的斷言
還悄悄長成不同寫法(一邊 >=、一邊 ==);抽出來之後這種分歧會被逼著講清楚。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 05:43:26 +00:00
jiantw83 and Claude Opus 5
f0beb1ff64
test(工作包): 釘住模板與「產生工作包」那一段的規則
...
模板的段落順序決定 wp-extract 解析得到什麼,待辦的巢狀寫法決定實作階段勾得到
哪一行——這兩件事寫死在測試裡,改動時才會被逼著一起改。
巢狀範例的斷言不比對固定縮排量,而是比對「最深的一層比最淺的深」,這樣重排
外層清單的縮排不會弄壞測試。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 05:43:25 +00:00
jiantw83 and Claude Opus 5
0cd25d49d4
feat(sdlc-analyze): 擴充為兩段式,第二段把共識變成工作包議題
...
第一段到共識摘要為止仍然完全不寫入;使用者點頭之後才進入第二段建立議題。
邊界條文隨之改寫成「共識摘要之前不對 Gitea 產生任何寫入」,並明列第二段
不做的事:不建相依、不掛 Milestone、不加看板、不寫人天估算——那些是後續
流程的工作,寫在這裡會讓兩顆工作包互相踩。
工作包的切法、標題規則(動詞加名詞、禁止 WP-01 這類流水編號)、待辦與驗收
的巢狀寫法(附可照抄的範例)、架構圖依性質三選一,都在這一段定下來。
邊界的斷言跟著條文一起改,因為條文換了語意;分開成兩顆 commit 的話中間那顆
會是紅的。另補一條斷言:第一段不得出現任何寫入型腳本的名字。
Closes #7
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 05:43:24 +00:00
jiantw83 and Claude Opus 5
8a405de189
test(sdlc-analyze): 覆蓋時程清單的估算前提
...
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 05:11:07 +00:00
jiantw83 and Claude Opus 5
2b06dfd663
test(sdlc-analyze): 釘住四份清單與正本的結構
...
這一顆沒有腳本,交付的就是文件本身,所以驗的是文件的結構:四份清單各有足夠
的檢查項與提問指引、正本逐一指名它們且順序為架構→邏輯→資料→時程、一次一題、
兩個固定選項、共識摘要只印不寫,以及「不對 Gitea 寫入」有被寫成明確邊界。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 05:11:06 +00:00
jiantw83 and Claude Opus 5
930270f545
refactor(測試): 正本的共用檢查抽到 helpers
...
description 前綴、沒有 frontmatter、不出現平台專屬字樣——這三件事每一份流程
正本都要驗,第二份正本出現時就該抽出來,而不是再抄一次。順帶把讀正本、讀規則
正本、讀模板三個路徑組合也收在同一處。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 05:11:06 +00:00
jiantw83 and Claude Opus 5
06de8980ca
test(issue-extract): 封住圍欄、表格與分頁的回頭路
...
八條新案例,每一條都對應一個實際重現過的缺陷:~~~ 圍欄、圍欄內的假清單項、
混用圍欄標記、未閉合圍欄的歸屬、逸脫的直線、第二條分隔列、巢狀攤平,以及
留言分頁。
把修正還原成舊版解析器後,這批測試有五條會失敗;修正回來則全綠——確認測試
真的抓得到,不是陪跑。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 05:00:29 +00:00
jiantw83 and Claude Opus 5
fd80d9772a
test(issue-extract): 以模板變體覆蓋抽取契約
...
解析是整條鏈的上游,解析錯則下游全錯,所以模板變體餵得比別處雜:缺段落、
段落在但列表是空的、checkbox 已勾與未勾、中英混排、編號清單、名詞表只有
表頭、body 全空、出現契約外的段落。
留言的部分驗兩件事:未整併則數只算沒有 +1 的,以及留言內容一個字都不得出現
在輸出裡——後者用整份 stdout 做子字串比對,比逐欄檢查更難繞過。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 05:00:27 +00:00
jiantw83 and Claude Opus 5
9b9ab33a53
test(issue-create): 覆蓋建立議題與試跑預覽的契約
...
沿用既有接縫:子行程執行、stub server 錄下每一筆請求。涵蓋標籤名稱解析、
未知標籤中止、不碰 labels 的寫入端點、同標題不重建、前後空白視為同一顆,
以及寫入型腳本一樣跑滿前置檢查。
試跑的部分特別驗「預覽要忠實」:預覽的 body 必須看得出標籤會被貼上、標籤
錯字在試跑就該擋下、同名議題已存在時預覽不得預告要建立議題,且全程不發出
任何寫入請求。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 04:44:47 +00:00
jiantw83 and Claude Opus 5
9e57a5db99
test(sdlc-plan): 釘住正本與模板的結構
...
正本與模板是檔案而非程式,卻是本工作包實際交付的東西:模板段落順序決定下游
解析得到什麼,正本的平台中立性決定轉接檔能不能一份寫到底。以測試釘住段落
順序、佔位格式、description 前綴、平台專屬字樣的缺席、流程圖的上限,以及
模板不得寫死 mermaid 圍欄。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 04:44:47 +00:00
jiantw83 and Claude Opus 5
b0bd061852
refactor(測試): 共用假 Gitea 的啟動與環境變數
...
每支測試各自寫一次「啟動 stub、登記 close、組環境變數」已經重複三次,抽到
helpers 之後新增測試檔只要一行。各檔仍保留自己的預設路由,因為那是該檔的
情境設定,不該共用。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 04:44:45 +00:00
jiantw83 and Claude Opus 5
4ae097f1e1
fix(腳本契約): 長輸出不再被截斷,查重翻頁加上上限
...
process.stdout.write 之後立刻 process.exit 會截斷輸出——stdout 接到 pipe 時
寫入是非同步的。改為等 write 的 callback 回來再退出。實測舊寫法在約 83KB 處
被切斷,新增的回歸測試以 8000 筆標籤覆蓋這條路徑。
findIssueByTitle 的翻頁原本沒有上限,Gitea 若持續回滿一頁就會無限打下去。
加上 200 頁上限,超過即以 DEDUPE_LIMIT 報錯而非無聲回 null——無聲回 null 會
讓呼叫端把既有議題再建一次,正好是冪等查重要防的事。
parseFlags 取值時不再於三元運算式內遞增迴圈變數,改為獨立敘述。
測試工具的 maxBuffer 調高到 64MB:預設 1MB 會在長輸出時砍掉子行程,那是測試
工具的限制而非腳本的問題。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-09-17 04:26:51 +00:00