# Diagnostic codes

> Every diagnostic emitted by harn check , harn lint , and harn fmt carries a stable HARN-<CAT>-<NNN> code. Codes are dispatchable: agents, IDEs, and the hosted error pages all...

Website: https://harnlang.com/diagnostics.html

This page documents Harn, which is pre-1.0. Language, standard library, and CLI APIs may change. If the intended version is unclear, clarify before using this page.

---

<!-- GENERATED by `harn explain --catalog --format markdown` -- do not edit by hand. -->
<!-- Source of truth: crates/harn-parser/src/diagnostic_codes.rs. Run `make sync-diagnostics-catalog` to regenerate. -->

<!-- markdownlint-disable MD013 MD024 -->

Every diagnostic emitted by `harn check`, `harn lint`, and `harn fmt` carries a stable `HARN-<CAT>-<NNN>` code. Codes are dispatchable: agents, IDEs, and the hosted error pages all read the same `apiStability: stable` contract, so cross-tooling integrations never have to regex on prose.

Look up a single code interactively:

```sh
harn explain HARN-TYP-014
harn explain HARN-TYP-014 --json
```

The structured JSON sidecar that drives this page is committed at [`docs/diagnostics-catalog.json`](https://github.com/burin-labs/harn/blob/main/docs/diagnostics-catalog.json) — its `schemaVersion: 1` shape is the contract consumed by downstream tooling (an IDE host's diagnostic panel, a cloud platform's hosted error pages). Regenerate locally with `make sync-diagnostics-catalog`.

Prose-style tour of common shape and nilable diagnostics: [Reading shape diagnostics](./reading-shape-diagnostics.md).

## Repair safety classes

Repairs are tagged with a six-level safety class so `harn fix --apply --safety <ceiling>` and IDE auto-apply policies can dispatch without inspecting individual edits:

| Class | Meaning |
|---|---|
| `format-only` | Whitespace, trivia, or canonical layout only. Always safe to auto-apply. |
| `behavior-preserving` | Intended not to change observable runtime behavior. |
| `scope-local` | Confined to the current local scope or file; blast radius does not cross a public surface. |
| `surface-changing` | Touches a signature, export, or call-site surface other files can observe. |
| `capability-changing` | Required capabilities or sandbox profile may change. |
| `needs-human` | Planning hint only — propose, never auto-apply. |

## Categories

| Category | Title | Codes |
|---|---|---:|
| [`TYP`](#typ--type-checker) | Type checker | 29 |
| [`PAR`](#par--parser--lexer) | Parser / lexer | 6 |
| [`NAM`](#nam--naming-and-resolution) | Naming and resolution | 12 |
| [`CAP`](#cap--capabilities) | Capabilities | 8 |
| [`LLM`](#llm--llm-calls) | LLM calls | 4 |
| [`ORC`](#orc--orchestration-constructs) | Orchestration constructs | 12 |
| [`STD`](#std--stdlib-usage) | Stdlib usage | 5 |
| [`PRM`](#prm--prompt-templates) | Prompt templates | 7 |
| [`MOD`](#mod--modules-and-exports) | Modules and exports | 7 |
| [`RMD`](#rmd--reminder-lifecycle) | Reminder lifecycle | 8 |
| [`SUS`](#sus--suspend--resume-lifecycle) | Suspend / resume lifecycle | 13 |
| [`LNT`](#lnt--lint-rules) | Lint rules | 76 |
| [`FMT`](#fmt--formatter) | Formatter | 3 |
| [`IMP`](#imp--import-resolution) | Import resolution | 3 |
| [`OWN`](#own--ownership-and-mutability) | Ownership and mutability | 4 |
| [`RCV`](#rcv--error-recovery) | Error recovery | 3 |
| [`MAT`](#mat--match-exhaustiveness) | Match exhaustiveness | 3 |
| [`POL`](#pol--runtime-policies) | Runtime policies | 2 |
| [`MET`](#met--compile-time-meta-restrictions) | Compile-time meta restrictions | 1 |
| [`CST`](#cst--const-eval-sandbox) | Const-eval sandbox | 4 |
| [`CMP`](#cmp--bytecode-compilation) | Bytecode compilation | 1 |

## TYP — Type checker

Harn's static type checker rejects programs whose types do not unify. Type errors block compilation — Harn refuses to run a program until they are fixed.

| Code | Summary | Repair | Safety |
|---|---|---|---|
| [`HARN-TYP-001`](#harn-typ-001) | expected and actual types are incompatible | `casts/insert-explicit-conversion` | `scope-local` |
| [`HARN-TYP-002`](#harn-typ-002) | binary operator is not defined for the operand types | — | — |
| [`HARN-TYP-003`](#harn-typ-003) | string concatenation should be rewritten as interpolation | `style/string-interpolation` | `behavior-preserving` |
| [`HARN-TYP-004`](#harn-typ-004) | returned expression does not match the declared return type | `casts/insert-explicit-conversion` | `scope-local` |
| [`HARN-TYP-005`](#harn-typ-005) | assigned value does not match the target type | `casts/insert-explicit-conversion` | `scope-local` |
| [`HARN-TYP-006`](#harn-typ-006) | argument value does not match the parameter type | `casts/insert-explicit-conversion` | `scope-local` |
| [`HARN-TYP-007`](#harn-typ-007) | initializer does not match the declared variable type | `casts/insert-explicit-conversion` | `scope-local` |
| [`HARN-TYP-008`](#harn-typ-008) | closure return expression does not match its declared type | `casts/insert-explicit-conversion` | `scope-local` |
| [`HARN-TYP-009`](#harn-typ-009) | field value does not match its declared type | `casts/insert-explicit-conversion` | `scope-local` |
| [`HARN-TYP-010`](#harn-typ-010) | method receiver or result type is incompatible | `casts/insert-explicit-conversion` | `scope-local` |
| [`HARN-TYP-011`](#harn-typ-011) | callable does not accept type arguments | — | — |
| [`HARN-TYP-012`](#harn-typ-012) | type argument does not satisfy the generic parameter | — | — |
| [`HARN-TYP-013`](#harn-typ-013) | generic call has the wrong number of type arguments | — | — |
| [`HARN-TYP-014`](#harn-typ-014) | declaration has the wrong number of type parameters | — | — |
| [`HARN-TYP-015`](#harn-typ-015) | type argument does not satisfy a where-clause constraint | — | — |
| [`HARN-TYP-016`](#harn-typ-016) | expression must be iterable | — | — |
| [`HARN-TYP-017`](#harn-typ-017) | subscript index type is invalid | `casts/insert-explicit-conversion` | `scope-local` |
| [`HARN-TYP-018`](#harn-typ-018) | expression must be callable | — | — |
| [`HARN-TYP-019`](#harn-typ-019) | cast cannot be proven valid | `casts/remove-unchecked` | `scope-local` |
| [`HARN-TYP-020`](#harn-typ-020) | type name cannot be resolved | `imports/fix-path` | `scope-local` |
| [`HARN-TYP-021`](#harn-typ-021) | variant type is used in an invalid position | — | — |
| [`HARN-TYP-022`](#harn-typ-022) | struct literal is invalid | — | — |
| [`HARN-TYP-023`](#harn-typ-023) | enum construction is invalid | — | — |
| [`HARN-TYP-024`](#harn-typ-024) | pattern binding is invalid for the expected type | — | — |
| [`HARN-TYP-025`](#harn-typ-025) | optional access is invalid for the receiver type | — | — |
| [`HARN-TYP-026`](#harn-typ-026) | thrown value type is not covered by the callable's declared throws set | — | — |
| [`HARN-TYP-027`](#harn-typ-027) | constant tuple index is outside the fixed arity | — | — |
| [`HARN-TYP-028`](#harn-typ-028) | declared parameter has no type annotation | `types/annotate-parameter` | `surface-changing` |
| [`HARN-TYP-029`](#harn-typ-029) | type predicate contract is invalid | — | — |

## PAR — Parser / lexer

The lexer or parser raises these before type checking begins. Harn cannot build an AST from the source until the offending token sequence is repaired.

| Code | Summary | Repair | Safety |
|---|---|---|---|
| [`HARN-PAR-001`](#harn-par-001) | parser found an unexpected token | — | — |
| [`HARN-PAR-002`](#harn-par-002) | parser reached end of file while expecting syntax | — | — |
| [`HARN-PAR-003`](#harn-par-003) | lexer found an unexpected character | — | — |
| [`HARN-PAR-004`](#harn-par-004) | string literal is unterminated | — | — |
| [`HARN-PAR-005`](#harn-par-005) | block comment is unterminated | — | — |
| [`HARN-PAR-006`](#harn-par-006) | integer literal is out of range for int (i64) | — | — |

## NAM — Naming and resolution

Name resolution failed: the identifier, field, or attribute referenced does not match anything in the visible scope. Harn cannot proceed without a binding.

| Code | Summary | Repair | Safety |
|---|---|---|---|
| [`HARN-NAM-001`](#harn-nam-001) | variable name cannot be resolved | `bindings/rename-to-closest` | `scope-local` |
| [`HARN-NAM-002`](#harn-nam-002) | function name cannot be resolved | `bindings/rename-to-closest` | `scope-local` |
| [`HARN-NAM-003`](#harn-nam-003) | attribute name is not recognized | — | — |
| [`HARN-NAM-004`](#harn-nam-004) | field name does not exist on the target type | `bindings/rename-to-closest` | `scope-local` |
| [`HARN-NAM-005`](#harn-nam-005) | method name does not exist on the receiver type | `bindings/rename-to-closest` | `scope-local` |
| [`HARN-NAM-006`](#harn-nam-006) | argument name is duplicated | — | — |
| [`HARN-NAM-008`](#harn-nam-008) | builtin name cannot be resolved | `bindings/rename-to-closest` | `scope-local` |
| [`HARN-NAM-009`](#harn-nam-009) | function call targets a deprecated declaration | `stdlib/migrate-renamed` | `scope-local` |
| [`HARN-NAM-010`](#harn-nam-010) | declaration reference cannot be resolved | `bindings/rename-to-closest` | `scope-local` |
| [`HARN-NAM-011`](#harn-nam-011) | attribute is attached to an unsupported declaration | — | — |
| [`HARN-NAM-012`](#harn-nam-012) | attribute argument is invalid | — | — |
| [`HARN-NAM-101`](#harn-nam-101) | `fn main` must take an explicit `harness: Harness` parameter | `bindings/thread-harness-needs-param` | `surface-changing` |

## CAP — Capabilities

A host capability call (file I/O, network, HITL approval, tool host, etc.) failed static validation. Capabilities are the trust boundary between Harn scripts and the embedding host, so checks are strict by design.

| Code | Summary | Repair | Safety |
|---|---|---|---|
| [`HARN-CAP-001`](#harn-cap-001) | capability payload is invalid | — | — |
| [`HARN-CAP-004`](#harn-cap-004) | capability result must be checked | `errors/check-or-rescue` | `scope-local` |
| [`HARN-CAP-005`](#harn-cap-005) | host capability operation is not declared | — | — |
| [`HARN-CAP-006`](#harn-cap-006) | host capability call must use a static operation name | — | — |
| [`HARN-CAP-007`](#harn-cap-007) | tool host capability binding is invalid | `manual/review-capability-binding` | `needs-human` |
| [`HARN-CAP-008`](#harn-cap-008) | declared host capability operation is not served | — | — |
| [`HARN-CAP-201`](#harn-cap-201) | harness capability denied by active sandbox profile | — | — |
| [`HARN-CAP-301`](#harn-cap-301) | child agent effect set exceeds the parent's declared effects | `policy/narrow-child-effects` | `surface-changing` |

## LLM — LLM calls

A `harness.llm.call(...)` invocation violates the schema Harn enforces. Schema-validated, provider-portable LLM calls are a load-bearing Harn contract; drift in the options table is rejected at check time.

| Code | Summary | Repair | Safety |
|---|---|---|---|
| [`HARN-LLM-003`](#harn-llm-003) | LLM call is missing schema validation | `llm/add-schema` | `surface-changing` |
| [`HARN-LLM-004`](#harn-llm-004) | LLM schema option is invalid | — | — |
| [`HARN-LLM-005`](#harn-llm-005) | prompt branches on provider identity instead of capability flags | `llm/use-capability-flag` | `capability-changing` |
| [`HARN-LLM-006`](#harn-llm-006) | provider, model, and requested options form a known-unsafe composition | — | — |

## ORC — Orchestration constructs

An orchestration construct — agent / workflow / pipeline / tool definition, or a call to an orchestration builtin — is shaped in a way the orchestrator cannot accept.

| Code | Summary | Repair | Safety |
|---|---|---|---|
| [`HARN-ORC-001`](#harn-orc-001) | orchestration construct has invalid arity | — | — |
| [`HARN-ORC-002`](#harn-orc-002) | orchestration construct argument has invalid type | — | — |
| [`HARN-ORC-003`](#harn-orc-003) | agent declaration is invalid | — | — |
| [`HARN-ORC-004`](#harn-orc-004) | workflow declaration is invalid | — | — |
| [`HARN-ORC-005`](#harn-orc-005) | tool declaration is invalid | — | — |
| [`HARN-ORC-006`](#harn-orc-006) | pipeline declaration is invalid | — | — |
| [`HARN-ORC-007`](#harn-orc-007) | select construct is invalid | — | — |
| [`HARN-ORC-008`](#harn-orc-008) | statement cannot be reached | `control-flow/remove-dead` | `behavior-preserving` |
| [`HARN-ORC-009`](#harn-orc-009) | Flow invariant attribute set is invalid | — | — |
| [`HARN-ORC-010`](#harn-orc-010) | execution target path cannot be found | — | — |
| [`HARN-ORC-011`](#harn-orc-011) | a self-deadlock acquire would block forever | — | — |
| [`HARN-ORC-012`](#harn-orc-012) | a wait-for graph cycle would block forever | — | — |

## STD — Stdlib usage

A stdlib symbol is used in a way Harn does not support, or has been renamed/removed and the call site still references the old surface.

| Code | Summary | Repair | Safety |
|---|---|---|---|
| [`HARN-STD-001`](#harn-std-001) | stdlib symbol has been renamed or deprecated | `stdlib/migrate-renamed` | `scope-local` |
| [`HARN-STD-002`](#harn-std-002) | stdlib call is invalid | — | — |
| [`HARN-STD-003`](#harn-std-003) | builtin call has invalid arity | — | — |
| [`HARN-STD-101`](#harn-std-101) | public stdlib function is missing declared metadata | `doc/add-stdlib-metadata` | `behavior-preserving` |
| [`HARN-STD-102`](#harn-std-102) | public stdlib function is missing an explicit return type | — | — |

## PRM — Prompt templates

A prompt template (`.harn.prompt` / `.prompt`) failed validation, either because its front matter is missing required fields or because the body references slots the schema does not declare.

| Code | Summary | Repair | Safety |
|---|---|---|---|
| [`HARN-PRM-001`](#harn-prm-001) | prompt template cannot be parsed | — | — |
| [`HARN-PRM-002`](#harn-prm-002) | prompt template has too many capability-aware branches | `manual/needs-human` | `needs-human` |
| [`HARN-PRM-003`](#harn-prm-003) | prompt construction risks direct injection | `prompts/escape-injection` | `scope-local` |
| [`HARN-PRM-004`](#harn-prm-004) | prompt template branches on provider identity | `llm/use-capability-flag` | `capability-changing` |
| [`HARN-PRM-005`](#harn-prm-005) | prompt references a tool outside the declared surface | `prompts/add-tool-to-surface` | `surface-changing` |
| [`HARN-PRM-006`](#harn-prm-006) | prompt references a deferred tool without tool search | `prompts/add-tool-to-surface` | `surface-changing` |
| [`HARN-PRM-007`](#harn-prm-007) | prompt or template target cannot be found | — | — |

## MOD — Modules and exports

A module-level import declaration cannot be satisfied or has been authored in a shape Harn rejects. Module boundaries are checked before the body is type-checked.

| Code | Summary | Repair | Safety |
|---|---|---|---|
| [`HARN-MOD-001`](#harn-mod-001) | module import cannot be resolved | `imports/fix-path` | `scope-local` |
| [`HARN-MOD-002`](#harn-mod-002) | module import is unused | `imports/remove-unused` | `behavior-preserving` |
| [`HARN-MOD-003`](#harn-mod-003) | module imports are not in canonical order | `imports/reorder` | `format-only` |
| [`HARN-MOD-004`](#harn-mod-004) | module export is invalid | — | — |
| [`HARN-MOD-005`](#harn-mod-005) | module imports expose colliding names | — | — |
| [`HARN-MOD-006`](#harn-mod-006) | module re-exports conflict | — | — |
| [`HARN-MOD-007`](#harn-mod-007) | imported module failed to compile | — | — |

## RMD — Reminder lifecycle

Reminder lifecycle errors are raised by `session/remind` and friends when the payload, tags, or scheduling do not match the documented contract.

| Code | Summary | Repair | Safety |
|---|---|---|---|
| [`HARN-RMD-001`](#harn-rmd-001) | reminder lifecycle option key is not recognized | — | — |
| [`HARN-RMD-002`](#harn-rmd-002) | reminder payload shape is invalid | — | — |
| [`HARN-RMD-003`](#harn-rmd-003) | retired provider-specific reminder role-hint diagnostic | — | — |
| [`HARN-RMD-004`](#harn-rmd-004) | discardable reminder has no TTL | — | — |
| [`HARN-RMD-005`](#harn-rmd-005) | reminder propagate value is not recognized | — | — |
| [`HARN-RMD-006`](#harn-rmd-006) | reminder provider returned a malformed reminder spec | — | — |
| [`HARN-RMD-007`](#harn-rmd-007) | too many reminder providers are enabled | — | — |
| [`HARN-RMD-008`](#harn-rmd-008) | hook event does not support reminder effects | — | — |

## SUS — Suspend / resume lifecycle

Suspend / resume lifecycle errors are raised when a worker is suspended, resumed, or queried outside the lifecycle states the operation supports.

| Code | Summary | Repair | Safety |
|---|---|---|---|
| [`HARN-SUS-001`](#harn-sus-001) | suspend_agent target worker is not running | — | — |
| [`HARN-SUS-002`](#harn-sus-002) | ResumeConditions validation failed | — | — |
| [`HARN-SUS-003`](#harn-sus-003) | resume_agent target worker is not suspended | — | — |
| [`HARN-SUS-004`](#harn-sus-004) | resume snapshot cannot be loaded or used | — | — |
| [`HARN-SUS-005`](#harn-sus-005) | agent_await_resumption was invoked outside agent_loop structural handling | — | — |
| [`HARN-SUS-006`](#harn-sus-006) | concurrent resume changed the worker before resume could complete | — | — |
| [`HARN-SUS-007`](#harn-sus-007) | ResumeConditions trigger could not be registered | — | — |
| [`HARN-SUS-008`](#harn-sus-008) | resume timeout action is unsupported | — | — |
| [`HARN-SUS-009`](#harn-sus-009) | resume input failed agent_loop input validation | — | — |
| [`HARN-SUS-010`](#harn-sus-010) | closed suspended worker cannot be resumed | — | — |
| [`HARN-SUS-011`](#harn-sus-011) | replay resume input hash diverges from journaled suspension | — | — |
| [`HARN-SUS-012`](#harn-sus-012) | replay drain decision prompt hash diverges from journaled receipt | — | — |
| [`HARN-SUS-013`](#harn-sus-013) | lifecycle receipt signed timestamp failed verification | — | — |

## LNT — Lint rules

Lints are not hard errors. The code compiles, but Harn flags the pattern as likely-incorrect, unidiomatic, or risky in a production agent. Most lints can be auto-fixed.

| Code | Summary | Repair | Safety |
|---|---|---|---|
| [`HARN-LNT-001`](#harn-lnt-001) | renamed stdlib symbol lint | `stdlib/migrate-renamed` | `scope-local` |
| [`HARN-LNT-002`](#harn-lnt-002) | cyclomatic complexity lint | — | — |
| [`HARN-LNT-003`](#harn-lnt-003) | naming convention lint | `style/rename-to-convention` | `surface-changing` |
| [`HARN-LNT-004`](#harn-lnt-004) | eager collection conversion lint | `collections/prefer-lazy` | `scope-local` |
| [`HARN-LNT-005`](#harn-lnt-005) | redundant clone lint | `clones/remove-redundant` | `behavior-preserving` |
| [`HARN-LNT-006`](#harn-lnt-006) | long-running workflow cleanup lint | `manual/needs-human` | `needs-human` |
| [`HARN-LNT-007`](#harn-lnt-007) | MCP tool annotations lint | `manual/needs-human` | `needs-human` |
| [`HARN-LNT-008`](#harn-lnt-008) | PR open without secret scan lint | — | — |
| [`HARN-LNT-009`](#harn-lnt-009) | shadow variable lint | `bindings/rename-shadow` | `scope-local` |
| [`HARN-LNT-010`](#harn-lnt-010) | persona hook target lint | — | — |
| [`HARN-LNT-011`](#harn-lnt-011) | dead code after return lint | `control-flow/remove-dead` | `behavior-preserving` |
| [`HARN-LNT-012`](#harn-lnt-012) | let then return lint | `control-flow/flatten` | `behavior-preserving` |
| [`HARN-LNT-013`](#harn-lnt-013) | unhandled approval result lint | `errors/check-or-rescue` | `scope-local` |
| [`HARN-LNT-014`](#harn-lnt-014) | unused variable lint | `bindings/rename-unused` | `behavior-preserving` |
| [`HARN-LNT-015`](#harn-lnt-015) | unused pattern binding lint | `bindings/rename-unused` | `behavior-preserving` |
| [`HARN-LNT-016`](#harn-lnt-016) | unused parameter lint | `bindings/rename-unused` | `behavior-preserving` |
| [`HARN-LNT-017`](#harn-lnt-017) | unused import lint | `imports/remove-unused` | `behavior-preserving` |
| [`HARN-LNT-018`](#harn-lnt-018) | mutable never reassigned lint | `bindings/make-immutable` | `behavior-preserving` |
| [`HARN-LNT-019`](#harn-lnt-019) | unused function lint | `declarations/remove-unused` | `surface-changing` |
| [`HARN-LNT-020`](#harn-lnt-020) | unused type lint | `declarations/remove-unused` | `surface-changing` |
| [`HARN-LNT-021`](#harn-lnt-021) | persona body must call steps lint | — | — |
| [`HARN-LNT-022`](#harn-lnt-022) | undefined function lint | — | — |
| [`HARN-LNT-023`](#harn-lnt-023) | pipeline return type lint | — | — |
| [`HARN-LNT-024`](#harn-lnt-024) | missing harndoc lint | `doc/add-harndoc` | `behavior-preserving` |
| [`HARN-LNT-025`](#harn-lnt-025) | assert outside test lint | — | — |
| [`HARN-LNT-026`](#harn-lnt-026) | prompt injection risk lint | `prompts/escape-injection` | `scope-local` |
| [`HARN-LNT-027`](#harn-lnt-027) | connector effect policy lint | — | — |
| [`HARN-LNT-028`](#harn-lnt-028) | unnecessary cast lint | `casts/remove-redundant` | `behavior-preserving` |
| [`HARN-LNT-029`](#harn-lnt-029) | untyped dict access lint | `types/validate-boundary-value` | `scope-local` |
| [`HARN-LNT-030`](#harn-lnt-030) | constant logical operand lint | `expressions/simplify` | `behavior-preserving` |
| [`HARN-LNT-031`](#harn-lnt-031) | pointless comparison lint | `expressions/simplify` | `behavior-preserving` |
| [`HARN-LNT-032`](#harn-lnt-032) | comparison to bool lint | `expressions/simplify` | `behavior-preserving` |
| [`HARN-LNT-033`](#harn-lnt-033) | invalid binary operator literal lint | — | — |
| [`HARN-LNT-034`](#harn-lnt-034) | redundant nil ternary lint | `expressions/simplify` | `behavior-preserving` |
| [`HARN-LNT-035`](#harn-lnt-035) | empty block lint | `blocks/remove-empty` | `scope-local` |
| [`HARN-LNT-036`](#harn-lnt-036) | unnecessary else return lint | `control-flow/flatten` | `behavior-preserving` |
| [`HARN-LNT-037`](#harn-lnt-037) | duplicate match arm lint | `match/remove-duplicate-arm` | `behavior-preserving` |
| [`HARN-LNT-038`](#harn-lnt-038) | require in test lint | — | — |
| [`HARN-LNT-039`](#harn-lnt-039) | break outside loop lint | — | — |
| [`HARN-LNT-040`](#harn-lnt-040) | template parse lint | — | — |
| [`HARN-LNT-041`](#harn-lnt-041) | blank line between items lint | `format/reformat` | `format-only` |
| [`HARN-LNT-042`](#harn-lnt-042) | trailing comma lint | `format/reformat` | `format-only` |
| [`HARN-LNT-043`](#harn-lnt-043) | unnecessary parentheses lint | `format/reformat` | `format-only` |
| [`HARN-LNT-044`](#harn-lnt-044) | template variant explosion lint | `manual/needs-human` | `needs-human` |
| [`HARN-LNT-045`](#harn-lnt-045) | require file header lint | `format/reformat` | `format-only` |
| [`HARN-LNT-046`](#harn-lnt-046) | template provider identity branch lint | `llm/use-capability-flag` | `capability-changing` |
| [`HARN-LNT-047`](#harn-lnt-047) | import order lint | `imports/reorder` | `format-only` |
| [`HARN-LNT-048`](#harn-lnt-048) | prefer optional shorthand lint | `expressions/simplify` | `behavior-preserving` |
| [`HARN-LNT-049`](#harn-lnt-049) | legacy doc comment lint | `doc/migrate-comment-style` | `format-only` |
| [`HARN-LNT-050`](#harn-lnt-050) | removed LLM options lint | `llm/migrate-removed-option` | `scope-local` |
| [`HARN-LNT-051`](#harn-lnt-051) | unnecessary safe navigation lint | `expressions/simplify` | `behavior-preserving` |
| [`HARN-LNT-052`](#harn-lnt-052) | ambient clock builtin replaced by `harness.clock.*` | `bindings/thread-harness-clock` | `scope-local` |
| [`HARN-LNT-053`](#harn-lnt-053) | ambient stdio builtin replaced by `harness.stdio.*` | `bindings/thread-harness` | `scope-local` |
| [`HARN-LNT-054`](#harn-lnt-054) | ambient fs builtin replaced by `harness.fs.*` | `bindings/thread-harness-fs` | `scope-local` |
| [`HARN-LNT-055`](#harn-lnt-055) | ambient env builtin replaced by `harness.env.*` | `bindings/thread-harness-env` | `scope-local` |
| [`HARN-LNT-056`](#harn-lnt-056) | ambient random builtin replaced by `harness.random.*` | `bindings/thread-harness-random` | `scope-local` |
| [`HARN-LNT-057`](#harn-lnt-057) | ambient net builtin replaced by `harness.net.*` | `bindings/thread-harness-net` | `scope-local` |
| [`HARN-LNT-058`](#harn-lnt-058) | if / while / guard condition is statically known to always succeed or always fail | — | — |
| [`HARN-LNT-059`](#harn-lnt-059) | project rule-engine or native lint rule | — | — |
| [`HARN-LNT-060`](#harn-lnt-060) | inline options dict bypasses the typed option constructors | `types/add-shape-annotation` | `surface-changing` |
| [`HARN-LNT-061`](#harn-lnt-061) | nil coalesce fallback has no effect | `expressions/simplify` | `behavior-preserving` |
| [`HARN-LNT-062`](#harn-lnt-062) | nil coalesce fallback is unreachable | — | — |
| [`HARN-LNT-063`](#harn-lnt-063) | non-null assertion `!` on an already-non-nil value | `expressions/simplify` | `behavior-preserving` |
| [`HARN-LNT-064`](#harn-lnt-064) | a mutable variable captured from an enclosing scope is reassigned inside a `parallel`/`spawn` body, so concurrent branches share one cell and race | — | — |
| [`HARN-LNT-065`](#harn-lnt-065) | nil coalesce fallback repeats the left identifier | `expressions/simplify` | `behavior-preserving` |
| [`HARN-LNT-066`](#harn-lnt-066) | the result of a pure collection method is discarded, so the call has no effect on the receiver | — | — |
| [`HARN-LNT-068`](#harn-lnt-068) | prompt template names a filter the engine does not implement | — | — |
| [`HARN-LNT-069`](#harn-lnt-069) | helper accepts root Harness but uses only narrow capability handles | `bindings/attenuate-harness` | `surface-changing` |
| [`HARN-LNT-070`](#harn-lnt-070) | public API has too many same-typed positional parameters | — | — |
| [`HARN-LNT-071`](#harn-lnt-071) | global builtin has moved to a Harness capability method | `bindings/thread-harness-method` | `scope-local` |
| [`HARN-LNT-072`](#harn-lnt-072) | call names a builtin whose declared exposure keeps Harn source from naming it | — | — |
| [`HARN-LNT-073`](#harn-lnt-073) | parameter carrying a narrow capability handle is not named for that capability | `bindings/name-capability-parameter` | `surface-changing` |
| [`HARN-LNT-074`](#harn-lnt-074) | explicitly unused private pipeline input can be removed | `bindings/remove-unused-pipeline-input` | `surface-changing` |
| [`HARN-LNT-075`](#harn-lnt-075) | tool handler returns a freeform dict, so its outcome must be inferred from key names instead of declared by its type | — | — |
| [`HARN-LNT-076`](#harn-lnt-076) | tool handler reaches the privileged host wire | — | — |
| [`HARN-LNT-077`](#harn-lnt-077) | record literal copies fields one by one from a value that `pick` can select | `records/pick-fields` | `behavior-preserving` |

## FMT — Formatter

The formatter could not produce a canonical layout — either the input contains a construct it does not know how to render, or a layout rule was violated in a way auto-fix cannot resolve.

| Code | Summary | Repair | Safety |
|---|---|---|---|
| [`HARN-FMT-001`](#harn-fmt-001) | formatter could not parse the source | — | — |
| [`HARN-FMT-002`](#harn-fmt-002) | source is not in canonical format | `format/reformat` | `format-only` |
| [`HARN-FMT-003`](#harn-fmt-003) | formatter normalized trailing comma layout | `format/reformat` | `format-only` |

## IMP — Import resolution

Import resolution failed at a deeper layer than `MOD` — the file, symbol, or package referenced in an import declaration could not be located, parsed, or exposed.

| Code | Summary | Repair | Safety |
|---|---|---|---|
| [`HARN-IMP-001`](#harn-imp-001) | import target cannot be resolved | `imports/fix-path` | `scope-local` |
| [`HARN-IMP-002`](#harn-imp-002) | imported symbol does not exist | — | — |
| [`HARN-IMP-003`](#harn-imp-003) | import graph contains a cycle | — | — |

## OWN — Ownership and mutability

Harn's binding-and-mutability discipline rejects this usage. `let` bindings may not be reassigned; `mut` bindings should actually be reassigned somewhere.

| Code | Summary | Repair | Safety |
|---|---|---|---|
| [`HARN-OWN-001`](#harn-own-001) | immutable binding is reassigned | `bindings/make-mutable` | `scope-local` |
| [`HARN-OWN-002`](#harn-own-002) | mutable binding is never reassigned | `bindings/make-immutable` | `behavior-preserving` |
| [`HARN-OWN-003`](#harn-own-003) | owned value escapes its valid scope | — | — |
| [`HARN-OWN-004`](#harn-own-004) | unvalidated boundary value is used directly | — | — |

## RCV — Error recovery

A recovery construct (`try`, `rescue`) is in an invalid position or shaped in a way Harn's error-recovery rules cannot accept.

| Code | Summary | Repair | Safety |
|---|---|---|---|
| [`HARN-RCV-001`](#harn-rcv-001) | rescue construct is outside a function body | `errors/wrap-in-fn` | `surface-changing` |
| [`HARN-RCV-002`](#harn-rcv-002) | try construct is outside a function body | `errors/wrap-in-fn` | `surface-changing` |
| [`HARN-RCV-003`](#harn-rcv-003) | rescue construct is invalid | — | — |

## MAT — Match exhaustiveness

A `match` expression is incomplete, ambiguous, or otherwise invalid. Harn requires arms to cover every variant of the scrutinee type — partial matches must opt in with an explicit catch-all.

| Code | Summary | Repair | Safety |
|---|---|---|---|
| [`HARN-MAT-001`](#harn-mat-001) | match expression is not exhaustive | `match/add-missing-arms` | `scope-local` |
| [`HARN-MAT-002`](#harn-mat-002) | match expression contains a duplicate arm | `match/remove-duplicate-arm` | `behavior-preserving` |
| [`HARN-MAT-003`](#harn-mat-003) | match pattern is invalid | — | — |

## POL — Runtime policies

A runtime policy (pool backpressure, scheduling, quotas) rejected the attempt. Policies are configurable, but defaults are tuned for safety over throughput.

| Code | Summary | Repair | Safety |
|---|---|---|---|
| [`HARN-POL-001`](#harn-pol-001) | pool backpressure rejected a submit | — | — |
| [`HARN-POL-002`](#harn-pol-002) | fail-fast pool has no immediate capacity | — | — |

## MET — Compile-time meta restrictions

A `const` binding's right-hand side must be a pure expression evaluable at compile time under the const-eval sandbox. These codes flag constructs the sandbox rejects.

| Code | Summary | Repair | Safety |
|---|---|---|---|
| [`HARN-MET-001`](#harn-met-001) | expression is not permitted in a const initializer | — | — |

## CST — Const-eval sandbox

The bounded const-eval sandbox enforces step, recursion, and capability limits on every `const` initializer so a hostile or accidental expression cannot stall the compiler.

| Code | Summary | Repair | Safety |
|---|---|---|---|
| [`HARN-CST-001`](#harn-cst-001) | const initializer exceeded the step budget | — | — |
| [`HARN-CST-002`](#harn-cst-002) | const initializer exceeded the recursion depth budget | — | — |
| [`HARN-CST-003`](#harn-cst-003) | const initializer attempted a sandboxed capability | — | — |
| [`HARN-CST-004`](#harn-cst-004) | const initializer raised a runtime error during evaluation | — | — |

## CMP — Bytecode compilation

The bytecode compiler rejected a program that parsed and type-checked. These are structural / codegen errors the type checker does not model — `harn check` runs the compile pass too, so anything that would stop `harn run` is reported up front.

| Code | Summary | Repair | Safety |
|---|---|---|---|
| [`HARN-CMP-001`](#harn-cmp-001) | the program failed to compile to bytecode | — | — |

## Code reference

### `HARN-TYP-001`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

expected and actual types are incompatible

- **Repair:** `casts/insert-explicit-conversion` &nbsp;·&nbsp; **Safety:** `scope-local`
- Insert an explicit conversion or correct the operand type
- **See also:** [`HARN-TYP-005`](#harn-typ-005), [`HARN-TYP-006`](#harn-typ-006), [`HARN-TYP-004`](#harn-typ-004), [`HARN-TYP-007`](#harn-typ-007), [`HARN-TYP-009`](#harn-typ-009)

#### What it means

Harn's type checker compared the inferred ("actual") type of an expression
against the type the surrounding context expects, and the two did not unify.
This is the most common type error — it covers any mismatch that doesn't fall
into one of the more specific TYP codes (assignment, argument, return, etc.).

#### How to fix

- Adjust the expression so its inferred type matches the surrounding
  context.
- Widen the declared type at the binding / parameter / return position to
  accept the actual type.
- Convert the value explicitly (`as`, a stdlib coercion, etc.) when a safe
  conversion exists.
- If the mismatch is between a concrete type and an optional, use
  `?`-chaining or supply a default with `??`.

### `HARN-TYP-002`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

binary operator is not defined for the operand types

### `HARN-TYP-003`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

string concatenation should be rewritten as interpolation

- **Repair:** `style/string-interpolation` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Rewrite string concatenation as an interpolation literal

### `HARN-TYP-004`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

returned expression does not match the declared return type

- **Repair:** `casts/insert-explicit-conversion` &nbsp;·&nbsp; **Safety:** `scope-local`
- Insert an explicit conversion or correct the operand type
- **See also:** [`HARN-TYP-001`](#harn-typ-001), [`HARN-TYP-008`](#harn-typ-008)

### `HARN-TYP-005`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

assigned value does not match the target type

- **Repair:** `casts/insert-explicit-conversion` &nbsp;·&nbsp; **Safety:** `scope-local`
- Insert an explicit conversion or correct the operand type
- **See also:** [`HARN-TYP-001`](#harn-typ-001), [`HARN-TYP-007`](#harn-typ-007)

### `HARN-TYP-006`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

argument value does not match the parameter type

- **Repair:** `casts/insert-explicit-conversion` &nbsp;·&nbsp; **Safety:** `scope-local`
- Insert an explicit conversion or correct the operand type
- **See also:** [`HARN-TYP-001`](#harn-typ-001), [`HARN-TYP-012`](#harn-typ-012)

### `HARN-TYP-007`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

initializer does not match the declared variable type

- **Repair:** `casts/insert-explicit-conversion` &nbsp;·&nbsp; **Safety:** `scope-local`
- Insert an explicit conversion or correct the operand type
- **See also:** [`HARN-TYP-001`](#harn-typ-001), [`HARN-TYP-005`](#harn-typ-005)

### `HARN-TYP-008`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

closure return expression does not match its declared type

- **Repair:** `casts/insert-explicit-conversion` &nbsp;·&nbsp; **Safety:** `scope-local`
- Insert an explicit conversion or correct the operand type
- **See also:** [`HARN-TYP-004`](#harn-typ-004)

### `HARN-TYP-009`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

field value does not match its declared type

- **Repair:** `casts/insert-explicit-conversion` &nbsp;·&nbsp; **Safety:** `scope-local`
- Insert an explicit conversion or correct the operand type
- **See also:** [`HARN-TYP-001`](#harn-typ-001), [`HARN-TYP-022`](#harn-typ-022)

### `HARN-TYP-010`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

method receiver or result type is incompatible

- **Repair:** `casts/insert-explicit-conversion` &nbsp;·&nbsp; **Safety:** `scope-local`
- Insert an explicit conversion or correct the operand type
- **See also:** [`HARN-TYP-001`](#harn-typ-001), [`HARN-TYP-018`](#harn-typ-018)

### `HARN-TYP-011`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

callable does not accept type arguments

- **See also:** [`HARN-TYP-012`](#harn-typ-012), [`HARN-TYP-013`](#harn-typ-013)

unsupported)

### `HARN-TYP-012`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

type argument does not satisfy the generic parameter

- **See also:** [`HARN-TYP-013`](#harn-typ-013), [`HARN-TYP-015`](#harn-typ-015)

mismatch)

### `HARN-TYP-013`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

generic call has the wrong number of type arguments

- **See also:** [`HARN-TYP-012`](#harn-typ-012), [`HARN-TYP-014`](#harn-typ-014)

### `HARN-TYP-014`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

declaration has the wrong number of type parameters

- **See also:** [`HARN-TYP-013`](#harn-typ-013)

#### What it means

A generic declaration (function, type alias, or struct) was written with a
different number of type parameters than the use-site supplies. Harn refuses
to silently fill in missing parameters or discard extra ones — the count must
match exactly.

#### Example

```harn,ignore
fn pair<T>(a: T, b: T) -> [T; 2] { [a, b] }

// HARN-TYP-014: pair takes 1 type parameter, not 2
const xs = pair::<int, string>(1, "two")
```

#### How to fix

- Supply exactly as many type arguments as the declaration declares, or omit
  the explicit list and let Harn infer them.
- If the declaration itself is wrong, edit the `<T, U, ...>` list on the
  declaration to match the arity the callers expect.
- For type aliases and structs, the same rule applies: `Map<K, V>` needs two
  arguments, not one and not three.

### `HARN-TYP-015`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

type argument does not satisfy a where-clause constraint

- **See also:** [`HARN-TYP-012`](#harn-typ-012)

### `HARN-TYP-016`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

expression must be iterable

### `HARN-TYP-017`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

subscript index type is invalid

- **Repair:** `casts/insert-explicit-conversion` &nbsp;·&nbsp; **Safety:** `scope-local`
- Insert an explicit conversion or correct the operand type

### `HARN-TYP-018`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

expression must be callable

### `HARN-TYP-019`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

cast cannot be proven valid

- **Repair:** `casts/remove-unchecked` &nbsp;·&nbsp; **Safety:** `scope-local`
- Remove the unchecked cast or guard it with a type test

### `HARN-TYP-020`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

type name cannot be resolved

- **Repair:** `imports/fix-path` &nbsp;·&nbsp; **Safety:** `scope-local`
- Replace the import path with a resolvable target

### `HARN-TYP-021`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

variant type is used in an invalid position

### `HARN-TYP-022`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

struct literal is invalid

### `HARN-TYP-023`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

enum construction is invalid

### `HARN-TYP-024`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

pattern binding is invalid for the expected type

### `HARN-TYP-025`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

optional access is invalid for the receiver type

### `HARN-TYP-026`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

thrown value type is not covered by the callable's declared throws set

#### What it means

A function, tool, pipeline, or `fn` closure declared a typed exception channel
with a `throws E` (or `throws E1 | E2`) clause, but the callable can surface a
thrown value whose type is not a member of `E`. Harn checks every `throw` site —
and every value propagated out of a `try`/`catch` — against the declared set, so
the declared channel is an exhaustive description of what the callable may throw.

The clause is opt-in: a callable with no `throws` clause is not throw-checked and
never raises this error. Once a clause is present, though, it must account for
every escaping error.

#### Catch-exhaustiveness

An error handled inside the callable does not count against its `throws` set. The
check mirrors the runtime:

- A typed `catch (e: E)` handles a thrown error only when its type is `E` (the VM
  matches thrown *enum* errors by name and rethrows the rest), so it subtracts
  `E` from what escapes.
- An untyped `catch` is a catch-all and absorbs every error the body can throw.
- A `throw` in a `catch` or `finally` body always escapes and is checked against
  the declared set.

So a `try`/`catch` whose handler does not cover an error the body can throw makes
that error part of the callable's thrown set — and it must then be declared (or
handled) or this error fires.

#### How to fix

- Add the missing type to the `throws` clause, e.g. widen `throws NotFound` to
  `throws NotFound | ParseError`.
- Handle the error inside the callable with a `catch` that covers its type, so it
  no longer escapes.
- Convert the thrown value to a declared type before it leaves the callable.
- Remove the `throws` clause entirely if the callable should not constrain what
  it throws (this reverts to the historical unconstrained behavior).

### `HARN-TYP-027`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

constant tuple index is outside the fixed arity

A `tuple<T0, ...>` has a statically known number of positions. A constant
subscript must name one of those positions, including Harn's negative-index
spelling (`-1` is the final position).

Use an in-bounds index, destructure the tuple, or widen it to a `list<T>` when
the collection is intentionally variable-length.

### `HARN-TYP-028`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

declared parameter has no type annotation

- **Repair:** `types/annotate-parameter` &nbsp;·&nbsp; **Safety:** `surface-changing`
- Annotate the parameter with the inferred type, or `unknown` and narrow it at the dynamic boundary

A parameter with no type annotation is unchecked in both directions. The body
may reach for any member on it, and every caller may pass any value. Nothing
recovers the type later, so the mistake shows up at run time as a failed member
access instead of at check time with a code and a repair.

The rule covers named declarations: `fn`, `pub fn`, `gen fn`, `pipeline`,
`tool`, methods in an `impl` block, and signatures in an `interface` block. A
default value does not exempt a parameter, because the default constrains only
the call that omits the argument.

Closure and lambda parameters are not covered. The checker types those from the
position the literal appears in, so they are inferred rather than implicit.

#### Fix it

Annotate the parameter:

```harn
fn greet(user: {name: string}) -> string { return "hi ${user.name}" }
```

When a value really is unconstrained, say so:

```harn
fn passthrough<T>(value: T) -> T { return value }
```

Use a generic when input and output have the same shape. Use `unknown` at a
genuine dynamic boundary, then narrow or validate it before use.

#### Migrate a codebase

`harn fix --apply --code HARN-TYP-028 <path>` infers each parameter's type from
how the body uses it and from the arguments at every call site in the module
graph, writes the annotation, and reports how many parameters it could not
prove anything about. Those fall back to `unknown` for a human to refine.

For an unattended runtime-version migration of code written before this rule,
use `harn fix --apply --safety behavior-preserving --preserve-implicit-any
--json <path>`. That compatibility mode writes only explicit `any`, preserving
the former unchecked call contract. Its census fails if any eligible parameter
is unresolved or receives a narrower annotation. It does not replace the
surface-changing authoring repair above.

### `HARN-TYP-029`

**Category:** `TYP` (Type checker) &nbsp;·&nbsp; **API stability:** `stable`

type predicate contract is invalid

A type predicate tells callers how a boolean result narrows one argument. Harn
checks the function body before trusting that claim.

This error means the declaration does not prove its contract. Common causes
include:

- The predicate names a missing, untyped, or rest parameter.
- The narrower type is not a subtype of the parameter type.
- The body does not end with one return condition.
- The true branch does not prove the narrower type.
- A two-sided predicate claims too much about the false branch.
- The predicate targets a generic type parameter.

#### Fix it

Use a two-sided predicate when true and false both give exact type facts:

```harn
fn is_text(value: unknown) -> value is string {
  return type_of(value) == "string"
}
```

Add `implies` when only a true result proves the type:

```harn
fn is_nonempty_text(value: unknown) -> implies value is string {
  return type_of(value) == "string" && len(value) > 0
}
```

A false result in the second example may still mean an empty string, so Harn
does not narrow the false branch.

### `HARN-PAR-001`

**Category:** `PAR` (Parser / lexer) &nbsp;·&nbsp; **API stability:** `stable`

parser found an unexpected token

#### How to fix

- Re-read the source around the highlighted span and restore the missing token(s).
- If the surrounding construct is a multi-line expression, check brace / bracket balance.

### `HARN-PAR-002`

**Category:** `PAR` (Parser / lexer) &nbsp;·&nbsp; **API stability:** `stable`

parser reached end of file while expecting syntax

#### How to fix

- Re-read the source around the highlighted span and restore the missing token(s).
- If the surrounding construct is a multi-line expression, check brace / bracket balance.

### `HARN-PAR-003`

**Category:** `PAR` (Parser / lexer) &nbsp;·&nbsp; **API stability:** `stable`

lexer found an unexpected character

#### How to fix

- Re-read the source around the highlighted span and restore the missing token(s).
- If the surrounding construct is a multi-line expression, check brace / bracket balance.

### `HARN-PAR-004`

**Category:** `PAR` (Parser / lexer) &nbsp;·&nbsp; **API stability:** `stable`

string literal is unterminated

#### How to fix

- Re-read the source around the highlighted span and restore the missing token(s).
- If the surrounding construct is a multi-line expression, check brace / bracket balance.

### `HARN-PAR-005`

**Category:** `PAR` (Parser / lexer) &nbsp;·&nbsp; **API stability:** `stable`

block comment is unterminated

comment)

#### How to fix

- Re-read the source around the highlighted span and restore the missing token(s).
- If the surrounding construct is a multi-line expression, check brace / bracket balance.

### `HARN-PAR-006`

**Category:** `PAR` (Parser / lexer) &nbsp;·&nbsp; **API stability:** `stable`

integer literal is out of range for int (i64)

An integer literal must fit in a 64-bit signed integer (`int`), i.e. be in the
range `-9223372036854775808 ..= 9223372036854775807`. Harn does not silently
widen an out-of-range integer literal to a float, because that would lose both
the exact value (distinct literals collapse onto the same `float`) and the
`int` type.

#### How to fix

- If you meant a floating-point value, write it with a decimal point or
  exponent so it lexes as a `float` (e.g. `9223372036854775808.0`).
- If you need the most negative `int`, build it by arithmetic — the sign is not
  part of the literal, so `9223372036854775808` overflows on its own:
  `-9223372036854775807 - 1`.
- Otherwise the value genuinely does not fit in `int`; rework the computation to
  stay within range.

### `HARN-NAM-001`

**Category:** `NAM` (Naming and resolution) &nbsp;·&nbsp; **API stability:** `stable`

variable name cannot be resolved

- **Repair:** `bindings/rename-to-closest` &nbsp;·&nbsp; **Safety:** `scope-local`
- Rename to the closest in-scope identifier
- **See also:** [`HARN-NAM-002`](#harn-nam-002), [`HARN-NAM-010`](#harn-nam-010)

#### How to fix

- Define the missing name, import it, or fix the typo.
- Confirm the symbol is exported by the module you're importing from.

### `HARN-NAM-002`

**Category:** `NAM` (Naming and resolution) &nbsp;·&nbsp; **API stability:** `stable`

function name cannot be resolved

- **Repair:** `bindings/rename-to-closest` &nbsp;·&nbsp; **Safety:** `scope-local`
- Rename to the closest in-scope identifier
- **See also:** [`HARN-NAM-008`](#harn-nam-008), [`HARN-NAM-010`](#harn-nam-010)

#### How to fix

- Define the missing name, import it, or fix the typo.
- Confirm the symbol is exported by the module you're importing from.

### `HARN-NAM-003`

**Category:** `NAM` (Naming and resolution) &nbsp;·&nbsp; **API stability:** `stable`

attribute name is not recognized

- **See also:** [`HARN-NAM-012`](#harn-nam-012), [`HARN-NAM-011`](#harn-nam-011)

#### How to fix

- Define the missing name, import it, or fix the typo.
- Confirm the symbol is exported by the module you're importing from.

### `HARN-NAM-004`

**Category:** `NAM` (Naming and resolution) &nbsp;·&nbsp; **API stability:** `stable`

field name does not exist on the target type

- **Repair:** `bindings/rename-to-closest` &nbsp;·&nbsp; **Safety:** `scope-local`
- Rename to the closest in-scope identifier
- **See also:** [`HARN-NAM-005`](#harn-nam-005), [`HARN-TYP-022`](#harn-typ-022)

#### How to fix

- Define the missing name, import it, or fix the typo.
- Confirm the symbol is exported by the module you're importing from.

### `HARN-NAM-005`

**Category:** `NAM` (Naming and resolution) &nbsp;·&nbsp; **API stability:** `stable`

method name does not exist on the receiver type

- **Repair:** `bindings/rename-to-closest` &nbsp;·&nbsp; **Safety:** `scope-local`
- Rename to the closest in-scope identifier
- **See also:** [`HARN-NAM-004`](#harn-nam-004), [`HARN-TYP-018`](#harn-typ-018)

Raised when `receiver.method()` names a method the receiver's statically known
type does not have — either an interface-constraint method that no bound
declares, or a method that does not exist on a concrete builtin (`string`,
`list`, `set`, `int`, `float`, `bool`) or `struct` receiver.

#### How to fix

- Fix the typo — the message suggests the closest available method.
- Call a method that exists on the type (see "available methods" in the help),
  or add it to the type's `impl` block.
- If the receiver is genuinely dynamic, narrow or annotate it so the intended
  type — and its methods — are in scope.

### `HARN-NAM-006`

**Category:** `NAM` (Naming and resolution) &nbsp;·&nbsp; **API stability:** `stable`

argument name is duplicated

#### How to fix

- Define the missing name, import it, or fix the typo.
- Confirm the symbol is exported by the module you're importing from.

### `HARN-NAM-008`

**Category:** `NAM` (Naming and resolution) &nbsp;·&nbsp; **API stability:** `stable`

builtin name cannot be resolved

- **Repair:** `bindings/rename-to-closest` &nbsp;·&nbsp; **Safety:** `scope-local`
- Rename to the closest in-scope identifier

#### How to fix

- Define the missing name, import it, or fix the typo.
- Confirm the symbol is exported by the module you're importing from.

### `HARN-NAM-009`

**Category:** `NAM` (Naming and resolution) &nbsp;·&nbsp; **API stability:** `stable`

function call targets a deprecated declaration

- **Repair:** `stdlib/migrate-renamed` &nbsp;·&nbsp; **Safety:** `scope-local`
- Rename the call to the renamed stdlib symbol

#### How to fix

- Define the missing name, import it, or fix the typo.
- Confirm the symbol is exported by the module you're importing from.

### `HARN-NAM-010`

**Category:** `NAM` (Naming and resolution) &nbsp;·&nbsp; **API stability:** `stable`

declaration reference cannot be resolved

- **Repair:** `bindings/rename-to-closest` &nbsp;·&nbsp; **Safety:** `scope-local`
- Rename to the closest in-scope identifier

#### How to fix

- Define the missing name, import it, or fix the typo.
- Confirm the symbol is exported by the module you're importing from.

### `HARN-NAM-011`

**Category:** `NAM` (Naming and resolution) &nbsp;·&nbsp; **API stability:** `stable`

attribute is attached to an unsupported declaration

- **See also:** [`HARN-NAM-003`](#harn-nam-003), [`HARN-NAM-012`](#harn-nam-012)

#### How to fix

- Define the missing name, import it, or fix the typo.
- Confirm the symbol is exported by the module you're importing from.

### `HARN-NAM-012`

**Category:** `NAM` (Naming and resolution) &nbsp;·&nbsp; **API stability:** `stable`

attribute argument is invalid

- **See also:** [`HARN-NAM-003`](#harn-nam-003), [`HARN-NAM-011`](#harn-nam-011)

#### How to fix

- Define the missing name, import it, or fix the typo.
- Confirm the symbol is exported by the module you're importing from.

### `HARN-NAM-101`

**Category:** `NAM` (Naming and resolution) &nbsp;·&nbsp; **API stability:** `stable`

`fn main` must take an explicit `harness: Harness` parameter

- **Repair:** `bindings/thread-harness-needs-param` &nbsp;·&nbsp; **Safety:** `surface-changing`
- Add a `harness: Harness` parameter where the stdio capability handle is required and update local callers

#### What it means

When a Harn program declares a top-level `fn main`, the runtime auto-invokes
it with the script's `Harness` capability handle. The convention is therefore
strict: the entrypoint must be exactly `fn main(harness: Harness) { ... }` —
or `fn main(_harness: Harness) { ... }` when the handle is intentionally
unused. It takes one parameter, named `harness` or `_harness`, typed
`Harness`, no defaults, no rest.

Any other shape (zero parameters, a renamed parameter, a missing or
non-`Harness` type annotation, extra parameters, or a default value) fails
this check before bytecode is emitted, so the runtime never tries to bind a
mismatched signature.

The `Harness` value gives the script typed access to its capability sub-handles
via field access (`harness.stdio`, `harness.term`, `harness.clock`,
`harness.fs`, `harness.env`, `harness.random`, `harness.net`,
`harness.process`, `harness.channels`, `harness.system`, `harness.llm`).
Threading the handle through `main` replaces ambient stdio, terminal, clock,
filesystem, environment, randomness, network, process, crypto, system, and LLM
catalog globals.

#### How to fix

Rewrite the entrypoint with the canonical signature:

```harn
fn main(harness: Harness) {
  harness.stdio.println("Hello, world!")
}
```

If you do not need any capabilities — for example, a script that only does
pure computation — prefix the parameter with `_` to mark it deliberately
unused:

```harn
fn main(_harness: Harness) {
  // pure logic …
}
```

If the function does not need to be the entrypoint, rename it (e.g. `helper`,
`run_once`) so the convention does not apply.

### `HARN-CAP-001`

**Category:** `CAP` (Capabilities) &nbsp;·&nbsp; **API stability:** `stable`

capability payload is invalid

#### How to fix

- Match the capability signature documented in the Harn capability spec.
- If approval / receipt handling is required, wire it through `human_approval` or the equivalent before calling the capability.

### `HARN-CAP-004`

**Category:** `CAP` (Capabilities) &nbsp;·&nbsp; **API stability:** `stable`

capability result must be checked

- **Repair:** `errors/check-or-rescue` &nbsp;·&nbsp; **Safety:** `scope-local`
- Check the result or wrap the call in a `rescue` block
- **See also:** [`HARN-RCV-001`](#harn-rcv-001), [`HARN-RCV-002`](#harn-rcv-002)

#### How to fix

- Match the capability signature documented in the Harn capability spec.
- If approval / receipt handling is required, wire it through `human_approval` or the equivalent before calling the capability.

### `HARN-CAP-005`

**Category:** `CAP` (Capabilities) &nbsp;·&nbsp; **API stability:** `stable`

host capability operation is not declared

- **See also:** [`HARN-CAP-006`](#harn-cap-006)

#### How to fix

- Match the capability signature documented in the Harn capability spec.
- If approval / receipt handling is required, wire it through `human_approval` or the equivalent before calling the capability.

### `HARN-CAP-006`

**Category:** `CAP` (Capabilities) &nbsp;·&nbsp; **API stability:** `stable`

host capability call must use a static operation name

name required)

#### How to fix

- Match the capability signature documented in the Harn capability spec.
- If approval / receipt handling is required, wire it through `human_approval` or the equivalent before calling the capability.

### `HARN-CAP-007`

**Category:** `CAP` (Capabilities) &nbsp;·&nbsp; **API stability:** `stable`

tool host capability binding is invalid

- **Repair:** `manual/review-capability-binding` &nbsp;·&nbsp; **Safety:** `needs-human`
- Review the capability binding; the fix is not mechanical

#### How to fix

- Match the capability signature documented in the Harn capability spec.
- If approval / receipt handling is required, wire it through `human_approval` or the equivalent before calling the capability.

### `HARN-CAP-008`

**Category:** `CAP` (Capabilities) &nbsp;·&nbsp; **API stability:** `stable`

declared host capability operation is not served

Harn found a declared host operation that the target host does not serve.
`harn check` reads the file named by `host_served_capabilities_path`. ACP checks
the operations advertised by the connected host when a prompt starts.

Add the operation to the host, remove the declaration, or list its exact name
in `runtime_installed_host_operations` if its handler is added at runtime.

### `HARN-CAP-201`

**Category:** `CAP` (Capabilities) &nbsp;·&nbsp; **API stability:** `stable`

harness capability denied by active sandbox profile

- **See also:** [`HARN-CAP-001`](#harn-cap-001)

#### What it means

A `harness.fs.*`, `harness.env.*`, `harness.random.*`, `harness.net.*`,
`harness.system.*`, or `harness.llm.*` method was rejected by the active
sandbox profile.
Examples:

- `harness.fs.write_text("/etc/passwd", ...)` from a script whose
  `workspace_roots` only include `./build/` — the path is outside the
  permitted set.
- `harness.net.get("https://example.com")` from a script whose egress
  allowlist does not include `example.com`.
- Any `harness.*` capability under `SandboxProfile::OsHardened` when the
  required platform mechanism is unavailable.

The runtime raises the rejection as a typed `tool_rejected` error so
the harness method surface stays narrow — every script-visible
capability tightens the same way at the same boundary, instead of each
ambient builtin growing its own bespoke deny diagnostic.

When the requested operation can be evaluated against the profile
ahead of time (literal path or URL argument, well-known method on a
sub-handle), `harn check` and `harn lint` surface the same diagnostic
at static-check time so callers don't have to actually execute the
script to discover the denial.

#### How to fix

- Widen the active sandbox profile via `CapabilityPolicy::workspace_roots`
  or the egress allowlist if the request is legitimate. The relevant
  policy lives in `~/.config/harn/policy.toml` or the per-script
  `CapabilityPolicy` overlay.
- Use `harn graph --json` to inspect which capabilities your script
  actually needs, then narrow the call site to those.
- For tests, switch from `Harness::real()` to `Harness::mock()` /
  `Harness::null()` so the call is recorded without touching the host.

### `HARN-CAP-301`

**Category:** `CAP` (Capabilities) &nbsp;·&nbsp; **API stability:** `stable`

child agent effect set exceeds the parent's declared effects

- **Repair:** `policy/narrow-child-effects` &nbsp;·&nbsp; **Safety:** `surface-changing`
- Narrow the child agent's effects to a subset of the parent's, or widen the parent's declared effects
- **See also:** [`HARN-CAP-001`](#harn-cap-001), [`HARN-CAP-007`](#harn-cap-007)

#### What it means

A spawned child agent requests typed side-effects (`harness.net.*`,
`harness.fs.*`, `llm_call`, tool dispatch, ...) that are not part of the
parent agent's declared effect set. The dispatcher (and `harn check`'s
static analyzer) enforce that a child's effect set must be a subset of
the parent's so an over-delegated child can never escape its parent's
trust boundary.

Specifically: at least one effect on the child handoff is not covered by
any effect declared on the parent. Coverage is structural:

- **Kind** must match by family (`stdio`, `fs`, `net`, `llm`, `tool`,
  `hostcall`, `persona`, `spawn`).
- **Scope** must be at least as permissive on the parent
  (`read`/`observe` ≤ `write` ≤ `mutate`).
- **Resource**, when declared on the parent, must match the child's
  exactly. A parent with no `resource` covers any child resource of the
  same kind/scope family.

This diagnostic surfaces from two paths and they are intentionally
identical:

- **Static**: `harn check` derives parent and child effect sets via the
  same capability analysis that backs `harn graph --json` and emits
  `HARN-CAP-301` when a child statically out-grants its parent.
- **Runtime**: at spawn time the dispatcher folds the parent's
  `current_execution_policy()` into an effect set, compares it against
  the child's computed effects, and refuses the spawn with a typed
  `EffectInheritanceViolation` deny event. The event payload carries the
  same `HARN-CAP-301` code and `policy/narrow-child-effects` repair id.

#### How to fix

The repair id is `policy/narrow-child-effects`. Two shapes are
typically valid:

1. **Narrow the child** — remove the over-granted calls from the child
   pipeline body, or guard them behind a capability the parent already
   declares. This is the safe default; the child stops asking for what
   it does not need.
2. **Widen the parent** — declare the missing effect on the parent
   pipeline (e.g. add a real `harness.net.get(...)` call in the parent's
   entrypoint, or extend the parent's `policy.capabilities` map). Only
   apply this when widening the parent's trust surface is itself an
   intentional design change — it touches a public surface.

Both shapes are marked `safety: surface-changing`, so `harn fix --apply
--safety surface-changing` will dispatch the chosen repair.

#### Stability

The matched `EffectInheritanceViolation` runtime payload's `_type`
discriminator (`effect_inheritance_violation`) is part of the stable
contract.

### `HARN-LLM-003`

**Category:** `LLM` (LLM calls) &nbsp;·&nbsp; **API stability:** `stable`

LLM call is missing schema validation

- **Repair:** `llm/add-schema` &nbsp;·&nbsp; **Safety:** `surface-changing`
- Add a typed output schema to the LLM call
- **See also:** [`HARN-LLM-004`](#harn-llm-004)

#### How to fix

- Pass a `schema:` option that validates the model output, with `schema_retries:` as appropriate.
- Drop or rename deprecated options to the names listed in the LLM call quickref.
- Pick capability flags (`tool_calling: true`, etc.) instead of branching on `provider:` identity.

### `HARN-LLM-004`

**Category:** `LLM` (LLM calls) &nbsp;·&nbsp; **API stability:** `stable`

LLM schema option is invalid

- **See also:** [`HARN-LLM-003`](#harn-llm-003)

#### How to fix

- Pass a `schema:` option that validates the model output, with `schema_retries:` as appropriate.
- Drop or rename deprecated options to the names listed in the LLM call quickref.
- Pick capability flags (`tool_calling: true`, etc.) instead of branching on `provider:` identity.

### `HARN-LLM-005`

**Category:** `LLM` (LLM calls) &nbsp;·&nbsp; **API stability:** `stable`

prompt branches on provider identity instead of capability flags

- **Repair:** `llm/use-capability-flag` &nbsp;·&nbsp; **Safety:** `capability-changing`
- Branch on a capability flag instead of provider identity
- **See also:** [`HARN-PRM-004`](#harn-prm-004)

#### How to fix

- Pass a `schema:` option that validates the model output, with `schema_retries:` as appropriate.
- Drop or rename deprecated options to the names listed in the LLM call quickref.
- Pick capability flags (`tool_calling: true`, etc.) instead of branching on `provider:` identity.

### `HARN-LLM-006`

**Category:** `LLM` (LLM calls) &nbsp;·&nbsp; **API stability:** `stable`

provider, model, and requested options form a known-unsafe composition

#### How to fix

- Remove a portable generation option that the selected route does not support, or choose a compatible route.
- For `cache` or `prompt_cache_ttl`, declare prompt-cache support and
  selectable TTL values for the custom route, or remove the request.
- Put a provider-native control below `provider_options.<provider>` instead of spelling it as a portable top-level option.
- Omit `tool_format` to use the catalog default, or select the catalog-recommended format.
- Choose a provider/model route whose declared tool-calling channel supports the requested format.
- For a deliberate probe, add a non-empty `tool_format_override_reason`;
  agent loops record the override event, and provider-call records expose the
  effective format and native tool count.

Dynamic values remain under the runtime guard. Custom generation routes remain
open-world, but custom cache controls require authored capability facts because
Harn must select a provider-specific lowering. This diagnostic only rejects a
literal composition the registry can already prove unsafe.

### `HARN-ORC-001`

**Category:** `ORC` (Orchestration constructs) &nbsp;·&nbsp; **API stability:** `stable`

orchestration construct has invalid arity

#### What it means

An orchestration construct — agent / workflow / pipeline / tool definition, or a
`select` block — does not satisfy the structural rules Harn enforces. These
constructs carry runtime semantics that depend on a small set of well-formed
shapes.

#### How to fix

- Re-read the orchestration construct's spec section and align the arity / type / structure.

### `HARN-ORC-002`

**Category:** `ORC` (Orchestration constructs) &nbsp;·&nbsp; **API stability:** `stable`

orchestration construct argument has invalid type

#### What it means

An orchestration construct — agent / workflow / pipeline / tool definition, or a
`select` block — does not satisfy the structural rules Harn enforces. These
constructs carry runtime semantics that depend on a small set of well-formed
shapes.

#### How to fix

- Re-read the orchestration construct's spec section and align the arity / type / structure.

### `HARN-ORC-003`

**Category:** `ORC` (Orchestration constructs) &nbsp;·&nbsp; **API stability:** `stable`

agent declaration is invalid

#### What it means

An orchestration construct — agent / workflow / pipeline / tool definition, or a
`select` block — does not satisfy the structural rules Harn enforces. These
constructs carry runtime semantics that depend on a small set of well-formed
shapes.

#### How to fix

- Re-read the orchestration construct's spec section and align the arity / type / structure.

### `HARN-ORC-004`

**Category:** `ORC` (Orchestration constructs) &nbsp;·&nbsp; **API stability:** `stable`

workflow declaration is invalid

#### What it means

An orchestration construct — agent / workflow / pipeline / tool definition, or a
`select` block — does not satisfy the structural rules Harn enforces. These
constructs carry runtime semantics that depend on a small set of well-formed
shapes.

#### How to fix

- Re-read the orchestration construct's spec section and align the arity / type / structure.

### `HARN-ORC-005`

**Category:** `ORC` (Orchestration constructs) &nbsp;·&nbsp; **API stability:** `stable`

tool declaration is invalid

#### What it means

An orchestration construct — agent / workflow / pipeline / tool definition, or a
`select` block — does not satisfy the structural rules Harn enforces. These
constructs carry runtime semantics that depend on a small set of well-formed
shapes.

#### How to fix

- Re-read the orchestration construct's spec section and align the arity / type / structure.

### `HARN-ORC-006`

**Category:** `ORC` (Orchestration constructs) &nbsp;·&nbsp; **API stability:** `stable`

pipeline declaration is invalid

#### What it means

An orchestration construct — agent / workflow / pipeline / tool definition, or a
`select` block — does not satisfy the structural rules Harn enforces. These
constructs carry runtime semantics that depend on a small set of well-formed
shapes.

#### How to fix

- Re-read the orchestration construct's spec section and align the arity / type / structure.

### `HARN-ORC-007`

**Category:** `ORC` (Orchestration constructs) &nbsp;·&nbsp; **API stability:** `stable`

select construct is invalid

#### What it means

An orchestration construct — agent / workflow / pipeline / tool definition, or a
`select` block — does not satisfy the structural rules Harn enforces. These
constructs carry runtime semantics that depend on a small set of well-formed
shapes.

#### How to fix

- Re-read the orchestration construct's spec section and align the arity / type / structure.

### `HARN-ORC-008`

**Category:** `ORC` (Orchestration constructs) &nbsp;·&nbsp; **API stability:** `stable`

statement cannot be reached

- **Repair:** `control-flow/remove-dead` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Remove the unreachable code

#### What it means

An orchestration construct — agent / workflow / pipeline / tool definition, or a
`select` block — does not satisfy the structural rules Harn enforces. These
constructs carry runtime semantics that depend on a small set of well-formed
shapes.

#### How to fix

- Re-read the orchestration construct's spec section and align the arity / type / structure.

### `HARN-ORC-009`

**Category:** `ORC` (Orchestration constructs) &nbsp;·&nbsp; **API stability:** `stable`

Flow invariant attribute set is invalid

invalid)

#### What it means

An orchestration construct — agent / workflow / pipeline / tool definition, or a
`select` block — does not satisfy the structural rules Harn enforces. These
constructs carry runtime semantics that depend on a small set of well-formed
shapes.

#### How to fix

- Re-read the orchestration construct's spec section and align the arity / type / structure.

### `HARN-ORC-010`

**Category:** `ORC` (Orchestration constructs) &nbsp;·&nbsp; **API stability:** `stable`

execution target path cannot be found

#### What it means

An orchestration construct — agent / workflow / pipeline / tool definition, or a
`select` block — does not satisfy the structural rules Harn enforces. These
constructs carry runtime semantics that depend on a small set of well-formed
shapes.

#### How to fix

- Re-read the orchestration construct's spec section and align the arity / type / structure.

### `HARN-ORC-011`

**Category:** `ORC` (Orchestration constructs) &nbsp;·&nbsp; **API stability:** `stable`

a self-deadlock acquire would block forever

#### What it means

The VM detected an acquire that can never succeed and would otherwise block
forever, so it surfaced an error instead of hanging. Two deterministic,
provably-unresolvable cases are caught:

- **Re-entrant mutex.** A lexical `mutex { ... }` block acquires a
  non-reentrant, capacity-1 lock. Entering a second `mutex` block on the same
  key while the first is still held — directly or through a called function —
  can never be granted, because the sole permit holder is the task that is now
  waiting on it.
- **Self-join.** A task `await`s its own join handle. The handle only resolves
  when the task finishes, so waiting on it from inside the task itself can
  never complete.

Harn's static checks (like Rust's borrow checker) prevent data races but not
deadlocks; this runtime guard is the analogue of the Go runtime's
"all goroutines are asleep — deadlock!" detection for the cases that are
provable with no false positives.

#### How to fix

- Do not nest `mutex { ... }` blocks that resolve to the same lock, and avoid
  calling a function that opens a `mutex` block from inside another one.
  Restructure so the lock is released before it is acquired again, or hold a
  single lock around the whole critical section.
- Never `await` the handle of the current task. Await it from the task that
  spawned it instead.

This error is non-retryable: it indicates a structural concurrency bug, not a
transient failure.

### `HARN-ORC-012`

**Category:** `ORC` (Orchestration constructs) &nbsp;·&nbsp; **API stability:** `stable`

a wait-for graph cycle would block forever

#### What it means

The VM detected that every active task in the current execution tree is waiting,
and the outstanding channel operations cannot match a sender with a receiver.
Without this guard the run would block forever.

This check only fires when the runtime can prove the wait is closed-world:
running tasks, sleeping tasks, time-bounded selects, and `deadline { ... }`
scopes keep the guard from reporting a deadlock.

#### How to fix

Make sure every blocking `receive` has a task that can still send to that
channel, and every blocking `send` on a full channel has a task that can still
receive from it. If the wait is intentionally optional, use `try_receive`,
`select timeout`, `harness.runtime.channel_select(..., timeout_ms)`, or wrap the
operation in a `deadline { ... }` block.

This error is non-retryable: it indicates a structural concurrency bug, not a
transient failure.

### `HARN-STD-001`

**Category:** `STD` (Stdlib usage) &nbsp;·&nbsp; **API stability:** `stable`

stdlib symbol has been renamed or deprecated

- **Repair:** `stdlib/migrate-renamed` &nbsp;·&nbsp; **Safety:** `scope-local`
- Rename the call to the renamed stdlib symbol

#### What it means

A stdlib symbol is being used in a way Harn does not support, or has been
renamed / deprecated since the script was written. The stdlib is the live source
of truth for what's available.

#### How to fix

- Switch to the supported / renamed stdlib API listed in the diagnostic help text.

### `HARN-STD-002`

**Category:** `STD` (Stdlib usage) &nbsp;·&nbsp; **API stability:** `stable`

stdlib call is invalid

#### What it means

A stdlib symbol is being used in a way Harn does not support, or has been
renamed / deprecated since the script was written. The stdlib is the live source
of truth for what's available.

#### How to fix

- Switch to the supported / renamed stdlib API listed in the diagnostic help text.

### `HARN-STD-003`

**Category:** `STD` (Stdlib usage) &nbsp;·&nbsp; **API stability:** `stable`

builtin call has invalid arity

#### What it means

A stdlib symbol is being used in a way Harn does not support, or has been
renamed / deprecated since the script was written. The stdlib is the live source
of truth for what's available.

#### How to fix

- Switch to the supported / renamed stdlib API listed in the diagnostic help text.

### `HARN-STD-101`

**Category:** `STD` (Stdlib usage) &nbsp;·&nbsp; **API stability:** `stable`

public stdlib function is missing declared metadata

- **Repair:** `doc/add-stdlib-metadata` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Add `@effects` and `@errors` fields to the stdlib function's doc block

#### What it means

Every public stdlib function ships with a prose summary plus two
machine-meaningful metadata fields above its `pub fn` declaration so that
`harn graph --json`, the LSP hover, generated docs, and downstream agents
can read a single source of truth for the function's runtime contract.

The required fields are:

| Field      | Purpose                                                                                           |
|------------|---------------------------------------------------------------------------------------------------|
| `@effects` | Capabilities the function may touch (e.g. `fs.read`, `stdio.write`, `llm.call`). `[]` means pure. |
| `@errors`  | Error variants the function may surface. `[]` means infallible.                                   |

Two more fields are recognized but optional:

| Field            | Purpose                                                                       |
|------------------|--------------------------------------------------------------------------------|
| `@api_stability` | Stability promise (`experimental`, `internal`, `deprecated`). Absent ⇒ stable. |
| `@example`       | Hand-written usage example. Absent ⇒ tooling derives one from the signature.   |

Only write an `@example` when it shows something the type signature cannot
(non-obvious argument shapes, a multi-step idiom). LSP hover and
`harn graph --json` synthesize a signature-derived example otherwise.

The lint warns when a `pub fn` declared inside an embedded stdlib module
(`crates/harn-stdlib/src/stdlib/**/*.harn`) is missing a required field
from the `/** ... */` HarnDoc block immediately above it.

#### How to fix

Add the missing fields to the function's HarnDoc block:

```harn,ignore
/**
 * Render the project README.
 *
 * @effects: [fs.read, fs.write]
 * @errors: [FileNotFound, PermissionDenied]
 */
pub fn render_readme(path: string) -> Result<string, FsError> { ... }
```

The lint considers `@effects: []` and `@errors: []` valid declarations — they
explicitly assert "no effects" and "infallible" respectively, which is what
agents need to read out of the graph.

### `HARN-STD-102`

**Category:** `STD` (Stdlib usage) &nbsp;·&nbsp; **API stability:** `stable`

public stdlib function is missing an explicit return type

#### What it means

Every public function exported from Harn's embedded standard library is a
contract boundary. The function's return type is part of that contract: callers,
generated docs, LSP hovers, schema export, and downstream agents should be able
to depend on the producer shape without rediscovering it from implementation
details.

This lint warns when a `pub fn` declared inside
`crates/harn-stdlib/src/stdlib/**/*.harn` omits an explicit `-> Type`
annotation. Private helpers remain inferable.

#### How to fix

Declare the narrowest honest return type in the function signature:

```harn,ignore
pub fn read_json(path: string) -> Result<JsonValue, JsonReadError> {
  ...
}
```

Use named closed records for finite object shapes, `Result<T, E>` for fallible
operations, and typed maps such as `dict<string, V>` for true open-key maps.
Do not silence the lint with `any` or open `dict` unless the value is genuinely
opaque and the caller must narrow it before use.

### `HARN-PRM-001`

**Category:** `PRM` (Prompt templates) &nbsp;·&nbsp; **API stability:** `stable`

prompt template cannot be parsed

- **See also:** [`HARN-PRM-007`](#harn-prm-007)

#### What it means

A prompt template (`.harn.prompt` / `.prompt`) failed validation, either because
the template body is malformed or because it references the model surface in a
way that violates Harn's prompt safety rules.

#### How to fix

- Fix the template syntax (`{{ }}`, `{% %}` are the only structural delimiters).
- Replace identity-based branching with capability flags from the LLM call options.

### `HARN-PRM-002`

**Category:** `PRM` (Prompt templates) &nbsp;·&nbsp; **API stability:** `stable`

prompt template has too many capability-aware branches

- **Repair:** `manual/needs-human` &nbsp;·&nbsp; **Safety:** `needs-human`
- Plan a human-led change; auto-apply is not safe here
- **See also:** [`HARN-LNT-044`](#harn-lnt-044)

#### What it means

A prompt template (`.harn.prompt` / `.prompt`) failed validation, either because
the template body is malformed or because it references the model surface in a
way that violates Harn's prompt safety rules.

#### How to fix

- Fix the template syntax (`{{ }}`, `{% %}` are the only structural delimiters).
- Replace identity-based branching with capability flags from the LLM call options.

### `HARN-PRM-003`

**Category:** `PRM` (Prompt templates) &nbsp;·&nbsp; **API stability:** `stable`

prompt construction risks direct injection

- **Repair:** `prompts/escape-injection` &nbsp;·&nbsp; **Safety:** `scope-local`
- Pass the untrusted input through a structured placeholder
- **See also:** [`HARN-LNT-026`](#harn-lnt-026)

#### What it means

A prompt template (`.harn.prompt` / `.prompt`) failed validation, either because
the template body is malformed or because it references the model surface in a
way that violates Harn's prompt safety rules.

#### How to fix

- Fix the template syntax (`{{ }}`, `{% %}` are the only structural delimiters).
- Replace identity-based branching with capability flags from the LLM call options.

### `HARN-PRM-004`

**Category:** `PRM` (Prompt templates) &nbsp;·&nbsp; **API stability:** `stable`

prompt template branches on provider identity

- **Repair:** `llm/use-capability-flag` &nbsp;·&nbsp; **Safety:** `capability-changing`
- Branch on a capability flag instead of provider identity
- **See also:** [`HARN-LLM-005`](#harn-llm-005), [`HARN-LNT-046`](#harn-lnt-046)

branch)

#### What it means

A prompt template (`.harn.prompt` / `.prompt`) failed validation, either because
the template body is malformed or because it references the model surface in a
way that violates Harn's prompt safety rules.

#### How to fix

- Fix the template syntax (`{{ }}`, `{% %}` are the only structural delimiters).
- Replace identity-based branching with capability flags from the LLM call options.

### `HARN-PRM-005`

**Category:** `PRM` (Prompt templates) &nbsp;·&nbsp; **API stability:** `stable`

prompt references a tool outside the declared surface

- **Repair:** `prompts/add-tool-to-surface` &nbsp;·&nbsp; **Safety:** `surface-changing`
- Add the referenced tool to the declared tool surface

#### What it means

A prompt template (`.harn.prompt` / `.prompt`) failed validation, either because
the template body is malformed or because it references the model surface in a
way that violates Harn's prompt safety rules.

#### How to fix

- Fix the template syntax (`{{ }}`, `{% %}` are the only structural delimiters).
- Replace identity-based branching with capability flags from the LLM call options.

### `HARN-PRM-006`

**Category:** `PRM` (Prompt templates) &nbsp;·&nbsp; **API stability:** `stable`

prompt references a deferred tool without tool search

- **Repair:** `prompts/add-tool-to-surface` &nbsp;·&nbsp; **Safety:** `surface-changing`
- Add the referenced tool to the declared tool surface

deferred reference)

#### What it means

A prompt template (`.harn.prompt` / `.prompt`) failed validation, either because
the template body is malformed or because it references the model surface in a
way that violates Harn's prompt safety rules.

#### How to fix

- Fix the template syntax (`{{ }}`, `{% %}` are the only structural delimiters).
- Replace identity-based branching with capability flags from the LLM call options.

### `HARN-PRM-007`

**Category:** `PRM` (Prompt templates) &nbsp;·&nbsp; **API stability:** `stable`

prompt or template target cannot be found

#### What it means

A prompt template (`.harn.prompt` / `.prompt`) failed validation, either because
the template body is malformed or because it references the model surface in a
way that violates Harn's prompt safety rules.

#### How to fix

- Fix the template syntax (`{{ }}`, `{% %}` are the only structural delimiters).
- Replace identity-based branching with capability flags from the LLM call options.

### `HARN-MOD-001`

**Category:** `MOD` (Modules and exports) &nbsp;·&nbsp; **API stability:** `stable`

module import cannot be resolved

- **Repair:** `imports/fix-path` &nbsp;·&nbsp; **Safety:** `scope-local`
- Replace the import path with a resolvable target
- **See also:** [`HARN-IMP-001`](#harn-imp-001), [`HARN-IMP-002`](#harn-imp-002)

#### What it means

A module-level import declaration cannot be satisfied or has been authored in a
non-canonical form. The module graph must resolve cleanly before any further
checks.

#### How to fix

- Confirm the import path resolves on disk and the imported symbol is exported.
- Run `harn lint --fix` to auto-sort / dedupe import groups.

### `HARN-MOD-002`

**Category:** `MOD` (Modules and exports) &nbsp;·&nbsp; **API stability:** `stable`

module import is unused

- **Repair:** `imports/remove-unused` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Remove the unused import
- **See also:** [`HARN-LNT-017`](#harn-lnt-017)

#### What it means

A module-level import declaration cannot be satisfied or has been authored in a
non-canonical form. The module graph must resolve cleanly before any further
checks.

#### How to fix

- Confirm the import path resolves on disk and the imported symbol is exported.
- Run `harn lint --fix` to auto-sort / dedupe import groups.

### `HARN-MOD-003`

**Category:** `MOD` (Modules and exports) &nbsp;·&nbsp; **API stability:** `stable`

module imports are not in canonical order

- **Repair:** `imports/reorder` &nbsp;·&nbsp; **Safety:** `format-only`
- Reorder imports into canonical grouping

#### What it means

A module-level import declaration cannot be satisfied or has been authored in a
non-canonical form. The module graph must resolve cleanly before any further
checks.

#### How to fix

- Confirm the import path resolves on disk and the imported symbol is exported.
- Run `harn lint --fix` to auto-sort / dedupe import groups.

### `HARN-MOD-004`

**Category:** `MOD` (Modules and exports) &nbsp;·&nbsp; **API stability:** `stable`

module export is invalid

#### What it means

A module-level import declaration cannot be satisfied or has been authored in a
non-canonical form. The module graph must resolve cleanly before any further
checks.

#### How to fix

- Confirm the import path resolves on disk and the imported symbol is exported.
- Run `harn lint --fix` to auto-sort / dedupe import groups.

### `HARN-MOD-005`

**Category:** `MOD` (Modules and exports) &nbsp;·&nbsp; **API stability:** `stable`

module imports expose colliding names

#### What it means

A module-level import declaration cannot be satisfied or has been authored in a
non-canonical form. The module graph must resolve cleanly before any further
checks.

#### How to fix

- Confirm the import path resolves on disk and the imported symbol is exported.
- Run `harn lint --fix` to auto-sort / dedupe import groups.

### `HARN-MOD-006`

**Category:** `MOD` (Modules and exports) &nbsp;·&nbsp; **API stability:** `stable`

module re-exports conflict

#### What it means

A module-level import declaration cannot be satisfied or has been authored in a
non-canonical form. The module graph must resolve cleanly before any further
checks.

#### How to fix

- Confirm the import path resolves on disk and the imported symbol is exported.
- Run `harn lint --fix` to auto-sort / dedupe import groups.

### `HARN-MOD-007`

**Category:** `MOD` (Modules and exports) &nbsp;·&nbsp; **API stability:** `stable`

imported module failed to compile

#### What it means

An `import` in this file resolves to a module that could not itself be lexed or
parsed. Because the target never produced an AST, none of its symbols exist, so
callers would otherwise see every imported name reported as "undefined" at their
own call sites — pointing you at the wrong file. This diagnostic instead names
the broken module and surfaces its real lex/parse error.

#### How to fix

- Open the imported module named in the message and fix the lex/parse error
  reported there (the message includes its failing line and column).
- Re-run `harn check` on the imported module directly to confirm it compiles,
  then re-check this consumer.

### `HARN-RMD-001`

**Category:** `RMD` (Reminder lifecycle) &nbsp;·&nbsp; **API stability:** `stable`

reminder lifecycle option key is not recognized

- **See also:** [`HARN-RMD-002`](#harn-rmd-002), [`HARN-RMD-005`](#harn-rmd-005)

Reminder lifecycle option tables reject unknown keys so reminder shape stays stable across
transcript transforms, hooks, and bridge integrations.

Use only the documented `transcript.inject_reminder` keys: `body`, `tags`, `dedupe_key`,
`ttl_turns`, `preserve_on_compact`, `propagate`, and `role_hint`.

For `transcript.clear_reminders`, use at least one selector from `id`, `tag`, or `dedupe_key`.

### `HARN-RMD-002`

**Category:** `RMD` (Reminder lifecycle) &nbsp;·&nbsp; **API stability:** `stable`

reminder payload shape is invalid

- **See also:** [`HARN-RMD-001`](#harn-rmd-001), [`HARN-RMD-005`](#harn-rmd-005)

The reminder payload shape is invalid.

`session/remind` expects a typed reminder object, not a user-message payload.
Provide a non-empty `body`, use string `tags`, a positive integer `ttl_turns`
when present, boolean `preserve_on_compact`, and one of the documented
`propagate` and `role_hint` values. Put host-specific extension fields under
`_meta`.

### `HARN-RMD-003`

**Category:** `RMD` (Reminder lifecycle) &nbsp;·&nbsp; **API stability:** `stable`

retired provider-specific reminder role-hint diagnostic

This diagnostic is retired and retained only so stored diagnostics and tooling
can continue to resolve its stable code. Directives now use one provider-neutral
model-facing envelope and `role_hint` no longer selects a provider-specific
slot.

Use `authority` to express `contract`, `corrective`, or `advisory` precedence.

### `HARN-RMD-004`

**Category:** `RMD` (Reminder lifecycle) &nbsp;·&nbsp; **API stability:** `stable`

discardable reminder has no TTL

A reminder literal sets `preserve_on_compact: false` while leaving
`ttl_turns` unset or `nil`. That reminder can live forever during normal
turns, but it is allowed to disappear at the next transcript compaction.

Set a finite `ttl_turns` for short-lived nudges, or set
`preserve_on_compact: true` when the reminder must survive compaction.

### `HARN-RMD-005`

**Category:** `RMD` (Reminder lifecycle) &nbsp;·&nbsp; **API stability:** `stable`

reminder propagate value is not recognized

- **See also:** [`HARN-RMD-001`](#harn-rmd-001), [`HARN-RMD-002`](#harn-rmd-002)

A reminder specified an unsupported `propagate` value. Reminder propagation must
be one of `all`, `session`, or `none`.

Use `all` for reminders that should follow child agents, `session` for the
current session only, and `none` for reminders that should never propagate.

Example fix:

```harn
transcript.inject_reminder(transcript(), {
  body: "Check the task ledger before continuing.",
  propagate: "session",
})
```

### `HARN-RMD-006`

**Category:** `RMD` (Reminder lifecycle) &nbsp;·&nbsp; **API stability:** `stable`

reminder provider returned a malformed reminder spec

- **See also:** [`HARN-RMD-002`](#harn-rmd-002)

A reminder provider closure returned a value that could not be parsed as a
`ReminderSpec`. Provider closures may return `nil`, a reminder spec, an effect
such as `{reminder: {...}}`, or a list of those effects.

Return a dict with a non-empty `body` and only supported reminder fields.

Example fix:

```harn
register_reminder_provider({
  id: "custom",
  subscribes_to: ["session_idle"],
  evaluate: { _ctx ->
    return {
      reminder: {body: "Re-check current session state.", ttl_turns: 1},
    }
  },
})
```

### `HARN-RMD-007`

**Category:** `RMD` (Reminder lifecycle) &nbsp;·&nbsp; **API stability:** `stable`

too many reminder providers are enabled

- **See also:** [`HARN-RMD-004`](#harn-rmd-004)

An `agent_loop` enables more than eight distinct reminder providers. Many
providers can inject overlapping ambient context and increase prompt size.

Disable providers that are not useful for the loop, or split the loop into
smaller stages with different reminder settings.

Example fix:

```harn
agent_loop(task, nil, {
  reminders: {providers: ["token_pressure", "idle_nudge"]},
})
```

### `HARN-RMD-008`

**Category:** `RMD` (Reminder lifecycle) &nbsp;·&nbsp; **API stability:** `stable`

hook event does not support reminder effects

- **See also:** [`HARN-RMD-006`](#harn-rmd-006)

A hook handler returned a reminder effect from a lifecycle event that cannot
inject reminders. Worker lifecycle events are observational and must not mutate
the active transcript with reminder effects.

Move the reminder to a session, tool, step, or persona hook that runs at a
turn-boundary mutation point.

Example fix:

```harn
fn main(harness: Harness) {
  harness.agent.register_session_hook(
    "post_turn", { _hook_harness, _event ->
      return {
        reminder: {body: "Review worker progress before continuing."},
      }
    })
}
```

### `HARN-SUS-001`

**Category:** `SUS` (Suspend / resume lifecycle) &nbsp;·&nbsp; **API stability:** `stable`

suspend_agent target worker is not running

- **See also:** [`HARN-SUS-003`](#harn-sus-003), [`HARN-SUS-010`](#harn-sus-010)

`suspend_agent(...)` can only create a new cooperative suspension for a
running worker. A worker that is awaiting input, completed, failed, cancelled,
or interrupted no longer has a running turn boundary where a suspend checkpoint
can be installed.

Use the worker summary status to branch before suspending. For terminal workers,
spawn a fresh worker instead. Calling `suspend_agent(...)` again on an already
suspended worker remains an idempotent summary read.

### `HARN-SUS-002`

**Category:** `SUS` (Suspend / resume lifecycle) &nbsp;·&nbsp; **API stability:** `stable`

ResumeConditions validation failed

- **See also:** [`HARN-SUS-007`](#harn-sus-007), [`HARN-SUS-008`](#harn-sus-008)

`ResumeConditions` did not match the supported shape for a suspended agent.
The object may contain `trigger`, `timeout`, and `on_event`. Trigger entries
must parse as trigger specs, timeout entries need a positive
`duration_minutes`, and event entries must be valid runtime event topics.

Validate untrusted condition tables with `parse_resume_conditions(...)` or
`agent_await_resumption(...)` before passing them to `suspend_agent(...)`.

### `HARN-SUS-003`

**Category:** `SUS` (Suspend / resume lifecycle) &nbsp;·&nbsp; **API stability:** `stable`

resume_agent target worker is not suspended

- **See also:** [`HARN-SUS-001`](#harn-sus-001), [`HARN-SUS-006`](#harn-sus-006)

`resume_agent(...)` was called on a live worker that is not currently in the
`suspended` state. Warm resume is only meaningful for workers with an active
suspension envelope.

Check the worker summary before resuming. Use `send_input(...)` for workers
that are awaiting input, inspect terminal summaries directly, or load a
snapshot path when you only need to restore persisted state.

### `HARN-SUS-004`

**Category:** `SUS` (Suspend / resume lifecycle) &nbsp;·&nbsp; **API stability:** `stable`

resume snapshot cannot be loaded or used

- **See also:** [`HARN-SUS-003`](#harn-sus-003)

`resume_agent(...)` could not load the requested snapshot, or the snapshot was
not usable as a worker state. The path may be missing, stale, unreadable, or
from an incompatible worker-state format.

Use the `snapshot_path` from the latest worker summary and keep the worker
state directory available across process restarts. If the snapshot belongs to
an older incompatible runtime, spawn a replacement worker instead of resuming.

### `HARN-SUS-005`

**Category:** `SUS` (Suspend / resume lifecycle) &nbsp;·&nbsp; **API stability:** `stable`

agent_await_resumption was invoked outside agent_loop structural handling

- **See also:** [`HARN-SUS-002`](#harn-sus-002)

The model-facing `agent_await_resumption` tool is structural. `agent_loop`
intercepts that tool call, records audit metadata, and turns it into a
suspend checkpoint. Invoking the tool handler directly bypasses that control
flow, so Harn rejects it.

Call `agent_await_resumption(reason, conditions?)` only to build or normalize
the request shape, or let an `agent_loop` turn handle the lifecycle tool call.

### `HARN-SUS-006`

**Category:** `SUS` (Suspend / resume lifecycle) &nbsp;·&nbsp; **API stability:** `stable`

concurrent resume changed the worker before resume could complete

- **See also:** [`HARN-SUS-003`](#harn-sus-003), [`HARN-SUS-010`](#harn-sus-010)

A resume operation started while the worker looked suspended, but another
operator, trigger, or timeout changed the worker state before the resume could
finish. Harn rejects the later resume so callers do not accidentally treat a
race as a successful wake-up.

Refresh the worker summary and retry only if the worker is still suspended.
For trigger-driven resumes, ensure only one trigger or timeout path remains
armed for the suspension.

### `HARN-SUS-007`

**Category:** `SUS` (Suspend / resume lifecycle) &nbsp;·&nbsp; **API stability:** `stable`

ResumeConditions trigger could not be registered

- **See also:** [`HARN-SUS-002`](#harn-sus-002), [`HARN-SUS-008`](#harn-sus-008)

The `conditions.trigger` entry was valid enough to request an auto-resume
binding, but Harn could not register or resolve the trigger in the live
dispatcher registry.

Check the trigger kind, event matchers, and registry availability. Prefer
normalizing the full `ResumeConditions` table before suspension so shape
errors surface as `HARN-SUS-002`.

### `HARN-SUS-008`

**Category:** `SUS` (Suspend / resume lifecycle) &nbsp;·&nbsp; **API stability:** `stable`

resume timeout action is unsupported

- **See also:** [`HARN-SUS-002`](#harn-sus-002), [`HARN-SUS-007`](#harn-sus-007)

An auto-resume timeout used an `on_timeout` action Harn does not implement.
Supported actions are `resume_with_summary`, `resume_with_input`, and `fail`.

Use `parse_resume_conditions(...)` to normalize timeout settings before
suspending, or update the timeout table to one of the supported actions.

### `HARN-SUS-009`

**Category:** `SUS` (Suspend / resume lifecycle) &nbsp;·&nbsp; **API stability:** `stable`

resume input failed agent_loop input validation

- **See also:** [`HARN-SUS-003`](#harn-sus-003)

`resume_agent(...)` received resume input that could not be converted into a
non-empty task prompt for the resumed agent loop.

Pass `nil` when resuming without new input, or pass a non-empty string or value
whose string form is suitable as the next task prompt.

### `HARN-SUS-010`

**Category:** `SUS` (Suspend / resume lifecycle) &nbsp;·&nbsp; **API stability:** `stable`

closed suspended worker cannot be resumed

- **See also:** [`HARN-SUS-003`](#harn-sus-003), [`HARN-SUS-006`](#harn-sus-006)

The target worker was closed or cancelled after being suspended. Closing a
worker removes the suspension envelope and rejects future resume attempts
against that live worker handle.

Treat the worker as terminal. Spawn a new worker, or restore an explicit
snapshot path only when you intentionally want to inspect persisted state.

### `HARN-SUS-011`

**Category:** `SUS` (Suspend / resume lifecycle) &nbsp;·&nbsp; **API stability:** `stable`

replay resume input hash diverges from journaled suspension

The replay runtime computed a different resume-input fingerprint than the
one captured in the original run's [`ResumptionReceipt`]. Lifecycle replay
must be deterministic: the second run must feed the suspended worker the
exact same payload it received the first time, because the worker's
post-resume trajectory was hashed into the journaled state.

Common causes:

- A pipeline that used to depend on wall-clock time or another
  non-deterministic input now produces a different resume payload on
  replay. Capture the payload itself (or use `harness.testing.clock_set(...)`) so the
  replayed runtime can reproduce the original value.
- The journal entry was edited after the original run completed. The
  signed timestamp on the receipt detects this — verify the receipt's
  signature with `verify_lifecycle_receipt_signature(...)` before
  blaming the runtime.
- The settlement agent (drain phase) changed its mind and produced a
  different resume input. Record a fresh `ResumptionReceipt` in the
  current run instead of replaying the stale one.

If the new payload is intentionally different (eg. you are running a
deliberate counterfactual, not a determinism replay), open the trace as
a fresh recording rather than feeding it back through the replay oracle.

### `HARN-SUS-012`

**Category:** `SUS` (Suspend / resume lifecycle) &nbsp;·&nbsp; **API stability:** `stable`

replay drain decision prompt hash diverges from journaled receipt

The replay runtime tried to memoize a settlement-agent drain decision
against a [`DrainDecisionReceipt`] whose `prompt_hash` does not match the
candidate prompt the second run computed. Drain decisions are journaled
so that replay can skip the settlement agent's LLM call — but if the
prompt drifts, the recorded `action` no longer reflects what the agent
would actually decide.

Common causes:

- The pipeline's settlement-agent prompt template or its inputs (the
  unsettled-state snapshot, the budget summary, the worker list) changed
  between record and replay. Either re-record the trace or roll the
  template back to its recorded form.
- The drain item set itself changed shape (eg. a new suspended subagent
  appeared on replay). Fix the upstream determinism gap first, then the
  drain receipt will line up.

The recorded receipt is still safe to inspect via
`harness.agent.lifecycle_receipts_snapshot()`, and its signed timestamp
tells you when the original decision was minted. Treat the mismatch as a
signal that the replay is no longer a determinism check.

### `HARN-SUS-013`

**Category:** `SUS` (Suspend / resume lifecycle) &nbsp;·&nbsp; **API stability:** `stable`

lifecycle receipt signed timestamp failed verification

A [`SuspensionReceipt`], [`ResumptionReceipt`], or [`DrainDecisionReceipt`]
carries a `SignedLifecycleTimestamp` whose HMAC signature does not match
the receipt's identifying fields under the per-process signing salt.

The signature binds `(kind, at_ms, subject_id, initiator_id)`, so any of
the following will trip this diagnostic:

- The receipt was minted by a *different* `harn` process and is now
  being verified after a restart. Per-process salts are intentional —
  cross-process verification requires migrating to the longer-lived
  `provenance` chain (see `build_signed_receipt`).
- The journal entry was edited after the fact (eg. someone hand-tweaked
  `at_ms` or `initiator_id` in the on-disk record). The replay oracle
  rejects the tampered receipt before relying on it.
- The signing algorithm or key id changed across a Harn upgrade. The
  current algorithm is `hmac-sha256` with key id `local-session`.

If the original process is gone but you still need to read the
receipts, treat the on-disk payloads as advisory only — the unsigned
fields (`handle`, `reason`, `input_hash`, `action`) are still
informational, but `replay_resume_input` / `replay_drain_decision` will
refuse to memoize against an unverified receipt.

### `HARN-LNT-001`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

renamed stdlib symbol lint

- **Repair:** `stdlib/migrate-renamed` &nbsp;·&nbsp; **Safety:** `scope-local`
- Rename the call to the renamed stdlib symbol
- **See also:** [`HARN-STD-001`](#harn-std-001)

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-002`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

cyclomatic complexity lint

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-003`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

naming convention lint

- **Repair:** `style/rename-to-convention` &nbsp;·&nbsp; **Safety:** `surface-changing`
- Rename to match the casing convention for this kind

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-004`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

eager collection conversion lint

- **Repair:** `collections/prefer-lazy` &nbsp;·&nbsp; **Safety:** `scope-local`
- Replace the eager collection step with a lazy variant

conversion)

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-005`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

redundant clone lint

- **Repair:** `clones/remove-redundant` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Remove the redundant clone

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-006`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

long-running workflow cleanup lint

- **Repair:** `manual/needs-human` &nbsp;·&nbsp; **Safety:** `needs-human`
- Plan a human-led change; auto-apply is not safe here

cleanup)

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-007`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

MCP tool annotations lint

- **Repair:** `manual/needs-human` &nbsp;·&nbsp; **Safety:** `needs-human`
- Plan a human-led change; auto-apply is not safe here

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-008`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

PR open without secret scan lint

scan)

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-009`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

shadow variable lint

- **Repair:** `bindings/rename-shadow` &nbsp;·&nbsp; **Safety:** `scope-local`
- Rename the shadowing binding to a distinct name

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-010`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

persona hook target lint

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-011`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

dead code after return lint

- **Repair:** `control-flow/remove-dead` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Remove the unreachable code

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-012`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

let then return lint

- **Repair:** `control-flow/flatten` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Flatten the unnecessary control flow construct

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-013`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

unhandled approval result lint

- **Repair:** `errors/check-or-rescue` &nbsp;·&nbsp; **Safety:** `scope-local`
- Check the result or wrap the call in a `rescue` block

result)

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-014`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

unused variable lint

- **Repair:** `bindings/rename-unused` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Mark an unused binding without changing callable arity

#### How to fix

- Use the `_` discard binding for side-effect-only local expressions, for
  example `let _ = cleanup()`.
- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-015`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

unused pattern binding lint

- **Repair:** `bindings/rename-unused` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Mark an unused binding without changing callable arity

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-016`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

unused parameter lint

- **Repair:** `bindings/rename-unused` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Mark an unused binding without changing callable arity

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Functions, closures, public or extended pipelines, and fixture- or table-bound
  tests keep positional arity and prefix the unused parameter with `_`.
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-017`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

unused import lint

- **Repair:** `imports/remove-unused` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Remove the unused import
- **See also:** [`HARN-MOD-002`](#harn-mod-002)

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-018`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

mutable never reassigned lint

- **Repair:** `bindings/make-immutable` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Declare the never-reassigned binding with `const` instead of `let`
- **See also:** [`HARN-OWN-002`](#harn-own-002)

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-019`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

unused function lint

- **Repair:** `declarations/remove-unused` &nbsp;·&nbsp; **Safety:** `surface-changing`
- Remove the unused declaration

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-020`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

unused type lint

- **Repair:** `declarations/remove-unused` &nbsp;·&nbsp; **Safety:** `surface-changing`
- Remove the unused declaration

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-021`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

persona body must call steps lint

steps)

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-022`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

undefined function lint

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-023`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

pipeline return type lint

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-024`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

missing harndoc lint

- **Repair:** `doc/add-harndoc` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Add a `///` doc comment describing this declaration

#### What it means

A public function has no `/** */` HarnDoc block above its declaration.

This rule is **opt-in**: it only runs when the nearest `harn.toml` sets

```toml
[lint]
require_docstrings = true
```

Out of the box, public functions in user scripts and pipelines need no
doc comments. Embedded stdlib sources always enforce docstrings (the
HARN-STD-101 metadata lint relies on the doc block existing).

#### How to fix

- Add a `/** ... */` block with a one-line prose summary above the
  declaration. No structured tags are required; LSP hover derives a usage
  example from the type signature.
- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Or leave `require_docstrings` unset if the project doesn't want the
  requirement.

### `HARN-LNT-025`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

assert outside test lint

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-026`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

prompt injection risk lint

- **Repair:** `prompts/escape-injection` &nbsp;·&nbsp; **Safety:** `scope-local`
- Pass the untrusted input through a structured placeholder
- **See also:** [`HARN-PRM-003`](#harn-prm-003)

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-027`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

connector effect policy lint

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-028`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

unnecessary cast lint

- **Repair:** `casts/remove-redundant` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Remove the redundant cast

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-029`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

untyped dict access lint

- **Repair:** `types/validate-boundary-value` &nbsp;·&nbsp; **Safety:** `scope-local`
- Validate the parsed value with schema_expect() or schema_check() before reading it

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-030`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

constant logical operand lint

- **Repair:** `expressions/simplify` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Simplify the expression to its canonical form

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-031`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

pointless comparison lint

- **Repair:** `expressions/simplify` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Simplify the expression to its canonical form

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

#### NaN caveat

A self-comparison such as `x == x` or `x != x` is **not** constant when `x` is a
NaN float: `x == x` is `false` and `x != x` is `true` for NaN (and the same
holds element-wise for lists/dicts containing a NaN). The lint therefore only
offers an auto-fix when the operand provably cannot be NaN (a non-float literal,
or a list/dict built only from such literals). When the operand could be a float
the lint still warns — a self-comparison is usually a typo — but leaves the code
untouched and points you at `is_nan(...)`, the idiomatic NaN test.

### `HARN-LNT-032`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

comparison to bool lint

- **Repair:** `expressions/simplify` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Simplify the expression to its canonical form

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-033`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

invalid binary operator literal lint

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-034`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

redundant nil ternary lint

- **Repair:** `expressions/simplify` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Simplify the expression to its canonical form

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-035`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

empty block lint

- **Repair:** `blocks/remove-empty` &nbsp;·&nbsp; **Safety:** `scope-local`
- Remove the empty block or fill in an explicit body

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-036`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

unnecessary else return lint

- **Repair:** `control-flow/flatten` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Flatten the unnecessary control flow construct

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-037`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

duplicate match arm lint

- **Repair:** `match/remove-duplicate-arm` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Remove the duplicated match arm
- **See also:** [`HARN-MAT-002`](#harn-mat-002)

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-038`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

require in test lint

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-039`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

break outside loop lint

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-040`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

template parse lint

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-041`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

blank line between items lint

- **Repair:** `format/reformat` &nbsp;·&nbsp; **Safety:** `format-only`
- Apply canonical formatting

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-042`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

trailing comma lint

- **Repair:** `format/reformat` &nbsp;·&nbsp; **Safety:** `format-only`
- Apply canonical formatting

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-043`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

unnecessary parentheses lint

- **Repair:** `format/reformat` &nbsp;·&nbsp; **Safety:** `format-only`
- Apply canonical formatting

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-044`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

template variant explosion lint

- **Repair:** `manual/needs-human` &nbsp;·&nbsp; **Safety:** `needs-human`
- Plan a human-led change; auto-apply is not safe here
- **See also:** [`HARN-PRM-002`](#harn-prm-002)

explosion)

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-045`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

require file header lint

- **Repair:** `format/reformat` &nbsp;·&nbsp; **Safety:** `format-only`
- Apply canonical formatting

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-046`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

template provider identity branch lint

- **Repair:** `llm/use-capability-flag` &nbsp;·&nbsp; **Safety:** `capability-changing`
- Branch on a capability flag instead of provider identity
- **See also:** [`HARN-PRM-004`](#harn-prm-004)

identity branch)

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-047`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

import order lint

- **Repair:** `imports/reorder` &nbsp;·&nbsp; **Safety:** `format-only`
- Reorder imports into canonical grouping

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-048`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

prefer optional shorthand lint

- **Repair:** `expressions/simplify` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Simplify the expression to its canonical form

shorthand)

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-049`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

legacy doc comment lint

- **Repair:** `doc/migrate-comment-style` &nbsp;·&nbsp; **Safety:** `format-only`
- Migrate the legacy comment to canonical doc syntax

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-050`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

removed LLM options lint

- **Repair:** `llm/migrate-removed-option` &nbsp;·&nbsp; **Safety:** `scope-local`
- Replace the removed option with its supported equivalent

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-051`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

unnecessary safe navigation lint

- **Repair:** `expressions/simplify` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Simplify the expression to its canonical form

navigation)

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`).
- Suppress the lint with an attribute only when the surrounding code is intentionally non-idiomatic.

### `HARN-LNT-052`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

ambient clock builtin replaced by `harness.clock.*`

- **Repair:** `bindings/thread-harness-clock` &nbsp;·&nbsp; **Safety:** `scope-local`
- Replace the ambient clock builtin with the corresponding `harness.clock.*` method
- **See also:** [`HARN-NAM-101`](#harn-nam-101), [`HARN-LNT-001`](#harn-lnt-001)

#### What it means

The lint fires on any call to `now_ms`, `monotonic_ms`, `sleep_ms`,
`timestamp`, or `elapsed`. These were ambient clock-capability builtins in
the pre-`Harness` runtime. Time access now routes through the
`harness.clock.*` sub-handle so capability requirements appear in the type
system instead of being hidden in the stdlib surface.

The legacy effectful globals are removed. This lint supplies an actionable
migration repair before the checker reports the removed symbol.

#### How to fix

- Run `harn fix --apply --safety surface-changing` over the file. Calls inside
  an existing Harness boundary are rewritten in place; otherwise the fixer
  threads an explicit Harness parameter through local callers.
- Run lint again. `capability-attenuation` suggests replacing an unnecessarily
  broad helper parameter with the narrow nominal handle it actually uses.

### `HARN-LNT-053`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

ambient stdio builtin replaced by `harness.stdio.*`

- **Repair:** `bindings/thread-harness` &nbsp;·&nbsp; **Safety:** `scope-local`
- Thread the existing `harness` binding through local helper calls and replace the ambient stdio builtin with `harness.stdio.*`
- **See also:** [`HARN-NAM-101`](#harn-nam-101), [`HARN-LNT-001`](#harn-lnt-001)

#### What it means

The lint fires on calls to `print`, `println`, `eprint`, `eprintln`,
`read_line`, and `prompt_user`. These were ambient stdio-capability builtins
in the pre-`Harness` runtime. Stdio access now routes through the
`harness.stdio.*` sub-handle so capability requirements are visible in the
type system.

This lint is emitted during auto-repair planning so existing call sites can be
migrated before the removed builtin produces an unknown-name diagnostic. New
code should use `harness.stdio.print`, `harness.stdio.println`,
`harness.stdio.eprint`, `harness.stdio.eprintln`,
`harness.stdio.read_line`, or `harness.stdio.prompt`.

#### How to fix

- Run `harn fix --apply --safety surface-changing` over the file. Calls inside
  an existing Harness boundary are rewritten in place; otherwise the fixer
  threads an explicit Harness parameter through local callers.
- Run lint again. `capability-attenuation` suggests replacing an unnecessarily
  broad helper parameter with `HarnessStdio`.

### `HARN-LNT-054`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

ambient fs builtin replaced by `harness.fs.*`

- **Repair:** `bindings/thread-harness-fs` &nbsp;·&nbsp; **Safety:** `scope-local`
- Replace the ambient fs builtin with the corresponding `harness.fs.*` method
- **See also:** [`HARN-NAM-101`](#harn-nam-101), [`HARN-LNT-001`](#harn-lnt-001)

#### What it means

The lint fires on any call to `read_file`, `write_file`, `file_exists`,
`delete_file`, `append_file`, `append_file_locked`, `list_dir`, `mkdir`,
`copy_file`, `temp_dir`, `mkdtemp`, `stat`, `move_file`, `read_lines`,
`walk_dir`, `glob`, or `find_text`.
These were ambient fs-capability builtins in the pre-`Harness` runtime.
Filesystem access now routes through the `harness.fs.*` sub-handle so
capability requirements appear in the type system instead of being
hidden in the stdlib surface.

The legacy effectful globals are removed. This lint supplies an actionable
migration repair before the checker reports the removed symbol.

#### How to fix

- Run `harn fix --apply --safety surface-changing` over the file. Calls inside
  an existing Harness boundary are rewritten in place; otherwise the fixer
  threads an explicit Harness parameter through local callers.
- Run lint again. `capability-attenuation` suggests replacing an unnecessarily
  broad helper parameter with `HarnessFs`.

### `HARN-LNT-055`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

ambient env builtin replaced by `harness.env.*`

- **Repair:** `bindings/thread-harness-env` &nbsp;·&nbsp; **Safety:** `scope-local`
- Replace the ambient env builtin with the corresponding `harness.env.*` method
- **See also:** [`HARN-NAM-101`](#harn-nam-101), [`HARN-LNT-001`](#harn-lnt-001)

#### What it means

The lint fires on calls to the ambient `env` and `env_or` builtins.
Environment access now routes through the `harness.env.*` sub-handle so
capability requirements appear in the type system instead of being
hidden in the stdlib surface.

The legacy effectful globals are removed. This lint supplies an actionable
migration repair before the checker reports the removed symbol.

#### How to fix

- Run `harn fix --apply --safety surface-changing` over the file. Calls inside
  an existing Harness boundary are rewritten in place; otherwise the fixer
  threads an explicit Harness parameter through local callers.
- Run lint again. `capability-attenuation` suggests replacing an unnecessarily
  broad helper parameter with `HarnessEnv`.

### `HARN-LNT-056`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

ambient random builtin replaced by `harness.random.*`

- **Repair:** `bindings/thread-harness-random` &nbsp;·&nbsp; **Safety:** `scope-local`
- Replace the ambient random builtin with the corresponding `harness.random.*` method
- **See also:** [`HARN-NAM-101`](#harn-nam-101), [`HARN-LNT-001`](#harn-lnt-001)

#### What it means

The lint fires on calls to the ambient `random`, `random_int`,
`random_choice`, and `random_shuffle` builtins. Randomness now routes
through the `harness.random.*` sub-handle so capability requirements
appear in the type system instead of being hidden in the stdlib surface.

The legacy effectful globals are removed. Use the matching
`harness.random.*` method (`random` → `harness.random.f64`, `random_int` →
`harness.random.range`, etc.). Seeded streams via an explicit `Rng` handle remain available
through the `Rng.*` surface for tests that need deterministic output.

#### How to fix

- Run `harn fix --apply --safety surface-changing` over the file. Calls inside
  an existing Harness boundary are rewritten in place; otherwise the fixer
  threads an explicit Harness parameter through local callers.
- Run lint again. `capability-attenuation` suggests replacing an unnecessarily
  broad helper parameter with `HarnessRandom`.

### `HARN-LNT-057`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

ambient net builtin replaced by `harness.net.*`

- **Repair:** `bindings/thread-harness-net` &nbsp;·&nbsp; **Safety:** `scope-local`
- Replace the ambient net builtin with the corresponding `harness.net.*` method
- **See also:** [`HARN-NAM-101`](#harn-nam-101), [`HARN-LNT-001`](#harn-lnt-001)

#### What it means

The lint recognizes removed ambient network calls and supplies a migration
repair. Requests, downloads, streams, sessions, SSE, WebSockets, and servers
all route through the `HarnessNet` interface so capability requirements appear
in the type system instead of being hidden in a global surface. Pure response
constructors and event encoders remain ordinary globals.

The legacy effectful globals are not a compatibility surface: ordinary source
must use the corresponding `harness.net.*` method. The lint exists so old
source gets an actionable repair before the checker reports the removed
symbol.

#### How to fix

- Run `harn fix --apply --safety surface-changing` over the file. Calls inside
  an existing Harness boundary are rewritten in place; otherwise the fixer
  threads an explicit Harness parameter through local callers.
- Run lint again. `capability-attenuation` suggests replacing an unnecessarily
  broad helper parameter with `HarnessNet`.

### `HARN-LNT-058`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

if / while / guard condition is statically known to always succeed or always fail

#### What it means

The condition of an `if`, `while`, or `guard` is statically known — one of
its two branches is unreachable. The lint fires for two patterns:

##### 1. Constant-evaluable conditions

The condition reduces to a known boolean using only literal operands and
short-circuit / negation rules. Examples:

- `if true { … }`, `if false { … }` — direct booleans.
- `if nil { … }`, `if 0 { … }`, `if "" { … }` — falsy literals.
- `if (true || some_call()) { … }` — short-circuits to `true`.
- `if (some_call() && false) { … }` — short-circuits to `false`.
- `if !!true { … }` — chains of negations on a constant.

Side-effecting subexpressions inside a vacuous compound (`some_call()`
above) still execute, but their result is dead — usually a mistake left
behind by a partial refactor.

##### 2. Statically-determined `schema_is` / `is_type`

The variable's static type already proves whether the predicate matches:

- **Always true** — `x`'s static type is a subtype of the schema, so the
  truthy branch narrows to nothing new and the falsy branch is dead.
  Example: `x: int` and `schema_is(x, int)`, or `x: {a: int, b: string}`
  and `schema_is(x, {b: string})` (width subtyping).
- **Always false** — `x`'s static type and the schema are disjoint. The
  truthy branch is dead. Example: `x: int` and `schema_is(x, string)`.

The check is deliberately conservative: it skips when `x: unknown` or
`x: any` (the open-world top types — `schema_is` is informative there),
and it requires shape fields to match optionality before reporting
"always true" (an optional field in `x` can be absent at runtime, so the
predicate may still legitimately fail). The same shape is used by
[`no-unnecessary-condition` in typescript-eslint][tse] and the
[`unnecessary-invariant` rule in Flow][flow].

[tse]: https://typescript-eslint.io/rules/no-unnecessary-condition/
[flow]: https://flow.org/en/docs/linting/rule-reference/#toc-unnecessary-invariant

#### How to fix

- If the check is genuinely dead code, remove the surrounding `if` /
  `while` / `guard` and inline (or delete) the live branch.
- If you intended a *narrower* schema, change `S` to the narrower shape
  (e.g. a tagged variant, not the parent type).
- If the variable is meant to come from an untrusted boundary, type it
  as `unknown` (or `any`) and validate at the boundary; the lint will
  then correctly stay silent.

### `HARN-LNT-059`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

project rule-engine or native lint rule

#### What it means

A project-supplied rule matched here. This is not a built-in lint: it is a
structural rule (a `*.toml` pattern from the project's `[rules] ruleDirs`) run
through the linter via the rule engine (`harn-rules`), a `.lint.harn` script
rule loaded from `ruleDirs`, or a trusted native rule library loaded from
`[rules] nativeRuleDirs`.

The message, severity, and any suggested fix come from the matched rule. The
diagnostic's reported rule id is the project rule's id, so you filter it with
`disable_rules` (or `[lint]` config) by that id, exactly like a built-in.

#### Why it fires

The project declared one or more rule directories:

```toml
[rules]
ruleDirs = ["rules"]
nativeRuleDirs = ["native-rules"]
```

and a rule in one of them matched this code. Declarative rules pair a structural
`pattern` with a `message`; script rules return findings from `lint(source)`;
native rules emit diagnostics through the `harn_lint::native` ABI. Rules that
carry a fix surface it as a machine-applicable lint fix.

#### How to fix

- Address the issue the rule describes (see the rule's `message`).
- If the rule carries a fix, `harn lint --fix` applies it. Declarative codemod
  fixes can also be applied by `harn codemod`.
- To silence it, disable the rule by its id, or remove/adjust the rule in your
  `ruleDirs` / `nativeRuleDirs`.

#### See also

- The rule engine: `harn scan`, `harn codemod`, and the `harn-rules` skill.
- Project rule discovery: `[rules] ruleDirs` and `nativeRuleDirs` in
  `harn.toml`.

### `HARN-LNT-060`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

inline options dict bypasses the typed option constructors

- **Repair:** `types/add-shape-annotation` &nbsp;·&nbsp; **Safety:** `surface-changing`
- Annotate the dict with a concrete shape type
- **See also:** [`HARN-LNT-050`](#harn-lnt-050), [`HARN-LNT-029`](#harn-lnt-029)

#### What it means

`agent_loop` and `workflow_execute` accept large option surfaces. Passing an
inline dict literal at the call site skips the typed structural aliases
(`AgentSpec` from `std/agent/options`, `StageSpec` and friends from
`std/workflow/options`), so option typos and wrongly-typed values are only
discovered at runtime — or silently ignored.

The dict still executes exactly as before; this lint is a deprecation-level
nudge toward the single documented path.

#### How to fix

- Bind the options to an annotated `let` first:
  `let opts: AgentSpec = {...}` then `agent_loop(task, system, opts)`.
- Or build the options through a typed constructor:
  `agent_preset(kind, {...})` / `agent_options({...})` from
  `std/agent/options`, or `workflow_stage_spec({...})` from
  `std/workflow/options`.
- Suppress the lint only for throwaway probes and fixtures where the inline
  dict is the point.

### `HARN-LNT-061`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

nil coalesce fallback has no effect

- **Repair:** `expressions/simplify` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Simplify the expression to its canonical form

#### What it means

The fallback side of a nil-coalescing expression is the literal `nil`:

```harn,ignore
const value = task?.flag ?? nil
```

That expression is equivalent to `task?.flag`. If the left side is present, its
value is returned; if it is absent, `?? nil` returns `nil`, which is already the
left side's absent value.

#### How to fix

Remove the fallback:

```harn
const value = task?.flag
```

Use a real default only when the surrounding code needs a non-nil value.

The same rule applies to `false` used as the exact positive condition of an
assertion:

```harn,ignore
assert(task?.ready ?? false)
```

`assert` accepts any value and applies Harn truthiness. Both `nil` and `false`
fail, so the fallback cannot change the result. Assert the value directly:

```harn
assert(task?.ready)
```

Keep boolean fallbacks used outside this exact position. In particular, a
negation or a `?? true` fallback can change how a missing value behaves.

### `HARN-LNT-062`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

nil coalesce fallback is unreachable

#### What it means

The left side of a `??` expression has a statically non-nil type, so the
fallback branch can never run. This often happens after a typed conversion or
producer already returns a concrete value:

```harn
fn parse_number(raw: string) -> int {
  return 0
}

const raw: string? = nil
const value = parse_number(raw ?? "0") ?? 0
```

The outer fallback is redundant when `parse_number(...)` has a non-nil result
type.

#### How to fix

Remove the unreachable fallback:

```harn
fn parse_number(raw: string) -> int {
  return 0
}

const raw: string? = nil
const value = parse_number(raw ?? "0")
```

If the fallback really is needed, change the left expression or annotation so
its type accurately includes `nil`.

### `HARN-LNT-063`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

non-null assertion `!` on an already-non-nil value

- **Repair:** `expressions/simplify` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Simplify the expression to its canonical form

A non-null assertion (`expr!`) was applied to a value whose type is already
non-nil, so the assertion does nothing and can be removed.

#### How to fix

- Apply the lint's auto-fix where one is offered (`harn lint --fix`) to drop the
  trailing `!`.
- Suppress the lint with an attribute only when the surrounding code is
  intentionally non-idiomatic.

### `HARN-LNT-064`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

a mutable variable captured from an enclosing scope is reassigned inside a `parallel`/`spawn` body, so concurrent branches share one cell and race

A variable declared in an enclosing scope is reassigned inside a `parallel` or
`spawn` body. Harn closures capture by reference, and `parallel`/`spawn` bodies
are lowered into closures whose captured cells are shared — by `Arc` — with
every concurrent branch. Reassigning such a variable therefore mutates a single
shared cell from many branches at once.

This is memory-safe (the cell is a mutex), but the writes race: branches
interleave at every `await` point (an `llm_call`, a host call), so a
read-modify-write such as `total = total + x` silently loses updates, and under
a multi-threaded runtime it is a genuine data race in the logical sense.

#### How to fix

- Prefer returning a value from each branch and combining the results after the
  fan-out, which is deterministic and lock-free:

  ```harn
  const parts = parallel each items { item -> compute(item) }
  const total = fold(parts, 0, { acc, part -> acc + part })
  ```

- If a shared mutable accumulator is genuinely required, guard it explicitly and
  accept that ordering is nondeterministic.
- Reassigning a variable that is declared *inside* the branch body is fine and
  never flagged — only captures from an enclosing scope trip this lint.

### `HARN-LNT-065`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

nil coalesce fallback repeats the left identifier

- **Repair:** `expressions/simplify` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Simplify the expression to its canonical form

#### What it means

Both sides of a nil-coalescing expression are the same identifier:

```harn,ignore
const value = task ?? task
```

For a plain identifier, this is equivalent to `task`. If `task` is present, the
left side wins; if it is `nil`, the fallback evaluates to the same `nil` value.

#### How to fix

Remove the fallback:

```harn
const value = task
```

If a real recovery path is needed, replace the right side with an actual
default or explicitly handle `nil` before using the value.

### `HARN-LNT-066`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

the result of a pure collection method is discarded, so the call has no effect on the receiver

A `list` / `dict` / `set` / `string` method was called as a statement and its
result thrown away, so the call has no effect at all.

Every method on Harn's built-in collections is **pure**. They are persistent,
copy-on-write values: `appending` clones the receiver, adds an item, and
returns a *new* list. It never modifies the receiver.

```harn
const l = []
l.appending(1)      // error[HARN-LNT-066]: no effect — `l` is still []
l.appending(2)      // error[HARN-LNT-066]
// l == []
```

The method name reads as a value-producing operation, but its result still has
to be consumed. Without this diagnostic, discarding it would typecheck, run
without error, and quietly build an empty list.

#### How to fix

Assign the result back. Because the binding's value changes, it must be `let`
rather than `const` (see the binding rule in the language spec — `const` means
this binding's value never changes):

```harn
let l = []
l = l.appending(1)
l = l.appending(2)
// l == [1, 2]
```

When building a collection in a loop, the same shape applies:

```harn
let out = []
for item in items {
  out = out.appending(transform(item))
}
```

To call a method purely for a side effect inside it — which only a closure
argument can produce — the result is not discarded in the same sense, and this
lint does not fire. Prefer a `for` loop over `map` when you want effects.

#### When it does not fire

- The tail expression of a value-producing block (a closure body, `match` arm,
  `if`/`else` branch, `try`, or `block { … }`), where the value *is* the
  block's result rather than a discarded statement.
- A call taking a closure argument (`items.map({ item -> … })`), which may
  perform effects and so is not provably inert.
- A receiver rooted at `harness`, whose methods exist for their effects.
- A method name the file also declares in an `impl` block, which may be a
  user-defined method with effects of its own.

### `HARN-LNT-068`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

prompt template names a filter the engine does not implement

A `.harn.prompt` or `.prompt` expression names a filter the template engine
does not implement. Filter names are valid identifiers, so this is a lint error
rather than a template parse error.

```harn-prompt,ignore
Hello {{ name | uppr }}
```

#### How to fix

Replace the name with one of the built-in filters. Near misses include a
machine-applicable suggestion, so `harn lint --fix` can repair `uppr` to
`upper`.

```harn-prompt
Hello {{ name | upper }}
```

### `HARN-LNT-069`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

helper accepts root Harness but uses only narrow capability handles

- **Repair:** `bindings/attenuate-harness` &nbsp;·&nbsp; **Safety:** `surface-changing`
- Replace the root Harness parameter with the single capability the helper uses

An ordinary function takes the root `Harness`, but every use in its body reads
only one or two capabilities off it. Root authority belongs at entrypoints and
orchestration boundaries. A reusable helper should ask for what it actually
uses, so a reader can tell from the signature what the helper can touch.

#### How to fix

When the helper uses one capability, change the parameter to the `Harness*`
type the diagnostic names and pass that sub-handle at each call site:

```harn
fn load_manifest(fs: HarnessFs, path: string) -> string {
  return fs.read_text(path)
}

fn main(harness: Harness) {
  harness.stdio.println(load_manifest(harness.fs, "harn.toml"))
}
```

When it uses two, take them as one record rather than two parameters, so each
call site names what it grants:

```harn
fn refresh_index(io: {fs: HarnessFs, tools: HarnessTools}, path: string) {
  io.tools.invoke("index", {source: io.fs.read_text(path)})
}

fn main(harness: Harness) {
  refresh_index({fs: harness.fs, tools: harness.tools}, "src/index.md")
}
```

A record keeps each grant named at the call site. Two positional handles are
easy to swap by accident, and the swap still type-checks if the shapes are
similar.

`harn fix --apply --safety surface-changing` performs both rewrites. It changes
the parameter, updates every use inside the helper, then narrows the argument at
the call sites it can see (`harness` becomes `harness.fs`, or becomes
`{fs: harness.fs, tools: harness.tools}`). It reuses the existing parameter
name so the new binding cannot shadow anything else in scope, which is why a
narrowed parameter can come out of this repair still called `harness`.
`HARN-LNT-073` reports that and renames it, so running
`harn fix` again finishes the job.

#### When to keep root `Harness`

Keep it when the function genuinely coordinates several capabilities or hands
authority onward. Runtime entrypoints keep it too, because the host calls those
signatures directly: `main`, jobs, trigger handlers, registered callbacks, and
the standard connector exports. The lint reads connector exceptions from the
connector ABI registry, and stays quiet whenever authority escapes the function
or the surrounding code cannot prove that narrowing is safe.

### `HARN-LNT-070`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

public API has too many same-typed positional parameters

A public function takes four or more positional parameters of the same type.
A caller who swaps two of them still type-checks, and nothing at the call site
says which value is which.

#### How to fix

Take the group as one record instead, so the call site names each value:

```harn
pub fn draw_box(rect: {x: int, y: int, width: int, height: int}) {
  // ...
}

draw_box({x: 0, y: 0, width: 80, height: 24})
```

This is guidance about a public API's readability, not a limit on how many
parameters a function may have. It does not fire for private helpers, or for
signatures where the types already tell the values apart. Parameters with
defaults do count, since callers still pass them positionally. A rest parameter
does not.

### `HARN-LNT-071`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

global builtin has moved to a Harness capability method

- **Repair:** `bindings/thread-harness-method` &nbsp;·&nbsp; **Safety:** `scope-local`
- Replace the ambient runtime builtin with its typed `harness.*` method and thread authority through local callers

This builtin was called as a global, but it performs an effect, so it now lives
on a `Harness` capability instead. Reaching it through the harness is what makes
a script's effects readable from its signatures.

#### How to fix

Call the method on the handle the diagnostic names, and pass that handle into
the helper that needs it:

```harn
fn report(stdio: HarnessStdio, message: string) {
  stdio.println(message)
}
```

Some globals that returned a single value are now a field on a structured
snapshot. For example, `platform()` becomes `harness.system.platform().os`, and
`username()` becomes `harness.system.identity().username`.

Keep the root `Harness` at entrypoints and at boundaries that genuinely
coordinate several capabilities. Elsewhere, pass the narrowest handle that
covers what the function does.

`harn fix --apply --safety surface-changing` rewrites these calls for you,
including calls inside `${...}` string interpolation, and adds the parameter to
the local callers that need to supply the handle.

### `HARN-LNT-072`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

call names a builtin whose declared exposure keeps Harn source from naming it

Every builtin declares an *exposure* saying which surface may reach it. Two of
those values are closed to scripts:

- `privileged_wire` — a trusted embedder primitive. Only artifacts stamped with
  privileged provenance may call it; user modules cannot name or re-export it.
- `runtime_internal` — a compiler or runtime implementation detail that is never
  source-visible.

The VM still registers those builtins, because the runtime and the host bridge
call them. That is why this reads as a lint rather than an unknown name: the
name exists, it is simply not yours to call, and the typechecker will reject the
program.

A `Harness` capability method also reports here when it is called as a bare
global — the method is real, but it is reached through its handle.

#### How to fix

Call the surface the diagnostic names.

For a capability method, thread the handle:

```harn
fn summarize(obs: HarnessObs, message: string) {
  obs.log_info(message)
}
```

For `host_call`, there is no single replacement, because it was a generic
dispatcher rather than one operation. Almost every target it dispatched to has a
declared capability method, so the rewrite is per-namespace:

```harn
host_call("ast.outline", {path: p})   // before
harness.ast.outline(p)                // after
```

When the operation name is a string literal, the diagnostic resolves it for you
and names the destination method — including across spelling differences, so
`host_call("prmonitor.run_commands", ...)` reports `harness.pr_monitor.run_commands`.
A computed operation name cannot be resolved, so those calls get the generic
route; look the target up with `harn contracts builtins`.

An operation that no capability declares is one your host provides. Reach it
through the callable root installed with `register_callable_host_operation`,
as `<root>.<operation>`.

Argument shapes do not carry over, and the diagnostic deliberately does not
guess them. `host_call` packed its arguments into one dict keyed by the host's
names, while a typed method takes the parameters its own signature declares —
a mapping that happens to compile is not necessarily the one you meant.

If a script genuinely needs a privileged wire, it has to run as a privileged
artifact; calling it from ordinary source will not work regardless of how the
call is spelled.

### `HARN-LNT-073`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

parameter carrying a narrow capability handle is not named for that capability

- **Repair:** `bindings/name-capability-parameter` &nbsp;·&nbsp; **Safety:** `surface-changing`
- Rename the capability parameter and its references after the capability it carries

A parameter is typed as a narrow capability handle — `HarnessNet`, `HarnessFs`,
`HarnessStdio`, and so on — but is named something else, most often `harness`.
The type says the function holds one capability; the name says it holds root
authority. Readers and call sites believe the name.

```harn
pub fn ack(harness: HarnessNet, url: string) {
  return harness.http_post(url, {})
}
```

Nothing here is unsound, but `harness.http_post(...)` reads like a root handle
with a surprising method on it. The narrowing that
`HARN-LNT-069` asks for is only legible once the name carries
it too.

#### How to fix

Name the parameter after the capability's field on `Harness`, which is the same
name a call site already uses to produce it:

```harn
pub fn ack(net: HarnessNet, url: string) {
  return net.http_post(url, {})
}

fn main(harness: Harness) {
  ack(harness.net, "https://example.invalid/ack")
}
```

Harn arguments are positional, so a parameter rename moves no call site.
`harn fix --apply --safety surface-changing` performs it, rewriting the
parameter and every reference to it inside the function.

This also finishes what `HARN-LNT-069` starts. That repair
narrows the type but reuses the existing parameter name, so its output can
still read `harness: HarnessNet`; this lint then renames it.

#### When the lint stays quiet

The rename must be provably safe from the function alone, so the lint reports
nothing when:

- the capability's name is already bound in the function — as another
  parameter, or anywhere in the body — because renaming onto it would capture
  that binding;
- a nested function or closure rebinds either name, because its inner
  references belong to a different binding;
- the parameter is a rest parameter, or the type is root `Harness`, which is
  correctly named `harness`.

A dict key that happens to share the parameter's name is a record field, not a
reference, so `{harness: harness}` becomes `{harness: net}` and the record's
shape is unchanged.

### `HARN-LNT-074`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

explicitly unused private pipeline input can be removed

- **Repair:** `bindings/remove-unused-pipeline-input` &nbsp;·&nbsp; **Safety:** `surface-changing`
- Remove an explicitly unused test pipeline input

#### What it means

An underscore-prefixed input on a private, unextended pipeline is unused and
has no default, rest, or caller-owned contract. Unattributed host and operational
pipelines and bare `@test` declarations qualify. Unattributed `test_*` pipelines
and other attributes retain their runner, fixture, and table-owned inputs.

#### How to fix

Review external host callers, then apply the surface-changing fix explicitly to
remove the input. Keep and type a named input when the pipeline uses the value.

### `HARN-LNT-075`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

tool handler returns a freeform dict, so its outcome must be inferred from key names instead of declared by its type

A tool handler's return value is what tells the runtime whether the operation
succeeded. When that value is a plain dict, nothing in it says so. The runtime
has to guess from key names, and every reader of the result has to guess the
same way.

That guess cannot be finished. A dict carrying a `status` key may be declaring
a failure or merely reporting progress, and no set of key names separates the
two, because the value carries no type saying which it is. A handler is equally
free to return `{failed: true}` or `{error_code: 7}`, which no convention
covers, and those read as success.

This is not hypothetical. A handler returning `{ok: false}` had its refusal
rendered to display text before anything classified it, and every dict-shaped
refusal was reported a success until `harn#7884` fixed the reader.

#### How to fix

Return a typed struct. The type declares the outcome, so no reader has to infer
it:

```harn
struct ApplyOutcome {
  ok: bool,
  message: string,
}

fn apply_handler(args: dict) -> ApplyOutcome {
  if args.blocked {
    return ApplyOutcome{ok: false, message: "the rewrite was refused"}
  }
  return ApplyOutcome{ok: true, message: "applied"}
}
```

When the handler's result is text the model should read, return the handler
result envelope, which renders its `text` verbatim and carries structured data
beside it:

```harn
fn search_handler(args: dict) -> dict {
  return {
    schema: "harn.agent_tool_handler_result.v1",
    text: "3 matches",
    data: {matches: 3},
  }
}
```

#### Severity

This reports as a warning while in-tree handlers migrate. It becomes an error
once no untyped handler result remains, at which point outcome classification
stops being a heuristic over key names.

### `HARN-LNT-076`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

tool handler reaches the privileged host wire

`host_call(...)` belongs to the trusted entry boundary selected by an embedding
host. A tool handler runs later under model or client control, where that wire
is not a stable capability: its bridge can be absent even when the same call
worked while assembling the pipeline.

Read the host-owned value before registering the handler and pass it through a
closure or an explicit typed capability. At runtime, reaching `host_call` from
a handler raises an error that names the unavailable operation; it never falls
through to a standalone default that can be mistaken for an empty host answer.

### `HARN-LNT-077`

**Category:** `LNT` (Lint rules) &nbsp;·&nbsp; **API stability:** `stable`

record literal copies fields one by one from a value that `pick` can select

- **Repair:** `records/pick-fields` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Replace the field-by-field record copy with `pick`

#### What it means

A record literal repeats every field name twice to copy fields from one
value:

```harn,ignore
const ctx = {env: harness.env, fs: harness.fs, tools: harness.tools}
```

`pick` does the same job in one call and keeps each field's type:

```harn,ignore
const ctx = pick(harness, ["env", "fs", "tools"])
```

The rule fires only when the rewrite can't change behavior: the literal has
two or more entries, every entry copies a field of the same name from one
value, and each field is one the checker knows is present. That covers the
root `Harness`, a parameter or binding with a record type, and a struct value.
A dictionary key or an optional field stays as it is, because a missing key
gives `nil` in the literal but is left out by `pick`.

#### How to fix

Replace the literal with `pick` and the field names in the same order:

```harn
fn main(harness: Harness) {
  const ctx = pick(harness, ["env", "fs"])
  harness.stdio.println(ctx.fs.exists(ctx.env.get_or("APP_CONFIG", ".")))
}
```

`harn lint --fix` and `harn fix --apply` make this change automatically.

### `HARN-FMT-001`

**Category:** `FMT` (Formatter) &nbsp;·&nbsp; **API stability:** `stable`

formatter could not parse the source

#### What it means

The formatter could not produce a canonical layout for this source — either
because parsing failed first, or because the existing layout drifts from the
canonical form.

#### How to fix

- Run `harn fmt` to bring the file to canonical form, or fix the parse error blocking the formatter.

### `HARN-FMT-002`

**Category:** `FMT` (Formatter) &nbsp;·&nbsp; **API stability:** `stable`

source is not in canonical format

- **Repair:** `format/reformat` &nbsp;·&nbsp; **Safety:** `format-only`
- Apply canonical formatting

#### What it means

The formatter could not produce a canonical layout for this source — either
because parsing failed first, or because the existing layout drifts from the
canonical form.

#### How to fix

- Run `harn fmt` to bring the file to canonical form, or fix the parse error blocking the formatter.

### `HARN-FMT-003`

**Category:** `FMT` (Formatter) &nbsp;·&nbsp; **API stability:** `stable`

formatter normalized trailing comma layout

- **Repair:** `format/reformat` &nbsp;·&nbsp; **Safety:** `format-only`
- Apply canonical formatting

#### What it means

The formatter could not produce a canonical layout for this source — either
because parsing failed first, or because the existing layout drifts from the
canonical form.

#### How to fix

- Run `harn fmt` to bring the file to canonical form, or fix the parse error blocking the formatter.

### `HARN-IMP-001`

**Category:** `IMP` (Import resolution) &nbsp;·&nbsp; **API stability:** `stable`

import target cannot be resolved

- **Repair:** `imports/fix-path` &nbsp;·&nbsp; **Safety:** `scope-local`
- Replace the import path with a resolvable target
- **See also:** [`HARN-MOD-001`](#harn-mod-001), [`HARN-IMP-002`](#harn-imp-002)

#### What it means

Import resolution failed at a deeper layer than `MOD` — the file, symbol, or
module graph cannot be constructed. Compilation cannot proceed.

#### How to fix

- Add the missing module or symbol, or update the import path.
- Break import cycles by extracting the shared definitions into a third module.

### `HARN-IMP-002`

**Category:** `IMP` (Import resolution) &nbsp;·&nbsp; **API stability:** `stable`

imported symbol does not exist

#### What it means

A selective import named a symbol the target module does not make available.
Two cases trigger this:

- The symbol genuinely does not exist in the target module, or import
  resolution failed at a deeper layer than `MOD` (the file or module graph
  cannot be constructed).
- The symbol **exists but is not exported**: it is a non-`pub` function in a
  module that marks something else `pub`. A module that marks at least one
  function `pub` opts into explicit exports, so its non-`pub` functions are
  private and cannot be imported by name — the same rule wildcard imports
  already follow, and the same rule TypeScript, Rust, and Go enforce.

#### How to fix

- Add the missing module or symbol, or update the import path.
- If the symbol is meant to be part of the module's API, mark it `pub`.
- To exercise a private helper from a test, co-locate the test (a `pipeline`
  or `fn` in the same file sees module-private functions directly), rather
  than importing the private name.
- Break import cycles by extracting the shared definitions into a third module.

### `HARN-IMP-003`

**Category:** `IMP` (Import resolution) &nbsp;·&nbsp; **API stability:** `stable`

import graph contains a cycle

- **See also:** [`HARN-IMP-001`](#harn-imp-001)

#### What it means

Import resolution failed at a deeper layer than `MOD` — the file, symbol, or
module graph cannot be constructed. Compilation cannot proceed.

#### How to fix

- Add the missing module or symbol, or update the import path.
- Break import cycles by extracting the shared definitions into a third module.

### `HARN-OWN-001`

**Category:** `OWN` (Ownership and mutability) &nbsp;·&nbsp; **API stability:** `stable`

immutable binding is reassigned

- **Repair:** `bindings/make-mutable` &nbsp;·&nbsp; **Safety:** `scope-local`
- Declare the binding with `let` so it can be reassigned
- **See also:** [`HARN-OWN-002`](#harn-own-002)

#### How to fix

- Declare the binding with `let` (mutable) instead of `const` (immutable) if it really needs to be reassigned.
- Restructure so owned values do not escape their scope.

### `HARN-OWN-002`

**Category:** `OWN` (Ownership and mutability) &nbsp;·&nbsp; **API stability:** `stable`

mutable binding is never reassigned

- **Repair:** `bindings/make-immutable` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Declare the never-reassigned binding with `const` instead of `let`
- **See also:** [`HARN-LNT-018`](#harn-lnt-018)

#### How to fix

- Declare the binding with `const` (immutable) instead of `let` (mutable) since it is never reassigned.
- Restructure so owned values do not escape their scope.

### `HARN-OWN-003`

**Category:** `OWN` (Ownership and mutability) &nbsp;·&nbsp; **API stability:** `stable`

owned value escapes its valid scope

#### What it means

A binding annotated with `owned<T>` carries sole ownership of a drop-able
resource (a file, channel, MCP session, transcript writer, sync permit). The
compiler emits an implicit `defer { drop(x) }` at the binding's enclosing
block so the resource closes deterministically when control leaves the scope.

Returning the binding by name — or otherwise transferring it out of the scope
without declaring the transfer in the type — defeats that contract: the auto-
drop never fires, and the resource leaks until the runtime garbage-collects
the handle (which, for OS resources, may be never).

```harn
fn open_log() -> channel {
  const ch: owned<channel> = channel("log", 64)
  return ch    // HARN-OWN-003: `ch` escapes; auto-drop is bypassed
}
```

#### How to fix

- **Transfer ownership explicitly** by declaring the function's return type as
  `owned<T>`. The caller then receives an owned binding and is responsible for
  dropping it (or transferring it on again):

  ```harn
  fn open_log() -> owned<channel> {
    const ch: owned<channel> = channel("log", 64)
    return ch    // OK — ownership flows to the caller
  }
  ```

- **Confine the value to a narrower scope** with `block { ... }`. Owned values
  declared inside the block drop before the enclosing function continues:

  ```harn
  fn write_log(msg: string) -> nil {
    block {
      const ch: owned<channel> = channel("log", 64)
      send(ch, msg)
    }   // `ch` drops here, before `write_log` continues
    nil
  }
  ```

- **Drop the value explicitly** with `drop(x)` if you need to release earlier
  than the enclosing block would close it.

### `HARN-OWN-004`

**Category:** `OWN` (Ownership and mutability) &nbsp;·&nbsp; **API stability:** `stable`

unvalidated boundary value is used directly

#### How to fix

- Validate values returned by boundary APIs such as `json_parse`,
  `llm_call`, and `llm_completion` before accessing fields or indexes.
- Prefer a typed result schema at the call site when the boundary supports it,
  or pass the value through `schema_expect()` / guard it with `schema_is()`
  before property or subscript access.
- A type annotation on the binding also validates: since harn#6252 a declared
  type is checked where it is written, exactly as a declared parameter type is
  checked where it is passed. `const doc: {name: string} = json_parse(text)`
  rejects a payload whose `name` is not a string. Constructing a struct with
  annotated fields (harn#6268) checks those fields the same way.
- Choose between them by the report you want on failure. A binding assertion
  names the binding and the declared type; `schema_expect()` names the field
  that failed and why. For a payload from outside the program, the second is
  usually worth the extra line.

### `HARN-RCV-001`

**Category:** `RCV` (Error recovery) &nbsp;·&nbsp; **API stability:** `stable`

rescue construct is outside a function body

- **Repair:** `errors/wrap-in-fn` &nbsp;·&nbsp; **Safety:** `surface-changing`
- Move the construct inside a function body
- **See also:** [`HARN-RCV-002`](#harn-rcv-002), [`HARN-RCV-003`](#harn-rcv-003)

#### What it means

A recovery construct (`try`, `rescue`) is in an invalid position or is shaped in
a way Harn's structured-error handling does not support.

#### How to fix

- Move the construct inside a function body, or wrap it in one.
- Use `try` / `rescue` only around expressions that can produce structured errors.

### `HARN-RCV-002`

**Category:** `RCV` (Error recovery) &nbsp;·&nbsp; **API stability:** `stable`

try construct is outside a function body

- **Repair:** `errors/wrap-in-fn` &nbsp;·&nbsp; **Safety:** `surface-changing`
- Move the construct inside a function body
- **See also:** [`HARN-RCV-001`](#harn-rcv-001)

#### What it means

A recovery construct (`try`, `rescue`) is in an invalid position or is shaped in
a way Harn's structured-error handling does not support.

#### How to fix

- Move the construct inside a function body, or wrap it in one.
- Use `try` / `rescue` only around expressions that can produce structured errors.

### `HARN-RCV-003`

**Category:** `RCV` (Error recovery) &nbsp;·&nbsp; **API stability:** `stable`

rescue construct is invalid

#### What it means

A recovery construct (`try`, `rescue`) is in an invalid position or is shaped in
a way Harn's structured-error handling does not support.

#### How to fix

- Move the construct inside a function body, or wrap it in one.
- Use `try` / `rescue` only around expressions that can produce structured errors.

### `HARN-MAT-001`

**Category:** `MAT` (Match exhaustiveness) &nbsp;·&nbsp; **API stability:** `stable`

match expression is not exhaustive

- **Repair:** `match/add-missing-arms` &nbsp;·&nbsp; **Safety:** `scope-local`
- Add arms covering the missing variants
- **See also:** [`HARN-MAT-003`](#harn-mat-003), [`HARN-MAT-002`](#harn-mat-002)

#### What it means

A `match` expression is incomplete, ambiguous, or otherwise invalid. Harn
enforces exhaustive matching to keep agent decision logic auditable.

#### How to fix

- Add arms covering the remaining variants, or use a wildcard `_` arm as the explicit fallback.
- Remove duplicate arms — each variant should appear at most once.

### `HARN-MAT-002`

**Category:** `MAT` (Match exhaustiveness) &nbsp;·&nbsp; **API stability:** `stable`

match expression contains a duplicate arm

- **Repair:** `match/remove-duplicate-arm` &nbsp;·&nbsp; **Safety:** `behavior-preserving`
- Remove the duplicated match arm
- **See also:** [`HARN-MAT-001`](#harn-mat-001), [`HARN-LNT-037`](#harn-lnt-037)

#### What it means

A `match` expression is incomplete, ambiguous, or otherwise invalid. Harn
enforces exhaustive matching to keep agent decision logic auditable.

#### How to fix

- Add arms covering the remaining variants, or use a wildcard `_` arm as the explicit fallback.
- Remove duplicate arms — each variant should appear at most once.

### `HARN-MAT-003`

**Category:** `MAT` (Match exhaustiveness) &nbsp;·&nbsp; **API stability:** `stable`

match pattern is invalid

#### What it means

A `match` expression is incomplete, ambiguous, or otherwise invalid. Harn
enforces exhaustive matching to keep agent decision logic auditable.

#### How to fix

- Add arms covering the remaining variants, or use a wildcard `_` arm as the explicit fallback.
- Remove duplicate arms — each variant should appear at most once.

### `HARN-POL-001`

**Category:** `POL` (Runtime policies) &nbsp;·&nbsp; **API stability:** `stable`

pool backpressure rejected a submit

`pool.submit(...)` attempted to enqueue work into a bounded pool whose
`backpressure` policy was already full and configured with
`on_full: "fail_submitter"`.

Increase the queue depth, choose a different `on_full` policy, wait for
existing task handles to drain, or catch the error and retry from your
own orchestration policy.

### `HARN-POL-002`

**Category:** `POL` (Runtime policies) &nbsp;·&nbsp; **API stability:** `stable`

fail-fast pool has no immediate capacity

`pool.submit(...)` targeted a pool configured with
`Backpressure().fail_fast`, but all worker slots were already occupied.
Fail-fast pools do not queue work.

Catch the error if rejection is expected, raise `max_concurrent`, or use
`Backpressure().queue(...)` when callers should wait, drop, or retain a
bounded queue instead.

### `HARN-MET-001`

**Category:** `MET` (Compile-time meta restrictions) &nbsp;·&nbsp; **API stability:** `stable`

expression is not permitted in a const initializer

#### What it means

A `const` binding's right-hand side must be a pure expression that can be
folded at compile time by the bounded const-evaluator. The expression
referenced something the evaluator does not permit: a call into a non-const
function, a property access on a host object such as `harness`, a runtime
construct (spawn / parallel / select / try / yield / emit / await), a
loop, an assignment, or any builtin outside the curated const-friendly
allowlist.

Rejected:

```harn,ignore
const Z = harness.clock.now()              // host capability
const W = spawn { 1 }                      // runtime construct
const Q = some_user_fn()                   // user function call
```

Accepted:

```harn
const X: int = 5 + 3
const Y: string = format("hello-{}", X)
const NS: list = [1, 2, X]
const COUNT: int = len([1, 2, 3])

// reads to silence the unused-variable lint in the example
const _ = [X, Y, NS, COUNT]
```

#### How to fix

- Move the side-effecting computation into a regular `let` binding inside a
  pipeline or function body.
- If the value really is a compile-time constant, restructure it as
  arithmetic, string concatenation, literal collections, or reads of
  earlier `const` bindings.

#### Stability

Adding pure builtins to the const-eval allowlist is backwards-compatible —
newly permitted expressions stop being rejected.

### `HARN-CST-001`

**Category:** `CST` (Const-eval sandbox) &nbsp;·&nbsp; **API stability:** `stable`

const initializer exceeded the step budget

#### What it means

The bounded compile-time evaluator counts every reduction step it
performs while folding a `const` initializer. When the running count
exceeds `MAX_STEPS` (default 100,000), evaluation is aborted with this
diagnostic. The cap is enforced on every step, not amortized, so a
hostile or accidental quadratic expression cannot stall the compiler.

```harn,ignore
// Rejected (would expand far beyond the step budget):
const HUGE = sum_to(1000000)
```

#### How to fix

- Pre-compute the value off-line and embed a literal.
- Reduce the expression to a smaller closed form.
- If the work genuinely belongs at runtime, switch from `const` to `let`.

#### Stability

The default budget may grow over time; tightening it would require a
deprecation cycle.

### `HARN-CST-002`

**Category:** `CST` (Const-eval sandbox) &nbsp;·&nbsp; **API stability:** `stable`

const initializer exceeded the recursion depth budget

#### What it means

The compile-time evaluator tracks how deeply it has recursed into nested
expressions or conditionals. Exceeding `MAX_DEPTH` (default 256) aborts
evaluation with this diagnostic. The cap protects the compiler thread's
stack and prevents pathological deeply-nested literals from creating an
unbounded stack frame chain.

#### How to fix

- Flatten deeply-nested literals (e.g. a 1,000-level nested ternary).
- Break the constant into intermediate `const` bindings.

#### Stability

The default budget may grow over time; tightening it would require a
deprecation cycle.

### `HARN-CST-003`

**Category:** `CST` (Const-eval sandbox) &nbsp;·&nbsp; **API stability:** `stable`

const initializer attempted a sandboxed capability

#### What it means

The compile-time evaluator denies any expression that would touch the
filesystem, network, environment, the current process, host bindings
(`harness.*`), the orchestration runtime, or any other ambient side
effect. Even a syntactically reachable call into one of those surfaces
is rejected before evaluation runs — the const-eval sandbox refuses to
mediate I/O or non-determinism.

```harn,ignore
// Rejected:
const X = harness.fs.read_text("/etc/passwd")
const Y = harness.env.get("HOME")
const Z = spawn { ... }
const W = harness.clock.now()
```

#### How to fix

- Move the impure expression into a `let` binding inside a pipeline.
- Replace I/O with a literal value or with a pure transformation of
  already-folded constants.

#### Stability

The denylist is enforced by allowlist (only listed pure builtins are
accepted), so newly added stdlib functions stay sandboxed by default.

### `HARN-CST-004`

**Category:** `CST` (Const-eval sandbox) &nbsp;·&nbsp; **API stability:** `stable`

const initializer raised a runtime error during evaluation

#### What it means

The expression was syntactically eligible for compile-time evaluation,
but the evaluator hit a value-level error during folding: integer
overflow on a literal arithmetic, division by zero, indexing past the
end of a literal list, an undefined identifier the const-eval
environment cannot resolve, or a type mismatch on a binary operator.

```harn,ignore
// Rejected at compile time:
const ZERO = 1 / 0
const OOB = [1, 2, 3][9]
const BAD = "a" + 1
```

#### How to fix

- Inspect the offending operand and supply a valid value.
- If the expression depends on a value that is only known at runtime,
  use `let` instead of `const`.

### `HARN-CMP-001`

**Category:** `CMP` (Bytecode compilation) &nbsp;·&nbsp; **API stability:** `stable`

the program failed to compile to bytecode

The program parsed and type-checked, but the bytecode compiler rejected it.
These are structural or codegen errors the type checker does not model yet —
for example an unsupported nested list/dict pattern in a `match` arm, a `break`
or `continue` outside a loop, `try*` outside a function, or a malformed string
interpolation hole.

`harn check` runs this compilation pass (discarding the bytecode) so that any
error which would stop `harn run` is reported up front, rather than only when
the program is executed.

#### How to fix

- Read the message: it names the specific construct the compiler could not
  lower and usually how to rewrite it.
- For nested `match` patterns, bind the element with an identifier and match it
  in a nested `match`.
- For `break`/`continue`, ensure they appear inside a loop.

---

## Read next

- [Error handling](https://harnlang.com/error-handling.md)
- [Reading shape diagnostics](https://harnlang.com/reading-shape-diagnostics.md)
