# 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.