refactor(nanoclaw): own one Harness client for the adapter's lifetime - #960
Closed
chughtapan wants to merge 2 commits into
Closed
refactor(nanoclaw): own one Harness client for the adapter's lifetime#960chughtapan wants to merge 2 commits into
chughtapan wants to merge 2 commits into
Conversation
The adapter carried two constructors and production only ever reached the one that built MoltZapService and MoltZapChannelCore directly, because nothing could resolve a daemon endpoint. The slot now carries its port, so the profile name alone resolves a client. NanoClaw registers its channel through a zero-argument factory that runs at module import, so there is no seam to inject a client through. The adapter therefore owns the scope itself: it acquires in setup and releases in teardown. That inverts the borrowed-client contract, so the borrowing constructor goes rather than surviving beside it. The simulator writes the slot and stops there. It does not start a daemon: the adapter starts its own, and a real nanoclaw checkout has no simulator to start one for it. Two daemons would bind the same slot's single port. The channel stays one file. The asset copier installs exactly this module into a downloaded checkout, so acquisition lives here or behind a client export, never in a sibling. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P76aaa1STr3WPZ3nDascta
This was referenced Aug 5, 2026
Owner
Author
|
Superseded by #974, which collapses this stack into one PR against |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Plan step 11. Stacked on #959. Completes the adapter half of the cutover — both runtimes now reach MoltZap only through
HarnessClient.Why NanoClaw is not a copy of the OpenClaw cutover
Two constraints OpenClaw does not have, and both fail silently if you get them wrong.
The factory takes no arguments.
registerChannelAdapter("moltzap", { factory: () => makeMoltZapAdapter() })runs at module import inside NanoClaw's own process, so there is no seam to inject a client through.fromHarnessClientdocumented "the caller owns the client's scope" — but there is no such caller. The adapter now owns the scope itself: acquire insetup(), release inteardown(). That inverts the borrowed-client contract, so the borrowing constructor is deleted rather than kept beside the owning one.The channel is one file.
copy-nanoclaw-assets.mjsinstalls exactlysrc/channels/moltzap.tsinto a downloaded checkout. A sibling module compiles locally, passes every unit test, and breaks only in a real install. Verified the diff adds no new import — the only added specifier is@moltzap/client, and no new file appears undernanoclaw-channel/src.The simulator does not start a daemon
The plan originally said it should. That instruction predates
harnessClientForProfile, which is self-starting — a simulator-side daemon plus an adapter-side daemon would bind the slot's singlemcpPortand failEADDRINUSE.The decisive reason is not symmetry with OpenClaw: a real NanoClaw checkout has no simulator, so an adapter that depends on one being started for it only works under test. The simulator's job is to write the slot (
reserveSlotMcpPort+writeMoltZapProfileConfig) and passMOLTZAP_PROFILE+MOLTZAP_CONFIG_HOME.An acquire-without-spawn export (
harnessClientForRunningProfile) was considered and rejected — a second acquisition path existing so one caller can skip spawning is the parallel-path shape this program deletes. One slot, one owner.Gates
pnpm nx run workspace:precommit— exit 0 (includeslint:sloppy-code-guard)git grep 'MoltZapService|MoltZapChannelCore|fromProfile|fromService' -- 'packages/nanoclaw-channel/**'returns nothing, includingAGENTS.mdand generatedMODULE.mdnanoclaw-channel/src— the packaging constraint holdsDocs
packages/nanoclaw-channel/AGENTS.mdno longer describes "an injectedHarnessClientor the transitionalMoltZapChannelCorepath… profile/core-backed until profile-to-MCP acquisition is available." That sentence described the exact gap this stack closed.packages/client/AGENTS.mdalso said adapters "wrapMoltZapChannelCore". That stopped being true two PRs ago;MoltZapChannelCorenow sits behind the loopback boundary insidemoltzapd.ADR conformance (R-pass)
20260801-harness-is-one-profile-slot-daemonCONFORMSHarnessClientonly; the grep gate above is zero.HarnessClientis the sole adapter-facing capability20260801-harness-client-owns-runtime-contextCONFORMSMOLTZAP_EVAL_MODE) is retained — orthogonal to transport.No new divergence; no ledger row changes.
🤖 Generated with Claude Code
https://claude.ai/code/session_01P76aaa1STr3WPZ3nDascta