Skip to content

ISSUE-354: Resolve declarative settings doctor checks from the project scope - #357

Merged
bguidolim merged 2 commits into
mainfrom
bruno/ISSUE-354-doctor-settings-scope
Jul 28, 2026
Merged

ISSUE-354: Resolve declarative settings doctor checks from the project scope#357
bguidolim merged 2 commits into
mainfrom
bruno/ISSUE-354-doctor-settings-scope

Conversation

@bguidolim

@bguidolim bguidolim commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

The declarative hookEventExists and settingsKeyEquals doctor checks read the global ~/.claude/settings.json unconditionally. Project-scoped sync writes hook entries and settings keys to the project's settings.local.json, so for any project-scoped pack these checks were pointed at a file their artifacts were never written to — passing when the global file happened to hold the event or key, failing when the pack had installed correctly. Both directions were silent.

Also carries a one-line formatting-config fix, because a clean main currently fails the lint job and this PR would otherwise be red for reasons unrelated to it.

Closes #354

Changes

  • Settings-reading checks now resolve the project's settings.local.json before the global settings.json. That order matches Claude Code's own precedence, so the check reports on the configuration actually in effect; doctor output names the file that answered.
  • A settings file that exists but cannot be parsed is reported in the result message instead of being skipped and falling through silently.
  • mcs pack validate warns when a pack declares scope on a check type that ignores it. scope selects the base directory for an author-supplied path, so it only ever applied to the four path-based check types — the schema docs advertised it as universal, which is how two types came to accept and quietly discard it.
  • Disables the wrapIfStatementBodies SwiftFormat rule. Newer releases wrap single-line if x { return y } bodies, which this codebase writes inline throughout, so a clean main fails swiftformat --lint --strict . on 20 files, 109 violations, all from that one rule. The lint job installs SwiftFormat unpinned, so this is a moving target; declaring the existing style in config keeps the check stable across upgrades, where pinning the tool version would freeze the repo on an old release and still leave local swiftformat runs rewriting unrelated lines.

Test plan

  • swift test passes locally
  • swiftformat --lint . and swiftlint pass without violations — 0/138 files require formatting (was 20 on main), SwiftLint 0 violations
  • Affected commands verified with a real pack — mcs pack validate was run against a real pack and emits the new warning. mcs sync / mcs doctor could not be exercised safely: Environment resolves the home directory via NSHomeDirectory(), which ignores HOME, so a sandboxed CLI run would read and write the real ~/.mcs and ~/.claude. Covered instead by integration tests that drive the runner with an injected sandbox environment, asserting both a pass when the event exists only in the project file and a failure when it exists nowhere.

To verify manually in a scratch project: sync a pack whose check is hookEventExists, confirm the hook lands in <project>/.claude/settings.local.json and not in ~/.claude/settings.json, then run mcs doctor → expect a pass reading registered in settings.local.json (this fails before the change). Move the event to the global file → expect a pass reading registered in settings.json. Corrupt the project file → expect a warning naming it as unreadable.

Checklist for engine changes
  • Integration tests updated for new features (LifecycleIntegrationTests or DoctorRunnerIntegrationTests)
  • Docs updated if behavior changed (CLAUDE.md, docs/, techpack.yaml schema in ExternalPackManifest.swift)

- hookEventExists and settingsKeyEquals now read project settings.local.json
  before global settings.json, so project-scoped packs are verified against the
  file their artifacts were actually written to
- Report settings files that exist but cannot be parsed instead of discarding
  the error and falling through silently
- Warn from mcs pack validate when a check declares scope on a type that ignores it
- Newer SwiftFormat releases wrap single-line `if x { return y }` bodies,
  which this codebase writes inline throughout — a clean main fails
  `swiftformat --lint --strict .` on 20 files under 0.62.1
- Declares the existing style in config rather than pinning the tool version,
  so the check is stable across SwiftFormat upgrades
@bguidolim
bguidolim enabled auto-merge (squash) July 28, 2026 21:50
@bguidolim
bguidolim merged commit 6c19774 into main Jul 28, 2026
4 checks passed
@bguidolim
bguidolim deleted the bruno/ISSUE-354-doctor-settings-scope branch July 28, 2026 21:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Declarative doctor checks hookEventExists and settingsKeyEquals ignore scope

1 participant