What's new in Cejel
Cejel keeps two version tracks. This is the CLI track —
the binary, npm package, GitHub Action, Docker image, and MCP server. Changes to the scoring
rubric are tracked separately, with a full before/after corpus delta, in the
rubric changelog.
Install or update any time with
npx @cejel/cejel@latest .
v0.6.1
2026-10-06Changed: release SBOMs list what each binary and the image bundle, and are attested (#404). Each standalone binary’s SPDX SBOM is now generated from the build: it lists the packages bundled into the binary with their exact versions, the Node.js runtime it embeds, and the binary’s SHA-256. The 0.6.0 SBOMs listed only the scanned directory and the binary. Each SBOM is attested to its binary and can be checked with gh attestation verify <binary> -R BargLabs/cejel --predicate-type https://spdx.dev/Document/v2.3. The Docker image keeps its SBOM of base-image packages and gains a second SPDX attestation listing the packages Cejel bundles. A release whose SBOM is empty, or is missing a required package, now fails before anything is published or attested. The npm package does not carry an SBOM. No score, band or report field changes.
Changed: the certificate reads correctly the first time (#415). Five changes to wording and layout on the terminal, HTML and Markdown certificates, from a first read of the 0.6.0 certificate for expressjs/express. A static coverage percentage of 0 is no longer shown as 0/100 percent, which read as “0% coverage”; the certificate now says that Cejel does not run tests and how the rubric scores the absence. The summary box and “What was established” count dimensions the same way and say how many do not apply. “What to do next” no longer tells you to make files readable when Cejel skipped them only for their file type. A Conditional, At risk or Unverified verdict with no critical or warning finding now says it comes from the overall score’s band. The note about the product name is in plain English. No score, band, verdict or abstention moves. report.json, summary.json, badge.json and badge.svg are byte-identical, so attestation.json binds the same report digest; reportFormatVersion stays 1.4.
Inspect the v0.6.1 release → · Read the OCI and MCP release-chain proof →
v0.6.0
2026-10-03Added: cejel export gitlab-codequality <report.json> [-o gl-code-quality-report.json] (#401). Writes a Cejel report’s located findings as a GitLab Code Quality report, offline. Only findings with both a file and a measured line are exported. Findings scoped to a whole file (line: null) and ingested third-party findings are not exported; a footer on stderr counts them, so nothing is silently dropped. Severity maps critical to critical, warning to major and info to info; minor and blocker are never emitted. No score, band or report field changes.
Added: a GitLab CI template, ci/gitlab/cejel.gitlab-ci.yml (#401), documented in docs/gitlab-ci.md. It is an includable job that runs the scan, publishes the Code Quality report as artifacts:reports:codequality, keeps .cejel/ as an artifact, and fails the job below an optional CEJEL_MIN_SCORE. Include it from this release’s tag. The template is covered by structural tests in this repository; it has not yet been run on a GitLab instance by us.
Changed: the GitHub Action’s description is shorter (#402), to fit GitHub Marketplace’s limit of 125 characters. Inputs, outputs and behaviour are unchanged.
Inspect the v0.6.0 release → · Read the OCI and MCP release-chain proof →
v0.5.0
2026-10-02--min-score now exits non-zero when fewer than half of the applicable dimensions were measured, even when the score clears the minimum. Before this, a score resting on a handful of measured dimensions passed the gate as readily as a fully measured one. Now the gate refuses if the measured share of applicable (not not_applicable) dimensions is below one half, overall or in any category. Who is affected: CI jobs that run --min-score on low-coverage repositories, such as a small or newly created codebase where most dimensions return insufficient_data. Those jobs used to pass and will now fail. How to see why: stderr reads Cejel: cannot evaluate the required minimum N/4.0 because fewer than half of the applicable dimensions were measured in at least one score (…), with the measured/applicable counts in the parentheses. The scores in the report do not change; only the exit code does.
The HTTP MCP server is stateless: certificates and badges come back with the scan that produced them (#369, merged as #388). The cejel://last-scan/certificate.html and cejel://last-scan/badge.svg resources are removed. They never worked over HTTP: each request builds a fresh server, so a resource read could not see the scan from an earlier request and always answered "No scan has run yet". The scan tool now takes an optional artifacts list (certificate, badge) and returns the requested HTML certificate and SVG badge in the same response. Who is affected: HTTP MCP clients that read those two resources after calling scan; pass artifacts to the scan call instead. The stdio MCP server and the CLI are unchanged.
Report format 1.4: each metric records the weight share it actually contributed (#370, merged as #389). Metrics gain appliedWeightShare, and the HTML certificate reads it from the report instead of recomputing it at render time, so the weights a certificate shows are the ones the signed report carries. reportFormatVersion moves from 1.3 to 1.4. No score, band or behaviour fingerprint changes; a test pins that the field moves neither. Who is affected: consumers that validate reports against a closed 1.3 schema.
The HTTP MCP summary response no longer rejects valid scan results (#369, merged as #388). Its strict schema did not allow the per-finding dimensionBand and displaySummary fields or the content-read summary that scan results carry, so some valid scans failed schema validation on the way out.
Inspect the v0.5.0 release → · Read the OCI and MCP release-chain proof →
v0.4.11
2026-09-25A2 under the default rubric missed a populated PostgreSQL password in a committed .env file's DATABASE_URL (#336). A DATABASE_URL=postgres(ql)://user:password@… in a non-template .env/.env.* file is now a committed-secret finding, in both the current-tree and the history scan, and an unchanged value is not reported twice. A placeholder-shaped password is not flagged, and .env.example-style templates are not read. This is a change to A2 under the calibrated default. It moved no row of the published corpus, which contains no non-template .env file. See the 0.4.11 entry in leaderboard/RUBRIC_CHANGELOG.md.
A3 prod_readiness_primitives under the default rubric missed a real, registered Express global error handler. An independent evaluator ran 0.4.10 against five pinned revisions of his production system; every revision reported no error boundary despite a real handler being present and wired in. We do not have his code, so this widens on a fixture catalogue of public Express idioms instead of his specific file (src/witan/__tests__/a3-error-boundary-idioms.test.ts has the full table). Now credited: a handler with its first parameter renamed _err or its fourth renamed _next; a class method registered via .bind(this) from an export class (TypeScript); and a handler file under a conventional server/ root using a .mjs/.cjs extension. Also fixed a related false positive found while building the catalogue: a commented-out // app.use(errorHandler); line was wrongly making an otherwise-dead, never-registered stub count as reachable, because the reachability check read raw file text without stripping comments. Stated limits, not silently widened: a three-parameter handler with next omitted entirely stays uncredited (Express recognizes error middleware by arity, and matching on names alone there would be the arity-only false positive this detector's shape pattern exists to avoid). This is a recall change on prod_readiness_primitives under the default rubric; it carries a paired before/after delta record at unchanged corpus commits in docs/experiments/v17-behaviour-delta-post-0.4.10-2026-09-23/. No fixture in the existing v17/v22 byte-stability or A3 suites changed output — this widening only reaches repositories that hit the newly-credited idioms.
The (prospective, witan-rubric-v23) health_readiness_route info-severity finding missed a real Express health route under several ordinary, public-documentation idioms , cataloged and regression-guarded in src/witan/__tests__/a3-health-route-idioms.test.ts. This finding is info-severity, not a scored metric — no A3 score moves; a certificate's "what to do next" does. Now also credits:
- a segment-internal
_/-prefix directly after the route's leading slash (/_health); - the Rails convention
/up, slash-anchored only — never as a bare decorator string, which would also match unrelated direction/toggle literals ('up' | 'down'); - the NestJS decorator idiom with no leading slash (
@Get('health')), requiring@Get(...)context for barehealth; context-free bare keywords are limited tohealthz,readiness, andliveness, so ordinary'ready','live', and'health'strings do not match; - a
functions/serverless-functions directory (Firebase/Netlify convention), added to this one signal's file-selection predicate rather than to the shared implementation-file allowlist other A3 signals also use.
Deliberately left as stated limits, not silently dropped: /ping, /status, and /alive (too generic, or not a named convention the way /up and /healthz are); a trailing query string (/health?probe=1 — crediting it would also credit an outbound probe/test URL, and a false assertion is worse than a miss here); and a health-check library imported with no literal path anywhere in the repo (express-healthcheck, @godaddy/terminus, lightship) — whether the import itself should count as evidence is an open decision, not made in this change.
Two of the three file-selection examples this gap was originally reported against (src/server/health.controller.ts, app/api/health/route.ts) turned out to already be admitted by the existing predicate once checked against the running detector; only the functions/ case was a genuine gap.
A certificate that withheld content could print no disclosure line at all, and a reader could not tell a correct no-intersection ("no signal would have read it") from a disclosure that simply failed to print. On the 2026-09-21 reruns, a revision with two oversized withheld files produced identical certificates under the default rubric and the prospective witan-rubric-v23 — no withheld-path line under either — leaving open whether v23's withheld-path abstention correctly found no intersection or should have fired and silently did not. The certificate's "What was not established" section now carries exactly one withheld-path sentence on every certificate, never conditionally: "No content was withheld from any signal" when nothing was withheld; a "no signal that would have read them selected them" sentence when something was withheld but earns no signal's own file-selection test; the existing per-signal abstention disclosure, plus the count, when the intersection is earned and witan-rubric-v23's mechanism acted on it; and, new for every other rubric (including the calibrated public default), a sentence naming which signal would have read the withheld content and stating plainly that this rubric reports on what it read while the prospective v23 rubric abstains instead — the exact sentence the 2026-09-21 default-rubric certificate was missing if the second reading is the true one. report.json (report format 1.3) gains an additive withheldPaths array — one entry per withheld path, always present (empty when nothing was withheld), each carrying its skip reason, every signal whose own file-selection test would have read it, and whether the running rubric's mechanism acted on that intersection — computed from the same predicate calls the abstention itself consults, so the disclosure and the abstention can never drift apart. No scored metric moved on any fixture under any rubric.
Inspect the v0.4.11 release → · Read the OCI and MCP release-chain proof →
v0.4.10
2026-09-17The npm provenance gap is repaired prospectively. The published 0.4.10 package carries npm attestations from GitHub Actions, and its registry publisher is GitHub Actions rather than the package account. The immutable 0.4.9 package remains disclosed as published without npm provenance; a new release establishes the corrected artifact without rewriting that historical record. 0.4.9 was published by hand, by the operator, from a machine holding the package token and outside the workflow that generates provenance; the cause, the token inventory and the publishing-access change that followed are stated in the release notes, and manual publication with two-factor authentication remains possible, so workflow publication is the stated policy and not a registry-enforced guarantee.
Release identity is checked across each published route. The signed tag, public GitHub Release binaries, OCI image, Official MCP Registry record, Homebrew formula, and consumer-facing Action all resolve to 0.4.10. The published Windows, Linux aarch64, and npm package runs produced byte-identical reports for the same fixture. The floating Action v1 tag now resolves to this immutable release commit.
Inspect the v0.4.10 release → · Read the OCI and MCP release-chain proof → · Read the release notes →
v0.4.9
2026-09-16Every certificate states what it can see. Certificates produced by 0.4.9 and later describe the repository tree at its pinned revision. Evidence outside that tree, including a separate test repository, is neither seen nor claimed to be absent. Earlier certificates do not gain this disclosure retrospectively; their existing attestations remain valid.
Scoring changes under the same calibrated rubric are disclosed. The public paired 0.4.8 → 0.4.9 record at unchanged corpus commits found metric changes on four of 24 rows and one headline change: django, 3.2 → 3.1. React’s observability count moved 68 → 108 and alfred’s 64 → 73, with unchanged scores in both cases. These are wider pattern matches, not evidence that either repository became more observable. The test-script check also misses delegating test commands; that limitation remains disclosed in the rubric record. Default scans remain on v17, including its unresolved withheld-file scoring limitation; the newer abstention treatment is prospective.
Report format and scoring are separate comparisons. All 24 reports differ at byte level because report format 1.2 adds the optional rubricBehaviourFingerprint. This format change does not mean all 24 scores changed. A synthetic-corpus guard now detects measured scoring changes under an unchanged rubric identifier; matching fingerprints establish agreement on that corpus, not detection coverage.
The npm provenance gap remains visible. The published npm 0.4.9 package executes as 0.4.9, but its metadata omits gitHead and dist.attestations; the npm attestation endpoint returned HTTP 404 on 16 September. The OCI image has separately verified source provenance. The immutable npm version and release tag will not be replaced to repair this omission; the next release must restore npm provenance.
Inspect the v0.4.9 release → · Read the full public rubric delta and limits →
v0.4.8
2026-09-08A file Cejel declined to read under its own size limit was scored like one it couldn’t read at all. Measured effect: a design partner’s same-commit comparison at rubric v17 found 3.1/4.0 measuring 9 of 9 criteria under 0.4.5, 1.5/4.0 measuring 5 of 10 under 0.4.7 — same repository, same rubric. Two compounding defects, both present since 0.4.6. First, a file over the 512,000-byte content limit was mapped to affected rubric criteria by path shape (test-shaped, doc-shaped, and so on) before that criterion’s own collector ran on the content that was readable, wiping criteria even when other readable evidence fully answered them; a criterion now abstains only when its own collector genuinely has no readable evidence.
Declining to read a file is a disclosed limit, not evidence loss — and this release stops scoring it like evidence loss. Second, that abstention carried the same insufficientData flag as a genuine read failure, so the scorer kept it in the composite denominator at a punitive 0 instead of excluding it like ordinary insufficient_data. report.json’s signals now carry the skip reason: unreadable/denied_path stay a read failure and remain scored; too_large, excluded_by_extension, and non_regular_file are Cejel’s own disclosed coverage limits, excluded from the composite and never scored. The certificate’s disclosure text no longer calls a declined-to-read file “could not be read.” ADR-0001: coverage is disclosed, never discounts a score.
v0.4.7
2026-09-07The certificate now leads with what is wrong, shows the weight it actually applied, and folds away what a reader does not need first. Critical and warning findings across all criteria open certificate.html, critical first, before the scope prose, and the section says plainly when there are none (#268). Every rendered metric shows the weight applied after renormalisation across surviving metrics, identically on HTML, Markdown and terminal, instead of its nominal declared weight (#271). The glossary and the not-applicable group collapse by default behind native <details>, zero JavaScript, so the certificate stays one offline file and a machine reader still sees everything (#285). The “what to do next” field is derived from the same gap logic as “what was not established”, ranked so an unmeasured criterion that could change the verdict comes first, and every sentence names an absence rather than promising a score (#283). Presentation only: report.json, attestation.json, summary.json and badges are byte-unchanged.
A certificate can now say which CI attempt produced it. --run-attempt <n> records the run attempt; the GitHub Action forwards GITHUB_RUN_ATTEMPT automatically and nothing else ever fabricates one. It appears on the HTML and Markdown certificates and as an additive-optional predicate.githubRunAttempt on attestation.json, never inside report.json, whose byte-reproducibility for a pinned revision excludes per-invocation values (#282). --rubric-pin also accepts the prospective witan-rubric-v23-prospective-2026-09-06, explicit opt-in only; the public default stays calibrated v17 (#276, #277, #278).
The release itself was fixed. The MCP Registry publish validator compared server.json against the registry’s last observed state rather than the release being cut, which is how 0.4.6 published as 0.4.5 twice. It now compares against package.json, and release-identity metadata is bumped in the same commit that gets tagged; 0.4.7 is the first release for which that is true (#273). The release-currency check of this site was split so the homepage asserts every pinned @cejel/cejel@<version> string names the release, instead of grepping for a marker that had moved (#257). A defect-class census maps the shipped rule inventory against the 2024 CWE Top 25, the 2021 OWASP Top 10 and Cejel’s own D1–D8 taxonomy, with a CI check against drift; breadth only, no recall or precision claim (#274, #275).
v0.4.6
2026-09-02A versioned contract for bringing in your own signals. The CLI adds a generic --ingest contract v1 (version: "1.0") with a published JSON Schema and explicit additive-minor / breaking-major compatibility rules — a malformed or unversioned ingest document is now rejected loudly instead of guessed at. New attestations carry predicate.reportFormatVersion: "1.0".
The GitHub Action publishes only what it verified. Every invocation now runs in a fresh directory outside the scanned repository, so an early failure can no longer fall through to a committed workspace file. Public ingest across every entry point now shares one file validator with symlink and containment checks, and a scheduled full-tree mode extends the existing disclosure-boundary guard.
v0.4.5
2026-08-24Stable caller-supplied product identity and explicit rubric selection. The CLI adds --product-name so the same pinned repository emits the same identity fields across differently named checkouts, and --rubric-pin so a caller can explicitly select a supported rubric. Default scans remain on the calibrated v17 rubric.
Every certificate measurement now explains itself. Human-readable certificates state when score labels agree, explain why comparison does not apply, rename the bounded file-count ratio so it cannot be mistaken for prose verification, and give enumerable, binary, and conditional metrics exact reader-facing explanations. HTML metric values also stay inside their criterion cards.
v0.4.4
2026-08-18Certificate text stays inside its tooltip. Long unbroken metric descriptions now wrap inside the HTML certificate tooltip instead of overflowing its fixed-width box.
A named rubric must actually run. The sealed scoring path now rejects an explicitly supplied rubric version that is not wired into public dispatch, rather than issuing a certificate that names a rubric which did not execute. The release also adds clean-runner checks for the Smithery and OpenClaw MCP routes.
v0.4.3
2026-08-17Closes the stale-version trap. Every documented CLI invocation across the README, leaderboard site copy, and the calibration issue template now pins @latest or an explicit version, so npx @cejel/cejel can no longer silently resolve a stale cached package. A guard test fails the build if an unversioned invocation is reintroduced.
Readability fixes. Certificate tooltips now sit clearly above the page instead of blending into the content behind them. The Markdown certificate now carries the CLI version, matching the HTML and JSON certificates. Several metric labels and finding lists were corrected for consistency and accuracy across all three certificate formats. The release also states the recognized-CI boundary: which systems Cejel treats as real CI signal, and how that set can only change through a version-gated rubric change.
v0.4.2
2026-08-13A read-only currency verifier independently checks every supported release surface — npm, OCI, the MCP Registry, and GitHub — and fails closed on stale, unreachable, or inconsistent state, rather than trusting any one surface’s self-report.
Certificate clarity. Human-readable terminal, HTML, and Markdown certificates now include relying-party summaries, consistent measurements, and a plain-English glossary informed by external reviewer feedback. OCI distribution readback now authenticates before verifying the published image’s attestation and tagged source, and public-distribution validation rejects location fields and private-path-shaped text in public transparency artifacts.
v0.4.1
2026-08-12Byte-identical reports regardless of checkout location. report.json no longer embeds the absolute checkout path, so identical repository content and revision scanned in different directories produce byte-identical report artifacts. Reports from v0.4.0 and earlier keep their recorded path and remain verifiable.
Nothing else changed. This is a single-fix patch release: no scoring, rubric, certificate, or CLI behavior change. Prospective rubrics and in-development detector proposals remain unreleased.
v0.4.0
2026-08-09Stronger publication verification and two explicitly bounded prospective tools. The distribution workflow now reads the published MCP Registry record back, requires its immutable OCI digest, and verifies that digest’s signed provenance against the exact release tag and source commit. The release also carries prospective rubric v19 and a resource-bounded v2 discovery collector for explicit evaluation harnesses.
No silent scoring promotion. The v19 paired rescore completed all 24 rows with no score, status, coverage, placement, or non-B4 changes. It remains prospective; the public CLI default is still the separately holdout-calibrated v17 rubric. The v2 collector does not change the SHA-pinned v1.9 contract or ship the reserved Free LLM command family.
Inspect the v0.4.0 release → · Read the release evidence boundary →
v0.3.2
2026-08-07The published npm, Docker / OCI, standalone-binary, and MCP Registry release. Its published GitHub Release includes five native binaries, SPDX SBOMs, SHA256SUMS, and signed provenance. The source record names the v0.3.2 commit.
v0.3.1
2026-08-07The v0.3.1 npm and Docker / OCI release. Its source record names the v0.3.1 commit, so a reader can inspect the tagged source rather than rely on a release claim.
v0.2.2
2026-07-29Windows binaries — Cejel now ships a standalone single-file executable on all five platforms: macOS (Apple silicon + Intel), Linux (arm64 + x64), and Windows (x64). No Node, no npm, nothing installed. Every binary has an SPDX SBOM and SHA-256 checksum; the release also includes GitHub-signed build provenance, while own-platform verification receipts remain attached to the guarded build — because a trust tool you can’t verify is just another black box.
It runs inside your AI coding agent. Cejel is now documented as an MCP server you can add to OpenClaw (or any MCP client) through the shipped cejel-mcp bin, so an agent can check a repository’s evidence before you rely on it. It scans code you point it at — it does not watch or govern the agent’s actions.
Sharper certificates. Certificates now show both the producing CLI version and the exact rubric version, so reports from different installs explain their scoring identity. A dimension band that differs from the weighted score now carries an inline reconciliation instead of a bare number next to “verified.” A scan of a source tarball now warns when Git history was unavailable and a recent-PR metric may undercount. And the install docs force @latest to sidestep a stale npx cache. Thanks to the early testers who reported these.
Also folded in from main: hardened Git transport at the clone/checkout sinks, the free-core v50 multiple-comparisons disclosure, and a cross-repo preflight gate. Full commit-linked detail: CHANGELOG.md.
Earlier releases
0.2.1 and before0.2.1 was an npm-only certificate-presentation patch. 0.2.0 established the scoped public distribution: standalone binaries for macOS and Linux, the OCI image, and the Official MCP Registry listing. The complete history lives in the repository changelog.
Run the latest
Free, offline, deterministic. Nothing installed, nothing leaves your machine.