Rules & checks
Every rule exists because the defect in the right-hand column shipped once. This
is the list, and vsa check --list prints it on your machine.
vsa check # everything; exit 1 on any error
vsa check --only=dom-scope # one rule
vsa check --strict # warnings block too
vsa check --json # for CI
The rules
| Rule | What it requires | What it caught |
|---|---|---|
bundle-paths | Bundle and style entries resolve to git-tracked files, exact case | A misspelled component file, an untracked CSS file |
render-names | ScriptManager::render('x') names a bundle that exists | A bundle renamed in one place only |
global-name | A page or dialog's file name matches the global it declares | A component whose file and global disagreed |
declarations | No duplicate top-level names per shell, and no implicit globals | A dialog assigned without a declaration; clearTimeout shadowed |
globals-exist | Every PascalCase global a file uses is provided by its shell | Dialogs and list views that were never loaded on the page using them |
page-leaf | Nothing outside a page references that page's globals | Helpers reached across pages that happened to be loaded together |
reachable | Every page is in an app file or an alternative route, or named somewhere | A page that existed but nothing could open |
dom-scope | Pages query page.root, blade styles are scoped, the root id exists | 22 uses of document.* and 7 cases of CSS leaking across pages |
blade-escape | No request value printed raw; warns on any raw variable | A meta tag echoing a request value |
secrets | No API keys or secrets in client code | A chat API key and a Pusher secret in shipped JavaScript |
permission-keys | verifyAuth(), allowed() and access_mod() name the permission or module code, never a numeric id | An action guarded by id 327, which no store actually had — so the guard passed for everyone |
cdn-pinned | CDN URLs carry a version | A component pulled at @latest, which changed under a running app |
cdn-fresh | Every CDN URL a shell bundle names answers 200 (one HEAD each; offline is not a failure) | An unpublished version and a component renamed in a VSEL release — the app loads without it |
app-file | The app file is plain data; its pages exist and are loaded by the app's shell; the module is a ref code or null; navigation names only declared pages, once; labels exist in every locale; icons resolve; badge keys are mapped. A report must name a module, a category listed in report_categories, and only filters its report_filters dictionary defines | A menu link to a misspelled component, a generator writing module_id = 0, and a report whose filter the console then refused to save |
slugs | App-file slugs are unique, a single segment, and not a route group | — |
vsa-idiom | A page uses the live VSApp API, carries the .vs-page contract on its root, and names no class that no stylesheet defines | A generated nine-page app in which every page was dead on arrival — main_view.* threw at parse time and took every later file in the bundle with it — while check reported 0 errors |
epoch-committed | A changed asset_epoch is staged, judged against the index, not HEAD | A bump left in a working copy: cache:bust said “set”, nobody committed it, and every browser kept its old JS |
xauth-console | The console the xauth:* commands push to is the one .env signs in against | vsa.json naming one console while .env pointed at another: both halves reported success, and the only symptom was a menu item that never appeared |
Errors and warnings
An error is something that will fail at runtime or on deploy. A
warning is a rule the codebase does not fully meet yet — unscoped
page CSS, document.* inside a page, a raw variable in a report blade.
Warnings do not block. --strict makes them block, once that backlog is
cleared.
While a decision is pending, a specific finding can be parked in
vsa.json under knownIssues,
which turns that one error into a warning and records why.
How globals are resolved
Every page's markup and every script share one document, so the question
“is Foo defined?” really means “does any file this shell loads declare
Foo?”. To answer it, the checker collects declarations from the bundle
files, the shell's inline <script> blocks and its
<script src> files, the menus' and layouts' inline scripts, and
the scripts the core fetches at runtime.
VSEL components arrive from the CDN, so their declarations cannot be read from the
project. known-globals.json ships with the CLI and lists the globals
each CDN file declares; vsa check --update-globals records new ones.
Where checks run
- On save —
vsa serveruns them as you work - On commit — the project's pre-commit hook runs
vsa check, skipping the rules named inquickSkip(by default just the one that needs the network) - In CI —
vsa check --json, usually withvsa i18n:missing --lang=enbeside it