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
- Install and follow the quick start: a dry run shows exactly what would be uploaded, free and offline.
- What it finds and the rules reference describe every rule and the question it asks.
- Continuous integration sets JevGate up on pull requests with the GitHub Action, pre-commit or any other CI.
- How it works explains the evidence units and how code, not a chat model, turns answers into findings.
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)
| Rule | Example finding |
|---|---|
| File organization | This 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 simplification | sync_accounts mixes separate jobs in long blocks; lines 40–71 would be most useful as their own function. |
| Shared logic | createInvoice and createReceipt perform the same steps; one shared implementation would serve both. |
| Hardcoded values | Module 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)
| Rule | Example finding |
|---|---|
| Test value | test_total computes its expected value with the logic it tests. |
| Test redundancy | Three 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)
| Rule | Covers |
|---|---|
| Injection | Variables 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 data | Passwords, 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 settings | Certificate 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 control | SQL 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 |
| Workflows | GitHub 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)
| Rule | Example finding |
|---|---|
| Agent context | A section of CLAUDE.md only lists the scripts package.json already shows, and six harnesses load it at the start of every session. |
| Large docs | docs/operations/runbook.md holds several unrelated subjects; docs/plans/v0.2-plan.md mainly records finished work. |
| Staleness | docs/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. |
| Duplication | Section Release Workflow of CLAUDE.md states everything section Release of README.md states. |
| Code comments | save_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 file | Extensions | Maintainability | Tests | Security | Documentation |
|---|---|---|---|---|---|
| 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 platform | What JevGate understands |
|---|---|
| Hono, Express, Fastify, Koa | Route handlers written inline (app.post('/pages', async (c) => …)); error handlers (app.onError, setErrorHandler, four-parameter Express middleware) |
| NestJS | Exception 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 |
| SvelteKit | Server 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, FastAPI | Error handlers (@app.errorhandler, @app.exception_handler) |
| Django, Django REST framework | Views 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, Laravel | A 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, Rocket | Error 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, AsciiDoc | Section 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 Core | Controller 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 projects | Test 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 PostgreSQL | Row-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 |
| React | JSX components and hooks as functions; text shown as a JSX child is not treated as markup injection |
| RSpec, Minitest | Examples 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 DSLs | Methods 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 examples | Copies 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 classes | Methods 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 MVC | A 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 compilers | Minified and compiled output (a source map reference, very long lines) is skipped as generated |
| Copied libraries | A 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 |
| Migrations | Directories 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:
--basereviews 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 withfetch-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"]injevgate.tomlor--fail-on nonereports 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_requestscaps the API attempts of one run. Reaching it leaves the run incomplete instead of passing on partial evidence.--dry-runcounts 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 code | Meaning for the agent |
|---|---|
| 0 | The gate passed; consider findings may still be worth a look |
| 1 | The gate failed: act on the findings listed |
| 2 | The 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:
| Path | What it returns |
|---|---|
/snapshot | The full latest report |
/evidence | Findings and context per file |
/context-requests | Evidence a file still needs |
/changes?since=GENERATION | What 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
| Key | Default | Meaning |
|---|---|---|
upload_allow | every path | Globs of the paths that may be uploaded, including instruction files and context |
upload_deny | none | Globs never uploaded, even when allowed |
generated | built-in names | Globs of generated files, which are skipped |
tests | built-in conventions | Globs of additional test files |
context | none | Files always sent as related evidence, like --context |
rules | the default group | A list selects rules. A table gives each group or rule a level: review, consider, uncertain, report (judge, never fail) or off |
[[scope]] | none | paths (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_tests | false | Judge tests, like --include-tests |
model | jev-1.13.0 | TypeSafe model; a pinned version keeps results repeatable |
cache_ttl_secs | 3600 | Cache lifetime for the jev-latest and jev-preview aliases; pinned versions never expire |
max_requests | unlimited | Ceiling on API attempts per invocation |
concurrency | 6 | Ceiling on simultaneous requests (1–8) |
max_file_bytes | 262144 | Files larger than this are reported as needs-context, never truncated; generated and vendored files are skipped instead |
max_context_bytes | 32768 | Ceiling 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
| Format | Use |
|---|---|
agent (default) | Ranked findings with locations and next steps, for people and coding agents |
json | The full report: every file, finding, raw answer and probability, gate and usage |
jsonl | One compact report per line; one per evaluation with --watch |
github | GitHub Actions annotations and job summary, then the agent text |
sarif | A SARIF 2.1.0 log for GitHub code scanning and other SARIF readers: the findings the annotations show, error when they fail the gate |
gitlab | A 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 code | Meaning |
|---|---|
| 0 | Gate passed, or no supported file changed since --base |
| 1 | Gate failed |
| 2 | Run 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_allowandupload_deny.--dry-run --show-requestsprints 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_KEYfrom the environment, then--env-fileor the repository’s.env, then the key saved byjevgate 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_KEYfrom the environment, then--env-fileor the repository’s.env, then the key saved byjevgate auth login.jevgate auth statusshows 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). --baseneeds the history back to the fork point. Check out withfetch-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 withupload_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-requestsmax_requestsor--max-requestscapped the run. Raise it, or check fewer files with--baseor paths;--dry-runestimates what a run will ask.Another JevGate session owns latest.json- Another
checkor--watchis 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
- 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. - 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?”
- 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.
- Composition in code. Answers become
review,consider,note,clearoruncertainat 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
- 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…andFuzz…functions taking*testing.T,.Bor.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 likeShop.Tests;.Designer.cs,.g.csand.g.i.csfiles 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’stest(:unit) dois not one) and classes whose superclass ends inTest,TestCaseorSpec(Minitest::Test,ActiveSupport::TestCase) withtest_*methods ortest "…" doblocks;*_spec.rbfiles and Ruby files underspec/are test files. PHP tests are thetest…,@testor#[Test]methods of a class extending a…TestCase, and Pesttest(…)/it(…)calls;…Test.phpfiles are test paths. A Java class is a test class, whole, when it holds a method annotated@Test,@ParameterizedTestor another test annotation (a composed one whose name ends inTestincluded), a JUnit lifecycle method such as@BeforeEach, a nested test class, or extends JUnit 3’sTestCase;…Test,…Tests,…TestCaseand…ITfiles are test paths. A test that sends a request to a literal path (MockMvc’sget("/owners/{id}"), RestTemplate’sRequestEntity.get(…)) calls the Spring controller method whose@GetMapping,@PostMappingor@RequestMappingroute serves it, under its class’s prefix, by full name, the most literal route winning: controllers share method names such asinitCreationForm, 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. - 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 enclosingdescribe, 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@Nestedclasses 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 madesetBirthDatethe subject of validator tests and grouped unrelated pairs under a sharedcreate_userfixture. 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. JavaequalsandhashCodeoverrides 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. - 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 ofcompose.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 asfunctions[2].source; group IDs are Choice options. Stage and freshness hashes stay in localjevgatemetadata that is not uploaded. - 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,aroundandsetuphooks,let!, and theletandsubjectdefinitions 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 andmock_approutes 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 (eachandeach_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:1000meant 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 forevalof 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@Catchfilters, axumIntoResponseand actix-webResponseErrorfor an error type, Rocket catchers, ASP.NET CoreUseExceptionHandlerlambdas, exception filters,IExceptionHandlerand middleware classes whoseInvokecatches what the pipeline throws; a path given toUseExceptionHandlerre-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…Errorclasses (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 (FromSqlInterpolatedbinds,ServerCertificateCustomValidationCallbackreturning true skips certificates,ValidateIssuerchecks 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 theconstandstatic readonlyfields 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 onnext, a file’s path names its role (app/**/route.ts,pages/api/**,middleware.tsorproxy.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 asframework, 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)andNextResponse.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$queryRawUnsafeandsql.rawas not, the markup check namesdangerouslySetInnerHTML(also a site), and the unsafe settings ask whether a secret comes from a variable the build puts into browser code (NEXT_PUBLIC_). Anext.configfile’s setup is every top-level statement that holds an object, with its innermost objects as sites, sinceheaders()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_safeand 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_exemptand literal secret keys, andrequest.METAor 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_MODULEin a Dockerfile, CI ormanage.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; asetdefaultinmanage.pyorwsgi.pyis 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 developmentDEBUG = Trueor a signed webhook’scsrf_exemptweak 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 = …), middlewareprocess_exceptionand Django REST framework’sEXCEPTION_HANDLERare 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_execand 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 withechois its response, not a log; a page that only callsgenerateSessionToken()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 returningFileUploadExceptionmessages were error-detail reviews); and only variables placed into a query or path count for the origin, not an uploaded file’s contents.unserializeand uploaded file names are PHP checks, asked only of source that namesunserializeor 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 passis_numeric, clear it; values with some characters stripped do not), and where its paths come from (constants and a file name aswitchpicks clear it; names stored in a database or file are their own option). A page that reads a request also joins ids converted withintvaland 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 Kiroinclusion, a Roo Code mode folder, Junie’s precedence of its ownAGENTS.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; brstocksCLAUDE.mdwent 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 ofDo Notrules 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, sobackend/app/frontend/covers a build output), or one the section writes out for a code block (itsfilename=, 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, sincepnpm tsxruns one. A missing name whose one tracked namesake sits under the document’s directory, such as.tsxfor.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’spnpm drizzle-kitstayed 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 astreamTextcall 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- 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 ofcompose.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:paramentries of untyped functions stopped reading as filler whilenumerator: The numerator value.still does. A flag’s meaning ("-x", # Extract audio), a unit (// 16pxbeside1rem) and a category heading (// Fixedabove 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 theirsupabaseormigrationsdirectory, 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 importspacetimedb/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’spackage.jsonorCargo.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 insiderunscripts, 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 onpull_request_targetorworkflow_runare asked whether they run pull request code with secrets.
- 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 (
- 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 opensearch_pathare 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 ofgetare not four overlapping tests). A review always carries a finding. - 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 statscounts 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/jevgateinstalls the release binaries on macOS and Linux, and each release updates the formula. --format sarifwrites a SARIF 2.1.0 log for GitHub code scanning, GitLab and editors: the findings--format githubannotates, aserrorwhen they fail the gate andwarningotherwise, 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 gitlabwrites a GitLab Code Quality report, so merge requests show the findings: the ones--format githubannotates,majorwhen they fail the gate andminorotherwise, with JevGate’s fingerprints.jevgate completions SHELLprints a completion script for bash, zsh, fish, elvish or PowerShell, andjevgate 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|neverchooses, andNO_COLORandCLICOLOR_FORCEare honored; JSON and GitHub annotations are never colored. - pre-commit hooks:
jevgate-systemruns the installedjevgateon the staged changes, andjevgatebuilds it from source with Rust first. jevgate.schema.jsonis a JSON Schema ofjevgate.toml, generated from the configuration types with every rule name and level, andjevgate initwrites a#:schemaline 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.shinstalls a checked binary on Linux and macOS, andcargo binstall jevgatefinds 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 != nilchecks anddefercleanups, 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-levelsamples,*.Examples.*projects, Goexample_*_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 underassets,staticorvendorthat 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(…)orconfig(…)are not values; a finding that names no value is a note. - Comments: Sphinx version notes and
@author/@sinceblocks 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 commentsor--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 documentationnow also reads code files for their comments. Configs that namemaintainability, asjevgate initwrites, 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/subjectand support helpers as evidence. JUnit, TestNG, xUnit, NUnit and MSTest tests are recognized. - Frameworks: Django and Django REST framework (views with their routes and
|safetemplates, 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.configheaders), 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 letguards 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 aTest*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 --mergeaccepts 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 initwrites 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]].
| Rule | Key | Default | Question |
|---|---|---|---|
maintainability/file-organization | file_organization | yes | Would moving some members into a separate module (or tests into a separate test file) make the file easier to navigate and maintain? |
maintainability/function-simplification | function_simplification | yes | 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? |
maintainability/shared-logic | shared_logic | yes | Do the two sites perform the same steps for the same purpose, so one shared implementation would serve both? |
maintainability/hardcoded-values | hardcoded_values | yes | Does a value fixed in code change between deployments, need a descriptive name, or special-case one identity? |
security/injection | injection | opt-in | 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? |
security/sensitive-data | sensitive_data | opt-in | 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? |
security/unsafe-settings | unsafe_settings | opt-in | 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? |
security/access-control | access_control | opt-in | 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? |
security/workflows | workflows | opt-in | 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? |
tests/value | test_value | yes | Does the test check only its mocks, recompute the expected value with the code’s own logic, assert internal details, or mix unrelated behaviors? |
tests/redundancy | test_redundancy | yes | Do the two tests check the same behavior, with different or equivalent inputs? |
documentation/agent-context | agent_context | opt-in | Does a section restate what the repository’s files show, give generic advice, repeat what linters check, or record past work? |
documentation/large-docs | large_docs | opt-in | Would splitting the document make it easier to find and maintain, or does it mainly record past work? |
documentation/staleness | doc_staleness | opt-in | 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? |
documentation/duplication | doc_duplication | opt-in | Does one section state everything the other states, or do the two give different values or instructions for the same thing? |
documentation/comments | comments | opt-in | 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? |
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.configfiles, 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:
| Setting | Value |
|---|---|
clear_probability | 0.8 |
consider_leading_probability | 0.5 |
consider_probability | 0.8 |
deep_nesting | 4 |
location_probability | 0.65 |
long_branch_chain | 4 |
min_body_lines | 5 |
min_clone_bytes | 120 |
min_clone_statements | 3 |
min_file_lines | 100 |
review_probability | 0.8 |
Configuration reference
Generated from jevgate.schema.json,
which is generated from the configuration types. Configuration explains
how the keys work together.
| Key | Type | Meaning |
|---|---|---|
cache_ttl_secs | integer | Cache lifetime in seconds for the jev-latest and jev-preview aliases; pinned versions never expire. Default: 3600. |
concurrency | integer | Ceiling on simultaneous requests (1-8). Default: 6. |
context | list of strings | Files always sent as related evidence, like --context. |
fail_on | list of strings | The level for rules without their own, like --fail-on. Default: [“review”]. |
generated | list of strings | Globs of generated files, which are skipped, in addition to the built-in names. |
include_tests | boolean | Judge tests, like --include-tests. Default: false. |
max_context_bytes | integer | Ceiling on context bytes per request. Default: 32768. |
max_file_bytes | integer | Files larger than this are reported as needs-context, never truncated. Default: 262144. |
max_requests | integer | Ceiling on API attempts per invocation; flags can only lower it. Default: unlimited. |
model | string | TypeSafe model; a pinned version keeps results repeatable. --model overrides it. |
rules | rules list or table | A list selects rules; a table gives each group or rule a level. Default: the default group. |
scope | list of tables | Gate levels for the files some paths match, such as report-only tooling. |
tests | list of strings | Globs of additional test files. |
upload_allow | list of strings | Globs of the paths that may be uploaded, including instruction files and context. Default: every path. |
upload_deny | list of strings | Globs 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.
| Key | Type | Meaning |
|---|---|---|
fail_on | list of strings | The level for every rule in these files. |
paths | list of strings | Globs of the files this scope applies to. |
rules | object | Levels 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')