Only 1 of 36 plugin repos (decision-flow) runs claudecode-linter in CI at all #7

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

Origin

kotkan/claude-plugin-decision-flow#76 (closed) — same fleet-wide CI audit as the sibling issue filed alongside this one (path-filter exclusion finding). This is a separate finding: different affected repos, different remedy — keeping them apart on purpose.

Context

claudecode-linter (CCL) 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 class 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): 3 repos (anxious, inference-arbitrage, worktree-discipline) have a test.yaml pipeline with NO path filter, so they DO fire on skill/agent changes — but no pipeline in any of them invokes claudecode-linter. Net effect matches the sibling path-filter-exclusion finding (skill changes go unlinted), but the fix differs: these need a lint step added to an existing pipeline, not a new pipeline.

kotkan/claude-plugin-decision-flow is currently the ONLY repo in the fleet with any CCL coverage in CI at all (.woodpecker/lint.yml, v0.33.2, commits a532841/5c3e5c1 — verified: Woodpecker pipeline #7 on a skills-only commit ran that workflow alone, exit 0, 18s). Its step: 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. CCL exits non-zero only on ERROR-severity findings, so adopting repos should expect warnings to be non-fatal.

Scope note

Sibling finding (filed alongside this one, same title prefix) covers 9 repos whose CI path filters actively exclude the skills tree — a missing-pipeline problem. This issue is the 3 repos that already have an unfiltered pipeline but simply never call CCL — an add-a-step problem.

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) — same fleet-wide CI audit as the sibling issue filed alongside this one (path-filter exclusion finding). This is a **separate** finding: different affected repos, different remedy — keeping them apart on purpose. ## Context `claudecode-linter` (CCL) 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 class 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): 3 repos (`anxious`, `inference-arbitrage`, `worktree-discipline`) have a `test.yaml` pipeline with NO path filter, so they DO fire on skill/agent changes — but no pipeline in any of them invokes `claudecode-linter`. Net effect matches the sibling path-filter-exclusion finding (skill changes go unlinted), but the fix differs: these need a lint **step added to an existing pipeline**, not a new pipeline. `kotkan/claude-plugin-decision-flow` is currently the ONLY repo in the fleet with any CCL coverage in CI at all (`.woodpecker/lint.yml`, v0.33.2, commits `a532841`/`5c3e5c1` — verified: Woodpecker pipeline #7 on a skills-only commit ran that workflow alone, exit 0, 18s). Its step: `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`. CCL exits non-zero only on ERROR-severity findings, so adopting repos should expect warnings to be non-fatal. ## Scope note Sibling finding (filed alongside this one, same title prefix) covers 9 repos whose CI path filters actively exclude the skills tree — a missing-pipeline problem. This issue is the 3 repos that already have an unfiltered pipeline but simply never call CCL — an add-a-step problem. 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:16 +03:00
Author
Owner

Done — all 3 repos now invoke CCL in their existing pipeline, each verified by a real run

Audit re-verified on disk first (2026-08-14): anxious, inference-arbitrage and worktree-discipline each confirmed to have a test.yaml with no path: filter, and grep -rl claudecode-linter .woodpecker/ returned nothing in any of them. Accurate as filed. (anxious also carries a hooks-release.yml, which doesn't change the remedy.)

What shipped

A claudecode-linter step appended to each existing test.yaml — an added step, not a new pipeline, exactly as this issue scoped it. Pinned to claudecode-linter@2.1.232 (confirmed still current on npm), node:22-slim, not piped.

One design decision worth stating: the step is gated

when:
  - status: [success, failure]

Without it, Woodpecker skips later steps once an earlier one fails, so a broken SKILL.md would land unnoticed behind an unrelated red step — the lint would be present and not running, which is the exact failure shape this issue exists to close. anxious already uses this pattern for its go-parity step, with the same rationale.

That gating was then confirmed in practice rather than assumed — see worktree-discipline below.

The step would have landed RED on 1 of 3

Ran CCL locally against all three first. One genuine ERROR-severity finding, fixed in the same commit:

  • inference-arbitrageskill-md/description-no-angle-brackets: skills/offload-audit/SKILL.md's description contained a literal <plugin> placeholder. The rule permits angle brackets only inside HTML comments (the fleet's ROUTING TRIGGERS sentinels), not in prose. Reworded to "a plugin", preserving the trigger phrase. Also excluded tests/fixtures/**, which holds deliberate frontmatter snapshots of other plugins — linting them would let an upstream fixture refresh fail this repo's build over a file it doesn't own.

anxious passes at exit 0, but only because its .claudecode-lint.yaml downgrades three rules to info. One of those downgrades is now unnecessary and one is masking a live problem — both filed separately: oleks/claude-plugin-anxious#139 and oleks/claude-plugin-anxious#140. #140 matters to this issue's premise: skills/wip-claim/SKILL.md carries a 1228-character description — over the 1024 limit, and almost exactly the magnitude that broke decision-flow. The step added here will not catch it, by that repo's own deliberate config. Worth knowing that this repo's new coverage is real but not total.

Verification — the step actually ran, and exited 0

repo commit pipeline claudecode-linter step
anxious f8507b3 #69 success exit 0
inference-arbitrage 3e7413e #44 success exit 0
worktree-discipline 9cae6db #105 failure exit 0

worktree-discipline needs the honest note. Its pipeline is red — but the failure is the pre-existing suites step, not the step added here. That repo's main has been red on #100, #102, #103, #104 and #105; the red predates this change by days. Root cause diagnosed from the #105 log: the suites step's apk add installs bash git python3 coreutils but not jq, so one suite self-skips (SKIPPED (needs jq)) and parity reports 57 passed, 12 failed, most of those being pure JSON-whitespace artifacts of bash falling back off jq. It does not reproduce locally — the repo's own pre-push hook printed ALL SUITES PASSED on this machine. Filed with the diagnosis, along with one genuine bash-vs-Go parity bug found in the same log (bash mangles arghrgh in WTD_ISSUE name composition).

That run is also the best available evidence that the status: [success, failure] gating works as intended: shellcheck was skipped when suites failed, while claudecode-linter still ran and passed. The design goal is demonstrated, not just asserted.

Closing.

## Done — all 3 repos now invoke CCL in their existing pipeline, each verified by a real run Audit re-verified on disk first (2026-08-14): `anxious`, `inference-arbitrage` and `worktree-discipline` each confirmed to have a `test.yaml` with no `path:` filter, and `grep -rl claudecode-linter .woodpecker/` returned nothing in any of them. Accurate as filed. (`anxious` also carries a `hooks-release.yml`, which doesn't change the remedy.) ### What shipped A `claudecode-linter` step appended to each existing `test.yaml` — an added step, not a new pipeline, exactly as this issue scoped it. Pinned to `claudecode-linter@2.1.232` (confirmed still current on npm), `node:22-slim`, not piped. One design decision worth stating: the step is gated ```yaml when: - status: [success, failure] ``` Without it, Woodpecker skips later steps once an earlier one fails, so a broken SKILL.md would land unnoticed behind an unrelated red step — the lint would be *present* and *not running*, which is the exact failure shape this issue exists to close. `anxious` already uses this pattern for its `go-parity` step, with the same rationale. That gating was then confirmed in practice rather than assumed — see worktree-discipline below. ### The step would have landed RED on 1 of 3 Ran CCL locally against all three first. One genuine ERROR-severity finding, fixed in the same commit: - **`inference-arbitrage`** — `skill-md/description-no-angle-brackets`: `skills/offload-audit/SKILL.md`'s description contained a literal `<plugin>` placeholder. The rule permits angle brackets only inside HTML comments (the fleet's `ROUTING TRIGGERS` sentinels), not in prose. Reworded to "a plugin", preserving the trigger phrase. Also excluded `tests/fixtures/**`, which holds deliberate frontmatter snapshots of *other* plugins — linting them would let an upstream fixture refresh fail this repo's build over a file it doesn't own. `anxious` passes at exit 0, but only because its `.claudecode-lint.yaml` downgrades three rules to `info`. One of those downgrades is now unnecessary and one is masking a live problem — both filed separately: [oleks/claude-plugin-anxious#139](https://git.oleks.space/oleks/claude-plugin-anxious/issues/139) and [oleks/claude-plugin-anxious#140](https://git.oleks.space/oleks/claude-plugin-anxious/issues/140). **#140 matters to this issue's premise**: `skills/wip-claim/SKILL.md` carries a **1228-character** description — over the 1024 limit, and almost exactly the magnitude that broke decision-flow. The step added here will *not* catch it, by that repo's own deliberate config. Worth knowing that this repo's new coverage is real but not total. ### Verification — the step actually ran, and exited 0 | repo | commit | pipeline | `claudecode-linter` step | |---|---|---|---| | anxious | `f8507b3` | #69 ✅ success | ✅ exit 0 | | inference-arbitrage | `3e7413e` | #44 ✅ success | ✅ exit 0 | | worktree-discipline | `9cae6db` | #105 ❌ failure | ✅ **exit 0** | **worktree-discipline needs the honest note.** Its pipeline is red — but the failure is the pre-existing `suites` step, not the step added here. That repo's `main` has been red on #100, #102, #103, #104 and #105; the red predates this change by days. Root cause diagnosed from the #105 log: the `suites` step's `apk add` installs `bash git python3 coreutils` but **not `jq`**, so one suite self-skips (`SKIPPED (needs jq)`) and parity reports `57 passed, 12 failed`, most of those being pure JSON-whitespace artifacts of bash falling back off jq. It does not reproduce locally — the repo's own pre-push hook printed `ALL SUITES PASSED` on this machine. Filed with the diagnosis, along with one genuine bash-vs-Go parity bug found in the same log (bash mangles `argh` → `rgh` in `WTD_ISSUE` name composition). That run is also the best available evidence that the `status: [success, failure]` gating works as intended: `shellcheck` was **skipped** when `suites` failed, while `claudecode-linter` **still ran** and passed. The design goal is demonstrated, not just asserted. Closing.
oleks closed this issue 2026-08-14 11:52:36 +03:00
oleks added spent time 14 minutes 2026-08-14 11:52:49 +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:51 +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#7