16 KiB
OpenClaw Thin Adapter Refactor Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: Reduce openclaw-multi-session-plugins from a task/session/event owner to a thin adapter while preserving XWorkmate task awareness, artifact isolation, and OpenClaw Gateway E2E behavior.
Architecture: openclaw.svc.plus is upstream and read-only. XWorkmate must carry typed metadata through App -> Bridge -> Plugin gateway methods, persist the app/OpenClaw session mapping in SessionEntry.pluginExtensions through existing OpenClaw runtime APIs, and query OpenClaw native task records instead of custom task stores. Bridge remains the App protocol adapter and event normalizer, not a task/session source of truth.
Tech Stack: Dart/Flutter (xworkmate-app), Go (xworkmate-bridge), TypeScript/Vitest (openclaw-multi-session-plugins), OpenClaw 2026.6.1 runtime APIs as read-only upstream.
Hard Constraints
- Do not modify
/Users/shenlan/workspaces/cloud-neutral-toolkit/openclaw.svc.plus. - Do not rely on prompt text parsing for metadata.
- Do not reverse-map session keys from string conventions. The deterministic
agent:main:<appThreadKey>form is allowed only for initial native session creation before prepare persists the mapping. - Legacy string derivation and migration-only mapping fields are removed from the current flow.
- Do not restore plugin-owned task DB, session DB, or event bus.
- Validation must reference
docs/cases/, especiallydocs/cases/openclaw-gateway-e2e-regression/README.md.
Source-of-Truth Decision
- Task terminal state: OpenClaw native task registry when available.
- Transcript/progress events: OpenClaw native transcript/runtime events, transported through Bridge/App SSE if needed.
- Mapping truth:
SessionEntry.pluginExtensions[PLUGIN_ID]["xworkmate.sessionMapping"]. - Artifact expected dirs: typed xworkmate metadata, mirrored into mapping and explicit artifact gateway params.
Because upstream chat.send rejects unknown fields and upstream session_start does not carry xworkmate metadata, Bridge must call a plugin gateway method before chat.send to upsert mapping and prepare artifacts. That method is the local adapter seam that avoids modifying OpenClaw.
Target Metadata Shape
type XWorkmateTaskMetadataV1 = {
schemaVersion: 1;
appThreadKey: string;
openclawSessionKey?: string;
expectedArtifactDirs: string[];
requestId?: string;
externalTaskId?: string;
createdAt: string;
};
type XWorkmateSessionMappingV1 = {
schemaVersion: 1;
appThreadKey: string;
openclawSessionKey: string;
expectedArtifactDirs: string[];
createdAt: string;
updatedAt: string;
source: "session_start" | "bridge_prepare";
};
Task 1: Plugin Contract Tests for Durable Session Mapping
Files:
- Modify:
/Users/shenlan/workspaces/cloud-neutral-toolkit/openclaw-multi-session-plugins/src/taskState.ts - Modify:
/Users/shenlan/workspaces/cloud-neutral-toolkit/openclaw-multi-session-plugins/index.ts - Test:
/Users/shenlan/workspaces/cloud-neutral-toolkit/openclaw-multi-session-plugins/src/taskState.test.ts
Step 1: Write failing tests
Add tests for:
recordXWorkmateSessionMappingrequires typedappThreadKey.openclawSessionKeyis taken from trusted gateway params/session scope, not derived fromappThreadKey.- Mapping is written to
SessionEntry.pluginExtensions. - Idempotent same mapping updates
updatedAt. - Conflicting existing mapping fails closed.
- Missing typed mapping returns
mapping_not_found/invalid_lookup; current flow does not derive replacement keys.
Step 2: Run failing test
cd /Users/shenlan/workspaces/cloud-neutral-toolkit/openclaw-multi-session-plugins
pnpm test src/taskState.test.ts
Expected: FAIL until mapping helpers exist.
Step 3: Implement minimal plugin mapping helper
Implement helpers in src/taskState.ts:
normalizeXWorkmateTaskMetadataV1(input)normalizeExpectedArtifactDirs(input)upsertXWorkmateSessionMapping(api, metadata, openclawSessionKey, source)readXWorkmateSessionMapping(api, lookup)
Use api.runtime.agent.session.getSessionEntry and api.runtime.agent.session.patchSessionEntry if available. If runtime patch is unavailable, return structured mapping_not_found / invalid_lookup; do not silently fall back to an in-memory map.
Step 4: Run tests
pnpm test src/taskState.test.ts
pnpm typecheck
Expected: PASS.
Task 2: Plugin Gateway Method for Mapping + Artifact Prepare
Files:
- Modify:
/Users/shenlan/workspaces/cloud-neutral-toolkit/openclaw-multi-session-plugins/index.ts - Modify:
/Users/shenlan/workspaces/cloud-neutral-toolkit/openclaw-multi-session-plugins/src/exportArtifacts.ts - Test:
/Users/shenlan/workspaces/cloud-neutral-toolkit/openclaw-multi-session-plugins/src/exportArtifacts.test.ts
Step 1: Write failing tests
Add/extend tests for a new gateway method, preferably xworkmate.session.prepare, that:
- accepts
{ schemaVersion, appThreadKey, openclawSessionKey, expectedArtifactDirs, runId?, requestId? }; - rejects absolute paths and
..inexpectedArtifactDirs; - preserves expected dirs even when directories do not exist yet;
- calls artifact prepare before agent execution;
- returns mapping and artifact directory status.
Step 2: Run failing test
pnpm test src/exportArtifacts.test.ts
Expected: FAIL until gateway method is registered.
Step 3: Implement the method
In index.ts, register xworkmate.session.prepare. It should:
- call the mapping helper from Task 1;
- call
prepareXWorkmateArtifacts; - return
{ ok: true, mapping, artifactScope, artifactDirectory, expectedArtifactDirs }.
Keep xworkmate.artifacts.prepare/export/collect-and-snapshot as thin artifact operations. The in-memory plugin task store is removed and must not be reintroduced as a task source of truth.
Step 4: Run plugin suite
pnpm test
pnpm typecheck
pnpm pack:check
Expected: PASS.
Task 3: Plugin xworkmate.tasks.get Uses Native Task Registry
Files:
- Modify:
/Users/shenlan/workspaces/cloud-neutral-toolkit/openclaw-multi-session-plugins/src/taskState.ts - Modify:
/Users/shenlan/workspaces/cloud-neutral-toolkit/openclaw-multi-session-plugins/index.ts - Test:
/Users/shenlan/workspaces/cloud-neutral-toolkit/openclaw-multi-session-plugins/src/taskState.test.ts
Step 1: Write failing tests
Cover inputs:
{
taskId?: string;
runId?: string;
appThreadKey?: string;
openclawSessionKey?: string;
includeArtifacts?: boolean;
includeEvents?: boolean;
}
Expected structured errors:
mapping_not_foundtask_not_foundno_native_task_recordconflictinvalid_lookup
Step 2: Implement lookup
Rules:
appThreadKey -> openclawSessionKeymust read pluginExtensions mapping.runId/taskIdmust queryapi.runtime.tasks.runs.bindSession({ sessionKey }).resolve/get/list/findLatest.- If no native task exists, return
no_native_task_record; do not infer success from artifacts. - If
includeArtifacts, call export/list resolver and include artifact dir status plus expected dirs.
Step 3: Run plugin checks
pnpm test src/taskState.test.ts src/exportArtifacts.test.ts
pnpm typecheck
Expected: PASS.
Task 4: Bridge Sends Typed Metadata and Stops Session String Derivation
Files:
- Modify:
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-bridge/internal/acp/orchestrator.go - Modify:
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-bridge/internal/acp/rpc_handler.go - Modify:
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-bridge/internal/acp/openclaw_thread_session_mapper.go - Modify:
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-bridge/internal/acp/types.go - Test:
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-bridge/internal/acp/web_contract_test.go
Step 1: Write failing bridge tests
Extend the case from docs/cases/openclaw-gateway-e2e-regression/README.md:
- five concurrent OpenClaw requests remain running/completed, not queued;
- Bridge calls
xworkmate.session.preparebeforechat.send; - request to plugin includes
appThreadKey,openclawSessionKey,expectedArtifactDirs; - no code path calls
ThreadSessionMapper.OpenClawSessionIDfor the new path; - events include
appThreadKey,openclawSessionKey,runId/taskIdwhere available.
Step 2: Run failing bridge test
cd /Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-bridge
go test ./internal/acp -run TestHTTPHandlerGatewayOpenClawHandlesFiveConcurrentE2ECases -count=1
Expected: FAIL until prepare call and metadata are wired.
Step 3: Implement Bridge changes
In startOpenClawGatewayTask:
- validate and normalize
expectedArtifactDirs; - compute or receive the actual OpenClaw session key from the Bridge request path, but do not use legacy string replacement;
- call plugin
xworkmate.session.preparebeforechat.send; - pass
expectedArtifactDirsto artifact export/list as explicit typed params; - treat plugin mapping conflicts as fail-closed.
In xworkmate.tasks.get handler:
- forward lookup to plugin/native path;
- stop reconstructing
OpenClawTaskRecordfromartifactScopeas a source of truth; - use native reconciliation on reconnect.
Step 4: Run bridge checks
go test ./internal/acp -count=1
go test ./...
go vet ./...
go build ./...
Expected: PASS.
Task 5: App Typed Lookup and Recovery Contract
Files:
- Modify:
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-app/lib/runtime/runtime_models_runtime_payloads.dart - Modify:
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-app/lib/runtime/go_task_service_client.dart - Modify:
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-app/lib/runtime/external_code_agent_acp_desktop_transport.dart - Modify:
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-app/lib/app/app_controller_desktop_thread_actions.dart - Test:
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-app/test/runtime/assistant_execution_target_test.dart - Test:
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-app/test/runtime/gateway_acp_client_auth_test.dart - Test:
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-app/test/runtime/desktop_thread_artifact_service_test.dart
Step 1: Write failing App tests
Use docs/cases/openclaw-gateway-e2e-regression/README.md as the fixture source:
- five canonical prompts submit with
appThreadKeyandexpectedArtifactDirs; xworkmate.tasks.getparams includeappThreadKey,openclawSessionKeywhen known,runId, and include flags;no_native_task_recordis rendered as an explicit recovery state, not mistaken for success;- artifact sidebar keeps no-artifact empty state and blocks stale files.
Step 2: Run failing tests
cd /Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-app
flutter test test/runtime/assistant_execution_target_test.dart
flutter test test/runtime/gateway_acp_client_auth_test.dart
flutter test test/runtime/desktop_thread_artifact_service_test.dart
Expected: FAIL until typed lookup fields are added.
Step 3: Implement App changes
- Add
appThreadKey,openclawSessionKey, andexpectedArtifactDirsto task association / task-get DTOs. - Keep current metadata generation, but rename/extend the contract so
appThreadKeyis explicit. - Preserve directories even if not present locally.
- Do not read or persist secrets differently.
Step 4: Run targeted App checks
flutter analyze
flutter test test/runtime/assistant_execution_target_test.dart
flutter test test/runtime/gateway_acp_client_auth_test.dart
flutter test test/runtime/desktop_thread_artifact_service_test.dart
flutter test test/features/assistant/assistant_artifact_sidebar_test.dart
Expected: PASS.
Task 6: Remove/Downgrade Custom State Centers
Files:
- Modify:
/Users/shenlan/workspaces/cloud-neutral-toolkit/openclaw-multi-session-plugins/src/taskState.ts - Modify:
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-bridge/internal/acp/openclaw_async_tasks.go - Modify:
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-bridge/internal/acp/types.go - Modify:
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-bridge/internal/acp/rpc_handler.go
Step 1: Delete dead primary state paths
Remove or downgrade:
- plugin in-memory task/session maps as truth;
- Bridge
OpenClawTaskRecordterminal authority; - Bridge task reassociation from
artifactScope; - session key string derivation as primary route.
Retain only transport/reconciliation caches that have clear owner, scope, and invalidation rules.
Step 2: Static guard
Run:
rg -n "ThreadSessionMapper|sessionMappingsBy|records = new Map|bridgeAgents" \
/Users/shenlan/workspaces/cloud-neutral-toolkit/openclaw-multi-session-plugins \
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-bridge \
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-app
Expected: only documented migration or tests remain.
Task 7: Docs and Case Updates
Files:
- Modify:
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-app/docs/ai-context/chain-map.md - Modify:
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-app/docs/architecture/chain-map-task-execution.md - Modify:
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-app/docs/architecture/chain-map-artifact-lifecycle.md - Modify:
/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-app/docs/cases/openclaw-gateway-e2e-regression/README.md
Step 1: Update source-of-truth docs
Document:
- mapping stored in
SessionEntry.pluginExtensions; - Bridge pre-chat
xworkmate.session.prepare; - expected dirs typed propagation;
- native task-registry terminal state;
- App SSE as transport only.
Step 2: Update case acceptance
Add explicit checks to the OpenClaw E2E case:
- pluginExtensions contains
draft:*<->agent:main:draft:*; expectedArtifactDirsvisible in plugin task output;xworkmate.tasks.getreturns native task orno_native_task_record;- no stale artifact display.
Task 8: Full Regression and Manual Verification
Automated commands:
# openclaw-multi-session-plugins
cd /Users/shenlan/workspaces/cloud-neutral-toolkit/openclaw-multi-session-plugins
pnpm test
pnpm typecheck
pnpm pack:check
# xworkmate-bridge
cd /Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-bridge
go test ./...
go vet ./...
go build ./...
# xworkmate-app
cd /Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-app
flutter analyze
flutter test test/runtime/assistant_execution_target_test.dart
flutter test test/runtime/gateway_acp_client_auth_test.dart
flutter test test/runtime/desktop_thread_artifact_service_test.dart
flutter test test/features/assistant/assistant_artifact_sidebar_test.dart
flutter test
Manual cases from docs/cases/:
OPENCLAW-E2E-001throughOPENCLAW-E2E-005fromdocs/cases/openclaw-gateway-e2e-regression/README.md.MANUAL-GATEWAY-001throughMANUAL-GATEWAY-004fromdocs/cases/core-integration-manual-cases.md.MANUAL-THREAD-002for cross-thread state/artifact isolation.
Acceptance evidence to record:
- App thread path:
$HOME/.xworkmate/threads/draft-1780636411666238-3. appThreadKey:draft:1780636411666238-3.openclawSessionKey:agent:main:draft:1780636411666238-3.- Plugin mapping exists in
SessionEntry.pluginExtensions. expectedArtifactDirssurvives App -> Bridge -> Plugin -> artifact resolver.- No Bridge primary path uses string replace/prefix derivation.
- Plugin has no primary custom task/session/event store.
Rollback Point
Keep the rollback boundary after Task 4:
- If Plugin mapping works but Bridge cannot safely pre-call
xworkmate.session.prepare, stop before App rollout. - Revert Bridge/App protocol changes while retaining plugin tests and docs.
- Do not introduce OpenClaw upstream patch as a workaround.
Residual Risks
- If ordinary OpenClaw
chat.senddoes not create native task records,xworkmate.tasks.getmust returnno_native_task_recorduntil Bridge uses an existing native task-producing OpenClaw path or a native registry mirror API exposed by the read-only upstream. - Because upstream
session_starthas no typed xworkmate metadata,source: "session_start"cannot be the main new-path source without upstream changes. In the no-upstream-change plan, usesource: "bridge_prepare"for current new writes and reservesession_startonly for future upstream support. - The plugin package may need dependency alignment with OpenClaw 2026.6.1 types before
pnpm typecheckis stable.