Skip to content

Metrics & Codes Reference

Dense cheat sheet for everything harness-score reports: scores, scopes, levels, dimensions, check IDs, configuration keys, CLI flags, Action inputs, and JSON fields. Remediation recipes live in chapter 8 — Measure & Improve.

Scores: maturity vs effective

CodeWhat it includesUsed for
maturityRepository files only (scopes: repo)Default CI gate, badge, --min-level, official team maturity
effectiveRepo ∪ configured global/extra scopesLocal “what the agent sees on this machine” when user/system harness is enabled

When no extra scopes are configured, effective equals maturity (same level, score, and checks). The report always includes both blocks for stable JSON.

Set which score gates CI with gate in config, --gate, or the Action gate input (maturity default).

Scopes

ScopeMeaningWhat is scanned
repoAlways onThe directory you pass to harness-score (default .)
userOpt-inAllowlisted user-level paths mapped to repo-relative shapes: ~/.cursor/*, ~/.claude/*, ~/.codeium/windsurf/* (Windsurf global rules alias), ~/Documents/Cline/Rules.clinerules/, ~/.continue/{rules,prompts}, ~/.agents/*, ~/.zed/commands, ~/.config/opencode/agents, global MCP/hooks configs, etc. See multi-harness — user-scope by tool. Not included: Copilot global (repo-only), Continue inline rules in config.yaml, IDE-only Cursor User Rules.
systemOpt-inReserved for validated system-wide installs (minimal in v1)
extraRootsOpt-inAdditional directories (relative or absolute) whose tree mirrors harness layout — e.g. a shared team harness checkout

Project files win over overlay paths on conflict (same relative path).

Not scanned: Cursor User Rules stored only in the IDE UI (not on disk), arbitrary home-directory walks, or secrets content in evidence strings.

Levels (L0–L4)

Official level names apply to maturity unless you set gate: effective.

LevelNameRequirements (all previous levels +)
L0Unharnessed
L1Documentedcontext ≥ 40%
L2Guidedcontext ≥ 60%; skills ≥ 30% or hooks ≥ 30%; hygiene ≥ 50%
L3Sensingsensors ≥ 60%; ci ≥ 50%
L4Self-correctinghooks ≥ 70%; total ≥ 80%

Full narrative: The Maturity Model.

Dimensions

IDTitleMax ptsMeasures
contextContext & Guides20AGENTS.md, scoped rules, README
skillsSkills & Commands17Skills, commands/workflows, subagents
hooksHooks & Guardrails14hooks.json / Claude settings hooks
sensorsSensors & Feedback20Tests, linter, types, formatter
ciCI Feedback14Pipeline, pre-commit
hygieneHygiene & Safety23.gitignore, secrets, lockfile, license, MCP hygiene

Total: 108 points.

Check catalog

Stable IDs — linked to remediation in Measure & Improve.

Context & Guides

IDPtsAnalyzes exactlyRemediation
CTX-014Root AGENTS.md, CLAUDE.md, or GEMINI.md existsctx-01
CTX-023Context file has ≥20 meaningful lines and ≥2 headingsctx-02
CTX-034At least one scoped rule file (any supported tool) or nested context filectx-03
CTX-043Every rule declares activation metadata in frontmatterctx-04
CTX-052Not every rule is blanket always-onctx-05
CTX-062No single rule file exceeds 500 linesctx-06
CTX-071README.md at repository rootctx-07
CTX-081No legacy .cursorrules without modern scoped rulesctx-08

Skills & Commands

IDPtsAnalyzes exactlyRemediation
SKL-014At least one SKILL.md under a recognized skills directoryskl-01
SKL-023Every skill has name: and description: in frontmatterskl-02
SKL-033Command/workflow files exist for any supported toolskl-03
SKL-042Skill descriptions are ≥40 charactersskl-04
AGT-013At least one subagent markdown fileagt-01
AGT-022Every subagent has name: and description: frontmatteragt-02

Hooks & Guardrails

IDPtsAnalyzes exactlyRemediation
HKS-014Hooks config exists and parses as JSONhks-01
HKS-022Hooks declare version/metadata and known event nameshks-02
HKS-034A gate-class hook is registered (shell/MCP/read/tool gate)hks-03
HKS-042A feedback-class hook is registered (post-edit/tool)hks-04
HKS-052Every hook script path referenced in config exists in the repohks-05

Sensors & Feedback

IDPtsAnalyzes exactlyRemediation
SNS-016Test runner configured (package.json script, pytest, go test, etc.)sns-01
SNS-025Linter configured (eslint, biome, ruff, golangci-lint, …)sns-02
SNS-034Type checking configured (tsconfig, mypy, pyright, …)sns-03
SNS-043Formatter configured (prettier, black, gofmt, …)sns-04
SNS-052At least one test file exists in the treesns-05

CI Feedback

IDPtsAnalyzes exactlyRemediation
CI-014CI pipeline file present (GitHub Actions, GitLab CI, …)ci-01
CI-024CI runs the test suiteci-02
CI-034CI runs lint or typecheckci-03
CI-042Pre-commit or git hook tooling installedci-04

Hygiene & Safety

IDPtsAnalyzes exactlyRemediation
HYG-014.gitignore presenthyg-01
HYG-023.gitignore covers environment fileshyg-02
HYG-034No unprotected .env files (without .env.example pattern)hyg-03
HYG-044MCP JSON configs contain no inline credential patternshyg-04
HYG-052LICENSE file presenthyg-05
HYG-063No credential-like signatures in harness markdown/JSONhyg-06
HYG-073Dependency lockfile committedhyg-07
HYG-084MCP configs use env interpolation for secretshyg-08

Configuration file (.harness-score.json)

Optional JSON at the scan root (strict schema — unknown keys error):

json
{
  "scopes": {
    "user": false,
    "system": false
  },
  "extraRoots": [
    { "id": "team-shared", "path": "../shared-harness" }
  ],
  "gate": "maturity",
  "extends": ["no-hooks"],
  "rules": {
    "HYG-05": "off"
  }
}
KeyTypeDefaultMeaning
scopes.userbooleanfalseInclude user-level harness overlay
scopes.systembooleanfalseInclude system-level overlay
extraRoots{ id, path }[][]Extra harness trees merged into effective
gate"maturity" | "effective""maturity"Which score --min-level uses
extendsstring[][]Named presets to apply (see below)
rulesRecord<checkId, severity>{}Per-check severity override, applied after every preset in extends

Precedence: CLI flags → Action inputs → config file → defaults. extends/rules are config-file-only in this release — there is no --extends/--rule CLI flag or Action input equivalent yet; use --config <path> to point at a .harness-score.json that sets them.

Team customization: extends and rules

Borrowed straight from ESLint's own vocabulary, because it's a vocabulary most teams already know:

  • rules overrides a single check's severity by ID: "HYG-05": "off". Severity is "off" or "error" in this release — "error" is the implicit default for every check, and "off" removes the check from both the numerator and the denominator of its dimension's score (it's structurally excluded, never scored as a failure). "warn" is a recognized value that's deliberately rejected today with a clear "not supported yet" error — it's reserved for a future advisory, non-blocking mode.
  • extends applies a named, maintainer-curated preset — a versioned, PR-reviewed bundle of rules overrides, not a free-form per-repo waiver. This preserves the same governance that already protects the check catalog: proposing a new preset goes through review (see CONTRIBUTING.md), not a silent local opt-out. Presets in extends are applied in array order, and any explicit rules entry always wins over a preset.

Every check excluded by extends/rules is always disclosed — in the terminal output (Preset: ... line), in the Markdown report (**Preset:** line and a status in the checks table), and in --json's preset field — never silently hidden behind a flag.

One exception, on purpose: HYG-03, HYG-04, and HYG-06 — the checks that detect actively leaked or exposed credentials — can never be set to "off", from either rules or a preset. Everything else in this config format leans on disclosure and PR review for integrity; these three are the one place that isn't negotiable.

Built-in presets

PresetEffectWhy
no-hooksSets HKS-01HKS-05 (the whole Hooks & Guardrails dimension, 14 of 108 points) to "off"For environments where local hook script execution is disallowed by policy — locked-down dev containers, regulated orgs, shared runners without permission to install hooks. Guardrails there are enforced in CI only.

Excluding an entire dimension has one honest consequence worth knowing up front: because L4 · Self-correcting is defined by runtime guardrail hooks (see the Maturity Model), a repository under no-hooks can never reach L4 — the level becomes capped, not "failed." report.level.capped is true and report.level.capReason explains why; every other dimension's score is completely unaffected. This is the scanner staying honest about what "self-correcting" means, not a penalty for excluding hooks.

CLI flags (scan configuration)

FlagMeaning
--config <file>Load config from a specific path
--scope userEnable user scope (comma-separated: user, system)
--gate maturity|effectiveScore used for --min-level
--min-level <0-4>Exit 1 when gated score is below level
--jsonFull report including scopes, gate, effective

GitHub Action inputs

InputDefaultMeaning
include-user-harnessfalsePass --scope user
include-system-harnessfalsePass --scope system
gatematurityPass --gate
config''Pass --config when set
min-level0Fail when gated score is below level

Outputs: level, level-name, percent (maturity); effective-level, effective-percent.

Report JSON fields (stable)

FieldDescription
rootAbsolute scan root
scopes.maturityAlways ["repo"]
scopes.effectivee.g. ["repo"], ["repo","user"]
gate"maturity" or "effective"
resolvedRootsOptional list of { scope, absPath } for overlays
level, score, dimensions, checksMaturity snapshot
effectiveSame shape: { level, score, dimensions, checks, detectedHarnesses }
detectedHarnessesTools seen in repo (informational)
truncatedWalk hit file cap
preset{ extends, rules, resolved } — team customization actually applied; resolved lists only checks whose severity differs from the default
level.capped, level.capReasoncapped is true when a blocking requirement for the next level can never be met under the current config (e.g. its dimension was excluded by a preset); capReason explains why
dimensions[].applicablefalse only when every check in that dimension resolved to "off"
checks[].severity"off" | "warn" | "error" — the resolved severity this scan used for that check

--diff compares maturity fields by default (top-level level / score / checks).