Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions .claude/skills/vet-redis-client/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
---
name: vet-redis-client
description: Score a community Redis client library against the docs team's quality checklist. Use when reviewing a PR or suggestion to add a client library to the redis.io clients page, or when asked to vet/evaluate a Redis client library.
---

# Vet a community Redis client library

Score a community-supported Redis client library against the docs team's 13-criterion checklist and produce an evidence-backed verdict (Recommend include / Needs discussion / Decline). Every score must cite a URL plus the specific fact observed (e.g. "last release v2.3.1, 2026-05-02"). Reviews, blog posts, project-published benchmarks, and social-media popularity are deliberately excluded as signals — only verifiable repo/registry facts count.

## 1. Identify the repository

The input is the client's GitHub repository, given as a URL or `owner/name`. If you are only given a package name, look up the package on its language's registry and follow the registry page's repository/homepage link to find the GitHub repo. If you cannot locate a source repository at all, stop and report that — an unreviewable source is disqualifying on its own.

## 2. Gather evidence

Use `gh` or the GitHub API when available; otherwise fetch the public HTML pages. Check all of the following before scoring:

- **GitHub repo front page** — last-commit date, stars, forks, "Used by" sidebar, license, README, archived banner.
- **`/releases` and `/tags`** — release recency and cadence over the last 2 years.
- **`/commits/<default-branch>`** — commit dates and distinct author names in the last 12 months (the contributors graph is JS-rendered; the commits list works).
- **Issues sorted by recently updated** (`/issues?q=is%3Aissue+sort%3Aupdated-desc`) and **`/pulls`** — maintainer responsiveness and triage.
- **CI status on the default branch** — README badges and the `/actions` tab.
- **`/security/advisories`** — open security advisories.
- **The language's package registry page** — npm / PyPI (use pepy.tech for downloads) / crates.io / pkg.go.dev / rubygems / packagist / nuget / pub.dev / hex.pm / Maven Central (JVM: Scala, Java, Kotlin) — for download counts, version/publish history, changelog, dependents. Not every registry publishes all of these: **Maven Central and pkg.go.dev publish no download figures at all**, so a missing count there is a property of the registry, never a finding about the project — see the B1 note below before scoring it.
- **The project's docs site and README** — quickstart, feature list, Redis version compatibility statement.

### Verification traps

**A single index returning zero is not evidence of absence.** Before scoring any criterion Fail on a missing-from-the-registry or missing-from-the-graph reading, confirm it against the authoritative store, or state that you could not. Three instances that have each been one step away from producing a wrong score on a real submission:

- **Maven Central's search index lags.** `search.maven.org/solrsearch` returned 0 hits for `com.github.ghostdogpr:sage-core_3` while all seven published versions were sitting in the artifact store. The authoritative check is the store itself: `https://repo1.maven.org/maven2/<group/path>/<artifact>/maven-metadata.xml`, which lists every version and a `lastUpdated` stamp.
- **GitHub's dependents graph does not parse every ecosystem's manifest** — notably `build.sbt`, so it reports "0 Repositories / 0 Packages" for *any* sbt project regardless of real usage. Treat a zero as no-signal, not as a Fail on its own, and say which instrument was blind.
- **`mvnrepository.com` (which does show a "Used By" count) returns HTTP 403 to scripted requests.** It is browser-only; do not cite it as a source that a re-run can reproduce.

## 3. Score against the checklist

Score each criterion Pass (2) / Warn (1) / Fail (0). Every criterion's evidence must cite a URL and a concrete fact.

**Each criterion's three bands must be mutually exclusive and exhaustive** — numeric ones as half-open intervals, qualitative ones as cases. If a plausible project matches two bands, or none, the row is broken rather than the reviewer's judgement: fix the row. This applies to the qualitative criteria exactly as much as the numeric ones; every band gap found in this checklist so far has been in a row whose wording read perfectly well as English.

