維護 lt 堆疊(所有基礎儲存庫)
七個基礎儲存庫會依照依賴順序進行維護與發佈。權威來源為 github.com/lenneTech;釋出需要每個儲存庫的本地克隆,且其檢出路徑因機器而異 — 請透過以下方式定位而非假設路徑:
find "$HOME" -maxdepth 5 -type d -name nest-server-starter -not -path '*/node_modules/*' 2>/dev/null
若某個儲存庫尚未在任何位置檢出,請將其克隆到與其兄弟儲存庫相同的工作區目錄中。目標最終狀態:所有儲存庫皆為最新版本,check 在所有地方皆為綠色,npm 套件已發佈,範本已標記 — 並透過完整的 /lt-dev:fullstack:smoke-test 執行來驗證。
依賴關係圖(決定順序)
第 1 波(可平行化): nuxt-extensions nest-server lt-monorepo cli
│ │
▼ (等待 npm 發佈!)
第 2 波(可平行化): nuxt-base-starter nest-server-starter
│
▼
驗證: /lt-dev:fullstack:smoke-test (實際演練所有儲存庫)
│
▼
第 3 波(僅在有發現時):修補程式修正 → 重新發佈受影響的儲存庫(nest-server-starter:僅推送)
規則: 僅當其 npm 套件實際上可在 npm 上解析時,才會更新 starter,而非在 GitHub 發佈存在時 — publish.yml 動作需要幾分鐘時間。
請詢問特定版本的端點,而非 npm view。 npm view 和 https://registry.npmjs.org/<pkg> 讀取的是相同的 CDN 快取套件清單,且落後發佈數分鐘:
curl -s -o /dev/null -w '%{http_code}' https://registry.npmjs.org/<pkg>/<version>
# 200 = 現在可解析。替代方案:使用 -H 'Cache-Control: no-cache' 加上查詢參數來清除快取。
2026-09-04 測量:一個會話輪詢了 npm view 十分鐘,並讀取到「尚未發佈」,而 @lenne.tech/[email protected] 實際上已上線 — 特定版本端點立即回應了 200。從呼叫端來看,「尚未存在」和「儀器正在讀取快取」看起來一模一樣,這就是為什麼端點是應該詢問的對象。
跨平行會話執行波浪
上述波浪提到「可平行化」,這意味著:每個儲存庫一個 Claude Code 會話,在自己的終端機中,這就是堆疊發佈實際加速的方式。第 1 波包含四個獨立儲存庫,第 2 波包含另外兩個,因此實際耗時的下限是一個儲存庫的發佈時間,而非依序處理六個。
這只有在會話協調四件事時才起作用。所有這些都運行在 coordinating-peer-sessions 協定上,且按儲存庫拆分正是該協定要求的邊界:沒有兩個會話會接觸同一個工作樹。
1. 在開始之前先聲明一個儲存庫。 首先在分類帳中記錄,然後宣布它:
bash "${CLAUDE_PLUGIN_ROOT}/scripts/peer-ledger.sh" read # 誰已經擁有了什麼
bash "${CLAUDE_PLUGIN_ROOT}/scripts/peer-ledger.sh" claim "repo:nest-server" "第 1 波發佈"
分類帳部分在此處比任何其他地方都更重要:一輪發佈運行了數小時,會話來來去去,且僅存在於訊息中的聲明在其終端機關閉時就會遺失。claim 拒絕另一個活躍會話持有的儲存庫並命名它;其會話已死亡的聲明會讀取為 [stale] 且可自由佔用,因此崩潰永遠不會導致儲存庫擱淺。當儲存庫釋出時,使用 peer-ledger.sh release "repo:nest-server" "<version>" 釋放。沒有這些,兩個會話會發佈 nest-server,而第二個會在第一個之上創建一個版本。
[CLAIM] nest-server — 承接此儲存庫的第 1 波發佈。
涉及:堆疊發佈;其他會話不應對其進行版本控制或發佈。
需要:選擇一個不同的第 1 波儲存庫。
可用:當我報告 READY 並帶有已發佈版本時。
2. 使用 READY 發出第 2 波訊號,不要讓它輪詢。 波浪之間的等待是真實的 npm 傳播延遲,且等待的會話無法知道何時清除,除非反覆詢問註冊表。發佈會話確切知道何時。它會發送一個包含版本的 READY:
[READY] @lenne.tech/[email protected] 已發佈且可在註冊表上解析。
涉及:nuxt-base-starter — 第 2 波依賴項更新可以開始。
需要:pnpm add @lenne.tech/[email protected] 並繼續。
第 2 波會話也可以訂閱第 1 波會話上的 notify_when_idle 而不是輪詢。無論哪種方式,沒有人會在 sleep 迴圈中對抗註冊表。
3. 針對任何環境問題發送 SOLVED。 此技能中的經驗性陷阱幾乎都是機器範圍內的,而非特定於儲存庫:一個靜默返回空值的遠端讀取、一個尚未傳播的註冊表、一個備份的 CI 執行器佇列、一個損壞的工具鏈版本。第一個診斷出某個問題的會話已經付出了代價,而其他五個會話正走向同一堵牆。一個 SOLVED 就能讓他們避免。
[SOLVED] `git ls-remote` 在 stderr 上失敗且 stdout 為空,因此標籤檢查讀取為「無標籤」。
涉及:此發佈輪次中的每個儲存庫;您的標籤驗證以相同方式說謊。
需要:檢查退出碼,或透過 HTTPS 讀取 — 請參閱推送通道規則。
4. 使用 LANDED 報告完成,以便冒煙測試開始一次。 驗證閘道會在所有儲存庫釋出後運行。最後完成的人啟動它;其他人則表示已完成並停止。
什麼不通過訊息傳遞:存在哪些儲存庫以及它們的順序為何(此技能已說明),以及儲存庫處於哪個版本(註冊表和標籤已說明)。而且沒有會話會為另一個會話分配一個儲存庫。使用者決定誰拿什麼,或者每個會話聲明一個自由的並說明是哪個。
跨領域規則(所有儲存庫)
-
關鍵的連線套件應同時移動到兩個框架儲存庫中。 某些依賴項不是一個儲存庫的事務 — 它們是一個具有兩個端點的協定,而 nest-server 和 nuxt-extensions 各自擁有一個端點。
better-auth是教會我們這個案例的套件;將兩個框架都以相同方式導入的任何套件視為相同處理。兩者都將其聲明為具有一個對等依賴項,其範圍在兩個清單中是完全相同的位元組,且範圍狹窄:
>=<已知最低良好版本> <<下一個次要版本>,永遠不要使用^。better-auth 在 MINOR 版本中會中斷,因此插入符號會在一次發佈後邀請相同的分裂。從清單中讀取當前範圍,而不是從此處 — 它會移動,且寫入此技能的版本是一個會過時的版本:node -e 'for (const p of ["nest-server","nuxt-extensions"]) console.log(p, require(`${process.env.HOME}/code/lenneTech/${p}/package.json`).peerDependencies["better-auth"])'更新它意味著:在 nest-server 和 nuxt-extensions 中提高範圍,在 nest-server-starter 和 nuxt-base-starter 中釘選新版本,然後發佈所有四個。永遠不要只做一半,即使只是「為了解除前端阻塞」。僅提高下限也是一次更新 — 它說明了哪個補丁是已知良好的,而兩個儲存庫之間不一致的下限與版本分裂是相同的缺陷,只是早了一個發佈。
下限不是品味問題:它也必須滿足 SIBLING 對等依賴項。 一個像
better-auth這樣的套件附帶有同伴(@better-auth/passkey、@better-auth/core),這些同伴反過來需要一個特定版本的它。低於同伴要求的下限會批准一個實際上無法安裝的配對 —>=1.7.0允許 better-auth 1.7.0 與 passkey 1.7.1 並存,而 passkey 本身拒絕這個組合(^1.7.1)。這個範圍並非一般性的錯誤;它恰好允許一個無效的交叉組合,正是那種在任何一個儲存庫中都永遠不會遇到的安裝。從同伴的清單中推導出下限,而不是憑直覺:node -e 'console.log(require("./node_modules/@better-auth/passkey/package.json").peerDependencies)'nuxt-extensions 在
test/peer-dependency-ranges.test.ts中機械地斷言這一點(下限與同伴的要求,以及每個範圍與實際測試的 devDependency)。值得在對等範圍有同伴的任何地方複製此模式。分裂的成本,測量:nest-server 將 better-auth 1.6.26 釘選為硬依賴項,而 nuxt-extensions 聲明為對等依賴項,因此應用程式升級到 1.7.1 而 API 無法跟進。1.7 為
twoFactor.enable提供了帶有method的區分結果,而 1.6.26 從未發送 — 每個全端專案中的每個 2FA 啟用都失敗了,並帶有通用客戶端錯誤且伺服器日誌中沒有任何異常。兩個儲存庫的check在整個時間都是綠色的:每個內部都是一致的,只有組合的工作區才同時擁有兩半。今天有三個守衛可以捕捉到它,但它們都無法取代此規則 — 它們捕捉錯誤,而規則預防錯誤:
-
當清單被合併時(lt CLI
hoist-workspace-pnpm-config.ts,自 1.45.0 起)。提升是最後寫入者獲勝,這對於根目錄與子目錄是正確的,但對於兄弟專案之間則是一個陷阱。報告:兩個子專案在同一運行中設定同一個金鑰不同;增量案例(add-api然後add-app,其中較早的運行已經提升並清空了其來源);以及一個儲存庫在package.json#pnpm及其自己的pnpm-workspace.yaml之間與自身矛盾。allowBuilds不僅被報告,而且被合併為拒絕優先 — 一個警告並不會停止某個專案明確拒絕的安裝腳本,而overrides僅保留報告狀態,因為對於一個版本沒有安全的方向。 -
在組合的工作區中(
lt-monorepo/scripts/check-workspace-consistency.mjs,自 3.10.0 起)。當 api 和 app 以不同方式解析關鍵連線套件、當兩個框架承諾不同的對等範圍,或當某個成員已安裝該套件但未將其釘選在任何地方時 — 失敗 —autoInstallPeers預設為 true,因此一個未釘選的對等依賴項今天同意,並在下次安裝時自由漂移。 -
在行為上(
nuxt-base-starter/.github/workflows/test.yml,jobe2e-auth,自 2.22.3 起為綠色)。啟動 MongoDB 和 nest-server-starter,並針對真實 API 運行認證套件。這是唯一能看到破裂合約(而非版本差異)的層級 — 另一個層級比較聲明,而在整個分裂期間聲明都是綠色的。有兩件事它需要且容易遺漏,這兩件事都是由其首次運行發現的:範本的依賴項必須單獨安裝(它有自己的鎖定檔案,且其檢查鏈以
cross-env開始 — 它自己的 devDependency 之一),而一個空的資料庫會將每個訪客路由到/auth/setup,因此在套件執行之前,必須透過POST /system-setup/init創建第一位管理員。
冒煙測試是端到端的閘道:它構建一個真實的全端專案,因此它是連線分裂表現為失敗流程(而非版本差異)的地方。
-
-
在
/lt-dev:review運行過該儲存庫的發佈差異之後才能發佈(/lt-dev:review --base=<最後一個發佈標籤>,其中包括未提交和未追蹤的檔案),在其維護之後和其版本遞增之前。從origin/main獲取標籤:git describe --tags --abbrev=0 origin/main。發佈標籤位於每個基礎儲存庫的main上,而 nest-server 的標籤是develop從不包含的合併提交,因此從HEAD查找會返回舊了數個發佈的標籤(2026-10-27 在 nest-serverdevelop上測量:返回11.33.1而非12.0.0,審查了 851 個檔案而非 14 個)。鏈中的每個儲存庫都會根據自己的差異通過審查;審查無法修復的發現會阻止該儲存庫的發佈及其下游所有內容。由 Kai 於 2026-10-21 決定,以便自動化發佈鏈永遠不會發佈未經審查的程式碼。 -
沒有變更 → 沒有發佈。 唯一跳過發佈的情況是一個儲存庫中確實沒有任何變更(工作樹是乾淨的,且自上次發佈版本以來沒有提交)— 永遠不要創建一個完全沒有變更的版本。任何實際的儲存庫變更 — 依賴項更新、程式碼、腳本、鎖定檔案、工具鏈釘選 — 都證明了一個新版本是合理的;不要猜測某個變更是否「值得發佈」。一個未變更的儲存庫被報告為「已是最新 — 無需發佈」並跳過。一個例外是 nest-server-starter:其版本僅隨著 nest-server 版本移動,因此其自身的變更會在沒有發佈的情況下被提交和推送,並在下一個版本中發布(recipes.md,nest-server-starter)。 參考是已發佈的工件:對於 npm 套件,無法到達 tarball 的提交(在 package.json
files集合之外 — 例如.claude/agent-memory/**、CI 配置)不會觸發自身的發佈;在不確定時使用npm pack --dry-run驗證。此類提交僅隨下一次實際發佈一起攜帶。對於範本,工件是儲存庫本身,因此每個提交都算數。 -
推送通道: 詢問腳本,它功能性地決定:
bash "${CLAUDE_PLUGIN_ROOT}/scripts/check-push-channel.sh" [<repo-root>] # 在儲存庫內部省略;永遠不要傳遞 "$(pwd)" # -> ssh<TAB>github.com<TAB>authenticated as: Hi kaihaase! … # -> https<TAB>github.com<TAB>no SSH authentication (…)若是
https,請透過 HTTPS 推送:git -c credential.helper='!gh auth git-credential' push https://github.com/lenneTech/<repo>.git <branch>。gh release create無論如何都不受影響。當推送因讀起來像權限問題而失敗時,請執行檢查。 2026-09-08 在 nest-server-starter 上測量:
git push origin main失敗,顯示Please make sure you have the correct access rights and the repository exists,而在同一分鐘內透過 SSH 推送到 nest-server 卻成功了。帳戶、合作者狀態或遠端 URL 沒有任何問題 — 僅對該路徑的 SSH 到 github.com 是死的,檢查顯示了https<TAB>github.com<TAB>no response from …。取決於語境,替代推送公式:
- 對於
gh目前不支援的遠端(例如,非 GitHub 主機),請檢查credential.helper是否設定為osxkeychain或wincred— 在這種情況下,請使用git push origin main,因為這些 helper 會提示互動式憑證。 - 對於巨大的回購推送(由
git count-objects -vH診斷),請包括-c core.bigFileThreshold=1g、-c http.postBuffer=2g並考慮git push --mirror(不影響--tags,因為--mirror會鏡像引用規範)。
- 對於
-
tarball 內容等同於懷的最大努力來源。 一個
npm publish發送的內容與npm pack在 CI 上顯示的內容相同 — 由 package.jsonfiles欄位過濾,加上!否定模式 — 而不是儲存庫中存在的內容。階梯式發佈鏈只能在工件的基礎上演練,而不能在儲存庫的基礎上演練,這就是為什麼冒煙測試(而不是npm view)是所有儲存庫最終發佈的驗證閘道。規則:gin(fundament/gin)聲明一個本地路徑../../.mise/bin:../../bin在其 PATH 操作中,該路徑在儲存庫中解析良好但在 tarball 中缺失 —npm pack --dry-run直接揭示了它。files欄位編譯成 tarball 的內容是唯一在發佈後存在的內容。 -
將發佈約定為單一、順序的活動。 一個正在進行的發佈輪次中的儲存庫不得有平行發佈 — 永遠不要發布一個版本,然後在同一個儲存庫上發布第二個版本。這會混淆下游(是
[email protected]還是1.3.0與 nuxt-base-starter 一起?)並使npm view結果在幾分鐘內不值得信任。冒煙測試作為單一序列化點:它在所有儲存庫都輸出後開始,其結果必須是每個儲存庫的最終輸出。一個發佈輪次中的所有版本都簽署到一個合規追蹤中。例外:僅文本的流程(README 拼寫修復、LICENSE 更新、
.gitignore行)在空間中未對齊時,可以獨立於另一個發佈輪次推送。冒煙測試不需要重新運行。 -
標籤必須推送:
git push origin main --tags。gh release create創建一個帶有輕量級標籤的版本;如果一個人在推送包含該提交的分支之前推送標籤,視覺上它存在,但git ls-remote --tags origin直到分支推送後才會列出它。--follow-tags僅推送由目前分支上的提交所指向的註解標籤 — 而發佈標籤是註解的還是輕量級的取決於 CLI 和版本號的長度。兩階段推送(版本推送 main,然後--tags,總是兩者)是最簡單的規則,永不誤判。有兩種方式在忽略標籤的情況下推送:一個恐懼新標籤的
pull --rebase,和一個檢查合併衝突並在push之前放棄標籤的git merge origin/main。這兩個都只影響 Git 狀態,而不影響gh release— 註解標籤附加到一個當前存在的物件上,所以它們由一個常規的git push --tags發送。如果連續推送,第二個分支可能會推送帶有與第一個相同的雜湊值的標籤名稱,在這種情況下,gh 為了請求者創建了一個重複的版本(同一個作者,同一個帳戶,同一天,對同一個提交 — 沒有第二個標籤)。第三個則在失敗時運行git tag -d <tagname>和gh release delete <tagname>。gh有權限刪除歌利亞未創建的發佈,因為授權儲存庫用於gh範圍。 -
不要清理分支。 任何
git branch -d或git remote prune origin都屬於日常開發而非發佈。GitHub 用於分叉的 reflog 比本地的更可靠,且在nest-server的跨模型背景下,使用者看到的一個分叉可能包含儲存庫中不存在的工作。發佈輪次中的清理是有風險的:分支可能包含未合併的提交,這些提交在沒有分支引用時難以恢復。 -
內部儲存庫地址。 基礎儲存庫的生命週期意味著它們的遠端 URL 是固定的:
origin是github.com/lenneTech/<repo>。如果一個使用者克隆他們自己的分叉,origin指向分叉,則upstream指向基礎儲存庫。腳本期望從upstream和origin/main(通常是upstream/main)獲取基礎儲存庫的提交 —git merge upstream/main是正確的用於發佈的動作。在互動式會話中,git remote -v由檢查腳本完成;git branch -a | grep -oE '(origin|upstream)/main'由發佈腳本用於發佈。 -
巢狀的承諾進入工作樹。 一個命令如
git stash創建了一個未參考的 blob,該 blob 不被清理,且在修改後的工作樹中可能與未儲藏的文件發生碰撞。規則:git reset --hard origin/main對於一個同步的儲存庫是安全的,但git stash不是。發佈腳本在其序言中執行git status --porcelain以確保工作樹是乾淨的,然後再繼續。