fallow
Version:
Deterministic codebase intelligence for TypeScript and JavaScript. Quality, risk, architecture, dependencies, duplication, and safe cleanup evidence for humans, CI, and agents. Optional runtime intelligence layer (Fallow Runtime) adds production execution
1,360 lines (1,359 loc) • 72.5 kB
JSON
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "FallowConfig",
"type": "object",
"properties": {
"$schema": {
"type": [
"string",
"null"
],
"writeOnly": true
},
"extends": {
"type": "array",
"items": {
"type": "string"
},
"writeOnly": true
},
"entry": {
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"ignorePatterns": {
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"framework": {
"type": "array",
"items": {
"$ref": "#/$defs/ExternalPluginDef"
},
"default": []
},
"workspaces": {
"anyOf": [
{
"$ref": "#/$defs/WorkspaceConfig"
},
{
"type": "null"
}
],
"default": null
},
"ignoreDependencies": {
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"ignoreUnresolvedImports": {
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"ignoreExports": {
"type": "array",
"items": {
"$ref": "#/$defs/IgnoreExportRule"
},
"default": []
},
"ignoreCatalogReferences": {
"type": "array",
"items": {
"$ref": "#/$defs/IgnoreCatalogReferenceRule"
}
},
"ignoreDependencyOverrides": {
"type": "array",
"items": {
"$ref": "#/$defs/IgnoreDependencyOverrideRule"
}
},
"ignoreExportsUsedInFile": {
"$ref": "#/$defs/IgnoreExportsUsedInFileConfig",
"default": false
},
"ignoreDecorators": {
"type": "array",
"items": {
"type": "string"
}
},
"usedClassMembers": {
"type": "array",
"items": {
"$ref": "#/$defs/UsedClassMemberRule"
},
"default": []
},
"duplicates": {
"$ref": "#/$defs/DuplicatesConfig",
"default": {
"enabled": true,
"mode": "mild",
"minTokens": 50,
"minLines": 5,
"minOccurrences": 2,
"threshold": 0.0,
"ignore": [],
"ignoreDefaults": true,
"skipLocal": false,
"crossLanguage": false,
"ignoreImports": true,
"normalization": {},
"minCorpusSizeForShingleFilter": 1024,
"minCorpusSizeForTokenCache": 5000
}
},
"health": {
"$ref": "#/$defs/HealthConfig",
"default": {
"maxCyclomatic": 20,
"maxCognitive": 15,
"maxCrap": 30.0,
"crapRefactorBand": 5,
"coverage": null,
"coverageRoot": null,
"ignore": [],
"ownership": {
"botPatterns": [
"*\\[bot\\]*",
"dependabot*",
"renovate*",
"github-actions*",
"svc-*",
"*-service-account*"
],
"emailMode": "handle"
},
"suggestInlineSuppression": true
}
},
"rules": {
"$ref": "#/$defs/RulesConfig",
"default": {
"unused-files": "error",
"unused-exports": "error",
"unused-types": "error",
"private-type-leaks": "off",
"unused-dependencies": "error",
"unused-dev-dependencies": "warn",
"unused-optional-dependencies": "warn",
"unused-enum-members": "error",
"unused-class-members": "error",
"unused-store-members": "warn",
"unprovided-injects": "warn",
"unrendered-components": "warn",
"unused-component-props": "warn",
"unused-component-emits": "warn",
"unused-server-actions": "warn",
"unused-load-data-keys": "warn",
"prop-drilling": "off",
"thin-wrapper": "off",
"duplicate-prop-shape": "off",
"unresolved-imports": "error",
"unlisted-dependencies": "error",
"duplicate-exports": "error",
"type-only-dependencies": "warn",
"test-only-dependencies": "warn",
"circular-dependencies": "error",
"re-export-cycle": "warn",
"boundary-violation": "error",
"coverage-gaps": "off",
"feature-flags": "off",
"stale-suppressions": "warn",
"unused-catalog-entries": "warn",
"empty-catalog-groups": "warn",
"unresolved-catalog-references": "error",
"unused-dependency-overrides": "warn",
"misconfigured-dependency-overrides": "error",
"security-client-server-leak": "off",
"security-sink": "off",
"policy-violation": "warn",
"invalid-client-export": "warn",
"mixed-client-server-barrel": "warn",
"misplaced-directive": "warn",
"route-collision": "error",
"dynamic-segment-name-conflict": "error"
}
},
"boundaries": {
"$ref": "#/$defs/BoundaryConfig",
"default": {
"zones": [],
"rules": []
}
},
"flags": {
"$ref": "#/$defs/FlagsConfig",
"default": {
"configObjectHeuristics": false
}
},
"security": {
"$ref": "#/$defs/SecurityConfig",
"default": {}
},
"fix": {
"$ref": "#/$defs/FixConfig",
"default": {
"catalog": {
"deletePrecedingComments": "auto"
}
}
},
"resolve": {
"$ref": "#/$defs/ResolveConfig",
"default": {}
},
"production": {
"$ref": "#/$defs/ProductionConfig",
"default": false
},
"plugins": {
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"rulePacks": {
"description": "Paths to declarative rule-pack files (JSON or JSONC), relative to the\nproject root. Each pack declares `banned-call` / `banned-import` rules\nthat report as `policy-violation` findings. Packs are pure data: no\nproject code is executed. Invalid or missing packs fail config load.",
"type": "array",
"items": {
"type": "string"
}
},
"dynamicallyLoaded": {
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"overrides": {
"type": "array",
"items": {
"$ref": "#/$defs/ConfigOverride"
},
"default": []
},
"codeowners": {
"type": [
"string",
"null"
]
},
"publicPackages": {
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"regression": {
"anyOf": [
{
"$ref": "#/$defs/RegressionConfig"
},
{
"type": "null"
}
]
},
"audit": {
"$ref": "#/$defs/AuditConfig"
},
"sealed": {
"type": "boolean",
"default": false
},
"includeEntryExports": {
"type": "boolean",
"default": false
},
"autoImports": {
"type": "boolean",
"default": false
},
"cache": {
"$ref": "#/$defs/CacheConfig"
}
},
"additionalProperties": false,
"$defs": {
"ExternalPluginDef": {
"description": "A declarative plugin definition loaded from a standalone file or inline config.\n\nExternal plugins provide the same static pattern capabilities as built-in\nplugins (entry points, always-used files, used exports, tooling dependencies),\nbut are defined in standalone files or inline in the fallow config rather than\ncompiled Rust code.\n\nThey cannot do AST-based config parsing (`resolve_config()`), but cover the\nvast majority of framework integration use cases.\n\nSupports JSONC, JSON, and TOML formats. All use camelCase field names.\n\n```json\n{\n \"$schema\": \"https://raw.githubusercontent.com/fallow-rs/fallow/main/plugin-schema.json\",\n \"name\": \"my-framework\",\n \"enablers\": [\"my-framework\", \"@my-framework/core\"],\n \"entryPoints\": [\"src/routes/**/*.{ts,tsx}\"],\n \"configPatterns\": [\"my-framework.config.{ts,js}\"],\n \"alwaysUsed\": [\"src/setup.ts\"],\n \"toolingDependencies\": [\"my-framework-cli\"],\n \"usedExports\": [\n { \"pattern\": \"src/routes/**/*.{ts,tsx}\", \"exports\": [\"default\", \"loader\", \"action\"] }\n ]\n}\n```",
"type": "object",
"properties": {
"name": {
"description": "Unique name for this plugin.",
"type": "string"
},
"detection": {
"description": "Rich detection logic (dependency checks, file existence, boolean combinators).\nTakes priority over `enablers` when set.",
"anyOf": [
{
"$ref": "#/$defs/PluginDetection"
},
{
"type": "null"
}
],
"default": null
},
"enablers": {
"description": "Package names that activate this plugin when found in package.json.\nSupports exact matches and prefix patterns (ending with `/`).\nOnly used when `detection` is not set.",
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"entryPoints": {
"description": "Glob patterns for entry point files.",
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"entryPointRole": {
"description": "Coverage role for `entryPoints`.\n\nDefaults to `support`. Set to `runtime` for application entry points\nor `test` for test framework entry points.",
"$ref": "#/$defs/EntryPointRole",
"default": "support"
},
"configPatterns": {
"description": "Glob patterns for config files (marked as always-used when active).",
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"alwaysUsed": {
"description": "Files that are always considered \"used\" when this plugin is active.",
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"toolingDependencies": {
"description": "Dependencies that are tooling (used via CLI/config, not source imports).\nThese should not be flagged as unused devDependencies.",
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"usedExports": {
"description": "Exports that are always considered used for matching file patterns.",
"type": "array",
"items": {
"$ref": "#/$defs/ExternalUsedExport"
},
"default": []
},
"usedClassMembers": {
"description": "Class member method/property rules the framework invokes at runtime.\nSupports plain member names for global suppression and scoped objects\nwith `extends` / `implements` constraints when the method name is too\ncommon to suppress across the whole workspace.",
"type": "array",
"items": {
"$ref": "#/$defs/UsedClassMemberRule"
},
"default": []
}
},
"required": [
"name"
]
},
"PluginDetection": {
"description": "How to detect if a plugin should be activated.\n\nWhen set on an `ExternalPluginDef`, this takes priority over `enablers`.\nSupports dependency checks, file existence checks, and boolean combinators.",
"oneOf": [
{
"description": "Plugin detected if this package is in dependencies.",
"type": "object",
"properties": {
"package": {
"type": "string"
},
"type": {
"type": "string",
"const": "dependency"
}
},
"required": [
"type",
"package"
]
},
{
"description": "Plugin detected if this file pattern matches.",
"type": "object",
"properties": {
"pattern": {
"type": "string"
},
"type": {
"type": "string",
"const": "fileExists"
}
},
"required": [
"type",
"pattern"
]
},
{
"description": "All conditions must be true.",
"type": "object",
"properties": {
"conditions": {
"type": "array",
"items": {
"$ref": "#/$defs/PluginDetection"
}
},
"type": {
"type": "string",
"const": "all"
}
},
"required": [
"type",
"conditions"
]
},
{
"description": "Any condition must be true.",
"type": "object",
"properties": {
"conditions": {
"type": "array",
"items": {
"$ref": "#/$defs/PluginDetection"
}
},
"type": {
"type": "string",
"const": "any"
}
},
"required": [
"type",
"conditions"
]
}
]
},
"EntryPointRole": {
"description": "How a plugin's discovered entry points contribute to coverage reachability.",
"oneOf": [
{
"description": "Runtime/application roots that should count toward runtime reachability.",
"type": "string",
"const": "runtime"
},
{
"description": "Test roots that should count toward test reachability.",
"type": "string",
"const": "test"
},
{
"description": "Support/setup/config roots that should keep files alive but not count as runtime/test.",
"type": "string",
"const": "support"
}
]
},
"ExternalUsedExport": {
"description": "Exports considered used for files matching a pattern.",
"type": "object",
"properties": {
"pattern": {
"description": "Glob pattern for files.",
"type": "string"
},
"exports": {
"description": "Export names always considered used.",
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"pattern",
"exports"
]
},
"UsedClassMemberRule": {
"description": "A `usedClassMembers` entry from config or an external plugin.\n\nSupports either a plain member name or glob pattern (`\"agInit\"`,\n`\"enter*\"`) or a scoped rule that only applies when a class matches\nspecific `extends` / `implements` heritage clauses.",
"anyOf": [
{
"description": "Globally suppress this class member name or glob pattern for all classes.",
"type": "string"
},
{
"description": "Suppress these class member names only for matching classes.",
"$ref": "#/$defs/ScopedUsedClassMemberRule"
}
]
},
"ScopedUsedClassMemberRule": {
"description": "A heritage-constrained `usedClassMembers` rule.",
"type": "object",
"properties": {
"extends": {
"description": "Only apply when the class extends this parent class name.",
"type": [
"string",
"null"
]
},
"implements": {
"description": "Only apply when the class implements this interface name.",
"type": [
"string",
"null"
]
},
"members": {
"description": "Member names or glob patterns that should be treated as framework-used.",
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false,
"required": [
"members"
]
},
"WorkspaceConfig": {
"description": "Workspace configuration for monorepo support.",
"type": "object",
"properties": {
"patterns": {
"description": "Additional workspace patterns (beyond what's in root package.json).",
"type": "array",
"items": {
"type": "string"
},
"default": []
}
}
},
"IgnoreExportRule": {
"description": "Rule for ignoring specific exports.",
"type": "object",
"properties": {
"file": {
"description": "Glob pattern for files.",
"type": "string"
},
"exports": {
"description": "Export names to ignore (`*` for all).",
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"file",
"exports"
]
},
"IgnoreCatalogReferenceRule": {
"description": "Rule for suppressing an `unresolved-catalog-reference` finding.",
"type": "object",
"properties": {
"package": {
"type": "string"
},
"catalog": {
"type": [
"string",
"null"
]
},
"consumer": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": false,
"required": [
"package"
]
},
"IgnoreDependencyOverrideRule": {
"description": "Rule for suppressing dependency-override findings.",
"type": "object",
"properties": {
"package": {
"type": "string"
},
"source": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": false,
"required": [
"package"
]
},
"IgnoreExportsUsedInFileConfig": {
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/$defs/IgnoreExportsUsedInFileByKind"
}
]
},
"IgnoreExportsUsedInFileByKind": {
"type": "object",
"properties": {
"type": {
"type": "boolean",
"default": false
},
"interface": {
"type": "boolean",
"default": false
}
}
},
"DuplicatesConfig": {
"description": "Configuration for code duplication detection.",
"type": "object",
"properties": {
"enabled": {
"description": "Whether duplication detection is enabled.",
"type": "boolean",
"default": true
},
"mode": {
"description": "Detection mode: strict, mild, weak, or semantic.",
"$ref": "#/$defs/DetectionMode",
"default": "mild"
},
"minTokens": {
"description": "Minimum number of tokens for a clone.",
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 50
},
"minLines": {
"description": "Minimum number of lines for a clone.",
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 5
},
"minOccurrences": {
"description": "Minimum number of occurrences (instances of the same clone) before a\ngroup is reported. Defaults to 2 (every duplicated pair is reported).\nRaise this to focus on widespread copy-paste worth refactoring and skip\ncontext-sensitive pairs.",
"type": "integer",
"format": "uint",
"minimum": 2,
"default": 2
},
"threshold": {
"description": "Maximum allowed duplication percentage (0 = no limit).",
"type": "number",
"format": "double",
"default": 0.0
},
"ignore": {
"description": "Additional ignore patterns for duplication analysis.",
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"ignoreDefaults": {
"description": "Merge built-in generated-framework ignore patterns with `ignore`.\n\nSet to `false` to use only the user-provided `ignore` list.",
"type": "boolean",
"default": true
},
"skipLocal": {
"description": "Only report cross-directory duplicates.",
"type": "boolean",
"default": false
},
"crossLanguage": {
"description": "Enable cross-language clone detection by stripping type annotations.\n\nWhen enabled, TypeScript type annotations (parameter types, return types,\ngenerics, interfaces, type aliases) are stripped from the token stream,\nallowing detection of clones between `.ts` and `.js` files.",
"type": "boolean",
"default": false
},
"ignoreImports": {
"description": "Exclude module-wiring declarations from clone detection.\n\nDefaults to `true`: token-identical module wiring is a structural\nproperty of well-formatted code, not copy-paste, so it should not\nsurface as clone groups. Set to `false` to count module wiring again.\nWhen enabled, ES imports, re-export declarations, and top-level static\nCommonJS `require(\"...\")` binding declarations are stripped from the\ntoken stream before clone detection. Dynamic imports, side-effect\n`require()` calls, nested `require()` calls, dynamic require arguments,\nand mixed declarations are still counted.",
"type": "boolean",
"default": true
},
"normalization": {
"description": "Fine-grained normalization overrides on top of the detection mode.",
"$ref": "#/$defs/NormalizationConfig",
"default": {}
},
"minCorpusSizeForShingleFilter": {
"description": "Minimum tokenized file count before focused duplicate analysis prefilters\nunchanged files with k-token shingles.",
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 1024
},
"minCorpusSizeForTokenCache": {
"description": "Minimum source file count before the persistent duplication token cache\nactivates. Below this threshold the cache load/save overhead exceeds the\ntokenize savings, so the cache stays disabled even when not running with\n`--no-cache`.",
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 5000
}
}
},
"DetectionMode": {
"description": "Detection mode controlling how aggressively tokens are normalized.\n\nSince fallow uses AST-based tokenization (not lexer-based), whitespace and\ncomments are inherently absent from the token stream. The `Strict` and `Mild`\nmodes are currently equivalent. `Weak` mode additionally blinds string\nliterals. `Semantic` mode blinds all identifiers and literal values for\nType-2 (renamed variable) clone detection.",
"oneOf": [
{
"description": "All tokens preserved including identifier names and literal values (Type-1 only).",
"type": "string",
"const": "strict"
},
{
"description": "Default mode -- equivalent to strict for AST-based tokenization.",
"type": "string",
"const": "mild"
},
{
"description": "Blind string literal values (structure-preserving).",
"type": "string",
"const": "weak"
},
{
"description": "Blind all identifiers and literal values for structural (Type-2) detection.",
"type": "string",
"const": "semantic"
}
]
},
"NormalizationConfig": {
"description": "Fine-grained normalization overrides.\n\nEach option, when set to `Some(true)`, forces that normalization regardless of\nthe detection mode. When set to `Some(false)`, it forces preservation. When\n`None`, the detection mode's default behavior applies.",
"type": "object",
"properties": {
"ignoreIdentifiers": {
"description": "Blind all identifiers (variable names, function names, etc.) to the same hash.\nDefault in `semantic` mode.",
"type": [
"boolean",
"null"
]
},
"ignoreStringValues": {
"description": "Blind string literal values to the same hash.\nDefault in `weak` and `semantic` modes.",
"type": [
"boolean",
"null"
]
},
"ignoreNumericValues": {
"description": "Blind numeric literal values to the same hash.\nDefault in `semantic` mode.",
"type": [
"boolean",
"null"
]
}
}
},
"HealthConfig": {
"description": "Configuration for complexity health metrics (`fallow health`).",
"type": "object",
"properties": {
"maxCyclomatic": {
"description": "Maximum allowed cyclomatic complexity per function (default: 20).\nFunctions exceeding this threshold are reported.",
"type": "integer",
"format": "uint16",
"minimum": 0,
"maximum": 65535,
"default": 20
},
"maxCognitive": {
"description": "Maximum allowed cognitive complexity per function (default: 15).\nFunctions exceeding this threshold are reported.",
"type": "integer",
"format": "uint16",
"minimum": 0,
"maximum": 65535,
"default": 15
},
"maxCrap": {
"description": "Maximum allowed CRAP (Change Risk Anti-Patterns) score per function\n(default: 30.0). CRAP combines cyclomatic complexity with test\ncoverage: high complexity plus low coverage produces a high CRAP\nscore. Functions meeting or exceeding this threshold are reported.\nUse `--coverage` with Istanbul data for accurate per-function CRAP;\notherwise fallow estimates coverage from the module graph.",
"type": "number",
"format": "double",
"default": 30.0
},
"crapRefactorBand": {
"description": "Band below `maxCyclomatic` where CRAP-only findings also receive a\nsecondary `refactor-function` action (default: 5). Set to `0` to only\nsuggest refactoring when cyclomatic already meets the configured\nthreshold.",
"type": "integer",
"format": "uint16",
"minimum": 0,
"maximum": 65535,
"default": 5
},
"coverage": {
"description": "Path to Istanbul-format coverage data for accurate per-function CRAP\nscores. Relative paths resolve against the project root. The CLI\n`--coverage` flag and `FALLOW_COVERAGE` environment variable override\nthis value.",
"type": [
"string",
"null"
],
"default": null
},
"coverageRoot": {
"description": "Absolute prefix to strip from Istanbul file paths before CRAP matching.\nUse when coverage was generated under a different checkout root in CI\nor Docker. The CLI `--coverage-root` flag and `FALLOW_COVERAGE_ROOT`\nenvironment variable override this value.",
"type": [
"string",
"null"
],
"default": null
},
"ignore": {
"description": "Glob patterns to exclude from complexity analysis.",
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"thresholdOverrides": {
"description": "Per-file or per-function threshold overrides. These keep exceptional\nfunctions visible as configured numeric ceilings instead of hiding them\nbehind binary suppressions.",
"type": "array",
"items": {
"$ref": "#/$defs/HealthThresholdOverride"
}
},
"ownership": {
"description": "Ownership analysis configuration. Controls bot filtering and email\nprivacy mode for `--ownership` output.",
"$ref": "#/$defs/OwnershipConfig",
"default": {
"botPatterns": [
"*\\[bot\\]*",
"dependabot*",
"renovate*",
"github-actions*",
"svc-*",
"*-service-account*"
],
"emailMode": "handle"
}
},
"suggestInlineSuppression": {
"description": "Whether health JSON output emits `suppress-line` action hints\nalongside complexity findings (default: `true`). Set to `false` to\nopt out across the project: useful for teams that manage suppressions\nexclusively through `// fallow-ignore-*` comments authored by hand or\nthrough the `fallow.suppress` LSP code action, but who do not want\nCI-driven `suppress-line` action hints in their JSON output.\n`--baseline` activates auto-omission regardless of this setting,\nsince baseline files are a separate suppression mechanism.",
"type": "boolean",
"default": true
}
},
"additionalProperties": false
},
"HealthThresholdOverride": {
"description": "Per-file or per-function health threshold override.",
"type": "object",
"properties": {
"files": {
"description": "Project-root-relative file globs this override applies to.",
"type": "array",
"items": {
"type": "string"
}
},
"functions": {
"description": "Exact emitted function names this override applies to. Empty means every\nfunction in matching files.",
"type": "array",
"items": {
"type": "string"
}
},
"maxCyclomatic": {
"description": "Local cyclomatic complexity ceiling.",
"type": [
"integer",
"null"
],
"format": "uint16",
"minimum": 0,
"maximum": 65535
},
"maxCognitive": {
"description": "Local cognitive complexity ceiling.",
"type": [
"integer",
"null"
],
"format": "uint16",
"minimum": 0,
"maximum": 65535
},
"maxCrap": {
"description": "Local CRAP ceiling.",
"type": [
"number",
"null"
],
"format": "double"
},
"reason": {
"description": "Human-readable rationale for the exception.",
"type": [
"string",
"null"
]
}
},
"additionalProperties": false,
"required": [
"files"
]
},
"OwnershipConfig": {
"description": "Configuration for ownership analysis (`fallow health --hotspots --ownership`).",
"type": "object",
"properties": {
"botPatterns": {
"description": "Glob patterns (matched against the author email local-part) that\nidentify bot or service-account commits to exclude from ownership\nsignals. Overrides the defaults entirely when set.",
"type": "array",
"items": {
"type": "string"
},
"default": [
"*\\[bot\\]*",
"dependabot*",
"renovate*",
"github-actions*",
"svc-*",
"*-service-account*"
]
},
"emailMode": {
"description": "Privacy mode for emitted author emails. Defaults to `handle`.\nOverride on the CLI via `--ownership-emails=raw|handle|anonymized`.\nThe legacy spelling `hash` is still accepted for compatibility.",
"$ref": "#/$defs/EmailMode",
"default": "handle"
}
}
},
"EmailMode": {
"description": "Privacy mode for author emails emitted in ownership output.\n\nDefaults to `handle` (local-part only, no domain) so SARIF and JSON\nartifacts do not leak raw email addresses into CI pipelines.",
"oneOf": [
{
"description": "Show the raw email address as it appears in git history.\nUse for public repositories where history is already exposed.",
"type": "string",
"const": "raw"
},
{
"description": "Show the local-part only (before the `@`). Mailmap-resolved where possible.\nDefault. Balances readability and privacy.",
"type": "string",
"const": "handle"
},
{
"description": "Show a stable `xxh3:<16hex>` pseudonym derived from the raw email.\nNon-cryptographic; suitable to keep raw emails out of CI artifacts\n(SARIF, code-scanning uploads) but not as a security primitive:\na known list of org emails can be brute-forced into a rainbow table.\nUse in regulated environments where even local-parts are sensitive.",
"type": "string",
"const": "anonymized"
},
{
"description": "Legacy spelling for [`EmailMode::Anonymized`].",
"type": "string",
"const": "hash"
}
]
},
"RulesConfig": {
"description": "Per-issue-type severity configuration.\n\nControls which issue types cause CI failure, are reported as warnings,\nor are suppressed entirely. Most fields default to `Severity::Error`.\n\nRule names use kebab-case in config files (e.g., `\"unused-files\": \"error\"`).",
"type": "object",
"properties": {
"unused-files": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"unused-exports": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"unused-types": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"private-type-leaks": {
"$ref": "#/$defs/Severity",
"default": "off"
},
"unused-dependencies": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"unused-dev-dependencies": {
"$ref": "#/$defs/Severity",
"default": "warn"
},
"unused-optional-dependencies": {
"$ref": "#/$defs/Severity",
"default": "warn"
},
"unused-enum-members": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"unused-class-members": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"unused-store-members": {
"description": "Store members (Pinia `state` / `getters` / `actions` key, or a\nsetup-store returned key) declared but never accessed by any consumer\nproject-wide. Defaults to `warn`, not `error` like the closed-set\nclass/enum member rules: a store has an OPEN declaration surface\n(plugins, `$onAction`, dynamic dispatch) so analyzer confidence is\ngenuinely lower; warn encodes that without failing CI. Promotable to\n`error` once validated on a codebase.",
"$ref": "#/$defs/Severity",
"default": "error"
},
"unprovided-injects": {
"description": "Vue `inject(KEY)` / Svelte `getContext(KEY)` whose symbol KEY is\n`provide`/`setContext`'d nowhere in the project (the\ninjected-never-provided dead-half). Defaults to `warn`, not `error`:\na DI key has an open provide surface (plugins, app-level provide) so\nanalyzer confidence is lower; warn encodes that without failing CI.",
"$ref": "#/$defs/Severity",
"default": "error"
},
"unrendered-components": {
"description": "Vue/Svelte single-file component reachable in the module graph but\nrendered nowhere in the project (the imported-but-never-rendered\ndead-half). Defaults to `warn`, not `error`: a component can be rendered\nreflectively (dynamic `<component :is>`), so analyzer confidence is\nlower; warn encodes that without failing CI.",
"$ref": "#/$defs/Severity",
"default": "error"
},
"unused-component-props": {
"description": "Vue `<script setup>` `defineProps` declared prop referenced nowhere\ninside its own single-file component (neither `<script>` nor\n`<template>`). The single-file dead-input direction. Defaults to `warn`,\nnot `error`: a prop can be part of a deliberately-stable public component\nAPI, so analyzer confidence is lower; warn encodes that without failing\nCI.",
"$ref": "#/$defs/Severity",
"default": "error"
},
"unused-component-emits": {
"description": "Vue `<script setup>` `defineEmits` declared event emitted nowhere inside\nits own single-file component (no `emit('<name>')` call). The single-file\ndead-input direction. Defaults to `warn`, not `error`: an emit can be part\nof a deliberately-stable public component API, so analyzer confidence is\nlower; warn encodes that without failing CI.",
"$ref": "#/$defs/Severity",
"default": "error"
},
"unused-server-actions": {
"description": "Next.js Server Action (an export of a `\"use server\"` file) referenced by\nno code in the project: no import-and-call, no `action={fn}` binding, no\n`<form action={fn}>`. Cross-graph dead-export direction, reclassified out\nof `unused-export` for `\"use server\"` files. Defaults to `warn`, not\n`error`: the rule is new and false-negative-preferring, and reflective\naction-dispatch shapes can hide a real consumer; warn encodes that\nwithout failing CI until corpus-validated.",
"$ref": "#/$defs/Severity",
"default": "error"
},
"unused-load-data-keys": {
"description": "SvelteKit `+page.{ts,server.ts,js,server.js}` `load()` return-object key\nread by no consumer: not off the sibling `+page.svelte`'s `data.<key>`,\nnor project-wide via `page.data.<key>` / `$page.data.<key>`. Cross-file\ndead-input direction. Defaults to `warn`, not `error`: the rule is new and\nfalse-negative-preferring (a whole-object `data` pass abstains), and a\nload fetch can have side effects so deletion is a human call; warn encodes\nthat without failing CI until corpus-validated.",
"$ref": "#/$defs/Severity",
"default": "warn"
},
"prop-drilling": {
"description": "React/Preact prop forwarded unchanged through `>= N` intermediate\npass-through components until a component that substantively consumes it.\nA graph-derived health signal. Defaults to `off` (opt-in), like\n`private-type-leak` / `security-*`: the located per-chain records and the\nsmall capped health penalty are dormant until the user enables the rule.",
"$ref": "#/$defs/Severity",
"default": "off"
},
"thin-wrapper": {
"description": "A React/Preact component whose entire body is `return <Child {...props}/>`\n(pure structural indirection, a candidate for inlining). A graph-derived\nhealth signal. Defaults to `off` (opt-in), like `prop-drilling`: the\nlocated per-wrapper records are dormant until the user enables the rule.",
"$ref": "#/$defs/Severity",
"default": "off"
},
"duplicate-prop-shape": {
"description": "Three or more React/Preact components across two or more files whose\nstatically-harvested prop NAME set is identical after stripping ubiquitous\nDOM / passthrough names (a missing shared `Props` type / base component).\nA graph-derived structural-refactor health signal. Defaults to `off`\n(opt-in), like `thin-wrapper`: the located per-component records are\ndormant until the user enables the rule.",
"$ref": "#/$defs/Severity",
"default": "off"
},
"unresolved-imports": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"unlisted-dependencies": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"duplicate-exports": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"type-only-dependencies": {
"$ref": "#/$defs/Severity",
"default": "warn"
},
"test-only-dependencies": {
"$ref": "#/$defs/Severity",
"default": "warn"
},
"circular-dependencies": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"re-export-cycle": {
"$ref": "#/$defs/Severity",
"default": "warn"
},
"boundary-violation": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"coverage-gaps": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"feature-flags": {
"$ref": "#/$defs/Severity",
"default": "off"
},
"stale-suppressions": {
"$ref": "#/$defs/Severity",
"default": "warn"
},
"unused-catalog-entries": {
"$ref": "#/$defs/Severity",
"default": "warn"
},
"empty-catalog-groups": {
"$ref": "#/$defs/Severity",
"default": "warn"
},
"unresolved-catalog-references": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"unused-dependency-overrides": {
"$ref": "#/$defs/Severity",
"default": "warn"
},
"misconfigured-dependency-overrides": {
"$ref": "#/$defs/Severity",
"default": "error"
},
"security-client-server-leak": {
"description": "Opt-in (default off): a `\"use client\"` file that transitively imports a\nmodule reading a non-public `process.env` secret. Surfaced only by\n`fallow security`; never under bare `fallow` or the `audit` gate.",
"$ref": "#/$defs/Severity",
"default": "off"
},
"security-sink": {
"description": "Opt-in (default off): a syntactic tainted-sink candidate matched against\nthe data-driven catalogue (`security_matchers.toml`). ONE knob gates ALL\ncatalogue categories. Surfaced only by `fallow security`; never under\nbare `fallow` or the `audit` gate.",
"$ref": "#/$defs/Severity",
"default": "off"
},
"policy-violation": {
"description": "Master severity for rule-pack findings (`rulePacks` config). Defaults\nto `warn` so enabling a brand-new policy pack never hard-fails CI on\nits first run; individual pack rules opt up via `\"severity\": \"error\"`.\n`off` is a kill switch that disables the whole evaluator (per-rule\nseverity cannot resurrect it).",
"$ref": "#/$defs/Severity",
"default": "warn"
},
"invalid-client-export": {
"description": "A `\"use client\"` file that exports a Next.js server-only /\nroute-segment config name (e.g. `metadata`, `revalidate`, `GET`).\nNext.js rejects this at build time; fallow catches it statically.\nDefaults to `warn`.",
"$ref": "#/$defs/Severity",
"default": "warn"
},
"mixed-client-server-barrel": {
"description": "A barrel file that re-exports BOTH a `\"use client\"` origin module AND a\nserver-only origin module. Importing one name from such a barrel drags\nthe other's directive context across the React Server Components\nboundary (the Next.js App Router footgun). Defaults to `warn`.",
"$ref": "#/$defs/Severity",
"default": "warn"
},
"misplaced-directive": {
"description": "A `\"use client\"` / `\"use server\"` directive written as an expression\nstatement after a non-directive statement (an import, a const), so the\nRSC bundler parses it as an ordinary string and silently ignores it.\nThe intended client/server boundary never takes effect. Defaults to\n`warn`.",
"$ref": "#/$defs/Severity",
"default": "warn"
},
"route-collision": {
"description": "Two or more Next.js App Router route files that resolve to the same URL\nwithin one app-root. Next.js fails the build (\"You cannot have two\nparallel pages that resolve to the same path\"); fallow catches it\nstatically and names every colliding file. Defaults to `error`: the\nproject already fails `next build`, so flagging it as an error aligns\nfallow's exit code with the build it mirrors.",
"$ref": "#/$defs/Severity",
"default": "error"
},
"dynamic-segment-name-conflict": {
"description": "Sibling Next.js dynamic route segments at one tree position using\ndifferent param spellings (`[id]` vs `[slug]`). Next.js throws \"You\ncannot use different slug names for the same dynamic path\" at dev and\nproduction runtime when the position is hit; `next build` does NOT catch\nit (the build succeeds), so CI passes while the route crashes on its\nfirst request. fallow catches it statically. Defaults to `error`: the\nroute is a deterministic runtime crash on first request, so failing CI\nis the honest signal even though `next build` stays green (this is the\n\"error-runtime\" severity tier, shared with `route-collision`).",
"$ref": "#/$defs/Severity",
"default": "error"
}
}
},
"Severity": {
"description": "Severity level for rules.\n\nControls whether an issue type causes CI failure (`error`), is reported\nwithout failing (`warn`), or is suppressed entirely (`off`).",
"oneOf": [
{
"description": "Report and fail CI (non-zero exit code).",
"type": "string",
"const": "error"
},
{
"description": "Report but don't fail CI.",
"type": "string",
"const": "warn"
},
{
"description": "Don't detect or report.",
"type": "string",
"const": "off"
}
]
},
"BoundaryConfig": {
"description": "Architecture boundary configuration.",
"type": "object",
"properties": {
"preset": {
"description": "Optional built-in preset.",
"anyOf": [
{
"$ref": "#/$defs/BoundaryPreset"
},
{
"type": "null"
}
]
},
"zones": {
"description": "Zone definitions.",
"type": "array",
"items": {
"$ref": "#/$defs/BoundaryZone"
},
"default": []
},
"rules": {
"description": "Zone import rules.",
"type": "array",
"items": {
"$ref": "#/$defs/BoundaryRule"
},
"default": []
},
"coverage": {
"description": "Optional policy for files that match no zone.",
"$ref": "#/$defs/BoundaryCoverageConfig"
},
"calls": {
"description": "Optional forbidden-call policy for zoned files.",
"$ref": "#/$defs/BoundaryCallsConfig"
}
}
},
"BoundaryPreset": {
"description": "Built-in architecture presets.",
"oneOf": [
{
"description": "Layered architecture.",
"type": "string",
"const": "layered"
},
{
"description": "Hexagonal / ports-and-adapters.",
"type": "string",
"const": "hexagonal"
},
{
"description": "Feature-Sliced Design.",
"type": "string",
"const": "feature-sliced"
},
{
"description": "Bulletproof React.",
"type": "string",
"const": "bulletproof"
}
]
},
"BoundaryZone": {
"description": "A zone grouping files by directory pattern.",
"type": "object",
"properties": {
"name": {
"description": "Zone name.",
"type": "string"
},
"patterns": {
"description": "Membership patterns.",
"type": "array",
"items": {
"type": "string"
}
},
"autoDiscover": {
"description": "Directories whose children become zones.",
"type": "array",
"items": {
"type": "string"
}
},
"root": {
"description": "Optional subtree scope.",
"type": [
"string",
"null"
]
}
},
"required": [
"name"
]
},
"BoundaryRule": {
"description": "An import rule between zones.",
"type": "object",
"properties": {
"from": {
"description": "Source zone.",
"type": "string"
},
"allow": {
"description": "Allowed target zones.",
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"allowTypeOnly": {
"description": "Allowed type-only targets.",
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"from"
]
},
"BoundaryCoverageConfig": {
"description": "Boundary zone coverage policy.",
"type": "object",
"properties": {
"requireAllFiles": {
"description": "Report source files that do not match any boundary zone.",
"type": "boolean"
},
"allowUnmatched": {
"description": "Glob patterns for files that may remain unmatched by any zone.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
"BoundaryCallsConfig": {
"description": "Boundary forbidden-call policy. Applies only to files classified into a\nzone; unzoned files are unrestricted, matching the import rules.",
"type": "object",
"properties": {
"forbidden": {
"description": "Callee patterns that files in a zone may not call.",
"type": "array",
"items": {
"$ref": "#/$defs/ForbiddenCallRule"