- name
- elf-team-rule
- description
- ELF EXPRESS 團隊 git 分支規範(貢獻型 fork/私有 fork/自有專案)與 Superpower 開發方式的 skill 載入、Plan 容量規則。
當任務涉及在任何 repo 開分支、合併、同步上游、發佈 release、送 PR 給原作者、建立新 repo 或新 fork、補 BRANCHING.md、
設定分支保護、核對 release 分支 CI/CD 為什麼沒過、檢查分支與來源(上游/dev/release/main)是否對齊、
正式版出問題要緊急修正(hotfix)、使用者要求「直接改 release/main」、
多個 AI 同時開發、PR 合進 `dev` 的合併方式、整理 commit、`.gitattributes`/行尾、
或建立/拆分一個 Task 很多的 Plan 時觸發。
- metadata
- {"version":"1.3.1","owner":"Elf Express"}
# ELF EXPRESS 團隊規範:git 分支(elf-team-rule)
> 這份 skill 是團隊分支規範給 AI 的執行版本。正本在 Linear:
> [分支規範:三種 repo 類型](https://linear.app/elf-express/document/分支規範三種-repo-類型貢獻型-fork私有-fork自有專案-128bf6e5e1f9)。
> 兩者衝突時以 Linear 為準,並回報使用者。
>
> 詳細流程在 `references/`:三種類型各一份(01–03)、release CI/CD 核對(04)、分支對齊檢查(05)。
> `assets/` 是三種類型的 `BRANCHING.md` 範本。
## 1. 何時使用
- 任何 git 分支操作之前:開分支、合併、同步上游、快轉 `release` 或 `main`、刪分支
- 建立新 repo、新 fork、新私有 fork,或替既有 repo 補 `BRANCHING.md`
- 要對原作者送 PR(第 1 種)
- 要設定 GitHub 分支保護或 ruleset
- 發版(由誰執行見規則 39;版號與 tag 的做法見 `cicd/elf-cicd-versioning`;沒有產品版號的 repo 見規則 40)
- 「release 的 CI/CD 為什麼沒過」「發版後部署失敗」→ `references/04-release-ci-check.md`
- 「main 跟上游有沒有對齊」「release 是不是 dev 測過的版本」「pr 分支跟 dev 改的一不一樣」→ `references/05-alignment-check.md`
- 要建立或接手一個 Task 很多的 Plan
- 正式版出問題要緊急修正,或有人要求「直接改 release/main」→ 規則 25–27
## 2. 固定規則
**Skill 載入(Superpower 開發方式)**
1. 每一段開發或 git 任務開始時,MUST 先載入 superpowers 的 `using-superpowers`(同步後名稱 `_superpowers__skills__using-superpowers`);工作單在 Linear 時,接著載入 `workflow__elf-linear-superpowers`。
沒有安裝這些 skill 的 AI,照 Linear『開發流程規範』與『流程總覽(給所有 AI)』操作;『開發流程規範』是 Linear 團隊文件「開發流程規範:Spec/Plan × superpowers」。
為什麼:superpowers 決定「先設計、再計畫、再實作、再驗證」的順序,沒先載入就會直接動手寫,跳過設計與兩道驗證。
2. 之後 MUST 依任務再載入本 skill 與對應的領域 skill(例如 `backend__elf-dotnet`、`cicd__elf-cicd-versioning`)。
3. MUST 在動手前確認必要的 skill 都已載入;不確定需要哪些時,先列出來給使用者看。
**判斷 repo 類型(第 0 步,每次都做)**
4. MUST 先讀 repo 根目錄的 `BRANCHING.md`,第一行會寫「本 repo 為【第 X 種】」。
5. 沒有 `BRANCHING.md` 時,用 `git remote -v`、`git branch -a` 推測類型,MUST 把推測告訴使用者、確認後才動手,並建議用 `assets/` 的範本補上 `BRANCHING.md`。
為什麼:同一個指令在三種類型的意義不同。例如 `git merge --ff-only upstream/main`,在第 1 種是正確的同步,在第 2 種會跳過我們第 1 種的 `release`、直接吃進原作者未測的版本。
6. `BRANCHING.md` 與實際 remote 不一致時(例如寫第 2 種卻沒有 `upstream`),MUST 停下來回報,不自行判斷哪邊對。
**分支**
7. `main` 只用來同步:只快轉(`--ff-only`),MUST NOT 在上面提交,MUST NOT 當穩定版或部署來源。
8. `release` 是唯一的穩定版:給下游同步、給部署使用,MUST 只從測過的 `origin/dev` 快轉(緊急修正例外:從 `origin/hotfix/*` 快轉,見規則 25),MUST NOT 提交、MUST NOT force push。
為什麼:第 2 種的 `main` 用 `--ff-only` 從第 1 種的 `release` 同步,`release` 一旦改寫歷史,所有下游同步都會失敗。
9. `dev` 是整合測試:只能經由開發分支的 PR 合併進來,MUST NOT 直接在 `dev` 上開發。例外只有兩種,都由維護者直接推送:第 1、2 種的「`main` → `dev` 同步合併」(見規則 18),以及 hotfix 發佈後的「`release` → `dev` 合回」(見規則 26)。
10. 開發分支命名 MUST 為 `<類型>/e-<編號>-<英文短名>`,例如 `feat/e-235-elf-team-rule`、`fix/e-88-upload-timeout`、`hotfix/e-250-login-crash`;沒有 Linear 單時才用 `<類型>/<英文短名>`。類型限 `feat`、`fix`、`docs`、`chore`、`test`、`hotfix`。短名用英文小寫加連字號。
編號:有 `[Spec]`+`[Plan]` 時用 **Plan 編號**;沒有 Spec/Plan 的單一工作單(例如緊急修正)用**該單編號**。本規則是團隊唯一的分支命名,Linear「開發流程規範」引用本規則,不另外定義。
**工作單狀態**:
- 有 `release` 分支(已導入 dev/release,第 1–3 種)的 repo:
- 開 PR → Plan「進行中」(自動);PR 合進 `dev` → 「驗證中」(自動,Eddie 依目標分支設定);Spec 維持「進行中」。
- 「已完成」由 AI 在步驟 7(`workflow__elf-linear-superpowers`)**手動設定**,三個條件都要滿足:① `release` 已快轉到含這些提交的 `origin/dev`;② 該提交的 check runs 全部通過(規則 28);③ 有部署或派發步驟的 repo 已確認新版生效(例:skillshare-source 已 `skillshare sync`)。Plan 完成後 Spec 由父子自動化關閉。
- hotfix:快轉到 `release`、不經 PR,從「進行中」到「已完成」全部手動,用同樣三個條件(① 改為 `release` 已快轉到該 `origin/hotfix/*`)。規則 25 的 draft PR 在 `release` 快轉後會被 GitHub 標成已合併,可能觸發 Linear 的 merge 自動化;三個條件還沒全部滿足就被改成「已完成」時,手動改回「進行中」並留言原因。
- 沒有 `release` 分支(還沒導入 dev/release)的 repo:維持 Linear 預設規則「merge → 已完成」,合進主線即發版。
- 過渡期:Eddie 在 E-310 留言確認自動化已設定之前,`dev` 合併後若被自動關單,AI 手動把 Plan 改回「驗證中」、Spec 改回「進行中」,留言「自動化依 dev 合併觸發,實際尚未發版」。
為什麼:Linear 的 PR 自動化(開 PR → 進行中、合進 `dev` → 驗證中)靠分支名稱裡的 `e-<編號>` 對應工作單;`release` 是快轉推送、沒有 PR,Linear 的 PR 自動化不會觸發,所以「已完成」要手動設定。分支名稱 MUST NOT 含中文或全形符號,Linear 自動產生的 `gitBranchName` 常帶中文標題,不要直接用。
11. 開發分支 MUST 從 `origin/dev` 開出,合回 `dev` 前先在開發分支上 `git merge origin/dev` 並跑完測試。唯一例外是 `hotfix/`:從 `origin/release` 開出、不經 PR 合回 `dev`,見規則 25、26。
12. 第 1 種送原作者的 PR MUST 用 `pr/<英文短名>`,從 `main` 開出,只放 cherry-pick 的提交,一個分支一個主題。
**一定要停下來回報**
13. 遇到以下任何一種,MUST 停止操作、說明現況、等使用者決定:
- `git merge --ff-only` 失敗(`main` 或 `release` 被直接提交過,或上游改寫了歷史)
- 任何合併、cherry-pick、rebase 出現衝突
- 需要 `--force`、`--force-with-lease`、`reset --hard`、刪除遠端分支(規則 19 的清理除外)等破壞性操作
- 工作目錄有未提交的變更,而接下來的操作會切換分支
- 無法確定 repo 類型,或 `BRANCHING.md` 與 remote 設定不一致
- 使用者的要求違反本規範(例如「直接推到 main」):說明違反哪一條,請使用者確認是否例外
- `git push` 被拒(non-fast-forward):MUST NOT 改用 `pull --rebase`、`reset` 或 force push 讓它過
- 在本規範沒有指示的 detached HEAD 上準備提交(規則 26 的替代做法本身會 `git switch --detach origin/dev`,所以只擋本規範沒有指示的情況,不把 detached HEAD 整個列為停止條件)
14. 回報 MUST 包含:目前分支、執行的指令、錯誤訊息原文、判斷的原因、可選的處理方式。
15. 對外的動作(對原作者開 PR、留言、推送到別人的 repo)MUST 先取得使用者明確同意。
**Plan 容量(Plan & Task Capacity)**
16. 單一 Plan 的 Task MUST 控制在 3–5 個;超過 5 個時,MUST 停止新增 Task 並提議拆分。預估總工時是否超過團隊標準容量,依 Plan 的估點分三級:
- 估點 **13 以上**:MUST 拆單。
- 估點 **8**:可以不拆,但 MUST 在 Plan 留言說明不拆的理由。
- 估點 **5 以下**:照常進行。
為什麼:Task 太多時上下文切換成本高、審查與測試會塞車,整個 Plan 的進度一起被拖慢。
為什麼(估點三級):依使用者 2026-10-17 查到的 Linear 狀態歷程,E-240(13 點)實作約 3 天,審查後退回重改;E-122(8 點)在「已完成」與「進行中」之間來回 3 次。E-237、E-239 都估 8 點,實作只花 27 分鐘~2 小時,所以估點不能當唯一門檻;E-307 照 5 個 Task 切,約 1 小時完成、沒有退回。
17. 拆分原則:優先按模組(不同業務領域分開);其次按依賴(前置任務獨立成一個 Plan,完成並測試後再啟動後續);拆完每份都要能獨立開發、測試、交付,太小不能獨立交付的就併入某個 Plan,當成其中一個 Task(打勾清單),不開子工作單。
依開發流程規範「一個 Spec 對應一個 Plan」,拆 Plan 就是拆 Spec:新開 `[Spec]`+`[Plan]` 一組,不在同一個 Spec 底下開第二個 Plan。
提問範本:「目前此 Plan 下已有 X 個 Tasks,為避免開發進度延宕與上下文切換,建議將其拆分為 [Plan A] 與 [Plan B]。請問是否同意拆分?」
拆成**兩組以上** `[Spec]`+`[Plan]` 時,MUST:(1) 在該專案建立里程碑,名稱用這件事的主題;(2) 把每一組 Spec 與 Plan 都放進這個里程碑;(3) 有先後順序的,用 blocked by 標出依賴。只有一組時不建里程碑。
為什麼:拆開後的工作單散在專案裡,沒有里程碑就看不出哪幾張是同一件事。
工作單結構 MUST 最多兩層:`[Spec]` → `[Plan]`,一張 Spec 只有一張 Plan;**Plan 底下不開子工作單**,Task 一律是 Plan 描述裡的打勾清單。不需要設計的小單(單純維運、明確的錯誤修正、緊急修正)維持單一工作單,不套 Spec+Plan。
Label:階段 `Spec`/`Plan` 單選;「工作類型」群組單選一個(`新功能`/`改善`/`錯誤`/`維運`),加在 Spec(單一工作單就加在該單),Plan 不另加;`稽核` 可另外加。
為什麼:AI 依 label 與層級判斷流程走到哪一步;Task 拆成子工作單後要讀十幾張單才知道進度,最容易接不上。
**衝突處理與分支清理**
18. 第 1、2 種的 `main` → `dev` 同步合併,沒有衝突時可以由維護者直接 `git push origin dev`;有衝突時 MUST 先回報,使用者同意後改在 `merge/conflict-<YYYYMMDD>` 分支處理(同一天已有就加 `-2`),經 PR 合回 `dev`;這個 PR MUST 用「Create a merge commit」合併,MUST NOT squash 或 rebase。
為什麼(merge commit):squash 或 rebase 會產生新提交,`main`(或 hotfix 的 `release`)就不再是 `dev` 的祖先,下次同步又衝突、下次發佈無法快轉。
為什麼:同步合併沒有新內容,強制走 PR 只是多一道手續;有衝突時才需要留下可審查的紀錄。
19. 開發分支合進 `dev` 並確認無誤後,MUST 刪除本地與遠端分支(`git branch -d <分支>`、`git push origin --delete <分支>`);`pr/` 分支在原作者合併或拒絕後刪除;`merge/conflict-*` 合回 `dev` 後刪除。worktree 的清理見規則 36。
**分支保護**
20. `main`、`release` MUST 設定:禁止 force push、禁止刪除、只允許維護者 push。MUST NOT 設定「必須經過 PR」。
為什麼:這兩條分支是在本機 `merge --ff-only` 後直接 push 更新的;GitHub 的 PR 合併(merge commit/squash/rebase)都會產生新提交或新 SHA,無法快轉,設了「必須 PR」之後同步與發佈就推不上去。
21. `dev` MUST 設定:禁止 force push、禁止刪除;開發分支一律經 PR,維護者保留直接 push 權限給規則 18 的同步合併與規則 26 的 hotfix 合回。`dev` 設了 required status checks(elf-cicd-review 規則 17)時,維護者 MUST 列在 bypass 名單:本機做出的同步合併提交沒有任何 check,不 bypass 就推不上去(2026-10-13 沙盒實測:required status checks 對直接推送也生效,admin 列入 bypass 後才推得上去;E-244)。
**核對 CI/CD 與對齊**
22. 判斷某個分支的 CI/CD 狀態,MUST 以該分支**目前指向的提交**上的 check runs 為準(`gh api "repos/<owner>/<repo>/commits/<branch>/check-runs?filter=all"`),MUST NOT 只看 `gh run list -b <branch>`。
為什麼:`release` 是快轉過來的,它的提交通常是在 `dev` 上被 CI 跑過的,那些 run 的分支欄位寫的是 `dev`。CI 沒有設 push `release` 觸發的 repo,`gh run list -b release` 會回「沒有任何 run」,失敗的 check run 卻照樣掛在提交上(skillshare-source 2026-10-11 實測)。完整步驟見 `references/04-release-ci-check.md`。
23. MUST NOT 用推測取代查證:失敗原因以 check run 的 annotations 或 log 原文為準,查不到就回報「查不到」,不要寫成結論。
24. 快轉 `release` 或 `main` 前,MUST 依 `references/05-alignment-check.md` 確認來源與目標的關係,並用 `git log --oneline <目標>..<來源>` 列出會帶進去的提交給使用者看過。
**hotfix 與發佈前 CI**
25. 正式版要緊急修正、而 `dev` 上有未測完的內容時,MUST 走 hotfix(第 1、2、3 種皆同):
- 從 `origin/release` 開出 `hotfix/e-<編號>-<英文短名>`(`git switch -c hotfix/e-<編號>-<短名> origin/release`),在上面修改、測試、推送。
- 推送後 CI MUST 在該提交上執行:依 `elf-cicd-*`,CI 在 push `hotfix/**` 時就會觸發;repo 的 CI 還沒加 `hotfix/**` 觸發時,才對 `release` 開一個 **draft PR**(`gh pr create --draft --base release --head hotfix/e-<編號>-<短名>`,標題註明「僅觸發 CI,不要合併」),讓 `pull_request` 觸發的 CI 在該提交上執行。`release` 快轉後 GitHub 會自動把這個 PR 標成已合併;放棄發佈時關閉它。repo 沒有任何 workflow 時不用開。開了 draft PR 仍沒有任何 check run(例如 workflow 的 `pull_request` 只篩 `main`、`dev`)→ 視為 CI 不合規(規則 29):回報並建議用 `chore/` 修正觸發條件;這次要發佈,比照「專案沒有 CI」需要使用者明確同意例外(規則 28)。
為什麼:`release` 只能快轉到 CI 通過的提交(規則 28);舊版 `elf-cicd-*` 的 CI 只在 push `dev`/`release` 與 `pull_request` 時觸發,hotfix 分支單純推送不會有任何 check run,draft PR 是給還沒更新 CI 的 repo 用的。
- 發佈前 MUST 停下,給使用者看 `git log --oneline origin/release..origin/hotfix/<名>` 與該提交的 check runs(依規則 28 判斷),使用者同意後,才由規則 39 決定的執行者 `git fetch origin && git switch release && git merge --ff-only origin/release && git merge --ff-only origin/hotfix/<名> && git push origin release`(先把本機 `release` 更新到最新,`release` 被別人動過時第二個快轉才會在本機失敗,而不是到 push 才被拒)。
- 快轉失敗(開出 hotfix 後 `release` 又被更新)→ 依規則 13 停下回報;使用者同意後在 hotfix 分支上 `git merge --no-edit origin/release`、測試、`git push`,讓 CI 在新提交上執行,再依規則 28 重新判斷後發佈。
為什麼:從 `dev` 發佈會連同未測完的內容一起出去;從 `origin/release` 開出,修正的基礎就是使用者正在用的版本,`release` 仍然只快轉。
26. hotfix 發佈後 MUST 立刻把 `release` 合回 `dev`:先 `git fetch origin && git switch dev && git merge --ff-only origin/dev` 把本機 `dev` 更新到最新,並確認 `git log --oneline origin/dev..dev` 是空的(快轉失敗或有輸出,代表本機 `dev` 有沒推的提交,依規則 13 停下;使用者要保留那些提交時,改用 `git switch --detach origin/dev && git merge --no-edit origin/release && git push origin HEAD:dev`),再 `git merge --no-edit origin/release`,沒有衝突時由維護者直接 `git push origin dev`(規則 9 的例外);有衝突先 `git merge --abort`,依規則 13 停下回報,使用者同意後從 `origin/dev` 開出 `merge/conflict-<YYYYMMDD>` 處理、經 PR 合回 `dev`(依規則 18 用 merge commit)。第 3 種在 `release` 快轉後就可以把 `main` 從 `release` 快轉,不必等合回完成;第 1 種發佈後提醒使用者下游第 2 種可同步,修正也可依 `pr/` 流程送原作者;第 2 種只在自己的 repo 修,不回饋。最後依規則 19 刪除 hotfix 分支。
為什麼:不合回 `dev`,下一次 `dev → release` 會無法快轉,修正也會在下一版消失。
27. MUST NOT 在 `release`、`main` 上直接修改或提交,即使是緊急修正。使用者要求「直接改 release」時,依規則 13 說明違反規則 8、27,並引導改走規則 25。
28. 快轉 `release`(來源是 `origin/dev` 或 `origin/hotfix/*`)前,MUST 查要發佈那個提交的 check runs,**每個 check 只看最新一次結果**(`check-runs?filter=latest&per_page=100`;規則 22 的 `filter=all` 只用在排查),步驟見 `references/04-release-ci-check.md` 第 0 節:
- **全部通過**:每個 check run 的 status 為 `completed`,conclusion 為 `success`、`neutral` 或 `skipped` → 列出提交與結果,使用者同意後快轉。
- **有失敗或執行中**:任何 conclusion 為 `failure`、`cancelled`、`timed_out`、`action_required`、`stale`,或 status 不是 `completed` → MUST NOT 發佈;失敗原因依規則 23 讀原文。`stale` 沒有可讀的 log,告訴使用者後重跑該 check(`gh run rerun`),再重新判斷。
- **完全沒有 check runs**,先分兩種情況:
- repo 有 `.github/workflows/`,但這個提交沒有觸發(例如 CI 沒有 `hotfix/**` 觸發、也沒開規則 25 的 draft PR)→ 先補上觸發方式、等 CI 跑完再判斷,MUST NOT 回報成「專案沒有 CI」;補了仍不觸發,依規則 25 當成 CI 不合規處理。
- repo 沒有任何 workflow(專案沒有 CI)→ MUST 停下,回報「發佈內容未經自動測試」,使用者**明確同意這次例外**才快轉,並建議開 Linear 工作單依 `elf-cicd-*` 補上 CI。
29. 發現 CI 不合規(以 `elf-cicd-*` 目前的規定為準,例如 push 觸發缺 `dev`、`release`、`hotfix/**`,或沒有彙總 job `ci`)時,MUST 回報並建議用 `chore/` 分支修正;MUST NOT 自行修改 CI 設定,也 MUST NOT 因此跳過規則 28 的檢查。
**本機防護(git-guard)**
30. 每台電腦 MUST 安裝 git-guard(skillshare-source 的 `scripts/git-guard/install.sh`),每次操作前用 `install.sh --check .` 確認(§3)。git-guard 擋下推送時,MUST 照訊息改正做法;MUST NOT 用 `--no-verify`、改 `core.hooksPath`、刪除或修改 hook 等方式繞過。放行值 `ELF_ALLOW=<SHA 前 8 碼以上>` 只能在使用者對**那一個提交**明確同意例外後使用(例如專案沒有 CI,規則 28),回報時 MUST 寫明用了放行值與原因。
為什麼:規則寫在文件裡擋不住 AI 判斷錯誤;git-guard 在推送當下檢查 `release`、`main`、`dev` 的來源與形狀,GitHub 組織 ruleset(`elf-protect-branches`、`elf-require-ci`)再擋沒測過的內容(E-244)。繞過它就等於回到沒有防護的狀態。
- husky repo 執行 `pnpm install` 後,husky 會把 `core.hooksPath` 改回 `.husky/_`,git-guard 隨之失效;`--check .` 會報出來,經使用者同意後重跑 `install.sh --repo .`。
- Git LFS 的 hook 由 git-guard 代為執行,不需要(也不要)`git lfs install --force`。Python 的 `pre-commit install` 遇到全機 `core.hooksPath` 會拒絕安裝,做法見 `scripts/git-guard/README.md`。
**多 AI 並行**
31. 一個 session 對應一個 worktree、一條分支、一張 Plan;兩個 session MUST NOT 用同一條分支。session 開始前 MUST 確認工作目錄就是這張 Plan 的 worktree(repo 外同層、分支名稱符合規則 10);MUST NOT 在 Orca 自動建立的 `.ocrca/…` 目錄裡做事。
為什麼:E-302(2026-10-17)停在 Orca 建的中文分支目錄,後來另開 worktree,舊的那個因為正是當下的工作目錄而刪不掉。
32. 會改到同一批檔案的單 MUST 用 blocked by 排序(例:E-308 blocked by E-307)。
**並行標註**:同一專案有兩組以上未完成的單時(里程碑一定隸屬某個專案,所以也涵蓋同一里程碑的情況;與巡檢 W6 的範圍一致),Spec 定案前 MUST 在「## 並行」一節寫一行,格式固定(巡檢 W6 逐字判斷,與 Spec 模板的欄位文字完全一致):
- `並行:可(與 E-xxx)`(多張用 `、` 分隔,例 `並行:可(與 E-308、E-315)`)
- `並行:不可(blocked by E-xxx)`:本單被別張單擋住
- `並行:不可(blocks E-xxx)`:本單擋住別張單
- 兩種關係同時存在時寫在同一行:`並行:不可(blocked by E-xxx;blocks E-yyy)`
- 括號裡的單號寫關係另一端、Linear 上實際設定關係的那張單;關係設在 Plan 上就寫 Plan 號,不要換成 Spec 號。例:E-317 寫 `並行:不可(blocks E-338)`(Linear 上的關係是 E-338 blocked by E-318,E-318 是 E-317 的 Plan)。
- 下一行 `理由:` 寫兩邊要改的檔案清單比對結果。
- 里程碑描述列出整體順序與可並行的組合。
為什麼:沒標 blocked by 不等於確認過可以並行,可能只是沒人檢查。
為什麼(blocks):原本「不可」只有 `blocked by` 一種格式;E-315 擋住 E-318 時沒有格式可寫,只能塞進「理由:」,巡檢 W6 與其他 session 都看不到(2026-10-18)。
33. 只改自己負責的單;對別張單 MUST 只用留言,由負責那張單的 session 更新描述。
為什麼:Linear 描述是後寫的覆蓋先寫的,兩邊同時修改時,一邊的內容會悄悄消失(E-302 改了 E-303 的描述)。
34. 會改動共用本機狀態的操作(skillshare 設定與 `skillshare sync`、全域 git 設定、Credential Manager)執行前,MUST 確認沒有其他 session 依賴它,並在里程碑或相關的單留言告知。
為什麼:E-303 跑 sync 時,E-302 正在拿 audit 數字做改前改後比對,兩邊基準不同。
35. 刪除或移動共用路徑前,MUST 在相關的單上確認沒有人在用。
為什麼:E-303 刪了舊 clone,稍早的稽核正在讀那份。
- 規則 34、35 的「確認有沒有人在用」:查同一里程碑、同一專案裡狀態為「進行中」或「驗證中」的 Plan,有就在那張單留言;判斷不了就問使用者。
36. session 收尾時 MUST 清理自己建立的 worktree 與分支(規則 19 的分支刪除之外加上 worktree),並把 Orca 自動建立、沒用到的 `.ocrca/…` 目錄列進清理清單。
為什麼:E-302、E-310 都留下了這種目錄。
**合併方式**
37. 開發分支的 PR 合進 `dev` MUST 用「Create a merge commit」,MUST NOT squash 或 rebase。
為什麼:
1. 可以追溯:每個 Task 是一個帶 Plan 編號的 commit(例 `fix(E-307): …`),審查逐 Task 進行;merge commit 保留這些 commit,稽核問「哪一步、誰審的」時對得到 Plan 的勾選紀錄與審查留言,squash 會把這條線切斷。
2. 與規則 18 一致:全團隊只有一種合併方式,AI 不用每次判斷。
3. 不用 rebase:rebase 產生新 SHA,PR 上審查過、CI 跑過的提交就不是實際進 `dev` 的那一批。
- 衍生要求:每個 commit 都會保留,提交要有意義,不留 `wip`、`fix typo` 這類零碎提交。**合併前在自己分支上整理 commit 是允許的**(做法見規則 38);禁止的是**合併時** squash 或 rebase。
38. 整理 commit 分三個時間點:
| 時間點 | 做法 |
| -- | -- |
| 第一次推送之前 | 可以整理:`git commit --fixup <SHA>`,再 `GIT_SEQUENCE_EDITOR=: git rebase -i --autosquash "$(git merge-base HEAD origin/dev)"`,**在 Git Bash 執行**(`變數=值 指令` 的寫法在 PowerShell 不成立;AI 環境不支援互動式 rebase)。整理完 `git diff <整理前的 HEAD> HEAD` 輸出 MUST 為空,證明程式內容沒有變動。整理 fixup 不換基準點,所以 diff 必須為空。要跟上新的 `dev`(`git rebase origin/dev`)是另一個獨立步驟,也只能在第一次推送前做;做完不套用「diff 為空」的檢查,改成重跑該 Task 的測試與 repo 的驗證(例:skillshare-source 的 `scripts/validate.sh`)。推送後要跟上 `dev`,依規則 11 用 `git merge origin/dev`。 |
| 已推送、還沒開 PR | 依規則 13 停下來問使用者;同意後才可對**自己的開發分支** `git push --force-with-lease` |
| 已開 PR | MUST NOT 改寫歷史,一律新增 commit 修正(例 `fix(E-xx): address review — …`)。為什麼:PR 上的審查留言與 check runs 綁在當時的 SHA,改寫後審查紀錄對不上程式碼。這種 commit 不算零碎提交 |
- `dev`、`release`、`main` 任何情況 MUST NOT force push(既有規則 8、13、20、21 不變)。
**發版**
39. 本規則只適用於有 `release` 分支(已導入 dev/release,第 1–3 種)的 repo;沒有 `release` 分支(還沒導入 dev/release)的 repo 維持規則 10 的「merge → 已完成」,合進主線就等於發版。
發版是獨立的一步,包括快轉 `release`(第 3 種接著把 `main` 快轉到 `release`),以及有派發步驟的 repo 跑派發(例:`skillshare sync`)。每次發版 MUST 由 Eddie 指定一個 session 負責,或由 Eddie 自己做;依規則 24 列出提交給使用者看時,一併確認由誰負責。
快轉與實際派發由誰執行,依 repo 的規定:
- repo 允許 AI 推 `release`:由被指定的 session 執行。
- repo 規定 AI 不推 `release`/`main`(例:skillshare-source 的 `AGENTS.md`「AI agents open a feature branch and a PR; they never push to `dev`, `release` or `main`.」):快轉與實際派發由 Eddie 執行;被指定的 session 只負責快轉前列出提交(規則 24)、查 check runs(規則 28)、發版後驗證,以及通知其他相關的單。
- repo 沒寫明 AI 能不能推 `release`/`main`:視同不能推,比照上一項由 Eddie 執行。
限制與例外:
- 沒被指定的 session MUST NOT 為發版快轉 `release`(第 3 種含接著快轉 `main`),也 MUST NOT 跑派發。
- 第 1、2 種同步上游時快轉 `main`(`references/01-contribution-fork.md` B1、`references/02-private-fork.md` B1)不是發版,不受本規則限制。
- 測試例外(不算派發,任何 session 都可以執行):`skillshare sync --dry-run`,以及用拋棄式 `XDG_CONFIG_HOME`(`targets` 為空或只指向暫存目錄,且 `sources.skills` 指向自己的 worktree,不指向共用的 `release` clone)執行的 skillshare 指令或 `scripts/validate.sh`。這些都不會改動實際的目標與共用的來源。
相關的單:
- 其他相關的單合進 `dev` 後停在「驗證中」,等發版的 session 或 Eddie 通知,才做各自的發版後驗證、結單(規則 10)與清理(規則 19、36)。
- 單的範圍寫「不發版」時,MUST 同時寫明由哪張單或誰負責發版。
為什麼:E-316 與 E-309 一起發版時,兩個 session 都準備快轉 `release`/`main`,由審計員臨時指定 E-309 執行才沒有重複(2026-10-18);nginxWebUI E-286/E-287 寫了「不發版」,合併後沒有任何一張單負責發版(後來另開 E-330)。
40. 沒有產品版號的 repo(skillshare-source、mcp-library),目前的發版方式是:依規則 24、28 快轉 `release`,再快轉 `main`;有派發步驟的照做(skillshare-source:AI 不推 `release`/`main`,快轉與 `skillshare sync` 由 Eddie 執行,被指定的 session 依規則 39 負責列出提交、查 check runs、發版後驗證與通知;mcp-library:由 push `release` 觸發 Docker Publish)。不打 tag、不改版號。mcp-library 的 `docker-publish.yml` 已經接受 `v*.*.*` tag,但在 E-337 導入產品版號與 `vX.Y.Z` tag 之前不推 tag。
為什麼:`cicd/elf-cicd-versioning` 只涵蓋有產品版號與 `release.yml` 的應用程式,這兩個 repo 的發版方式原本沒有寫在任何規則裡(2026-10-19 查證:`gh api repos/elf-express/{mcp-library,skillshare-source}/tags` 都是空的)。
## 3. 標準範本
### 每次操作前
```bash
git status # 工作目錄乾淨嗎?
git branch --show-current # 現在在哪個分支?
git fetch --all --prune # 遠端狀態是最新的嗎?
sh <skillshare source>/scripts/git-guard/install.sh --check . # git-guard 已安裝、沒被蓋掉?(規則 30)
```
- `--check` 失敗:停下,把訊息給使用者看,請使用者照訊息安裝或更新;repo 被 husky 等蓋掉時,經使用者同意後執行 `install.sh --repo .`。`<skillshare source>` 是 `skillshare status` 的 Source 路徑。
- 目前在 `main`、`release`、`dev`,而使用者要求「提交程式碼」或「修改內容」:立刻停止並回報。
- 要提交時,確認在開發分支(`feat/`、`fix/`、`docs/`、`chore/`、`test/`、`hotfix/`)上。
### 第 0 步:判斷類型
```bash
head -1 BRANCHING.md 2>/dev/null || echo "沒有 BRANCHING.md"
git remote -v
git branch -a
```
| 線索 | 推測 |
| -- | -- |
| `upstream` 指向外部作者的 repo | 第 1 種:貢獻型 fork |
| `upstream` 指向 `elf-express/` 底下的 repo | 第 2 種:私有 fork |
| 沒有 `upstream` | 第 3 種:自有專案 |
線索只能拿來提問,不能直接動手。確定類型後讀對應的詳細規範:
| 類型 | 說明 | 詳細規範 |
| -- | -- | -- |
| 第 1 種:貢獻型 fork | fork 原作者,自己用,也送 PR 回饋 | `references/01-contribution-fork.md` |
| 第 2 種:私有 fork | 以我們的第 1 種為基底的私有版本,不回饋 | `references/02-private-fork.md` |
| 第 3 種:自有專案 | 我們自己建立,沒有上游 | `references/03-own-project.md` |
### 整條鏈的流向
```
原作者 main ──快轉──▶ 第1種 main ──合併──▶ 第1種 dev ──快轉──▶ 第1種 release
│
快轉
▼
第2種 release ◀──快轉── 第2種 dev ◀──合併── 第2種 main
第3種:feat/xxx ──合併──▶ dev ──快轉──▶ release ──快轉──▶ main
緊急修正(三種皆同,規則 25、26):
origin/release ──開出──▶ hotfix/e-xx-name ──快轉──▶ release ──合回──▶ dev
```
### 新 repo 補上 BRANCHING.md
1. 依類型複製 `assets/BRANCHING-type1-contribution-fork.md`、`assets/BRANCHING-type2-private-fork.md` 或 `assets/BRANCHING-type3-own-project.md` 到 repo 根目錄,改名為 `BRANCHING.md`。
2. 把 `{{...}}` 換成實際值;不知道的值(例如部署位置)問使用者,不要自己編。
3. 主線不叫 `main`(例如 `master`)時,把實際名稱寫進 `BRANCHING.md`。
4. 在 `CLAUDE.md` 或 `AGENTS.md` 加一行:
```
動手前先讀 BRANCHING.md,依 elf-team-rule 規範操作分支。
```
5. 放入 `.gitattributes`,與 `BRANCHING.md` 分開成**獨立 commit**:
1. 複製 `assets/gitattributes` 到 repo 根目錄,改名為 `.gitattributes`。repo 已有 `.gitattributes` 時,範本加在檔案**最前面**,原有規則一行都不動。
為什麼:`.gitattributes` 後面的規則優先,加在前面,原有的特定規則(例如 LFS 的 `filter=lfs -text`)才會繼續生效。
在 GitHub 查看