Files
ISEP/docs/governance/sdd-gitea-governance.md
Leo aec7f3a980 治理 M4.7:把票移出里程碑必須寫進里程碑描述(closes #24 的規範面)
leo 2026-08-20 抓到:v0.2.0 顯示 100%,但那是把 #5 移出去之後的 100%,
畫面上看不出降 scope 發生過。

與 §3.4(審核完沒關票 ⇒ 數字偏低)是同一個病的兩面——
畫面上的數字不等於實際狀態,而 leo 只看得到畫面。

禁的不是降 scope(卡人閘時降 scope 是 M4.3 要的),禁的是降得無聲無息。
痕跡要留在他會經過的地方=里程碑描述,不是票裡、不是對話裡。
2026-08-20 16:33:08 +08:00

428 lines
25 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SDD × Gitea 治理規範
```
version: 0.6.0
status: 現行(本檔的修改依 §9 走 issue)
scope: 所有安裝 ISEP 的環境(本機 CC 與雲端 CC)
distribution: 隨 ISEP 發佈,ISEP repo 是唯一編輯點(§11
lineage: v0.5.0 draft by claude.ai(存檔 _draft-claude-ai-v0.5.0.md
→ v0.6.0 由總管對照現場實況修訂,逐條分歧見 DIVERGENCE-v0.5.0-to-v0.6.0.md
```
---
## 0. 公理
1. **意圖真相在 SDD,狀態真相在 Gitea,知識真相在 Wiki。** 時態分工:SDD 未來式、Gitea 現在式、Wiki 過去式(§12)。禁止交叉寫入。
2. **Issue 是唯一任務介面。** 任何工作不經 issue 不得開始。issue specPR deliverablerelease 交付原子單位。
3. **封路優於守規。** 凡可結構性擋掉的違規路徑,不依賴代理或人的自律。
🔴 **封的是「動作」,不是「措辭」**leo 2026-08-17):自然語言的變體無限,blacklist 追不完;動作有限且可枚舉。
4. **兩軸正交。** Tracking issue 管 scope 軸,milestone 管 time 軸。
5. **治理本體只有一份,而且沒有子集。** 封裝於 ISEP;本機與雲端都是**安裝目標**,不是編輯目標(§11)。
🔴 **不存在「薄殼只裝一部分」這種東西**——那正是 `InkStoneCo#57` 的成因。
6. **審核不是任務,是路徑。** 審核 review PR 並 merge,是交付的唯一必經動作(§3.0)。
7. **人是特殊 executor。** 人的兩種介入——審核者(`Human`)與執行者(`human/exec`)——分別建模,走同一狀態機(§6)。
---
## 1. 物件模型(五層 + 一橫切)
```
SDD 文件(docs/
└─ Tracking issuelabel: hub ← scope 軸
└─ Leaf issue ← 最小工作單位
└─ PRcloses #n ← deliverable
└─ Release tag ← 交付原子單位
Milestone ────────────────────────────── ← time 軸,橫切上樹
```
| 物件 | 職責 | 明確不做 |
|---|---|---|
| SDD 文件 | 意圖、範圍、非目標、驗收哲學、驗收腳本位置 | 不含 leaf task、不記進度 |
| Tracking issue | scope 聚合、討論容器、驗收閘 | 不掛 milestone、不直接對應 PR |
| Leaf issue | 一個 PR 的 spec(或一個人執動作,§6.2) | 不再拆子票(要拆=升格,§5.5) |
| Milestone | 一個可測試版本的 timebox | 不承載討論、不表達語意分組 |
| PR | 實作 測試,merge 時自動關 leaf | 不手動關票 |
| Release | milestone 驗收通過後打 tag | 不在 README 裡宣稱版本(§4.6 |
---
## 2. 連結規則
- **R2.1** SDD 的 tasksjourney 段落只連 tracking issue,禁止連 leaf。
- **R2.2** 每張 leaf 必屬**恰好一個** tracking issuetracking 的 task list 登記 leaf 首行回鏈 `Parent: #n`)。
- **R2.3** 每張 leaf 最多屬一個 milestone。未排程 不掛(即 backlog)。
- **R2.4** Tracking issue 不掛 milestone。其子票可分屬多個 milestone。
- **R2.5** 順序關係一律用 Gitea 原生 dependency`/issues/{n}/dependencies`,已驗證可用),禁止只寫文字。
- **R2.6** 跨 repo 的票**不搬**——Gitea 沒有 issue transfer API(已驗證)。改用互鏈:在收斂方開 hub,OP 內以 `owner/repo#N` 全稱指回。
🔴 票號一律寫全稱,裸號跨 repo 會撞號。
---
## 3. 完成語意與票的狀態機
### 3.0 狀態機
```
s/triage ──驗傷通過──▶ s/backlog ──排進 milestone──▶ s/todo
│ 領票(self-assign
s/doing ──開 PRcloses #n)──▶ s/review
▲ │ 總管 merge
│ ▼
s/pending closed
(卡外部/等依賴) │ 若這一版要 leo 實驗
s/stage
```
七個態,全部 `exclusive: true`(Gitea 平台保證同票至多一個,非法狀態不可表示):
| 態 | 意思 | 誰轉進來 |
|---|---|---|
| `s/triage` | 新進來的,還沒驗傷 | 任何人開票 |
| `s/backlog` | 確定要做,還沒排 sprint | 驗傷者 |
| `s/todo` | 已排進 milestone,等人領 | 總管 |
| `s/doing` | 有人在做(已 self-assign | executor 自己 |
| `s/review` | PR 已開,等總管 merge | executor 自己 |
| `s/stage` | 已部署 stage,等 leo 實際驗收 | 總管 |
| `s/pending` | 卡住——等外部/等依賴 | 任何人,但要寫明卡什麼 |
- **S3.0.1(領票)** 領任務 self-assign `s/doing`。**留言認領不算**——留言不改變狀態。
- **S3.0.2(完成)** 宣告完成 = 開 PR(含 `closes #n`)+ 轉 `s/review`。**留言說做完不構成完成**(人執票例外見 §6.2)。
- **S3.0.3(審核)** 總管審核 review PR 並 mergemerge 即自動關票。沒有獨立的審核票。
- **S3.0.4(退回)** 審核不通過:PR request changes,票退回 `s/doing`,續作。**禁止關 PR 重開新票**(會弄丟審核軌跡)。
- **S3.0.5`s/review` vs `s/stage`)** 兩者是不同的等待,不可互相取代:
`s/review` = 程式碼還沒進 main,等**總管**;`s/stage` 已上測試環境,等 **leo 打開來看**
### 3.13.3 完成語意
- **D3.1** Leaf 關閉 local_done。唯一觸發:PR merge 且含 `closes #n`(人執票例外 §6.2)。禁止手動關閉。
- **D3.2** Tracking issue 關閉 path_done(1) 子票全關【必要】;(2) 驗收腳本通過【充分】;(3) `Human` 已放行。
- **D3.3** 子票全關但驗收未過:tracking 保持開啟,開新 leaf 修復並掛回。**禁止「先關再說」。**
### 3.4 🔴 總管的收工義務(本規範最常被違反的一條)
> leo 2026-08-20 原話:「先前你建立 milestone,但當 subagent 完成工作,你審核後卻沒有關票,
> 導致 milestone 0% 完工,等說了才去關票,**這些都是大問題**。」
- **審核通過的當下就要關票,不是收工前補、不是被提醒才補。**
- 判準:**milestone 的百分比就是 leo 唯一看得到的進度**。票沒關 = 對他而言那件事沒發生。
- 「我審完了但還沒關」不是一個合法狀態——審完 = merge = 票自動關;沒關就表示你沒真的審完。
---
## 4. Milestone 規則(= sprint 可測試版本)
- **M4.1** 命名 `vX.Y`,必設 due date。
- **M4.2** Deliverable **一個可測的新版本號**。所有掛入 leaf 關閉後,可從預設分支打出通過驗收的版本。
- **M4.3** **Timebox 到期不自動關、不自動打 tag。** 到期強制做一次降 scope 對帳(未完成 leaf 搬下一 milestone)並通知 leo。
🔴 自動打 tag 會製造假交付——tag 永遠只在「驗過了」之後發生。
- **M4.4** Milestone 關閉 release tag,一對一。「已交付」唯一合法形式是 **tag 存在且裝得起來**;打 tag 前置 open issues 0(§8 E12)。
- **M4.5** Description 只寫版本目標一句 tracking 連結。討論回 tracking issue。
- **M4.7(降 scope 必須留痕)** 🔴 **把票移出里程碑,必須同時寫進該里程碑的 description。**
格式:原本幾張、移走哪幾張、為什麼、移去哪裡。
- **禁的不是降 scope**——卡人閘時降 scope 就是 M4.3 要的。禁的是**降得無聲無息**。
- 為什麼寫在 description 不是寫在票裡:**leo 看的是百分比那個畫面**,
他不會為了確認 100% 是不是真的而去逐張點票。**痕跡要留在他會經過的地方。**
- 來由(2026-08-20 實犯):`v0.2.0` 本來 6 張,`#5` 卡人閘被移出 ⇒ 分母 6 變 5 ⇒ 顯示 100%。
leo 當場問「為什麼還有很多 issues 開放中」。
🔴 這與 §3.4(審核完沒關票 ⇒ 數字偏低)是**同一個病的兩面**:
畫面上的數字不等於實際狀態,而 leo 只看得到畫面。
- 機械閘:`inkstone/ISEP#24`
- **M4.6** 🔴 **release note 寫在 Gitea Releases 裡,不寫在 README。**leo 2026-08-20:「release 不是寫在 readme,要放在 release 裡」)
README 不得自行宣稱版本號——那會產生第二份會漂移的版本真相。
---
## 5. Issue 關閉分類
關閉必掛恰好一個 `close/*``exclusive: true`):
| Label | 語意 | 關閉者 |
|---|---|---|
| `close/merged` | PR merge 自動關(正常路徑) | 系統 |
| `close/human-exec` | 人執票完成,人手動關(§6.2) | 人 |
| `close/duplicate` | 重複,指向舊票(關新留舊) | 人或代理 |
| `close/wontfix` | 討論後決定不做 | **只有 leo** |
| `close/stale` | 逾期無資訊 | job |
| `close/split` | 拆分後關(§5.5) | 人或代理 |
| `close/transferred` | 屬別的 repo,已在那邊重開 | 人或代理 |
- **C5.5(拆分)** (a) 升格 tracking(加 `hub`)或 (b) 關閉掛 `close/split`,子票掛原 parent。
- **C5.6** `close/wontfix` 只有人可執行。代理只能提議。
- **C5.7** 既有的 `duplicate` 標籤**封存不刪**(刪除會把它從所有歷史票上摘掉)。
---
## 6. 人的兩種介入
### 6.1 `Human`——人是審核者(正交 overlay)
- **H6.1.1** `Human` assignee = Leo:工作由代理完成,人只放行或否決。
- **H6.1.2** 🔴 **`Human` 不是流程狀態,是正交維度**(leo 2026-08-17)——可疊在任何 `s/*` 上,不與 `s/*` 二選一。
- **H6.1.3** 代理不得關閉帶此標籤的 issue、不得 merge 帶此標籤的 PR。
- **H6.1.4** 只有人可移除標籤;移除即放行。
- **H6.1.5** 必設節點(可增不可減):tracking issue 關閉前、`close/wontfix`、milestone 建立與 scope 圈選、**部署至 prod 的 PR**。
- **H6.1.6ISEP 自身的修改:閘在 release,不在每個 PR**
原稿把「治理 plugin 修改 PR」列為必設人閘。**照字面走會讓 leo 變成每個 PR 的瓶頸**——
那正是北極星第一條要拔掉的東西(「為了更聰明而讓 leo 更忙」的設計一律違背)。
但完全拿掉,代理就能**悄悄放寬管自己的規則**——那是 leo 被咬過的那類病。
兩者兼顧的做法:
- 治理與閘的變更,**總管可以 merge**(loop 不停)
- 但**每一個 release note 必須逐條列出這一版改了哪些治理規則與哪些閘**,
`docs/governance/` 的 diff 要在 release note 裡點名
- **release 就是那道人閘**:leo 驗版本時一次看到所有規則變更,該打回就打回
**把同步的逐 PR 審批,改成非同步的逐版本審批。** 沒有東西是悄悄改掉的,而 loop 不會停。
- **H6.1.7** 🔴 **交棒給 leo = 三個機械動作,缺一件等於沒交棒**leo 2026-08-17):
① 總管自己先在 stage 驗過 → ② 把票**指派給 Leo** → ③ 掛 `Human`
在對話裡說「你可以驗了」不算——那句話會捲走,assignee 與標籤不會。
順序不可顛倒:沒驗過就指派 = 把第一次驗證推給他。
### 6.2 `human/exec`——人是執行者(票的屬性)
- **H6.2.1** 當 deliverable 所需的能力只有人擁有(GUI-only 設定、實體權限開通、法定簽署、terminal consent),
票掛 `human/exec` assignee Leo。人視為特殊 subagent,走 §3.0 同一狀態機。
- **H6.2.2** 人執票的 deliverable 是**環境狀態改變**,不是 PR。完成方式:人在票上留 `[decision] 已完成:做了什麼` → 人手動關票 → 掛 `close/human-exec`
這是唯一合法的手動關票路徑。
- **H6.2.3** 驗證不靠自述:人執票通常是下游票的 dependency;關票解鎖後,接手代理若發現設定沒生效即 fail fast,**下游失敗自動 reopen 人執票**。
- **H6.2.4** 🔴 代理判定「這件事只有人能做」時:開 `human/exec` 設好 dependency + 通知,然後**繼續做其他不被 block 的票**,不空轉等待。
### 6.3 Telegram 通知(人閘的開路配套)
- **H6.3.1** 觸發:`Human``human/exec` 被掛上且 assignee Leo 時,即時發 Telegram。
通道與帳號**不寫死在本檔**,真相源是 `wiki/agent-memory.md` 的通知通道段。
- **H6.3.2** 訊息格式(白話鐵律:標題一眼懂、代號必附一句人話):
```
🔔 #123 需要你|[放行] 或 [只有你能做]
一句話:這張票要你做什麼(≤40 字,不准只寫代號)
動作:review PR #45 並 merge 到 CF 後台開啟 X 權限
卡誰:此票 block 了 #124 #125
連結:https://git.uncle6.me/...
```
- **H6.3.3** 節流:同票同狀態只通知一次;24 小時未處理提醒一次;之後併入每日 digest。
🔴 但「等了幾天」要標出來——對 leo 重複=服務,不催這件事就不會發生。
- **H6.3.4** 通知是**投影不是狀態**:訊息遺失不影響治理正確性,真相永遠在 Gitea 票上。
---
## 7. 討論路由
```
新 issues/triage)→ 驗傷 →
├─ 可做,獨立 → 掛入 tracking,排 milestone → 走 §3
├─ 可做,有依賴 → 同上 dependencyR2.5
├─ 只有人能做 → human/exec dependency 通知(§6.2
├─ 重複 → close/duplicate
├─ 不做 → close/wontfix(只有 leo
├─ 太大 → 升格 hub 或拆分(C5.5)
└─ 走錯棚 → 在正確 repo 重開,本張 close/transferredR2.6:不能搬)
```
多票收斂:建 tracking issuescope),**不是直接建 milestone**。
當且僅當「這批票 = 恰好一個可出貨版本」才同時建 milestone。
🔴 **開工第一個動作是建里程碑並拉既有 issue,不是開新票**leo 2026-08-19)。
里程碑**一張新任務都不准增**;撈不到對應的票 ⇒ 那才是真缺口 ⇒ 去票池補一張,不是在里程碑裡編。
🔴 **票名一律 User Story**`身為<誰>,我要<什麼>,我才<為什麼>`
不准自創分類前綴(`【版本】``👤 裁決題:` 都被廢除過)。
---
## 8. 封路清單(每條標明現況,已有的不重造)
| # | 封什麼路 | 用什麼封 | 現況 |
|---|---|---|---|
| E1 | 直接 push 預設分支 | branch protectionPR-only | ◐ 現有 `main-and-prod-push-guard.sh` 語意相反(擋 subagent、放行總管)。**本版只對 ISEP 開 PR-only**,見 §8.1 |
| E2 | PR 不關聯 issue | PR 模板必含 `closes #`,缺漏即 fail | ❌ 待建 |
| E3 | 手動關 leaf | hook 攔截;只放行 `close/*` 與 §6.2 人執路徑 | ❌ 待建 |
| E4 | 被 block 的票先關 | Gitea issue dependency | ✅ 平台原生,已驗證可用 |
| E5 | Milestone 悄悄過期 | job:到期做降 scope 對帳 + 通知(**不自動關、不自動打 tag**) | ❌ 待建(先做成手動腳本,見 §8.2) |
| E6 | 代理越過人閘 | PreToolUse hook | ◐ 現有 `irreversible-dispatch-guard.sh``prod-write-guard.sh` 覆蓋一部分 |
| E7 | SDD 內出現 leaf 連結 | pre-commit lint | ❌ 待建 |
| E8 | Tracking issue 掛 milestone | job 摘除並告警 | ❌ 待建 |
| E9 | 留言變成推理長文 | hook:超長**不 reject**,要求移去 wiki 再留指標 | ❌ 待建(放寬版,見 §8.3) |
| E10 | 就地修改治理檔案 | 安裝目錄唯讀 + hook,導向 ISEP 開 issue | ❌ 待建 |
| E11 | 本機雲端版本漂移 | SessionStart hook 比對版本,不一致 fail-fast | ◐ 現有 `skill-deploy-drift-guard.sh` 管的是 skill 不是 plugin 版本 |
| E12 | 宣稱交付但沒有 release | 版本三處一致閘:`plugin.json` tag release 存在;且 milestone open issues 0 不准打 tag | ◐ 現有 `delivery-police.sh``claim-verify-police.sh` 部分覆蓋 |
| E13 | 審核被遺忘 | Stop hook**我自己開的** `s/review` 存在且**這回合完全沒碰它** → 擋一次 | ◐ 現有 `empty-handed-stop-guard.sh` 判準不同,兩者要合併不要疊加 |
| E14 | Subagent 空口宣稱完成 | SubagentStop hook | ✅ **已有** `subagent-claim-worksheet.sh` `empty-handed-stop-guard.sh` |
| E15 | 代理硬做只有人能做的事 | 代理端無對應 tool 或 token;唯一出口是開 `human/exec` 票 | ◐ 部分(credential 機制已擋一部分,D36 |
| E16 | 標籤漂移 | 走 Gitea labels API 依 `labels.yaml` 校正:缺的補、改的還原、多的**只告警不刪** | ❌ 待建(`ISEP#4` |
### 8.1 E1 的適用範圍(裁決記錄)
**只有 ISEP 走 PR-only;其他 repo 維持現行「總管推 main 自己裁」。**
- 理由:ISEP 壞掉 **所有 session 一起壞**,值得多一道摩擦;其他 repo 壞掉只影響自己。
- 一刀切到 14 個 repo 會在 leo 最忙的時候把整條線卡在「等總管開 PR」。
- 這條命中四題公式的「跨專案結構」⇒ 已記為裁決題(`DIVERGENCE` §E1),leo 可打回。
### 8.2 所有 job 第一版都不依賴 Gitea Actions runner
- 做成 `scripts/` 底下可手動跑、也可由 SessionStart hook 順手跑的東西。
- 理由:**這台 Gitea 有沒有掛 runner,本規範撰寫時未經查證。**
依賴一個不確定存在的東西,壞掉的形式是「以為有人在跑」——比「沒人跑」貴得多。
- 確認有 runner 後才升級成排程,升級走 §9。
### 8.3 E9 為什麼放寬(不是偷懶)
leo 2026-08-17 實測:文字層的閘那天 **8 次誤攔、0 次正確攔截**,且方向穩定——
**紅線寫得越細,命中關鍵字的機率越高 ⇒ 那些閘在懲罰謹慎。**
所以 E9 擋的是「長內容放錯地方」這個**動作**,處置是**導引**(移去 wiki 留指標),不是 reject。
---
## 9. 本規範的迭代
- 存於 ISEP `docs/governance/`;各環境為唯讀安裝副本。
- 修改路徑:ISEP 開 leaf(掛 governance tracking)→ PR → `Human` 放行 → merge → release → 各端升級。
- 版本:規則增刪 = minor,措辭 = patch,公理 major。**ISEP plugin 版本 本規範版本。**
- 每次 milestone 回顧問一句:**規範有沒有被繞過?** 有 → 補 §8 的封路,**不加一句「請遵守」**。
---
## 10. 留言規範
### 10.1 OP 唯一狀態原則
- **OP 是票的唯一狀態容器**,持續編輯;留言是 append-only 的稽核 log,只記 delta。
- 了解一張票只讀 OP。討論收斂即寫回 OP,留言留 `[decision] 已更新 OP:改了 X,因為 Y`
### 10.2 留言格式(BLUF 類型標籤)
```
[類型] 一句話結論(≤40 字)
理由:
- (最多 3 個 bullet,每個 ≤1 行)
下一步:(一行,或「無」)
詳細:(連結至 PR/commit/wiki,禁止貼內文)
```
類型:`decision` `question` `blocker` `progress` `proposal`
能用 OP 的 task list 打勾表達的進度**不留言**;重大 `proposal` 加掛 `Human`
### 10.3 推理軌跡出口
推理、嘗試、失敗分析走 wiki-capture 進 WikiKBDB,留言以 `詳細:` 指向。
**推理進 wiki,結論進留言,留言只帶指標。**
---
## 11. ISEP(單點分發與同步)
### 11.1 實際結構(2026-08-20 實況,不是理想圖)
```
ISEP/
├── .claude-plugin/
│ ├── plugin.json # 版本號真相源之一(§8 E12 要三處一致)
│ └── marketplace.json # host 發現這個 plugin 的入口
├── docs/governance/ # 本規範 + 原稿存檔 + 分歧說明
├── labels.yaml # 標籤唯一真相(§11.4)
├── hooks/ # 41 支 hooks.json51 條註冊)
├── commands/ # 7 支 slash command
├── skills/ # 2 支
├── scripts/ # 23 支
└── system-dev/wiki/ # ISEP 自己的知識庫(只記 ISEP 的事)
```
🧹 **已知待清理**`scripts/install.sh` 實際是 **system-dev-template 的安裝器**(在裝 wikiSDD),
搬家時混進來的,與 plugin 無關。
### 11.2 分發規則
- **P11.2.1** 本機與雲端安裝**同一個 release**;來源只有 ISEP 的 release tag。
- **P11.2.2** 治理修改只發生在 ISEP;執行環境發現需調整 → 回 ISEP 開 issueE10)。
- **P11.2.3** Session 啟動比對版本(E11);不一致 fail-fast,不降級執行。
- **P11.2.4** 升級是原子動作:整包替換,禁止 cherry-pick。
- **P11.2.5** 🔴 **沒有子集。** 本機與雲端裝的是**逐位元組相同**的一份。
(原稿的「薄殼只裝 shell-safe 子集」已刪除——那條是舊薄殼模型的殘留,
正是 `InkStoneCo#57`「薄殼比真身少 7 支閘」的成因本身。)
### 11.3 載入契約(原稿缺這節,而這是最容易默默失敗的一格)
Claude Code **只在 session 啟動當下**讀一次 cwd 的設定。因此:
- **L11.3.1** 雲端 session 的 cwd 是 GitHub 上的啟動 repoISEP 必須在**那個時刻**就被 host 認得。
- **L11.3.2** 「事後 clone 進來」對 hook 可行(hook 是每次工具呼叫才解析路徑),
**commandskill 不可行**(它們啟動時就被掃描)——這是 `InkStoneCo#14` 的根因。
- **L11.3.3** ISEP 是 **private** repo,任何載入路徑都必須先解決憑證。
憑證只准取名字(D36),**不得寫進任何檔案、commit 或 issue**。
- **L11.3.4** 驗收標準:能貼出**雲端那邊實際載到的清單**,逐條對上 ISEP 的註冊條數。
「我推上去了」不是驗收。
### 11.4 標籤分發(labels.yaml
- **P11.4.1(唯一真相)** 全部標籤定義(名稱、顏色、描述、exclusive)只存在於 ISEP 的 `labels.yaml`
Gitea 上所見皆為投影;改標籤 = 改 `labels.yaml`,走 §9。
- **P11.4.2(分發通道)** **API 通道是唯一依賴**:走 labels API 掃所有受治理 repo 校正。
Gitea 伺服器端的模板檔(建新 repo 時 GUI 播種)是**可選的便利品**,
獨立腳本、不掛在安裝流程上——它需要重啟 Gitea,而重啟的成本與風險不該由安裝觸發。
- **P11.4.3(依賴方向)** 正確性只依賴 API 通道。模板通道壞掉的後果僅是「建新 repo 的下拉是舊版」,
而新 repo 納管後第一次 sync 即被校正。
- **P11.4.4(不刪原則)** 🔴 **同步永不刪除標籤**——刪除會把它從所有歷史票上摘掉,不可逆。
非規範標籤只列出告警。
- **P11.4.5(平台級不變量)** `s/*``close/*``p/*``type/*` 一律 `exclusive: true`
Gitea 1.26.4 支援,已驗證):同 scope 同票至多一個,由平台保證——**非法狀態不可表示**,無需 hook 維護。
`Human``human/exec``hub` 是正交維度,**不 exclusive**。
---
## 12. 書寫路由(SDD × Gitea × Wiki
### 12.1 時態原則
| 載體 | 時態 | 回答的問題 | 變動頻率 | 誰寫 |
|---|---|---|---|---|
| SDD | 未來式 | 要做什麼、為什麼、怎樣算完成 | 低 | 代理可寫,**範圍/驗收/非目標的變更必經人閘** |
| Gitea | 現在式 | 誰在做、做到哪、卡在哪、順序 | 高 | 人與代理 |
| Wiki | 過去式 | 怎麼想、試過什麼、學到什麼 | append-only | 主要是代理 |
(原稿寫「SDD 寫入者是人、代理僅提案」——與現況相反,照字面走第一天就違規。
管的應該是**哪些欄位需要 leo 點頭**,不是誰握筆。)
### 12.2 路由判準(寫之前問一句:這段內容改變的是什麼?)
- 「要做什麼」(範圍、驗收、非目標)→ SDD,必經 issue + 人閘(改意圖 = 改合約)
- 「現在狀態」(認領、進度、卡點、定案)→ Gitea(OP 或合規留言)
- 「我們知道什麼」(推理、失敗、可復用教訓)→ Wiki
### 12.3 典型錯置與矯正
| 錯置 | 矯正 |
|---|---|
| SDD 長出 task 清單、進度勾選 | 移至 Gitea;E7 攔截 |
| Issue 留言寫滿推理長文 | 移至 Wiki 留指標;E9 導引 |
| Wiki 記錄「目前進度」 | 刪除;進度只存在於 Gitea。Wiki 引用票時寫「當時」 |
| Issue OP 修改驗收標準 | 退回:先開 SDD 修改 issue(人閘),定案後 OP 才同步 |
| **README 宣稱版本號** | 刪除;版本只存在於 releaseM4.6)。2026-08-20 實犯 |
### 12.4 連結方向(單向依賴)
SDD → 只連 trackingR2.1)。Gitea → 可連 SDD 錨點與 wiki。Wiki → 可連票號與 commit(歷史快照語意)。
Telegram → 純投影。**Wiki 壞不影響 GiteaGitea 壞不影響 SDD,通知丟不影響一切。**
---
## 附:Label 全集(快照;唯一真相為 `labels.yaml`
```
狀態機(exclusive s/triage s/backlog s/todo s/doing s/review s/stage s/pending
關閉分類(exclusive close/merged close/human-exec close/duplicate close/wontfix
close/stale close/split close/transferred
人(正交,不 exclusive Human ← 人是審核者
human/exec ← 人是執行者
優先(exclusive p/high p/low
種類(exclusive type/bug type/feature type/governance type/chore
結構(正交) hub ← tracking issue 標記
封存不刪 duplicate ← 由 close/duplicate 取代,保留在歷史票上
```