Mori DocumentationGitHub ↗
Mori v0.34.0 documentation · Library scopes and support bundles require v0.33.0 or later. Check release notes against your installed version.
GUIDES & REFERENCE Markdown source ↗

Project configuration and ignore handling

Mori can load one strict JSON configuration named .mori.json. It searches from the current working directory upward. Use --config <path> to select a different file or --no-config to disable configuration loading.

Unknown fields, malformed JSON, non-regular files, and files larger than one MiB are errors. Relative baseline paths resolve from the configuration file's directory, and baseline documents are bounded to 16 MiB. A selected profile supplies defaults, explicit configuration fields override profile values, and explicit command-line values override both. Repeated --exclude and --language-pair values are additive. A command-line --comparison-domain overrides the configured domain. --sql-dialect overrides the configured SQL parser dialect.

Create a review-oriented config with mori init [directory]. The command writes deterministic, explicit values so the file remains reviewable if a future Mori version changes a profile. It never replaces an existing .mori.json unless --force is supplied. mori init --stdout writes the template without changing the filesystem, and --profile review|explore|sql selects the template.

Profiles

profile accepts:

Profiles deliberately do not guess project-specific test, migration, generated router, or framework exclusions. Add those only after auditing the repository's source categories and the first report.

The selected profile is recorded in schema-18 and later reports. Omitting profile retains the legacy defaults. A CLI --profile replaces a configured profile; explicit fields from the config are then applied, followed by explicit CLI flags. An explicit language-selection mode replaces the profile's mode, so --profile review --cross-language-only does not require a separate --same-language-only=false flag.

Fields

{
  "profile": "review",
  "threshold": 0.85,
  "min_tokens": 40,
  "max_groups": 250,
  "max_occurrences": 10,
  "max_pairs": 5000000,
  "max_file_bytes": 2097152,
  "workers": 8,
  "format": "json",
  "comparison_domain": "code",
  "fragment_selection": "all",
  "sql_dialect": "generic",
    "embedded_sql": false,
    "statement_blocks": false,
    "block_statements": 3,
    "max_blocks_per_function": 64,
  "ranking": "review",
  "priority_paths": ["**/auth/**=25", "**/security/**=25"],
  "same_language_only": true,
  "cross_language_only": false,
  "language_pairs": [],
  "fail_on_match": false,
  "require_coverage": true,
    "min_file_coverage": 0.95,
    "max_zero_fragment_files": 2,
    "fail_on_warning": false,
    "fail_on_parse_diagnostic": true,
  "exclude_generated": true,
  "baseline": "mori-baseline.json",
  "baseline_scope": "content",
  "exclude": ["**/*_test.go"],
  "respect_ignore": true
}

Named scopes

Monorepositories can keep reviewed scan surfaces in scopes and select one with --scope NAME:

{
  "profile": "review",
  "scopes": {
    "backend": {
      "roots": ["cmd", "internal"],
      "exclude": ["**/*_test.go"],
      "priority_paths": ["**/auth/**=25"]
    },
    "ios": {
      "roots": ["Sources", "Packages"],
      "same_language_only": true
    }
  }
}

Scope roots must be relative paths inside the configuration project. Top-level settings apply first, the selected scope overrides or adds its settings, and explicit CLI flags remain last. Positional scan paths override the scope's default roots without discarding its other policy. The selected name and roots participate in baseline scan-profile digests and appear as configuration.scope and configuration.scope_roots.

comparison_domain accepts one case-insensitive domain ID printed by mori languages. The selected domain is applied before parsing, so other domains do not affect file counts, warnings, focus, or pair limits. An empty or omitted value selects every registered domain.

sql_dialect accepts generic or postgresql and defaults to generic. It selects the parser for every discovered .sql file and does not affect non-SQL files. Use separate scans when one repository contains multiple SQL dialects.

