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

14 KiB
Raw Permalink Blame History

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. 安裝位置與檔案

建議放在:

<workspace>/plugins/continuity

generic/manual 路徑至少需要這些檔案:

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

可先用這個命令確認:

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 範例

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 接法

最小接法

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

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 怎麼準備

建議先從:

plugins/continuity/examples/openclaw.continuity.example.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

對照範例:

plugins/continuity/examples/approved-plan-receipt.example.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 前先驗證

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 範例

await plugin.writeReceipt({
  receiptDir: 'state/approved-plan-continuity',
  receipt,
});

receiptDir 先建立

cd <workspace>
mkdir -p state/approved-plan-continuity

9. manual/generic preflight 驗證流程

這一段是本文件最重要的實作 checklist。

Case A應該被 gate 擋下

測這種輸入:

  • task 已完成
  • 下一步已知
  • 同一 approved plan
  • 有 nextDerivedAction
  • 沒有 dispatchReceipt
  • closure 想直接完成

例如:

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 中,存在:
[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 本體測試

cd <workspace>/plugins/continuity
npm test

Step 2快速 smoke

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

確認輸出真的含有:

[APPROVED_PLAN_CONTINUITY_GATE]

12. 宿主實作者最常踩的坑

坑 1只有 planner output沒有實際 prompt injection

這會讓 continuity 只停在「有計算」,沒有真正影響 agent 行為。

坑 2把 dry-run next action 當成已 dispatch

nextDerivedAction != null 不等於 dispatchReceipt != null

坑 3payload 缺少 planIdcurrentTask

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作為最低整合基準。