Skip to content

feat(check): support knip 6 JSON reporter output and bump knip to 6.17.1 - #2826

Draft
marschattha wants to merge 6 commits into
mainfrom
ma/knip-6
Draft

feat(check): support knip 6 JSON reporter output and bump knip to 6.17.1#2826
marschattha wants to merge 6 commits into
mainfrom
ma/knip-6

Conversation

@marschattha

Copy link
Copy Markdown
Member

Summary

Stacked on #2825 (knip 6 requires the node 22 runtime). Updates the knip output parser for knip 6's restructured JSON reporter and promotes knip to 6.17.1.

knip 6 changed its JSON reporter output:

  • unused files moved from a top-level files array into per-issue rows with a files entry
  • enumMembers / classMembers changed from maps keyed by parent symbol to flat lists carrying the parent on each identifier's namespace field

The parser now accepts both shapes via an untagged KnipMembers enum, so knip 5 and 6 outputs produce identical issues — rule keys, messages, and categories are unchanged, preserving issue identity for existing users' ignores and triage records.

Updates

Plugin Previous New Published Age Notes
knip 5.88.1 6.17.1 2026-06-16 36d parser handles both v5 and v6 reporter shapes

Stability policy

6.17.1 is the newest release passing the 30-day stability gate; everything from 6.18.0 (29 days) through 6.27.0 is inside the window. No unwithdrawn security advisories affect knip (repo-level and npm ecosystem checked). latest_version records 6.27.0.

Test results

  • New parse_v6 unit test alongside the existing knip 5 parser test
  • knip 6.17.1 produces findings identical to 5.88.1 across all three fixtures (diffed snapshot contents)
  • Plain-mode jest runs both 5.88.1 and 6.17.1 snapshots: all passed
  • cargo test: 1087 passed, 0 failed

Follow-up

knip also ships a codeclimate reporter. qlty has no Code Climate output-format parser today; adding one and migrating knip to it would end the JSON-shape chasing but changes issue identity (rule keys/categories), so it's deliberately out of scope here.

Test plan

🤖 Generated with Claude Code

marschattha and others added 6 commits July 22, 2026 03:56
Node 21 is an odd-numbered, EOL release line whose engine ranges are
excluded by current eslint, knip, oxlint, and prisma releases. npm
silently skips optional dependencies (including native bindings) whose
engines exclude the running node, breaking installs.

Closes #2814

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
eslint 10 no longer bundles @eslint/js, so the basic_9 fixture config
now enables the three exercised rules directly instead of extending
js.configs.recommended. The config works unchanged across eslint 9
and 10, keeping all historical snapshots valid.

10.5.0 is the newest release passing the 30-day stability gate
(published 2026-06-12) with no known security advisories.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Uses the version-gated >=1.0.0 driver block added previously for the
oxlint 1.x --format github output change. The new snapshot records
proper rule keys (e.g. eslint(no-cond-assign)) where the 0.11.1
format offered none.

1.70.0 is the newest release passing the 30-day stability gate
(published 2026-06-15) with no known security advisories.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
prisma 7 no longer allows a url property in schema-file datasource
blocks, while prisma 5/6 require it, so a separate basic_7 fixture
covers 7.x and the original fixture keeps covering the 5.x snapshots.

7.8.0 is the newest release passing the 30-day stability gate
(published 2026-04-22) with no known security advisories.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
knip 6 restructured its JSON reporter: unused files moved from a
top-level files array into per-issue rows, and enumMembers /
classMembers changed from maps keyed by parent symbol to flat lists
with the parent on each identifier's namespace field. The parser now
accepts both shapes, so knip 5 and 6 outputs produce identical issues.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Verified that knip 6.17.1 produces findings identical to 5.88.1
across all three fixtures with the dual-format parser.

6.17.1 is the newest release passing the 30-day stability gate
(published 2026-06-16) with no known security advisories.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@qltysh

qltysh Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Qlty


Coverage Impact - ubuntu-latest

This PR will not change total coverage.

Modified Files with Diff Coverage (2)

RatingFile% DiffUncovered Line #s
Coverage rating: D Coverage rating: C
qlty-check/src/parser/knip.rs89.3%83-86, 221-229
Coverage rating: F Coverage rating: F
qlty-check/src/tool/tool_builder.rs100.0%
Total89.3%
🤖 Increase coverage with AI coding...
In the `ma/knip-6` branch, add test coverage for this new code:

