Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

JevGate

JevGate is a code-review gate. It asks small, precise questions about your code and turns the answers into findings you can act on.

JevGate parses your repository locally and builds small units of evidence: a function, a file outline, a pair of copies, a test, a documentation section. It asks TypeSafe Jev short, typed questions about each one. Code, not a chat model, combines the answers into a verdict. Each finding has a location, a probability and a concrete next step, so an agent or CI job can act on it and a person can check it quickly.

JevGate: consider · gate passed · 42 files · 118 API requests · 263410 input tokens · ~$0.0111

Consider (2):
  src/billing/invoices.ts:88 [maintainability/shared-logic] `createInvoice` and `createReceipt`
    perform the same steps for the same purpose (0.93). Differences: `invoices`→`receipts`.
    → Move the shared steps into one implementation
  src/api/search.py:41 [security/injection] `search_orders` places its parameters into a
    database query without binding, escaping or checking them; a caller passing outside
    input would make it exploitable (0.88).
    → Pass the values as bound query parameters

Where to start

JevGate is open source under MIT or Apache-2.0: source, issues and releases on GitHub.

Install

brew install tech-byte-frontier/tap/jevgate   # macOS and Linux, with Homebrew
curl -fsSL https://raw.githubusercontent.com/Tech-Byte-Frontier/jevgate/main/install.sh | sh   # Linux and macOS
cargo binstall jevgate            # any platform, with cargo-binstall
cargo install jevgate --locked    # build from source; needs Rust 1.90 or later

Each release has binaries for Linux (x86_64 and arm64, static), macOS (Apple silicon and Intel) and Windows (x86_64), with SHA-256 checksums and build provenance: gh attestation verify <archive> --repo Tech-Byte-Frontier/jevgate. The install script checks the checksum and installs to ~/.local/bin; set JEVGATE_VERSION or JEVGATE_INSTALL_DIR to change the version or place.

jevgate completions bash|zsh|fish|powershell prints a shell completion script and jevgate man a man page; Homebrew installs both.

Reviewing needs a TypeSafe API key. Git is needed only for --base and the staleness rule.

Quick start

jevgate init                              # write a commented jevgate.toml for this repository
jevgate auth login                        # validate and save your TypeSafe API key
jevgate check --dry-run --show-requests   # see exactly what would be uploaded; free and offline
jevgate check --report                    # review, then open a local HTML dashboard
jevgate baseline                          # accept today's findings; later checks fail only on new ones

More ways to run it:

jevgate check src/billing --verbose               # one directory, with notes and per-file detail
jevgate check --rule default --rule security      # add the security group
jevgate check --rule documentation                # agent instruction files, project docs and code comments
jevgate check --rule comments                     # only code comments
jevgate check --include-tests                     # also judge tests
jevgate check --base origin/main --format json    # changed files only, for agents and scripts
jevgate check --watch                             # re-check on save
jevgate baseline --merge                          # after a --base or path check: accept its findings, keep the rest
jevgate baseline mark wrong src/api/search.ts:41  # record why an accepted finding was accepted
jevgate baseline stats                            # each rule's rate of findings marked wrong

Every command documents itself: jevgate --help gives the workflow, exit codes, files and environment, and jevgate check --help explains each flag and the JSON report. -h prints a short summary.

What it finds

Maintainability (on by default)

RuleExample finding
File organizationThis file holds several features that would be easier to find apart; the upload helpers would be most useful as their own module. Test files are judged too, at most as a consider.
Function simplificationsync_accounts mixes separate jobs in long blocks; lines 40–71 would be most useful as their own function.
Shared logiccreateInvoice and createReceipt perform the same steps; one shared implementation would serve both.
Hardcoded valuesModule constants fix a value that differs between deployments; apply_discount special-cases one specific customer.

Tests (with --include-tests; file organization judges test files without it)

RuleExample finding
Test valuetest_total computes its expected value with the logic it tests.
Test redundancyThree tests of parse_date check the same behavior; one parameterized test could hold them.

Security (opt-in with --rule security; each finding names a CWE)

RuleCovers
InjectionVariables reaching SQL, shell commands, evaluated code, HTML, file paths, outbound URLs or redirect targets without binding, escaping or checks; in C#, types named by input or chosen by the data being deserialized; in Django code, request data given to pickle or yaml.load; in PHP, unserialize and uploaded file names
Sensitive dataPasswords, tokens or personal data written to logs; internal error details sent to clients, judged per error message and once per error handler (app.onError, setErrorHandler, Express error middleware, Flask and FastAPI handlers, Django error views and process_exception middleware, Django REST framework’s EXCEPTION_HANDLER, NestJS filters, axum IntoResponse and actix-web ResponseError for error types, ASP.NET Core exception handlers, PHP set_exception_handler, Slim and Laravel handler classes); in Django code, also the server’s environment or settings sent to clients (request.META)
Unsafe settingsCertificate checks turned off, weak password hashing, non-cryptographic random secrets, permissive CORS, session cookies without Secure/HttpOnly, secrets in environment variables the build puts into browser code (NEXT_PUBLIC_, VITE_); in C#, also developer exception pages outside development, token signature or lifetime checks turned off, signing keys written in the code, and secrets derived from data others know; in Django code, also debug mode for the deployed site, csrf_exempt views and secret keys written in settings
Access controlSQL row-level policies that let every user reach other users’ rows or trust user_metadata; SECURITY DEFINER functions without a fixed search_path or a caller check; grants that open writes to every user. SpacetimeDB modules (TypeScript and Rust, any kind of application): public tables of users’ private data, views that return other users’ rows, reducers that change rows their arguments choose or admin-only settings without checking the caller, and scheduled reducers clients can call in 1.x
WorkflowsGitHub Actions run scripts that execute text outside people write (${{ github.event.pull_request.title }}); pull_request_target or workflow_run jobs that run pull request code with secrets

Documentation (opt-in with --rule documentation)

