From 8d07c89859e004e8ba8e66623b814d2b8eb8ae63 Mon Sep 17 00:00:00 2001
From: Tauan BF <11513929+tauanbinato@users.noreply.github.com>
Date: Fri, 25 Sep 2026 19:22:55 -0300
Subject: [PATCH 1/2] Publish a documentation site
site/ is an mdBook: guide pages from the README's sections, pages on coding agents and troubleshooting, and rules, configuration and command-line reference pages generated from the binary, jevgate.schema.json and --help. The Docs workflow builds it on pull requests and publishes it to GitHub Pages from main.
---
.github/workflows/docs.yml | 50 +++
.gitignore | 4 +
CONTRIBUTING.md | 4 +
site/book.toml | 19 ++
site/build.sh | 9 +
site/generate.py | 154 ++++++++++
site/src/SUMMARY.md | 31 ++
site/src/changelog.md | 3 +
site/src/ci.md | 52 ++++
site/src/configuration.md | 43 +++
site/src/how-it-works.md | 567 +++++++++++++++++++++++++++++++++++
site/src/install.md | 14 +
site/src/introduction.md | 27 ++
site/src/languages.md | 48 +++
site/src/limits.md | 6 +
site/src/output.md | 23 ++
site/src/privacy-and-cost.md | 7 +
site/src/quick-start.md | 26 ++
site/src/troubleshooting.md | 41 +++
site/src/what-it-finds.md | 41 +++
20 files changed, 1169 insertions(+)
create mode 100644 .github/workflows/docs.yml
create mode 100644 site/book.toml
create mode 100755 site/build.sh
create mode 100644 site/generate.py
create mode 100644 site/src/SUMMARY.md
create mode 100644 site/src/changelog.md
create mode 100644 site/src/ci.md
create mode 100644 site/src/configuration.md
create mode 100644 site/src/how-it-works.md
create mode 100644 site/src/install.md
create mode 100644 site/src/introduction.md
create mode 100644 site/src/languages.md
create mode 100644 site/src/limits.md
create mode 100644 site/src/output.md
create mode 100644 site/src/privacy-and-cost.md
create mode 100644 site/src/quick-start.md
create mode 100644 site/src/troubleshooting.md
create mode 100644 site/src/what-it-finds.md
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 0000000..1166642
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,50 @@
+name: Docs
+# Builds the documentation site (site/build.sh) and publishes it to GitHub
+# Pages from main. Pull requests that touch what the site is made from only
+# build it, so a broken page or include fails before it merges.
+on:
+ push:
+ branches: [main]
+ pull_request:
+ paths: ["site/**", "README.md", "CHANGELOG.md", "docs/classification-cascade.md", "jevgate.schema.json", ".github/workflows/docs.yml"]
+ workflow_dispatch:
+permissions:
+ contents: read
+concurrency:
+ group: docs-${{ github.ref }}
+ cancel-in-progress: true
+env:
+ MDBOOK_VERSION: 0.5.4
+ MDBOOK_SHA256: 5222beabd3e37dc5be0d18ff99b79058469354db5c220153a1b92db5ba12be89
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+ - name: Install Rust
+ run: rustup toolchain install stable --profile minimal
+ - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
+ - name: Install mdBook
+ run: |
+ archive="mdbook-v$MDBOOK_VERSION-x86_64-unknown-linux-musl.tar.gz"
+ curl -fsSL -o "$RUNNER_TEMP/$archive" "https://github.com/rust-lang/mdBook/releases/download/v$MDBOOK_VERSION/$archive"
+ echo "$MDBOOK_SHA256 $RUNNER_TEMP/$archive" | sha256sum -c -
+ mkdir -p "$RUNNER_TEMP/bin" && tar -xzf "$RUNNER_TEMP/$archive" -C "$RUNNER_TEMP/bin"
+ echo "$RUNNER_TEMP/bin" >> "$GITHUB_PATH"
+ - run: site/build.sh
+ - uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
+ with:
+ path: site/book
+ deploy:
+ if: github.event_name != 'pull_request'
+ needs: build
+ runs-on: ubuntu-latest
+ permissions:
+ pages: write
+ id-token: write
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ steps:
+ - id: deployment
+ uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
diff --git a/.gitignore b/.gitignore
index 6b3d85b..ea9bd8a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,3 +29,7 @@ GEMINI.md
*.log
/video/
/roadmap.md
+
+# The documentation site is built by site/build.sh; its reference pages are generated.
+/site/book/
+/site/src/reference/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ead9ec6..82b6fd5 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -38,6 +38,10 @@ JevGate asks the model small questions and decides findings in code. When you ch
[docs/classification-cascade.md](docs/classification-cascade.md) describes the evidence units and composition rules.
+## Documentation site
+
+The site at is built from `site/` with [mdBook](https://rust-lang.github.io/mdBook/): `site/build.sh` generates the rules, configuration and command-line reference pages from a release build, then builds the book into `site/book`. Guide pages are in `site/src`; the reference pages are generated, so change the rule catalog, the configuration types or the `--help` text instead.
+
## Pull requests
- Keep a pull request to one change, with tests.
diff --git a/site/book.toml b/site/book.toml
new file mode 100644
index 0000000..f7b2701
--- /dev/null
+++ b/site/book.toml
@@ -0,0 +1,19 @@
+[book]
+title = "JevGate"
+description = "A code-review gate for CI and coding agents"
+authors = ["Tech Byte Frontier"]
+language = "en"
+src = "src"
+
+[build]
+build-dir = "book"
+create-missing = false
+
+[output.html]
+site-url = "/jevgate/"
+git-repository-url = "https://github.com/Tech-Byte-Frontier/jevgate"
+edit-url-template = "https://github.com/Tech-Byte-Frontier/jevgate/edit/main/site/{path}"
+no-section-label = true
+
+[output.html.search]
+limit-results = 20
diff --git a/site/build.sh b/site/build.sh
new file mode 100755
index 0000000..2b27c86
--- /dev/null
+++ b/site/build.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+# Build the documentation site into site/book: the reference pages are
+# generated from a release build of JevGate, so they match its --help,
+# `jevgate rules` and jevgate.schema.json. Needs mdbook on PATH.
+set -euo pipefail
+cd "$(dirname "$0")/.."
+cargo build --release --locked --quiet
+python3 site/generate.py target/release/jevgate jevgate.schema.json site/src/reference
+mdbook build site
diff --git a/site/generate.py b/site/generate.py
new file mode 100644
index 0000000..e59e570
--- /dev/null
+++ b/site/generate.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python3
+"""Write the site's reference pages from JevGate itself.
+
+ generate.py JEVGATE SCHEMA OUT_DIR
+
+rules.md comes from `jevgate rules --format json`, configuration.md from
+jevgate.schema.json, and cli.md from each command's --help, so the pages
+always describe the binary they were built with.
+"""
+import json
+import subprocess
+import sys
+from pathlib import Path
+
+COMMANDS = ["auth", "check", "baseline", "rules", "init", "completions", "man", "serve"]
+GROUPS = {
+ "maintainability": "On by default.",
+ "tests": "On by default; judged with `--include-tests` or `include_tests = true`.",
+ "security": "Opt-in: `--rule security`, or a level in `[rules]`.",
+ "documentation": "Opt-in: `--rule documentation`, or a level in `[rules]`.",
+}
+
+
+def run(binary, *args):
+ return subprocess.run([binary, *args], check=True, capture_output=True, text=True).stdout
+
+
+def rules_page(binary):
+ rules = json.loads(run(binary, "rules", "--format", "json"))
+ version = run(binary, "--version").strip()
+ lines = [
+ "# Rules reference",
+ "",
+ f"Generated from `jevgate rules --format json` ({version}). 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 |",
+ "|---|---|---|---|",
+ ]
+ for rule in rules:
+ anchor = rule["id"].replace("/", "-")
+ default = "yes" if rule["default_enabled"] else "opt-in"
+ lines.append(
+ f"| [`{rule['id']}`](#{anchor}) | `{rule['key']}` | {default} | {cell(rule['inspection'])} |"
+ )
+ group = None
+ for rule in rules:
+ if rule["group"] != group:
+ group = rule["group"]
+ lines += ["", f"## {group.capitalize()}", "", GROUPS.get(group, "")]
+ anchor = rule["id"].replace("/", "-")
+ lines += [
+ "",
+ f'',
+ f"### `{rule['id']}`",
+ "",
+ f"**Question:** {rule['inspection']}",
+ "",
+ f"- **Key:** `{rule['key']}` · **Version:** {rule['version']}"
+ + (" · **Needs tests:** yes" if rule["requires_tests"] else ""),
+ f"- **Looks at:** {rule['scope']}",
+ f"- **Evidence unit:** {rule['unit']}",
+ f"- **Acceptable:** {rule['acceptable_example']}",
+ ]
+ policy = rules[0]["decision_policy"]
+ lines += [
+ "",
+ "## Decision policy",
+ "",
+ "Answers become findings in code, at the same thresholds for every rule:",
+ "",
+ "| Setting | Value |",
+ "|---|---|",
+ ]
+ lines += [f"| `{name}` | {value:g} |" for name, value in sorted(policy.items())]
+ return "\n".join(lines) + "\n"
+
+
+def configuration_page(schema_path):
+ schema = json.loads(Path(schema_path).read_text())
+ lines = [
+ "# Configuration reference",
+ "",
+ "Generated from [`jevgate.schema.json`](https://github.com/Tech-Byte-Frontier/jevgate/blob/main/jevgate.schema.json),",
+ "which is generated from the configuration types. [Configuration](../configuration.md) explains",
+ "how the keys work together.",
+ "",
+ "| Key | Type | Meaning |",
+ "|---|---|---|",
+ ]
+ for name, spec in sorted(schema["properties"].items()):
+ lines.append(f"| `{name}` | {kind(spec, schema)} | {cell(spec.get('description', ''))} |")
+ scope = schema["$defs"]["Scope"]
+ lines += ["", "## `[[scope]]`", "", cell(scope.get("description", "")), "", "| Key | Type | Meaning |", "|---|---|---|"]
+ for name, spec in sorted(scope["properties"].items()):
+ lines.append(f"| `{name}` | {kind(spec, schema)} | {cell(spec.get('description', ''))} |")
+ levels = schema["$defs"]["Level"]["anyOf"][0]["enum"]
+ names = schema["$defs"]["Scope"]["properties"]["rules"]["propertyNames"]["enum"]
+ lines += [
+ "",
+ "## Levels",
+ "",
+ ", ".join(f"`{level}`" for level in levels) + ". `off` is accepted in `[rules]` only.",
+ "",
+ "## Rule names",
+ "",
+ ", ".join(f"`{name}`" for name in names) + ".",
+ ]
+ return "\n".join(lines) + "\n"
+
+
+def kind(spec, schema):
+ if "$ref" in spec:
+ return "rules list or table"
+ if spec.get("type") == "array":
+ items = spec.get("items", {})
+ return "list of tables" if "$ref" in items else "list of " + items.get("type", "value") + "s"
+ return spec.get("type", "value")
+
+
+def cli_page(binary):
+ lines = [
+ "# Command-line reference",
+ "",
+ f"Generated from `--help` ({run(binary, '--version').strip()}). "
+ "`jevgate man COMMAND` prints the same text as a man page.",
+ "",
+ "## `jevgate`",
+ "",
+ "```text",
+ run(binary, "--help").rstrip(),
+ "```",
+ ]
+ for command in COMMANDS:
+ lines += ["", f"## `jevgate {command}`", "", "```text", run(binary, command, "--help").rstrip(), "```"]
+ return "\n".join(lines) + "\n"
+
+
+def cell(text):
+ return text.replace("|", "\\|").replace("\n", " ")
+
+
+def main():
+ binary, schema, out = sys.argv[1:4]
+ out = Path(out)
+ out.mkdir(parents=True, exist_ok=True)
+ (out / "rules.md").write_text(rules_page(binary))
+ (out / "configuration.md").write_text(configuration_page(schema))
+ (out / "cli.md").write_text(cli_page(binary))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/site/src/SUMMARY.md b/site/src/SUMMARY.md
new file mode 100644
index 0000000..a343011
--- /dev/null
+++ b/site/src/SUMMARY.md
@@ -0,0 +1,31 @@
+# Summary
+
+[JevGate](introduction.md)
+
+# Getting started
+
+- [Install](install.md)
+- [Quick start](quick-start.md)
+- [What it finds](what-it-finds.md)
+- [Supported languages and frameworks](languages.md)
+
+# Using JevGate
+
+- [Continuous integration](ci.md)
+- [Coding agents](agents.md)
+- [Configuration](configuration.md)
+- [Output and exit codes](output.md)
+- [Privacy and cost](privacy-and-cost.md)
+- [Troubleshooting](troubleshooting.md)
+
+# Background
+
+- [How it works](how-it-works.md)
+- [Limits](limits.md)
+- [Changelog](changelog.md)
+
+# Reference
+
+- [Rules](reference/rules.md)
+- [Configuration keys](reference/configuration.md)
+- [Command line](reference/cli.md)
diff --git a/site/src/changelog.md b/site/src/changelog.md
new file mode 100644
index 0000000..91e0332
--- /dev/null
+++ b/site/src/changelog.md
@@ -0,0 +1,3 @@
+# Changelog
+
+{{#include ../../CHANGELOG.md:3:}}
diff --git a/site/src/ci.md b/site/src/ci.md
new file mode 100644
index 0000000..ab31710
--- /dev/null
+++ b/site/src/ci.md
@@ -0,0 +1,52 @@
+# Continuous integration
+
+A pull request review on GitHub Actions, with the [JevGate action](https://github.com/Tech-Byte-Frontier/jevgate-action):
+
+```yaml
+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.17.0
+```
+
+The action installs a checked release binary, keeps `.jevgate/cache` in the Actions cache and runs `jevgate check --base --format github`; `args` passes more flags, such as `--rule security`. It runs on Linux, macOS and Windows runners.
+
+`--format github` annotates the changed lines with each finding. A finding that fails the gate is an error; the others are warnings. A Markdown table goes to the job summary, and the usual text goes to the log. The full JSON report is always at `.jevgate/latest.json` if you want to keep it as an artifact.
+
+- **Changed files only:** `--base` reviews what changed since the fork point with that revision, the same files a pull request diff shows, plus uncommitted and untracked files. It needs the history, so check out with `fetch-depth: 0`. When no supported file changed, the run passes without any request.
+- **Cache:** answers are stored under a hash of the exact request: source, questions and model. Restoring an older cache is always safe, and unchanged code costs nothing on the next run.
+- **Advisory or blocking:** `fail_on = ["none"]` in `jevgate.toml` or `--fail-on none` reports findings without failing. A run that could not finish (missing key, provider rejection, request budget reached) still exits 2, so an outage never passes as a clean review.
+- **A policy the change cannot edit:** a pull request can edit `jevgate.toml`. To apply the reviewed policy of the base branch instead, read it with `--config`:
+
+ ```sh
+ git show "$BASE_SHA:jevgate.toml" > "$RUNNER_TEMP/jevgate.toml"
+ jevgate check --config "$RUNNER_TEMP/jevgate.toml" --base "$BASE_SHA" --format github
+ ```
+
+- **Forks:** GitHub withholds secrets from pull requests opened from forks, so there the run exits 2 with "No API key configured". Skip the job for forks, or run it only on branches of the repository.
+- **Budgets:** `max_requests` caps the API attempts of one run. Reaching it leaves the run incomplete instead of passing on partial evidence. `--dry-run` counts the planned requests the cache already answers, so its estimate covers only what the cache lacks; follow-ups depend on answers and are not counted.
+- **Transient failures:** rate limits, overload and server or edge errors (HTTP 408, 429, 500, 502–504, 520–524, 529) are retried up to four attempts; a timeout or dropped connection is retried once, since the first send may have run.
+- **Report-only paths:** give tooling its own level with `[[scope]]` (below), so scripts are reported while product code gates.
+
+Before each commit, with [pre-commit](https://pre-commit.com), review what is staged:
+
+```yaml
+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
+```
+
+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.
diff --git a/site/src/configuration.md b/site/src/configuration.md
new file mode 100644
index 0000000..e9358c3
--- /dev/null
+++ b/site/src/configuration.md
@@ -0,0 +1,43 @@
+# 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`](https://github.com/Tech-Byte-Frontier/jevgate/blob/main/jevgate.schema.json), which completes keys, rule names and levels and flags mistakes as you type.
+
+```toml
+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](reference/configuration.md) lists every key with its type, and the rule names and levels it accepts.
diff --git a/site/src/how-it-works.md b/site/src/how-it-works.md
new file mode 100644
index 0000000..374e66e
--- /dev/null
+++ b/site/src/how-it-works.md
@@ -0,0 +1,567 @@
+# How it works
+
+1. **Local analysis, nothing uploaded.** Tree-sitter parsers find functions, methods, types and registered callbacks, such as route handlers written inline in `app.post('/pages', async (c) => …)`. They measure nesting, group a file's members, find renamed copies, map tests to the functions they call, and list the statements where a value reaches another program. This evidence locates and scopes; it never decides a finding.
+2. **Small, literal questions.** Each request covers one small unit and asks a few questions, such as "Would splitting this function make it easier to understand?" or "Does this function put a variable into the text of an SQL query instead of binding it?"
+3. **Follow-ups only where needed.** When an answer is split, JevGate gathers more evidence (callee signatures, callers, a specific check) and asks once more instead of guessing.
+4. **Composition in code.** Answers become `review`, `consider`, `note`, `clear` or `uncertain` at a 0.80 threshold. Raw probabilities stay in the JSON report.
+
+The rest of this page describes the evidence units and composition rules in detail.
+
+## Evidence units
+
+JevGate asks Jev short, literal questions about small units of evidence that code
+has already built, then composes the answers in code.
+
+### Why units
+
+A single request per file carried the whole source, every rule's candidates and
+several broad questions. Large states lowered decisiveness, and reconciling
+file-wide answers with per-operation probes let decisive concerns disappear.
+Units send only what one question needs: one function's source, a file's member
+signatures, or one candidate pair.
+
+### Pipeline
+
+1. **Eligibility and purpose.** Deterministic roles, generated-code headers,
+ compiled or minified output (a trailing source map reference, or nine tenths
+ of the file in lines of 1,000 bytes or more) and structural test markers
+ decide which code the application rules and the test rules see. Only a test
+ path without structural tests gets a file-purpose request. Go tests are
+ `Test…`, `Benchmark…` and `Fuzz…` functions taking `*testing.T`, `.B` or `.F`.
+ C# tests are whole classes marked `[TestFixture]` or `[TestClass]` or
+ holding a method marked `[Fact]`, `[Theory]`, `[Test]`, `[TestCase]`,
+ `[TestCaseSource]`, `[TestMethod]` or `[DataTestMethod]`, and every C# file
+ of a test project directory named like `Shop.Tests`; `.Designer.cs`,
+ `.g.cs` and `.g.i.cs` files are generated.
+ Ruby tests are RSpec groups and examples written as statements
+ (`describe`, `context`, `it`, `specify`, `its`, titled by a string or not
+ at all, so a Rakefile's `test(:unit) do` is not one) and classes whose
+ superclass ends in `Test`, `TestCase` or `Spec` (`Minitest::Test`,
+ `ActiveSupport::TestCase`) with `test_*` methods or `test "…" do` blocks;
+ `*_spec.rb` files and Ruby files under `spec/` are test files.
+ PHP tests are the `test…`, `@test` or `#[Test]` methods of a class
+ extending a `…TestCase`, and Pest `test(…)`/`it(…)` calls; `…Test.php`
+ files are test paths.
+ A Java class is a test class, whole, when it holds a method annotated
+ `@Test`, `@ParameterizedTest` or another test annotation (a composed one
+ whose name ends in `Test` included), a JUnit lifecycle method such as
+ `@BeforeEach`, a nested test class, or extends JUnit 3's `TestCase`;
+ `…Test`, `…Tests`, `…TestCase` and `…IT` files are test paths. A test
+ that sends a request to a literal path (MockMvc's `get("/owners/{id}")`,
+ RestTemplate's `RequestEntity.get(…)`) calls the Spring controller method
+ whose `@GetMapping`, `@PostMapping` or `@RequestMapping` route serves it,
+ under its class's prefix, by full name, the most literal route winning:
+ controllers share method names such as `initCreationForm`, and a request
+ names no method, so such tests had no code under test.
+ Astro, Vue and
+ Svelte files are parsed as their scripts: Astro frontmatter and `