Day 44 of 49 · Monday · Learning

The config that failed openConcept

A single trailing comma made your entire global Claude config invisible — 53 allow rules, 52 deny rules, every hook, and your default model. Nothing failed loudly. This is what 'failing open' looks like.

Catch-up progress
44/49
Why this matters to you

Tonight's weekly review ran a JSON parse on ~/.claude/settings.json and it threw. `claude doctor` had been reporting 'Invalid or malformed JSON' for days. The comma was left behind by the Aug 11 lesson's own hands-on — rewriting the six malformed Glob() deny rules as Read(...). The repair introduced the outage.

A config file has two ways to be wrong. It can say the wrong thing — you typed the wrong path, allowed the wrong command — and the system does something you didn't want. Or it can be unreadable, and the system does nothing at all, quietly, using its built-in defaults instead. The first kind announces itself. The second kind is invisible, because a program that can't read your preferences doesn't know it had any.

That's 'failing open': when the safety mechanism breaks, the gate swings open rather than shut. Your settings.json is mostly protective — 52 deny rules stopping reads of .env files, SSH keys, .pem certificates, your Documents and Pictures folders. While the file was unparseable, none of those existed. Neither did your 53 allow rules, which is the other half of the damage: headless jobs that depend on pre-approved tools hit permission walls with no human to click Approve. The Aug 11 lesson diagnosed exactly that failure for the JobHunt sweep.

The nastiest detail is the provenance. This break came from a fix. You went in to correct six malformed deny rules — a real problem, correctly identified — and left a comma where JSON forbids one. Every edit to a config is a chance to zero it out, and the edits most likely to break it are the ones you make while improving it.

Worked example

The check that would have caught it, and the one line that did the damage:

# What the file said (line 113-114) — JSON does not allow that last comma
#   "Read(/Users/tom/Library/Mobile Documents/**)",
#   ]

# The whole diagnostic, in one command:
python3 -c "import json; json.load(open('/Users/tom/.claude/settings.json'))"

# And what the tool already knew:
claude doctor | grep -A2 'Invalid settings'

# After the fix — count what came back online:
python3 -c "
import json; d=json.load(open('/Users/tom/.claude/settings.json'))
p=d['permissions']
print('allow:', len(p['allow']), 'deny:', len(p['deny']))
print('hooks:', list(d['hooks']), 'model:', d['model'])"
▶ Do it now
  1. Run the `json.load` one-liner above against ~/.claude/settings.json and ~/.claude/settings.local.json. Both should be silent — silence is a pass.
  2. Open PDB/scripts/job_health_check.py and find the check registry near the bottom (the artifact_check / heartbeat_check / daemon_check list).
  3. Add a config_check that json.loads both settings files and returns False with the exception text on failure. It costs about eight lines and follows the same (bool, message) contract as the others.
  4. Break it on purpose: add a trailing comma to a scratch copy, point the check at it, and confirm the 🚨 message names the file and the line number. Then undo it and re-run — you want to see the check pass, fail, and pass again.

Gotchas

Go deeper: Claude Code settings reference
One-card takeaway

A config that won't parse doesn't fail loudly — it fails open, silently, using defaults you never chose. Validate it in the monitor, not in your memory.