feat/plan-analyze-timing/main #63

Merged
admin merged 4 commits from feat/plan-analyze-timing/main into master 2026-09-18 00:52:27 +00:00
Member

摘要

規劃與分析的時間不再憑空消失。/sdlc-report 的數字裡從此規劃、分析、實作三段都在,
估算的落差看得出來源——在此之前碼錶只在領取工作包時起動,報表上規劃永遠是零。

需求議題

#56

工作包議題

#57

變更內容

四顆 commit:

  • 5638593 refactor(lib): 停錶與議題工時清單下沉到 lib
  • 81d02bf feat(timer,time-log): 起錶配上停錶,並補登議題建立之前的時間
  • 7ee050d docs(sdlc-plan,sdlc-analyze): 兩份正本各自起停自己的錶
  • 46c6db8 feat(time-log): 對既有議題重跑時補到當下,每一輪各記一筆

新增:

  • scripts/time-log.js — 補登一段沒有錶記到的工時
  • test/time-log.test.js — 12 條
  • test/helpers/prompt-doc.js 的 promptStep — 以步驟名稱(不是編號)框出正本裡的某一步

修改:

  • scripts/timer.js — 加上 --stop
  • scripts/lib.js — stopStopwatch 由 pr-create.js 下沉,簽章改成與同伴一致的 (login, repo, index)
  • scripts/pr-create.js — 改用下沉後的 stopStopwatch
  • prompts/sdlc-plan.md、prompts/sdlc-analyze.md — 各加一節「計時範圍」與起停步驟
  • prompts/sdlc-feat.md — 「碼錶一律由使用者自己停」收斂成只講別顆議題
  • test/timer.test.js(+6 條)、test/sdlc-plan-assets.test.js(+6 條)、
    test/sdlc-analyze-assets.test.js(+5 條)、test/sdlc-feat-assets.test.js、
    test/overview-artifact.test.js、test/wp-schedule-assets.test.js

設計重點

補登的長度由腳本算,不由 agent 做減法。 交給 agent 等於讓兩邊的時鐘與時區各算一次,
而算錯了報表上看不出來。--since 收指令開始的時刻,終點由腳本自己判斷。

終點看議題是不是這一輪建立的。 是就補到議題建立那一刻;對既有議題重跑就補到補登的
當下——拿舊的建立時間當終點會算出負數,等於把那一輪的工夫丟掉,而那正是這顆議題要修的
毛病,只是換個位置出現。重跑是累計不是覆蓋,每一輪各記一筆。

防重複只留一條:錶已經跑在這顆議題上。 補登排在起錶之前,錶在跑代表這一輪已經走到
起錶那一步,補下去會與錶涵蓋的區間重疊。曾經用過的「議題上已經有工時就跳過」與累計互斥,
拿掉了。

--stop 只停 --index 指的那一顆。 別顆議題上的錶一律不碰:Gitea 在別顆議題上起新錶
會靜默地停掉並記錄前一顆,那種靜默結算正是領取鎖那條規則當初要擋的,不能在這裡反過來
製造它。claim.js 一個字都沒動。

analyze 的錶起在第一段開頭。 分析最耗時的正是共識之前那一段(四類疑點逐題問到收斂),
從第二段才起的話那段時間永遠是零。因此該正本的邊界由「共識摘要之前不對 Gitea 產生任何
寫入」改寫成「不寫入任何內容」,並把碼錶明文除外、寫上理由:它記的是工時,不是內容。
這一條事前與 repo 擁有者確認過。

plan 與 analyze 各記各的。 plan 在自己的回報那一步就停錶,所以同一顆需求議題上會有
兩筆工時。「每個階段停掉自己起的錶」與「兩段合成一筆」無法同時成立,而錶跨階段跑的代價
(跑完就去開會,錶跑一整天)比多一筆紀錄大得多。議題 #57 的規格已就此更正。

正本之間改以步驟名稱互指。 編號會整批位移,名字不會;promptStep 讓測試用同一套規則
框出某一步,別人插一步時不會無聲地框到另一段內容上。

解決的問題

  • 規劃階段的工時完全不計:議題建立之前那段沒有標的可起錶,現在以補登記上去。
  • 分析階段的工時完全不計:錶現在起在它分析的那顆需求議題上。
  • 錶跨階段跑:兩道指令各自停掉自己起的那一支。
  • 對既有議題重跑時補登靜默 no-op(相減為負),那一輪的規劃時間會掉。
  • --dry-run 下看不出「這一步會不會真的做」:兩支都先讀現況再印出將發出的請求。

