diff --git a/plugins/continuity/AGENT_GUIDE.md b/plugins/continuity/AGENT_GUIDE.md new file mode 100644 index 0000000..9a0cb41 --- /dev/null +++ b/plugins/continuity/AGENT_GUIDE.md @@ -0,0 +1,701 @@ +# Continuity Plugin MVP: Agent Installation and Operations Guide + +> Audience: OpenClaw agents / implementers +> +> This is a hands-on runbook and checklist, not a conceptual overview. +> If you only need to know whether to install it, how to wire it, and how to verify it, start here instead of reading the full README first. + +## 0. When should you install this plugin? + +### Recommended situations + +Install this plugin if any of the following are true in your workspace: + +- You run **approved-plan** style long-running or multi-step tasks. +- You do not want the agent to treat a finished task as “the whole job is done” by default. +- You need the agent to decide whether there is a **next task to dispatch** inside the same approved plan. +- You need a **dispatch receipt** as evidence that the next step was actually dispatched, instead of only saying so in prose. +- You already use `hooks/force-recall/handler.ts` and want to inject the approved-plan continuity gate into the agent prompt. +- You do not have a `force-recall` hook, but you do have your own preflight / planner / orchestrator and want to call the generic continuity adapter directly. + +### Cases where you can skip it for now + +You can defer installation, or treat this plugin as reference only, when: + +- Your workspace has no approved-plan / auto-chain / dispatch flow. +- You only need ordinary single-turn chat behavior and do not need a continuity hard gate. +- You do not have a `force-recall` hook and you also do not plan to add generic glue code in your own host/orchestrator. + +--- + +## 1. Installation target and expected outcome + +After installation, the minimum expected result is: + +- your host (for example `force-recall` or your own preflight path) can load `plugins/continuity/src/index.mjs` +- the host calls one of these during preflight: + - `runForceRecallContinuityAdapter(...)` + - `runGenericPreflightContinuityAdapter(...)` + - `runManualContinuityPreflight(...)` +- the agent prompt contains a visible `[APPROVED_PLAN_CONTINUITY_GATE] ... [/APPROVED_PLAN_CONTINUITY_GATE]` block +- when an approved-plan task is finished but there is no real next-step dispatch receipt, the gate blocks an incorrect closure +- plugin tests and the relevant host smoke path pass + +--- + +## 2. Where should it live? + +### Standard location + +Place the plugin here inside the workspace: + +```text +/plugins/continuity +``` + +Recommended layout (adjust as needed for your host): + +```text +/ + hooks/ + force-recall/ + handler.ts + HOOK.md + plugins/ + continuity/ + AGENT_GUIDE.zh-TW.md + AGENT_GUIDE.md + README.zh-TW.md + README.md + HOOK.md + GENERIC_INTEGRATION.zh-TW.md + GENERIC_INTEGRATION.md + package.json + examples/ + src/ + test/ + scripts/ + test_force_recall_long_task_preflight.mjs +``` + +### Actual path for this document set + +```text +/home/alice/.openclaw/workspace/plugins/continuity +``` + +--- + +## 3. Pre-install checklist + +Before you start, confirm: + +- [ ] your workspace has `hooks/force-recall/handler.ts`, or you already know which generic/manual host path will call the plugin +- [ ] your workspace has `scripts/test_force_recall_long_task_preflight.mjs` if you intend to use the `force-recall` path +- [ ] your workspace is willing to adopt the approved-plan continuity hard gate +- [ ] you understand that `force-recall` is the most mature adapter, but not the only host path +- [ ] you understand that the receipt is a **minimum contract**, not a complete workflow engine + +Quick checks: + +```bash +cd +ls plugins/continuity/package.json +ls plugins/continuity/src/index.mjs +ls hooks/force-recall/handler.ts +ls scripts/test_force_recall_long_task_preflight.mjs +``` + +If you do not have `force-recall`, see `GENERIC_INTEGRATION.zh-TW.md` or `GENERIC_INTEGRATION.md` for the generic/manual path. + +--- + +## 4. Required files + +### Required files + +At minimum, you should have: + +```text +plugins/continuity/ + package.json + src/index.mjs + src/adapters/force-recall.mjs + src/adapters/generic-preflight.mjs + src/config/defaults.mjs + src/config/schema.mjs + src/continuity/evaluator.mjs + src/continuity/receipt-validator.mjs + src/continuity/receipt-store.mjs + examples/openclaw.continuity.example.json + examples/approved-plan-receipt.example.json +``` + +### Strongly recommended files + +```text +plugins/continuity/ + README.zh-TW.md + README.md + HOOK.md + AGENT_GUIDE.zh-TW.md + AGENT_GUIDE.md + GENERIC_INTEGRATION.zh-TW.md + GENERIC_INTEGRATION.md + test/continuity.config.test.mjs + test/continuity.receipt-validator.test.mjs + test/continuity.receipt-store.test.mjs + test/continuity.evaluator.test.mjs + test/continuity.plugin.test.mjs + test/continuity.smoke.test.mjs +``` + +### If files are missing, what should you restore first? + +Priority order: + +1. `src/index.mjs` +2. `src/adapters/force-recall.mjs` +3. `src/adapters/generic-preflight.mjs` +4. `src/continuity/evaluator.mjs` +5. `src/continuity/receipt-validator.mjs` +6. `src/continuity/receipt-store.mjs` +7. `examples/openclaw.continuity.example.json` +8. `test/continuity.smoke.test.mjs` + +--- + +## 5. Direct installation steps + +### Path A: you are already inside the same repo/workspace + +- [ ] confirm `plugins/continuity` exists +- [ ] confirm `package.json` and `src/index.mjs` exist +- [ ] confirm your chosen host entrypoint exists + +Verification: + +```bash +cd +find plugins/continuity -maxdepth 3 -type f | sort +``` + +### Path B: you want to copy it into another OpenClaw workspace + +From the source workspace: + +```bash +cd +rsync -av plugins/continuity/ /plugins/continuity/ +``` + +Then verify inside the target workspace: + +```bash +cd +find plugins/continuity -maxdepth 3 -type f | sort +``` + +--- + +## 6. How to apply the example config + +### Step 1: copy the example continuity config + +Source example: + +```text +plugins/continuity/examples/openclaw.continuity.example.json +``` + +Example content: + +```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" + } + } +} +``` + +### Step 2: only change the minimum fields first + +Usually you only need to verify these first: + +- [ ] `enabled` +- [ ] `planMatchers` +- [ ] `legalTerminalStates` +- [ ] `receiptDir` +- [ ] `requireRealDispatchReceipt` +- [ ] `allowReplyClosureWithoutDispatch` +- [ ] `adapter.forceRecall.enabled` +- [ ] `adapter.forceRecall.injectBlockLabel` +- [ ] `adapter.genericPreflight.enabled` +- [ ] `adapter.genericPreflight.injectBlockLabel` + +### Step 3: recommended minimum application strategy + +#### Situation A: you want the current approved-plan MVP behavior + +Use the example as-is in most cases: + +- `planMatchers = ["approved-plan"]` +- `legalTerminalStates = ["waiting_user", "blocked", "pending_verification"]` +- `requireRealDispatchReceipt = true` +- `allowReplyClosureWithoutDispatch = false` + +#### Situation B: your planner uses a different plan name + +Adjust: + +```json +{ + "planMatchers": ["your-approved-plan-name"] +} +``` + +#### Situation C: you want a different injected block label + +Adjust: + +```json +{ + "adapter": { + "genericPreflight": { + "enabled": true, + "injectBlockLabel": "YOUR_CONTINUITY_GATE" + } + } +} +``` + +### Step 4: create the receipt directory up front + +```bash +cd +mkdir -p state/approved-plan-continuity +``` + +--- + +## 7. How to wire the host/hook + +## 7.1 Integration points to confirm + +The most mature integration point is still: + +```text +hooks/force-recall/handler.ts +``` + +But that is no longer the only path. A generic or manual preflight host can also call the plugin directly. + +A typical host-side flow should look like this: + +1. run long-task preflight / wrapper, or equivalent host preflight +2. run gate lock if you have one +3. run auto-chain planner, or derive next action in your own planner +4. load the continuity plugin +5. call the appropriate adapter +6. prepend the returned continuity block into the prompt body shown to the agent + +### At minimum, look for these symbols + +- `plugins/continuity/src/index.mjs` +- `runForceRecallContinuityAdapter` +- `runGenericPreflightContinuityAdapter` +- `runManualContinuityPreflight` +- `APPROVED_PLAN_CONTINUITY_GATE` + +Example checks: + +```bash +cd +grep -n "runForceRecallContinuityAdapter" hooks/force-recall/handler.ts +grep -n "APPROVED_PLAN_CONTINUITY_GATE" hooks/force-recall/handler.ts +``` + +## 7.2 Minimum `force-recall` wiring example + +```js +import plugin from './plugins/continuity/src/index.mjs'; + +const out = plugin.runForceRecallContinuityAdapter({ + config: plugin.defaultConfig, + wrapperResult, + autoChainPlanResult, +}); + +if (out?.block) { + context.bodyForAgent = `${out.block}\n${context.bodyForAgent}`; +} +``` + +## 7.3 Minimum generic/manual wiring example + +If you do **not** have `force-recall`, feed your own preflight result into the generic adapter: + +```js +import plugin from './plugins/continuity/src/index.mjs'; + +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, + }, +}); +``` + +If you only need a one-off manual continuity preflight: + +```js +const out = plugin.runManualContinuityPreflight({ + config: plugin.defaultConfig, + planId: 'approved-plan-1', + currentTask: 'task-3', + taskState: 'complete', + nextDerivedAction: { type: 'message_subagent', task: 'continue' }, + replyClosureState: 'waiting_user', +}); +``` + +## 7.4 Real checks implementers should perform + +- [ ] the host can import `plugins/continuity/src/index.mjs` +- [ ] the chosen adapter function exists +- [ ] relevant input is passed in +- [ ] `config` is passed in, at least `defaultConfig` to start +- [ ] when `out.block` is non-empty, it is prepended to the agent-facing prompt/body +- [ ] the final agent prompt really contains the continuity gate block + +## 7.5 Relationship between receipt and continuity input + +If your flow really dispatches the next step, do **not** only say “dispatched” in plain text. Emit a real `dispatchReceipt`. + +Minimum receipt shape: + +- `planId` +- `currentTask` +- `nextDerivedAction` +- `dispatchedAt` +- `dispatchRunId` +- `childSessionKey` +- `replyClosureState` + +Reference example: `plugins/continuity/examples/approved-plan-receipt.example.json` + +--- + +## 8. Suggested implementation workflow + +### Checklist: first-time plugin integration into an existing workspace + +- [ ] create/copy `plugins/continuity` +- [ ] confirm `src/index.mjs` can be loaded by the host +- [ ] confirm the example config is readable +- [ ] create `state/approved-plan-continuity/` +- [ ] confirm your host has a continuity adapter call site +- [ ] confirm the continuity block is prepended into the agent prompt/body +- [ ] if a real dispatch occurs, persist a receipt +- [ ] run plugin tests +- [ ] run the host smoke path +- [ ] confirm the injected prompt block is present + +### Checklist: first-time receipt persistence + +- [ ] prepare a valid receipt object +- [ ] validate it with `validateReceipt()` / `isValidReceipt()` first +- [ ] persist it with `writeReceipt({ receiptDir, receipt })` +- [ ] confirm the receipt file lands in `state/approved-plan-continuity/` +- [ ] rerun continuity smoke tests so you do not mistake “looks like a receipt” for a real passing state + +--- + +## 9. How to run smoke tests after installation + +### Minimum smoke test + +Run the plugin tests first: + +```bash +cd /plugins/continuity +npm test +``` + +If you only want a quick plugin-only verification: + +```bash +cd /plugins/continuity +node test/continuity.smoke.test.mjs +``` + +### Hook integration smoke test + +```bash +cd +node scripts/test_force_recall_long_task_preflight.mjs +node --check hooks/force-recall/handler.ts +``` + +### Recommended full smoke order + +This order makes failures easier to localize: + +1. plugin unit/integration tests + +```bash +cd /plugins/continuity +npm test +``` + +2. hook syntax check + +```bash +cd +node --check hooks/force-recall/handler.ts +``` + +3. hook preflight smoke test + +```bash +cd +node scripts/test_force_recall_long_task_preflight.mjs +``` + +4. manual inspection: confirm the block is really injected + +Look for: + +```text +[APPROVED_PLAN_CONTINUITY_GATE] +``` + +--- + +## 10. Smoke test success criteria + +The minimum install is successful only if all of these are true: + +- [ ] `npm test` passes +- [ ] `node --check hooks/force-recall/handler.ts` passes, if you use that path +- [ ] `node scripts/test_force_recall_long_task_preflight.mjs` passes, if you use that path +- [ ] the continuity gate block is visible in test output or debug prompt output +- [ ] when there is no receipt, the gate fails / hard-gates the missing-dispatch case +- [ ] legal terminal states such as `waiting_user`, `blocked`, or `pending_verification` are not misclassified + +--- + +## 11. What to check first when something fails + +Follow this order. Do not start by changing random code. + +### Layer 1: path and file existence + +- [ ] `plugins/continuity/src/index.mjs` exists +- [ ] your host entrypoint exists +- [ ] `plugins/continuity/examples/openclaw.continuity.example.json` exists +- [ ] `plugins/continuity/examples/approved-plan-receipt.example.json` exists + +Commands: + +```bash +cd +ls plugins/continuity/src/index.mjs +ls hooks/force-recall/handler.ts +ls plugins/continuity/examples/openclaw.continuity.example.json +ls plugins/continuity/examples/approved-plan-receipt.example.json +``` + +### Layer 2: is the host really calling the plugin? + +- [ ] your host imports or dynamically loads the continuity plugin +- [ ] your host calls the correct adapter function +- [ ] your host prepends `out.block` into the agent prompt/body + +### Layer 3: is the config sensible? + +Check first: + +- [ ] `enabled === true` +- [ ] the chosen adapter is enabled +- [ ] `planMatchers` matches your approved-plan name +- [ ] `receiptDir` is writable +- [ ] `requireRealDispatchReceipt` matches your intended policy +- [ ] `allowReplyClosureWithoutDispatch` has not accidentally weakened the gate + +### Layer 4: is the receipt real, not placeholder data? + +Common problems: + +- only “planning to dispatch”, but no real receipt +- incomplete receipt shape +- empty `dispatchRunId` or `childSessionKey` +- invalid `replyClosureState` +- writing to the wrong directory + +### Layer 5: is the host preflight data sufficient? + +The continuity adapter depends on preflight inputs such as: + +- wrapper/planner output in the `force-recall` path +- normalized continuity source data in the generic path + +So verify: + +- [ ] the host really carries forward long-task / approved-plan state +- [ ] the planner really produced `derivedAction` / `nextDerivedAction` +- [ ] the continuity input can be built from host data +- [ ] closure state is derived correctly + +--- + +## 12. Quick debugging commands + +### Run the plugin test suite + +```bash +cd /plugins/continuity +npm test +``` + +### Check hook syntax + +```bash +cd +node --check hooks/force-recall/handler.ts +``` + +### Check continuity keywords in the handler + +```bash +cd +grep -n "runForceRecallContinuityAdapter" hooks/force-recall/handler.ts +grep -n "APPROVED_PLAN_CONTINUITY_GATE" hooks/force-recall/handler.ts +``` + +### Check whether receipts are actually written + +```bash +cd +find state/approved-plan-continuity -maxdepth 2 -type f | sort +``` + +--- + +## 13. Current limitations + +Accept these limitations before installation: + +- this plugin is an extracted **approved-plan continuity hard gate**, not a general workflow engine +- `force-recall` is still the most mature adapter, but not the only adapter +- if your workspace has no `force-recall`, you still need to connect host data into the generic/manual path yourself +- config is still “module defaults + caller input”, not a full installer/registry flow +- the receipt store only writes files; it does not manage retention, cleanup, or indexing +- the receipt validator checks only the minimum contract; it does not deeply validate every `nextDerivedAction` semantic variant +- if you only perform dry-run planning and never create a real dispatch receipt, the gate may still fail by design; that is expected behavior, not a bug + +--- + +## 14. Recommended delivery standard: what counts as “another agent can follow this”? + +If you hand this plugin to another agent or implementer, the handoff should at least satisfy all of the following: + +- [ ] they understand the plugin purpose without re-reading the entire conversation +- [ ] they know where the files should live +- [ ] they know which files must be copied +- [ ] they know how to wire the `force-recall` hook, if applicable +- [ ] they know which example config fields to check first +- [ ] they know how to run smoke tests +- [ ] they know the first debugging layers to inspect +- [ ] they understand the current limits and do not mistake this for a full workflow framework + +If all eight points are covered, this has reached the “agent can follow it” bar. + +--- + +## 15. One-page execution summary + +If you are in a hurry, do only these: + +1. put the plugin here: + +```text +/plugins/continuity +``` + +2. confirm the host is wired to: + +- `plugins/continuity/src/index.mjs` +- `runForceRecallContinuityAdapter(...)` or `runGenericPreflightContinuityAdapter(...)` +- prepend `out.block` into the agent prompt/body + +3. apply the example config: + +```text +plugins/continuity/examples/openclaw.continuity.example.json +``` + +4. create the receipt directory: + +```bash +mkdir -p /state/approved-plan-continuity +``` + +5. run tests: + +```bash +cd /plugins/continuity && npm test +cd && node --check hooks/force-recall/handler.ts +cd && node scripts/test_force_recall_long_task_preflight.mjs +``` + +6. confirm the prompt contains: + +```text +[APPROVED_PLAN_CONTINUITY_GATE] +``` + +7. confirm that without a real dispatch receipt, the task is not incorrectly treated as completed. + +--- + +## 16. Reference files + +- `plugins/continuity/README.zh-TW.md` +- `plugins/continuity/README.md` +- `plugins/continuity/HOOK.md` +- `plugins/continuity/GENERIC_INTEGRATION.zh-TW.md` +- `plugins/continuity/GENERIC_INTEGRATION.md` +- `plugins/continuity/examples/openclaw.continuity.example.json` +- `plugins/continuity/examples/approved-plan-receipt.example.json` +- `hooks/force-recall/handler.ts` +- `scripts/test_force_recall_long_task_preflight.mjs` + +If you need the Traditional Chinese operator version, use `AGENT_GUIDE.zh-TW.md` first; this file is the English counterpart. \ No newline at end of file diff --git a/plugins/continuity/GENERIC_INTEGRATION.md b/plugins/continuity/GENERIC_INTEGRATION.md new file mode 100644 index 0000000..be857b2 --- /dev/null +++ b/plugins/continuity/GENERIC_INTEGRATION.md @@ -0,0 +1,591 @@ +# Continuity Plugin: Generic / Manual Integration Runbook + +> Audience: host / orchestrator / planner implementers **without** a `force-recall` hook +> +> This document explains how to install the continuity plugin even when your host does not use `hooks/force-recall/handler.ts`, how to provide payloads, how to run generic/manual preflight, and how to verify that the continuity block is actually enforced. + +> 中文版:`GENERIC_INTEGRATION.zh-TW.md` + +--- + +## 0. What problem does this runbook solve? + +Read this first if any of the following apply: + +- your host does not have a `force-recall` hook +- you have your own planner / preflight / orchestrator +- you want to feed approved-plan state into the continuity plugin manually +- you need a clear payload contract showing the minimum required fields +- you need a verification path that proves the continuity block is really preventing incorrect closure + +One-line summary: + +**You can install the continuity plugin without `force-recall`, but you must provide generic continuity input yourself and inject the returned block into the agent prompt.** + +--- + +## 1. Minimum installation target + +At minimum, the integration should achieve all of the following: + +- your host can import `plugins/continuity/src/index.mjs` +- your host can call either: + - `runGenericPreflightContinuityAdapter(...)`, or + - `runManualContinuityPreflight(...)` +- your host prepends `out.block` into the prompt/body shown to the agent +- when a task is finished but there is a known next step and no real dispatch receipt, the continuity gate blocks “just close the whole thing” behavior +- `cd plugins/continuity && npm test` passes + +--- + +## 2. Install location and files + +Recommended location: + +```text +/plugins/continuity +``` + +For generic/manual integration, you should at least have: + +```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.md +``` + +Quick check: + +```bash +cd +find plugins/continuity -maxdepth 3 -type f | sort +``` + +--- + +## 3. Which APIs are used on the generic/manual path? + +`src/index.mjs` already re-exports the APIs most hosts need: + +- `defaultConfig` +- `normalizeContinuityConfig()` +- `validateContinuityConfig()` +- `buildGenericContinuityInput()` +- `createGenericPreflightContinuityAdapter()` +- `runGenericPreflightContinuityAdapter()` +- `runManualContinuityPreflight()` +- `validateReceipt()` +- `isValidReceipt()` +- `writeReceipt()` + +For the shortest path, most integrations only need: + +- `runGenericPreflightContinuityAdapter(...)` +- `runManualContinuityPreflight(...)` +- `writeReceipt(...)` + +--- + +## 4. Minimum continuity input contract + +The generic adapter accepts a **host-agnostic continuity input**. + +According to `src/continuity/types.md`, the practical minimum fields are: + +- `planId`: string +- `currentTask`: string +- `taskState`: string | null +- `nextDerivedAction`: object | null +- `replyClosureState`: string | null +- `dispatchReceipt`: object | null +- `nextTaskKnown`: boolean +- `sameApprovedPlan`: boolean +- `taskBoundaryStop`: boolean +- `highRiskStop`: boolean + +Optional fields: + +- `nextTaskId`: string | null +- `nextTaskKey`: string | null +- `derivedAction`: object | null +- `metadata`: object + +### Minimum workable payload example + +```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', + }, +}; +``` + +### When should this payload trigger the gate? + +A common high-risk case is: + +- `taskState = 'complete'` +- `nextTaskKnown = true` +- `sameApprovedPlan = true` +- `nextDerivedAction != null` +- `dispatchReceipt = null` +- `replyClosureState = 'completed'` + +That usually means: **the current task is done, the next task is already known, but you still have no real dispatch receipt and are about to close the whole run anyway.** + +That is exactly the core case the continuity gate is supposed to block. + +--- + +## 5. How to wire the generic adapter + +### Minimal wiring + +```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}`; +} +``` + +### Which returned fields matter most? + +Most hosts should inspect: + +- `out.input` +- `out.result` +- `out.evaluation` +- `out.block` +- `out.meta.adapterName` +- `out.meta.hostAgnostic` + +### When must you not ignore `out.block`? + +If `out.block` is non-empty, the plugin generated a continuity gate block intended for prompt injection. + +**Do not treat the integration as finished just because you logged `evaluation`.** + +The real minimum bar is: + +- `out.block` exists +- it is injected into the agent prompt/body +- the agent will actually see it downstream + +--- + +## 6. How to use the manual runner + +If you do not yet have a full generic source builder, or you want a quick single-point preflight harness, use the 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}`; +} +``` + +### When is the manual runner useful? + +- you do not yet have a full planner but want to validate gate behavior first +- you want an integration smoke test +- you are migrating an older host and need a temporary bridge + +### The manual runner is not a substitute for real integration + +Treat it as a **preflight harness**, not the final architecture. + +Once behavior is confirmed, move back to the generic adapter and map real host data into `source`. + +--- + +## 7. How to prepare config + +Start from: + +```text +plugins/continuity/examples/openclaw.continuity.example.json +``` + +The example looks like this: + +```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" + } + } +} +``` + +### Fields to verify first on the generic/manual path + +- [ ] `enabled = true` +- [ ] `planMatchers` matches your approved-plan naming +- [ ] `legalTerminalStates` matches your closure policy +- [ ] `receiptDir` is writable +- [ ] `requireRealDispatchReceipt = true` in most cases +- [ ] `allowReplyClosureWithoutDispatch = false` in most cases +- [ ] `adapter.genericPreflight.enabled = true` +- [ ] `adapter.genericPreflight.injectBlockLabel` is the label you expect + +--- + +## 8. How to provide receipts + +If your host really dispatches the next step, do not just say “dispatched” in metadata or logs. Provide a real `dispatchReceipt`. + +### Minimum receipt shape + +The current validator expects at least: + +- `planId` +- `currentTask` +- `nextDerivedAction` +- `dispatchedAt` +- `dispatchRunId` +- `childSessionKey` +- `replyClosureState` + +Reference file: + +```text +plugins/continuity/examples/approved-plan-receipt.example.json +``` + +Example content: + +```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" +} +``` + +### Validate before writing + +```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('; ')}`); +} +``` + +### Write a receipt + +```js +await plugin.writeReceipt({ + receiptDir: 'state/approved-plan-continuity', + receipt, +}); +``` + +### Create the receipt directory first + +```bash +cd +mkdir -p state/approved-plan-continuity +``` + +--- + +## 9. Generic/manual preflight verification flow + +This is the most important checklist in the document. + +### Case A: the gate should block + +Test an input where: + +- the task is complete +- the next task is known +- it is the same approved plan +- a next derived action exists +- no dispatch receipt exists +- closure is trying to mark the run completed + +Example: + +```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, + }, +}); +``` + +Expected outcome: + +- `out.block` is non-empty +- `out.evaluation` shows that this is not a safe direct closure state +- once injected, the agent should be warned not to close the entire job prematurely + +### Case B: legal terminal states should not be falsely blocked + +Examples: + +- `replyClosureState = 'waiting_user'` +- or `blocked` +- or `pending_verification` + +Expected outcome: + +- valid waiting / blocked / pending-verification states are not misclassified as incorrect closure + +### Case C: a real receipt should differ from dry-run behavior + +Replace `dispatchReceipt` with a valid receipt object and run again. + +Confirm that: + +- the plugin recognizes the receipt +- behavior differs from the `dispatchReceipt = null` case +- merely having `nextDerivedAction` does not automatically count as dispatched + +--- + +## 10. How to verify the continuity block is actually injected into the prompt + +This step is commonly skipped, but it is what separates “calculated something” from “integration actually works.” + +### Minimum proof required + +You should prove all three of these: + +1. the plugin returned `out.block` +2. the host prepended `out.block` into the agent prompt/body +3. the final prompt actually contains: + +```text +[APPROVED_PLAN_CONTINUITY_GATE] +``` + +### Recommended approaches + +- print the beginning of the final prompt during preflight/orchestrator tests +- dump prompt body in debug mode +- add an integration smoke assertion for the block label + +### What is *not* enough + +The following are insufficient on their own: + +- seeing only `out.evaluation` +- seeing only a console log that says the adapter ran +- seeing a prepend line in code without verifying the actual prompt output + +--- + +## 11. Recommended smoke test order + +### Step 1: plugin test suite + +```bash +cd /plugins/continuity +npm test +``` + +### Step 2: quick plugin smoke + +```bash +cd /plugins/continuity +node test/continuity.smoke.test.mjs +``` + +### Step 3: your generic/manual host integration test + +If you have your own orchestrator, add at least one smoke case for: + +- no receipt + trying to complete -> should be gated +- legal terminal state -> should not be falsely blocked +- valid receipt present -> behavior should differ from the no-receipt case + +### Step 4: manual prompt block inspection + +Confirm the output really contains: + +```text +[APPROVED_PLAN_CONTINUITY_GATE] +``` + +--- + +## 12. Common pitfalls for host implementers + +### Pitfall 1: planner output exists, but no prompt injection + +That means continuity is only “computed”, not actually affecting agent behavior. + +### Pitfall 2: treating dry-run next action as equivalent to dispatched + +`nextDerivedAction != null` does **not** mean `dispatchReceipt != null`. + +### Pitfall 3: missing `planId` or `currentTask` + +If `buildGenericContinuityInput()` cannot derive those core fields, it may fail to produce a usable input. + +### Pitfall 4: incorrect legal terminal state config + +For example, if your host legitimately uses `waiting_user` but your config omits it from `legalTerminalStates`, you may get false positives. + +### Pitfall 5: a receipt file exists, but the receipt is invalid + +Validate first with `validateReceipt()`. + +### Pitfall 6: testing only the happy path + +At minimum, test: + +- a case that should fail/gate +- a case that should pass or not be falsely blocked +- the difference between “with receipt” and “without receipt” + +--- + +## 13. One-page generic/manual checklist + +### Install + +- [ ] copy `plugins/continuity` into the workspace +- [ ] confirm `src/index.mjs` can be imported +- [ ] create `state/approved-plan-continuity/` + +### Config + +- [ ] apply the example config +- [ ] enable `adapter.genericPreflight.enabled` +- [ ] confirm `receiptDir` is writable + +### Host wiring + +- [ ] prepare a generic continuity payload +- [ ] call `runGenericPreflightContinuityAdapter(...)` or `runManualContinuityPreflight(...)` +- [ ] prepend `out.block` into the agent prompt/body + +### Receipt + +- [ ] only create a receipt when dispatch really happened +- [ ] validate with `validateReceipt()` first +- [ ] then call `writeReceipt()` + +### Verification + +- [ ] `cd plugins/continuity && npm test` +- [ ] verify that missing receipts block incorrect closure +- [ ] verify that legal terminal states are not falsely blocked +- [ ] verify that the final prompt contains the continuity block + +--- + +## 14. What counts as a complete generic/manual integration? + +The integration is only genuinely complete when all of the following are true: + +- not only the plugin is installed, but the host really calls the adapter +- not only evaluation exists, but the block is actually injected into the agent prompt +- not only dry-run planning exists, but a real dispatch can produce a real receipt +- not only the happy path is tested, but both gate-fail and gate-pass behavior are verified +- the documentation is sufficient for an unfamiliar host implementer to know which fields to provide and how to validate the result + +If all five are true, you are much closer to “an unfamiliar host can install this from the docs.” + +--- + +## 15. Reference files + +- `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` + +If your host later adds a dedicated adapter, keep this generic/manual runbook anyway as the minimum integration baseline. \ No newline at end of file diff --git a/plugins/continuity/GENERIC_INTEGRATION.zh-TW.md b/plugins/continuity/GENERIC_INTEGRATION.zh-TW.md new file mode 100644 index 0000000..364ecbc --- /dev/null +++ b/plugins/continuity/GENERIC_INTEGRATION.zh-TW.md @@ -0,0 +1,591 @@ +# Continuity Plugin:Generic / 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 +/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 +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 +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 1:plugin 本體測試 + +```bash +cd /plugins/continuity +npm test +``` + +### Step 2:快速 smoke + +```bash +cd /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`。 + +### 坑 3:payload 缺少 `planId` 或 `currentTask` + +`buildGenericContinuityInput()` 若拿不到這些關鍵欄位,可能直接回不出有效 input。 + +### 坑 4:把合法 terminal state 設定錯 + +例如你宿主其實接受 `waiting_user`,但 config 沒放進 `legalTerminalStates`,就可能造成誤判。 + +### 坑 5:receipt 寫檔了,但內容不合法 + +先 `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,作為最低整合基準。 \ No newline at end of file