Files
approved-plan-continuity-ha…/plugins/continuity/GENERIC_INTEGRATION.zh-TW.md

591 lines
14 KiB
Markdown
Raw 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.
# Continuity PluginGeneric / Manual Integration Runbook
> 目標讀者:**沒有 `force-recall` hook 的宿主 / orchestrator / planner 實作者**
>
> 這份文件專門說明:當你的 host 沒有 `hooks/force-recall/handler.ts` 時,如何仍然安裝 continuity plugin、提供 payload、執行 manual/generic preflight並驗證 continuity gate block 真的有生效。
> 英文版:`GENERIC_INTEGRATION.md`
---
## 0. 這份 runbook 解決什麼問題
如果你的宿主屬於以下任一情境,請優先看這份:
- 你沒有 `force-recall` hook。
- 你有自己的 planner / preflight / orchestrator。
- 你想手動把 approved-plan 狀態餵給 continuity plugin。
- 你需要一份「最少要給哪些欄位」的 payload 規格。
- 你需要知道如何驗證 continuity block 有真的擋住錯誤結案,而不是只是在文件裡說可以。
一句話總結:
**沒有 `force-recall` 也能裝 continuity plugin但你必須自己提供 generic continuity input並把回傳的 block 接進 agent prompt。**
---
## 1. 最小安裝目標
安裝完成後,至少要達到以下結果:
- 你的 host 能 import `plugins/continuity/src/index.mjs`
- 你的 host 能呼叫:
- `runGenericPreflightContinuityAdapter(...)`,或
- `runManualContinuityPreflight(...)`
- 你的 host 能把回傳的 `out.block` prepend 到 agent 會看到的 prompt/body
- 當任務完成、但還有下一步且沒有真實 dispatch receipt 時continuity gate 會擋住「直接結案」
- `cd plugins/continuity && npm test` 可通過
---
## 2. 安裝位置與檔案
建議放在:
```text
<workspace>/plugins/continuity
```
generic/manual 路徑至少需要這些檔案:
```text
plugins/continuity/
package.json
src/index.mjs
src/adapters/generic-preflight.mjs
src/config/defaults.mjs
src/config/schema.mjs
src/continuity/engine.mjs
src/continuity/evaluator.mjs
src/continuity/receipt-validator.mjs
src/continuity/receipt-store.mjs
src/continuity/types.md
examples/openclaw.continuity.example.json
examples/approved-plan-receipt.example.json
README.zh-TW.md
README.md
GENERIC_INTEGRATION.zh-TW.md
```
可先用這個命令確認:
```bash
cd <workspace>
find plugins/continuity -maxdepth 3 -type f | sort
```
---
## 3. generic/manual 路徑會用到哪些 API
`src/index.mjs` 已經 re-export 這幾個你最需要的 API
- `defaultConfig`
- `normalizeContinuityConfig()`
- `validateContinuityConfig()`
- `buildGenericContinuityInput()`
- `createGenericPreflightContinuityAdapter()`
- `runGenericPreflightContinuityAdapter()`
- `runManualContinuityPreflight()`
- `validateReceipt()`
- `isValidReceipt()`
- `writeReceipt()`
如果你只想最短路徑完成整合,通常會直接用:
- `runGenericPreflightContinuityAdapter(...)`
- `runManualContinuityPreflight(...)`
- `writeReceipt(...)`
---
## 4. continuity input 最小 contract
generic adapter 吃的是 **host-agnostic continuity input**
`src/continuity/types.md`,實務上最少要準備:
- `planId`: string
- `currentTask`: string
- `taskState`: string | null
- `nextDerivedAction`: object | null
- `replyClosureState`: string | null
- `dispatchReceipt`: object | null
- `nextTaskKnown`: boolean
- `sameApprovedPlan`: boolean
- `taskBoundaryStop`: boolean
- `highRiskStop`: boolean
可選欄位:
- `nextTaskId`: string | null
- `nextTaskKey`: string | null
- `derivedAction`: object | null
- `metadata`: object
### 最小可運作 payload 範例
```js
const source = {
planId: 'approved-plan-1',
currentTask: 'task-3',
taskState: 'complete',
nextTaskKnown: true,
sameApprovedPlan: true,
taskBoundaryStop: true,
highRiskStop: false,
nextTaskId: 'task-4',
nextDerivedAction: {
type: 'message_subagent',
task: 'continue with task-4',
},
replyClosureState: 'completed',
dispatchReceipt: null,
metadata: {
host: 'custom-orchestrator',
},
};
```
### 什麼情況下這個 payload 會觸發 gate
常見高風險情境:
- `taskState = 'complete'`
- `nextTaskKnown = true`
- `sameApprovedPlan = true`
- `nextDerivedAction != null`
- `dispatchReceipt = null`
- `replyClosureState = 'completed'`
這類情況通常代表:**目前 task 做完了、下一步也知道了,但你還沒有真實派發 receipt卻準備把整件事收掉。**
這正是 continuity gate 要攔的核心情境。
---
## 5. generic adapter 接法
### 最小接法
```js
import plugin from './plugins/continuity/src/index.mjs';
const out = plugin.runGenericPreflightContinuityAdapter({
config: plugin.defaultConfig,
source,
});
if (out?.block) {
agentPromptBody = `${out.block}\n${agentPromptBody}`;
}
```
### 回傳值你至少要看哪些欄位
`out` 常用欄位:
- `out.input`
- `out.result`
- `out.evaluation`
- `out.block`
- `out.meta.adapterName`
- `out.meta.hostAgnostic`
### 什麼時候不該忽略 `out.block`
只要 `out.block` 非空,就表示 plugin 產生了應注入 prompt 的 continuity gate block。
**不要只把 `evaluation` 印 log就算整合完成。**
真正有完成整合的最低標準,是:
- 有拿到 `out.block`
- 有把它接到 agent prompt/body
- agent 後續真的會看到該 block
---
## 6. manual runner 接法
如果你沒有完整 generic source builder或者只想先做一次單點 preflight 驗證,可用 manual runner
```js
import plugin from './plugins/continuity/src/index.mjs';
const out = plugin.runManualContinuityPreflight({
config: plugin.defaultConfig,
planId: 'approved-plan-1',
currentTask: 'task-3',
taskState: 'complete',
nextDerivedAction: {
type: 'message_subagent',
task: 'continue with task-4',
},
replyClosureState: 'completed',
nextTaskKnown: true,
sameApprovedPlan: true,
taskBoundaryStop: true,
dispatchReceipt: null,
});
if (out?.block) {
agentPromptBody = `${out.block}\n${agentPromptBody}`;
}
```
### manual runner 適合什麼情況
- 你還沒有完整 planner但想先驗證 gate 行為
- 你要做 integration smoke test
- 你在 migration 階段,先用手動欄位對接舊宿主
### manual runner 不代表可以跳過正式整合
manual runner 比較像 **preflight harness**,不是完整 host integration 的替代品。
等你確認行為正確後,仍建議回到 generic adapter把 host 真實資料映射進 source。
---
## 7. config 怎麼準備
建議先從:
```text
plugins/continuity/examples/openclaw.continuity.example.json
```
開始,內容大致如下:
```json
{
"enabled": true,
"planMatchers": ["approved-plan"],
"legalTerminalStates": [
"waiting_user",
"blocked",
"pending_verification"
],
"receiptDir": "state/approved-plan-continuity",
"requireRealDispatchReceipt": true,
"allowReplyClosureWithoutDispatch": false,
"debug": false,
"adapter": {
"forceRecall": {
"enabled": true,
"injectBlockLabel": "APPROVED_PLAN_CONTINUITY_GATE"
},
"genericPreflight": {
"enabled": true,
"injectBlockLabel": "APPROVED_PLAN_CONTINUITY_GATE"
}
}
}
```
### generic/manual 路徑先檢查這些欄位
- [ ] `enabled = true`
- [ ] `planMatchers` 符合你的 approved plan 名稱
- [ ] `legalTerminalStates` 符合你的 closure policy
- [ ] `receiptDir` 可寫
- [ ] `requireRealDispatchReceipt = true`(通常建議保持)
- [ ] `allowReplyClosureWithoutDispatch = false`(通常建議保持)
- [ ] `adapter.genericPreflight.enabled = true`
- [ ] `adapter.genericPreflight.injectBlockLabel` 有你想要的 label
---
## 8. receipt 怎麼提供
如果你的宿主真的有派發下一步,不要只在 metadata 或 log 裡寫「已派發」,請提供真實 `dispatchReceipt`
### 最小 receipt shape
依目前 validator至少要有
- `planId`
- `currentTask`
- `nextDerivedAction`
- `dispatchedAt`
- `dispatchRunId`
- `childSessionKey`
- `replyClosureState`
對照範例:
```text
plugins/continuity/examples/approved-plan-receipt.example.json
```
範例內容:
```json
{
"planId": "example-plan",
"currentTask": "task-01",
"nextDerivedAction": {
"kind": "delegate",
"target": "subagent",
"task": "placeholder"
},
"dispatchedAt": "2026-04-24T16:43:00+08:00",
"dispatchRunId": "example-run",
"childSessionKey": "session-placeholder",
"replyClosureState": "pending_verification"
}
```
### 寫 receipt 前先驗證
```js
import plugin from './plugins/continuity/src/index.mjs';
const receiptValidation = plugin.validateReceipt(receipt);
if (!receiptValidation.ok) {
throw new Error(`Invalid continuity receipt: ${receiptValidation.errors.join('; ')}`);
}
```
### 寫 receipt 範例
```js
await plugin.writeReceipt({
receiptDir: 'state/approved-plan-continuity',
receipt,
});
```
### receiptDir 先建立
```bash
cd <workspace>
mkdir -p state/approved-plan-continuity
```
---
## 9. manual/generic preflight 驗證流程
這一段是本文件最重要的實作 checklist。
### Case A應該被 gate 擋下
測這種輸入:
- task 已完成
- 下一步已知
- 同一 approved plan
- 有 nextDerivedAction
- 沒有 dispatchReceipt
- closure 想直接完成
例如:
```js
const out = plugin.runGenericPreflightContinuityAdapter({
config: plugin.defaultConfig,
source: {
planId: 'approved-plan-1',
currentTask: 'task-3',
taskState: 'complete',
nextTaskKnown: true,
sameApprovedPlan: true,
taskBoundaryStop: true,
nextTaskId: 'task-4',
nextDerivedAction: { type: 'message_subagent', task: 'continue' },
replyClosureState: 'completed',
dispatchReceipt: null,
},
});
```
你應該預期:
- `out.block` 非空
- `out.evaluation` 顯示這不是可安全直接結案的狀態
- agent prompt 若接上此 block應被提醒不可把整件事直接收掉
### Case B有合法 terminal state不應誤擋
例如:
- `replyClosureState = 'waiting_user'`
-`blocked`
-`pending_verification`
你應該預期:
- 不會把合法等待/阻塞/待驗證情境誤當成錯誤結案
### Case C有真實 receipt應與 dry-run 區分
`dispatchReceipt` 換成合法 receipt object再重跑一次。
你要確認:
- plugin 有辨識 receipt
- 行為和 `dispatchReceipt = null` 時不同
- 不是只因為 `nextDerivedAction` 存在,就一律當成已派發
---
## 10. 如何驗證 continuity block 真的有被接進 prompt
這一步很常被忽略,但它才是整合成功與否的分水嶺。
### 最低驗證標準
你至少要證明以下三件事:
1. plugin 有回傳 `out.block`
2. host 有把 `out.block` prepend 到 agent prompt/body
3. 最終 agent 真正看到的 prompt 中,存在:
```text
[APPROVED_PLAN_CONTINUITY_GATE]
```
### 建議做法
- 在 preflight / orchestrator 測試輸出中印出最終 prompt 前段
- 或在 debug mode 下 dump prompt body
- 或加一個 integration smoke test直接 assert block label 存在
### 不能只驗證什麼
以下都 **不夠**
- 只看到 `out.evaluation`
- 只看到 console log 說有 run adapter
- 只看到 code 裡有 prepend 那一行,但沒有實際驗證 prompt 結果
---
## 11. 建議 smoke test 順序
### Step 1plugin 本體測試
```bash
cd <workspace>/plugins/continuity
npm test
```
### Step 2快速 smoke
```bash
cd <workspace>/plugins/continuity
node test/continuity.smoke.test.mjs
```
### Step 3你的 generic/manual host integration test
如果你有自家 orchestrator請至少補一個 smoke case
- 無 receipt + 想 completed → 應被 gate
- 合法 terminal state → 不應誤擋
- 有合法 receipt → 行為應與無 receipt 不同
### Step 4人工檢查 prompt block
確認輸出真的含有:
```text
[APPROVED_PLAN_CONTINUITY_GATE]
```
---
## 12. 宿主實作者最常踩的坑
### 坑 1只有 planner output沒有實際 prompt injection
這會讓 continuity 只停在「有計算」,沒有真正影響 agent 行為。
### 坑 2把 dry-run next action 當成已 dispatch
`nextDerivedAction != null` 不等於 `dispatchReceipt != null`
### 坑 3payload 缺少 `planId` 或 `currentTask`
`buildGenericContinuityInput()` 若拿不到這些關鍵欄位,可能直接回不出有效 input。
### 坑 4把合法 terminal state 設定錯
例如你宿主其實接受 `waiting_user`,但 config 沒放進 `legalTerminalStates`,就可能造成誤判。
### 坑 5receipt 寫檔了,但內容不合法
`validateReceipt()`,不要跳過。
### 坑 6只測 happy path
至少要測:
- 應 fail 的情況
- 應 pass / 不誤擋的情況
- 有 receipt 與無 receipt 的差異
---
## 13. 一頁版 generic/manual checklist
### 安裝
- [ ] 複製 `plugins/continuity` 到 workspace
- [ ] 確認 `src/index.mjs` 可 import
- [ ] 建立 `state/approved-plan-continuity/`
### config
- [ ] 套用 example config
- [ ] 開啟 `adapter.genericPreflight.enabled`
- [ ] 確認 `receiptDir` 可寫
### host 接法
- [ ] 準備 generic continuity payload
- [ ] 呼叫 `runGenericPreflightContinuityAdapter(...)``runManualContinuityPreflight(...)`
- [ ]`out.block` prepend 到 agent prompt/body
### receipt
- [ ] 真有派發時才產生 receipt
- [ ]`validateReceipt()`
- [ ]`writeReceipt()`
### 驗證
- [ ] `cd plugins/continuity && npm test`
- [ ] 驗證無 receipt 時會擋錯誤結案
- [ ] 驗證合法 terminal state 不誤擋
- [ ] 驗證最終 prompt 含 continuity block
---
## 14. 什麼叫 generic/manual 整合完成
只有在以下條件都成立時,才算真的完成:
- 不是只完成 plugin 安裝,而是 host 真的有呼叫 adapter
- 不是只拿到 evaluation而是 block 真的有注入 agent prompt
- 不是只做 dry-run而是實際 dispatch 時可提供真實 receipt
- 不是只測 happy path而是有驗證 gate fail/pass 的差異
- 文件足夠讓陌生宿主實作者知道自己要準備哪些欄位、怎麼驗證
如果這五點都做到,才算接近「沒有 `force-recall` 的宿主也能照文件安裝」。
---
## 15. 參考文件
- `plugins/continuity/README.zh-TW.md`
- `plugins/continuity/README.md`
- `plugins/continuity/AGENT_GUIDE.zh-TW.md`
- `plugins/continuity/AGENT_GUIDE.md`
- `plugins/continuity/src/continuity/types.md`
- `plugins/continuity/examples/openclaw.continuity.example.json`
- `plugins/continuity/examples/approved-plan-receipt.example.json`
如果你的宿主未來會新增專屬 adapter也建議保留這份 generic/manual runbook作為最低整合基準。