Skip to content

ISSUE-449: Track pack settings ownership per sub-key - #451

Merged
bguidolim merged 3 commits into
mainfrom
bruno/ISSUE-449-settings-subkey-ownership
Oct 8, 2026
Merged

bguidolim merged 3 commits into
mainfrom
bruno/ISSUE-449-settings-subkey-ownership

Conversation

@bguidolim

@bguidolim bguidolim commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

A pack's settings file claimed whole top-level objects such as env, so re-syncing or removing the pack deleted the user's own entries inside them. Packs now own only the sub-keys they actually wrote, and anything already set there is kept. Array values such as permissions.allow are still owned as a whole (follow-up: #453).

Closes #449

Release note

  • FIX: Syncing or removing a pack no longer deletes your own env, permissions or other entries in the settings objects that pack also writes. When a pack's value clashes with one you already have, yours is kept and sync warns. A list value such as permissions.allow still belongs to whichever side set it first.

Changes

  • Ownership is recorded per sub-key, and only where the pack's value landed
  • Every sync resets each pack's recorded keys, so a pack whose values were all already set no longer keeps a stale claim that its removal could act on
  • Project sync merges onto the existing settings.local.json extra keys (env, permissions, …) instead of rebuilding them. Hooks and enabledPlugins are still rebuilt from the packs, and the file is still removed when only those remain (tracked with Plugin enablement is written under a key Claude Code ignores, and project syncs install plugins globally #450)
  • Records from older releases are narrowed to the keys the pack declares; when that can't be known, the object is left untouched with a warning

Test plan

  • swift test passes locally

  • swiftformat --lint . and swiftlint pass without violations

  • Affected commands verified with a real pack (e.g. mcs sync, mcs doctor)

  • Add a user env key to ~/.claude/settings.json, mcs sync --global twice with a pack whose settings file sets env → expect your key kept

  • mcs pack remove <pack> → expect only the pack's keys removed (Removed setting: env.<KEY>)

  • Pack value differs from an existing one → expect a "was not applied" warning and the existing value kept

  • Two packs set the same env value → removing the one that didn't land it keeps the key

Checklist for engine changes
  • Integration tests updated for new features (LifecycleIntegrationTests or DoctorRunnerIntegrationTests)

🤖 Generated with Claude Code

https://claude.ai/code/session_01CqExB5C9uXqPH2vKqj6dqa

- Record only the sub-keys a pack's settings file actually wrote, warning when an existing value wins
- Project sync merges onto the user's settings.local.json instead of rebuilding it from empty
- Narrow legacy whole-object records to the keys the pack declares, leaving unknown ones untouched
A pack whose settings values were all already set kept its previous
settingsKeys, so removing it could delete a key another pack had since
landed, and a legacy whole-object record was never replaced.

Narrow the project-sync comment: array values such as permissions.allow
are still owned whole.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CqExB5C9uXqPH2vKqj6dqa
- Project sync carries over existing extra keys only; hooks and plugins are still rebuilt from packs
- Legacy expansion yields every declared path, the upper bound of what a sync could own
@bguidolim
bguidolim merged commit 3a15419 into main Oct 8, 2026
5 checks passed
@bguidolim
bguidolim deleted the bruno/ISSUE-449-settings-subkey-ownership branch October 8, 2026 15:11
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.

Settings files take ownership of whole top-level keys and delete the user's own env/permissions entries

2 participants