Complete reference for configuring Harness Engineering projects via harness.config.json.
Harness Engineering projects are configured via harness.config.json in the project root. The configuration is validated against a Zod schema at runtime.
{
"version": 1,
"name": "my-project",
"rootDir": "."
}- Type:
1(literal) - Required: Yes
Schema version number. Must be 1.
- Type:
string - Required: No
Human-readable project name used in logs and reports.
- Type:
string - Default:
"." - Required: No
Root directory of the project, relative to the config file location. All patterns in other fields are resolved relative to this directory.
- Type:
string - Default:
"./AGENTS.md" - Required: No
Path to the AGENTS.md file that defines agent roles and responsibilities.
- Type:
string - Default:
"./docs" - Required: No
Path to the documentation directory used by doc validation and generation tools.
- Type:
number(integer, minimum0) - Required: No
How often (in milliseconds) to check for CLI updates. Omit or set to 0 to disable update checks.
- Type:
Array<Layer> - Required: No
Defines the dependency layers in your project. Each layer declares which other layers it may depend on, enabling enforcement of a strict dependency hierarchy.
| Field | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Unique layer identifier |
pattern |
string |
Yes | Glob pattern matching files in this layer |
allowedDependencies |
string[] |
Yes | Names of layers this layer may import from |
{
"layers": [
{ "name": "types", "pattern": "src/types/**", "allowedDependencies": [] },
{ "name": "repository", "pattern": "src/repository/**", "allowedDependencies": ["types"] },
{
"name": "service",
"pattern": "src/service/**",
"allowedDependencies": ["types", "repository"]
},
{
"name": "api",
"pattern": "src/api/**",
"allowedDependencies": ["types", "repository", "service"]
}
]
}Layers are evaluated top-down. A file matching the api pattern that imports from a module matching types is allowed because "types" appears in allowedDependencies. An import from api into repository that is not listed would be flagged as a violation.
- Type:
Array<ForbiddenImport> - Required: No
Defines import restrictions that prevent specific file patterns from importing certain modules. Useful for keeping layers free of heavy runtime dependencies.
| Field | Type | Required | Description |
|---|---|---|---|
from |
string |
Yes | Glob pattern of source files the rule applies to |
disallow |
string[] |
Yes | Module names or patterns that must not be imported |
message |
string |
No | Custom error message shown when the rule is violated |
{
"forbiddenImports": [
{
"from": "src/types/**",
"disallow": ["express", "pg"],
"message": "Types layer must not depend on runtime libraries"
},
{
"from": "src/repository/**",
"disallow": ["express"],
"message": "Repository layer must not depend on the HTTP framework"
}
]
}- Type:
BoundaryConfig - Required: No
Configures boundary enforcement for files that must have a corresponding schema definition.
| Field | Type | Required | Description |
|---|---|---|---|
requireSchema |
string[] |
Yes | Glob patterns for files that must have a schema |
{
"boundaries": {
"requireSchema": ["src/api/**", "src/events/**"]
}
}- Type:
AgentConfig - Required: No
Controls how agent tasks are executed.
| Field | Type | Default | Description |
|---|---|---|---|
executor |
"subprocess" | "cloud" | "noop" |
"subprocess" |
Execution backend for agent tasks |
timeout |
number |
300000 |
Task timeout in milliseconds (5 min default) |
skills |
string[] |
-- | List of skill names available to the agent |
{
"agent": {
"executor": "subprocess",
"timeout": 600000,
"skills": ["check-dependencies", "detect-entropy", "analyze-diff"]
}
}- Type:
EntropyConfig - Required: No
Configures entropy detection, which identifies high-entropy strings (potential secrets or credentials) in source files.
| Field | Type | Default | Description |
|---|---|---|---|
excludePatterns |
string[] |
["**/node_modules/**", "**/*.test.ts"] |
Glob patterns to exclude from entropy scans |
autoFix |
boolean |
false |
Automatically apply fixes when detected |
{
"entropy": {
"excludePatterns": ["**/node_modules/**", "**/*.test.ts", "**/fixtures/**", "**/*.snap"],
"autoFix": false
}
}- Type:
SecurityConfig - Required: No
Configures security scanning for the project. When enabled, Harness scans source files for security issues such as hardcoded credentials, insecure patterns, and known vulnerabilities.
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
boolean |
true |
Whether security scanning is enabled |
strict |
boolean |
false |
When true, fail on any security warning |
rules |
Record<string, "off" | "error" | "warning" | "info"> |
-- | Rule-specific severity overrides keyed by rule ID |
exclude |
string[] |
-- | Glob patterns to exclude from security scans |
{
"security": {
"enabled": true,
"strict": false,
"rules": {
"SEC-CRY-001": "warning"
},
"exclude": ["**/node_modules/**", "**/dist/**", "**/*.test.ts", "**/tests/fixtures/**"]
}
}- Type:
PerformanceConfig - Required: No
Configures performance budgets and complexity thresholds. Each sub-field accepts a free-form record so you can define project-specific thresholds.
| Field | Type | Default | Description |
|---|---|---|---|
complexity |
Record<string, any> |
-- | Complexity thresholds per module or pattern |
coupling |
Record<string, any> |
-- | Coupling limits between modules |
sizeBudget |
Record<string, any> |
-- | Size budget for bundles or directories |
Additional properties are allowed and passed through to performance analyzers.
{
"performance": {
"complexity": {
"enabled": true,
"thresholds": {
"cyclomaticComplexity": { "error": 15, "warn": 10 },
"nestingDepth": { "warn": 4 },
"functionLength": { "warn": 50 },
"parameterCount": { "warn": 5 }
}
},
"coupling": {
"enabled": true,
"thresholds": {
"fanOut": { "warn": 15 },
"fanIn": { "info": 20 },
"couplingRatio": { "warn": 0.7 }
}
},
"sizeBudget": {
"enabled": false,
"budgets": {}
}
}
}- Type:
DesignConfig - Required: No
Configures design system and aesthetic consistency enforcement.
| Field | Type | Default | Description |
|---|---|---|---|
strictness |
"strict" | "standard" | "permissive" |
"standard" |
Strictness of design system enforcement |
platforms |
Array<"web" | "mobile"> |
[] |
Supported target platforms |
tokenPath |
string |
-- | Path to design tokens file (JSON or CSS) |
aestheticIntent |
string |
-- | Brief description of the intended aesthetic direction |
{
"design": {
"strictness": "strict",
"platforms": ["web", "mobile"],
"tokenPath": "src/tokens/design-tokens.json",
"aestheticIntent": "Minimal, accessible, high-contrast"
}
}- Type:
I18nConfig - Required: No
Configures internationalization management including locale settings, translation framework, and coverage requirements.
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
boolean |
false |
Whether i18n management is enabled |
strictness |
"strict" | "standard" | "permissive" |
"standard" |
Strictness of i18n rule enforcement |
sourceLocale |
string |
"en" |
The primary language used for development |
targetLocales |
string[] |
[] |
Locales that translations are required for |
framework |
"auto" | "i18next" | "react-intl" | "vue-i18n" | "flutter-intl" | "apple" | "android" | "custom" |
"auto" |
The i18n framework in use |
format |
string |
"json" |
Storage format for translation files |
messageFormat |
"icu" | "i18next" | "custom" |
"icu" |
Syntax used for message formatting |
keyConvention |
"dot-notation" | "snake_case" | "camelCase" | "custom" |
"dot-notation" |
Convention for translation keys |
translationPaths |
Record<string, string> |
-- | Mapping of locales to their file paths |
platforms |
Array<"web" | "mobile" | "backend"> |
[] |
Platforms targeted by this configuration |
industry |
string |
-- | Industry vertical for contextual translations |
coverage |
I18nCoverageConfig |
-- | Translation coverage requirements |
pseudoLocale |
string |
-- | Locale used for pseudo-localization testing |
mcp |
I18nMcpConfig |
-- | MCP server for AI-assisted translation |
| Field | Type | Default | Description |
|---|---|---|---|
minimumPercent |
number |
100 |
Minimum required translation percentage (0--100) |
requirePlurals |
boolean |
true |
Whether plural forms are required for all keys |
detectUntranslated |
boolean |
true |
Whether to detect untranslated strings in source |
| Field | Type | Required | Description |
|---|---|---|---|
server |
string |
Yes | Name or URL of the MCP server |
projectId |
string |
No | Project ID on the remote i18n platform |
{
"i18n": {
"enabled": true,
"strictness": "strict",
"sourceLocale": "en",
"targetLocales": ["fr", "de", "ja"],
"framework": "react-intl",
"format": "json",
"coverage": {
"minimumPercent": 95,
"requirePlurals": true,
"detectUntranslated": true
},
"mcp": {
"server": "crowdin-mcp",
"projectId": "my-project-123"
}
}
}- Type:
ReviewConfig - Required: No
Configures code review orchestration, including which AI models to use at different tiers.
| Field | Type | Required | Description |
|---|---|---|---|
model_tiers |
ModelTierConfig |
No | Custom model tier mappings for reviews |
| Field | Type | Required | Description |
|---|---|---|---|
fast |
string |
No | Model ID for fast/cheap operations |
standard |
string |
No | Model ID for standard reasoning tasks |
strong |
string |
No | Model ID for complex/critical analysis |
{
"review": {
"model_tiers": {
"fast": "claude-haiku-4",
"standard": "claude-sonnet-4",
"strong": "claude-opus-4"
}
}
}- Type:
IntegrationsConfig - Required: No
Tracks which MCP peer integrations are enabled and which have been dismissed by the user. Used by the harness doctor command to tailor integration suggestions.
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
string[] |
[] |
Tier 1 integrations explicitly enabled by the user |
dismissed |
string[] |
[] |
Integrations the user does not want doctor to suggest |
{
"integrations": {
"enabled": ["github-mcp", "linear-mcp"],
"dismissed": ["jira-mcp"]
}
}- Type:
KnowledgeConfig - Required: No
Configures the knowledge graph and domain inference. Domain inference classifies extracted nodes (files, signals, business facts) into a domain bucket (e.g. payments, auth, skills) used by drift detection, gap reporting, and the knowledge pipeline.
| Field | Type | Default | Description |
|---|---|---|---|
domainPatterns |
string[] |
[] |
Additional path patterns whose captured directory becomes the inferred domain |
domainBlocklist |
string[] |
[] |
Additional directory names that should never be treated as domains |
Both fields extend (not replace) the built-in defaults.
Each entry in domainPatterns uses the form prefix/<dir> where prefix is a single path segment and <dir> is the literal placeholder that captures the next segment as the domain name. The full regex is ^[\w.-]+\/<dir>$.
Built-in patterns (always active):
packages/<dir>— e.g.packages/auth/...→authapps/<dir>services/<dir>src/<dir>lib/<dir>
Built-in blocklist (always active): node_modules, .harness, dist, build, .git, coverage, .next, .turbo, .cache, out, tmp.
Domain inference resolves in this order (first match wins):
- Explicit
metadata.domainon the node - User-configured
knowledge.domainPatterns - Built-in patterns (
packages/<dir>,apps/<dir>, etc.) - Generic first-segment fallback (the leading directory of the path)
'unknown'if every prior step fails
Two refinements apply to all stages:
- Extension allowlist: only files with
.ts,.tsx,.js,.jsx,.mjs, or.cjsextensions are inferred. Other extensions return'unknown'. - Symmetric blocklist: if a path matches a pattern but the captured segment is on the blocklist, the result is
'unknown'directly — it does not fall through to later steps.
The canonical implementation lives at packages/graph/src/ingest/domain-inference.ts.
A monorepo that publishes Claude Code skills under agents/skills/<name>/ and wants those classified as the skills domain:
{
"knowledge": {
"domainPatterns": ["agents/skills/<dir>"],
"domainBlocklist": ["fixtures", "examples"]
}
}With this config:
agents/skills/harness-planning/SKILL.md→ domainharness-planningagents/skills/fixtures/sample.ts→'unknown'(segment is blocklisted)packages/graph/src/ingest/domain-inference.ts→ domaingraph(built-in pattern still wins forpackages/<dir>)
- Type:
ArchConfig - Required: No
Configures general architectural enforcement including metric thresholds and per-module overrides. Works alongside layers and forbiddenImports to provide comprehensive architecture health checks.
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
boolean |
true |
Whether architecture checks are enabled |
baselinePath |
string |
".harness/arch/baselines.json" |
Path to the architecture baselines file |
thresholds |
Record<MetricCategory, number | Record<string, number>> |
{} |
Global metric thresholds keyed by category |
modules |
Record<string, ThresholdConfig> |
{} |
Per-module threshold overrides |
Threshold keys correspond to architecture metric categories such as circular-deps, layer-violations, complexity, coupling, forbidden-imports, module-size, and dependency-depth. Each value can be a single number or a record of named sub-thresholds.
{
"architecture": {
"enabled": true,
"baselinePath": ".harness/arch/baselines.json",
"thresholds": {
"circular-deps": { "max": 0 },
"layer-violations": { "max": 0 },
"complexity": { "max": 15 },
"coupling": { "maxFanIn": 10, "maxFanOut": 8 },
"forbidden-imports": { "max": 0 },
"module-size": { "maxFiles": 30, "maxLoc": 3500 },
"dependency-depth": { "max": 7 }
},
"modules": {}
}
}- Type:
SkillsConfig - Required: No
Controls how skills are loaded, suggested, and tiered in the skill dispatcher.
| Field | Type | Default | Description |
|---|---|---|---|
alwaysSuggest |
string[] |
[] |
Skills to always suggest in the dispatcher, regardless of scoring |
neverSuggest |
string[] |
[] |
Skills to never suggest, even if they score highly |
tierOverrides |
Record<string, number> |
{} |
Override the tier (1--3) of specific skills by skill name |
{
"skills": {
"alwaysSuggest": ["detect-doc-drift", "check-dependencies"],
"neverSuggest": ["experimental-refactor"],
"tierOverrides": {
"my-custom-skill": 1
}
}
}- Type:
TraceabilityConfig - Required: No
Configures spec-to-implementation traceability checks. Ensures that specification documents have corresponding implementations and tracks coverage.
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
boolean |
true |
Whether traceability checks are enabled |
severity |
"error" | "warning" |
"warning" |
Severity level when coverage is below threshold |
minCoverage |
number |
0 |
Minimum required coverage percentage (0--100) |
includeSpecs |
string[] |
["docs/changes/*/proposal.md"] |
Glob patterns for specs to include in traceability checks |
excludeSpecs |
string[] |
[] |
Glob patterns for specs to exclude from traceability checks |
{
"traceability": {
"enabled": true,
"severity": "error",
"minCoverage": 80,
"includeSpecs": ["docs/changes/*/proposal.md", "docs/api/*.md"],
"excludeSpecs": ["docs/changes/archived/**"]
}
}- Type:
RoadmapConfig - Required: No
Configures roadmap management and external tracker synchronization.
| Field | Type | Required | Description |
|---|---|---|---|
tracker |
TrackerConfig |
No | External tracker sync settings |
| Field | Type | Required | Description |
|---|---|---|---|
kind |
"github" (literal) |
Yes | Tracker kind (currently only "github" is supported) |
repo |
string |
No | Repository in "owner/repo" format |
labels |
string[] |
No | Labels auto-applied to synced issues for filtering |
statusMap |
Record<RoadmapStatus, string> |
Yes | Maps roadmap status to external tracker status |
reverseStatusMap |
Record<string, string> |
No | Maps external status back to roadmap status |
The statusMap keys are roadmap statuses: "backlog", "planned", "in-progress", "done", "blocked". Values are the corresponding external tracker statuses (e.g., "open", "closed").
{
"roadmap": {
"tracker": {
"kind": "github",
"repo": "my-org/my-project",
"labels": ["roadmap"],
"statusMap": {
"backlog": "open",
"planned": "open",
"in-progress": "open",
"done": "closed",
"blocked": "open"
},
"reverseStatusMap": {
"open": "planned",
"closed": "done"
}
}
}
}- Type:
TelemetryConfig - Required: No
Configures anonymous usage telemetry. Telemetry is enabled by default and sends anonymized product analytics (skill usage, session duration, outcome) to a central PostHog instance via HTTP. No personally identifiable information is sent unless the user explicitly opts in via .harness/telemetry.json.
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
boolean |
true |
Whether anonymous telemetry collection is on |
There are three ways to disable telemetry (checked in this order):
- Environment variable:
DO_NOT_TRACK=1(ecosystem standard) - Environment variable:
HARNESS_TELEMETRY_OPTOUT=1 - Config file: Set
telemetry.enabledtofalseinharness.config.json
Any of these disables all telemetry -- no HTTP requests are made.
Users who want to associate telemetry with a project, team, or alias can configure identity fields in .harness/telemetry.json (gitignored, never committed):
{
"identity": {
"project": "myapp",
"team": "platform",
"alias": "cwarner"
}
}Use the CLI to manage identity:
# Set identity fields
harness telemetry identify --project myapp --team platform --alias cwarner
# Clear all identity fields
harness telemetry identify --clear
# View current telemetry state
harness telemetry status
harness telemetry status --jsonOn first use, a one-time notice is printed to stderr explaining that anonymous telemetry is collected and how to disable it. The notice is not repeated after the flag file .harness/.telemetry-notice-shown is created.
{
"telemetry": {
"enabled": true
}
}To disable:
{
"telemetry": {
"enabled": false
}
}- Type:
AdoptionConfig - Required: No
Configures adoption tracking, which records skill invocation metrics to .harness/metrics/adoption.jsonl via the adoption-tracker stop hook.
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
boolean |
true |
Whether adoption tracking is enabled |
{
"adoption": {
"enabled": true
}
}To disable adoption tracking:
{
"adoption": {
"enabled": false
}
}- Type:
PhaseGatesConfig - Required: No
Phase gates enforce that implementation files have corresponding specification documents. This ensures a spec-first development workflow.
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
boolean |
false |
Enable phase gate checks |
severity |
"error" | "warning" |
"error" |
Severity level for violations |
mappings |
Array<PhaseGateMapping> |
[{ implPattern: "src/**/*.ts", specPattern: "docs/changes/{feature}/proposal.md" }] |
Maps implementation files to spec documents |
| Field | Type | Default | Description |
|---|---|---|---|
implPattern |
string |
-- | Glob pattern matching implementation files |
specPattern |
string |
-- | Pattern for the required spec file ({feature} is replaced with the feature name derived from the implementation path) |
contentValidation |
boolean |
false |
When true, validate that the spec file contains a numbered requirements section |
{
"phaseGates": {
"enabled": true,
"severity": "warning",
"mappings": [
{ "implPattern": "src/**/*.ts", "specPattern": "docs/changes/{feature}/proposal.md" },
{ "implPattern": "src/api/**/*.ts", "specPattern": "docs/api/{feature}.md" }
]
}
}- Type:
TemplateConfig - Required: No
Metadata about the project template used to initialize this configuration. Typically set by harness init and not edited manually.
| Field | Type | Required | Description |
|---|---|---|---|
level |
"basic" | "intermediate" | "advanced" |
No | Template complexity level (JS/TS only) |
language |
"typescript" | "python" | "go" | "rust" | "java" |
No | Target language |
framework |
string |
No | Primary technology framework |
version |
number |
Yes | Template version number |
tooling |
ToolingConfig |
No | Language-specific tooling configuration |
| Field | Type | Required | Description |
|---|---|---|---|
packageManager |
string |
No | Package manager (e.g., "npm", "pnpm") |
linter |
string |
No | Linter tool (e.g., "eslint", "ruff") |
formatter |
string |
No | Formatter tool (e.g., "prettier", "black") |
buildTool |
string |
No | Build tool (e.g., "tsc", "vite") |
testRunner |
string |
No | Test runner (e.g., "vitest", "pytest") |
lockFile |
string |
No | Lock file name (e.g., "pnpm-lock.yaml") |
{
"template": {
"level": "intermediate",
"language": "typescript",
"framework": "express",
"version": 1,
"tooling": {
"packageManager": "pnpm",
"linter": "eslint",
"formatter": "prettier",
"buildTool": "tsc",
"testRunner": "vitest",
"lockFile": "pnpm-lock.yaml"
}
}
}A full harness.config.json for a layered API project:
{
"version": 1,
"name": "task-api",
"rootDir": ".",
"agentsMapPath": "./AGENTS.md",
"docsDir": "./docs",
"layers": [
{ "name": "types", "pattern": "src/types/**", "allowedDependencies": [] },
{ "name": "repository", "pattern": "src/repository/**", "allowedDependencies": ["types"] },
{
"name": "service",
"pattern": "src/service/**",
"allowedDependencies": ["types", "repository"]
},
{
"name": "api",
"pattern": "src/api/**",
"allowedDependencies": ["types", "repository", "service"]
}
],
"forbiddenImports": [
{
"from": "src/types/**",
"disallow": ["express", "pg"],
"message": "Types layer must not depend on runtime libraries"
}
],
"boundaries": {
"requireSchema": ["src/api/**"]
},
"security": {
"enabled": true,
"strict": false,
"rules": { "SEC-CRY-001": "warning" },
"exclude": ["**/node_modules/**", "**/dist/**"]
},
"performance": {
"complexity": {
"enabled": true,
"thresholds": { "cyclomaticComplexity": { "error": 15, "warn": 10 } }
},
"coupling": {
"enabled": true,
"thresholds": { "fanOut": { "warn": 15 } }
},
"sizeBudget": { "enabled": false, "budgets": {} }
},
"architecture": {
"enabled": true,
"thresholds": {
"circular-deps": { "max": 0 },
"layer-violations": { "max": 0 },
"complexity": { "max": 15 }
}
},
"agent": {
"executor": "subprocess",
"timeout": 300000,
"skills": ["check-dependencies", "detect-entropy"]
},
"entropy": {
"excludePatterns": ["**/node_modules/**", "**/*.test.ts"],
"autoFix": false
},
"phaseGates": {
"enabled": true,
"severity": "error",
"mappings": [
{ "implPattern": "src/**/*.ts", "specPattern": "docs/changes/{feature}/proposal.md" }
]
},
"traceability": {
"enabled": true,
"severity": "warning",
"minCoverage": 80,
"includeSpecs": ["docs/changes/*/proposal.md"]
},
"skills": {
"alwaysSuggest": ["detect-doc-drift"],
"neverSuggest": [],
"tierOverrides": {}
},
"design": {
"strictness": "standard",
"platforms": ["web"]
},
"i18n": {
"enabled": false,
"sourceLocale": "en",
"targetLocales": []
},
"review": {
"model_tiers": {
"fast": "claude-haiku-4",
"standard": "claude-sonnet-4"
}
},
"integrations": {
"enabled": ["github-mcp"],
"dismissed": []
},
"roadmap": {
"tracker": {
"kind": "github",
"repo": "my-org/my-project",
"labels": ["roadmap"],
"statusMap": {
"backlog": "open",
"planned": "open",
"in-progress": "open",
"done": "closed",
"blocked": "open"
},
"reverseStatusMap": {
"open": "planned",
"closed": "done"
}
}
},
"telemetry": {
"enabled": true
},
"template": {
"level": "intermediate",
"framework": "express",
"version": 1
}
}The smallest valid configuration:
{
"version": 1
}All other fields are optional and fall back to their defaults. This is useful when you want to adopt Harness Engineering incrementally, starting with just the AGENTS.md workflow and adding layers or gates later.
The configuration file is validated automatically when any Harness CLI command runs. You can also validate it explicitly:
npx harness validateIf validation fails, the error message will indicate which field has an invalid value and what was expected.
Last Updated: 2026-04-10