Skip to content

plugins sync: normalize enabledPlugins key order after installing new plugins #3196

Description

@kyle-sexton

Summary

When /claude-ops:plugins sync installs new catalog plugins (Step 4), Claude Code's own settings writer appends each new enabledPlugins key to the end of the map rather than inserting it in alphabetical position. The rest of the map is alphabetically sorted, so every sync that installs something leaves a visibly unsorted tail that never self-heals.

Observed

Machine: Windows 11, Claude Code 2.1.240, claude-ops 0.36.1, install_new: all.

A sync run installed five plugins at user scope. ~/.claude/settings.json afterwards:

    "wizard@melodic-software": false,
    "work-items@melodic-software": false,
    "x@melodic-software": false,
    "ai-slop@melodic-software": false,
    "context-budget@melodic-software": true,
    "coupling@melodic-software": false,
    "improvement@melodic-software": false,
    "overengineering@melodic-software": false
  },

The first ~70 entries are sorted; the five newly installed ones are appended after x@melodic-software.

Why it matters

  • The map is long enough (70+ entries) that scanning for a specific plugin depends on the sort order holding.
  • The tail is cumulative: every future sync that installs anything appends another unsorted block, so the file degrades monotonically.
  • For anyone whose ~/.claude/settings.json is managed (chezmoi, dotfiles repo, or any config-in-git setup), the append churns diffs in a way an ordered map would not.

Proposed change

After Step 4 installs anything, sync should normalize the enabledPlugins key order alphabetically for the scope it just wrote, and report that it did so.

Constraint to resolve first

This is not a free change — it collides with an existing invariant in the skill:

  • SKILL.md's Scope section states that sync "never writes a committed .claude/settings.json", and sync.md Step 5 reinforces it: converge is the only action permitted to touch committed settings, and only behind a confirm gate.
  • There is no claude plugin verb that reorders enabledPlugins. Sorting means a direct write to a settings file, which every step of sync currently routes around by design.

Suggested scoping that keeps the invariant intact:

  1. Apply the normalization only at user scope (~/.claude/settings.json). That file is machine-scope, not team-shared, and Step 5 already writes it automatically via claude plugin enable -s user, so a key reorder there is consistent with what the step is already permitted to do.
  2. Never normalize project scope — that is the committed, team-shared file the invariant protects. If a project-scope map is unsorted, report it, do not fix it.
  3. Make the write a strict reorder: keys and values byte-identical, order alone changed. If a semantic diff appears, abort and report rather than write.
  4. Emit a report row when normalization runs, so the settings-file write is never silent.

Alternative worth considering

The append behavior originates in Claude Code's settings writer, not in this plugin. An upstream issue asking claude plugin install to insert in sorted position would fix it for every consumer and make the workaround above unnecessary. That may be the better long-term home for the fix; the local normalization is the version that works today.

Environment note

On the machine where this was observed, the settings-file write could not be performed manually from the agent session — the auto-mode classifier denied both the Edit on ~/.claude/settings.json and a gh label list call. Any implementation should assume the write may require an explicit permission grant or a non-auto session, and should fail loudly rather than silently skipping when denied.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent-readyFully specified and briefed; eligible for autonomous pickup from the frontier.priority: lowNice-to-have, cosmetic, or speculative; opportunistic.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions