9 plugin repos' CI path filters exclude skills/agents/commands trees, so claudecode-linter never runs on them #6

Closed
opened 2026-08-14 10:41:09 +03:00 by oleks · 2 comments
Owner

Origin

kotkan/claude-plugin-decision-flow#76 (closed) — fixing that repo's own SKILL.md description-length breakage led to a fleet-wide audit of CI coverage for claudecode-linter (CCL), which enforces skill-md/description-max-length (1024 chars) and skill-md/valid-frontmatter on Claude Code plugin artifacts. A SKILL.md whose frontmatter breaks silently stops loading — no error. On decision-flow this already caused real damage: a 1065-char description sat undetected on main, a later edit took it to 1230, and the pre-push CCL hook then rejected EVERY push to that repo, including a branch deletion.

Finding

Read-only audit of all 36 subdirectories of ~/projects/claude-plugins/ (2026-08-14): 9 repos have a real Woodpecker pipeline whose when: path: filter actively excludes skills/**, agents/**, commands/** and .claude-plugin/**, with no other pipeline in the repo covering them. A change to those trees therefore triggers NO pipeline at all — meaning a broken SKILL.md can merge to main with zero CI signal.

Affected repos and their actual path filters:

  • agents-routing: *.go, go.mod, go.sum
  • anti-patterns: *.go, go.mod, go.sum
  • hook-instrument: *.go, go.mod, go.sum, tests/**, .woodpecker/**
  • hooklib: *.go, go.mod, go.sum
  • hyprpanel-state: *.go, go.mod, go.sum
  • memory: *.go, go.mod, go.sum
  • sirpa-agents: *.go, go.mod, go.sum
  • spec-kit: *.go, go.mod, go.sum plus a narrow markdownlint-sync pipeline
  • statusline: *.go, go.mod, go.sum

Root cause worth recording: these are all hooks-release.yml-shaped pipelines written to gate a Go binary. The filters are correct for their original purpose; nothing was ever written to cover the skills tree. This is a missing-pipeline problem, not a mis-copied-filter problem.

Fix template (already shipped and CI-verified)

kotkan/claude-plugin-decision-flow's .woodpecker/lint.yml (v0.33.2, commits a532841/5c3e5c1). It runs npx --yes claudecode-linter@2.1.232 --ignore 'tests/plugin-fixtures/**' . in a node:22-slim image, filtered to skills/**, commands/**, agents/**, .claude-plugin/**, hooks/hooks.json. Verified: Woodpecker pipeline #7 on a skills-only commit ran that workflow alone, exit 0, 18s. CCL exits non-zero only on ERROR-severity findings, so adopting repos should expect warnings to be non-fatal.

Scope note

This is separate from oleks/claude-plugin-cicd-insights (finding 2, filed alongside this one), which covers 3 repos that DO fire on skill/agent changes but never invoke CCL at all — different repos, different remedy (add a step vs. add a pipeline).

Not filed as a finding, context only: 23 further plugin repos have no .woodpecker/ directory whatsoever — weaker/possibly intentional, noting so it isn't re-derived.

## Origin kotkan/claude-plugin-decision-flow#76 (closed) — fixing that repo's own SKILL.md description-length breakage led to a fleet-wide audit of CI coverage for `claudecode-linter` (CCL), which enforces `skill-md/description-max-length` (1024 chars) and `skill-md/valid-frontmatter` on Claude Code plugin artifacts. A SKILL.md whose frontmatter breaks silently stops loading — no error. On decision-flow this already caused real damage: a 1065-char description sat undetected on `main`, a later edit took it to 1230, and the pre-push CCL hook then rejected EVERY push to that repo, including a branch deletion. ## Finding Read-only audit of all 36 subdirectories of `~/projects/claude-plugins/` (2026-08-14): 9 repos have a real Woodpecker pipeline whose `when: path:` filter actively excludes `skills/**`, `agents/**`, `commands/**` and `.claude-plugin/**`, with no other pipeline in the repo covering them. A change to those trees therefore triggers NO pipeline at all — meaning a broken SKILL.md can merge to `main` with zero CI signal. Affected repos and their actual path filters: - `agents-routing`: `*.go`, `go.mod`, `go.sum` - `anti-patterns`: `*.go`, `go.mod`, `go.sum` - `hook-instrument`: `*.go`, `go.mod`, `go.sum`, `tests/**`, `.woodpecker/**` - `hooklib`: `*.go`, `go.mod`, `go.sum` - `hyprpanel-state`: `*.go`, `go.mod`, `go.sum` - `memory`: `*.go`, `go.mod`, `go.sum` - `sirpa-agents`: `*.go`, `go.mod`, `go.sum` - `spec-kit`: `*.go`, `go.mod`, `go.sum` plus a narrow markdownlint-sync pipeline - `statusline`: `*.go`, `go.mod`, `go.sum` Root cause worth recording: these are all `hooks-release.yml`-shaped pipelines written to gate a Go binary. The filters are correct for their original purpose; nothing was ever written to cover the skills tree. This is a **missing-pipeline problem**, not a mis-copied-filter problem. ## Fix template (already shipped and CI-verified) kotkan/claude-plugin-decision-flow's `.woodpecker/lint.yml` (v0.33.2, commits `a532841`/`5c3e5c1`). It runs `npx --yes claudecode-linter@2.1.232 --ignore 'tests/plugin-fixtures/**' .` in a `node:22-slim` image, filtered to `skills/**`, `commands/**`, `agents/**`, `.claude-plugin/**`, `hooks/hooks.json`. Verified: Woodpecker pipeline #7 on a skills-only commit ran that workflow alone, exit 0, 18s. CCL exits non-zero only on ERROR-severity findings, so adopting repos should expect warnings to be non-fatal. ## Scope note This is separate from oleks/claude-plugin-cicd-insights (finding 2, filed alongside this one), which covers 3 repos that DO fire on skill/agent changes but never invoke CCL at all — different repos, different remedy (add a step vs. add a pipeline). Not filed as a finding, context only: 23 further plugin repos have no `.woodpecker/` directory whatsoever — weaker/possibly intentional, noting so it isn't re-derived.
oleks added the agent/wip label 2026-08-14 11:39:12 +03:00
oleks added this to the claude-plugin-cicd-insights-board project 2026-08-14 11:39:15 +03:00
Author
Owner

Done — all 9 repos now lint their component trees in CI, each verified by a real pipeline run

Audit re-verified on disk first (2026-08-14): all 9 repos confirmed to have a hooks-release.yml-shaped compile gate filtered to *.go/go.mod/go.sum, and grep -rl claudecode-linter .woodpecker/ returned nothing in any of them. The finding was accurate as filed.

What shipped

A new .woodpecker/lint.yml in each repo, adapted from the decision-flow template with two deliberate deviations:

  • labels: {arch: arm64} added. The template omits it. Three sibling pipelines in this fleet carry that label with a comment stating the single amd64 agent crash-loops on gRPC auth, so an unlabeled workflow can land on it and die — reading red in CI and green by hand. Filed against the template itself as kotkan/claude-plugin-decision-flow#80.
  • pull_request trigger added alongside push+branch: main, matching the fleet's documented double-run-avoidance convention, so PRs are covered once rather than not at all.

Path filter: skills/**, commands/**, agents/**, .claude-plugin/**, hooks/hooks.json, .claudecode-lint.yaml, .woodpecker/lint.yml. Pinned to claudecode-linter@2.1.232 (confirmed still the current npm version). Kept as a separate pipeline rather than widening the compile gate's filter.

The gate would have landed RED on 2 of 9 — so it caught real defects immediately

Ran CCL locally against every repo before writing any pipeline. Seven were already clean (warnings only, which are non-fatal). Two had genuine ERROR-severity findings, fixed in the same commit so the gate is green on arrival:

  • sirpa-agents — 72 errors. 71× agent-md/mcp-tools-resolve: its agents grant mcp__plugin_cluster_gitea-tools__* while plugin.json declared no dependency on the cluster plugin. Declared it, matching what anxious and spec-kit already do. Plus 1× marketplace-json/schema-valid: "source": "." fails the schema's ^\./.* pattern → "./".
  • statusline — 1 error. Same "source": ".""./" marketplace defect.

Worth recording, because it corrects a note in this fleet: anxious's .claudecode-lint.yaml downgrades agent-md/mcp-tools-resolve on the stated grounds that "the rule does not consult dependencies". That is no longer true in CCL 2.1.232. Verified directly on sirpa-agents — 71 findings with no dependencies key, 0 after adding it, 71 again after reverting. So the honest fix was to declare the dependency, not to suppress the rule. Filed as oleks/claude-plugin-anxious#139.

Verification — every pipeline actually fired and went green

Not just "committed". Each commit touched .woodpecker/lint.yml, which is inside its own path filter, so each triggered its own run. This mattered: agents-routing's own pipeline comment records sibling pipelines sitting at zero runs ever despite being committed, and memory's records a push landing before the repo was registered in Woodpecker.

repo commit pipeline result
agents-routing 8df77da #1 success
anti-patterns 763b061 #2 success
hook-instrument 6b9f89b #25 success
hooklib 60d4e28 #3 success
hyprpanel-state 85bf950 #3 success
memory b5bc1f8 #3 success
sirpa-agents 2655927 #2 success (lint workflow, claudecode-linter step exit 0)
spec-kit e8393f5 #4 success
statusline f441d3b #1 success

Note hooklib and hyprpanel-state have no skills/, agents/ or commands/ trees today — only .claude-plugin/. The filter is kept uniform across all nine anyway, so those trees are covered the day someone adds one.

Closing.

## Done — all 9 repos now lint their component trees in CI, each verified by a real pipeline run Audit re-verified on disk first (2026-08-14): all 9 repos confirmed to have a `hooks-release.yml`-shaped compile gate filtered to `*.go`/`go.mod`/`go.sum`, and `grep -rl claudecode-linter .woodpecker/` returned nothing in any of them. The finding was accurate as filed. ### What shipped A new `.woodpecker/lint.yml` in each repo, adapted from the decision-flow template with two deliberate deviations: - **`labels: {arch: arm64}` added.** The template omits it. Three sibling pipelines in this fleet carry that label with a comment stating the single amd64 agent crash-loops on gRPC auth, so an unlabeled workflow can land on it and die — reading red in CI and green by hand. Filed against the template itself as [kotkan/claude-plugin-decision-flow#80](https://git.oleks.space/kotkan/claude-plugin-decision-flow/issues/80). - **`pull_request` trigger added** alongside `push`+`branch: main`, matching the fleet's documented double-run-avoidance convention, so PRs are covered once rather than not at all. Path filter: `skills/**`, `commands/**`, `agents/**`, `.claude-plugin/**`, `hooks/hooks.json`, `.claudecode-lint.yaml`, `.woodpecker/lint.yml`. Pinned to `claudecode-linter@2.1.232` (confirmed still the current npm version). Kept as a separate pipeline rather than widening the compile gate's filter. ### The gate would have landed RED on 2 of 9 — so it caught real defects immediately Ran CCL locally against every repo before writing any pipeline. Seven were already clean (warnings only, which are non-fatal). Two had genuine ERROR-severity findings, fixed in the same commit so the gate is green on arrival: - **`sirpa-agents` — 72 errors.** 71× `agent-md/mcp-tools-resolve`: its agents grant `mcp__plugin_cluster_gitea-tools__*` while `plugin.json` declared no dependency on the `cluster` plugin. Declared it, matching what `anxious` and `spec-kit` already do. Plus 1× `marketplace-json/schema-valid`: `"source": "."` fails the schema's `^\./.*` pattern → `"./"`. - **`statusline` — 1 error.** Same `"source": "."` → `"./"` marketplace defect. Worth recording, because it corrects a note in this fleet: `anxious`'s `.claudecode-lint.yaml` downgrades `agent-md/mcp-tools-resolve` on the stated grounds that *"the rule does not consult `dependencies`"*. **That is no longer true in CCL 2.1.232.** Verified directly on sirpa-agents — 71 findings with no `dependencies` key, **0** after adding it, 71 again after reverting. So the honest fix was to declare the dependency, not to suppress the rule. Filed as [oleks/claude-plugin-anxious#139](https://git.oleks.space/oleks/claude-plugin-anxious/issues/139). ### Verification — every pipeline actually fired and went green Not just "committed". Each commit touched `.woodpecker/lint.yml`, which is inside its own path filter, so each triggered its own run. This mattered: `agents-routing`'s own pipeline comment records sibling pipelines sitting at **zero runs ever** despite being committed, and `memory`'s records a push landing before the repo was registered in Woodpecker. | repo | commit | pipeline | result | |---|---|---|---| | agents-routing | `8df77da` | #1 | ✅ success | | anti-patterns | `763b061` | #2 | ✅ success | | hook-instrument | `6b9f89b` | #25 | ✅ success | | hooklib | `60d4e28` | #3 | ✅ success | | hyprpanel-state | `85bf950` | #3 | ✅ success | | memory | `b5bc1f8` | #3 | ✅ success | | sirpa-agents | `2655927` | #2 | ✅ success (`lint` workflow, `claudecode-linter` step exit 0) | | spec-kit | `e8393f5` | #4 | ✅ success | | statusline | `f441d3b` | #1 | ✅ success | Note `hooklib` and `hyprpanel-state` have no `skills/`, `agents/` or `commands/` trees today — only `.claude-plugin/`. The filter is kept uniform across all nine anyway, so those trees are covered the day someone adds one. Closing.
oleks closed this issue 2026-08-14 11:52:36 +03:00
oleks added spent time 14 minutes 2026-08-14 11:52:45 +03:00
Author
Owner

⏱ worked | 14-08-26 | session oleks/8752153d | +14min | session-total 14min (lane-release) | elapsed from timeline

⏱ worked | 14-08-26 | session oleks/8752153d | +14min | session-total 14min (lane-release) | elapsed from timeline
oleks added agent/worked:oleks/8752153dagent/worked:oleks/8752153d:14min and removed agent/wip labels 2026-08-14 11:52:47 +03:00
Sign in to join this conversation.
1 Participants
Notifications
Total Time Spent: 14 minutes
oleks
14 minutes
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: oleks/claude-plugin-cicd-insights#6