Skip to content

Configure domains

Domains are named groups of source paths with their own coverage minimums. They work the same way for all sorts of languages — Python, TypeScript, Java, Rust, Go, and the rest. They are not language constructs and do not wrap packages, modules, or crates — they match directories and files in your repo so auth can require 90% while utilities stay at 60%.

Each domain has a name, match patterns, and a minimum coverage threshold:

policy:
domains:
- name: core
match: ["src/core/**"]
min: 85
- name: api
match: ["src/api/**"]
min: 80

match accepts either dialect. coverctl picks the resolver from the pattern shape and the project’s language:

Dialect When to use Examples
Path globs Python, JavaScript/TypeScript, Rust, Java, C#, and most non-Go trees src/api/**, lib/**, app/services/**
Go package paths Go modules (go.mod present) ./internal/auth/..., ./pkg/utils, ./cmd/...
Pattern Matches
src/core/** Everything under src/core/
src/api/** Everything under src/api/
lib/** Everything under lib/
src/*/service/** service trees one level under src/
Pattern Matches
./internal/core/... All packages under internal/core/
./cmd/... All packages under cmd/
./pkg/utils Only the pkg/utils package
./internal/*/service service packages one level deep
domains:
- name: core
match: ["src/core/**"]
min: 85
- name: api
match:
- "src/api/**"
- "src/handlers/**"
min: 80
- name: utils
match: ["src/utils/**"]
min: 70

Language-specific starter configs (including language: and profile:) are in Quick start.

Use the exclude field to skip files from coverage analysis:

exclude:
- "**/generated/**"
- "**/mocks/**"
- "**/*_test.go"
- "**/testdata/**"
- "**/migrations/**"
Pattern Matches
**/generated/** Any file under a generated directory
**/*_mock.go Any file ending in _mock.go
src/legacy/* Files directly in src/legacy/

coverctl can automatically detect domains from your project structure:

Terminal window
# Preview detected domains
coverctl detect --dry-run
# Write config
coverctl detect

Detection follows the layout of the detected language, not a single package model:

Language family Typical detected match shapes
Go ./internal/..., ./cmd/..., ./pkg/...
Python / JS / TS / Rust / … src/**, src/api/**, lib/**, crate/layout-specific globs

Go layout heuristics (when go.mod is present)

Section titled “Go layout heuristics (when go.mod is present)”
Directory Detected Domain
cmd/ CLI entry points
internal/core/ Core business logic
internal/domain/ Domain layer
internal/application/ Application services
internal/infrastructure/ Infrastructure adapters
internal/api/ API layer
pkg/ Public packages

Directories named generated, mocks, testdata, or vendor are automatically excluded. For other languages, run coverctl detect --dry-run to see the proposed globs for your tree.

When a file matches multiple domains, it’s assigned to the first matching domain. Order your domains from most specific to least specific:

domains:
- name: core-critical
match: ["src/core/critical/**"]
min: 95
- name: core
match: ["src/core/**"]
min: 85

With annotations.enabled: true, you can override domain assignment in source comments (language comment syntax varies; Go shown):

// coverctl:domain=critical
package validator
func Validate() error {
// This file belongs to "critical" domain
}
// coverctl:ignore
package generated
// This file is excluded from coverage
  1. Start with auto-detection: Use coverctl detect --dry-run to see suggested domains for your language
  2. Group by criticality: Higher thresholds for core business logic — names are labels, not package wrappers
  3. Use meaningful names: Domain names appear in reports and agent tool output
  4. Keep excludes minimal: Only exclude truly non-testable code
  5. Review overlaps: Check for unintended domain assignments

See Also