Skip to content

fix(mcp): keep Muse ownership across JSON key reordering - #1346

Merged
SaulMoro merged 1 commit into
Tencent:mainfrom
nateEc:fix/1221-muse-semantic-ownership
Oct 26, 2026
Merged

SaulMoro merged 1 commit into
Tencent:mainfrom
nateEc:fix/1221-muse-semantic-ownership

Conversation

@nateEc

@nateEc nateEc commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

A Muse save that merely reorders JSON object keys makes the order-sensitive legacy entryHash look like a member edit. teamai uninstall then retains the server even though its content is unchanged.

Keep the existing rendered hash for compatibility, and record a Muse-only content fingerprint from both Git reconciliation and HTTP MCP installation. It ignores object key order and an inferred stdio type, including nested object order, while keeping arrays and actual content changes meaningful. CLI removal uses that fingerprint; older records require a historical render matching both their original hash and installed value. Unverifiable legacy records remain in place with the existing warning.

Review follow-up:

Rebase only: keep both the newer Muse anchoring cases and this PR's reordered-server uninstall case. Retain the comparison-only legacy ownership view required by #1345. No additional runtime change was needed.

Validation on this branch, rebased onto fc8a229e:

  • npx tsc --noEmit, npm run lint, npm run build, git diff --check: passed.
  • npx vitest run src/__tests__/muse.test.ts src/__tests__/mcp-reconcile.test.ts src/__tests__/local-agent-mcp.test.ts: 396 passed.
  • After build, npx vitest run --config vitest.e2e.config.ts src/__tests__/e2e/muse-support.test.ts: 18 passed, using the real CLI and isolated local Git/HOME fixtures.

Local host: macOS, Node 22.22.1, Apple Git 2.50.1. Native Windows, live vendor applications and additional provider combinations were not run locally and are left to CI. This record replaces the earlier combined-snapshot validation paragraphs.

Refs #1221; this PR addresses only its stated independent defect.

@github-actions

Copy link
Copy Markdown

No findings in the specified PR diff.

The PR description documents sufficient testing, including a build followed by 7 real-CLI E2E tests covering immediate uninstall after a settings rewrite. A live Muse save and full provider × agent coverage are not required.

Review was read-only; I did not run, build, or install anything from the PR.

@SaulMoro SaulMoro left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was generated by AI during triage.

@nateEc Thanks, approving. This closes the key-order half of the #1221 Muse item.

We built this head, its merge-base 83f6d1db and main 54622c3a. We installed a stdio server with nested env and an HTTP server with nested headers, then simulated a Muse save that reverses the keys at every level and drops stdio's inferred type.

  • base and main: uninstall keeps both servers as "you changed it".
  • this head: uninstall removes both, and a server whose command was really edited is still kept.

Upgrade and downgrade both hold:

  • records written by base are recognized by this build, both before and after a pull adds valueHash;
  • base still reads records this build wrote, and only falls back to its order-sensitive judgment.

Non-Muse formats keep their ownership rules, since the new check only runs for format === 'muse' and entryHash is unchanged. The MCP unit files (397 tests) and muse-support pass, and the new tests fail on base. tsc, lint and CI pass.

Suggestions:

  • [P2 non-blocking] Merging with #1345 needs one line. We built this head merged with #1345 (c15f4f21). An unchanged legacy stdio server is then kept on uninstall, and muse.test.ts fails its legacy case, although all 8 muse-support e2e tests pass. #1345 judges a normalized copy, so the legacy fallback here has to judge that copy too:
    - : await judge(name, doc.servers[name], previous.hash) === 'teamai'
    + : await judge(name, comparable.servers[name], previous.hash) === 'teamai'
    With that change, all 41 Muse unit tests pass and the upgrade run removes both servers. Whichever of the two lands second needs it.
  • [P3 nit] moveToLocalScope rebuilds records without valueHash. That only reaches Claude and CodeBuddy targets, which never read it, so no decision changes today. The Muse stdio inference also lives in two modules.

CI review: it reported no findings and accepted the testing record. We agree for this PR on its own. The interaction with #1345 above is outside what it could see.

@SaulMoro

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

#1345 merged first, so this conflicts with main in src/mcp-reconcile.ts, src/local-agent.ts (after #1265), the Muse tests and troubleshooting. When rebasing, keep #1345's comparable view in the removal loop, otherwise a legacy Muse record without type fails the judge check and is kept on uninstall:

- : await judge(name, doc.servers[name], previous.hash) === 'teamai'
+ : await judge(name, comparable.servers[name], previous.hash) === 'teamai'

Please rerun src/__tests__/muse.test.ts and the legacy upgrade case in src/__tests__/e2e/muse-support.test.ts after the rebase. I will re-check and merge once it is clean.

@nateEc
nateEc force-pushed the fix/1221-muse-semantic-ownership branch from fc0bc34 to fddda28 Compare October 24, 2026 17:15
@nateEc

nateEc commented Oct 24, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the review follow-up at fddda28bf78c2aeacb049fd3dfb576c9bed5a474 on current main.

  • Rebased onto fix(mcp): preserve Muse-saved entries during reconciliation #1345 and feat(locate): rules, blocks, agents, and MCP go through locate (#1221) #1265, retaining the comparison-only comparable view in the legacy removal fallback. HTTP manifest writes use the new record object API and include Muse's optional content fingerprint.
  • muse.test.ts and local-agent-mcp.test.ts: 139 passed, including legacy records without valueHash, omitted stdio type, key reordering and real member edits.
  • Built-CLI muse-support.test.ts: 8 passed, including removal immediately after a simulated Muse rewrite and legacy upgrade. Build, type checking and lint passed.
  • Muse itself was unavailable; the settings rewrite is a fixture, as in the original verification.

