driving-work-to-done — 一件工作、一個入口、一個下一步
三支 skill 各做一站的事:refinement 簽下成功的定義,engineering 施工,verify-ac
判定。它們都不決定下一步是什麼——那寫在這裡,只寫在這裡。
一份工作有兩種成功條件,它們住在不同地方:
- 這張單獨有的(acceptance criteria)→ 進
refinement凍結的 fence。 - 這一類工作共用的(definition of done)→ 由領域知識帶進來,見〈載入領域知識〉。
把共用的那份寫進每一張單的 fence,等於每次都重簽同樣幾行不承載新資訊的東西。
一、有工作進來:要不要立案
判準只有一條:有沒有「怎麼算成功」需要人簽字。
| 這件事 | 立案? |
|---|---|
| 查一下、說明一段程式、跑個既有測試 | 否。什麼都沒改,沒有要簽的東西 |
| 會改到程式碼或行為——不論多小 | 是 |
| 不確定 | 是。立案的成本遠低於做完才發現目標不對 |
只讀的問題不走這條,其餘都走。以前這裡有一格是「改 typo、調一個顯然的常數 → 否, 成功的定義不會有爭議」,拆掉了:那一格的判準是「這件事夠小」,而那是每次都由當事人自己 判、每次都會判得更寬的東西。
把判斷與依據說出來,一句話就夠,讓人能當場推翻。不立案的到此為止,直接把事做完。
手上正在做別的事,但長出了一個不能消失的東西
開發途中會問出、查出、撞出只有當下知道的東西。停下來簽一張正式的單太貴——assertion 此刻也 簽不出來,因為怎麼算成功還沒想清楚。丟著又會消失:一個修正留在一個到不了別人手上的 地方,跟沒有那個修正是一樣的(2026-08-07 真的發生過,同一個 bug 因此咬了兩版)。
所以有第三條路:開一張種子單,然後回去做你原本在做的事。
開單那一刻要回答兩件事:這是哪一種單,以及這件事有沒有人已經記過了。
- 種類自己宣告,開單那條路不從名字或內容推。舉發實作途中撞到的問題,跟開發一個新 功能,問得出答案的東西不一樣——撞到的人當下就知道自己是誰、剛剛在讀哪個檔,而想加 新功能的人沒有「撞到」這件事,那兩格硬填會填出一個編的。
- 舉發那一種開之前會看到撞到了哪幾張,並且要對每一張寫下一句判斷。判斷的內容不 決定開不開得成單——說「都不是同一件」照樣開得出來。擋人的只有「沒有回答問題」。
bash .claude/skills/refinement/scripts/open-seed-issue.sh \
--issues issues --namespace <命名空間> --slug <名字> --kind report \
--who <誰、在做什麼工作時撞到的> --where <撞到的檔案> \
--what <做什麼> --when <什麼時候要> --why <想解決什麼> --how <拿什麼測> \
--note '<前因後果>' [--vs <單號>=<判斷>]...
新功能那一種是 --kind feature,不問 --who 與 --where,也不查重。跑 --help 看
兩種各要哪幾格。
這幾條規矩住在哪一份,看這件工作落在誰身上。 落在這套流程自己身上(skill、它們的
腳本、常駐規則)的話,那一份是 framework-self-dev——它說出為什麼是這兩種、查重的鍵
為什麼是檔案而不是標題。落在別的東西上的話,那個東西自己的知識說了算。
它不簽 assertion、不決定領域、不開 worktree、不碰你現在這張單。產出的單會出現在下面〈四〉
那個「接下來做哪一張」的答案裡,標成 seed:——所以它拿得給另一個 session 開工,而那個
session 從 refinement 開始。
已經在進行中的單不要重問。 同一件事往下做就是了——對已經簽過的東西再問一次是儀式, 不是把關。換成另一件會改變行為的事,才重新判斷。
二、現在在哪、下一步是什麼
不要問人,讀狀態:
bash .claude/skills/driving-work-to-done/scripts/spine-loop-state.sh where --state {issue}/.spine/loop-state.json
它會說出站別、有沒有停、還剩幾輪。任何時候不確定現在在哪就跑它——問人才是不知道 自己在哪的那個症狀。
它同時回答另一個「在哪」:這個工作區還是不是這張單當初落腳的那些。 開輪次那一刻
記下的身分由領域決定(核心只當它是一組不透明字串),之後每次問路都比一次。那是一組,
不是一個——一張單牽涉幾個地方由領域宣告的命令印幾行決定,核心不預設是一個。回 DRIFTED
表示現在動手的地方跟這張單預期的不同(少一個、多一個、值不同都算,而且它會說出少了哪些、
多了哪些)——可能是有人切走了,也可能是另一個 session 正在共用同一份工作區。回
unmeasurable 表示這一次什麼都沒比到,那不是「還在原地」。
| 現在的狀況 | 下一步 |
|---|---|
| 還沒有單 | refinement:判立案、寫 assertion、凍結、開輪次 |
| assertion 凍結好了 | engineering |
| 這一輪做完了、想知道算不算達成 | verify-ac |
verify-ac 判非 PASS,原因是實作沒到 |
回 engineering |
verify-ac 判非 PASS,原因是assertion 本身錯了 |
停 assertion_wrong,回 refinement 重簽 |
| 交付紀錄寫成了 | 這條流程走完了。之後怎麼出貨是專案自己的事 |
| 交付紀錄寫成了,而後來有人指出還要改的東西 | 回 engineering。改完重走 verify-ac——那一站要量的是改完之後的東西,不是上一次留下的判定 |
| 這件事不做了(放棄、被別的取代、需求消失) | close,見下方 |
交付之後還要改,不是一個新的開始,也不是一個停點。 那張單的成功定義沒有變——變的是
「實作有沒有走到那份定義」這個問題的答案。所以它回 engineering,而不是回 refinement
重簽、也不是另開一張單:另開一張的話,同一份 assertion 會有兩張單各自宣稱自己達成了它。
回去之後一定要重走判定。 上一次的判定是對上一棵樹下的,而它會留在原地看起來仍然有效
——一份綁在舊狀態上的 PASS,跟一份剛量出來的 PASS 長得一模一樣。真的是成功的定義本身錯了
(不是實作沒到),那才是 assertion_wrong,走上面那一列。
「不做了」是一個終點,不是一個停點。 四種停點都在等人回話,而這一種已經有答案了—— 它只是不會有實作。以前這種單沒有地方去:要嘛永遠躺在待辦裡被每一次「下一張做哪個」重新 看過一遍,要嘛被手動刪掉,而刪掉的那一張在下一次有人提出同一件事時說不出「這討論過」。
bash .claude/skills/driving-work-to-done/scripts/spine-loop-state.sh close \
--state {issue}/.spine/loop-state.json --note '<為什麼不做了>' [--by <人>]
理由是必填的:一個沒有理由的關閉,跟把單刪掉的差別只有磁碟空間。已經收斂的單它會拒絕—— 那張是做完的,不是不做的,而把兩者混成同一格之後就沒有人分得出來了。
換站不需要說服任何腳本。 advance 是記下來,不是請求核准。還會擋人的只有兩類:
不可逆的動作,以及證據不足。
三、只在四個地方停
流程停下來的時候要說出是哪一種,而且要留下紀錄——停在紀錄外等於沒停,回來的人只 看得到一張不動的單,看不到它為什麼不動。
| 停點 | --kind |
|---|---|
| assertion 不對,要人重簽 | assertion_wrong |
| 新增依賴/重造既有組件/擴大 security surface | surfaced_concern |
| 連續未收斂打到上限 | unconverged_cap(record 自己會寫) |
| 需要人授權的不可逆動作 | unauthorized_action |
bash .claude/skills/driving-work-to-done/scripts/spine-loop-state.sh stop \
--state {issue}/.spine/loop-state.json --kind surfaced_concern --note '<一句話>'
這四種以外的字串會被拒絕。這四種以外的理由不是停下來的理由——特別不包括「要不要 繼續?」「要我往下做嗎?」,那不是停點,那是把判斷推回給人。
四、沒撞停點就繼續
沒有停點就往下走,不要回頭問。 這包含跨單:手上有多張單而當下這張走不動了, 下一張由同一個地方回答,不是由人指定。
# 這一張單還能不能往下走
bash .claude/skills/driving-work-to-done/scripts/spine-loop-state.sh next --state {issue}/.spine/loop-state.json
# 手上這一整棵樹,接下來做哪一張
bash .claude/skills/driving-work-to-done/scripts/spine-loop-state.sh next --across-issues issues
單張回 continue 就繼續,回 stop:<kind> 才停。跨單回 next:{那張單相對於樹根的位置}——
它是拿去開檔案用的,不是身分:一張單的身分是它的名字,位置會隨狀態改變。並且把停住
的逐張列出來、把已收斂與已交付的算成數字——不列成清單但要有數字,一個安靜的第三態下一次
就會被當成看過了。
種子單也逐張列出來(seed: 開頭),不只算進數字。它們還沒簽 assertion,所以接手的第一站
是 refinement 而不是 engineering;而它們存在的整個理由就是「拿給另一個 session 開工」,
掉出這個答案的東西等於沒開。
幾個數字加起來要等於整棵樹。 一張單的狀態不一定住在這裡——命名空間宣告了解析器的, 權威在那個外部系統。那些單走不進站別,但它們仍然是單,所以它們有自己的數字,而不是消失: 最後一行把等式寫出來,分母是位置重算認得的那組單,不由這裡再數一次。逐張的清單只有一 份,在重算產出的那份「還沒出去的單」上——這裡指過去,不抄第二遍。
那個等式不是裝飾。2026-08-21 量到,這個答案只涵蓋 700 張單裡的 555 張,差的 145 張裡有 121 張還在中間態,包含正在進行的工作;而它們不在任何一格、也不在任何一個數字裡,輸出看 起來完全正常。少算的方向剛好是「看起來事情比較少」,所以沒有人會抱怨。
排序只看狀態:最靠近交付的先做(在製品不該堆高),同一站取最近動過的那張(那是「你剛剛在做 哪一張」寫在磁碟上的唯一痕跡)。命名空間叫什麼、單號多大都不參與。
流程自己的工具擋住你的時候,那不是停點。 一道關卡對一份已經被實證是對的交付判紅,是那 道關卡的缺陷,處理是修它然後繼續——把摩擦寫進可以改的那部分、把修法做掉。停在「被框架擋住了,你要 不要開一張單」等於把工具的問題變成人的決定,而工具是來幫忙的,不是來收費的。
同一件事的另一面:關卡判紅而你想繞過它的時候,先把產出改成它接受的樣子,不要把「用哪個 繞法」做成問題丟回去。關卡是權威,繞過去才需要人點頭。
寫摩擦的時候指名是哪一支
「這一套東西有沒有讓工作更常做對」只能從真的跑過的那幾次讀出來,而寫的時候不指名,事後
就讀不出來。2026-08-13 量到的:1,721 份單的正文裡有 345 份寫了摩擦,其中 323 份文件裡同時
出現某支 skill 的名字——但那是同現,不是歸因。一張走主流程的單本來就會提到 refinement 與
engineering,所以那 323 份回答不了「是哪一支害的」。素材有量,形狀不對。
所以摩擦與幫助各記一行,就寫在可以改的那部分裡,格式是:
<!-- SKILL-UTILITY: {skill 名} | helped|hurt | {一句話:發生了什麼} -->
兩個方向都要記。 只記摩擦的話,讀出來的結論永遠是「這套東西都在擋人」——而 2026-08-13 那一趟裡,兩次擋下真正不可逆的後果(公司代號要進公開 repo、副本漂開)跟六次 擋下正確的工作,是同一批紀錄該說出來的兩半。
讀它不需要工具:grep -rn 'SKILL-UTILITY' <單的根目錄>。不要為它寫一支腳本——它不擋任何
人,它是拿來讀的;一個會擋人的 utility 判準會在第三次被繞過。
它記得到的只有走過單的工作。 沒有立案就用掉的那些(查一下、跑個報告、看一次收件匣) 不留這種紀錄,所以這一份紀錄天生偏向「會改程式碼的工作」。這是已知的覆蓋缺口,讀結論的時候 要把它說出來,不要把「沒有紀錄」讀成「沒有問題」。
反覆出現的失敗長這樣,看到就是退化了:
- 把連續的意圖收斂成單步,做完一步就停下來報告;
- 停在階段邊界等人說「好」;
- 用一句「要不要繼續?」把判斷推回給人。
四之二、每一張本地開的單都帶著號
沒有號的單排不進待辦、也沒有一個穩定的東西給別的單引用,而它會一直長出來,到某次盤點才 被一次找到一批。號不需要人記,也不需要任何新的宣告——開單那條路自己算:
bash .claude/skills/driving-work-to-done/scripts/next-ticket-number.sh \
--issues <單的根目錄> --namespace <命名空間>
判準是樹裡已經有的東西:宣告了狀態解析器的命名空間,它的單來自外部系統,號也一樣來自 那裡——本地鑄造一個出來,下一個人會拿它去那個系統搜,然後搜到別的東西或什麼都沒有。沒宣告 的命名空間,本地就是權威,取現有最大號加一。
照著它算一個號再自己建目錄,是開單的第二條路。 走這一條的話,上面〈手上正在做別的
事〉那幾件事沒有東西替你做——種類、6W、查重、判斷,四樣都要自己做完再建目錄。那一份說
清楚要做什麼的知識跟著這件工作落在誰身上走:落在這套流程自己身上的是
framework-self-dev。
所以「這件事該開在哪個命名空間」有一個乾脆的答案:改框架本身的工作(skill、腳本、規則)
就是框架的工作,即使它講的內容是某一家公司的;產品工作的單來自那家公司的系統,我們從來
不鑄造那種號。2026-08-08 有兩張改 .claude/skills/ 的單因為「內容跟某家公司有關」被放進
那家公司的命名空間,於是它們永遠拿不到號。
四之三、一張單住在哪
位置是狀態的投影,而 --execute 會把投影落地——但只落在它證明得了身分的那幾張上。
重算算出每一張單該在哪一格,把那個答案寫進那張單自己的 {單}/.spine/placement.json 與人看
的那份清單,然後把搬得動的資料夾搬過去。
搬得動要先證明,四項缺一就不搬:這張單是鏈頂、身上有 .spine/、它的單號在整棵樹裡只有
一條路徑、目的地還不存在。任何一項不成立就留在原地,而報告會逐張說出卡的是哪一項。
四項各自擋掉一種真的發生過的事。DP-661 曾經把搬動整個拿掉,因為那一半自己製造了它要解決的
問題:一次沒搬完的搬動留下一個空殼,而那個空殼從此是同一個單號的第二條路徑,於是同一張單
有兩份 placement.json 說不同的話。這一版不靠「搬得比較小心」把它擋回去——擋回去的是
那四項先決條件,它們在搬之前就答得出來。
搬動用檔案系統的更名,不是 git mv。 單的那棵樹是好幾個工作階段共用的,而 git mv 會把
改動放進共用的索引,別人下一次送出就會把它一起帶走。更名之後 git 自己認得出改名,而索引一個
字都沒有被動過。
搬完之後,搬它的那一支自己把那次搬動 commit 進去。 少了這一步,被搬的單只活在一個沒有
任何 commit 指著的路徑上——舊路徑在 git 眼裡是 D、新路徑是 ??;任何人跑一次
git clean -fd,那些目錄整個消失,而 git checkout -- . 只救得回舊路徑那一份。這件事
2026-09-06 一天內發生兩次,一次在釋出尾段、一次在關單那條路——所以修的是搬動這一支,
不是它的某一個呼叫端:三個呼叫端各修一次的話,漏掉的那一條不會爆炸,只會安靜地少一筆。
那顆 commit 只帶被搬的那幾張單的路徑(舊的與新的各一條),因為單的那棵樹是共用的:
git add -A 收整棵樹,不帶 pathspec 的 git commit 送出整個索引,兩者都會以這一次搬動的
名義帶走別人手上還沒 commit 的東西。沒有搬動的那些重算一個 commit 都不寫——而重算被
記一輪順手觸發是常態,所以那個條件不是省事,是這一段能掛在這裡的前提。
單的那棵樹不是 git repo 的時候它說出來,然後照常完成重算:問不到不得讓它安靜地不做。
路徑投影的是歸屬,格子投影的是鏈頂那張單的狀態。 一張單有母單的話,它住在母單底下:
{命名空間}/{格}/[{日期}/]{鏈頂}/…/{它自己},中間每一層都是一個單號。母單鏈有幾層就有
幾層,不並排成一層。
子單不因為自己的狀態算到別的地方去。 同一個母單底下的子單狀態各不相同是常態——各自
算一格的話,那個母單就被切成好幾塊,而它正是人拿來理解工作的單位。子單自己的狀態沒有消失,
它算得出來,寫在那張單的 placement.json 與人看的那份清單上。
鏈上出現、樹裡還沒有的母單,重算逐張說出來,不補。 以前它會把那些目錄造出來——而一個 只當路徑用、沒有內容的母單層,翻樹的人在它身上讀不到任何東西,機器又會把它算成一張單。 要不要開那張單是人的決定。
單底下不是每一個目錄都是單。 帶著 .spine/ 的才是。少了這道判準,舊層留在單裡的那些
工作目錄會被一個一個算成單,然後被指名成一個不存在的重複。
但「不是單」與「不要穿過去」是兩件事。 不是單的目錄照樣要走進去看——樹上有一些只當路徑
用的中間層(沒有 .spine/、沒有正文,只有一個子單目錄,多半是過去那些搬動留下的),而在
那裡停下來的話,底下的單就永遠不進清單。那是一個會自己維持住的洞:孫單看不見 → 它的歸屬鏈
沒有人讀 → 孫單繼續看不見。2026-08-21 真樹上因此漏掉 19 張。
位置是狀態的投影,不是第二個權威。 一張單在哪一格由它自己的狀態算出來——所以要問「這張 單走到哪了」,讀那份紀錄,不要讀它躺在哪個資料夾。手動把一張單搬到別處,下一次重算會把它 搬回狀態說的那一格,而它搬不動的那幾張會留在原地並被指名。
流程搬。 record 寫完輪次就重算一次,把每一張單該在的那一格算出來、寫回去、搬過去:
bash .claude/skills/driving-work-to-done/scripts/place-issues-by-state.sh --issues <單的根目錄> --check
bash .claude/skills/driving-work-to-done/scripts/place-issues-by-state.sh --issues <單的根目錄> --execute
兩種模式的差別是寫不寫、搬不搬:--check 什麼都不寫、也不搬,有落差就 exit 1;
--execute 把推導結果寫回每一張單、重寫清單,並把四項先決條件都成立的那幾張搬到算出來的
那一格。推導結果跟上一次一樣的那些單,--execute 一個字都不寫——每跑一次就把整棵樹的
時間戳全部改寫一次,弄髒的是共用的工作區,而那些檔案的內容一個字都沒有變。
格子叫什麼不寫在散文裡。 那份清單是那支腳本自己的,--check 會把每一格與各裝幾張印
出來。抄一份到文件裡就有兩個答案,而漂掉的那一刻沒有人在看。
推導出來的結果會寫回單現在所在的那個目錄({單}/.spine/placement.json)——不是它「該
去」的那條路徑。所以一張單只有一份紀錄,而讀那份紀錄的程式走到單所在的地方就讀得到它。
位置給人看,那份紀錄給程式讀,
兩者同一次推導產出,所以不會有第二個答案。任何要問「這張單收斂了沒」的程式讀那一份,
不准從資料夾名推狀態——那條路徑一多一層資料夾就會靜靜地失效。
兩層推導,問不到就說出來
走過主流程的單:
{單}/.spine/loop-state.json就是權威,不需要問任何人。沒走過的:問那個命名空間自己宣告出來的解析器。宣告長這樣,掃的是所有 skill 的
SKILL.md:<!-- {任意前綴}-ISSUE-STATE-{命名空間}: {命令} -->核心不認得任何一個外部系統,也不認得任何一個外部狀態名。 換一個地方只要換那一行。
順序不能反:主流程的答案是本地的、確定的,讓一趟網路往返有機會覆蓋它,等於把權威交給一個會 逾時的東西。問不到不得讓重算說謊——那些單落進「等人歸位」那一格並被指名「這次沒問到」, 不沿用上一次的答案,也不讓整支停掉。
兩層都問不到的目錄(主流程之前的舊層、還沒開輪次的種子)不參與位置判定,留在原地,但 數量每次都會被印出來——那個數字由那一次重算自己印,不寫在散文裡。把它們全掃進「等人 歸位」那一格會讓那一格裝進幾百張,而那一格存在的意義是「機器問過了、答不出來、等人歸位」, 一個沒人看得完的抽屜等於沒有這一格。一個不被判定的第三態如果安靜,下一次就會有人以為那些 目錄都被檢查過了。
終局那幾格
做完了、出去了、不做了是三件事,不是同一件。「不做了」以前沒有地方放,於是它要嘛永遠
躺在待辦裡被每一次〈四〉重新看過一遍,要嘛被人手動刪掉——而刪掉的那一張,下一次有人提出
同一件事時說不出「這討論過,結論是不做」。走進那一格的方式是上面〈二〉的 close。
終局那幾格底下多一層日期:任何終局都該知道時間,一格裝一百張只能照名字翻。查不出日期的
放進 undated/——把整理它們的那天填進去,會讓一個看得出來的空白變成一個看不出來的謊。
但日期那一層不判定任何事。 它是給人翻的,不是第二個權威——一張單走到哪一種終局,看的是 它自己的狀態與紀錄,不是它落在哪個資料夾。
{單的根目錄}/OPEN.md 是同一次重算產出的人看版本:所有還在中間態的單,逐張帶著它在哪一格、
位置依據什麼推出來、上次動過多久、是不是自己的單。停滯只出現在那份清單上——一張在同一
格待了兩個月的單,那一格就是它正確的位置,「這樣算不算太久」由看的人決定,不由門檻代人
搬走它。
五、載入領域知識:這類工作怎麼算 done
refinement 判完要立案之後,判斷這件工作屬於哪個領域,載入對應的知識,並把載了什麼
記在單的狀態裡。
| 這件工作 | 載入 |
|---|---|
| 會改到程式碼、要進版控 | swe-knowledge |
| 不會改程式碼(報告、調查、文件、資料分析) | 沒有適用的領域 |
領域的決定就在開輪次那一步,不是之後補的欄位。這張單的改動會落在哪些地方也是:
bash .claude/skills/driving-work-to-done/scripts/spine-loop-state.sh init \
--state {issue}/.spine/loop-state.json --pack swe-knowledge \
--where <工作區路徑> [--where <另一個>]...
bash .claude/skills/driving-work-to-done/scripts/spine-loop-state.sh init \
--state {issue}/.spine/loop-state.json --pack none --why '<為什麼這件工作沒有領域完成條件>'
--where 給幾次就是幾個地方,值是不透明字串——核心把它記下來,之後每次問路原樣交還給
領域的腳本去求值。它不是「我現在站在哪」,是「這張單的改動會落在哪」:一張單住在
issues/ 而程式碼落在某個產品 repo 是常態,兩者不是同一個地方。宣告不出來就不開輪次,
因為從當下位置推出來的那一份對這種單永遠是錯的那一邊,而它之後每次比對都自洽。
需要知道這張單落在哪裡的時候讀這一支,不要自己再推一次:
bash .claude/skills/driving-work-to-done/scripts/spine-loop-state.sh landing \
--state {issue}/.spine/loop-state.json
DP-482 之前開的單狀態裡沒有這份宣告,問路會回 unlanded。補記(或在落腳處真的變了的時候
改記,改要帶人的原話):
bash .claude/skills/driving-work-to-done/scripts/spine-loop-state.sh land \
--state {issue}/.spine/loop-state.json --where <工作區路徑> [--authorization '<人的原話>']
init 還會跑那個領域宣告的開工條件——條件寫在 pack 自己的知識裡,核心只負責找到它、
跑它、不成立就拒絕開輪次。所以核心不認得任何一個領域的條件,換一個領域不用動核心。
它也會拒絕在同一個地方開第二輪:同一棵單的目錄樹裡已經有一張走到終局站別的單,而它記下的 身分與現在求出的有任何一個交集,就不開。交集就夠了——共用其中任何一個地方,新的一輪 就疊在別人的歷史上,最後只能一起出去。這件事 2026-08-03 真的發生過一次,三份交付紀錄釘在 同一個 head,而中間沒有任何一步說過話。拒絕訊息會指名是哪一張單、交集在哪,以及往下走的 兩條路(把那張單走完,或換一個工作區)。
中途要改判領域(做著做著發現它其實會動到程式碼)用這一支,它是覆蓋不是追加:
bash .claude/skills/driving-work-to-done/scripts/record-knowledge-pack.sh record \
--state {issue}/.spine/loop-state.json --pack swe-knowledge
「沒有適用的領域」是一個被記下來的選擇,不是欄位空著。 兩者在檔案裡長得不一樣, 在報告裡也要長得不一樣。
指名的 pack 載不到就停,不要照常往下走。 散文說「去載 X」而 X 不在,是完全安靜的:
routing 照樣把工作分派出去,只是分派到一份從未被讀取的程序。這個 repo 有過六支 skill
整段存在期間零載入而沒有人發現。record-knowledge-pack.sh 會拒絕一個解析不到 SKILL.md
的 pack 名字——它擋的就是這件事。
五之二、這一趟是誰在跑
init/advance/record 寫輪次狀態的時候,順手把「這一趟是誰在跑」記進
{issue}/.spine/loop-state.json 的 holders[]。核心不認得任何一種對象——它掃所有
SKILL.md 找這一行,跑它,把印出來的第一行原樣記下來:
<!-- {任意前綴}-ACTOR-IDENTITY: {命令} -->
形狀跟 ISSUE-STATE-{命名空間} 是同一套,但沒有後綴:「現在是誰在跑這一趟」只有一個答案,
而「這張單在哪一格」每個命名空間各有各的權威。沒有人宣告是一個答案,不是壞掉——那個
環境裡沒有東西回答得出這個問題,於是它記下這句話,往下走。
為什麼掛在這三個動作上:它們是流程走不過去就不能繼續的三步,所以「凡是走過流程的單都有」。 掛在一份自願寫的宣告上的話,覆蓋率就是「記得寫的那幾個」。
不覆寫,逐個對象各佔一格。 只有開輪次那一步寫的話,一張單換手之後那一格永遠是開單 的那個人;每次都覆寫的話,最後一個路過的人就成了「誰在做」。而覆寫的那一版連「不只一個 對象接著同一張單」都表達不出來——那正是要被看見的東西,不是要被收斂成一個的東西。
推不出來的時候記下理由,不記一個空字串。 空字串跟「沒有人接」在檔案裡長得一模一樣, 而它們要人做的事不同:一個是去看為什麼推不出來,一個是去找人接。
這一格不判定任何事。 它不改變任何一張單走到哪一站,也不是第二個回答「這張單在哪一格」 的地方——那個答案只有一個權威。誰讀它、怎麼印,是讀的那一端的事。
六、這一組東西要能整包搬走
driving-work-to-done + refinement + engineering + verify-ac + 領域 pack,複製到
另一個 repo 就能用。所以:
- 每一句指令都指向這一組 skill 目錄裡的東西,不指向 repo 根目錄的腳本、常駐規則、 或任何 git hook。那些在 claude.ai 與 Cowork 不存在——而指過去的那一刻不會有人報錯, 只會安靜地讀不到。
- 不假設命名空間叫什麼。
issues/{命名空間}/{單}/的中間那一段是誰決定的都不影響判定。 - 不假設領域。核心的散文裡不出現只有軟體工程才成立的詞——它們屬於
swe-knowledge, 不屬於這裡。
這三件事沒有關卡在守,是刻意的。 以前有一道 gate-layer-vocabulary 掃三行宣告,
判「哪些詞不得出現在哪幾支 skill 的散文裡」——那是一段散文在規定另一段散文:它擋不住任何不可逆的事,
但每一次無關的改動都要付錢。判準留在這裡給寫的人讀,寫錯層的代價由「有人把那一層單獨
搬走的時候帶不動」承擔,不由一道關卡代人承擔。
三種寫錯層的方式:
- 核心的散文出現只有軟體工程才成立的詞(branch、pull request、merge、CI、deploy)。
唯一的例外是凍結用的
git commit:凍結的簽名就是那個 commit,跟領域無關。 - 反過來,核心自己的詞出現在某一個產品的知識裡(停點怎麼分類、輪次狀態檔叫什麼)。
一支產品 skill 重講這些,那段話就不屬於它——它在別的產品也成立,複製過去的那一份會跟
這裡漂開。例外是指過來:說「這件事走
driving-work-to-done」是路標,不是複製; 判準是它有沒有自己重講一次那些詞的意思。 - 領域 pack 出現只有某一家公司才成立的東西。公司樣式由每一家自己的
workspace-config.yaml說了算,不在這裡抄第二份。
這裡不做的事
- 不判定達成。那是
verify-ac,它有 oracle。 - 不決定量測命令。那是
engineering的活區。 - 不出貨。交付紀錄寫成就是這條流程的終點,釋出尾段屬專案私有。