docs(ruff-format): document TOML inline-table opt-in-gate limitation - #896
Conversation
…(0.4.4) The pyproject.toml opt-in gate's line-anchored grep only recognizes the `[tool.ruff]` header form. TOML also allows the equivalent config as an inline table under a bare `[tool]` header, which Ruff itself honors but the gate does not detect — a false-negative opt-in, not a wrong edit. Per the issue's own triage decision, a robust fix needs real TOML parsing (inline tables span lines, nest arbitrarily, and the key can appear in any order), and a multi-pass grep heuristic risks false positives from an unrelated key or a commented-out line — out of scope for a fast, dependency-free, cross-platform bash hook. Documented the limitation in the hook, the README, and locked in the current (skip) behavior with a test case instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K1V3gkrfSf75isB8MiDy3o
|
Claude finished @kyle-sexton's task in 1m 34s —— View job Security Review — PR #896
FindingsNo security issues found. This PR is documentation-only with one new test case. Each changed file is analyzed below.
|
|
Claude finished @kyle-sexton's task in 1m 36s —— View job PR Review:
|
|
Post-green review classification (work lane):
No DEFER-FORBIDDEN class applies (docs+test-only change, no gate logic modified). No inline review threads to resolve (PR-level comments only). Flipping to ready, handing off to the babysit lane for ongoing watch. |
|
Claude finished @kyle-sexton's task in 1m 16s —— View job PR Review —
|
| Criterion | Status |
|---|---|
| Repo-agnostic | ✓ No hardcoded paths or project-specific values |
| Configurable without editing the plugin | ✓ No config changes |
| Plugin-form-safe | ✓ No ${CLAUDE_PLUGIN_ROOT} or path reach-outs |
| Versioned | ✓ 0.4.3 → 0.4.4 patch bump |
| Security-reviewed | ✓ No new code paths, no egress, no shell surface widened |
Ready to merge.
|
Claude finished @kyle-sexton's task in 1m 4s —— View job Security Review — PR #896
FindingsNo security issues found. This PR is documentation-only with one new test case. Each changed file is analyzed below.
|
## Summary `typos-format`'s hook only ran `typos --write-changes` when the consuming repo already had a `typos.toml`/`_typos.toml`/`.typos.toml`/`Cargo.toml` (`[*.metadata.typos]`)/`pyproject.toml` (`[tool.typos]`) file present. `typos` ships a built-in spelling dictionary and runs standalone with zero configuration — a repo config only widens the allowlist/exclude list, it is not an activation switch. The gate made the hook a silent no-op on exactly the zero-config repos it was meant to help, defeating the plugin's purpose. ## Fix Removed the opt-in config-gate (and its ancestor-walk grep logic) entirely. `typos --write-changes --force-exclude` now runs unconditionally on every edit, matching `markdown-format`'s existing unconditional pattern. `typos`'s own file-anchored config discovery still applies automatically when a config IS present (allowlist/exclude, precedence order) — the hook never re-implemented that discovery and still doesn't; only the activation gate is gone. Also updated: plugin manifest description/version, README (top-level + plugin), CHANGELOG, the `setup` skill's `check` step (config presence is now reported as informational only, never a gate), and the test suite (the old "gate OFF -> file left untouched" case is replaced with "runs unconditionally, fixes a real typo with no config present anywhere"). ## Verification Empirical before/after on an isolated zero-config git repo (`this document has a recieve typo`, no `typos.toml`/`_typos.toml`/`.typos.toml`/`Cargo.toml`/`pyproject.toml` anywhere in the ancestor chain): - **Before** (`origin/main`'s gated hook): file left untouched — `recieve` unfixed, hook exits silently. - **After** (this branch's unconditional hook): file rewritten in place — `recieve` → `receive`. Full test suite: `bash plugins/typos-format/hooks/typos-format.test.sh` — **41/41 passing**, including the new unconditional-fix case, kill-switch, residual-findings, exclude, and telemetry cases. Closes #884 ## Related - #872 — the merged PR that shipped the opt-in gate this PR removes. - #876 — covered a TOML inline-table detection gap in both `typos-format`'s and `ruff-format`'s opt-in-gate grep. #896 already closed it for the `ruff-format` half (documented as a known limitation there). The `typos-format` half is now moot: this PR deletes the opt-in gate and its grep entirely, so there is no gate left for that detection gap to affect. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K1V3gkrfSf75isB8MiDy3o Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Summary
ruff-format's pyproject.toml opt-in-gate check uses a line-anchored grep for a[tool.ruff]section header. TOML also allows the equivalent config as an inline table under a bare[tool]header ([tool]+ruff = { ... }) — Ruff itself honors this form, but the gate's grep does not recognize it, so a repo using that style is treated as "no config" and the hook silently skips (a false-negative opt-in, not an incorrect edit).Fix
Issue #876's own triage comment already decided the fix direction: "document the inline-table form as a known limitation in BOTH hooks... rather than add a fragile heuristic." A robust detection needs real TOML parsing (inline tables span lines, nest arbitrarily, and the key can appear in any order under
[tool]), and a multi-pass grep heuristic risks false positives from an unrelated key or a commented-out line — out of scope for a fast, dependency-free, cross-platform bash hook.This PR implements that decision for
ruff-format:Case 1d) that locks in the current, intentional (skip) behavior for the inline-table form.plugins/ruff-format/.claude-plugin/plugin.jsonto0.4.4+ CHANGELOG entry.This draft is itself the "veto before merge" checkpoint the triage comment asked for — if a maintainer wants a heuristic or a TOML-parse path instead, this is the place to say so before it merges.
Verification
bash plugins/ruff-format/hooks/ruff-format.test.sh— 52/52 passing, including the new inline-table case.shellcheckon both modified hook/test scripts — clean.Related
ruff-formathalf.typos-formatintentionally not touched in this PR. typos-format: opt-in config-gate makes the hook a silent no-op on zero-config repos, defeating the plugin's own auto-fix purpose #884 arguestypos-format's entire opt-in gate should be removed (typos runs zero-config by design; gating on config presence is itself the bug there). typos-format: opt-in config-gate makes the hook a silent no-op on zero-config repos, defeating the plugin's own auto-fix purpose #884's own triage explicitly notes: "if this lands (gate removed), typos-format/ruff-format: opt-in config grep misses TOML inline-table form for [tool.X] #876 (opt-in grep misses TOML inline-table) becomes MOOT [for typos-format] — no gate to fix." Documenting (or fixing) typos-format's identical grep limitation now would be wasted/conflicting work if typos-format: opt-in config-gate makes the hook a silent no-op on zero-config repos, defeating the plugin's own auto-fix purpose #884 lands first. Deferring typos-format's half of typos-format/ruff-format: opt-in config grep misses TOML inline-table form for [tool.X] #876 to typos-format: opt-in config-gate makes the hook a silent no-op on zero-config repos, defeating the plugin's own auto-fix purpose #884's resolution.Closes #876
Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01K1V3gkrfSf75isB8MiDy3o