- `qlty-check/src/parser/knip.rs` -- Lines 83-86 and 221-229

🚦 See full report on Qlty Cloud »

🛟 Help
  • Diff Coverage: Coverage for added or modified lines of code (excludes deleted files). Learn more.

  • Total Coverage: Coverage for the whole repository, calculated as the sum of all File Coverage. Learn more.

  • File Coverage: Covered Lines divided by Covered Lines plus Missed Lines. (Excludes non-executable lines including blank lines and comments.)

    • Indirect Changes: Changes to File Coverage for files that were not modified in this PR. Learn more.

@qltysh

qltysh Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Qlty


Coverage Impact - macos-15

This PR will not change total coverage.

Modified Files with Diff Coverage (2)

RatingFile% DiffUncovered Line #s
Coverage rating: D Coverage rating: C
qlty-check/src/parser/knip.rs89.3%83-86, 221-229
Coverage rating: F Coverage rating: F
qlty-check/src/tool/tool_builder.rs100.0%
Total89.3%
🤖 Increase coverage with AI coding...
In the `ma/knip-6` branch, add test coverage for this new code:

- `qlty-check/src/parser/knip.rs` -- Lines 83-86 and 221-229

🚦 See full report on Qlty Cloud »

🛟 Help
  • Diff Coverage: Coverage for added or modified lines of code (excludes deleted files). Learn more.

  • Total Coverage: Coverage for the whole repository, calculated as the sum of all File Coverage. Learn more.

  • File Coverage: Covered Lines divided by Covered Lines plus Missed Lines. (Excludes non-executable lines including blank lines and comments.)

    • Indirect Changes: Changes to File Coverage for files that were not modified in this PR. Learn more.

marschattha added a commit that referenced this pull request Jul 22, 2026
## Summary

Bumps the managed Node runtime from 21.7.3 (odd-numbered, EOL, non-LTS)
to 22.23.1 (latest 22 LTS "Jod"). Closes #2814.

Node 21 is a short-lived non-LTS line that the npm ecosystem
deliberately excludes: engine ranges target even LTS lines (`^20.19 ||
^22.12 || >=24`), and npm silently skips optional dependencies —
including native bindings — whose `engines` exclude the running node.
This blocked eslint 10, knip 6, oxlint 1.x, and prisma 7.

The download URLs in `qlty-check/src/tool/node.rs` are
version-templated, so this is a one-line change. The plugin promotions
this unblocks follow in stacked PRs: #2825 (eslint, oxc, prisma) and
#2826 (knip, which also needs a parser change).

## Test results

- Full node-linter jest matrix on node 22.23.1: 11/11 suites, 24 tests,
24 snapshots passed — every fixture at every historical snapshot version
(back to eslint 8.10.0, prettier 2.6.2) runs unmodified on the new
runtime
- `cargo test`: 1087 passed, 0 failed

## Test plan

- [ ] All linter tests pass in CI (including windows-2022)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@marschattha
marschattha force-pushed the ma/bump-node-runtime-22 branch from 1c1ebd3 to f631774 Compare July 27, 2026 22:00
marschattha added a commit that referenced this pull request Jul 28, 2026
## Summary

