Skip to content

feat: tier A checks for settings.json, hooks, and skill frontmatter - #2

Merged
scott-wueschinski-GTMify merged 1 commit into
mainfrom
feat/tier-a-checks
Jul 29, 2026
Merged

feat: tier A checks for settings.json, hooks, and skill frontmatter#2
scott-wueschinski-GTMify merged 1 commit into
mainfrom
feat/tier-a-checks

Conversation

@scott-wueschinski-GTMify

Copy link
Copy Markdown
Contributor

Completes tier A so gtmify-config can call the shared workflow. Three checks aimed at breakage that does not announce itself.

Script Catches
settings_gate.sh settings.json that does not parse, or a hook path that is missing or not executable
hook_smoke.sh A hook that fails its own --dry-run
frontmatter_gate.sh A skill with no name or description, which never triggers and never errors

The path mapping in settings_gate is the non-obvious part. Hook 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 never 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-run support and only then invokes it. Against gtmify-config that 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/*.md reached agents/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/agents only, 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

>> settings gate passed. 9 referenced script(s) exist and are executable.
   ran 1, skipped 10 (no safe flag), failed 0
>> frontmatter gate passed.          (all skills)

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=warning and bash -n clean.

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 v1 on merge, then the tier A caller in gtmify-config alongside the local pre-commit hook.

🤖 Generated with Claude Code

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>
@scott-wueschinski-GTMify
scott-wueschinski-GTMify merged commit ebd844e into main Jul 29, 2026
1 check passed
@scott-wueschinski-GTMify
scott-wueschinski-GTMify deleted the feat/tier-a-checks branch July 29, 2026 12:24
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