Files
reporting-governance-plugin/plugins/continuity

Continuity Plugin (MVP → generalized checkpoint)

中文版:README.zh-TW.md

This package extracts the current approved-plan continuity hard gate into a small installable, testable OpenClaw plugin.

The package still preserves the current approved-plan behavior, but it now moves one step closer to a more general engine + adapter structure:

  • a host-agnostic continuity engine input/output contract
  • the existing force-recall parity adapter
  • a new generic-preflight adapter and manual runner for non-force-recall integration

What this package currently provides

  • continuity config validation
  • dispatch receipt contract validation
  • receipt file writing
  • approved-plan continuity gate evaluation
  • prompt block generation for the continuity gate
  • a force-recall adapter that maps hook wrapper/planner output into continuity input
  • a generic-preflight adapter that accepts host-agnostic continuity input directly
  • a manual preflight runner for workspaces that do not use force-recall

Install location

Recommended location inside an OpenClaw workspace:

<workspace>/plugins/continuity

Possible integration layouts now include both the original force-recall path and a more generic path:

<workspace>/
  hooks/
    force-recall/
      handler.ts
      HOOK.md
  plugins/
    continuity/
      README.zh-TW.md
      README.md
      HOOK.md
      package.json
      examples/
      src/
      test/
  scripts/
    test_force_recall_long_task_preflight.mjs

Directory structure

plugins/continuity/
  README.zh-TW.md
  README.md
  HOOK.md
  package.json
  examples/
    approved-plan-receipt.example.json
    openclaw.continuity.example.json
  src/
    index.mjs
    adapters/
      force-recall.mjs
      generic-preflight.mjs
    config/
      defaults.mjs
      schema.mjs
    continuity/
      engine.mjs
      evaluator.mjs
      receipt-store.mjs
      receipt-validator.mjs
      types.md
  test/
    continuity.config.test.mjs
    continuity.evaluator.test.mjs
    continuity.plugin.test.mjs
    continuity.receipt-store.test.mjs
    continuity.receipt-validator.test.mjs
    continuity.smoke.test.mjs

Public surface

  • src/config/schema.mjs
  • src/config/defaults.mjs
  • src/continuity/engine.mjs
  • src/continuity/evaluator.mjs
  • src/continuity/receipt-validator.mjs
  • src/continuity/receipt-store.mjs
  • src/adapters/force-recall.mjs
  • src/adapters/generic-preflight.mjs
  • src/index.mjs

src/index.mjs currently re-exports:

  • defaultConfig
  • cloneDefaultConfig()
  • validateContinuityConfig() / normalizeContinuityConfig()
  • normalizeContinuityEngineInput()
  • createContinuityEngineResult() / createContinuityEngineContract()
  • evaluateContinuity() / buildContinuityGateBlock()
  • validateReceipt() / isValidReceipt()
  • slugifyReceiptSegment() / buildReceiptFilename() / writeReceipt()
  • buildApprovedPlanContinuityInput()
  • createForceRecallContinuityAdapter() / runForceRecallContinuityAdapter()
  • buildGenericContinuityInput()
  • createGenericPreflightContinuityAdapter() / runGenericPreflightContinuityAdapter()
  • runManualContinuityPreflight()

Host-agnostic engine contract

The generalized engine accepts a normalized continuity input with fields such as:

  • planId
  • currentTask
  • taskState
  • nextDerivedAction
  • replyClosureState
  • dispatchReceipt
  • nextTaskKnown
  • sameApprovedPlan
  • taskBoundaryStop
  • highRiskStop

Generalized adapters return a common contract:

  • input
  • result
  • evaluation
  • block
  • meta.adapterName
  • meta.hostAgnostic

See src/continuity/types.md for the concise contract notes.

Example config

Start from 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"
    }
  }
}

Defaults are defined in src/config/defaults.mjs.

Integration path A: force-recall

The original MVP integration point remains hooks/force-recall/handler.ts.

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}`;
}

Integration path B: generic/manual preflight

If your workspace does not use force-recall, you can still install and use the plugin by calling the generalized adapter or the manual runner directly.

Generic preflight adapter

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,
  },
});

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' },
  replyClosureState: 'waiting_user',
});

If out.block is non-empty, prepend it into the prompt/body seen by the agent.

Receipt contract

Minimum receipt shape:

  • planId
  • currentTask
  • nextDerivedAction
  • dispatchedAt
  • dispatchRunId
  • childSessionKey
  • replyClosureState

To persist a receipt:

import { writeReceipt } from './src/index.mjs';

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

Smoke test / verification

Required plugin verification:

cd plugins/continuity
npm test
node test/continuity.smoke.test.mjs

If your workspace uses force-recall, also run:

cd /path/to/workspace
node scripts/test_force_recall_long_task_preflight.mjs
node --check hooks/force-recall/handler.ts

Install and apply steps for another OpenClaw workspace

  1. Copy plugins/continuity into your workspace.
  2. Choose one integration path:
    • force-recall: load runForceRecallContinuityAdapter(...)
    • no force-recall: call runGenericPreflightContinuityAdapter(...) or runManualContinuityPreflight(...)
  3. Adjust config as needed, especially:
    • planMatchers
    • legalTerminalStates
    • receiptDir
    • adapter.forceRecall.injectBlockLabel
    • adapter.genericPreflight.injectBlockLabel
  4. If your dispatch flow creates child runs/sessions, persist a real receipt.
  5. Run plugin tests and the relevant workspace smoke path.
  6. Confirm the agent prompt contains the continuity gate block and that dry-run dispatch alone does not pass the gate.

Current limitations

  • This is still centered on the approved-plan continuity hard gate, not a full general workflow engine.
  • The generalized engine contract is intentionally minimal and conservative.
  • force-recall remains the most battle-tested adapter.
  • The receipt store only writes files; it does not manage retention, cleanup, or indexing.
  • The receipt validator checks the minimum contract only; it does not deeply validate every nextDerivedAction subtype.

Notes

  • Default legal terminal states are waiting_user, blocked, and pending_verification
  • The evaluator preserves current behavior, including missing_dispatch_receipt and missing_auto_next_dispatch
  • The new generic path makes the plugin more reusable even without the force-recall hook
  • HOOK.md describes the plugin/hook adapter contract boundary, not the full installation guide

Chinese documentation

See README.zh-TW.md for the Traditional Chinese version.