Promotes the three plugins unblocked by the node 22 runtime bump that
need no code changes. The runtime bump itself landed separately in #2827
(closing #2814), so this PR is now plugin promotions only.

npm silently skips optional dependencies (including native bindings)
whose `engines` exclude the running node, and some tools crash outright
— node 21 was excluded by current eslint, knip, oxlint, and prisma
engine ranges. The knip 6 bump requires a parser change and follows in a
separate stacked PR (#2826).

## Updates

| Plugin | Previous | New | Published | Age | Notes |
|--------|----------|-----|-----------|-----|-------|
| eslint | 9.39.4 | 10.5.0 | 2026-06-12 | 39d | fixture config no longer
imports `@eslint/js` (not bundled in eslint 10) |
| oxc | 0.11.1 | 1.70.0 | 2026-06-15 | 36d | uses the pre-existing
version-gated `>=1.0.0` driver block |
| prisma | 6.15.0 | 7.8.0 | 2026-04-22 | 90d | new `basic_7` fixture —
prisma 7 forbids datasource `url`, prisma 5/6 require it |

## Stability policy

All promoted versions have been published for at least 30 days and
checked against GitHub Security Advisories (repo-level and the npm
ecosystem database). No unwithdrawn advisories affect any promoted
version. Newer releases (eslint 10.7.0, oxlint 1.74.0, prisma 7.9.0)
were rejected by the 30-day gate; `latest_version` fields record them.

## Test results

- Plain-mode jest for touched linters (every fixture at every snapshot
version, old and new): all passed
- eslint's rewritten `basic_9` fixture config verified against the
existing 9.6.0 / 9.7.0 / 9.39.4 snapshots
- oxc's new snapshot records proper rule keys (`eslint(no-cond-assign)`)
where 0.11.1's output format offered none
- `cargo test`: 1087 passed, 0 failed

## Test plan

- [ ] All linter tests pass in CI (including windows-2022)
- [ ] No security advisories affect updated versions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Base automatically changed from ma/bump-node-runtime-22 to main July 28, 2026 20:15
marschattha added a commit that referenced this pull request Aug 28, 2026
## Summary

Update linter plugin versions with stability and security vetting.
Covers the
gaps the weekly `updateLinterVersions.ts` script leaves behind.

After this PR, **knip is the only linter with `latest_version` ahead of
`known_good_version`** (blocked on #2826), and every script-unreachable
linter
is either current or has a filed reason below.

## Updates

| Linter | Previous | New | Published | Age | Notes |
|--------|----------|-----|-----------|-----|-------|
| redocly | 2.32.2 | **2.41.0** | 2026-07-27 | 30d | ⚠️ security — see
below |
| shfmt | 3.8.0 | **3.13.1** | 2026-04-06 | 142d | ⚠️ also fixes a
silent-pass bug — see below |
| checkstyle | 13.5.0 | 13.9.0 | 2026-07-27 | 30d | |
| pmd | 7.25.0 | 7.26.0 | 2026-06-29 | 58d | |
| trufflehog | 3.95.5 | 3.96.0 | 2026-07-24 | 33d | security-sensitive
linter |
| swiftformat | 0.61.1 | 0.62.1 | 2026-07-07 | 50d | custom-download |
| swiftlint | 0.63.3 | 0.65.0 | 2026-06-27 | 60d | custom-download |
| terraform | 1.15.6 | 1.15.8 | 2026-07-08 | 49d | custom-download |
| eslint | 10.5.0 | 10.8.0 | 2026-07-24 | 33d | within the major from
#2825 |
| oxc (oxlint) | 1.70.0 | 1.76.0 | 2026-07-27 | 30d | within the major
from #2825 |
| prisma | 7.8.0 | 7.9.1 | 2026-07-27 | 30d | within the major from
#2825 |

## Security

**redocly 2.32.2 was vulnerable.** The pinned version falls inside the
range of
two advisories published 2026-07-27:

- **CVE-2026-63325** (high) — arbitrary code execution via Arazzo
`$faker`
  expression using `respect`; patched in 2.33.0.
- **CVE-2026-63225** (medium) — path traversal in the `split` command;
patched
  in 2.33.2.

2.41.0 clears both.

All other candidates were checked against
`/repos/{owner}/{repo}/security-advisories`
and the GitHub Advisory Database, filtering withdrawn advisories. The
only hits
were against versions far older than both current and target (checkstyle
< 8.29,
pmd <= 7.21.0, trufflehog < 3.81.9, terraform < 0.12.17, prisma <
2.20.0).

**trufflehog** is a security-sensitive linter (secret detection,
elevated
trust). Beyond the advisory check, its 3.96.0 release assets were
verified for
expected content types and its release notes reviewed — nothing
suspicious.

## shfmt was reporting unparseable files as clean

The version bump surfaced a live correctness bug, fixed here.

The format driver declared `success_codes = [0, 1]`, but with `-w` shfmt
exits
0 whether or not it rewrites the file — **exit 1 means only a parse or
I/O
error**. Accepting 1 as success meant any shell file shfmt could not
parse
produced no rewrite, no error, and zero issues. Verified across both
versions:

| input | shfmt 3.8.0 | shfmt 3.13.1 |
|-------|-------------|--------------|
| unterminated quote | 1 | 1 |
| unfinished `case` | 1 | 1 |
| unclosed `{` | 1 | 1 |
| `if true; then` / `fi` (empty body) | 0 | 1 |
| valid but misformatted | 0 | 0 |

The exit-code contract is identical across versions, so this is **not**
version-gated — 3.8.0 silently passes an unterminated quote today, and
gating
would preserve that for anyone pinning an old shfmt. shfmt 3.9 only made
the
parser stricter (an `if` with an empty body is now correctly rejected),
which is
what exposed the bug: the old fixture was invalid shell, so 3.13.1
refused it,
the rewrite never ran, and the test went green with zero findings.

The fixture now has a body, so old and new shfmt both parse it and both
still
find it misformatted. Its snapshot is unchanged — `output = "rewrite"`
records
only a generic formatting issue, not file contents. An invalid fixture
cannot be
kept as coverage once exit 1 is honored, since the harness asserts
`success: true` per fixture.

**Behavior change:** shell files that cannot be parsed now surface a
plugin
error instead of silently reporting clean.

Worth a follow-up audit: biome, ktlint, markdownlint, rubocop,
ruby-stree, and
standardrb also declare `[0, 1]` on rewrite-mode drivers. For several of
them
exit 1 legitimately means "issues found" and the config is correct — but
each
should be checked against its tool's actual contract.

## Coverage of script-unreachable linters

The script can only update a linter that declares `releases` or a
`runtime` in
{java, php, ruby, python, node}; anything else hits its `Unknown
runtime` throw.
Because it also cannot write their `latest_version`, these linters never
appear
in the usual "latest != known_good" gap scan either. Full enumeration:

| Linter | Why unreachable | Disposition |
|--------|-----------------|-------------|
| kube-linter | no releases, no runtime | already latest (0.8.3) |
| stringslint | no releases, no runtime | already latest (0.1.10) |
| swiftformat, swiftlint, terraform | no releases, no runtime |
**updated here** |
| shfmt | runtime `go` | **updated here** |
| clippy, rustfmt | runtime `rust` | see below |
| gofmt | runtime `go` | see below |
| hadolint | skip-list | too new (2.15.1 is 26d) |
| tsc | skip-list | no version fields (`hidden = true`) |

**clippy, rustfmt, and gofmt are runtime-coupled, not ordinary
plugins.**
`gofmt` 1.22.0 *is* the managed Go pin (`tool_builder.rs:148`), and
clippy/rustfmt track the Rust toolchain (pinned 1.77.2). Upstream is far
ahead
— Rust 1.98.0, Go 1.27.0 — but bumping them means moving the managed
runtimes,
the same class of work as the node 21→22 bump in #2814/#2825, with much
wider
blast radius. Deliberately out of scope here; worth its own issue.

## Stability policy

Every version here has been published for at least 30 days, no
overrides.
Newer releases rejected by the gate, to pick up next run: redocly
2.48.0,
checkstyle 14.0.0, eslint 10.9.1, oxlint 1.80.0, prisma 7.10.0,
swiftlint 0.65.1, terraform 1.15.9, hadolint 2.15.1 (26d).

Node linters were pre-checked with `npm view {pkg}@{ver} engines`
against the
managed runtime (22.23.1) — all satisfied.

## Also noticed

- The stale comments in `knip/plugin.toml` and `oxc/plugin.toml` still
blame
node 21.7.3. knip's real blocker is #2826; oxc's is simply obsolete.
Left
  untouched here.
- **#2813 (biome parser) looks closeable** — #2821 landed an rdjson
reporter and
  biome is already at 2.5.6 on main.

## Test results

All 11 updated linters pass in **plain mode**, which runs every fixture
at every
historical snapshot version — what CI effectively exercises. Each target
was
additionally run in compare-latest-snapshot mode. Apart from shfmt,
output was
byte-identical everywhere, so no snapshot changes were needed. Snapshots
were
inspected for non-zero findings to rule out vacuous passes (this is what
caught
shfmt).

Two fixtures fail *in compare mode only* — eslint `basic_8` (eslintrc,
ESLint 8
only) and prisma `basic` (prisma 5 schema). Compare mode forces the
target
version onto every fixture regardless of what it supports. Both were
confirmed
to fail identically at the **current** promoted versions (10.5.0 /
7.8.0), so
this is pre-existing harness behaviour, not a regression.

Custom-download URL templates were HEAD-checked at the target version
across
every platform variant (all 200) — no asset-name drift this round.

## Test plan

- [ ] All linter tests pass in CI
- [x] Download URLs verified for custom-download linters (all platforms,
200)
- [x] No security advisories affect updated versions
- [x] Node engines checked against the managed runtime
- [x] Every script-unreachable linter enumerated and dispositioned
- [ ] Watch the windows-2022 job specifically — local runs only cover
macOS

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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