fragment_selection accepts all (the default), production, or tests. The equivalent CLI option is --fragment-selection; named scopes can override it. It selects syntax-classified fragments after parsing, independently of file roots and exclusions. Conventional test paths and explicit Rust test attributes or unambiguously test-only conditional modules supply classification. See the exact conventions. Unclassified fragments remain production/unknown; tests is therefore not a universal test detector. Ordinary production closures remain eligible. Excluded fragment counts stay visible in file coverage, and a file with only excluded fragments is not evidence of analyzed comparison coverage. The selection participates in baseline and receipt compatibility.

Keep the base all selection for inclusive staged gates; choose a separate named scope for production-only exploration. This field advances the recorded configuration contract to schema 2; .mori.json still has no schema-version field and previously valid files remain readable.

Classification overrides

Use production_paths to retain a shipped library such as django/test in production selection. Use test_paths for test code with unconventional names. Both accept arrays of exact file or directory paths, with directory-boundary prefix matching, not globs. Config paths resolve relative to the configuration file. Named scopes can replace either array, including with an empty array. Repeated --production-path PATH and --test-path PATH flags add command-line paths relative to the current working directory. Opposing overlapping rules are rejected. Overrides supersede conventional path and inline test rules, but do not bypass discovery, ignores, generated exclusions or parse failures.

{
  "fragment_selection": "production",
  "production_paths": ["django/test"],
  "test_paths": ["tools/test-support"]
}

Effective configuration reports both optional arrays. Per-file excluded counts remain available for inspecting the result. The paths participate in baseline, receipt and staged-cache identity, and are redacted with other report paths. The configuration contract is now 3. Project-contract schema stays 1 and old contracts remain strictly readable and eligible for managed upgrade. Report 22 and baseline 4 retain their versions: the optional arrays are additive and omitted when unused, preserving existing default-profile digests. Consumers using strict JSON Schema validation should refresh the current schema file.

embedded_sql is an explicit opt-in for direct string arguments to recognized Go database methods. It requires comparison_domain to be sql-query and uses the selected sql_dialect. Arbitrary strings, variables, concatenations, and receiver-type inference remain outside extraction. Fixed safety limits skip a host file above 1,000 recognized calls and skip a decoded query above 256 KiB, with coverage warnings.

statement_blocks enables fixed-size statement windows inside code functions. block_statements accepts 2 through 10 and defaults to 3. max_blocks_per_function accepts 1 through 256 and defaults to 64. Exceeding the cap skips block extraction for that function with a visible coverage warning instead of returning a partial hidden sample. Embedded SQL and statement blocks are mutually exclusive scan modes.

ranking accepts structural or review and defaults to structural. Structural ordering sorts by similarity score, shared evidence mass, represented location-pair count, and stable identity. Review ordering first uses explicit location signals, then the existing structural order. It favors distinctively named candidates across directories, other cross-directory and cross-file candidates, and identities representing repeated location pairs. It does not change scores, fingerprints, candidate membership, focus priority, or baseline suppression. The equivalent command-line form is --ranking review.

priority_paths contains at most 32 deterministic GLOB=WEIGHT rules. Each weight must be an integer from 1 to 100. When review ranking is selected, a group receives a rule's weight once if either side of any retained location pair matches the doublestar glob. Repeat the equivalent --priority-path option for command-line use. Rules change presentation order only: they do not change similarity, fingerprints, baselines, or whether a group qualifies. Use them for reviewed project categories such as authorization or security paths; a matching path is not evidence that a finding is risky or actionable.

same_language_only compares within review families. TypeScript and TSX are one family and remain comparable, as do Bash/POSIX shell and Zsh, and PHP and Hack. It is mutually exclusive with cross_language_only and language_pairs. language_pairs accepts language IDs or families; the typescript selector includes both TypeScript and TSX, while php-hack includes both PHP and Hack. The concrete php selector still selects only PHP. Explicit pairs must belong to a selected comparison domain.

