feat(gate): buckets, traits, and the checks the brief calls load-bearing

Measured this build against a documentation brief for public repos. The
five original checks covered roughly one of its ten sections, so this
adds what a single repo can answer on its own.

New: required README headings per class (Non-goals is the cheapest
trust-builder there is), in-repo version consistency across manifest /
badge / CHANGELOG / tag, badge honesty, boilerplate, licence-claim,
and relative links. Findings now carry a BUCKET beside the level -
broken / missing / weakening - and output is grouped by it, because
that is the order the work gets done in.

Traits are a second axis beside class: class is structural and readable
off the catalog, a trait says what the code does. `security` attaches
SECURITY.md and a Known limitations section. The two names carrying it
are proposed, not measured - that list is the operator's.

Solo-maintained settles a category: CONTRIBUTING, CODE_OF_CONDUCT and
MAINTAINERS are required by no class. Consumer-facing documents are
untouched by that; SECURITY.md exists for the stranger who finds a hole.

Three bugs found by running against llm-security, not by reading:
- ~30 link findings, all noise. Regexes inside code spans are
  `[...](...)` to a naive scanner. Strip code first.
- `file:` and other schemes were treated as repo-relative paths.
- Relative links were resolved against the repo root instead of the
  file they sit in, calling two files missing that sat next to the
  README linking them.
Same fix applied to the boilerplate check: a document ABOUT placeholder
detection was tripping the placeholder detector.

Also removed this repo's own static tests badge. There is no CI - the
forge has zero Actions runners registered - so it could never become
real, and it is the exact anti-pattern the gate now flags.

67 tests. Against llm-security every remaining finding is real and
matches the census's independent hand-measurement.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WYJ3FHLtVgzFXMZ6UF598h
This commit is contained in:
Kjell Tore Guttormsen 2026-07-27 16:06:33 +02:00
commit 720850a9ad
7 changed files with 915 additions and 39 deletions

View file

@ -31,7 +31,29 @@ network call, `--json` for machine output, `--refresh` to compare the bundled
register against the live org listing.
**Never report a `SKIP` as a pass.** A SKIP means the gate could not see enough
to judge — an unreachable forge, a repo missing from the register. Say which.
to judge — an unreachable forge, an untagged repo, a link leaving the
repository. Say which.
**Report in buckets, not in a flat list.** Every finding also carries `broken`
(a stranger is blocked or misled), `missing` (an expected artefact is absent) or
`weakening` (present, but reads as amateur). That is the order the work actually
gets done in, and it is more useful to a reader than severity alone. The two
axes are independent: a README opening line that contradicts the published
description blocks nobody and is still an ERROR.
## Before you trust a run, look at the noise
The first version of the link check produced about thirty findings against a
real repository and every single one was noise: regexes inside code spans that
are `[...](...)` to a naive scanner, `file:` URLs, and relative links compared
against the repo root instead of the directory they sit in. The check now
strips code and resolves properly.
The lesson generalises, so apply it to your own reading: **when a check fires
many times in one repo, suspect the check before the repo.** A gate that is
wrong this often teaches people to ignore it, which is worse than not having it
at all. If you find a new noise source, fix the classifier and add the case as a
fixture — do not tell the user to ignore some of the output.
## What it measures, and what it cannot
@ -60,9 +82,46 @@ judgement call. `register/repos.json` holds it.
| standalone | pip/uv |
| org-profile | no install section |
A flat standard across all classes would demand a CONTRIBUTING from a CSS
library that takes no contributions and a ROADMAP from a five-line profile.
That is how a gate teaches people to switch it off.
A flat standard across all classes would demand a ROADMAP from a five-line
profile. That is how a gate teaches people to switch it off.
**Traits are a second axis.** Class is structural — readable off the catalog and
the remotes. A trait is about what the code *does*, which no remote knows.
`security` attaches what a tool owes once it handles untrusted input: a
`SECURITY.md` with a channel someone actually reads, and limitations stated with
their mechanism. Which repos carry a trait is the maintainer's judgement; do not
add or remove one on your own initiative, propose it.
## Who the reader is decides what is required
This project is solo-maintained, and that settles a whole category. Documents
written for *contributors*`CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`,
`MAINTAINERS.md`, PR templates, CLA/DCO — are required by no class here, because
there are no contributors and the published stance says so plainly. A code of
conduct with an unattended placeholder address is worse than none: it is a
visible unfinished template.
Documents written for *consumers and outsiders* are completely unaffected by
that, and working alone is never a reason to skip them: `SECURITY.md` exists for
the stranger who finds a hole, `LICENSE` for the one deciding whether they may
use it, non-goals and limitations for the one deciding whether it fits.
Do not turn this into a rule against having those files. A `CODE_OF_CONDUCT.md`
already sitting in a repo is a cleanup decision for the operator, not a gate
finding.
## Badges, and the one that is a lie
Only badges that measure something. A static image reading `tests: 642 passing`
is a claim dressed as evidence, and the gate flags it — an early draft of this
plugin's own README carried exactly that, which is how it got caught. Version,
licence and platform badges assert no run and are fine as static images.
There is no CI in this organisation: the forge has no Actions runner registered.
So do not add a CI badge, and do not pretend. The honest substitute is a single
command that runs the whole suite from a clean clone, stated in the README along
with the fact that nothing runs it automatically. Verifiability is the goal; CI
is one mechanism for it, and it is not available here.
## Two things about the install block worth knowing before you edit one