**Where a band offers alternative routes ("X **or** Y"), every lower band must explicitly exclude those routes** — otherwise the tie-break below silently kills the alternative route, and a documented path to Pass becomes unreachable whenever a lower band's condition also happens to hold.

**If more than one band still applies, score the lowest one** and say in the evidence cell that the row needs repair. A reviewer mid-review cannot rewrite the rubric, and two scorers resolving the same overlap by reading order is the failure this whole section exists to prevent. Prefer restructuring a row around the single axis it measures over appending another "or" clause to a band — an appended clause closes one case and opens an edge against every neighbouring band.

**A Fail requires positive evidence of the deficiency, from an instrument known to work for that ecosystem.** Where the figure does not exist, or the only instrument available is blind to the project's ecosystem, score **Warn** and name the gap — not Fail, and not Pass. This rule takes precedence over every band in the tables below: if a band's wording would produce a Fail on nothing more than a missing or unmeasurable reading, the score is Warn and the evidence cell says which instrument was blind. Absence of a claim is not evidence of absence of the thing claimed.

### A. Maintenance

| # | Criterion | Pass (2) | Warn (1) | Fail (0) | Where to look |
|---|-----------|----------|----------|----------|---------------|
| A1 | Recent commit activity | Last commit on default branch ≤ 6 months ago | > 6 and ≤ 18 months | > 18 months, or repo archived | GitHub repo front page / `…/commits/<default-branch>` |
| A2 | Release recency & cadence | ≥ 1 release in last 12 months **and** visible cadence over the last 2 years | Release in last 12 months but thin/irregular history | No release in 12 months | `…/releases`, `…/tags`, registry version list — a release counts whether it is evidenced by a git tag or by a registry publish; score only recency and cadence here, and leave missing tags to C2 |
| A3 | Bus factor | ≥ 2 distinct committers active in the last year | Exactly 1 active committer | 0 active committers in the last year | `…/commits` author names (contributors graph is JS-rendered; the commits list works) |
| A4 | Responsiveness | Every inbound channel is served: recent issues get maintainer replies **and** open PRs are triaged | Inbound is served but slowly or inconsistently, **or** there is too little inbound to judge (give the counts) | Any inbound channel is unserved: issues disabled, or open issues/PRs sitting untouched with no maintainer response | `…/issues?q=is%3Aissue+sort%3Aupdated-desc`, `…/pulls` |

### B. Adoption

| # | Criterion | Pass (2) | Warn (1) | Fail (0) | Where to look |
|---|-----------|----------|----------|----------|---------------|
| B1 | Registry downloads | ≥ the ecosystem's Pass threshold, per the table below | ≥ the Fail floor and < the Pass threshold | < the Fail floor | npmjs.com / pypi.org (pepy.tech) / crates.io / pub.dev / packagist.org / nuget.org / hex.pm package page |
| B2 | Downstream usage | ≥ 100 dependents, **or** ≥ 1 named independent downstream user of real substance | No named user of substance **and** either 1–99 dependents or no dependents figure obtainable for the ecosystem (state which instrument was blind) | 0 dependents from an instrument that does work for this ecosystem, and none named | GitHub repo sidebar "Used by", `…/network/dependents`, registry "dependents" tab; for Go, pkg.go.dev's "Imported by" count is a working substitute |

**B1 thresholds are ecosystem-relative.** State the number you found **and** the threshold triple you applied. Monthly downloads:

| Ecosystem | Pass | Fail floor |
|---|---|---|
| npm, PyPI | ≥ 100k | < 10k |
| crates.io, packagist, nuget | ≥ 20k | < 2k |
| pub.dev, hex.pm | ≥ 5k | < 500 |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

B1 threshold table breaks formulas

Medium Severity

B1 bands are defined as comparisons against a Pass threshold and Fail floor, but the threshold table cells already embed those operators (≥ 100k, &lt; 10k). Applying the band text literally yields nonsensical comparisons, so agents can mis-score boundary download counts.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2a8dbde. Configure here.


