Skip to content

feat(global-settings): derive the update list from settings.json and verify installed files (v2.7.0) - #804

Merged
WilcoLouwerse merged 1 commit into
mainfrom
feat/global-settings-integrity-check
Oct 1, 2026
Merged

WilcoLouwerse merged 1 commit into
mainfrom
feat/global-settings-integrity-check

Conversation

@WilcoLouwerse

Copy link
Copy Markdown
Contributor

Problem

The UPDATE REQUIRED notice that check-settings-version.sh emits carried a hand-maintained list of files to install. That list fell behind settings.json twice: v2.4.1 added two hooks it had missed, and block-polling.sh, registered in settings.json since v2.5.0, was never on it. Anyone who updated through the notice landed a settings.json that wires a hook whose script is absent, and the version check then reported them as up to date: it compared VERSION and nothing else.

Change

check-settings-version.sh (installed as a hook, hence the VERSION bump to 2.7.0):

  • The file list is derived, not maintained. The hook fetches the canonical settings.json from the same source it took VERSION from and emits one command block per hook script it registers, plus settings.json first and VERSION last. A hook that is wired is a hook that gets installed. Hook names are validated before they are placed in a command. If the canonical settings.json cannot be fetched, the list falls back to the installed one and a warning says so.
  • Installed files are verified when the versions match. Every registered hook is compared with its canonical copy (sha256, trailing newlines ignored because the printf '%s\n' install writes exactly one). settings.json is compared as parsed JSON without the model key: the VSCode model picker writes {"model": …} under relock option (B), and a model choice is a preference, not policy. Everything else in that file counts, and the notice names the differing top-level keys.
  • New notice: FILES OUT OF SYNC. Same unlock, contract and relock steps as an update, repair blocks for the affected files only, VERSION left alone. The phrase is repair my global settings. A canonical copy that could not be fetched is reported as not verified, never assumed fine.
  • Panel: a Files line (7/7 verified ✓, or the missing/changed counts).
  • The emitted command shapes are unchanged (literal canonical URL, or literal origin/main: path), so block-write-commands.sh allows them as before. The GitHub path costs two extra fetches per session (settings.json, then all hooks in one curl call); the git-fetch path costs none.

Docs: global-settings/README.md (new subsection under Updating) and docs/claude/global-claude-settings.md (session-start messages, "What check-settings-version.sh does").

Tests

New global-settings/tests/test-check-settings-version.sh, wired into hook-tests.yml. Runs offline: a throwaway file:// origin holding this branch's global-settings/, a clone the hook fetches from, and a fake home. The first part takes the git-fetch path; the second configures settings-repo-url and puts a curl shim in PATH that serves the same files, so the GitHub path and its one-call multi-file fetch are covered too. Positive control: an install pinned to v0.0.1 must be reported outdated.

Covered: every registered hook appears in the update list and VERSION is last; a hook registered only in settings.json appears without touching the script (derived, not hardcoded); clean install → up to date, all verified; missing hook, changed hook, changed settings.json (permissions) and invalid JSON → FILES OUT OF SYNC with blocks for those files only; model-only change and extra trailing newlines are not drift; an unfetchable canonical copy → "not verified".

Results: check-settings-version 78/78, block-polling 29/29, ShellCheck clean at all severities (repo .shellcheckrc).

After merge

Every developer gets the usual UPDATE REQUIRED for v2.7.0. From then on, an install with a missing or drifted hook is reported at the next session start instead of passing on the version number.

🤖 Generated with Claude Code

…verify installed files (v2.7.0)

check-settings-version.sh:

- The UPDATE REQUIRED notice no longer carries a hand-maintained file
  list. It fetches the canonical settings.json from the same source as
  VERSION and emits one command block per hook script it registers, plus
  settings.json first and VERSION last. The list had fallen behind
  settings.json twice: v2.4.1 added two hooks it had missed, and
  block-polling.sh, registered since v2.5.0, was never on it, so following
  the notice left that guard uninstalled while the version read as
  current. Hook names are validated before they land in a command; if the
  canonical settings.json cannot be fetched, the list falls back to the
  installed one and a warning says so.
- When the installed version matches the online one, every registered
  hook is compared with its canonical copy (sha256, trailing newlines
  ignored: the printf-based install writes exactly one) and settings.json
  as parsed JSON without `model` (the VSCode picker writes that key under
  relock option B; a model choice is a preference, not policy). A missing
  or changed file produces a FILES OUT OF SYNC notice with the same
  unlock/contract/relock steps and repair blocks for the affected files
  only; the phrase is "repair my global settings". A canonical copy that
  cannot be fetched is reported as not verified, never assumed fine.
- The status panel gains a Files line (7/7 verified, or the missing and
  changed counts).
- The GitHub path fetches settings.json and then all hooks in one curl
  call each; the git-fetch path uses git show. The emitted command shapes
  are unchanged, so block-write-commands.sh allows them as before.

README.md and docs/claude/global-claude-settings.md describe the derived
list, the integrity check and the new notice.

Tests: new tests/test-check-settings-version.sh runs offline against a
file:// origin through the git-fetch path and, via a curl shim in PATH,
the GitHub path; wired into hook-tests.yml. Suites: check-settings-version
78/78, polling 29/29. ShellCheck clean at all severities.

VERSION 2.6.0 -> 2.7.0.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@WilcoLouwerse
WilcoLouwerse merged commit 6084486 into main Oct 1, 2026
48 checks passed
@WilcoLouwerse
WilcoLouwerse deleted the feat/global-settings-integrity-check branch October 1, 2026 00:34
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.

1 participant