Skip to content

feat(format-plugins): declare and probe prerequisites in the five remaining format plugins - #5468

Merged
kyle-sexton merged 18 commits into
mainfrom
feat/5286-format-plugin-prerequisite-probes
Sep 30, 2026
Merged

kyle-sexton merged 18 commits into
mainfrom
feat/5286-format-plugin-prerequisite-probes

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Closes #5286

Summary

bash-format, ruff-format, typos-format, actionlint and powershell-format probed binaries with no manifest, so /claude-ops:prerequisites reported missing=0 on a host lacking their tools. Each now declares its prerequisites, probes at SessionStart and ships a check skill.

Fix

  • Each plugin ships prerequisites.json, a SessionStart probe, and skills/check; its hook notice names the check skill.
  • bash-format (shfmt, shellcheck) and ruff-format (ruff) move their missing-tool notices to the prerequisite class: once per session, shared by all agents, install route kept on renewal. The jq notice stays once per session and agent.
  • In every probed plugin whose hook has a missing-binary notice (all but powershell-format, whose hook exits quietly without pwsh), the SessionStart probe and the PostToolUse notice share one latch key, so the probe's notice counts as the first and the first PostToolUse notice stays silent until the renewal. actionlint's hook latched on actionlint-missing while its probe used actionlint-actionlint; the hook now uses the probe's key, and a suite case pins it.
  • scripts/check-prerequisite-probes.test.sh binds each notice text to its manifest and covers the new plugins.
  • docs/formatter-path-probes.md, the skill cheat sheet and the leaf-name registry list the new probes and skills.
  • claude-ops prerequisites Next section points at every formatter and linter check skill, /actionlint:check included.
  • The hooks.json description of each of the five plugins names the SessionStart probe.
  • ruff-format docs: the probe checks the working directory plus seven ancestors for .venv/bin/ruff, and say that a Windows host whose only ruff is .venv/Scripts/ruff.exe still gets the probe notice (the manifest holds one local_bin and the probe script is shared byte-for-byte by every probed plugin, so a second path is a shared-schema change left out of this PR).
  • powershell-format docs: a machine without pwsh is INFO in check, and the probe notice appears on it once per session while the plugin is enabled (owner decision Q1 A on hooks: missing external tools are not surfaced to the user; add a model-invocable fleet-wide prerequisites check (no auto-install) #4240); the setup skill and its evals no longer call it not-applicable by design.
  • Versions: actionlint 0.11.0, bash-format 0.9.0, powershell-format 0.9.0, ruff-format 0.8.0, typos-format 0.8.0, claude-ops 0.71.3, each with a CHANGELOG entry. The bash-format, ruff-format, typos-format and actionlint entries record the latch change.

Verification

  • Merged origin/main; the conflicts were the claude-ops and typos-format versions and CHANGELOGs (main ships claude-ops 0.71.2 and typos-format 0.7.9), resolved by re-bumping to claude-ops 0.71.3 and typos-format 0.8.0 above main's entries.
  • scripts/check-changelog-parity.sh --check, --check-order, --check-bump origin/main and --check-preserved origin/main: pass.
  • scripts/validate-plugins.sh: all manifests and the catalog validated.
  • scripts/sync-hook-utils.sh --check, scripts/sync-exec-bash.sh --check, scripts/check-changed-skills.sh origin/main, scripts/generate-cheatsheet.mjs --check, check-evals-quality.sh on the two edited evals files: pass.
  • Hook suites for actionlint (60 cases), bash-format (69), ruff-format (76), typos-format (205), powershell-format (96): 0 failed. The new actionlint case fails against the old actionlint-missing key.
  • scripts/check-prerequisite-probes.test.sh: 51 cases, 0 failed. It runs in CI (ci.yml).
  • check-prerequisites.test.sh (claude-ops): 30 cases, 0 failed.
  • The 0.x.5 CHANGELOG entries on main already say no probe ships (for example plugins/bash-format/CHANGELOG.md 0.8.5 and plugins/ruff-format/CHANGELOG.md 0.7.5). That correction satisfies AC4; the probes now exist.

Related

Refs #4240, #5096. PR 5377 covers playwright, biome and markdown separately.

🤖 Generated with Claude Code

kyle-sexton and others added 16 commits September 29, 2026 15:51
…check skill

Refs #5286

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…check skill

Refs #5286

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… check skill

Refs #5286

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e to the manifest

Refs #5286

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tart, add check skill

Refs #5286

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…obe in the path-probe table

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ite probes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he actionlint key

The shfmt, shellcheck and ruff notices moved to the prerequisite class, which
latches once per session across agents. The bash-format and ruff-format README
and setup text still said once per session and agent; only the jq notice is
per agent. Both CHANGELOG entries now record the widened latch, the kept
install route on renewal, and that the probe shares the hook's latch key.

actionlint's hook latched on actionlint-missing while its probe latched on
actionlint-actionlint, so both notices fired on a host without the binary. The
hook now uses the probe's key, pinned by a suite case.

Also list /actionlint:check in the prerequisites Next section and mark the
biome, go and markdown rows of the path-probe table as having a probe.

Refs #5286

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The new SessionStart probe latches on the same key as the PostToolUse
missing-typos notice, so the first PostToolUse notice stays silent until the
renewal. The 0.8.0 entry listed only the probe.

Refs #5286

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…in-prerequisite-probes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…or .venv/bin/ruff

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…in-prerequisite-probes

Re-bump claude-ops to 0.70.1 above main's 0.70.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… does

- hooks.json descriptions in actionlint, bash-format, ruff-format,
  typos-format and powershell-format name the SessionStart probe.
- ruff-format: the probe checks the working directory plus seven
  ancestors, not eight, and a Windows host whose only ruff is
  .venv/Scripts/ruff.exe still gets the probe notice.
- powershell-format: a machine without pwsh is INFO in check, and the
  probe notice appears on it once per session while the plugin is enabled.

Refs #5286

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kyle-sexton
kyle-sexton marked this pull request as ready for review September 30, 2026 03:08
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-30T03:11:40.161288Z 2e7fc09 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@claude

claude Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 7m 44s —— View job


Code review complete

  • Gather context (PR diff, CLAUDE.md/AGENTS.md, REVIEW.md)
  • Understand the request (CI code-review lane via /review:code-review)
  • Review changed files for correctness, error handling, test coverage, maintainability
  • Post inline findings (none met the bar — see below)
  • Post final summary

Scope reviewed: all 55 changed files (git diff origin/main...HEAD, head 2e7fc09) — the five new probe-prerequisite.sh/prerequisites.json/skills/check additions (actionlint, bash-format, ruff-format, typos-format, powershell-format), the hooks.json SessionStart wiring, the latch-key renames (actionlint-actionlint, bash-format-shfmt/shellcheck, ruff-format-ruff, typos-format-typos), and the docs/registry/CHANGELOG updates.

What I checked directly:

  • Confirmed probe-prerequisite.sh is byte-identical across all five new plugin copies (and identical to the pre-existing biome-format/go-format/markdown-format copies).
  • Validated every new/modified prerequisites.json and hooks.json with jq empty — all parse.
  • Traced the notice-key derivation ($plugin-$name from plugin.json/prerequisites.json) against each hook's hardcoded hook::notice_once key and confirmed they match exactly (e.g. ruff-format-ruff, bash-format-shfmt/bash-format-shellcheck, typos-format-typos, actionlint-actionlint), so the "probe's notice counts as the first" latch-sharing claim holds.
  • Cross-checked each hardcoded PostToolUse notice string against its manifest's install/check fields for exact substring match (what the new test cases assert) — all matched.
  • Verified alphabetical placement in docs/skill-cheat-sheet.md and scripts/skill-leaf-name-registry.txt, and the claude-ops CHANGELOG's 0.70.1/0.70.0 ordering.

Not independently verified: I could not execute the shell test suites or bash -n syntax checks in this sandbox (script execution required approval I wasn't able to obtain), so the PR's own reported hook-suite and check-prerequisite-probes.test.sh pass counts are author-claimed, not something I confirmed by running them myself.

Non-blocking observation (not filed as a finding): have() in probe-prerequisite.sh walks up to 8 directories from $PWD with no ceiling (e.g. ruff-format/hooks/probe-prerequisite.sh#L17-L27), unlike hook::walk_up_to's required-ceiling doctrine documented in the same hook-utils.sh ("a ceiling-less walk is somebody else's config governing an edit"). A .venv/bin/ruff in a directory above the actual project root could make the probe report ruff present when the project itself has none. I'm not filing this as a blocking finding: the script is copied byte-for-byte from the already-shipped biome-format/go-format/markdown-format probes, so it predates this PR rather than being introduced by it.

No correctness bugs specific to this PR's changes met the high-signal bar for an inline finding.

@claude

claude Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 1m 46s —— View job


Security review complete

Reviewed the diff (gh pr diff 5468) against HEAD 2e7fc09. No security issues found.

No security-relevant surface. This PR adds SessionStart prerequisite probes, prerequisites.json manifests, check skills, and matching docs/CHANGELOG entries for five plugins (actionlint, bash-format, ruff-format, typos-format, powershell-format). Specifically checked:

  • probe-prerequisite.sh (identical across all five plugins, e.g. bash-format): parses only its own plugin's static, repo-committed prerequisites.json with hand-rolled regex extraction, never network- or user-supplied JSON. field()'s regex name arg (name, local_bin, check, install) is always a hardcoded literal, never derived from input, so there's no regex/command injection path. have() only tests -x on a path built from the static local_bin value walking up from $PWD — no execution, no traversal into attacker-controlled input.
  • The hook-side notice changes (bash-format.sh, ruff-format.sh, typos-format.sh, actionlint-check.sh) are text/latch-key changes only (hook::notice_once ... prerequisite), no new logic paths.
  • No .github/workflows/** files are touched, so Actions-trigger/permissions hardening (zizmor's lane) doesn't apply here.
  • No instruction-surface files (CLAUDE.md, AGENTS.md, rules, skill/agent bodies with guardrails) are deleted, narrowed, or softened — this PR only adds probes, manifests, and skills, so the instruction-exception-register lens found nothing to flag.
  • Scanned the full diff for injection-prone patterns (curl, wget, eval, token/secret/password handling, plaintext http://) — none present; all install-route URLs are documentation strings displayed to the user, not fetched or executed.

Nothing rises to CRITICAL/IMPORTANT/SUGGESTION; this is a low-risk, internal-tooling change confined to static manifest parsing and notice text.
· Branch

🤖 Generated with Claude Code

…in-prerequisite-probes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@claude

claude Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 3m 18s —— View job


Security review complete

No security issues found.

What changed since the last completed security review on this PR (which ran against 2e7fc09, the prior head): gh pr diff 5468 against the current head (cb5128982d98fef7ba4daac1284dfaf10a4cc12d) still resolves to exactly the same 55 files reported in this PR's file list. Diffing 2e7fc09..cb51289 on the paths this PR actually touches shows only:

  • A claude-ops version/CHANGELOG bump (0.70.1 to 0.71.2) picking up unrelated upstream fixes to native_drift.py/OTEL pruning that merged from main — none of that logic lands in this PR's own files.
  • A docs/skill-cheat-sheet.md line removal (the retired map-states skill), from the same main merge.
  • A prose-only edit to typos-format's setup skill refusal wording (now citing the shared docs/plugin-philosophy.md refusal template) — no new install/write path introduced.

None of that touches the security-relevant surface (the shared probe-prerequisite.sh, the prerequisites.json manifests, or the hook notice/latch wiring), so the prior review's verdict still holds. I re-verified directly rather than relying solely on that:

  • probe-prerequisite.sh (e.g. actionlint's copy) parses only its own plugin's static, repo-committed prerequisites.json via hand-rolled regex extraction, never network- or user-supplied JSON. field()'s regex name argument is always a hardcoded literal (name, local_bin, check, install), never derived from input, so there is no regex/command-injection path. have() only tests -x on a path built from the static local_bin value — no execution.
  • All five new prerequisites.json manifests (e.g. typos-format, powershell-format, actionlint) are static, repo-authored data with no dynamic construction.
  • actionlint-check.sh L96-97 invokes actionlint -shellcheck= -pyflakes= -- "$AL_TARGET": the -- separator and quoting prevent flag injection even if a filename began with -.
  • No .github/workflows/** files are touched (zizmor's Actions-hardening lane doesn't apply here).
  • No instruction-surface files (CLAUDE.md, AGENTS.md, rules, skill/agent guardrail bodies) are deleted, narrowed, or softened — checked against the instruction-exception-register lens; nothing to flag.
  • Scanned the full diff for injection-prone patterns (curl, wget, eval, token/secret/credential handling, plaintext http://) — none present; install-route URLs are documentation strings shown to the user, never fetched or executed.

This is a low-risk, internal-tooling change confined to static manifest parsing and notice text; nothing rises to CRITICAL/IMPORTANT/SUGGESTION.
· Branch

🤖 Generated with Claude Code

@claude

claude Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 4m 45s —— View job


Code review complete

  • Gather context (PR diff via gh pr diff 5468, matches the 55-file changed-files list; origin/main as base)
  • Invoke /review:code-review and follow its instructions
  • Post inline findings (none met the bar — see below)
  • Post final summary

Scope reviewed: all 55 files in the PR diff at head cb51289 — probe-prerequisite.sh additions across actionlint/bash-format/ruff-format/typos-format/powershell-format, the new/modified prerequisites.json manifests, the five hooks.json SessionStart wirings, the latch-key changes in bash-format.sh/ruff-format.sh/typos-format.sh/actionlint-check.sh, the new skills/check additions and evals, and the docs/registry/CHANGELOG updates.

Note on scope: the diff between origin/main and this head is identical (same 55 files, same content) to what an earlier review already covered at commit 2e7fc09 — the commits since then (2e7fc09..cb51289) are exclusively unrelated origin/main merge traffic (discovery, go-format, instruction-placement, planning, source-control, etc.), none of which touches this PR's actual changed files. So this pass re-verified the same diff rather than finding new surface.

What I checked directly:

  • Confirmed probe-prerequisite.sh is byte-identical across all five new plugin copies.
  • Validated the notice-key derivation ($plugin-$name) in each probe-prerequisite.sh against the hardcoded hook::notice_once key in each hook (ruff-format-ruff, bash-format-shfmt/bash-format-shellcheck, typos-format-typos, actionlint-actionlint) — all match, confirming the shared-latch claim.
  • Verified plugins/actionlint/hooks/actionlint-check.sh now uses actionlint-actionlint (was actionlint-missing), matching its pre-existing prerequisites.json (unchanged by this PR, already shipped "name": "actionlint") and the new probe's derived key.
  • Cross-checked each hooks.json SessionStart row's --run-if-unset-or-true <PLUGIN>_ENABLED flag against that plugin's existing PostToolUse gate — all five match (ACTIONLINT_ENABLED, BASH_FORMAT_ENABLED, RUFF_FORMAT_ENABLED, TYPOS_FORMAT_ENABLED, POWERSHELL_FORMAT_ENABLED).
  • Verified the disclosed ruff-format Windows gap: the shared probe checks only .venv/bin/ruff (manifest's single local_bin), not .venv/Scripts/ruff.exe, while the PostToolUse hook (ruff-format.sh:128) checks both. This is explicitly documented in plugins/ruff-format/README.md (lines 57, 70-71) as a known, deferred limitation, not silently introduced.
  • Verified docs/skill-cheat-sheet.md and scripts/skill-leaf-name-registry.txt list the four new check skills in alphabetical order and completely.
  • Verified plugins/claude-ops/skills/prerequisites/SKILL.md's ## Next section names all eight formatter/linter check skills including /actionlint:check.

Not independently verified: shell test suite execution (*.test.sh, scripts/check-prerequisite-probes.test.sh) and validate-plugins.sh/check-changelog-parity.sh — I did not run these; the PR's reported pass counts are author-claimed.

Non-blocking observations (not filed as findings):

  • have() in probe-prerequisite.sh walks up to 8 ancestor directories from $PWD with no ceiling (e.g. ruff-format/hooks/probe-prerequisite.sh#L17-L27) — pre-existing in the already-shipped biome-format/go-format/markdown-format copies, not introduced here (flagged in the prior review at 2e7fc09 for the same reason).
  • The PR body's "Versions" line still says claude-ops 0.70.1, but the current head has claude-ops at 0.71.2 (a later origin/main merge advanced it past the described re-bump). Cosmetic staleness in the PR description text, not in code — plugin.json and CHANGELOG.md agree with each other at 0.71.2.

No correctness bugs specific to this PR's changes met the high-signal bar for an inline finding.
· Branch

…in-prerequisite-probes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@claude

claude Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 1m 45s —— View job


Code review complete

  • Gather context (PR diff, CLAUDE.md/AGENTS.md, custom instructions)
  • Invoke /review:code-review and follow its instructions
  • Post inline findings (none — see below)
  • Post final summary

Skip gate hit: this head already has a successful review from this lane that still applies.

This lane already completed a full code review of this PR's substantive diff at commit 2e7fc09 (2026-09-30T03:18:38Z) and again at cb51289 (2026-09-30T04:05:23Z), both with no blocking findings. I diffed cb51289..1bab88c (the current head) restricted to this PR's own changed files (docs/formatter-path-probes.md, docs/skill-cheat-sheet.md, scripts/skill-leaf-name-registry.txt, and the actionlint/bash-format/ruff-format/typos-format/powershell-format/claude-ops plugin directories) to confirm nothing new needs review:

  • The five probe scripts, prerequisites.json manifests, hooks.json wiring, hook latch-key changes, skills/check additions, and docs/registry updates are byte-identical to what was reviewed at cb51289.
  • The only genuine delta is a mechanical claude-ops version re-bump: plugins/claude-ops/.claude-plugin/plugin.json (0.71.2 → 0.71.3) and its CHANGELOG.md, renumbering this PR's own "prerequisites skill Next section" entry above an unrelated 0.71.2 entry (explain-usage description change) that landed on main and collided with this branch's prior bump during the latest origin/main merge. No logic, hook, or manifest content changed.
  • The four claude-ops skill SKILL.md files that also show up in a raw cb51289..1bab88c diff (audit-install-state, audit-performance, audit-skill-visibility, observability) are identical to origin/main — they're merge noise from main, not part of this PR's actual diff, and aren't in this PR's changed-files list.

No new review surface. The prior findings summary stands: no correctness bugs met the high-signal bar in either pass; the only non-blocking observation raised (the ceiling-less have() walk in probe-prerequisite.sh, pre-existing in already-shipped plugins, not introduced by this PR) was already disclosed and is unchanged.
· Branch

@claude

claude Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 1m 25s —— View job


Security review complete

No security issues found — skip gate applies (head already has a successful security review that still applies).

This head (1bab88c) is a merge of origin/main into the branch, on top of cb51289, which already had a completed security review in this thread. I verified the PR's actual diff is unaffected:

  • gh pr diff 5468 --name-only at the current head returns the same 55 files as the prior reviews.
  • git diff cb51289..1bab88c restricted to every path this PR touches (all five plugin directories, docs/formatter-path-probes.md, docs/skill-cheat-sheet.md, scripts/skill-leaf-name-registry.txt) shows zero changes to any of this PR's own files.
  • The only files that differ between cb51289 and 1bab88c are six plugins/claude-ops/** files this PR does not touch (skills/audit-install-state, audit-performance, audit-skill-visibility, observability, plus the plugin.json version bump and CHANGELOG.md) — unrelated origin/main traffic (a 0.71.2→0.71.3 bump and native-surface Boundary-section prose tightening) pulled in by the merge, outside this PR's diff.

So the security-relevant surface reviewed at cb51289 — probe-prerequisite.sh (static, repo-authored prerequisites.json parsing via hand-rolled regex with hardcoded field names, no injection path), the five new prerequisites.json manifests, the shared-latch hook::notice_once key wiring, and actionlint-check.sh's quoted actionlint -- "$AL_TARGET" invocation — is unchanged. No .github/workflows/** files are touched. No instruction-surface guardrail (CLAUDE.md, AGENTS.md, rules, skill/agent bodies) is deleted, narrowed, or softened by this PR's own changes.

Nothing rises to CRITICAL/IMPORTANT/SUGGESTION.
· Branch

@kyle-sexton
kyle-sexton merged commit f05e783 into main Sep 30, 2026
32 of 33 checks passed
@kyle-sexton
kyle-sexton deleted the feat/5286-format-plugin-prerequisite-probes branch September 30, 2026 05:19
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.

hooks: prerequisites.json, session-start probe and check skill for the remaining binary-probing format plugins

1 participant