roe.json, roe.yaml, or
roe.yml. It’s resolved by walking up from the analysis root to the nearest
directory containing one (like .eslintrc/tsconfig.json), or pointed to
explicitly with --config:
roe.json
wins. An explicit --config path that’s missing or malformed is a hard
error.
Fields
roe.json
Unknown fields are rejected, so a typo fails loudly instead of being
silently ignored.
Dupes options
Every field underdupes is optional and corresponds to a flag on
roe dupes:
This block is what calibrates a combined run:
roe check
takes no dupes flags of its own, so raising minTokens here is how a one-line
CI gate learns the threshold your codebase needs.
Health thresholds
Every field underhealth is optional and corresponds to a flag on
roe health:
Committing these is usually better than passing six flags on every CI
invocation, and it keeps local runs and CI in agreement.
baseline names a baseline file and is
resolved relative to the config file’s own directory, the way ignore globs
are — so "roe-baseline.json" means the one sitting next to roe.json, no
matter which subdirectory the run starts in. Setting it here rather than
passing --baseline is also what makes a bare roe and
roe check honour it, since neither takes health flags of
its own. A path that doesn’t exist is a hard error, not a silent
full-backlog run.
Entry-point files
entryPoints is the file-level counterpart to roots: where roots names
one symbol by its fully-qualified name, an entry-point glob keeps a whole
file’s contents alive. Every declaration in a matching file becomes a root,
so the file is never flagged and everything it references counts as used —
unlike an ignore glob, which only drops the findings in the matching file
itself.
Use it for files that are consumed in ways roe can’t see: a plugin host
loading them by path, string-based reflection, or an external tool reading
the file directly.
roe.json
ignore globs — a trailing / matches the whole directory, and
.. is unsupported. A pattern that matches no file at all is reported as a
note in the output, so a stale path can’t silently drop the protection it
promised.
Ignore globs
ignore patterns are resolved relative to the config file’s own directory.
A trailing / matches the whole directory, so "Generated/" needs no **.
Patterns containing .. are unsupported and produce a warning.
The top-level ignore list applies to all three commands — including
roe dupes and roe health’s circular dependencies, which span multiple
files and don’t map cleanly onto a single-line
inline suppression comment.
Each command’s section takes an ignore list of its own with the same
rules, unioned with the top-level list and applied only to that command.
That keeps a suppression as narrow as the exception it accepts: ignoring an
intentional duplicate through dupes.ignore doesn’t cost the file its
dead-code and health coverage.
roe.yaml
Precedence
Every setting follows the same order: an explicit command-line flag wins, otherwise the config file’s value applies, otherwise the built-in default. Foraggressive, roots, and libraryProjects that means an explicit
--aggressive, --root, or --library flag beats the config, falling back
to false / no extra roots / no extra library projects.
Two nuances: --aggressive can only turn the setting on — there is no
--no-aggressive to override a config’s "aggressive": true. And passing
--root or --library replaces the config’s list wholesale rather than
merging with it.
Health thresholds work the same way, per field: --max-complexity 12
overrides "maxComplexity" for that run, while every threshold you don’t
pass still comes from the config. Like --aggressive, --exclude-tests can
only turn the setting on.
Dupes settings follow the same per-field order: --min-tokens 100 overrides
"minTokens" for that run, and the rest still come from the config. --mode
carries a value, so unlike --aggressive and --exclude-tests it has no
on-only nuance — --mode exact overrides a config’s "semantic" cleanly.
Ignore lists and entryPoints sit outside this order entirely: neither has
a CLI flag, and a command’s own ignore list is unioned with the top-level
one rather than replacing it.
To confirm a config-only excludeTests or ignore actually applied, read the
roe health footer: the scanned counts narrow by whatever was excluded, and
an excluded: line names it. See
What “scanned” counts.