RuleExample finding
Agent contextA section of CLAUDE.md only lists the scripts package.json already shows, and six harnesses load it at the start of every session.
Large docsdocs/operations/runbook.md holds several unrelated subjects; docs/plans/v0.2-plan.md mainly records finished work.
Stalenessdocs/plans/v0.4-auth.md is a plan whose work is finished: the repository has a release tag v0.4.0, and 6 paths it names were since removed.
DuplicationSection Release Workflow of CLAUDE.md states everything section Release of README.md states.
Code commentssave_skill has 3 comments to clean up: at lines 214, 218 and 222 they repeat the code (# Create skill directory above skill_dir.mkdir(…)). A module docstring saying it was “split out of portfolio.py to stay under the 500-line budget” narrates an edit instead of the code as it is.

The documentation rules read the instruction files that coding agents load (AGENTS.md, CLAUDE.md, GEMINI.md, and Claude, Cursor, Copilot, Windsurf, Cline, Kiro, Junie and Roo Code rules), even when hidden or gitignored. Each section is asked whether it only restates the stack, the manifest’s commands, generic advice or a configured linter’s rules, and whether text loaded in every session applies to only one directory. Project documentation in Markdown, MDX, reStructuredText or AsciiDoc of 300 or more lines is judged from its headings alone. Code finds staleness and duplication candidates: named paths or scripts that no longer exist, release tags, deleted files, and shared wording outside code examples. Jev then judges each candidate. Code comments and docstrings of application code are judged one at a time with the code they are about (the declaration they document, the lines below them or the line they end): whether they only repeat that code, hold sentences that add nothing, narrate an edit instead of the code as it is, or are code turned off. License headers, tool directives, type annotations, authorship tags and Sphinx version notes are left out; documentation that only repeats its declaration or says it at length (framework section banners included) is at most a note, as is every such comment in a project whose README says its code is written for learners; and the comments of one definition that span fewer than three lines in all are a note. Documentation findings are at most consider. The run also estimates the tokens each harness loads at session start; these estimates are evidence and never fail the gate.

jevgate rules prints every rule with its question and default; the rules reference lists them with what each one looks at.

Supported languages and frameworks

✅ judged · ➖ not applicable

Language or fileExtensionsMaintainabilityTestsSecurityDocumentation
Rust.rs✅✅ #[test], #[cfg(test)]✅✅ comments
Python.py✅✅ pytest, unittest✅✅ comments
JavaScript.js .jsx .mjs .cjs✅✅ describe/it/test✅✅ comments
TypeScript.ts .tsx .mts .cts✅✅ describe/it/test✅✅ comments
Go.go✅✅ Test…(t *testing.T)✅✅ comments
C#.cs✅✅ xUnit, NUnit, MSTest✅✅ comments
Ruby.rb✅✅ RSpec, Minitest, Rails test "…" do✅ no Ruby framework handlers yet✅ comments
PHP.php .phtml✅✅ PHPUnit …TestCase classes, Pest test/it✅✅ comments
Java.java✅✅ JUnit 4 and 5, TestNG: @Test, @ParameterizedTest, @Nested, JUnit 3 TestCase✅✅ comments
Astro, Vue, Svelte.astro .vue .svelte✅ scripts only➖✅ scripts only✅ script comments
SQL (PostgreSQL, Supabase).sql➖➖✅ access control➖
GitHub Actions.github/workflows/*.yml➖➖✅ workflows➖
Markdown, MDX.md .mdx at the root, in docs/ or doc/, READMEs and CONTRIBUTING files; agent instruction files➖➖➖✅
reStructuredText, AsciiDoc.rst .adoc .asciidoc, in the same places➖➖➖✅
Framework or platformWhat JevGate understands
Hono, Express, Fastify, KoaRoute handlers written inline (app.post('/pages', async (c) => …)); error handlers (app.onError, setErrorHandler, four-parameter Express middleware)
NestJSException filters (@Catch)
Next.js (App Router and Pages Router)Route handlers (app/**/route.ts), Server Actions ('use server' files and functions), pages/api routes, middleware, client components, error boundaries and pages are named to Jev with who calls them and where they run, so a Server Action’s arguments read as client input and a client component’s requests as the user’s own; dangerouslySetInnerHTML, redirects to client-chosen URLs, raw Prisma and Drizzle queries ($queryRawUnsafe, sql.raw) as opposed to their binding tagged templates, NEXT_PUBLIC_ secrets, and next.config headers
SvelteKitServer load functions and form actions (+page.server.js, export const actions = {…}), endpoints (+server.js) and server hooks are named to Jev with who calls them, so their request, form data, URL and cookies read as client input, and cookies.set is read with its secure defaults
Flask, FastAPIError handlers (@app.errorhandler, @app.exception_handler)
Django, Django REST frameworkViews and viewsets with the URL routes that reach them, the templates they render with |safe or autoescaping off, and the module constants they use; settings modules, with secret literals redacted, the settings modules that import and override them, and the files that select them (DJANGO_SETTINGS_MODULE); management commands as run by hand; handler500-style error views, middleware process_exception and EXCEPTION_HANDLER as error handlers
PHP pages, Slim, LaravelA file’s top-level code is judged like a function, since a page script reads the request and writes the response; route closures ($app->get('/users', function …), Route::post(…)) and configuration closures (return function (App $app) {…}); error handlers (set_exception_handler, subclasses of Slim’s ErrorHandler and Laravel’s ExceptionHandler)
axum, actix-web, RocketError responses (IntoResponse or ResponseError for an error type, #[catch])
MDX sites (Next.js, Nextra, Docusaurus, Astro)Imports, exports, comments and component markup are dropped, but the prose components carry (a <Note>’s text, a properties table’s descriptions, with names and types as code) is read; frontmatter title names the page
Sphinx, AsciiDocSection titles by their adornment or = level; comments, attribute entries and options dropped; code directives, literal and listing blocks read as code; :file: and include:: targets checked as paths, :attr: and :class: read as code, and _build/ skipped
ASP.NET CoreController actions, minimal API route handlers (app.MapGet("/orders", …)) and inline middleware; exception handlers (UseExceptionHandler with a handler, IExceptionFilter, IExceptionHandler, middleware classes that catch what the pipeline throws); Entity Framework Core raw and interpolated SQL, CORS and cookie options, UseDeveloperExceptionPage, JWT validation options and the constants a setup names
.NET projectsTest projects named like Shop.Tests or Shop.UnitTests, and classes of [Fact], [Theory], [Test] or [TestMethod] methods anywhere; designer and source-generated files (.Designer.cs, .g.cs) are skipped as generated
Supabase and PostgreSQLRow-level security policies, SECURITY DEFINER functions, grants, and the claims an access token hook sets
SpacetimeDB (TypeScript and Rust modules)Public tables, views and reducers, checked against the caller (ctx.sender) and the framework version’s scheduling rules
ReactJSX components and hooks as functions; text shown as a JSX child is not treated as markup injection
RSpec, MinitestExamples with the groups they are declared in, the before hooks and the let/subject definitions they read, and the helpers they call from support files such as spec/support and test_helper.rb
Sinatra and other Ruby DSLsMethods of classes and modules (def, def self., class << self, define_method); blocks passed at class or file level as units named by their call (get('/invoices')); constants as hardcoded values
Monorepos and examplesCopies are compared within a package and across packages linked by a local dependency, not across separate example apps, templates or variants of one example (examples/login/raw and examples/login/sdk); copies inside example code are notes
Java classesMethods and constructors belong to their class, interface, enum constant or record; static fields are constants; equals and hashCode overrides, constructors storing fields and setters given literals are boilerplate or data, never copies; initial capacities and a number a method returns whole are not values to name; a class of the same package counts as imported
Spring MVCA MockMvc or RestTemplate test request reaches the controller method whose @GetMapping, @PostMapping or @RequestMapping route serves it, so the test is judged with that method as its code under test
Bundlers and compilersMinified and compiled output (a source map reference, very long lines) is skipped as generated
Copied librariesA library copied into the repository (a versioned file name such as jquery-3.6.0.js, the readable build beside a .min.js, a license banner naming a version, or a script under assets, static or vendor that opens with a whole license and copyright) is skipped as vendored, whatever its size
MigrationsDirectories named migrations, Rails’ db/migrate and timestamped scripts under db/, and Alembic’s alembic/versions are skipped as migrations; SQL migrations are still read for access control

Other files, such as Kotlin, are listed as skipped with the reason and never fail the gate.

Continuous integration

A pull request review on GitHub Actions, with the JevGate action:

name: JevGate
on: pull_request
permissions:
  contents: read
jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
        with:
          fetch-depth: 0 # --base compares with the fork point
      - uses: Tech-Byte-Frontier/jevgate-action@v1
        with:
          api-key: ${{ secrets.TYPESAFE_API_KEY }}
          version: 0.18.0

The action installs a checked release binary, keeps .jevgate/cache in the Actions cache and runs jevgate check --base <pull request base> --format github; args passes more flags, such as --rule security. It runs on Linux, macOS and Windows runners.

--format github annotates the changed lines with each finding. A finding that fails the gate is an error; the others are warnings. A Markdown table goes to the job summary, and the usual text goes to the log. The full JSON report is always at .jevgate/latest.json if you want to keep it as an artifact.

  • Changed files only: --base reviews what changed since the fork point with that revision, the same files a pull request diff shows, plus uncommitted and untracked files. It needs the history, so check out with fetch-depth: 0. When no supported file changed, the run passes without any request.

  • Cache: answers are stored under a hash of the exact request: source, questions and model. Restoring an older cache is always safe, and unchanged code costs nothing on the next run.

  • Advisory or blocking: fail_on = ["none"] in jevgate.toml or --fail-on none reports findings without failing. A run that could not finish (missing key, provider rejection, request budget reached) still exits 2, so an outage never passes as a clean review.

  • A policy the change cannot edit: a pull request can edit jevgate.toml. To apply the reviewed policy of the base branch instead, read it with --config:

    git show "$BASE_SHA:jevgate.toml" > "$RUNNER_TEMP/jevgate.toml"
    jevgate check --config "$RUNNER_TEMP/jevgate.toml" --base "$BASE_SHA" --format github
    
  • Forks: GitHub withholds secrets from pull requests opened from forks, so there the run exits 2 with “No API key configured”. Skip the job for forks, or run it only on branches of the repository.

  • Budgets: max_requests caps the API attempts of one run. Reaching it leaves the run incomplete instead of passing on partial evidence. --dry-run counts the planned requests the cache already answers, so its estimate covers only what the cache lacks; follow-ups depend on answers and are not counted.

  • Transient failures: rate limits, overload and server or edge errors (HTTP 408, 429, 500, 502–504, 520–524, 529) are retried up to four attempts; a timeout or dropped connection is retried once, since the first send may have run.

  • Report-only paths: give tooling its own level with [[scope]] (below), so scripts are reported while product code gates.

Before each commit, with pre-commit, review what is staged:

repos:
  - repo: https://github.com/Tech-Byte-Frontier/jevgate
    rev: v0.18.0
    hooks:
      - id: jevgate-system   # the jevgate on PATH; `jevgate` builds it with Rust instead

On GitLab, a merge request pipeline can show the findings in the merge request with a Code Quality report. Set TYPESAFE_API_KEY as a masked CI/CD variable:

jevgate:
  image: buildpack-deps:bookworm-scm   # any image with git, curl and tar
  variables:
    GIT_DEPTH: 0                       # --base compares with the fork point
  cache:
    key: jevgate-answers
    paths: [.jevgate/cache]
  script:
    - curl -fsSL https://raw.githubusercontent.com/Tech-Byte-Frontier/jevgate/main/install.sh | sh
    - ~/.local/bin/jevgate check --base "$CI_MERGE_REQUEST_DIFF_BASE_SHA" --format gitlab > gl-code-quality-report.json
  artifacts:
    when: always
    reports:
      codequality: gl-code-quality-report.json
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

Other CI systems work the same way: install with install.sh or cargo binstall, set TYPESAFE_API_KEY, keep .jevgate/cache between runs, and read the exit code or the JSON report.

Coding agents

JevGate’s default output is written for coding agents as much as for people: ranked findings, each with a location, a probability and a next step, and nothing hidden when an answer stays undecided.

Check before finishing

Ask the agent to review its own change before it reports back, for example in AGENTS.md or CLAUDE.md:

Before finishing, run `jevgate check --base origin/main`. Fix each `review` finding;
for a `consider`, fix it or say why the code should stay as it is.

--base limits the review to the files changed since that revision, plus uncommitted and untracked files, so a check costs only what the change touches, and cached answers make reruns free. The exit code says what to do next:

Exit codeMeaning for the agent
0The gate passed; consider findings may still be worth a look
1The gate failed: act on the findings listed
2The run could not finish (no key, provider rejection, request budget); report it, don’t treat it as a pass

Structured output

--format json prints the full report: every file, finding, raw answer and probability, and the gate. The same report is always written to .jevgate/latest.json, whatever the output format, so an agent can run the check once and read the details after. jevgate check --help explains its fields.

jevgate rules --format json lists every rule with the question it asks, so an agent can tell what a finding means without guessing.

Watching while editing

jevgate check --watch re-checks the selected files after each save and prints one JSON report per line. Alongside it, jevgate serve answers local tools, never browser pages, with read-only JSON:

PathWhat it returns
/snapshotThe full latest report
/evidenceFindings and context per file
/context-requestsEvidence a file still needs
/changes?since=GENERATIONWhat changed since a report generation

Documentation for agents

The opt-in documentation rules judge the instruction files agents load at the start of every session (AGENTS.md, CLAUDE.md, GEMINI.md, and Cursor, Copilot, Windsurf, Cline, Kiro, Junie and Roo Code rules): sections that only restate the manifest or generic advice, and text loaded in every session that applies to one directory. jevgate check --rule documentation also estimates the tokens each harness loads.

Configuration

jevgate init writes a commented jevgate.toml at the repository root. The command line wins over the file, except that upload patterns and budgets in the file are ceilings that flags can only narrow. Unknown keys are errors. Its first line points editors with TOML schema support (Even Better TOML, Taplo) to jevgate.schema.json, which completes keys, rule names and levels and flags mistakes as you type.

upload_allow = ["src/**", "tests/**"]   # only these paths may be uploaded
upload_deny = ["**/.env*", "**/*.pem", "**/*.key"]
include_tests = true
max_requests = 300

[rules]                                  # a level per group or rule
maintainability = "review"               # judge, and fail the gate on review findings
tests = "consider"
security = "consider"                    # opt-in group, enabled by naming it
"maintainability/hardcoded-values" = "report"   # judge but never fail; "off" skips it

[[scope]]                                # levels for the files these paths match
paths = ["scripts/**", "tools/**"]
fail_on = ["report"]                     # every rule: judge, never fail
rules = { security = "consider" }        # except these
KeyDefaultMeaning
upload_allowevery pathGlobs of the paths that may be uploaded, including instruction files and context
upload_denynoneGlobs never uploaded, even when allowed
generatedbuilt-in namesGlobs of generated files, which are skipped
testsbuilt-in conventionsGlobs of additional test files
contextnoneFiles always sent as related evidence, like --context
rulesthe default groupA list selects rules. A table gives each group or rule a level: review, consider, uncertain, report (judge, never fail) or off
[[scope]]nonepaths (globs), with fail_on for every rule and rules for rules or groups, as above; off is not accepted (use upload_deny). The last scope that matches a file and addresses a rule wins; flags win over scopes
fail_on["review"]The level for rules without their own, like --fail-on
include_testsfalseJudge tests, like --include-tests
modeljev-1.13.0TypeSafe model; a pinned version keeps results repeatable
cache_ttl_secs3600Cache lifetime for the jev-latest and jev-preview aliases; pinned versions never expire
max_requestsunlimitedCeiling on API attempts per invocation
concurrency6Ceiling on simultaneous requests (1–8)
max_file_bytes262144Files larger than this are reported as needs-context, never truncated; generated and vendored files are skipped instead
max_context_bytes32768Ceiling on context bytes per request

Rules are named by ID (maintainability/shared-logic), key (shared_logic) or group (maintainability, tests, security, documentation, default, all). The same names work in --rule, --skip-rule and --fail-on TARGET=LEVEL, and the most specific entry wins.

The configuration reference lists every key with its type, and the rule names and levels it accepts.

Output and exit codes

FormatUse
agent (default)Ranked findings with locations and next steps, for people and coding agents
jsonThe full report: every file, finding, raw answer and probability, gate and usage
jsonlOne compact report per line; one per evaluation with --watch
githubGitHub Actions annotations and job summary, then the agent text
sarifA SARIF 2.1.0 log for GitHub code scanning and other SARIF readers: the findings the annotations show, error when they fail the gate
gitlabA GitLab Code Quality report for merge requests: the same findings, major when they fail the gate and minor otherwise

Agent output is colored on a terminal; --color never, or NO_COLOR set to any value, turns it off, and --color always or CLICOLOR_FORCE turns it on for pipes and logs.

Findings are review (act on it), consider (worth a look) or note (optional, shown with --verbose, never failing the gate). A file whose answers stay undecided is uncertain, and one that cannot be judged without more evidence is needs-context; neither is hidden or counted as clear. A finding’s message shows the probability that set its level; a note shows none, and the JSON report keeps every raw value. Finished plans that share a directory are one finding. A hardcoded-value finding that cannot name its value is one level lower.

Exit codeMeaning
0Gate passed, or no supported file changed since --base
1Gate failed
2Run incomplete, invalid configuration or invalid usage

--fail-on review|consider|uncertain|none sets what fails the gate; --fail-on security=consider sets it for one group or rule. Baselined findings and notes never fail it.

jevgate baseline can record why each finding was accepted: intended (right, and meant to be so), later (right, to fix later) or wrong (mistaken), with --reason or jevgate baseline mark. Reasons survive later rewrites of the baseline, and jevgate baseline stats reports each rule’s share of findings marked wrong: labels from daily use, not the model’s own probabilities.

Privacy and cost

  • What is uploaded: only the selected units of source, bounded by upload_allow and upload_deny. --dry-run --show-requests prints every initial request body without credentials or network access.
  • Instruction files: uploaded only when a documentation rule is selected, and still bounded by the upload patterns.
  • Credentials: a check reads TYPESAFE_API_KEY from the environment, then --env-file or the repository’s .env, then the key saved by jevgate auth login (OS credential store, or an owner-only file). The key is never printed or written to reports.
  • Cost: every run prints its input tokens and an estimated cost. Cached answers cost nothing.
  • Secrets: out of scope on purpose, because judging secrets would mean uploading them. Use a local secret scanner.

Troubleshooting

The run exits 2

Exit code 2 means the run could not finish, or the configuration or command line is invalid. The message says which; an outage never passes as a clean review.

No API key configured. Run jevgate auth login, set TYPESAFE_API_KEY, or provide --env-file PATH
A check reads TYPESAFE_API_KEY from the environment, then --env-file or the repository’s .env, then the key saved by jevgate auth login. jevgate auth status shows which one a check would use and verifies it. On GitHub Actions, pull requests from forks don’t receive secrets: skip the job for them (if: github.event.pull_request.head.repo.full_name == github.repository).
Cannot find revision …; in CI, fetch it (for example fetch-depth: 0), or … and HEAD share no history
--base needs the history back to the fork point. Check out with fetch-depth: 0.
TypeSafe HTTP 403 (blocked by the provider's edge protection)
The provider’s firewall rejected a request because of what it contained, such as a test fixture holding an attack string. Find the file with --dry-run --show-requests, and exclude it with upload_deny; JevGate’s own repository does this for its HTML report’s escaping test.
Cannot connect to TypeSafe; request was not sent
A network problem before anything was sent. Rerun; cached answers are kept.
Session API request budget exhausted; restart with an explicit larger --max-requests
max_requests or --max-requests capped the run. Raise it, or check fewer files with --base or paths; --dry-run estimates what a run will ask.
Another JevGate session owns latest.json
Another check or --watch is running in the same repository. Stop it first.

Rate limits, overload and server errors (HTTP 408, 429, 500, 502–504, 520–524, 529) are retried up to four attempts before the run gives up, and a timeout or dropped connection is retried once.

Many files are uncertain

A file is uncertain when some of its answers stayed undecided after the follow-up questions. JevGate reports this instead of hiding it or counting the file as clear. --verbose lists each undecided unit and the question it stayed undecided on. It never fails the gate unless you ask for that with --fail-on uncertain.

A finding is wrong

Accept it with jevgate baseline, and record why with jevgate baseline mark wrong PATH:LINE; jevgate baseline stats counts each rule’s mistaken findings. Reporting it with the wrong finding template, with the finding from .jevgate/latest.json and a small piece of the code, is how the rules improve.

A file is skipped

Skipped files are listed with the reason: generated, vendored or minified code, migrations, an unsupported language, or a path outside the upload patterns. generated, tests and the upload patterns in jevgate.toml change what is selected. A file larger than max_file_bytes is not skipped but reported as needs-context, never truncated.

No colors, or escape codes in a log

Agent output is colored only on a terminal. --color never or NO_COLOR turns it off, and --color always or CLICOLOR_FORCE turns it on for pipes and logs.

How it works

  1. Local analysis, nothing uploaded. Tree-sitter parsers find functions, methods, types and registered callbacks, such as route handlers written inline in app.post('/pages', async (c) => …). They measure nesting, group a file’s members, find renamed copies, map tests to the functions they call, and list the statements where a value reaches another program. This evidence locates and scopes; it never decides a finding.
  2. Small, literal questions. Each request covers one small unit and asks a few questions, such as “Would splitting this function make it easier to understand?” or “Does this function put a variable into the text of an SQL query instead of binding it?”
  3. Follow-ups only where needed. When an answer is split, JevGate gathers more evidence (callee signatures, callers, a specific check) and asks once more instead of guessing.
  4. Composition in code. Answers become review, consider, note, clear or uncertain at a 0.80 threshold. Raw probabilities stay in the JSON report.

The rest of this page describes the evidence units and composition rules in detail.

Evidence units

JevGate asks Jev short, literal questions about small units of evidence that code has already built, then composes the answers in code.

Why units

A single request per file carried the whole source, every rule’s candidates and several broad questions. Large states lowered decisiveness, and reconciling file-wide answers with per-operation probes let decisive concerns disappear. Units send only what one question needs: one function’s source, a file’s member signatures, or one candidate pair.

Pipeline

  1. Eligibility and purpose. Deterministic roles, generated-code headers, compiled or minified output (a trailing source map reference, or nine tenths of the file in lines of 1,000 bytes or more) and structural test markers decide which code the application rules and the test rules see. Only a test path without structural tests gets a file-purpose request. Go tests are Test…, Benchmark… and Fuzz… functions taking *testing.T, .B or .F. C# tests are whole classes marked [TestFixture] or [TestClass] or holding a method marked [Fact], [Theory], [Test], [TestCase], [TestCaseSource], [TestMethod] or [DataTestMethod], and every C# file of a test project directory named like Shop.Tests; .Designer.cs, .g.cs and .g.i.cs files are generated. Ruby tests are RSpec groups and examples written as statements (describe, context, it, specify, its, titled by a string or not at all, so a Rakefile’s test(:unit) do is not one) and classes whose superclass ends in Test, TestCase or Spec (Minitest::Test, ActiveSupport::TestCase) with test_* methods or test "…" do blocks; *_spec.rb files and Ruby files under spec/ are test files. PHP tests are the test…, @test or #[Test] methods of a class extending a …TestCase, and Pest test(…)/it(…) calls; …Test.php files are test paths. A Java class is a test class, whole, when it holds a method annotated @Test, @ParameterizedTest or another test annotation (a composed one whose name ends in Test included), a JUnit lifecycle method such as @BeforeEach, a nested test class, or extends JUnit 3’s TestCase; …Test, …Tests, …TestCase and …IT files are test paths. A test that sends a request to a literal path (MockMvc’s get("/owners/{id}"), RestTemplate’s RequestEntity.get(…)) calls the Spring controller method whose @GetMapping, @PostMapping or @RequestMapping route serves it, under its class’s prefix, by full name, the most literal route winning: controllers share method names such as initCreationForm, and a request names no method, so such tests had no code under test. Astro, Vue and Svelte files are parsed as their scripts: Astro frontmatter and <script> contents, with every other byte a space, so lines stay the file’s.
  2. Local analysis (src/analysis/). Units with signatures, calls, references and control-flow nesting; callbacks registered through calls, including module-level route handlers named by their registration (app.post('/pages')); member groups by average linkage; callers that import the file (application code only, since tests calling a group do not make it a dependency); for file organization, each member’s line count and the file’s, and for a test file, its cases with their enclosing describe, class or module and the functions under test they call, grouped by shared suite, subject or helper. A subject that one type in scope owns is named with it (StringUtil::isBlank); a Java test file’s top-level class is not a suite, since every case would share it, while @Nested classes are. A pair of similar tests is about a function they share that is neither a camelCase getter or setter nor called by most of the file’s tests (a fixture or client every test uses), and that the fewest tests call: the first shared name made setBirthDate the subject of validator tests and grouped unrelated pairs under a shared create_user fixture. Test files get this outline without --include-tests; their finding is at most a consider. Who calls a group is evidence only: gating a split on callers of its own hid large files whose single caller is the rest of the program; Type-2 clone candidates grouped by overlapping copies, only within one package or packages linked by a local dependency (copies in side by side templates or example apps are separate projects); test cases with their subjects and similar pairs. A PHP file’s top-level statements outside functions and classes (and its <?= … ?> echoes) are one more unit, top-level code: a page script reads the request and writes the response there, so every security rule judges it like a function, while other languages’ top-level statements are judged for unsafe settings only. Closures registered through calls ($app->get('/users', …), Route::post(…)) or returned by a configuration file (return function (App $app) {…}) are functions of their own. A Java constructor’s statements that store a parameter, another object’s field or a literal in a field break a copy: two constructors filling different fields matched as copies and stayed undecided. So does a Java setter given one literal (owner.setCity("Madison")): a fixture built in a helper and an owner built inside a test matched as copies whose only differences were the values. Java equals and hashCode overrides offer no copies or values, and neither does an initial capacity (new ArrayList<>(4)) or the number a method returns whole (int cost() { return 7; }), which the method’s name already names.
  3. First pass (src/units/). One dispatch of every unit request. Functions, for simplification, hardcoded values and security, are packed eight per request within runs of functions, a run ending after a function whose name hashes to one of four values, so a function added, removed or resized re-asks only its run: packed in file order, one added function re-sent every later pack of the file (5 of 5 simplification requests of compose.rs, against 1 now). Runs add requests (25% to 106%, and 33% to 300% for instruction files), and every request is billed about 280 input tokens beyond its size, so a full first pass bills 4% to 7% more input (functions 6% to 13%, values 4% to 7%, security 3% to 5%). A function removed re-asks 24% to 47% fewer tokens, so the runs pay back after 19 to 30 edits. One in eight names ending runs cost under half as much extra on a full pass but saved a half to two thirds as much per edit, and left whole files in one run (clones.rs, literals.rs); one in four overtakes it after 31 to 40 edits. Tests are sent one per request, because unrelated tests in the same state left more answers undecided. State uses literal paths such as functions[2].source; group IDs are Choice options. Stage and freshness hashes stay in local jevgate metadata that is not uploaded.
  4. Follow-ups. One recheck per uncertain unit, with callee signatures, the enclosing functions or the file’s application source; a decisive recheck replaces the first answer and both are kept. A hardcoded-value unit is asked instead whether every value is of an acceptable kind; that check can only clear, since re-asking the concern per value added false findings. The unnamed-value check lists the kinds in its question: asked whether each value “explains itself”, it cleared none, even of field names. A function or file-organization note whose middle and top levels both stay under 0.50 gets the same recheck, and a decisive answer replaces it. An outline whose recheck stays undecided is asked, in a request of its own, what kind of file it is: one algorithm, type, resource, component, set of definitions, helpers or coordination serves one feature; the same kind of code written out per feature, or several unrelated features, serves several. Kinds that serve one feature at 0.80 clear it, and the others at 0.80 raise a consider. Weighing a split stayed near a third per level on such files, while naming the kind was decisive; asked beside the recheck, the kind moved the recheck’s own answers. A file too long to send whole gets no recheck, so its undecided first answer is asked the kind from the outline alone; large Java classes and their test files otherwise stayed uncertain. A test left undecided on whether it re-implements the code or checks only its mocks is asked again with the bodies of the functions it calls and its file’s imports, mocks and setup hooks (a part too long is left out, never cut); each answer replaces the first unless only the first is decisive. A Ruby test is sent with the groups it is declared in, since an RSpec example reads as a sentence continuing them and the outer group often names the class under test. Its recheck shows, instead of every hook of the file, what runs for it: its groups’ before, around and setup hooks, let!, and the let and subject definitions it reads (directly or through another), then the test helpers it and those hooks call, from its own file or the nearest support file (one without test cases sharing a directory with it; an RSpec group’s methods stay in its file). The note says a value built there is input to the code under test unless a mock returns it: with the file’s setup described as mocks, factory definitions and mock_app routes read as mocks, and a third of such tests stayed undecided. Ruby test pairs carry their groups and hooks when these differ, and are also asked whether each test checks something the other does not (another method, matcher, attribute, option or code path); “one adds nothing” is a review only when that is ruled out at 0.80. Copied RSpec examples for an alias and its original (each and each_pair) or for two predicates of one record were otherwise reviews. A controller method a test reaches through a request carries that route. The first pass names a literal worked out by hand, even with the arithmetic in a comment, as not re-implementing the code. Its “checks only its mocks” question names tests without any stub (a setter read back, a round trip, a benchmark), and tests checking which stub the code chose or the view and status a handler chose for stubbed data, as not hollow: those stayed near a third, as if every assertion were about the mocks. A pair of tests whose overlap spreads over the three levels is asked again with the body of the function both call: whether it throws before the rest of a test runs is in that body. Then one locate Choice per split finding picks the body block to extract, and one per hardcoded-value review or consider names the value it is about. Special-case findings in different files that name the same identity become one finding at the strongest site; the others are notes pointing at it. Numbers and paths are not grouped: 1000 meant metres per kilometre in one file and an image height in another. Security units whose presence answers are not clear get one trace before the rechecks: specific literal checks per kind, a Choice among the unit’s sites, and the origin of its values (or whether it runs only in development). One broad “is every value bound, escaped or checked?” stayed undecided even for eval of model output; a check per kind decides and names the kind. An injection whose origin stays unclear or is the function’s parameters is asked its origin and checks again with up to three callers; its answer replaces the traced one unless only the traced one is decisive. The markup check names text shown as a JSX child and CSS values or class names as escaped or inert; sending where each built string goes did not settle React units, the examples did. A sensitive-data trace lists the message argument of each error the function creates and asks which one, if any, carries another error’s text: the response is often written by an error handler in another file, and adding the handler to every unit also cleared real leaks. Each registered error handler (.onError(…), .setErrorHandler(…), Express four-parameter .use(…) middleware, Flask and FastAPI decorators, NestJS @Catch filters, axum IntoResponse and actix-web ResponseError for an error type, Rocket catchers, ASP.NET Core UseExceptionHandler lambdas, exception filters, IExceptionHandler and middleware classes whose Invoke catches what the pipeline throws; a path given to UseExceptionHandler re-executes a page judged as its own code) is asked once whether it sends clients more than the program’s own messages and codes, with the program’s …Error classes (Rust enums with their #[error] messages) and the functions of its file that it calls. A registration inside a comment or string literal registers nothing. An injection trace also gets the definitions of enums its sites name (ConfigKey.aiTag), so a fixed choice does not read as a parameter. Questions about C# files carry ASP.NET Core’s names for what they ask (FromSqlInterpolated binds, ServerCertificateCustomValidationCallback returning true skips certificates, ValidateIssuer checks claims, not certificates), and C# traces ask three more weak-setting checks (developer exception pages outside development, token signature or lifetime checks turned off, signing keys written in the code) and one injection check (types named by input or chosen by deserialized data). An unsafe-settings trace in C# also gets the const and static readonly fields the code names, often declared in another file, so a key written in the code does not read as configuration. Other languages keep their wording: the additions were measured on ASP.NET Core projects only. A broad weak-setting answer that none of the specific checks leans toward names no setting to change and is at most a note: on an action marked [AllowAnonymous] on purpose it was 0.85 while every check stayed at 0.30 or less. In a package that depends on next, a file’s path names its role (app/**/route.ts, pages/api/**, middleware.ts or proxy.ts, app/**/error.tsx, pages and layouts, next.config.*), and in any package a leading 'use server' or 'use client' directive, or a function body that starts with 'use server', marks Server Actions or a client component. The role goes into the file state of security and hardcoded-value questions as framework, and every such question’s note points to it: stated only in the state, a client component’s role did not clear its browser requests. Questions about how code reads (splits, outlines, tests) get no role: there it moved split answers without informing them. With it, a Server Action’s parameters read as client input (an injection that was a consider on its parameters became a review) and an error boundary as the browser’s own page. The injection trace asks one more literal check, whether a redirect target from a variable is checked (CWE-601): without it, redirect(next) and NextResponse.redirect(returnTo) were notes about URLs “it requests”. Only targets a request carries count: a link shortener’s redirect to the destination its owner saved was a review. The SQL check names tagged templates that bind (sql, $queryRaw) as handled and $queryRawUnsafe and sql.raw as not, the markup check names dangerouslySetInnerHTML (also a site), and the unsafe settings ask whether a secret comes from a variable the build puts into browser code (NEXT_PUBLIC_). A next.config file’s setup is every top-level statement that holds an object, with its innermost objects as sites, since headers() settings call nothing. Django code (Python that imports Django or Django REST framework, and settings modules) is asked Django’s names in the checks that have them and three more; other code keeps the common ones, so its cached answers stay valid. They name raw SQL (raw, extra, RawSQL), mark_safe and templates that write values with |safe, redirect() to route names or the program’s own paths, the storage API, Django’s password hashers and validation errors, and add deserializers of request data (pickle, yaml.load), debug mode, csrf_exempt and literal secret keys, and request.META or the settings sent to a client. A view is sent with the URL routes that reach it (a \d+ parameter holds digits), the templates it renders that write values unescaped, and the module constants it names; a management command is marked as run by hand, since its options came back as another party’s. A settings module is one unit whose statements are its settings, with secret literals redacted to their length (a dotted path such as a secret-key getter is not a secret). It is sent with the lines that select it (DJANGO_SETTINGS_MODULE in a Dockerfile, CI or manage.py) and with the settings modules that import it, directly or through others, each with its own selections and its assignments of the settings it sets again; a setdefault in manage.py or wsgi.py is marked as only a default, since shown bare it read as the deployed choice and made shared CORS settings that production sets again a review. Its sites put security settings first, then assignments into a setting (OPTIONS["ssl_cert_reqs"] = None), which URLs joined with paths had crowded out. Presence alone found a development DEBUG = True or a signed webhook’s csrf_exempt weak as surely as a deployed one, so a Django weak setting must be named by a specific check at the review threshold to be a consider or review; otherwise it is a note, and settings only development or tests run with are two levels lower. The secret, cookie and CORS checks ask about the deployed site, since asked about the module alone they flagged base settings that production sets again. The markup Choice asks a Django view what it sends back, since views that only redirect or render an escaping template split on the markup check. Since nearly every Django view places request values somewhere, an injection note that no check found (values from another party, every check undecided or clear) gets the settle Choices too: on django.nV, redirects to the view’s own paths with ids in them left seven such notes, which the redirect-target Choice cleared. The CSRF check names forms for visitors who are not signed in, such as a password reset request, as not acting for a user. Django error views (handler500 = …), middleware process_exception and Django REST framework’s EXCEPTION_HANDLER are error handlers, asked with the framework’s errors written for the user named as acceptable. A security unit still uncertain after its trace and recheck is asked, per undecided check and in a request of its own, one literal Choice that can only clear that check: where its URLs come from (a host of the program’s own at 0.80 clears an undecided URL check; a configured host sent another URL to fetch does not) and where the code runs (only in the user’s browser clears it); where its redirect targets come from (written in the code, returned by its own server or what callers pass, checked, or no redirect); how its markup is rendered (escaped by JSX or a template, or shown as text; PHP units are asked what they join instead, see below); which sites may send credentialed requests (none, listed origins, or any origin without credentials); what its logs write (only messages, ids and caught errors); or where its text goes (anywhere but a remote client at 0.80 clears undecided error details). Offered beside “a whole URL handed to it”, the browser lost for a client component’s fetch helper, which is why where code runs is its own Choice. A consider or note that rests on an undecided error-detail or URL check gets the destination or runs-in question, since it claims the text likely reaches a client or the request leaves a server. On three Next.js apps these Choices took the uncertain files from 56 to 34, most of them client components that navigate to fixed paths or render values as attributes. A Choice about what a query builder joins into SQL (its own clauses, numbers, or values handed to it) was tried for considers on parameters and dropped: it cleared a sort column taken from the request as readily as clauses with placeholders. The same question about paths cleared real traversals, reading names stored in an index as the program’s own, so path checks stay undecided until callers show more. The SQL check counts identifiers quoted by doubling embedded quotes as handled (identifiers cannot be bound), and the URL check excludes requests a web page sends from the user’s browser; on fresh repositories both had flagged such code, while the SQL and SSRF advisory functions kept their answers. PHP units read the presence questions and checks in PHP’s own terms (src/units/questions/php.rs), naming its functions (echo, shell_exec and backticks, mysqli_real_escape_string, password_hash, CURLOPT_SSL_VERIFYPEER); every other language keeps the general wording, so its requests and cached answers are unchanged. The PHP SQL check counts driver escaping inside quotes and numbers as handled: asked only about binding, DVWA’s escaped and quoted guestbook inserts were reviews. Text a page writes with echo is its response, not a log; a page that only calls generateSessionToken() or a session helper is not judged for what that helper does; the message of an exception class the program defines, caught by name, is its own text (four BookStack upload controllers returning FileUploadException messages were error-detail reviews); and only variables placed into a query or path count for the origin, not an uploaded file’s contents. unserialize and uploaded file names are PHP checks, asked only of source that names unserialize or an upload; a page that only showed an upload form stayed near 0.4 on whether it saves uploads. PHP units have three settle Choices of their own, asked whenever their check is not clear, even when it found a concern, since each can clear it: what the unit joins into HTML unescaped (request values, stored records and parameters holding text keep the check; escaped values and numbers, text the program produces such as errors and command output, HTML other code builds such as a page body an included file sets, and element data clear it), what its command lines hold (values each checked against a strict format, such as octets that pass is_numeric, clear it; values with some characters stripped do not), and where its paths come from (constants and a file name a switch picks clear it; names stored in a database or file are their own option). A page that reads a request also joins ids converted with intval and database errors, and the markup check found those at 0.9 while the origin question answered for the request. A markup check that found a variable which the Choice names as a request value or stored record is a review whatever the origin question said: an access log joining user names from the database stayed uncertain with the origin split. A page script’s consider or note names values whose origin it does not show, not parameters. On DVWA the PHP Choices left 13 of 329 injection units undecided, from 46. Agent instruction files (src/docs/) are found by name even when hidden or ignored, including Kiro steering files, Junie guidelines and rules, and Roo Code rules, each loaded by its harness’s documented rules (a Kiro inclusion, a Roo Code mode folder, Junie’s precedence of its own AGENTS.md). Each file’s heading sections, or the top-level blocks of a long section, are sent packed, within runs of sections that end after a heading hashing to one of four values (a long section’s blocks share its heading and stay together; brstocks CLAUDE.md went from 1 request to 4, billing 18% more input on a full run and 57% less when one section is removed), beside the nearest manifests (with the runtime versions they require: engines, packageManager, requires-python, rust-version), the configured linters and the directories. A section whose signals stay undecided is asked, alone, which kind of section it is (instructions, a description, a command list, generic advice or a record): its own kind at 0.80 raises the undecided signal, that kind at 0.20 or less clears it, and instructions clear an undecided “restates the repository”. On vercel/ai, API tables and import maps stayed near the middle on “only describes”, while naming the kind was decisive. The linter question asks whether a section is only style the listed tools check with their usual settings; asked whether it asked for such style, a list of Do Not rules with one import rule, or file naming no configured rule checks, stayed undecided. Code decides which harness loads each file and when, from its documented discovery rules. It also records loading facts: copies, unresolved imports, and files a harness skips or truncates. These are reported, never judged. Project documentation is Markdown, MDX, reStructuredText or AsciiDoc, read as Markdown with the file’s own lines (src/docs/format.rs): MDX drops imports, exports, comments and component markup but keeps the prose components carry, reStructuredText titles become headings by the order of their adornment styles, AsciiDoc titles by their = level, comments and attribute entries are dropped, and code directives, literal and listing blocks are fenced with their language. A document of 300 or more lines is sent as its headings only, with # marks for nesting. A split finding is then located with one Choice among its top-level parts; a split that stays undecided is asked which kind of document it is (a guide, a reference, a migration guide, an introduction, or a collection of unrelated subjects). The kinds that serve one subject at 0.80 clear it and a collection at 0.80 raises a consider: a quickstart, a migration guide and a package README each stayed near a third per level on the split. Per-section questions on project docs were dropped: on a labeled sample they found almost nothing, and they cost about five times more than an outline. Staleness and duplication candidates come from code. Staleness candidates are paths and scripts a section names that the repository lacks, with what Git shows about each. A span written with a code role (:attr:, :class:) is not a path, nor is a link that climbs above the repository (a README badge’s ../../actions/...); a path the ignore files cover (a bare name also as a directory, so backend/app/frontend/ covers a build output), or one the section writes out for a code block (its filename=, or the one path the paragraph before it names), is the reader’s own; a script where no command starts, such as “make sure”, is not one, and dependencies count as scripts, since pnpm tsx runs one. A missing name whose one tracked namesake sits under the document’s directory, such as .tsx for .ts, is named beside it. A check that stays undecided is asked, apart, what the section treats the names as (a current part of the repository, the reader’s own project, an example, not a file at all, or something removed); the repository at 0.20 or less clears it. A protocol method (tools/call), skill-relative example paths and a migration guide’s pnpm drizzle-kit stayed near 0.25 to 0.50 on the check and were decisive as a kind. Duplication candidates are section pairs where 30% of the smaller section’s three-word sequences recur in the other, at most three per pair of documents. Sequences come from the prose; a section with too little prose is compared on its prose, commands and settings, and program code is never compared: pairing on code sent hundreds of vercel/ai pages that shared a streamText call and nothing else. Project documents of separate packages are not paired, since each package’s README is read alone. A section that pairs with two or more others heads a family, and its members are asked against it alone. A document whose release is tagged, or whose named paths were deleted, is asked from its headings whether it is a plan; a plan with those facts is one finding, and its own candidates are not asked. The section check and the pair questions (does A state everything B states, and the reverse, as Scores whose middle “mostly” is acceptable; do they disagree, a Score whose middle “only in detail” is acceptable; are they about one subject; is one a translation of the other?) are follow-ups for the other documents, sent with each document’s title. A translation clears the repetition answers but not a disagreement. Different subjects settle what stays undecided, never a decided answer. A pair still undecided is asked, apart, how the two sections relate (one repeats the other, they overlap, they are written alike for different subjects, they contradict each other, or they describe different things); a repetition or a contradiction at 0.20 or less clears that check. Weighing coverage stayed near the middle for one step of two quickstarts or one option of guide and reference, while naming the relation was decisive: 18 of 654 pairs undecided on vercel/ai became
    1. The repetition findings that share a section are one finding at the section most of them name, listing the others. A Score on how much two sections overlap, asked of every pair, stayed on its middle level for almost every pair, so it is not asked. Code comments (documentation/comments) are collected from the parse tree of application code, outside tests: runs of line comments on consecutive lines are one comment, and Python docstrings are comments of the definition or module they open. License headers, tool directives (eslint-disable, # noqa, //go:), JSDoc type annotations, shebangs and comments without letters are left out. Each comment is sent with the code it is about: the declaration it documents (its signature when longer than 40 lines), the lines below it up to a blank line, another comment or the end of its block, the line it ends, or for a file’s own documentation the signatures of its definitions; with where it sits and the signature of the definition it sits in. Comments are packed eight per request within runs of definitions, a run ending after a definition whose name hashes to one of four values, so a comment added or removed re-asks only its run: packed in file order, one added comment re-sent every later pack of the file (6 of 6 requests of compose.rs, against 1 now), and a pack per definition doubled the requests. They are asked whether they only repeat that code (a Score whose middle level holds headings over a group of lines), whether sentences could go without losing anything (only comments of 20 or more words), whether they describe an edit instead of the code as it is, and whether they are code turned off (only comments whose lines read like statements). Asked of every comment, those two stayed near 0.5 on two-word trailing comments and on docstrings holding usage examples; asked only where they apply, psf/requests’ undecided comments went from 78 of 519 to 63 with the code check and to 35 with the wordiness one. The first wording of the wordiness question (“could it say the same in far fewer words?”) called every multi-line JSDoc block that explains a rounding rule or a matching strategy wordy; asked whether sentences add nothing, with parameter entries named by whether the signature writes their types, the Sphinx :param entries of untyped functions stopped reading as filler while numerator: The numerator value. still does. A flag’s meaning ("-x", # Extract audio), a unit (// 16px beside 1rem) and a category heading (// Fixed above fixed costs) are named as acceptable. A comment left undecided is asked again with the whole definition it sits in, then, alone, what kind of comment it is (a reason, caveat, reference, usage, summary or heading, against repeating the code, narrating steps, an edit or code turned off). SQL files (security/access-control) are split into statements that honor comments, quotes and dollar quotes. Each project’s files, grouped above their supabase or migrations directory, are read in path order, so a dropped or replaced policy or function is not judged. Each policy is sent with its table, the functions it calls, and the functions that set the token claims it reads, such as a custom access token hook: without the hook, 63 of one project’s policies stayed undecided on whether users can change the claim. A SECURITY DEFINER function goes with its grants and revokes of EXECUTE; a grant with whether its table has row-level security. The criteria name role checks, service roles, restrictive policies and trigger functions, which a literal “other users’ rows” question flagged. SpacetimeDB modules (TypeScript files that import spacetimedb/server, Rust files with #[table], #[reducer] or #[view] attributes) are read for access control too, whatever the application: each public table with the columns that name users, and each view and reducer with up to six functions it calls (two deep, in the module’s package). Lifecycle reducers are left out; a Rust reducer is sent with the table line that schedules it (scheduled_by), since the schedule is declared on the table. The framework version comes from the package’s package.json or Cargo.toml, since scheduled reducers are private in 2.x and callable by clients in 1.x; with no version, both are stated. Each definition gets a Score whose two lower levels are acceptable (the caller’s own data; data meant for every user) and concern Nouls: for a reducer, two literal checks (a row chosen by an argument without an ownership check; an admin-only change without checking the owner, an admin or a granting role). One broad Noul left most real definitions undecided and most broken reducers under 0.80. When access control is the only code rule, only the files of module packages are collected. Workflow jobs (security/workflows) are split by indentation. The parser lists the ${{ }} expressions inside run scripts, and Jev is asked whether one can hold text outside people write: one question over the whole job scored obvious injections 0.57 to 0.79. Jobs of workflows that run on pull_request_target or workflow_run are asked whether they run pull request code with secrets.
  5. Composition (src/units/compose.rs). Pure. On a Score whose top level is the actionable concern: review at 0.80 on the top level, consider at 0.80 on middle-or-top, clear when the top level is ruled out at 0.80, otherwise uncertain. Where the middle level says the code is fine as it is, a consider also needs the top level at 0.50; middle mass alone is an optional note. For hardcoded values and security, an answer still undecided after its follow-up is a note when it leans toward the concern (0.50, the leading probability) and stays uncertain otherwise: undecided answers leaning away were almost all acceptable code, and leaning toward held both real positives of the labeled set. When the own-messages check finds another error’s text in an error message, an error-detail answer leaning toward a client is a consider. Instruction sections are cleanups, so their findings are at most a consider. Comments are cleanups too: at most a consider, and documentation that only repeats the declaration it documents is at most a note, since documentation tools and docstring linters expect a summary even when it says what the name says. A definition’s comments that reach a consider are one finding, listing each comment with what is wrong with it, and when they span fewer than three lines in all they are a note. Undecided answers do not lean into notes, because on the labeled set that added notes to kept sections. A large document’s undecided history answer does lean into a note. Policies, grants and an open search_path are at most a consider, since a policy may cover data meant for everyone; an unchecked SECURITY DEFINER function and workflow findings can be reviews. A SpacetimeDB definition is a review when a concern Noul or the Score’s top level reaches 0.80, and clear when the Score’s acceptable levels do and nothing is at review; public tables and views are at most a consider, reducers can be reviews. A hardcoded-value review or consider whose value the locate Choice could not name is one level lower. Messages show the probability that set a finding’s level (a consider shows the middle-or-top mass, not the top level); notes show none. Finished plans in one directory become one finding identified by the directory, and the others become notes pointing at it. On its labeled set, no living document leaned past 0.50. Questions ask whether a change would help a reader (“would splitting it make it easier to understand?”), not how many tasks or purposes there are: Jev does not count reliably and reads “tasks” literally. Copies inside test cases are one level lower. Copies of three lines or fewer are at most a consider: in Java such a copy was as often an idiom, a pooled builder borrowed and released around one call, as a missing helper. A test that checks several unrelated behaviors is at most a note: on labeled tests, tables of inputs and browser journeys rated as high as tests that really mix behaviors. Overlapping test pairs of one subject become one consider for three or more tests only when the pairs connect them: two pairs that share no test stay two pairs (a pair of redirect tests and a pair of deny tests of get are not four overlapping tests). A review always carries a finding.
  6. Gate. --fail-on, [[scope]] levels per path and the baseline act on composed findings only. Baseline entries can carry a reason (intended, later, wrong) that survives rewrites; baseline stats counts them.

Constraints

  • Syntax supplies units, candidates, locations and eligibility (size, nesting), never verdicts; a unit below an eligibility floor is too small, never clear.
  • Keep questions atomic and literal; no thresholds, hashes or self-descriptions in uploaded state or questions.
  • Preserve raw answers, uncertainty and needs-context outcomes.
  • Version question wording (units::questions::VERSION) and composition (schema::COMPOSITION); question changes invalidate the cache by content.
  • Validate on small frozen sets through the CLI; keep results in ignored .jevgate/evaluation/.

Limits

  • Languages and frameworks: see the support table. Astro, Vue and Svelte markup is not read, only their scripts. PHP’s inline HTML is read only for its <?= … ?> echoes, and variables a page gets from the files it includes are not followed there: they are judged where those files set them.
  • Security scope: one function plus at most one hop of callers. This is not whole-program data-flow analysis. Access control reads the final state of policies, SECURITY DEFINER functions and grants across a project’s SQL files in path order, leaving out uninstall, teardown, rollback and down scripts; with --base, unchanged migrations are read for that state but not judged. It does not judge application-level authorization or dynamic SQL inside database functions.
  • Documentation scope: staleness works only from the paths, scripts, tags and deletions that Git and the manifests show; it does not compare prose with code behavior. Paraphrases that share little wording are not found as duplicates, nor are code examples that share only code; a translation is not a duplicate. Sphinx and AsciiDoc includes are not followed, and MDX expressions are not evaluated. A code comment is judged with the code next to it, not against what the whole program does, so a comment that no longer matches its code is not found. Token counts are estimates at four bytes per token.
  • Probabilities: these are model judgments, not measured accuracy. JevGate complements linters, type checkers, tests and dedicated security scanners; it does not replace them.

Changelog

Notable changes to JevGate. Versions follow Semantic Versioning; before 1.0, a minor version can change findings, flags and the report format.

Unreleased

0.18.0 - 2026-09-25

  • Documentation site: https://tech-byte-frontier.github.io/jevgate/, with guides, troubleshooting, a page on coding agents, and rules, configuration and command-line references generated from the binary. It is published with each release.
  • Homebrew: brew install tech-byte-frontier/tap/jevgate installs the release binaries on macOS and Linux, and each release updates the formula.
  • --format sarif writes a SARIF 2.1.0 log for GitHub code scanning, GitLab and editors: the findings --format github annotates, as error when they fail the gate and warning otherwise, with every rule’s question, related locations, the finding’s fingerprint and probability. Run errors and files that could not be judged are tool notifications.
  • --format gitlab writes a GitLab Code Quality report, so merge requests show the findings: the ones --format github annotates, major when they fail the gate and minor otherwise, with JevGate’s fingerprints.
  • jevgate completions SHELL prints a completion script for bash, zsh, fish, elvish or PowerShell, and jevgate man [COMMAND] a man page, both generated from the same definitions as --help. The Homebrew formula installs them.
  • Agent output is colored on a terminal: the headline by the gate’s outcome, review and consider headings, and each finding’s location. --color auto|always|never chooses, and NO_COLOR and CLICOLOR_FORCE are honored; JSON and GitHub annotations are never colored.
  • pre-commit hooks: jevgate-system runs the installed jevgate on the staged changes, and jevgate builds it from source with Rust first.
  • jevgate.schema.json is a JSON Schema of jevgate.toml, generated from the configuration types with every rule name and level, and jevgate init writes a #:schema line so editors with TOML schema support complete and check the file.

0.17.0 - 2026-09-25

  • Release binaries for Linux (x86_64 and arm64, static), macOS (Apple silicon and Intel) and Windows (x86_64), with SHA-256 checksums and build provenance (gh attestation verify). install.sh installs a checked binary on Linux and macOS, and cargo binstall jevgate finds the archives on every platform.
  • Windows: files are named with forward slashes, as on other platforms, so path rules (Next.js routes, Django modules, documentation roles) match there, and reports and baselines name a file the same way everywhere. Requests on Linux and macOS are unchanged, so cached answers stay valid.
  • The project has a security policy with private reporting, a contributing guide, this changelog, and issue templates, including one for reporting a wrong finding.

0.16.0 - 2026-09-25

Checked against 40 open-source projects in every supported stack (Rust, Python, JavaScript/TypeScript, Go, C#, Java, PHP, Ruby, Svelte, Astro, Vue, Supabase SQL, GitHub Actions), with every review and consider labeled by hand. On the first 17, reviews went from 140 to 107 and considers from 935 to 293, mostly false positives and repeated findings removed; undecided units stayed near 2%.

  • Test redundancy: two tests that check one behavior with different inputs are a note on their own; three or more linked by such pairs stay one consider. A review (“one adds nothing”) needs both tests to share the input case and expected outcome, or to read the same apart from their names; tests in different groups whose setup is not sent are at most a consider. Parameterized tests are suggested in Rust only when the crate uses rstest, test-case or yare.
  • Repeated findings are reported once: the pairs of a group of overlapping tests, and copies inside tests that a test-redundancy finding already names.
  • Shared logic: copies of four lines or fewer are at most a consider, and short copies in test code lower still; docstrings, Go’s if err != nil checks and defer cleanups, and lists of alike statements do not make a copy; variants of one example are not compared.
  • Example code (examples, demo, tutorial, docs_src, top-level samples, *.Examples.* projects, Go example_*_test.go): findings are at most notes, injection and sensitive data at most considers, and hardcoded values are not judged.
  • Skipped files: Rails (db/migrate) and Alembic (alembic/versions) migrations; scripts under assets, static or vendor that open with a whole license; shadcn components; files whose header says they were generated. SQL uninstall, teardown, rollback and down scripts are left out of the access-control state.
  • SvelteKit: server loads, form actions, endpoints and hooks are named to Jev with who calls them and cookies.set’s secure defaults. Functions in an object literal (export const actions = {…}) and functions assigned to properties (res.status = function…, Router.prototype.handle = …) are units.
  • Hardcoded values: protocol codes (HTTP status, file modes), environment variable and module names, hosts from configuration, and accounts the program creates are acceptable; module constants read from require, process.env, os.getenv, ENV, env(…) or config(…) are not values; a finding that names no value is a note.
  • Comments: Sphinx version notes and @author/@since blocks are left out; framework section banners, doc blocks above declarations and PHP file headers read as documentation; optional code with “uncomment to enable” is not code turned off; comments in projects whose README says they are written for learners are at most notes.
  • Follow-ups: an undecided cookie check is settled by what the cookie’s flags come to; the mock-only recheck names errors around stubs, fields derived from them, expected mock calls, local test servers and hooks passed as options; Rust’s random check names rand’s secure generators; Ruby’s exception check names Rails’ 404 and 500 pages. SECURITY DEFINER functions are sent with the project functions they call.
  • File organization: a consider that names no group is a note, and one whose choice leans toward two groups names both.
  • The hardcoded-values, unsafe-settings, test-redundancy, file-organization and comments rule versions are bumped; the first run re-asks hardcoded-value requests and some follow-ups once. Other cached answers stay valid.

0.15.0 - 2026-09-25

  • Functions, hardcoded values, security units and agent-instruction sections are packed into requests within runs of consecutive definitions, whose ends are chosen by each definition’s name rather than its position, as code comments have been since 0.14.1 (#3). Adding, removing or resizing one function now re-asks only the requests of its own run: in a function-dense file, one request instead of every request after it (5 of 5 before, in compose.rs). Each edit re-sends 24–47% fewer tokens.
  • Full runs send more, smaller requests: 25–106% more requests and 4–13% more billed input tokens on a first pass. With the answer cache kept between runs, as in CI, that is paid back after about 20–30 edits.
  • The first run after upgrading re-asks most function, value, security and instruction requests once (37–88% of them, depending on the project), since their grouping changed. The function-simplification, hardcoded-values, injection, sensitive-data, unsafe-settings and agent-context rule versions are bumped. Comment and other requests are unchanged, so their cached answers stay valid.
  • Shared logic no longer pairs two recursive tree walks as related steps when all they share is an early exit and the call to themselves (#4), and a function’s call to itself is no longer listed as a difference between copies. Whole copies of small walks, and walks that do work around their recursion, are still reported. On eight projects, including four tree-sitter consumers, only the mistaken pair disappeared.

0.14.1 - 2026-09-25

  • Code comments are packed into requests within runs of consecutive definitions, whose ends are chosen by each definition’s name rather than its position. Adding or removing one comment now re-asks only the requests of its own run: in a comment-dense file, one request instead of every request after it (6 of 6 before, in compose.rs). Full runs send 12–53% more comment requests but only 1–2% more tokens.
  • The first run after upgrading re-asks each comment once, since the grouping changed. No other rule’s requests change, so their cached answers stay valid.

0.14.0 - 2026-09-25

  • Code comments are judged: documentation/comments (opt-in with --rule comments or --rule documentation) asks whether each comment or docstring of application code only repeats the code next to it, holds sentences that add nothing, narrates an edit instead of describing the code as it is (“now uses…”, “split out of… to stay under the budget”), or is code turned off. Each comment is sent with the declaration it documents, the lines below it or the line it ends; license headers, tool directives and type annotations are left out. An undecided comment is asked again with its whole definition, then what kind of comment it is.
  • Comment findings are cleanups: at most a consider, documentation that only repeats its declaration at most a note, and one finding per function listing its comments by what is wrong with them, with an action to delete, shorten or rewrite. On seven projects, 44 of 57 considers were right when checked by hand, 11 debatable and 2 wrong; comment-rich codebases get none.
  • --rule documentation now also reads code files for their comments. Configs that name maintainability, as jevgate init writes, are unchanged.
  • Parsing, planning, composition and the question modules are split into smaller modules; every other rule sends byte-identical requests, so existing caches stay valid.

0.13.0 - 2026-09-24

  • Java, C#, PHP and Ruby are judged. Java and C# get the maintainability, test and security rules; PHP gets security and tests (PHPUnit, Pest), with a page script’s top-level code judged like a function; Ruby gets maintainability and tests (RSpec, Minitest), with each example’s groups, hooks, let/subject and support helpers as evidence. JUnit, TestNG, xUnit, NUnit and MSTest tests are recognized.
  • Frameworks: Django and Django REST framework (views with their routes and |safe templates, settings modules and the files that select them, CSRF exemptions, literal secrets, unsafe deserialization, error handlers), Next.js (route handlers, Server Actions, pages/api, middleware, client components, dangerouslySetInnerHTML, open redirects, raw Prisma and Drizzle queries, NEXT_PUBLIC_ secrets, next.config headers), ASP.NET Core (controller actions, minimal APIs, exception handlers, EF Core raw SQL, JWT options and signing keys written in code), Slim and Laravel, and Spring MVC tests linked to the controller method they reach.
  • Documentation rules also read MDX, reStructuredText and AsciiDoc. Undecided staleness, duplication and large-doc checks are settled by what a section treats its names as and how two sections relate, which removed most duplicate-section noise.
  • Security checks that stay undecided get a narrow follow-up question (where redirect targets come from, how markup is rendered, which origins CORS allows, what logs write, where code runs), which can only clear a check.
  • Tests: overlapping tests are grouped only when their pairs connect them; a test pair’s subject skips getters, setters and fixtures most tests call; undecided pairs and mock-only checks are asked again with the code under test.
  • Copies of three lines or fewer are at most a consider.

0.12.1 - 2026-09-24

  • 0.12.0 did not build on Rust 1.90, its declared minimum (if let guards are not stable there). 0.12.1 builds on 1.90 and later; behavior is unchanged from 0.12.0.
  • The test suite passes on macOS: test projects resolve their temp directory the way a check resolves the repository root.

0.12.0 - 2026-09-23 [YANKED]

Yanked: it did not build on Rust 1.90, its declared minimum. Use 0.12.1.

  • Libraries copied into a repository (a versioned file name such as jquery-3.6.0.js, the readable build beside a .min.js, or a license banner naming a version) are skipped as vendored, whatever their size. A single vendored file no longer leaves a check incomplete.
  • A test call counts only as its own statement, so a local test() inside library code no longer makes a file a test file, and a Test* class is a test only in files pytest collects.
  • Security: SQL identifiers quoted by doubling embedded quotes count as handled, requests sent from a web page in the user’s browser are excluded from the outbound-URL check, and an error-detail finding is first asked where the error text goes (a response, a terminal, a record) before being reported.
  • Generated-code markers are read through a whole leading comment.

These changes come from running 0.11.0 on six open-source repositories it had never seen, where most security considers were wrong.

0.11.0 - 2026-09-23

  • File organization judges large files and test files.
  • An undecided file outline is asked what kind of file it is.
  • Undecided URL and error-detail checks are settled by where values come from and where they go.
  • Each error-handler registration is resolved in its own step.

0.10.0 - 2026-09-23

  • Go code is judged.
  • More kinds of codebases are judged, and findings that were wrong on fresh repositories are dropped.
  • Headings are read when asking whether a section is a translation.

0.9.0 - 2026-09-23

  • Findings say what to do next.
  • SpacetimeDB modules are judged for access control.
  • More undecided answers are settled with follow-ups.

0.8.0 - 2026-09-23

  • jevgate baseline --merge accepts a partial check’s findings and keeps the rest.
  • Access-control checks see the claims a policy trusts and who may call a SECURITY DEFINER function.

0.7.0 - 2026-09-23

  • SQL access control (row-level security, SECURITY DEFINER functions, grants) and GitHub Actions workflows are judged.
  • Sharper test and hardcoded-value findings.
  • Request bodies are sent as compact JSON.

0.6.0 - 2026-09-23

  • The CLI is ready for CI: --format github, exit codes and the documentation for crates.io.

0.5.1 - 2026-09-23

  • Notes no longer hide undecided answers or real findings.

0.5.0 - 2026-09-23

  • Documentation rules (opt-in): agent instruction files, large project docs, finished plans, stale paths and repeated sections.

0.4.0 - 2026-09-23

  • Security rules (opt-in): injection, sensitive data and unsafe settings.
  • Rules are selected by group, gate levels are set per rule, and jevgate init writes a starting configuration.
  • The HTML report shows finding categories.

0.3.0 - 2026-09-22

  • Hardcoded values are judged: values that change between deployments, unnamed values and special cases.
  • Optional notes are separate from considers, and split findings are located.
  • Units that stay undecided are shown with the question they stayed undecided on.

0.2.1 - 2026-09-22

  • No panic when the output pipe closes.

0.2.0 - 2026-09-22

  • Focused evidence units replace one request per file: functions, file outlines, copy pairs and tests are asked about separately.
  • Exit codes are the quality gate.
  • Shared-logic findings need a located pair and a long shared span.

0.1.1 - 2026-09-18

  • README usage commands explained.

0.1.0 - 2026-09-18

  • First release: the maintainability CLI.

Rules reference

Generated from jevgate rules --format json (jevgate 0.18.0). A rule is named by its ID, its key or its group anywhere a rule is accepted: --rule, --skip-rule, --fail-on TARGET=LEVEL, [rules] and [[scope]].

RuleKeyDefaultQuestion
maintainability/file-organizationfile_organizationyesWould moving some members into a separate module (or tests into a separate test file) make the file easier to navigate and maintain?
maintainability/function-simplificationfunction_simplificationyesWould splitting the function into named functions make it easier to understand? For control flow nested four deep or four-branch chains: would flattening it help?
maintainability/shared-logicshared_logicyesDo the two sites perform the same steps for the same purpose, so one shared implementation would serve both?
maintainability/hardcoded-valueshardcoded_valuesyesDoes a value fixed in code change between deployments, need a descriptive name, or special-case one identity?
security/injectioninjectionopt-inDoes a variable that another party controls reach the text of a query, command, code, markup, file path, requested URL or redirect target, or a deserializer, without being bound, escaped or checked?
security/sensitive-datasensitive_dataopt-inDoes the function log a password, token, key or personal data, or send internal error details to a remote client? Does an error handler send clients more than the program’s own messages and codes?
security/unsafe-settingsunsafe_settingsopt-inDoes the code turn off a security check or choose a weak setting: certificate verification, password hashing, random tokens, CORS, cookies, or secrets in environment variables the build puts into browser code?
security/access-controlaccess_controlopt-inDoes a policy let every user it applies to reach other users’ rows, or trust a value users can change? Does a SECURITY DEFINER function leave search_path open or skip checking the caller? Does a grant open writes or private reads to every user? Does a public table hold users’ own data, a view return other users’ rows, or a reducer change rows its arguments choose, or admin-only settings, without checking the caller?
security/workflowsworkflowsopt-inCan a run script execute text that people outside the repository write? Does a job run pull request code while it has secrets or a write token?
tests/valuetest_valueyesDoes the test check only its mocks, recompute the expected value with the code’s own logic, assert internal details, or mix unrelated behaviors?
tests/redundancytest_redundancyyesDo the two tests check the same behavior, with different or equivalent inputs?
documentation/agent-contextagent_contextopt-inDoes a section restate what the repository’s files show, give generic advice, repeat what linters check, or record past work?
documentation/large-docslarge_docsopt-inWould splitting the document make it easier to find and maintain, or does it mainly record past work?
documentation/stalenessdoc_stalenessopt-inIs the document a plan whose work is finished, or does a section tell the reader to use a path or script that no longer exists?
documentation/duplicationdoc_duplicationopt-inDoes one section state everything the other states, or do the two give different values or instructions for the same thing?
documentation/commentscommentsopt-inDoes a comment only repeat its code, hold sentences that add nothing, narrate an edit instead of the code as it is, or hold code turned off?

Maintainability

On by default.

maintainability/file-organization

Question: Would moving some members into a separate module (or tests into a separate test file) make the file easier to navigate and maintain?

  • Key: file_organization · Version: 18
  • Looks at: application and test files with two or more members and 100 or more lines of member code
  • Evidence unit: file outline: member signatures and sizes, callers that import the file, and groups; a test file lists its cases with their suites and subjects; no bodies
  • Acceptable: One algorithm, one type and its helpers, one feature, or the tests of one subject

maintainability/function-simplification

Question: Would splitting the function into named functions make it easier to understand? For control flow nested four deep or four-branch chains: would flattening it help?

  • Key: function_simplification · Version: 14
  • Looks at: functions and methods with bodies of five or more lines
  • Evidence unit: one function’s source
  • Acceptable: One job whose steps belong together or already call named functions

maintainability/shared-logic

Question: Do the two sites perform the same steps for the same purpose, so one shared implementation would serve both?

  • Key: shared_logic · Version: 19
  • Looks at: renamed or exact copies of two or more statements across selected files and explicit context
  • Evidence unit: one representative pair per clone group, with its renamed names and values
  • Acceptable: Different work that only looks alike, or repetition the behavior requires

maintainability/hardcoded-values

Question: Does a value fixed in code change between deployments, need a descriptive name, or special-case one identity?

  • Key: hardcoded_values · Version: 4
  • Looks at: application functions and module constants that use literal values other than 0, 1, 2 or one-character strings
  • Evidence unit: one function’s source with its literal values, or a file’s module-level constants
  • Acceptable: Messages, formats, protocol names and values whose meaning the code around them makes clear

Security

Opt-in: --rule security, or a level in [rules].

security/injection

Question: Does a variable that another party controls reach the text of a query, command, code, markup, file path, requested URL or redirect target, or a deserializer, without being bound, escaped or checked?

  • Key: injection · Version: 6
  • Looks at: application functions with calls, built text or field assignments, and PHP page scripts
  • Evidence unit: one function’s source or a PHP file’s top-level code; then its statements as sites, and up to three callers when the origin of its values is unclear
  • Acceptable: Bound query parameters, argument lists, escaping templates, and values the program fixes or checks

security/sensitive-data

Question: Does the function log a password, token, key or personal data, or send internal error details to a remote client? Does an error handler send clients more than the program’s own messages and codes?

  • Key: sensitive_data · Version: 5
  • Looks at: application functions with calls, built text or field assignments, and PHP page scripts
  • Evidence unit: one function’s source or a PHP file’s top-level code; then its statements as sites and the message of each error it creates; one question per registered web error handler
  • Acceptable: Logging record ids and messages; generic error responses with details kept in server logs

security/unsafe-settings

Question: Does the code turn off a security check or choose a weak setting: certificate verification, password hashing, random tokens, CORS, cookies, or secrets in environment variables the build puts into browser code?

  • Key: unsafe_settings · Version: 4
  • Looks at: application functions, each file’s top-level statements that call something, the settings objects of next.config files, and PHP page scripts
  • Evidence unit: one function’s source or the file’s setup statements; then their statements as sites
  • Acceptable: MD5 for cache keys, non-cryptographic random for shuffling, secure defaults

security/access-control

Question: Does a policy let every user it applies to reach other users’ rows, or trust a value users can change? Does a SECURITY DEFINER function leave search_path open or skip checking the caller? Does a grant open writes or private reads to every user? Does a public table hold users’ own data, a view return other users’ rows, or a reducer change rows its arguments choose, or admin-only settings, without checking the caller?

  • Key: access_control · Version: 2
  • Looks at: SQL files: row-level security policies, SECURITY DEFINER functions and grants, in their final state across migrations; SpacetimeDB TypeScript modules: public tables, views and reducers
  • Evidence unit: one policy with its table and the functions it calls, one SECURITY DEFINER function, or one grant; one SpacetimeDB public table with its user columns, or one view or reducer with the functions it calls and the framework version
  • Acceptable: Policies tied to the user, account or membership; role checks; restrictive policies; public data; grants narrowed by row-level security; reducers that check the caller through ctx.sender, the module owner, an admin or a trusted service identity, or run only on a schedule

security/workflows

Question: Can a run script execute text that people outside the repository write? Does a job run pull request code while it has secrets or a write token?

  • Key: workflows · Version: 1
  • Looks at: GitHub Actions jobs in .github/workflows
  • Evidence unit: one job with the workflow’s triggers and permissions, and the ${{ }} expressions in its run scripts
  • Acceptable: Untrusted text passed through env variables; pull_request workflows; jobs that run only the base branch’s code

Tests

On by default; judged with --include-tests or include_tests = true.

tests/value

Question: Does the test check only its mocks, recompute the expected value with the code’s own logic, assert internal details, or mix unrelated behaviors?

  • Key: test_value · Version: 5 · Needs tests: yes
  • Looks at: test cases, with –include-tests
  • Evidence unit: one test’s source and the signatures it calls
  • Acceptable: A test that checks a result or effect a caller can observe

tests/redundancy

Question: Do the two tests check the same behavior, with different or equivalent inputs?

  • Key: test_redundancy · Version: 3 · Needs tests: yes
  • Looks at: similar tests of one function, with –include-tests
  • Evidence unit: one candidate pair of tests and their shared subject
  • Acceptable: Tests of different behaviors of one function

Documentation

Opt-in: --rule documentation, or a level in [rules].

documentation/agent-context

Question: Does a section restate what the repository’s files show, give generic advice, repeat what linters check, or record past work?

  • Key: agent_context · Version: 3
  • Looks at: agent instruction files that a harness loads: AGENTS.md, CLAUDE.md, GEMINI.md, and Claude, Cursor, Copilot, Windsurf and Cline rules
  • Evidence unit: one file’s heading sections, with the repository’s manifests, linters and directories
  • Acceptable: Project-specific commands, constraints, decisions and workflows the code does not show

documentation/large-docs

Question: Would splitting the document make it easier to find and maintain, or does it mainly record past work?

  • Key: large_docs · Version: 2
  • Looks at: project Markdown of 300 or more lines: root files, README and CONTRIBUTING anywhere, docs/ and doc/ (read even when ignored)
  • Evidence unit: one document’s headings in order, without its text
  • Acceptable: One long guide, reference or concept, and living procedures

documentation/staleness

Question: Is the document a plan whose work is finished, or does a section tell the reader to use a path or script that no longer exists?

  • Key: doc_staleness · Version: 3
  • Looks at: agent instruction files and project docs that name paths or scripts the repository lacks, or a released version
  • Evidence unit: a document’s headings when Git shows its release tagged or its paths deleted; then each section naming missing paths or scripts, with what Git shows about them
  • Acceptable: Outputs a command writes, local or ignored files, examples, and paths named as removed

documentation/duplication

Question: Does one section state everything the other states, or do the two give different values or instructions for the same thing?

  • Key: doc_duplication · Version: 3
  • Looks at: sections of different agent instruction files and project docs that share much of their wording
  • Evidence unit: one candidate pair of sections
  • Acceptable: Sections on the same subject where each adds something

documentation/comments

Question: Does a comment only repeat its code, hold sentences that add nothing, narrate an edit instead of the code as it is, or hold code turned off?

  • Key: comments · Version: 2
  • Looks at: comments and docstrings of application code, except license headers, tool directives and type annotations
  • Evidence unit: one comment with the code it is about: the declaration it documents, the lines below it or the line it ends; then the whole definition it sits in
  • Acceptable: Reasons, constraints, caveats, references, and documentation of what a definition returns or guarantees beyond its signature

Decision policy

Answers become findings in code, at the same thresholds for every rule:

SettingValue
clear_probability0.8
consider_leading_probability0.5
consider_probability0.8
deep_nesting4
location_probability0.65
long_branch_chain4
min_body_lines5
min_clone_bytes120
min_clone_statements3
min_file_lines100
review_probability0.8

Configuration reference

Generated from jevgate.schema.json, which is generated from the configuration types. Configuration explains how the keys work together.

KeyTypeMeaning
cache_ttl_secsintegerCache lifetime in seconds for the jev-latest and jev-preview aliases; pinned versions never expire. Default: 3600.
concurrencyintegerCeiling on simultaneous requests (1-8). Default: 6.
contextlist of stringsFiles always sent as related evidence, like --context.
fail_onlist of stringsThe level for rules without their own, like --fail-on. Default: [“review”].
generatedlist of stringsGlobs of generated files, which are skipped, in addition to the built-in names.
include_testsbooleanJudge tests, like --include-tests. Default: false.
max_context_bytesintegerCeiling on context bytes per request. Default: 32768.
max_file_bytesintegerFiles larger than this are reported as needs-context, never truncated. Default: 262144.
max_requestsintegerCeiling on API attempts per invocation; flags can only lower it. Default: unlimited.
modelstringTypeSafe model; a pinned version keeps results repeatable. --model overrides it.
rulesrules list or tableA list selects rules; a table gives each group or rule a level. Default: the default group.
scopelist of tablesGate levels for the files some paths match, such as report-only tooling.
testslist of stringsGlobs of additional test files.
upload_allowlist of stringsGlobs of the paths that may be uploaded, including instruction files and context. Default: every path.
upload_denylist of stringsGlobs never uploaded, even when allowed.

[[scope]]

[[scope]]: gate levels for the files paths match. fail_on applies to every rule there, and rules to single rules or groups. The last scope that matches a file and addresses a rule wins; other files and rules keep the levels set outside scopes.

KeyTypeMeaning
fail_onlist of stringsThe level for every rule in these files.
pathslist of stringsGlobs of the files this scope applies to.
rulesobjectLevels of single rules or groups in these files; off is not accepted (use upload_deny).

Levels

review, consider, uncertain, report, none, off. off is accepted in [rules] only.

Rule names

maintainability/file-organization, file_organization, maintainability/function-simplification, function_simplification, maintainability/shared-logic, shared_logic, maintainability/hardcoded-values, hardcoded_values, security/injection, injection, security/sensitive-data, sensitive_data, security/unsafe-settings, unsafe_settings, security/access-control, access_control, security/workflows, workflows, tests/value, test_value, tests/redundancy, test_redundancy, documentation/agent-context, agent_context, documentation/large-docs, large_docs, documentation/staleness, doc_staleness, documentation/duplication, doc_duplication, documentation/comments, comments, maintainability, security, tests, documentation, default, all.

Command-line reference

Generated from --help (jevgate 0.18.0). jevgate man COMMAND prints the same text as a man page.

jevgate

Code review gate that asks TypeSafe Jev small, literal questions about your code

JevGate parses the repository locally and builds small evidence units: a function, a file outline, a pair of copies, a test, a documentation section. It asks TypeSafe Jev short, typed questions about each one, and code, not a chat model, composes the answers into findings. Each finding has a location, a probability and a next step, and undecided answers are reported as uncertain instead of hidden.

Rule groups: maintainability (on by default), tests (with --include-tests), and the opt-in security and documentation groups.

Usage: jevgate <COMMAND>

Commands:
  auth         Save, inspect or remove your TypeSafe API credential
  check        Review code with TypeSafe Jev; exit 1 when the gate fails, 2 when the run is incomplete
  baseline     Accept the findings of the last complete check, so later checks fail only on new ones
  rules        List every rule with its group, default and the question it asks
  init         Write a commented jevgate.toml for this repository (offline)
  completions  Print a shell completion script (offline)
  man          Print a man page in roff (offline)
  serve        Serve the latest report as read-only JSON on localhost (run alongside `check --watch`)
  help         Print this message or the help of the given subcommand(s)

Options:
  -h, --help
          Print help (see a summary with '-h')

  -V, --version
          Print version

Workflow:
  jevgate init                              Write jevgate.toml: upload scope, rules and gate
  jevgate auth login                        Save an API key (or set TYPESAFE_API_KEY)
  jevgate check --dry-run --show-requests   Print every request body; no key, no network
  jevgate check                             Review and apply the gate
  jevgate baseline                          Accept current findings; later checks fail only on new ones
  jevgate baseline --merge                  Accept a partial check's findings, keeping the rest
  jevgate baseline mark wrong PATH[:LINE]   Record why a finding was accepted; `baseline stats` counts them

For agents and CI:
  jevgate check --base origin/main                   Only files changed since a revision
  jevgate check --base origin/main --format json     The full report, raw probabilities included
  jevgate check --base origin/main --format github   Annotations and a job summary on GitHub
  jevgate rules --format json                        Every rule and the question it asks

Exit codes:
  0      Gate passed, or no supported file changed since --base
  1      Gate failed
  2      Run incomplete (no key, provider rejection, request budget reached), invalid
         configuration or invalid usage
  128+N  Interrupted by signal N

Files (at the repository root):
  jevgate.toml            Configuration; `jevgate init` writes a commented one
  jevgate-baseline.json   Accepted findings; commit it
  .jevgate/cache/         Answers by request hash; safe to restore and save in CI
  .jevgate/latest.json    The last report, the same JSON as --format json
  .jevgate/report.html    HTML dashboard, with --report

Environment:
  TYPESAFE_API_KEY          API key; takes precedence over every saved credential
  JEVGATE_CREDENTIAL_STORE  Where `auth login` saves: auto, keyring or file
  JEVGATE_CONFIG_DIR        Absolute directory for file-stored credentials
  CI                        When set, --report writes the dashboard without opening a browser
  NO_COLOR, CLICOLOR_FORCE  Turn agent output color off or on where --color is auto

`jevgate <command> --help` explains each command; -h prints a summary. `jevgate completions SHELL`
and `jevgate man [COMMAND]` print shell completions and man pages.

jevgate auth

Save, inspect or remove your TypeSafe API credential

A check finds its key in this order: the TYPESAFE_API_KEY environment variable, then the file named by `check --env-file` (by default the repository's `.env`), then the key saved by `jevgate auth login`. In CI, set TYPESAFE_API_KEY from a secret; nothing needs to be saved.

Usage: jevgate auth <COMMAND>

Commands:
  login   Validate a TypeSafe API key and save it for every repository
  status  Show which credential a check would use; exit 0 when it works, 2 otherwise
  logout  Remove saved credentials; TYPESAFE_API_KEY and repository .env files are left alone
  help    Print this message or the help of the given subcommand(s)

Options:
  -h, --help
          Print help (see a summary with '-h')

Examples:
  jevgate auth login                               Hidden prompt; saved in the OS credential store
  jevgate auth login --with-key < key.txt          Read the key from stdin
  jevgate auth status                              Show which key a check would use and verify it
  jevgate auth status --offline --json             Same, without contacting TypeSafe
  jevgate auth logout

jevgate check

Review code with TypeSafe Jev; exit 1 when the gate fails, 2 when the run is incomplete

Parses the selected files locally, sends small evidence units (a function, a file outline, a pair of copies, a test, a documentation section, a code comment) with short questions, and composes the answers into findings. Unchanged units are answered from `.jevgate/cache`, so a re-run only pays for what changed. Every run writes the full report to `.jevgate/latest.json`, whatever the output format.

Findings are `review` (act on it), `consider` (worth a look) or `note` (optional; never fails the gate). A file whose answers stay undecided is `uncertain`; one that cannot be judged without more evidence is `needs-context`.

Settings resolve in this order: flags, then `jevgate.toml`, then defaults. Upload patterns and budgets in the file are ceilings that flags can only narrow.

Usage: jevgate check [OPTIONS] [PATHS]...

Arguments:
  [PATHS]...
          Files or directories to review [default: discovered application source]
          
          Without paths, JevGate walks the repository (respecting .gitignore) and selects application source in Rust, Python, JavaScript, TypeScript, Go, C#, Ruby, PHP and Java. Tests, generated code and vendored files are classified and skipped with a reason. `upload_allow`/`upload_deny` in jevgate.toml still bound what is sent.

Options:
  -h, --help
          Print help (see a summary with '-h')

Scope:
      --base <REVISION>
          Review only files changed against this Git revision (commit, branch or tag)
          
          Includes committed, staged, unstaged and untracked changes. Deleted files are listed in the report. The revision must exist locally: in CI, check out with full history (for example `fetch-depth: 0`). When no supported file changed, the run is complete and exits 0.

      --include-tests
          Also judge tests: test value, redundancy, and shared logic among tests
          
          Without it, test files are judged only for file organization. Also set by `include_tests = true` in jevgate.toml.

      --context <PATH>
          Related file sent as evidence for shared logic, callers and test subjects (repeatable)
          
          The file must be inside the repository and is sent only with the requests it informs. Also set by `context` in jevgate.toml.

      --source-extension <EXT>
          Also review this file extension as text (repeatable, without the dot)

      --config <FILE>
          Read this configuration instead of <repository root>/jevgate.toml
          
          The repository root is still found from the working directory. Use it in CI to apply a reviewed policy that the change under review cannot edit.

Rules and gate:
      --rule <RULE>
          Select a rule ID, key or group (repeatable) [default: the `default` group]
          
          Groups: maintainability, tests, security, documentation, default (every rule on by default) and all. Naming any rule replaces the configured selection, so add `--rule default` to keep the defaults. Test rules also need --include-tests. `jevgate rules` lists every rule.

      --skip-rule <RULE>
          Deselect a rule ID, key or group (repeatable); applied after --rule and jevgate.toml

      --fail-on <[TARGET=]LEVEL>
          What fails the gate: LEVEL for every rule, or TARGET=LEVEL (repeatable) [default: review]
          
          LEVEL is review, consider (also fails on review), uncertain, or none (advisory; `report` is accepted as a synonym). TARGET is a rule ID, key or group, for example `security=consider`; the most specific target wins. Flags replace `fail_on` and `[rules]` levels from jevgate.toml for the rules they address. Notes and baselined findings never fail the gate. An incomplete run exits 2 regardless of the gate.

Output:
      --format <FORMAT>
          Output format [default: agent; jsonl with --watch; json with --show-requests]

          Possible values:
          - agent:  Ranked findings with locations and next steps, for people and coding agents
          - json:   The full report as one pretty-printed JSON document
          - jsonl:  One compact JSON report per line; one per evaluation while watching
          - github: GitHub Actions annotations and a job summary, then the agent text
          - sarif:  A SARIF 2.1.0 log, for GitHub code scanning and other SARIF readers
          - gitlab: A GitLab Code Quality report, for merge request widgets

      --color <WHEN>
          Color agent output: auto, always or never
          
          `auto` colors a terminal unless NO_COLOR is set, and any output when CLICOLOR_FORCE is set; on Windows, only Windows Terminal and terminals that set TERM count. Other formats are never colored.

          Possible values:
          - auto:   On a terminal, unless NO_COLOR is set; CLICOLOR_FORCE turns it on elsewhere
          - always
          - never
          
          [default: auto]

      --verbose
          Show optional notes, every consider finding and per-file detail in agent output

      --report
          Also write .jevgate/report.html and open it in a browser (not opened when CI is set)

      --dry-run
          List the selected files, rules and planned requests without credentials, network or writes
          
          Planned first-pass requests the cache already answers are counted apart and cost nothing; follow-ups depend on the answers and are not known.

      --show-requests
          With --dry-run, include every initial request body (the exact source and questions)
          
          Follow-up requests depend on answers and are not known in advance.

Model, budgets and cache:
      --model <MODEL>
          TypeSafe model; pin a version for repeatable results [default: jev-1.13.0]
          
          Also set by `model` in jevgate.toml. Answers are cached per model, so changing it re-asks every unit.

      --max-requests <N>
          Stop after this many API attempts in this invocation, watch updates included
          
          Reaching the budget leaves the run incomplete (exit 2) rather than passing on partial evidence. `max_requests` in jevgate.toml is a ceiling this flag can only lower.

      --concurrency <N>
          Maximum simultaneous TypeSafe requests (1-8)
          
          [default: 6]

      --max-file-bytes <BYTES>
          Per-file read limit; a larger file is reported as needs-context, never truncated
          
          [default: 262144]

      --max-context-bytes <BYTES>
          Total bytes of --context files per request; context is never truncated
          
          [default: 32768]

      --cache-ttl-secs <SECONDS>
          Cache lifetime for the jev-latest and jev-preview aliases [default: 3600]
          
          Answers from a pinned model version never expire. Also set by `cache_ttl_secs` in jevgate.toml.

      --refresh
          Ignore cached answers for this invocation and ask again

      --cache-only
          Use cached answers only and never contact TypeSafe; unanswered units leave the run incomplete

      --env-file <FILE>
          Credential file holding TYPESAFE_API_KEY [default: <repository root>/.env]
          
          The TYPESAFE_API_KEY environment variable takes precedence.

Watch:
      --watch
          Keep running and re-check the selected files after each save
          
          Writes .jevgate/latest.json after every evaluation and prints one JSON report per line. Pair with `jevgate serve` or --report.

      --debounce-ms <MS>
          Wait this long after the last save before evaluating
          
          [default: 500]

      --poll-ms <MS>
          How often to look for saves
          
          [default: 250]

Examples:
  jevgate check                                    Discovered application source, default rules
  jevgate check src/billing --verbose              One directory, with notes and per-file detail
  jevgate check --base origin/main --format json   Changed files only, machine-readable
  jevgate check --rule default --rule security     Add the opt-in security group
  jevgate check --rule documentation               Agent instruction files, project docs and code comments
  jevgate check --rule comments                    Only code comments: repeated code, filler, narrated edits
  jevgate check --include-tests                    Also judge test value and redundancy
  jevgate check --fail-on none                     Advisory: never exits 1; exits 2 when incomplete
  jevgate check --fail-on review --fail-on security=consider
  jevgate check --dry-run --show-requests          Exactly what would be uploaded, offline
  jevgate check --cache-only                       Replay cached answers; never contact TypeSafe

Reading the JSON report (--format json or .jevgate/latest.json):
  complete           false when any selected file was not judged; the exit code is then 2
  gate               passed, reasons, new_findings, baselined_findings
  files[].status     clear, note, consider, review, uncertain, needs-context,
                     not-applicable, skipped or error
  files[].findings   rule, strength, line, message, action, locations,
                     concern_probability, fingerprint, baselined
  files[].dimensions per rule: status, unit counts and the units left undecided
  files[].judgments  every raw answer, first pass and follow-ups
  api_requests, paid_input_tokens, paid_output_tokens   this run's usage

jevgate baseline

Accept the findings of the last complete check, so later checks fail only on new ones

Writes `jevgate-baseline.json` at the repository root from `.jevgate/latest.json`. Commit the file. Findings are matched by a fingerprint of rule, path, unit and evidence, so unrelated edits keep them accepted. Offline: no source is read or sent.

Each accepted finding can record why it was accepted: `intended` (right about the code, which is meant to be this way), `later` (right, to fix later) or `wrong` (the finding is mistaken). `baseline stats` turns these reasons into each rule's rate of wrong findings.

Usage: jevgate baseline [OPTIONS]
       jevgate baseline <COMMAND>

Commands:
  mark   Record why accepted findings were accepted
  stats  Count accepted findings by rule and reason, with each rule's rate of wrong findings
  help   Print this message or the help of the given subcommand(s)

Options:
      --merge
          Keep earlier accepted findings for files the last check did not cover
          
          Without it, the file is replaced, so after a `--base` or path-limited check the findings accepted for every other file are dropped. With it, entries for files the check covered, or that were deleted, are replaced by what the check found, and the rest are kept.

      --reason <REASON>
          Record this reason on findings accepted now without one
          
          Findings already accepted keep the reason they have.

          Possible values:
          - intended: The finding is right; the code is meant to be this way
          - later:    The finding is right; it will be fixed later
          - wrong:    The finding is mistaken

  -h, --help
          Print help (see a summary with '-h')

Examples:
  jevgate baseline                                  Accept every finding of the last check
  jevgate baseline --merge --reason later           Accept a partial check's findings as known debt
  jevgate baseline mark wrong src/api/search.ts:41  A mistaken finding
  jevgate baseline mark intended scripts --rule maintainability/hardcoded-values
  jevgate baseline stats                            Wrong findings per rule

jevgate rules

List every rule with its group, default and the question it asks

A rule is named by its ID (`maintainability/shared-logic`), its key (`shared_logic`) or its group (`maintainability`, `tests`, `security`, `documentation`, plus `default` and `all`) anywhere a rule is accepted: `--rule`, `--skip-rule`, `--fail-on TARGET=LEVEL` and `[rules]`.

Usage: jevgate rules [OPTIONS]

Options:
      --format <FORMAT>
          `table` for people; `json` adds scope, evidence unit, version and decision policy
          
          [default: table]
          [possible values: table, json]

  -h, --help
          Print help (see a summary with '-h')

jevgate init

Write a commented jevgate.toml for this repository (offline)

Limits uploads to the detected source and test directories and to agent instruction files, denies credential files, and lists every rule group with its gate level. Review the file before the first paid check.

Usage: jevgate init [OPTIONS]

Options:
      --force
          Replace an existing jevgate.toml

  -h, --help
          Print help (see a summary with '-h')

jevgate completions

Print a shell completion script (offline)

Usage: jevgate completions <SHELL>

Arguments:
  <SHELL>
          bash, zsh, fish, elvish or powershell
          
          [possible values: bash, elvish, fish, powershell, zsh]

Options:
  -h, --help
          Print help (see a summary with '-h')

Examples:
  jevgate completions bash > ~/.local/share/bash-completion/completions/jevgate
  jevgate completions zsh > "${fpath[1]}/_jevgate"
  jevgate completions fish > ~/.config/fish/completions/jevgate.fish
  jevgate completions powershell >> $PROFILE

jevgate man

Print a man page in roff (offline)

Without a command, the page for `jevgate`; with one, the page for that command, such as `jevgate-check`.

Usage: jevgate man [COMMAND]

Arguments:
  [COMMAND]
          A command: auth, check, baseline, rules, init, serve or completions

Options:
  -h, --help
          Print help (see a summary with '-h')

Examples:
  jevgate man > ~/.local/share/man/man1/jevgate.1
  jevgate man check > ~/.local/share/man/man1/jevgate-check.1
  jevgate man check | man -l -                  Read a page without installing it (man-db)

jevgate serve

Serve the latest report as read-only JSON on localhost (run alongside `check --watch`)

Answers GET requests from local tools, never from a browser page: `/snapshot` (the full report), `/evidence` (findings and context per file), `/context-requests` (evidence a file still needs) and `/changes?since=GENERATION` (what changed since a report generation).

Usage: jevgate serve [OPTIONS]

Options:
      --port <PORT>
          Local port to listen on
          
          [default: 47831]

  -h, --help
          Print help (see a summary with '-h')