Stop clobbering the Claude settings.local.json personal layer#729
Conversation
c29613d to
e9c9aef
Compare
There was a problem hiding this comment.
Pull request overview
This pull request adjusts how the config repo distributes Claude Code settings so ./config/pull no longer overwrites developers’ personal .claude/settings.local.json, while moving the shared permissions into the committed shared settings templates.
Changes:
- Stop distributing (and deleting)
.claude/settings.local.json; move shared permissions into.claude/settings.json/.claude/settings-hugo.json. - Update
.gitignoreto ignore.claude/settings.local.json(and add an ignore for.claude/plans/), and remove.idea/misc.xmlfrom being tracked/shared. - Bump
ToolBasesnapshot version from2.0.0-SNAPSHOT.402to2.0.0-SNAPSHOT.403.
Reviewed changes
Copilot reviewed 7 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Updates documentation about Claude settings distribution behavior. |
| migrate | Stops touching settings.local.json and adds logic around .idea/misc.xml handling. |
| buildSrc/src/main/kotlin/io/spine/dependency/local/ToolBase.kt | Bumps ToolBase snapshot version. |
| .idea/misc.xml | Removes tracked IntelliJ misc.xml from the repo. |
| .gitignore | Stops re-including .idea/misc.xml; ignores Claude plan scratch + personal local settings file. |
| .claude/settings.local.json | Removes the config-distributed local settings layer. |
| .claude/settings.json | Adds plansDirectory and moves shared allowlist entries here. |
| .claude/settings-hugo.json | Adds plansDirectory and shared allowlist entries for Hugo-only repos. |
| .agents/tasks/claude-settings-local-personal-layer.md | Adds a task-plan document for this change. |
Files not reviewed (1)
- .idea/misc.xml: Generated file
e9c9aef to
d71ee0e
Compare
settings.local.json personal layerThere was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4158d5e68e
ℹ️ 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".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5f274b46e7
ℹ️ 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".
`migrate` copied `.claude/settings.local.json` into every consumer and overwrote it on each `./config/pull` (the Hugo-only branch even `rm -f`'d it). In Claude Code that file is the gitignored, per-developer personal-override layer (precedence user < project < local), so distributing it silently wiped a developer's overrides and forced the file into Git — contrary to its "local, not committed" role. - `migrate` no longer creates, overwrites, or deletes `settings.local.json`; a `./config/pull` leaves the personal file untouched. - The genuinely org-wide permissions it carried (`Skill(pre-pr)` and the `version-bumped` guard) move into the shared `settings.json` / `settings-hugo.json`; the `echo "exit=$?"` session cruft is dropped. - The distributed `.gitignore` baseline now ignores `/.claude/settings.local.json`, and config un-tracks its own copy to dogfood the personal-layer contract. - Both distributed templates gain `"plansDirectory": ".agents/tasks"` so plan/status docs default into the repo-local, non-clobbered task dir org-wide. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wrap two over-length lines and correct the stale "not part of the PR" line (the plan file is committed as the tracked task doc, per AGENTS.md). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
"doing either" implied two actions but three precede it; say "any of these ... or forced the file into Git" (per docs review). Comment-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude Code's plan-mode files get harness-assigned random slugs, so routing them into `.agents/tasks/` would mix random-named files into the meaningfully-named task namespace. Point `plansDirectory` at a gitignored `.claude/plans/` scratch dir instead (in both distributed templates), and ignore it in the baseline; durable, agent-authored task docs stay under `.agents/tasks/`. Mirrors agents PR #30. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The work is complete and the PR description is the durable record. Per .agents/tasks/README.md the plan file is deleted rather than landing in master; this also resolves the missing-frontmatter review note by not shipping the file. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A consumer whose `.gitignore` is a legacy unmarked raw copy of an older baseline still carries `!.idea/misc.xml` (the re-inclusion the current baseline dropped). `update-gitignore.sh`'s first-migration path preserves every consumer negation into the repo-local block, which sits AFTER the managed baseline — so, gitignore being last-match-wins, the stale negation re-included `.idea/misc.xml`, leaving it committable even after a `git rm --cached`. An already-migrated consumer hits the same via the steady-state path (the negation was baked into repo-local by an earlier first-migration). The secret trailer does not catch this — `misc.xml` is not a secret glob. Add a `retired_negations` set (currently `!.idea/misc.xml`) and strip it from the repo-local region in BOTH the steady-state and first-migration paths, so the baseline's `.idea/*.xml` ignore wins. Genuine consumer negations and custom lines are untouched. Covered by two new regression cases in `test-update-gitignore.sh` (steady-state and first-migration); the full suite passes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
388946b to
fcbe1a3
Compare
settings.local.json personal layer
Summary
Config hygiene for Claude Code settings, plus a hardening fix to the
.gitignoremerge. (An earlier revision of this PR also excluded
.idea/misc.xml; that is nowhandled on
masterby #728, so this PR defers to it — see "Reconciliation" below.)1. Claude
settings.local.json— the personal-override layer (primary)In Claude Code,
.claude/settings.local.jsonis the gitignored, per-developerpersonal-override layer (
user < project < local).migratedistributed and overwroteit on every pull (the Hugo-only branch even
rm -f'd it), silently wiping personaloverrides and forcing the file into Git.
migrateno longer creates, overwrites, or deletes it.Skill(pre-pr),Skill(pre-pr:*),version-bumped) move into the sharedsettings.json/settings-hugo.json; theecho "exit=$?"cruft is dropped..gitignorebaseline ignores/.claude/settings.local.json; configun-tracks its own copy.
2.
plansDirectory→ gitignored.claude/plansBoth distributed templates set
"plansDirectory": ".claude/plans", and the baselineignores
/.claude/plans/. Claude Code plan-mode files get harness-assigned random slugs,so they belong in a gitignored scratch dir; durable, meaningfully-named task docs stay
under
.agents/tasks/. (Mirrors agents PR #30.)3. Harden the
.gitignoremerge against retired baseline negationsscripts/update-gitignore.shnow strips "retired" baseline!-negations from aconsumer's repo-local region in both the first-migration and steady-state paths, so a
negation config has removed from the baseline can't survive in repo-local (after the
managed block) and re-include a now-ignored path. First entry:
!.idea/misc.xml(thenegation #728 retired). Complements #728's
migrate-side scrub by fixing it at-source,with two new regression cases in
scripts/test-update-gitignore.sh.4. Bundled:
ToolBase→2.0.0-SNAPSHOT.403A small, pre-existing lockstep version bump in
buildSrc/.../ToolBase.kt. Publishes onmerge.
Reconciliation with #728
mastermerged #728 ("Stop tracking .idea/misc.xml; treat it as project-local"), whichhandles the
.idea/misc.xmlexclusion more completely (config keeps its own copy as theshared seed; consumers untrack via
migrate). This PR's earlier misc.xml changes weredropped in favor of #728 — the branch now contains no misc.xml changes of its own, and
config's
.idea/misc.xmlstays tracked. The only related piece kept is theupdate-gitignore.shretired-negation hardening (#3), which is broader than misc.xml andcarries test coverage.
Migration note for consumers
A repo that already committed
.claude/settings.local.jsonkeeps tracking it until aone-time
git rm --cached .claude/settings.local.json(gitignore does not untrack analready-tracked file).
Verification
bash -n migrate; scratch simulation confirms a developer's personalsettings.local.jsonsurvives a pull; the moved permissions +plansDirectoryland inthe distributed templates.
scripts/test-update-gitignore.sh— full suite passes, including the newretired-negation cases (first-migration + steady-state); Secrets invariants intact.
spine-code-review,review-docs,dependency-audit,kotlin-engineer— all APPROVE.🤖 Generated with Claude Code