fix(claude-ops): stop sync writing committed settings, restoring the invariant - #2539
Conversation
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
…invariant SKILL.md states converge is the one action that may touch a committed .claude/settings.json, and only behind a per-plugin confirm. Step 5 broke that: it issued `claude plugin enable <id> -s project` for any currentProject gap, and 0.31.8 verified that call writes exactly that file. 0.31.8 documented the exposure and asked the report to name it, which is a mitigation, not a fix — and in the meantime the invariant itself had been softened to describe the violation. Step 5 now enables automatically only where the write is not team-shared state: user scope, and local scope, which writes the gitignored settings.local.json. A project-scope gap becomes an Action-needed row carrying the runnable cd-into-its-own-projectPath command with a fully-qualified id, so the user runs it deliberately and reviews the diff. Confirming was rejected as the alternative. converge can afford a confirm gate because it aborts outright in an autonomous session; sync is the on-demand and headless maintenance action with no such abort, so there may be no human to answer. The write must not happen rather than be authorized. No sync path writes a committed settings file after this change, so SKILL.md and converge.md state the invariant plainly again instead of carving out sync. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c41c73bccb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Main shipped its own 0.31.10 while this branch was open. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
c41c73b to
df05d59
Compare
|
Claude finished @kyle-sexton's task in 2m 4s —— View job Security reviewScope: Result: No security issues foundThis PR is doc-only ( Checked specifically:
Note (out of scope for this lane): Codex's review already flagged a real logic bug at sync.md#L182-L199 — for an id installed at both |
|
Last security-reviewed head: |
|
Claude finished @kyle-sexton's task in 2m 24s —— View job Code review complete
Scope: reviewed the 6 changed files ( Finding posted inlineOne correctness issue, posted as an inline comment on Independently confirmed Codex's P2 flag on the same lines. For a plugin id installed at both Everything else checked out
|
|
Claude has reviewed this PR 2 times. The lane skips further automatic reviews after 5; deleting this comment resets the count. |
…h would break Review found the reported remediation can fail by construction. An id with no enabledPlugins entry anywhere but install records at both user and project scope yields two rows in one run: the user row enables first, and `enable -s project` gates on the merged effective value rather than that scope's raw map, so the command the project row hands the user then fails with "already enabled at project scope" — the opposite of the runnable-command goal. Step 5 now emits the project row only for ids it did not enable at user or local scope in the same run. Skipping is correct, not a convenience: after the user enable the plugin already loads in that project by scope precedence, so nothing is broken; only the team-shared declaration is absent, and that is the user's call rather than drift for sync to report as actionable. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 1m 19s —— View job Security reviewScope: Result: No security issues foundThis PR remains doc-only ( The prior review of this PR (posted at head
Confirms the write-without-confirmation path stays closed, and this incremental commit doesn't introduce any new security-relevant surface. Note (out of scope for this lane): the correctness fix itself (suppressing the project row) is a |
|
Claude finished @kyle-sexton's task in 2m 8s —— View job Code review complete
Scope: re-reviewed all 6 changed files at HEAD Result: no outstanding findingsThis push resolves the one issue raised in earlier review rounds. Both Codex and my own prior pass flagged that The current diff adds exactly the fix that gap needed, at
I verified this against Rest of the sweep:
No inline comments filed — nothing new to flag. |
Rebase onto main (which shipped sync fix as 0.31.11) and carry the morning-brief taxonomy fix as 0.31.12. Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
…are degradation (#2540) ## Summary The morning-brief Queues section no longer hardcodes the melodic-software queue label taxonomy on live runs. Default labels are unchanged, but the script now filters to labels that actually exist in the target repo (same degrade-when-absent pattern as the telemetry-issue path). Consuming repos with a different labeling scheme no longer see misleading `0`/`?` count rows. ## Changes - Live queue counts filter against `gh label list` (or `--repo-labels-json` in tests) - Degrade to "no queue labels found" when none of the configured defaults exist - Add `--queue-labels` (comma-separated pin) and `--decision-label` flags - Parked-decisions section degrades when the decision label is absent - Bump `claude-ops` to **0.31.12** (main already shipped 0.31.11 via #2539) - Regression tests for degradation, partial overlap, and pinned labels ## Test plan - [x] `bash plugins/claude-ops/skills/morning-brief/morning-brief.test.sh` (81 cases) ## Related No linked issue Fixes #610 --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
…de (#2613) No linked issue ## Summary Re-verifies `docs/CLOUD-SESSIONS.md` against rung-1 raw-markdown fetches of the three live cloud pages (`web-quickstart`, `claude-code-on-the-web`, `cloud-environments`) and adds `docs/CLOUD-FLEET-SETUP.md`, a goal-oriented guide for getting every melodic-software repository runnable in Claude Code cloud sessions from one shared environment. ## Fix - `docs/CLOUD-SESSIONS.md`: folds in the new `web-quickstart` page (onboarding split out of `claude-code-on-the-web`, including the `/web-setup` terminal flow), records the roughly-five-minute setup-script cache-build budget and its bearing on the guarded bootstrap one-liner, and notes Claude Tag as an environment-sharing surface plus self-hosted environments as the routed alternative. Verification stamp scoped honestly: the three cloud pages carry 2026-08-13; other linked pages keep 2026-07-30. - `docs/CLOUD-FLEET-SETUP.md` (new): fleet audit of all 13 attachable active repos (toolchain pins: .NET SDK 10.0.302/10.0.400, Node 24.18.0, Python 3.14, Go 1.26.6, pwsh), a shared-environment design with a paste-ready setup script, manifest-driven per-repo SessionStart hook + `settings.json` templates, routine starters, a post-creation verification checklist, and network-allowlist fallbacks for the .NET and Python installers. Records the finding that this repo's `session-start.sh` is present but unregistered in committed `settings.json` (contradicting CLOUD-SESSIONS.md), left to an explicit decision because claude-ops converge owns committed plugin enablement (#2539). ## Verification - All three cloud pages fetched via the raw `.md` channel per the upstream-drift convention (slugs confirmed canonical in `llms.txt`, first headings checked); every existing CLOUD-SESSIONS.md anchor and restated claim matched the live text before the three deltas were folded in. - Fleet audit derived from shallow clones of each repo's default branch on 2026-08-13. - `npx markdownlint-cli2` clean on both files; the embedded hook template passes `shellcheck` (one expected SC1091 info for the sourced nvm.sh). ## Related - #2539 (claude-ops converge owns committed settings — why the unregistered-hook finding is reported, not patched) - `docs/conventions/upstream-drift/README.md` (fetch-route and stamp discipline followed) 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01AXaqhrFZeGTzbCJun12Ngx --- _Generated by [Claude Code](https://claude.ai/code/session_01AXaqhrFZeGTzbCJun12Ngx)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
…ste-once rollout kit (#2677) No linked issue ## Summary Cloud sessions could never use this repo's plugins on turn one: the SessionStart hook installs the full catalog, but Claude Code builds its plugin/command registry at process start — before hook effects land — and never re-reads it. This PR records that finding with its evidence and fix, and adds the copy-paste rollout kit for moving the whole fleet (and every account environment) onto the layout that works. ## Fix - `docs/CLOUD-SESSIONS.md`: document the same-session limit observed 2026-08-15 (hook completed `65 enabled, 65 newly installed, 0 failed` with the on-disk registry fully populated, yet the same session resolved no plugin command or skill), plus the confirmation that a stop/resume restarts the process and loads everything; reframe the environment setup-script one-liner from a performance lever to the required pre-launch slot for plugin liveness. - `.claude/hooks/session-start.sh`: state the timing limit in the header — hook-time installs serve the *next* process start, so the environment setup script must run this bootstrap at cache build for a session to start with plugins loaded. - `docs/CLOUD-FLEET-SETUP.md`: refresh the stale "bootstrap is currently unwired" finding (settings.json registers the hook on `main` and it ran), fix the verification step that relied on `/plugin` (unavailable in cloud sessions), and point step 1 at the rollout kit. - `prompts/cloud-bootstrap-rollout.md` (new): the paste-once account environment stub, the prompt for creating the shared `cloud/env-setup.sh` in `melodic-software/standards`, and the per-repo migration prompt that renames `.claude/hooks/session-start.sh` to `.claude/cloud-bootstrap.sh` — one script, two callers (environment setup script pre-launch; SessionStart hook for per-session drift repair) — and settles on one Default environment per account. ## Verification - `markdownlint-cli2`, `typos`, and `editorconfig-checker` pass on all touched files; `shellcheck` and `shfmt -d` pass on the hook (comment-only change). - Empirical basis verified live in the cloud session that produced this PR: installed-on-disk vs empty session registry, a failed mid-session skill probe, and full catalog loading after resume (this repo's own commit-convention hook began enforcing mid-session once loaded). - Official-docs basis: hooks reference documents no same-session pickup; `/plugin` and `--plugin-dir` are unavailable in cloud sessions; the setup script runs after clone and before the session process. ## Related `docs/CLOUD-SESSIONS.md` §"Plugins in sessions on this repo", `docs/CLOUD-FLEET-SETUP.md`, `docs/MIGRATION-PLAYBOOK.md` (same-version commit drift), #2061, #2539, #2657 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_018NhKrvZ4Jz1qK2wWDWXiZd --- _Generated by [Claude Code](https://claude.ai/code/session_018NhKrvZ4Jz1qK2wWDWXiZd)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Summary
SKILL.mdstates the rule the skill is built around:sync.mdStep 5 broke it. It issuedclaude plugin enable <id> -s projectfor everycurrentProject: truecompleteness gap, and 0.31.8 verified on Claude Code 2.1.228 that this callwrites exactly that file. So the default, headless-capable action silently modified a team-shared
tracked file. 0.31.8 documented the exposure and asked the report to name it — a mitigation, not a
fix — and in the interim the invariant itself had been softened in
SKILL.mdandconverge.mdtodescribe the violation rather than remove it.
The fix. Step 5 now enables automatically only where the write is not team-shared state:
user— machine-scope~/.claude/settings.json.local— the gitignored.claude/settings.local.json(verified in claude-ops plugins skill: enable -s project writes committed settings, and a subdirectory install is invisible to currentProject #2526).project— never enabled automatically. Reported as an "Action needed" row carrying therunnable command, in the
cd-into-its-own-projectPathformconverge.mdmandates and with thefully-qualified id
gotchas.mdrequires, so acting on it is a copy rather than a reconstruction.Why not confirm instead of skip.
convergecan afford a confirm gate because it aborts in anautonomous session —
CLAUDE_CODE_REMOTE,/loop,/schedule.synchas no such abort by design;it is the on-demand and headless maintenance action, and
/claude-ops:lanesruns it from scheduledbackground sessions. A path that writes team-shared state cannot be made safe by asking when there
may be nobody to answer. The write must not happen.
After this change no
syncpath writes a committed settings file, soSKILL.mdandconverge.mdstate the invariant plainly again instead of carving out
sync.Test plan
markdownlint-cli2on all changed markdown — 0 issues.lychee --offline— 0 errors.check-skill.shonplugins/claude-ops/skills/plugins— PASS, 0 errors, 0 warnings.scripts/affected-tests.sh --run— every changed file is a recorded no-suite class.plugin.jsonparses; version 0.31.9 → 0.31.10.syncwrites committedsettings — none remain. The 0.31.8 "Known exposure" paragraph is gone, and the two softened
invariant statements are restored.
Related
Closes #2535
Completes the remediation deferred in #2526 / #2527, which documented this exposure but left it
unscheduled. Underlying CLI behavior is recorded upstream in
anthropics/claude-code#81058.