Skip to content

claude-ops sync Step 5 writes committed settings, violating the skill's own only-converge-touches-committed-settings invariant #2535

Description

@kyle-sexton

Deferred from #2526 / #2527, which documented the exposure but left the remediation unscheduled.

The invariant

skills/plugins/SKILL.md states it directly:

Never silently fixes drift it finds. sync mutates only via the documented CLI actions below;
converge is the one action that can touch a committed .claude/settings.json, and only after an
explicit per-plugin confirm.

The violation

sync.md Step 5 issues claude plugin enable <id> -s <that scope> for every completeness gap in a
verifiable scope, which includes project when currentProject: true. Verified on Claude Code
2.1.228 (#2526): enable -s project writes the project's committed .claude/settings.json, adding
enabledPlugins[<id>] = true and re-serializing the file in Claude Code's key order.

So sync — the default action — silently writes a team-shared tracked file. 0.31.8 documented this
as a "known exposure" and told the step to name it in the report. That is a mitigation, not a fix.

Why confirming is not the fix

converge.md opens with an autonomous-session abort because it can touch committed settings:
CLAUDE_CODE_REMOTE set, or invocation via /loop or /schedule, aborts immediately, because no
human is present to answer a confirm.

sync has no such abort and is not meant to: it is the on-demand/headless maintenance action, and
/claude-ops:lanes runs it from scheduled background sessions. A code path that writes team-shared
state cannot be made safe by confirming when there may be nobody to confirm. The write must not
happen at all.

Proposed fix

Step 5 restricts its automatic enable to scopes that are not team-shared state:

This restores the invariant: after the change, no sync code path writes a committed settings file.
It trades a small amount of automatic completeness for the guarantee the skill already advertises,
and the report still tells the user exactly what to run.

converge is unaffected — it keeps its confirm gate and its autonomous abort, and its V1 scope
already excludes enable-state, so a project-scope enable gap is not something it can absorb.

Expected

  • sync.md Step 5: the scope rule above, replacing the 0.31.8 "known exposure" paragraph.
  • SKILL.md Report: an "Action needed" row for project-scope enable gaps carrying the exact command.
  • scope-semantics.md: the sync consequence bullet updated — no longer an exposure, now a
    documented restriction.

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

    priority: needs-triageDefault until a priority tier is assigned.work-class: scopedA briefed fix or small feature; blast radius bounded by the brief, tests exist.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions