Pseudo-Code 文件輸出模板

寫入 spec/{feature}/{feature}.pseudo.md 的標準格式。 Partner skill 在最終輸出時組裝此模板。

全域編碼:被下游(Code Javadoc/別份 spec)引用的 §-section(公開方法契約)是本階段「對外」項目,於該節標題掛全域碼 §{SHORT}-PC-{N}(NUMBER 沿用本地 § 號、保留完整小數階層,如 §7.3.6 → §{SHORT}-PC-7.3.6)。{SHORT}spec-codes.md純內部、未被跨界引用的子節點維持本地 §N.M.Z 不動(兩層並存)。引用上游 FS 邊界用其全域碼 §{SHORT}-FS-{n}。格式見 workflow §可追溯性「全域編碼」。


模板全文

# {功能名稱} 虛擬碼
 
> 來源:[OOA]({feature}.ooa.md)
> 上游 functional spec:[fp]({feature}.fp.md)
> 最後同步 commit:`{git sha — 由實作者於每次 PR 更新}`
> 狀態:living doc — 程式碼異動時必須同步本檔
 
---
 
## 0. 名詞表
 
| 名詞 | 中文 | 定義 |
|---|---|---|
| {Term} | {中文} | {定義 — 與 OOA 領域詞彙一致} |
| ... | ... | ... |
 
> 名詞表必須涵蓋所有出現在後續虛擬碼中的非標準詞彙。
> 未列入名詞表的詞不得出現在虛擬碼中。
 
---
 
## 1. {高階流程名稱}
 
### 1.1 {子流程名稱}
 
**對應實作**`com.leanplay.{module}.{layer}.{ClassName}#{methodName}`
 
**輸入**
- `arg1`: {說明}
- `arg2`: {說明}
 
**輸出**
- {說明}
 
**虛擬碼**
 
```pseudo
function {methodName}(arg1, arg2):
    // 1.1.1 {步驟說明}
    if {condition}:
        {action}
    // 1.1.2 {步驟說明}
    {action}
    // 1.1.3 {步驟說明}
    while {condition}:
        {action}
    return {result}
```
 
**設計理由**
- 為何選此演算法(如:greedy vs. backtracking)
- 替代方案:{未採用的方案與原因}
 
**例外情境**
- §1.1.1 — `InvalidInputException` when {condition}
- §1.1.3 — `CapacityExceededException` when {condition}
 
**測試對應**
- 正常:functional-spec §UC A1 正常值
- 異常:functional-spec `§{SHORT}-FS-2`
- 邊界:functional-spec `§{SHORT}-FS-5`
 
---
 
### 1.2 {下一個子流程}
 
(同上格式)
 
---
 
## 2. {下一個高階流程}
 
(同上格式)
 
---
 
## 廢棄歷史
 
> 為保留追溯,廢棄段落不刪除,僅標記。
 
### ~~§1.3 {舊段落名}~~
 
**廢棄於**:{commit sha} / {日期}
**廢棄原因**:{原因}
**替代**:見 §1.5
 
---
 
## 簽核
 
- **編輯者**____ / 日期:____
- **Reviewer**____ / 日期:____

各區塊撰寫指南

標頭區塊(檔案最上方)

  • 來源上游 functional spec:相對路徑 markdown 連結(本檔在 spec/{feature}/(兩層深);同 feature 各階段為同層 sibling、直接用檔名),方便 IDE / 編輯器跳轉
  • 最後同步 commit:實作 PR 合併後由實作者更新;首次建立時填 初版
  • 狀態行:照抄,提醒讀者這是 living doc

連結慣例(GFM)

完整規則見 product-module-development-workflow.md §可追溯性「連結慣例(GFM)」。本階段重點與一條不變量

  • breadcrumb / 跨檔:相對路徑 markdown 連結 [文字](../path.md)
  • 同檔 §章節跳轉:§X、§X.Y 是標準 heading(## 1.### 1.1),同檔引用用 markdown 連結 [§1.1](#11-子流程名稱)
  • §編號穩定性與連結語法無關:§ 為 append-only、永不重編;廢棄段落保留並標 ~~刪除線~~Code 的 Javadoc 引用的是 § 文字編號(如 §1.1.2),不是 block-id——兩套機制獨立,不要用 ^id 取代 § 編號。
  • 標籤一律含非數字字元;純數字 #1 不是合法 tag。

§0 名詞表

  • 三欄:英文(與 class 名一致)、中文、定義
  • 動詞與名詞並列
  • 不放普世概念(RepositoryService 不需要解釋;SlidingWindowpending RSR 需要)
  • 領域名詞對齊 OOA 領域詞彙與 vault 根 glossary.md 的標準形;遇 glossary 沒有的新領域術語,提醒使用者登記

每個子流程(§X.Y)必含區塊

區塊必須說明
對應實作完整 package + class + method
輸入一行一參數,含領域意涵
輸出一句話
虛擬碼步驟編號 §X.Y.Z
設計理由⚠️若有分歧必填
例外情境⚠️若有 raise 必填
測試對應指向 functional spec 的 use case I/O 範例或邊界條件編號

虛擬碼語法(重申)

function name(arg):
    // §X.Y.Z 中文註解
    if condition:
        action
    while condition:
        action
    for item in collection:
        action
    raise SomeException
    return result

關鍵字:function / if / else / while / for / in / return / raise / and / or / not / is / null

  • 賦值用 :=(與 == 視覺區隔)
  • 不寫 Java 型別
  • 不寫 DI、log、技術 exception
  • 中文註解就是 §X.Y.Z 編號的載體;對外 §-section 的權威引用形式為全域碼 §{SHORT}-PC-{N},code Javadoc 引用此全域碼

廢棄段落

### ~~§1.3 舊的批次擴張規則~~
 
**廢棄於**:2026-06-15 commit `abc123`
**廢棄原因**:客戶要求改為齊套優先,原批次優先邏輯不適用
**替代**:見 §1.5

不刪除、不重編號。讓 Javadoc 中的舊引用仍可定位。


完整範例片段

來自 spec/fine-schedule/fine-schedule.pseudo.md(節錄):

## 1. 主迴圈
 
### 1.1 滑動窗口推進
 
**對應實作**`com.leanplay.fineschedule.application.FineScheduler#run`
 
**輸入**
- `request`: 含 VSMs、Rough RSRs、Resources、Horizon
- `windowSize`: 預設一日
 
**輸出**
- `FineScheduleResult`:ScheduledOperations + 未排工單 + Trace
 
**虛擬碼**
 
```pseudo
function run(request, windowSize):
    // 1.1.1 初始化
    cursor := request.horizon.start
    ledger := new SchedulingLedger()
    pending := request.roughRSRs
    // 1.1.2 主迴圈
    while cursor < request.horizon.end:
        window := SlidingWindow(cursor, cursor + windowSize)
        // 1.1.3 三策略協作
        supply := supplyStrategy.supplyFor(window, request.resources)
        decision := assignmentStrategy.assign(window, supply, pending, ledger)
        result := stackingStrategy.stack(decision, supply, ledger)
        // 1.1.4 推進
        pending := pending minus result.confirmed
        cursor := cursor + windowSize
    return FineScheduleResult(ledger.scheduled, pending, ledger.trace)
```
 
**設計理由**
- 為何 sliding window 而非一次性整段最佳化:客戶資料規模 (~10k 工單) 使整段 NP-hard 不可行;window 大小可調,預設 1 日對齊工廠日曆。
- 替代方案:CP-SAT 整段求解(已試,求解時間不可接受)
 
**例外情境**
- 主迴圈本身不 raise;下游策略可 raise,由主迴圈不捕捉直接往上拋
 
**測試對應**
- 正常:functional-spec §UC A1 正常值(單一資源、24h horizon)
- 邊界:functional-spec `§{SHORT}-FS-3`(horizon = 1 window size,跑一輪即結束)
- 邊界:functional-spec `§{SHORT}-FS-4`(pending 為空,主迴圈跑 0 輪)