For a registry not listed, pick a triple in proportion to that ecosystem's size and **state it** — the Pass threshold and a Fail floor an order of magnitude below it, matching the pattern above. Naming all three bands is the requirement; the specific numbers are a starting point a reviewer may override with a stated alternative. Two registries are absent from the table because they publish no figures at all rather than because a triple is missing — do not invent one for them, see the note below.

**Ecosystems that publish no download figures at all:** Maven Central (JVM — Scala, Java, Kotlin) and pkg.go.dev (Go). B1 is unscoreable there, so cap it at Warn and state that the number does not exist rather than implying the project has poor uptake — the same limitation hits every client in that language. Note the asymmetry: Go loses B1 but keeps B2 via "Imported by", whereas a JVM project's dependents are only visible through named adopters (`mvnrepository.com`'s "Used By" is browser-only, per the traps above).

### C. Engineering quality

| # | Criterion | Pass (2) | Warn (1) | Fail (0) | Where to look |
|---|-----------|----------|----------|----------|---------------|
| C1 | CI + tests | A real test suite, run by CI, green on the default branch | A real test suite exists but CI does not run it green — absent, red, or lint/build only | No meaningful test suite, whatever CI does | README badges, `…/actions`, `test/` or `spec/` dirs |
| C2 | Release hygiene | On the official registry, versions ordered under a consistently applied scheme (semver, CalVer or similar), changelog/release notes maintained, **and** published versions traceable to a source state (git tags or equivalent) | On the registry with consistent versioning but missing either or both of the other two — no changelog, or no tags to trace versions to | Not on the official registry, or versioning inconsistent enough that release order is unclear | Registry page + changelog tab, `…/tags` vs registry versions (on the JVM, diff the tags against `repo1.maven.org/maven2/…/maven-metadata.xml`, not the search index). Publisher verification (npm provenance, PyPI trusted publishing, signed artifacts) is worth recording as a note where the registry shows it, but is **not** scored — most ecosystems do not expose it, so scoring it would penalise the language rather than the project |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C2 gap for stale changelog

Medium Severity

C2 Pass requires a maintained changelog, but Warn only covers a missing changelog (or missing tags). A registry project with consistent versioning, tags, and a present but abandoned changelog matches none of the three bands, so scorers have to improvise.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2a8dbde. Configure here.

| C3 | License & security | OSI-approved license, **and** the advisories endpoint answers with none open | OSI-approved license, but advisory status cannot be established (endpoint unavailable, advisories not enabled) | Non-OSI or no license, or an open unpatched advisory | Repo sidebar License, `…/security/advisories` |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C3 open versus unpatched gap

Medium Severity

C3 Pass requires the advisories endpoint to show none open, while Fail and the hard stop require an open unpatched advisory. A listed or open advisory that already has a patch matches neither Pass, Warn, nor Fail, so security scoring becomes ambiguous on ordinary post-fix GHSA history.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2a8dbde. Configure here.


### D. Redis-specific fit

| # | Criterion | Pass (2) | Warn (1) | Fail (0) | Where to look |
|---|-----------|----------|----------|----------|---------------|
| D1 | Modern feature coverage (RESP3, cluster, TLS, ACL/AUTH, pub/sub, pipelining) | ≥ 4 of these 6 covered | 2–3 covered | ≤ 1 covered | README feature list, changelog, API reference (search for RESP3/cluster/TLS/subscribe/pipeline) |
| D2 | Stated Redis version compatibility | Docs explicitly state compatibility with a current major — Redis 7.x or later | Stated but only for an older range (≤ 6.x), **or** stated without version detail, **or** unstated but implied by recent feature work | No statement, and nothing implying one | README, docs site, CI matrix |

### E. Documentation

