Configuration
harnd init writes .harnd/config.yaml, and it is safe to hand-edit. Here is what every key in it means.
.harnd/config.yaml is generated by harnd init and updated by a handful of other commands (link, rename, refresh). The file itself says it is safe to hand-edit — it is the source of truth, not a cache of one.
schema_version: "2.0.0"
harness:
id: hrn_01k...
name: my-project
description: ""
project:
root: ..
defaults:
gates:
- no-raw-color
- no-raw-spacing
scan:
components:
- "packages/ui/src/**"
paused:
reason: "waiting on a known scan bug"
until_version: "0.3.0"
cloud:
linked: false
api_url: https://harnd.com
harness_id: hrn_...schema_version
Required. A string in "2.x.y" form. harnd refresh updates an existing .harnd/ to the schema the installed CLI expects; it is a dry run by default, and applies changes only with --write.
language
Optional. "en" or "ja", default "en". This is the language of generated files — for example .harnd/agents/harness.md and the rationale text on default gates. It is not the language of the CLI's own output, which is always English regardless of this setting. Set it with harnd init --lang ja.
harness
harness.id— Generated once atinit(hrn_…), and never changes after that, even acrossinit --force.harness.name— A human-readable name. The recommended way to change it later isharnd rename <name>, not hand-editing this field directly — though the file as a whole is safe to hand-edit.harness.description— Optional, free text.
project.root
The path from .harnd/ to the repository root, relative. In almost every setup this is .., since .harnd/ lives one level below the root.
defaults.gates
Internal bookkeeping: the list of default gate rules this harness has already been offered. harnd refresh uses it to tell a gate you deliberately removed apart from a gate that did not exist yet when you ran init — without it, refresh could not tell the two apart and would resurrect gates you removed on purpose. It exists so removed gates never come back; it is not meant for hand-editing.
scan.components
Optional. An array of repo-relative glob patterns declaring where to look for components. By default, scan only looks in directories whose purpose is unambiguous by name — components/, ui/, and similar conventions, plus route-colocated files under app/. Set this explicitly for a monorepo (packages/ui/src/**) or a non-standard layout, such as Feature-Sliced Design's src/features/**/ui/**.
This is detection scope, not validation scope. Gates have their own, separate scope.include. Conflating the two can wrongly mark real components as deprecated when they fall outside a gate's scope but are in fact still in use.
scan.paused
Optional. Declares that you have deliberately stopped running scan. A paused: block is not for hiding that you stopped — the staleness warning keeps showing either way. It changes the warning from “you're being negligent” to “declared, with a reason.”
scan.paused.reason— Required ifpaused:is present at all. Apaused:block with no reason is treated as malformed and ignored — harnd says so explicitly rather than silently accepting it.scan.paused.until_version— Optional. Use this when the reason is “waiting on a specific harnd bug or version” rather than a standing policy. Once you are running that version or later,scanandvalidatewill tell you that you can probably clear the pause. Harnd never clears it automatically — you always do that by hand.
cloud
All of the local commands — init, scan, validate — leave this section alone. It only changes once you run harnd link.
cloud.linked— Set totruebyharnd link. Alwaysfalseright afterinit, since local commands need no account.cloud.api_url— Recorded byharnd link(defaulthttps://harnd.com). Not secret.cloud.harness_id— The cloud-side id, recorded if known. Informational only:harnd syncresolves the actual destination from your API key, not from this field.