Skip to content

test(simulator): prove the OpenClaw plugin acquires its own client - #972

Closed
chughtapan wants to merge 1 commit into
impl/architecture-rules-and-restartfrom
impl/drop-harness-client-seam
Closed

test(simulator): prove the OpenClaw plugin acquires its own client#972
chughtapan wants to merge 1 commit into
impl/architecture-rules-and-restartfrom
impl/drop-harness-client-seam

Conversation

@chughtapan

Copy link
Copy Markdown
Owner

Closes the last three open items from the slate's verification table.

1. The decisive assertion now runs

The plan's step-10 gate ends with: harness-adapters.integration.test.ts → startOpenClawGateway constructs the plugin as createMoltzapChannelPlugin() with no injected client and still completes the round trip. The OpenClaw case injected one, so that assertion had never executed — the only end-to-end proof of the production acquisition path was the NanoClaw case.

The plugin now acquires the slot's client itself. One slot names one loopback port, so nothing else may open a second daemon against it: the peer opens the conversation and the membership boundary is asserted from the peer's side, which is the shape runNanoClawCase already had. assertConversationBoundary is replaced by assertPeerConversationBoundary.

Coverage moved, not lost: HarnessClientService.startConversation is still exercised by moltzapd.integration.test.ts → starts a conversation and round-trips a bound reply through MCP only and by harness-client.test.ts.

2. harnessClientForAccount stays — a recommendation I reversed

I previously said this should be deleted, and step 10's grep does list it. Having looked at what depends on it, deleting it is the wrong call:

  • It returns HarnessClientService — the sanctioned adapter-facing capability. It is not a second route to the daemon the way the deleted createService / createCore were, which returned MoltZapService / MoltZapChannelCore.
  • Four OpenClaw suites drive pure presentation logic (inbound projection, delivery formatting, contract properties) through a fake turn stream. Deleting the seam converts all four into daemon-spawning integration tests, for no safety gain.

So the field stays, marked @internal and documented as test-only in both the plugin type and the fixture header. git grep harnessClientForAccount -- packages/ | grep -v '\.test\.ts\|test-utils/' returns only the declaration and its own read — no shipped source supplies it, and #962's checkAdapterFile rule already blocks the real bypasses by symbol and subpath.

I'd rather flag this as a plan step that stopped making sense than execute it and degrade the test suite.

3. Divergence ledger — rows 1 and 6 closed

Row 1 was the corpus's only confirmed binding divergence and was still open: packages/evals had neither AGENTS.md nor CLAUDE.md, packages/simulator had no CLAUDE.md. All seven packages now carry both.

Row 6ConversationWithParticipants has zero consumers outside packages/client, but it names the return type of HarnessClientService.startConversation, so the export is correct and the doc comment was the wrong half. Rewritten to say what is true.

Rows 2 and 3 remain: contested, v2-owned, unresolvable until a main-resident HarnessClient contract is admitted. Row 5 was withdrawn (v2 renamed on its own branch; main's table matches main's tree). Row 7 closed in #961.

Full re-derived ledger and the per-row verification table are posted to #926: #926 (comment)

Verification

Run against the new gate shape, since workspace:precommit no longer exists after main's hook retiering:

  • pnpm typecheck — 0
  • pnpm lint — 0 (includes arch:check across 9 projects and the new lint:script-reachability)
  • pnpm format:check — 0
  • pnpm docs:generate — no drift
  • simulator integration suite — 3/3, OpenClaw 59s, NanoClaw 45s, restart 46s

One miss worth recording: deleting assertConversationBoundary orphaned Schema, agentName, and INITIAL_CONTENT. The integration test passed regardless — only pnpm lint catches that, and I reported the item done before running it.

The plan's decisive assertion for the adapter cutover was that the plugin
starts with no injected client and still completes the round trip. The
OpenClaw case injected one, so the assertion had never run.

The plugin now acquires the slot's client itself. One slot names one loopback
port, so nothing else may open a second daemon against it: the peer opens the
conversation and the membership boundary is asserted from the peer's side,
which is the shape the NanoClaw case already had.

`harnessClientForAccount` stays, marked test-only. It hands back a
HarnessClientService, so it is not a second route to the daemon the way the
deleted createService/createCore were, and deleting it would turn four
presentation suites into daemon-spawning integration tests. No shipped source
supplies it.

Closes divergence ledger rows 1 and 6: every package now carries AGENTS.md
and CLAUDE.md, and ConversationWithParticipants says what is true — it has no
consumer outside packages/client, but it names a public return type, so the
comment was the wrong half rather than the export.
@chughtapan

Copy link
Copy Markdown
Owner Author

Superseded by #974, which collapses this stack into one PR against main. This branch's commits are all contained in it — nothing is lost, and this thread stays readable. Closing to leave one place to review.

@chughtapan chughtapan closed this Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant