feat(adopt): reverse-projection for settings.json mcpServers#61
Merged
triologue-agents[bot] merged 1 commit intomasterfrom May 3, 2026
Merged
feat(adopt): reverse-projection for settings.json mcpServers#61triologue-agents[bot] merged 1 commit intomasterfrom
triologue-agents[bot] merged 1 commit intomasterfrom
Conversation
Closes the round-trip gap from PR #59 (task 7059d92b). Previously adopt only captured hooks; hand-edits to settings.json's mcpServers block had no migration path back into the manifest, leaving the user stuck on drift-refuse the next apply. derive.ts gains parseSettingsMcpServers + manifestMcpProjection + computeMcpDrift, the inverse of buildMcpServers in generate-settings.ts. Drift comes in two flavors: - "new": settings has an entry the manifest doesn't → append. - "modified": same name, different command/env → replace by name. Replace preserves manifest-only fields (health, enabled: false). The review subagent flagged this as a blocker: without preservation, hand-editing the command field would silently wipe doctor/probe/policy metadata. buildMcpEntry now takes the existing McpServer (if any) and carries health + enabled-false forward; enabled: true is the schema default and stays implicit. mutate.ts gains an mcp_replace action via replaceOrAppendByName that finds the first map node with a matching name and overwrites it; appends if not found. The replacement is whole-node (no field-level merge in the YAML AST), so the field-preservation merge happens at the buildMcpEntry layer where it has type-safe access to the existing McpServer. CLI output extended to report MCP counts and replacements separately: adopted 1 hook (h) + 2 MCP servers (a, b); replaced existing manifest entry for: b from settings.json into harness.yaml Tests: 18 new in tests/cli/adopt.test.ts: - 7 derive unit tests covering parse / projection / drift / mcpEqual edge cases (length mismatch, env mismatch, locked-in silent-drop for non-string args). - 11 integration tests including: new-entry adoption, replace-modified, env preservation, idempotent re-adopt, hook+mcp combined drift, declined-no-write, health preservation, enabled:false preservation, AND a full byte-identical round-trip (apply → hand-edit → adopt → apply, with strict bytes-equal on the settings.json output). - 655/655 total tests pass; existing adopt behavior unchanged when no mcpServers block is present. Manual smoke verified end-to-end in /tmp: apply seeds settings.json → hand-edit changes command + adds new server → adopt --yes captures both (replaced + new) → apply re-emits settings.json matching the hand-edit verbatim. Refs: agent-tasks 7059d92b Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
triologue-agents Bot
pushed a commit
that referenced
this pull request
May 3, 2026
* chore(release): v0.6.0 Cuts harness v0.6.0, the first minor release on top of the 0.5.0 npm distribution. Headline: the Phase-5 adoption-blocker cycle closes end-to-end. - harness apply --target / --merge / --force (PR #58): write the rendered settings.json directly into a Claude Code settings discovery path, with 3-way merge that preserves user-managed top-level keys. - apply translates tools.mcp[] into the settings.json mcpServers block (PR #59): closes the Phase 5 #1a caveat where init.mcp_servers in a `claude -p --settings <apply'd>` session was empty. - apply prints a Next-steps hint after a successful run, plus --quiet and --json (PR #60): copy-pasteable wire-up commands prevent the hallucination loop that motivated this work. - adopt reverse-projection for mcpServers into tools.mcp[] (PR #61): hand-edits to settings.json's mcpServers block round-trip back into the manifest. Manifest-only fields (health, enabled: false) are preserved on replace. The full apply → hand-edit → adopt → apply cycle is byte-identical on the settings.json bytes. Bumps: - package.json 0.5.0 → 0.6.0 (npm-published version) - src/cli/index.ts .version() 0.4.0 → 0.6.0 (was already stale at v0.5.0 cut; corrected here) - package-lock.json regenerated Dogfood (7 smokes against the built dist): - --version emits 0.6.0 - apply --target --merge preserves env + permissions, adds hooks + mcpServers - --quiet suppresses Next-steps hint - Next-steps hint present without --quiet, verify line includes --settings, no --output-dir hallucination - apply → hand-edit mcpServers → adopt → re-apply produces byte-identical settings.json - --json emits machine-readable outcome - validate --check-lock detects target tampering 655/655 unit tests pass. After tag v0.6.0: - release.yml creates the GitHub Release with the CHANGELOG body - publish-npm.yml publishes @lannguyensi/harness@0.6.0 Refs: agent-tasks b55f7b2b Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(release): bump remaining 0.4.0 version strings to 0.6.0 Caught by the v0.6.0 release review. The CLI Commander .version() at src/cli/index.ts was already bumped, but four parallel sites still hardcoded 0.4.0: - src/cli/doctor/format.ts header banner ("harness 0.4.0") - src/probes/mcp.ts MCP clientInfo - src/policies/ledger-client.ts MCP clientInfo - src/runtime/ledger-record.ts MCP clientInfo Plus the corresponding test assertions in tests/cli/{doctor,program}.test.ts. All bumped here to 0.6.0. Long-term fix tracked as agent-tasks e4a4f118 (source version from package.json instead of hand-maintaining 6 sites). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Lan Nguyen Si <nguyen-si@publicplan.de> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
Closes task
7059d92b, the round-trip gap from PR #59. Hand-edits to settings.json'smcpServersblock now flow back into the manifest viaharness adopt, completing the apply ↔ adopt cycle for MCP servers.Drift detection comes in two flavors:
Replacement preserves manifest-only fields (
health, explicitenabled: false) so that hand-editing the command does not silently wipe doctor/probe/policy metadata or re-enable a server the user explicitly turned off.CLI output reports MCP counts + replacements separately:
Test plan
tests/cli/adopt.test.ts:mcpServersblock is present./tmpconfirmsapply → hand-edit → adopt → applyproduces identical settings.json output.health/enabled: falseon replace, plus a SHOULD-FIX that the round-trip test was structural-only (not byte-identical). Both addressed inline before push:buildMcpEntrynow takes the existingMcpServerand mergeshealth/enabled: false; the round-trip test now does the full four-step cycle with bytes-equal assertion. Three additional NIT-level coverage gaps (length-mismatchmcpEqual, env-mismatch, silent-drop documentation) closed with new tests.Reviewer notes
This completes the four-PR adoption-blocker cycle from the 2026-05-03 readiness review:
d38f6f91):--target/--mergeto wire generated settings.json into Claude Code.62380337): apply translatestools.mcp→mcpServers.517aa919): apply prints Next-steps hint, plus--quiet/--json.7059d92b): adopt round-tripsmcpServersback intotools.mcp.End user can now:
harness apply --target ~/.claude/settings.json --mergeonce → hand-editmcpServersdirectly in their canonical settings file →harness adopt ~/.claude/settings.jsonto bring the change back into the manifest →harness applyre-emits the same bytes. No drift-refuse cul-de-sac.Refs: agent-tasks
7059d92b