影響的功能

  • /sdlc-plan、/sdlc-analyze 各多兩步(起錶/停錶)與一節「計時範圍」;plan 另多一步
    「記下開始時間」與一次補登。
  • /sdlc-report 的數字會變大——規劃與分析從此有工時。這是本次要的結果。
  • /sdlc-feat 只改一句措辭(「碼錶一律由使用者自己停」→ 只講別顆議題的錶),行為不變。
  • pr-create 的停錶行為不變,只是改叫 lib 的共用實作。
  • claim.js(領取鎖)未改動。
  • scripts/report.js 未改動:它讀 /user/times,補登的工時本來就進得去。

測試結果

npm test(含新增的 23 條):

ℹ tests 823
ℹ suites 0
ℹ pass 823
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 11168.6262

另在議題 #57 上實跑過真實路徑(該 repo 的 enable_time_tracker 是 true):

--- 1. 補登 ---
{"ok":true,"data":{...,"since":"2026-09-18T00:42:47.207Z","迄":"2026-09-18T00:44:48.469Z","依據":"補登當下","秒數":121,"補登":true}}
--- 2. 起錶 ---
{"ok":true,"data":{...,"碼錶中":true,"已在計時":false}}
--- 3. 錶在跑時補登應跳過 ---
{"ok":true,"data":{...,"秒數":296,"補登":false,"note":"碼錶已經跑在這顆議題上。補登排在起錶之前,錶在跑就代表這一輪補過了,補下去會與錶重疊。"}}
--- 4. 停錶 ---
{"ok":true,"data":{...,"碼錶已停":true}}
--- 5. 再停一次(錶已經沒在跑) ---
{"ok":true,"data":{...,"碼錶已停":false,"note":"碼錶本來就沒在這顆議題上運轉,這一步略過;別顆議題上的錶不由這裡代停。"}}
--- 6. report --week ---
"總計": {"實際秒": 128, "實際工時": "0h 02m"}
"議題": [{"index": 57, "實際秒": 128, "實際工時": "0h 02m"}]

驗證用的那兩筆工時(121 秒與 7 秒)驗完已刪除,議題 #57 上目前一筆工時都沒有。

其他

reviewer 可自行重現:git diff origin/master 後 npm test;真實路徑以
--dry-run 觀察即可,不必真的寫入。

## 摘要 規劃與分析的時間不再憑空消失。`/sdlc-report` 的數字裡從此規劃、分析、實作三段都在, 估算的落差看得出來源——在此之前碼錶只在領取工作包時起動,報表上規劃永遠是零。 ## 需求議題 #56 ## 工作包議題 #57 ## 變更內容 四顆 commit: - `5638593` refactor(lib): 停錶與議題工時清單下沉到 lib - `81d02bf` feat(timer,time-log): 起錶配上停錶,並補登議題建立之前的時間 - `7ee050d` docs(sdlc-plan,sdlc-analyze): 兩份正本各自起停自己的錶 - `46c6db8` feat(time-log): 對既有議題重跑時補到當下,每一輪各記一筆 新增: - `scripts/time-log.js` — 補登一段沒有錶記到的工時 - `test/time-log.test.js` — 12 條 - `test/helpers/prompt-doc.js` 的 `promptStep` — 以步驟**名稱**(不是編號)框出正本裡的某一步 修改: - `scripts/timer.js` — 加上 `--stop` - `scripts/lib.js` — `stopStopwatch` 由 `pr-create.js` 下沉,簽章改成與同伴一致的 `(login, repo, index)` - `scripts/pr-create.js` — 改用下沉後的 `stopStopwatch` - `prompts/sdlc-plan.md`、`prompts/sdlc-analyze.md` — 各加一節「計時範圍」與起停步驟 - `prompts/sdlc-feat.md` — 「碼錶一律由使用者自己停」收斂成只講別顆議題 - `test/timer.test.js`(+6 條)、`test/sdlc-plan-assets.test.js`(+6 條)、 `test/sdlc-analyze-assets.test.js`(+5 條)、`test/sdlc-feat-assets.test.js`、 `test/overview-artifact.test.js`、`test/wp-schedule-assets.test.js` ## 設計重點 **補登的長度由腳本算,不由 agent 做減法。** 交給 agent 等於讓兩邊的時鐘與時區各算一次, 而算錯了報表上看不出來。`--since` 收指令開始的時刻,終點由腳本自己判斷。 **終點看議題是不是這一輪建立的。** 是就補到議題建立那一刻;對既有議題重跑就補到補登的 當下——拿舊的建立時間當終點會算出負數,等於把那一輪的工夫丟掉,而那正是這顆議題要修的 毛病,只是換個位置出現。**重跑是累計不是覆蓋**,每一輪各記一筆。 **防重複只留一條:錶已經跑在這顆議題上。** 補登排在起錶之前,錶在跑代表這一輪已經走到 起錶那一步,補下去會與錶涵蓋的區間重疊。曾經用過的「議題上已經有工時就跳過」與累計互斥, 拿掉了。 **`--stop` 只停 `--index` 指的那一顆。** 別顆議題上的錶一律不碰:Gitea 在別顆議題上起新錶 會靜默地停掉並記錄前一顆,那種靜默結算正是領取鎖那條規則當初要擋的,不能在這裡反過來 製造它。`claim.js` 一個字都沒動。 **analyze 的錶起在第一段開頭。** 分析最耗時的正是共識之前那一段(四類疑點逐題問到收斂), 從第二段才起的話那段時間永遠是零。因此該正本的邊界由「共識摘要之前不對 Gitea 產生任何 寫入」改寫成「不寫入任何**內容**」,並把碼錶明文除外、寫上理由:它記的是工時,不是內容。 這一條事前與 repo 擁有者確認過。 **plan 與 analyze 各記各的。** plan 在自己的回報那一步就停錶,所以同一顆需求議題上會有 兩筆工時。「每個階段停掉自己起的錶」與「兩段合成一筆」無法同時成立,而錶跨階段跑的代價 (跑完就去開會,錶跑一整天)比多一筆紀錄大得多。議題 #57 的規格已就此更正。 **正本之間改以步驟名稱互指。** 編號會整批位移,名字不會;`promptStep` 讓測試用同一套規則 框出某一步,別人插一步時不會無聲地框到另一段內容上。 ## 解決的問題 - 規劃階段的工時完全不計:議題建立之前那段沒有標的可起錶,現在以補登記上去。 - 分析階段的工時完全不計:錶現在起在它分析的那顆需求議題上。 - 錶跨階段跑:兩道指令各自停掉自己起的那一支。 - 對既有議題重跑時補登靜默 no-op(相減為負),那一輪的規劃時間會掉。 - `--dry-run` 下看不出「這一步會不會真的做」:兩支都先讀現況再印出將發出的請求。 ## 影響的功能 - `/sdlc-plan`、`/sdlc-analyze` 各多兩步(起錶/停錶)與一節「計時範圍」;plan 另多一步 「記下開始時間」與一次補登。 - `/sdlc-report` 的數字會變大——規劃與分析從此有工時。這是本次要的結果。 - `/sdlc-feat` 只改一句措辭(「碼錶一律由使用者自己停」→ 只講別顆議題的錶),行為不變。 - `pr-create` 的停錶行為不變,只是改叫 lib 的共用實作。 - `claim.js`(領取鎖)未改動。 - `scripts/report.js` 未改動:它讀 `/user/times`,補登的工時本來就進得去。 ## 測試結果 `npm test`(含新增的 23 條): ``` ℹ tests 823 ℹ suites 0 ℹ pass 823 ℹ fail 0 ℹ cancelled 0 ℹ skipped 0 ℹ todo 0 ℹ duration_ms 11168.6262 ``` 另在議題 #57 上實跑過真實路徑(該 repo 的 `enable_time_tracker` 是 `true`): ``` --- 1. 補登 --- {"ok":true,"data":{...,"since":"2026-09-18T00:42:47.207Z","迄":"2026-09-18T00:44:48.469Z","依據":"補登當下","秒數":121,"補登":true}} --- 2. 起錶 --- {"ok":true,"data":{...,"碼錶中":true,"已在計時":false}} --- 3. 錶在跑時補登應跳過 --- {"ok":true,"data":{...,"秒數":296,"補登":false,"note":"碼錶已經跑在這顆議題上。補登排在起錶之前,錶在跑就代表這一輪補過了,補下去會與錶重疊。"}} --- 4. 停錶 --- {"ok":true,"data":{...,"碼錶已停":true}} --- 5. 再停一次(錶已經沒在跑) --- {"ok":true,"data":{...,"碼錶已停":false,"note":"碼錶本來就沒在這顆議題上運轉,這一步略過;別顆議題上的錶不由這裡代停。"}} --- 6. report --week --- "總計": {"實際秒": 128, "實際工時": "0h 02m"} "議題": [{"index": 57, "實際秒": 128, "實際工時": "0h 02m"}] ``` 驗證用的那兩筆工時(121 秒與 7 秒)驗完已刪除,議題 #57 上目前一筆工時都沒有。 ## 其他 reviewer 可自行重現:`git diff origin/master` 後 `npm test`;真實路徑以 `--dry-run` 觀察即可,不必真的寫入。
jiantw83 added 4 commits 2026-09-18 00:51:46 +00:00
停錶原本只長在 pr-create 裡,而規劃與分析接下來也要停自己的錶(議題 #57)。
那段容錯邏輯——「錶沒在跑」的狀態碼隨站台版本而異,認的是狀態碼在 409/500
這一組**且**訊息說的是碼錶——各寫一份遲早會在某一邊漏掉一種狀態碼,而它漏掉的
症狀正是「事情做完了卻回報失敗」。

順手把簽章改成與同伴一致的 (login, repo, index):fetchIssue、listIssueTimes、
stopwatchOnIssue 都這樣收,只有它收一條手組的路徑字串。

新增 listIssueTimes 供補登判斷「這顆議題上已經有工時了嗎」。它在議題讀得到卻
404 時報 TIME_TRACKER_OFF,與四層前置檢查第四層說同一句話——試跑不跑前置檢查,
那句話得由它自己說,否則試跑看到的是一句看不懂的 404。

議題 #57

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
碼錶只在領取工作包時起動,所以工時報表上規劃與分析永遠是零。久了會讓人以為
規劃不花時間,而那正是估算失準最常見的來源。

timer 加上 --stop,而且**只停 --index 指的那一顆**。每個階段停掉自己起的那一支,
錶就不會跨階段跑——跑完就去開會而錶跑一整天,報表當場失真。反過來,別顆議題上的錶
一律不碰:Gitea 在別顆議題上起新錶會靜默地停掉並記錄前一顆,那種靜默結算正是領取鎖
那條規則當初要擋的,不能在這裡反過來製造它。錶本來就沒在跑不算失敗,這一步多半排在
回報之前,報成失敗只會讓人以為前面那件事沒做成而重跑一次。

time-log 補登議題建立之前那一段——讀齊輸入、逐項詢問、組出議題內容,往往是整個 plan
最耗時的部分,而那時候議題還不存在,沒有標的可起錶。長度由腳本自己算(議題的建立時間
減掉 --since),交給 agent 做減法等於讓兩邊的時鐘各算一次,而算錯了報表上看不出來。
**不設時間上限、照實補登**:中途去開會的兩小時會一起算進去,換來這個流程不必為此
多長一題出來問使用者。

補登不是冪等的動作,所以兩種情況跳過不補:錶已經跑在這顆議題上(補登排在起錶之前,
錶在跑就代表這一段補過了),以及這顆議題上已經有工時(整個流程跑完過一次)。工時記
重複比記不到更難在報表上被發現,所以判斷偏向不補。

時間追蹤在現有環境下可能是關著的,真實路徑跑不起來;兩支都靠 --dry-run 與 stub
server 測,共 27 條。

議題 #57

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
起錶與停錶寫在同一份正本裡成對出現,讀的人一眼看得出這段計時涵蓋到哪;兩份各加一節
「計時範圍」把兩端指出來。

sdlc-plan 記兩段:議題建立之前那段在第一步記下開始時間、議題建立之後以補登記上去;
之後起錶,回報那一步停錶。補登排在議題建立之後、起錶之前,順序反過來的話補登會被
time-log 當成「這一步做過了」而跳過。

sdlc-analyze 在它分析的那顆需求議題上起錶,起點放在第一段開頭——分析最耗時的正是
共識之前那一段,從第二段才起的話那段時間永遠是零。因此「共識摘要之前不對 Gitea
產生任何寫入」改寫成「不寫入任何**內容**」,並把碼錶明文除外、寫上理由:它記的是
工時,不是內容。這一條與 repo 擁有者確認過。

sdlc-feat 的「碼錶一律由使用者自己停」收斂成「**別顆議題上的**錶一律由使用者自己停」
——它原本讀起來像全域規則,而現在每道指令都會停自己起的那一支。

正本之間改以步驟**名稱**互指,不用編號:編號會整批位移,名字不會。測試跟著改用新的
promptStep 輔助函式,以名字框出某一步的內容,別人插一步時不會無聲地框到另一段上。

議題 #57

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
原本的終點一律是議題的建立時間,所以對既有議題重跑時相減為負,補登直接 no-op——
那一輪的規劃時間就這樣掉了,而那正是這顆議題要修的毛病,只是換個位置出現。

終點改成看議題是不是這一輪建立的:是就補到議題建立那一刻(第一次跑),不是就補到
補登的當下(重跑)。回報多出 `迄` 與 `依據` 兩個欄位,讓人一眼看出這一筆補的是哪一段。

「這顆議題上已經有工時就跳過」這條規則拿掉了,它與累計互斥。防重複只剩一條:錶已經
跑在這顆議題上——那一段已經有錶在記,補下去會與錶涵蓋的區間重疊。連帶把 lib 的
listIssueTimes 一起移除,沒有人再用它。

實跑驗過(議題 #57):補登 121 秒、起錶、停錶 7 秒,兩筆都進得了週報,驗完刪除。
議題 #57 的規格同步更正,另外兩處過期的敘述也一併改掉。

議題 #57

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
admin approved these changes 2026-09-18 00:52:24 +00:00
admin merged commit 7995e74332 into master 2026-09-18 00:52:27 +00:00
admin deleted branch feat/plan-analyze-timing/main 2026-09-18 00:52:27 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: plugins/tea-sdlc#63