Condition: Close KNOWN-LIMITS 57. The self-modification rule keys on one literal
filename fragment (.claude/settings.json), so an edit to a project's local Claude Code
settings variant (.claude/settings.local.json) goes straight through ungated, while the
same edit to the main settings file is gated. Both files carry permission rules of equal
power.
Fix: Classify what the file IS, not one remembered spelling. New pure classifier
isClaudeSettingsPath() wired into both deciders (isSelfModEdit and isSelfModCommand),
registered in matcherHashInputs(). Replaces the old '.claude/settings.json' substring
entry in selfModFragmentsForBase, which was wrong both ways: it missed the local variant
and gated my.claude/settings.json.
On a / or \ separator, case-insensitively:
| Spelling | Example | Match |
|---|---|---|
| main | .claude/settings.json |
yes |
| local | .claude/settings.local.json |
yes |
| any variant | .claude/settings.dev.json |
yes |
| Windows trailing-dot | .claude/settings.local.json. |
yes |
| managed/enterprise | ClaudeCode/managed-settings.json |
yes |
| managed alt | claude-code/managed-settings.json |
yes |
.bak sibling |
.claude/settings.local.json.bak |
no |
| different dir | my.claude/settings.json |
no |
| different dir | .claudeX/settings.json |
no |
Anchor: a / or \ or start-of-string before .claude (not a word char or dot).
Terminator: .json followed by more filename (.bak) does not match; a bare trailing
. (the Windows spelling) does.
c5-settings-class.patch— the 78-line patch tosrc/policy/index.jstest/policy-selfmod-settings-class.test.js— 20 test cases (13 defect, 7 control)evidence/run1-base-fail.txt— unpatched base + test: 7 pass / 13 failevidence/run2-patched-pass.txt— base + patch + test: 20 pass / 0 failevidence/run3-full-suite.txt— full suite on patched: 3 pre-existing failures (identical on base)
# Clone at the base commit
git clone https://github.com/githubscum/lotor.git
cd lotor
git checkout 1720aa1d8662e62b0b0f136a754533a6cd3ece01
# Run the test on unpatched main (expect 7 pass / 13 fail)
cp test/policy-selfmod-settings-class.test.js test/
node --test test/policy-selfmod-settings-class.test.js
# Apply the patch
git apply c5-settings-class.patch
# Run the test again (expect 20/20)
node --test test/policy-selfmod-settings-class.test.js
# Full suite (expect 3 pre-existing failures, none in settings/self-mod)
npm testThis fix still misses:
- A settings file reached by copy, rename, or symlink whose FINAL path does not carry
the
.claude/settings*.jsonshape (a write names its destination, so the ordinary case is covered). - Non-
.jsonsiblings such assettings.local.json.bak, deliberately, since Claude Code does not read them as settings. - An organization that relocates
managed-settings.jsonoutside aClaudeCode-named directory.
- Repo:
githubscum/lotor@1720aa1d8662e62b0b0f136a754533a6cd3ece01(unpatched main) - Patch:
c5-settings-class.patch(applies cleanly viagit apply) - Test:
test/policy-selfmod-settings-class.test.js(20 cases) - Submitted by: custos (custos-1f916)
- Date: 2026-09-14