| # | Criterion | Pass (2) | Warn (1) | Fail (0) | Where to look |
|---|-----------|----------|----------|----------|---------------|
| E1 | Quickstart | Install → connect → set/get a fresh user can follow, in the README or one hop from it | Partial (examples exist but no clean end-to-end path, or non-English gaps) | No usable quickstart anywhere | Repo README and the page it links to for getting started (a quickstart on the docs site counts; note where it lives, since a README with no inline snippet is worth saying out loud) |
| E2 | Docs beyond README | Hand-written guides or a hand-written docs site beyond the README, currently maintained | A generated API reference only, however current it is (docs.rs, dartdoc, javadoc, readthedocs autodoc), **or** hand-written docs that are no longer maintained (say how you can tell) | Nothing beyond README | Docs link in README/registry sidebar (readthedocs, pub.dev dartdoc, docs.rs, etc.) — the quickstart already scored in E1 does not by itself satisfy this; E2 asks what exists *beyond* it |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E2 quickstart-only docs gap

Medium Severity

The E2 note says an E1 quickstart alone does not satisfy this row, but Pass still matches a hand-written docs site beyond the README, and Fail is only “nothing beyond README.” A docs site whose only page is that quickstart therefore matches Pass by the table, is excluded by the note, and matches neither Warn nor Fail.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2a8dbde. Configure here.


## 4. Total and verdict

13 criteria × 2 points = **26 max**.

| Total | Verdict |
|-------|---------|
| ≥ 21 | **Recommend include** |
| 14–20 | **Needs discussion** (list the specific Warn/Fail items to resolve) |
| < 14 | **Decline** (cite the failing criteria in the PR response) |

**Hard stops regardless of total:** archived repo, no OSI license, or open unpatched security advisory → **Decline**. Check these first; if one applies, still complete the scorecard for the record, but the verdict is Decline no matter the total.

## 5. Output format

Produce, in this order:

1. A header naming the library, its repo URL, its registry URL, and the review date.
2. A scorecard table with columns: criterion | score | evidence. Every evidence cell must contain at least one URL and the concrete fact observed (dates, counts, version numbers). If evidence for a criterion could not be found, score Warn at best and say what is missing.
3. The total (n / 26) and the verdict per the thresholds above, noting explicitly whether any hard stop applies.
4. A 2–3 sentence summary of the standout strengths and weaknesses.

Keep observations that are not criteria out of the scorecard and out of anything published — note them separately for the reviewer. They carry no score, and in a public reply they read as a judgement on the author rather than on the library.

## 6. Record the verdict

Do not save the scorecard as a file in this repo: readers expect a checked-in file to be current, and half of a scorecard's facts (open-issue counts, "last commit 5 days ago", the total itself) are false within months. A dated snapshot belongs somewhere that is understood to be a snapshot.

**On an accept**, put it in the squash commit message of the PR that adds the table row. That commit touches exactly the one line being justified, so `git blame` on the row leads a future reviewer straight to the evidence. Write it as a snapshot: name the review date and the version observed, keep the four-or-so non-Pass criteria in full with their evidence, and compress the passes to a single line — a Pass is cheap to re-derive, whereas *why* a criterion was scored down (and what instrument was blind when it was) is not.

Anchor it with trailers so the set stays enumerable across reviews (`git log --grep='^Vetted:'`), using the vocabulary in `.claude/skills/_shared/commit-trailers.md`:

```
Vetted: <library> <n>/26 <verdict> <YYYY-MM-DD>
Gaps: <what could not be measured, and which instrument was blind>
Recheck: <expiry condition — an age, or a signal such as the sole maintainer going quiet>
```

This repo squashes with `squash_merge_commit_message = COMMIT_MESSAGES`, so the default body is the contributor's WIP commits concatenated and the scorecard is silently discarded. Override it explicitly:

```
gh pr merge <n> --squash --subject "<subject>" --body-file <scorecard-file>
```

**On a decline**, the PR reply comment is the record — a closed PR is still discoverable by searching the repo URL. There is no commit to carry it, so the accept and decline paths are deliberately asymmetric; a queryable data file only earns its place if the volume of reviews ever makes that gap hurt.
Loading