The PR description contains the updated verification record. Ready for another review.

@github-actions

Copy link
Copy Markdown

No findings in the specified PR diff.

The PR description documents sufficient testing: a build followed by 8 built-CLI E2E tests, including immediate uninstall after a simulated Muse settings rewrite and legacy upgrade. A live Muse save and full provider × agent coverage are not required.

The earlier review reported no findings, so there are no prior findings to mark resolved.

Review was read-only; I did not run, build, or install anything from the PR.

@SaulMoro SaulMoro left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was generated by AI during triage.

@nateEc Thanks, approving again at fddda28. The legacy fallback now judges with comparable.servers[name], as #1345 needed. On the rest of the change, the earlier approval holds. With the built CLI, a server whose keys Muse reordered (and whose inferred type it dropped) is removed by uninstall --agent muse on this head and kept on the base. A real command edit is still kept with a warning. Upgrade from a base install and downgrade to the base both behave. The new unit tests and the new e2e case fail on the base. Build, tsc, lint, the PR's unit files (139), muse-support (8/8) and the 16 e2e files that touch MCP removal pass.

It now conflicts with main in src/__tests__/e2e/muse-support.test.ts, after #1344 added its cases to the same file. Keeping both sets of cases resolves it, and I'll merge it once it's rebased and green.

  • [P3 nit] moveToLocalScope still rebuilds records without valueHash (src/local-agent.ts). Muse never reaches that path today. The Muse stdio inference lives in both mcp-format.ts and mcp-reconcile.ts. The new sharing.md paragraph repeats the troubleshooting rule, and the body dates its follow-up 2026-10-25.

The CI review reports no findings. We agree. All CI jobs pass.

- Keep legacy rendered hashes intact and add a Muse-only content fingerprint that sorts nested object keys, infers omitted stdio types and preserves meaningful array order.
- Record the same fingerprint from Git reconciliation and HTTP MCP installs. During CLI removal, recognize equivalent saves while keeping real command, argument, environment and header edits.
- For legacy records, require a team-history render matching both the original hash and installed value; keep unverifiable entries rather than treating any historical value as ownership proof.
- Verify 128 Muse/HTTP MCP unit tests and 7 built-CLI Muse E2E cases, including immediate uninstall after key reordering, legacy-history boundaries and true edits; run TypeScript/lint and update bilingual guidance.

Refs: Tencent#1221
@nateEc
nateEc force-pushed the fix/1221-muse-semantic-ownership branch from fddda28 to 4218e88 Compare October 26, 2026 04:38
@github-actions

Copy link
Copy Markdown

No findings in the specified PR diff.

The PR description documents sufficient testing: build plus representative built-CLI E2E verification, including immediate uninstall after a simulated Muse settings rewrite. A live Muse save and full provider × agent coverage are not required.

Earlier reviews reported no findings, so none need marking resolved.

Review was read-only; I did not run, build, or install anything from the PR. I reviewed the requested diff ending at 4218e882, not the later fddda28b head mentioned in the description.

@nateEc

nateEc commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

Updated and rebased onto main fc8a229e. Rebase only: keep both the newer Muse anchoring cases and this PR's reordered-server uninstall case. Retain the comparison-only legacy ownership view required by #1345. No additional runtime change was needed.

Verification: 396 focused unit tests passed; types, lint and build passed. Post-build real-CLI muse-support: 18 passed. Native Windows and live vendor/provider combinations remain CI coverage. The PR body now describes this branch's current verification rather than an older combined snapshot.

Updated head: 4218e882.

@SaulMoro SaulMoro left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was generated by AI during triage.

@nateEc Re-approving at 4218e88. The PR's own patch is unchanged from fddda28. It rebases cleanly on current main, and tsc plus the PR's unit files pass on the merged tree. Rechecked on this head:

  • The three unit files pass (396), and the muse-support e2e passes (18).
  • Real CLI: with a reordered Muse save, uninstall removes the server. A real edit to the entry keeps it and prints the warning.
  • All CI jobs pass at this head.

The legacy-fallback P2 from the first round is fixed (comparable.servers[name] in src/mcp-reconcile.ts). The CI review reports no findings. We agree.

[P3 nit] The commit message says 128 unit and 7 e2e tests. The body and our runs say 396 and 18. That doesn't matter for a squash merge.

@SaulMoro
SaulMoro merged commit 4289604 into Tencent:main Oct 26, 2026
18 checks passed
STiFLeR7 added a commit to STiFLeR7/teamai-cli that referenced this pull request Oct 26, 2026
Rebased onto upstream main (6 commits landed since this branch was
cut, including Tencent#1346 which added these .reverse() calls). Same fix
as the rest of this PR: switched to Array#toReversed(), since none of
these sites reuse the original array reference after reversing it.
STiFLeR7 added a commit to STiFLeR7/teamai-cli that referenced this pull request Oct 26, 2026
Rebased onto upstream main (6 commits landed since this branch was
cut, including Tencent#1346 which added these .reverse() calls). Same fix
as the rest of this PR: switched to Array#toReversed(), since none of
these sites reuse the original array reference after reversing it.
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.

3 participants