maintainability-agent docs
Reference

Configuration Schema

The config file is JSON, conventionally named maintainability-agent.json, and currently uses "version": 1.

The machine-readable schema lives at:

maintainability-agent.schema.json

Minimal Config

{
  "version": 1
}

Common Fields

Configuration locations and precedence

Configuration merges from least to most specific: built-in defaults, then the user file at $XDG_CONFIG_HOME/maintainability-agent/config.json (default ~/.config/maintainability-agent/config.json), then the repository's maintainability-agent.json or explicit --config file. Later tiers win via the same recursive merge used for repository configuration. Documented environment overrides, where a setting has one, remain the highest-precedence one-run value; there is no generic environment-to-JSON replacement.

Consent to run a repository's test suite lives only in the user file, under test_execution.commands, keyed by the repository's resolved root: the command you agreed to, or an empty list for a decline. A repository cannot supply it.

Loading either the user or repository tier defaults analyzers.run to true. An explicit run: false survives until a later tier explicitly overrides it. With neither file, load_config(None) retains the built-in run: false path.

Per-user first-run state lives at $XDG_STATE_HOME/maintainability-agent/state.json (default ~/.local/state/maintainability-agent/state.json). Its seen object maps an absolute repository root to an ISO date. Missing, corrupt, or unreadable user configuration and state are treated as absent rather than aborting an audit.

Analyzer policy (analyzers)

Controls whether the external pool runs and which analysis tools may run in it. Full background, including the tool inventory and what each class means, is in the analyzer pool.

"analyzers": {
  "run": true,
  "depth": "moderate",
  "license_policy": "permissive",
  "prompt_when_interactive": true,
  "allow_tools": [],
  "deny_tools": [],
  "deny_license_classes": [],
  "deny_concerns": ["security"],
  "timeout_seconds": 120
}

Precedence is fixed and not configurable: every deny wins, including over allow_tools. An organization's prohibition must not be overridable by a per-repository opt-in. Within what remains, allow_tools admits, then the depth and license tiers decide.

Slugs come from src/maintainability_audit/_assets/analyzer-catalog.json. To see exactly what a configuration selects, and why everything else was left out:

python tools/resolve_pool.py                    # resolve the current config
python tools/resolve_pool.py --explain pylint   # why is one tool in or out?
python tools/resolve_pool.py --depth all        # what-if, without editing the file

A slug that is not in the catalog is a config error, not a silent no-op.

Changed-code policy (policy.changed_code)

Holds the code a change touches to a stricter bar than code it leaves alone (Decision 15) — how a team adopts a quality gate on a codebase it cannot fix all at once.

"policy": {
  "changed_code": {
    "max_function_lines": 40,
    "max_complexity": 10,
    "max_cognitive_complexity": 15
  }
}

Security pillar (security)

The security pillar is measured by secure-code-agent, which this tool runs for you. A full run can take several minutes on a large repository.

"security": {
  "timeout_seconds": 900
}

Economic context

ADR 004 v1. Optional. The audit CLI reads these keys (tests/test_economic_context.py).

Optional. Absent, the report is a normal maintainability report: no $ block, work order stays in risk×effort order. Present, it never changes the 0–5 score or verified_grade.

"economic_context": {
  "version": 1,
  "currency": "USD",
  "planning_horizon_months": 12,
  "loaded_engineering_cost_per_hour": { "low": 90, "base": 140, "high": 210 }
}

When the labor range is missing and stdin is a TTY, a first interactive run may ask and write this block into maintainability-agent.json. Non-TTY never asks. analyzers.prompt_when_interactive: false suppresses the ask. Environment overrides for one run, not persisted unless the ask writes the file: MAINTAINABILITY_LABOR_LOW, MAINTAINABILITY_LABOR_BASE, MAINTAINABILITY_LABOR_HIGH, MAINTAINABILITY_CURRENCY, MAINTAINABILITY_HORIZON_MONTHS.

This section is the shipped v1 contract.

Semantic policy

ADR 003 option C. Optional and absent by default. The first increment is TypeScript only and contributes report findings and prompt-only candidates, never score weights.

"semantic_policy": {
  "version": 1,
  "domain_types": [
    {
      "name": "order-status-public-boundary",
      "paths": ["src/orders/api.ts"],
      "boundary": "public",
      "symbol": "updateStatus",
      "required_type": "OrderStatus"
    }
  ],
  "operations": [
    {
      "name": "session-operation-review",
      "paths": ["src/session/operations.ts"],
      "capability_type": "SessionCapability",
      "operation_contract": "Op<TResult>"
    }
  ]
}

Broad naming rules are outside v1. In particular, policy cannot say every *_id must be a value object. Missing type analysis is reported as unknown coverage rather than zero semantic violations.

Validation

Any JSON Schema validator that supports draft 2020-12 can validate the config.