feat: tier A checks for settings.json, hooks, and skill frontmatter - #2
Merged
Merged
Conversation
Completes tier A so gtmify-config can call this workflow. Three checks aimed at the class of breakage that does not announce itself: config that silently stops working on somebody else's machine. settings_gate.sh resolves every script path settings.json references and asserts it exists and is executable. The interesting part is the path mapping: commands are authored as "$HOME"/.claude/hooks/x.sh because that is where they live on a workstation, where ~/.claude/hooks symlinks into this repo. CI has no ~/.claude, so that prefix is rewritten to the repo root or every hook reads as missing. hook_smoke.sh runs a hook ONLY if the hook advertises a --dry-run flag, detected by grepping the script. Hooks are arbitrary code with side effects; one of them commits and pushes, another writes to Supabase. Executing them blindly in CI would be reckless. Against gtmify-config that means 1 ran and 10 skipped, and the report says exactly that rather than implying full coverage. frontmatter_gate.sh asserts skills declare a name and description. A skill with no description never triggers and never errors either: it looks installed and does nothing, which is the most expensive kind of broken. TWO OF MY OWN BUGS, both caught by running against the real repo rather than trusting the code, and both worth recording because they are easy to reintroduce: 1. The settings token regex reported all 9 hooks as missing. A character class that accepts a quote starts matching at the CLOSING quote of "$HOME", yielding $HOME"/.claude/... which resolves to nothing. Quotes are now stripped before paths are extracted. 2. The frontmatter check reported 55 failures, every one false. Git pathspec wildcards match across slashes by default, so `agents/*.md` reached `agents/instructions/*.md`, which are Paperclip instruction files and prose docs with no frontmatter by design. Now uses `:(glob)` magic so `*` stops at a slash, and the default scope is skills plus .claude/agents only. A gate with 55 false positives gets deleted the same day, so scope here is deliberately narrow and extendable with --glob rather than broad and wrong. Incidental finding while testing, not fixed here: gtmify-config/agents/ contains no Claude Code agent definitions at all, only Paperclip instructions and documentation, despite CLAUDE.md describing "6 global agents in ~/.claude/agents/". All three no-op cleanly in a repo that has none of these files. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Completes tier A so
gtmify-configcan call the shared workflow. Three checks aimed at breakage that does not announce itself.settings_gate.shsettings.jsonthat does not parse, or a hook path that is missing or not executablehook_smoke.sh--dry-runfrontmatter_gate.shnameordescription, which never triggers and never errorsThe path mapping in
settings_gateis the non-obvious part. Hook commands are authored as"$HOME"/.claude/hooks/x.shbecause that is where they live on a workstation, where~/.claude/hookssymlinks into this repo. CI has no~/.claude, so that prefix is rewritten to the repo root or every hook reads as missing.hook_smokenever executes a hook that lacks a declared no-op flag. Hooks are arbitrary code with side effects: one commits and pushes, another writes to Supabase. It greps for--dry-runsupport and only then invokes it. Againstgtmify-configthat is 1 run and 10 skipped, and the output says exactly that rather than implying full coverage.Two of my own bugs, found by running against the real repo
Both are recorded inline because they are easy to reintroduce.
The settings regex reported all 9 hooks as missing. A character class that accepts a quote starts matching at the closing quote of
"$HOME", producing$HOME"/.claude/...which resolves to nothing. Quotes are now stripped before paths are extracted.The frontmatter check reported 55 failures, every one false. Git pathspec wildcards match across slashes by default, so
agents/*.mdreachedagents/instructions/*.md, which are Paperclip instruction files and prose documentation with no frontmatter by design. Now uses:(glob)magic so*stops at a slash, with scope defaulting to skills and.claude/agentsonly, extendable via--glob.A gate with 55 false positives gets deleted the same day. Scope is deliberately narrow rather than broad and wrong.
Verified against the real repo
All three also no-op cleanly in a repo that has none of these files, which is how tiers B and C stay unaffected.
shellcheck --severity=warningandbash -nclean.Incidental finding, not fixed here
gtmify-config/agents/contains no Claude Code agent definitions at all, only Paperclip instruction files and documentation, despite CLAUDE.md describing "6 global agents in ~/.claude/agents/". Worth reconciling separately: either those agents were never Claude Code agents, or they went missing.Next
Move
v1on merge, then the tier A caller ingtmify-configalongside the local pre-commit hook.🤖 Generated with Claude Code