max_groups bounds distinct content-pair identities. max_occurrences bounds the locations shown for each fingerprint while retaining the exact occurrence count. Zero opts into an unlimited value and should be used deliberately. --max-matches remains a deprecated alias for --max-groups.

require_coverage requires at least one supported file and one extracted comparison fragment. When the requirement is not met, Mori still writes the deterministic report and its coverage warning, then exits with status 4. This distinguishes an unsupported or inapplicable scan from a clean scan with no qualifying similarity groups. The command-line override is --require-coverage; use --require-coverage=false to disable a configured requirement for an explicitly exploratory invocation.

min_file_coverage accepts 0 through 1; zero disables the policy. Its numerator is the number of analyzed files producing at least one comparison fragment, and its denominator is every analyzed supported file. Files with status excluded_generated remain visible evidence but do not enter the denominator. The command-line form is --min-file-coverage.

max_zero_fragment_files accepts -1 or greater; -1 disables the policy. It limits analyzed supported files that produced no comparison fragments. The command-line form is --max-zero-fragment-files.

fail_on_warning fails on any discovery, parsing, resource, focus, or coverage warning. fail_on_parse_diagnostic is narrower and fails only when a file has Tree-sitter parse diagnostics. Their command-line forms are --fail-on-warning and --fail-on-parse-diagnostic. Every strict coverage policy writes the report first and then exits with status 4. Coverage policy failure takes precedence over finding status 3, and baseline update or prune checks the policies before changing its baseline file.

exclude_generated excludes supported files only when Mori recognizes a conservative generated-source comment marker in the first 8 KiB, including Code generated ... DO NOT EDIT, @generated, and common automatically generated-file headers. The exact Generated by typeshare header is also recognized without treating arbitrary Generated by comments as generated source. The equivalent command-line flag is --exclude-generated. Excluded files remain visible in file_coverage with status excluded_generated, the recognized marker class, and zero fragments. The default remains false, so upgrading never silently removes source from a scan.

Ignore files

Directory scans honor .gitignore and .moriignore files from the working directory through nested scan directories. Rules are evaluated in order; .moriignore is read after .gitignore in the same directory, so it can add or negate repository rules. Common VCS, dependency, and build directories are still excluded by Mori's built-in policy. A linked worktree's .git control file is VCS metadata and is not included in unsupported-extension counts.

Ignore files support the Git-style rules Mori needs for repository discovery: comments, negation, directory suffixes, anchoring, and *, ?, and ** wildcards. An ignored directory is still traversed when an applicable negation may re-include a descendant. Invalid, oversized, symlinked, or non-regular ignore files fail the scan instead of silently weakening exclusions.

Ignore files affect directory traversal only. A file passed explicitly remains visible. Repeated --exclude globs remain additive and continue to exclude an explicitly requested file. Use --no-ignore to disable both ignore-file types.

Schema-18 JSON reports record the effective profile, named scope, strict coverage policies, options, and every loaded ignore file with its SHA-256 content evidence under configuration so a scan can be reproduced. Review focus remains CLI-only: --focus-path is repeatable, --changed-since always requires an explicit locally available Git revision, and repeatable --changed-worktree PATH=REVISION values give every additional Git worktree its own revision. Dynamic Git revisions are intentionally not project config.

--staged is also CLI-only. It reads tracked sources, ignore files, .mori.json, and any configured or explicit baseline from one immutable Git-index snapshot and records the HEAD and index digest under configuration.input. A staged baseline must be a tracked regular file inside that worktree and no larger than 16 MiB; Mori fails closed rather than reading an untracked, external, or working-tree copy. Combine staged mode with --include-focused to bypass ordinary ignore rules for changed files while retaining explicit excludes, generated-source policy, and resource bounds. Add --require-focused-coverage when every supported non-deleted focused path must be analyzed; failures still write the report and exit with status 4.

See Scan selection controls for the complete validation, execution, compatibility, and reporting contract.

Scores describe structural similarity; they do not prove behavioral equivalence.
Source stays local. Mori on GitHub