RFC 編寫與維護
此技能用於編寫與維護輕量級 RFC — 即編號的 Markdown 設計提案,它們與所描述的程式碼一起存放在儲存庫中。RFC 在設計之前(或期間)捕捉設計內容:問題、當前程式碼狀態、已鎖定的決策及其理由,以及故意排除的範圍。整個集合由單一 INDEX.md 追蹤,方便在一張表格中瀏覽完整的設計歷史。
這裡的 RFC 是工作文件,而非官僚流程:它們的存在是為了讓決策不會因情境遺失而失效,讓一個被接手的設計「只是一個小型 PR,沒有更多」,並讓被拒絕的替代方案不會被反覆重新提起。
使用此技能的時機
- 使用者要求撰寫 RFC、設計文件、設計提案、技術規格或架構提案
- 使用者希望在實作之前記錄或鎖定一項設計決策 — 包含它所擊敗的替代方案,或某個方法被拒絕的原因
- 使用者要求更新 RFC(狀態變更、執行筆記、標記為已出貨或已拒絕)
- 使用者要求建立、索引或重新組織
rfcs/(或rfc/)目錄 - 一個大型功能討論應被捕捉為持久性文件
不應使用此技能的時機
- 使用者想要產品文件、README 或使用者面向的文件(非設計提案)
- 使用者在已有既定 ADR 慣例的儲存庫中想要 ADR — 請遵循該慣例
- 變更非常微小,以至於提交訊息或 PR 描述足以承擔
目錄與索引
位置。 RFC 存放在儲存庫根目錄的單一扁平目錄中:rfcs/(首選預設)或 rfc/。在建立任何內容之前,請先檢查是否存在任一名稱的目錄並遵循它。如果兩者都不存在,則建立 rfcs/。
Gitignore 由使用者決定,非您決定。 有些專案會提交 RFC;有些則將其加入 gitignore 作為本地工作筆記。除非被明確要求,否則切勿為 RFC 目錄新增或移除 .gitignore 條目。如果目錄已被 gitignore,INDEX.md 的標頭應說明此事(請參閱範本),以便讀者知道它為何不在儲存庫歷史中。
INDEX.md 是整個集合的事實來源。 它包含三件事:
- 下一個可用編號,明確陳述 — 編號在平行建立時會衝突,因此索引指定下一個編號,並且每次 RFC 建立時都會在同一變更中更新它。
- 索引表格:
| # | 標題 | 狀態 | 一行摘要 |,每個 RFC 一行,編號連結到檔案。 - 狀態圖例。
如果目錄存在但有一個 README.md 扮演此角色,請將其視為索引。如果被要求從頭設定,請使用 INDEX.md — 複製 references/index-template.md。
編號與檔名
- 編號為 4 位數,補零,單調遞增:
0001、0002、… - 分配方式:從
INDEX.md讀取「下一個可用編號」,與ls目錄內容交叉比對(索引可能過時),取得下一個未使用的整數。 - 檔名:
NNNN-kebab-case-title.md。請保持檔名中的編號與# RFC NNNN — TitleH1 標題同步 — 否則它們會漂移,且連結會雙向失效。 - 絕不重新對現有 RFC 進行編號。編號是識別碼,不是需要整理的排序。
狀態
- 📝 草稿 — 已提案,尚未開始(「設計鎖定、需求待定」的 RFC 仍屬於草稿)
- 🚧 進行中 — 部分出貨
- ✅ 完成 — 完全出貨
- ❌ 已拒絕/已撤回 — 保留檔案;記錄拒絕可防止重新爭論
狀態存在於兩個必須一致的地方:RFC 標頭中的 **Status:** 行以及索引表格的「狀態」欄位。請在同一變更中更新兩者。
RFC 結構
在撰寫新的 RFC 之前,請先閱讀 references/rfc-template.md 並從它開始。結構如下:
標頭區塊(H1 正下方的項目符號列表,在任何章節之前):
**Status:**— 表情符號 + 文字,可選擇添加註釋(例如「可執行 — 一個 PR」、「設計已鎖定,尚未排程」)**Scope:**— 一個密集的段落:此 RFC 涵蓋的範圍以及它故意不涵蓋的範圍。這是某人決定是否閱讀其餘部分時會讀的段落。**Related:**— 連結到正在觸及的程式碼(指向儲存庫的相對連結)、其他 RFC、先前案例、外部參考資料**Discussion:**(可選) — 連結到設計正在或曾經被辯論的地方(PR、議題、討論串);不同意見的讀者應前往該處而非複製一份文件**Origin:**(可選) — 設計從何處移植或概括而來(如果有的話)
編號章節。 對於一份完整的 RFC,應包含以下內容:
- 摘要 — 出貨內容,用幾句話說明
- 動機 — 問題,附帶來自實際程式碼庫的證據
- 目前狀態 — 現有內容,根據程式碼驗證,而非依賴記憶
- 目標/非目標 — 明確說明兩者
- 設計 — 核心內容;根據工作流程或元件使用子章節,並在關鍵處使用真實的簽名/結構/程式碼區塊來固定設計。對於有爭議的選擇,保留被拒絕的替代方案及其失敗原因 — 次要決定用一句話說明,若選擇塑造了設計則使用
### 已考慮的替代方案子章節 - 測試 — 如何驗證設計
- 文件 — 隨之出貨的文件
- 排除範圍 — 明確說明並附帶理由:每個項目說明為何被排除以及什麼情況會改變此決定
- 風險 — 誠實的失敗模式,包括文件被誤讀的風險
- 未解決問題 — 在設計被視為鎖定之前必須解決的事項,相較於實作可以自由決定的事項;命名未知問題勝過在開發過程中默默解決它
- 決策 — 一個編號的鎖定決策表格;這正是讓交接變得簡單且避免重新爭論的關鍵。如果某項決策非顯著地限制了未來,該行應予以說明 — 一個決策的後果即為下一個決策的背景
- 階段 — 先上線什麼,什麼取決於什麼
根據 RFC 的份量調整規模。 一個小型設計鎖定 RFC 只需要標頭區塊、設計、非目標和決策表格。不要將兩頁的 RFC 擴充到十二個章節;也不要將系統級提案縮減為三個章節。對於所使用的任何子集,請保持章節編號連續。
寫作風格
- 每個主張都要以程式碼為基礎。「目前狀態」和「動機」應引用檔案、行級事實和測量數據 — 使用相對路徑連結它們。僅憑記憶論證的 RFC 只是帶有標題的虛構故事。
- 記錄決策時附帶理由 — 以及代價。 決策表格是合約;內文承載推理過程。被拒絕的替代方案應有一句說明其失敗原因(記錄了權衡的替代方案會保持被拒絕;僅記錄為「被拒絕」的替代方案會被重新提出)。明確關閉未來某扇門的決策,應在其行中說明。
- 及時勝於完美。 一個粗糙但存在的 RFC 勝過一個完美但從未寫出的 RFC(Oxide 的 RFD 規則:「及時而非完美」)。草稿文字可以粗糙;範圍段落和決策表格則不應如此。
- 誠實面對限制。 如果某個機制被延後、受限或已知不完整,請在 RFC 中說明,而不是讓讀者自行發現。失敗封閉的措辭(「拒絕」、「引發」、「故意未排程」)勝過樂觀的模糊表述。
- 密集勝於冗長。 寧願一個承載重點的段落勝過三個淺薄的段落。特別是索引的一行摘要:它必須足夠獨立,讓讀者可以略過 RFC。
工作流程
編號分配、從範本建立檔案、索引行與下一個可用編號更新、以及漂移檢測等簿記工作,都是機械性的,scripts/rfc_index.py 可以處理這些工作,避免手動分配可能產生的衝突:
python3 scripts/rfc_index.py check # 索引 vs 檔案、H1 vs 檔名、狀態、下一個可用編號
python3 scripts/rfc_index.py next # 下一個可用編號
python3 scripts/rfc_index.py new "Title" # 分配 + 實例化範本 + 索引行 + 遞增
python3 scripts/rfc_index.py new "Title" --number 42 # 保留編號,或重新建立已刪除的 RFC
(路徑相對於此技能的目錄;從儲存庫根目錄來看,腳本位於 skills/rfc-writer/scripts/rfc_index.py。僅 new 子命令為寫入操作;如果儲存庫不是當前工作目錄,請在子命令前後加上 --root DIR。)思考工作 — 設計內容、一行摘要的主張、狀態何時變更 — 則由您負責。
A — 建立新的 RFC
- 找到 RFC 目錄(
rfcs/或rfc/);如果不存在,請先執行工作流程 D。 - 分配下一個編號並實例化檔案:
rfc_index.py new "Title"— 它會建立編號、寫入範本、新增索引行並更新下一個可用編號。步驟 3 和 4 仍由您負責:它會留下未填寫的範本,並在索引中寫入字面值TODO: one-line summary。手動方式:從索引讀取下一個可用編號,並與ls交叉比對 — 編號在平行建立時會衝突。 - 根據
references/rfc-template.md的結構填入檔案,並根據設計的份量調整規模。在撰寫「目前狀態」之前,請先調查實際的程式碼 — 這是大部分的工作。 - 將佔位符的索引一行摘要替換為密集且自含的摘要。
B — 更新現有的 RFC
- 當工作部分或完全出貨時,更新
**Status:**行 — 並附註出貨的內容和時間(例如「2026-06-29 出貨:…;僅剩 P5」)。 - 如果執行結果偏離了設計,請在 RFC 中記錄分歧(狀態註記或相關章節中的修正案) — 不要默默地改寫歷史;決策日誌應保持僅追加。
- 在索引表格中反映狀態(以及如果結構變更,則反映一行摘要)。
- 被拒絕的設計保留 ❌ 並留在目錄中。
C — 維護索引
執行 rfc_index.py check — 它會報告每個沒有索引行的檔案(反之亦然)、H1 與檔名不符、標頭與表格狀態不一致、重複編號,以及不是真正可用的下一個可用編號。修復它所命名的問題(修復需要判斷:哪個狀態為真,一行摘要應為何),然後重新執行直到一切正常。報告哪些項目不同步。
D — 初始化 RFC 目錄
- 建立
rfcs/(除非使用者想要rfc/或該目錄已存在)。 - 從
references/index-template.md建立INDEX.md,填入專案名稱並將下一個可用編號設為0001。 - 不要碰
.gitignore— 說明提交與忽略目錄取決於使用者選擇。
參考資料
references/rfc-template.md— RFC 骨架,附帶各章節指引;在撰寫任何新的 RFC 之前請先閱讀references/index-template.md— INDEX.md 骨架;在初始化目錄時使用
相關技能
altitude-docs— 設計之後出貨的使用者面向文件;RFC 的文件章節指向它self-audit— 在合併之前,對執行 RFC 的分支進行對抗性審查keep-a-changelog— 記錄出貨內容;RFC 記錄為何以該方式建構