1use std::{fmt, str::FromStr};
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
24pub enum Category {
25 Typ,
26 Par,
27 Nam,
28 Cap,
29 Llm,
30 Orc,
31 Std,
32 Prm,
33 Mod,
34 Rmd,
35 Sus,
36 Lnt,
37 Fmt,
38 Imp,
39 Own,
40 Rcv,
41 Mat,
42 Pol,
43 Met,
45 Cst,
47 Cmp,
50}
51
52impl Category {
53 pub const ALL: &'static [Category] = &[
54 Category::Typ,
55 Category::Par,
56 Category::Nam,
57 Category::Cap,
58 Category::Llm,
59 Category::Orc,
60 Category::Std,
61 Category::Prm,
62 Category::Mod,
63 Category::Rmd,
64 Category::Sus,
65 Category::Lnt,
66 Category::Fmt,
67 Category::Imp,
68 Category::Own,
69 Category::Rcv,
70 Category::Mat,
71 Category::Pol,
72 Category::Met,
73 Category::Cst,
74 Category::Cmp,
75 ];
76
77 pub const fn as_str(self) -> &'static str {
78 match self {
79 Category::Typ => "TYP",
80 Category::Par => "PAR",
81 Category::Nam => "NAM",
82 Category::Cap => "CAP",
83 Category::Llm => "LLM",
84 Category::Orc => "ORC",
85 Category::Std => "STD",
86 Category::Prm => "PRM",
87 Category::Mod => "MOD",
88 Category::Rmd => "RMD",
89 Category::Sus => "SUS",
90 Category::Lnt => "LNT",
91 Category::Fmt => "FMT",
92 Category::Imp => "IMP",
93 Category::Own => "OWN",
94 Category::Rcv => "RCV",
95 Category::Mat => "MAT",
96 Category::Pol => "POL",
97 Category::Met => "MET",
98 Category::Cst => "CST",
99 Category::Cmp => "CMP",
100 }
101 }
102}
103
104impl fmt::Display for Category {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 f.write_str(self.as_str())
107 }
108}
109
110#[derive(Debug, Clone, Copy, PartialEq, Eq)]
112pub struct RegistryEntry {
113 pub code: Code,
114 pub identifier: &'static str,
115 pub category: Category,
116 pub summary: &'static str,
117}
118
119macro_rules! diagnostic_codes {
120 ($($variant:ident, $identifier:literal, $category:ident, $summary:literal;)*) => {
121 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
123 pub enum Code {
124 $($variant,)*
125 }
126
127 impl Code {
128 pub const ALL: &'static [Code] = &[
129 $(Code::$variant,)*
130 ];
131
132 pub const fn as_str(self) -> &'static str {
133 match self {
134 $(Code::$variant => $identifier,)*
135 }
136 }
137
138 pub const fn category(self) -> Category {
139 match self {
140 $(Code::$variant => Category::$category,)*
141 }
142 }
143
144 pub const fn summary(self) -> &'static str {
145 match self {
146 $(Code::$variant => $summary,)*
147 }
148 }
149
150 pub const fn explanation(self) -> &'static str {
152 match self {
153 $(Code::$variant => include_str!(
154 concat!("diagnostic_codes/explanations/", $identifier, ".md")
155 ),)*
156 }
157 }
158 }
159
160 pub const REGISTRY: &[RegistryEntry] = &[
161 $(RegistryEntry {
162 code: Code::$variant,
163 identifier: $identifier,
164 category: Category::$category,
165 summary: $summary,
166 },)*
167 ];
168 };
169}
170
171diagnostic_codes! {
172 TypeMismatch, "HARN-TYP-001", Typ, "expected and actual types are incompatible";
173 InvalidBinaryOperator, "HARN-TYP-002", Typ, "binary operator is not defined for the operand types";
174 StringInterpolationRewrite, "HARN-TYP-003", Typ, "string concatenation should be rewritten as interpolation";
175 ReturnTypeMismatch, "HARN-TYP-004", Typ, "returned expression does not match the declared return type";
176 AssignmentTypeMismatch, "HARN-TYP-005", Typ, "assigned value does not match the target type";
177 ArgumentTypeMismatch, "HARN-TYP-006", Typ, "argument value does not match the parameter type";
178 VariableTypeMismatch, "HARN-TYP-007", Typ, "initializer does not match the declared variable type";
179 ClosureReturnTypeMismatch, "HARN-TYP-008", Typ, "closure return expression does not match its declared type";
180 FieldTypeMismatch, "HARN-TYP-009", Typ, "field value does not match its declared type";
181 MethodTypeMismatch, "HARN-TYP-010", Typ, "method receiver or result type is incompatible";
182 GenericTypeArgumentUnsupported, "HARN-TYP-011", Typ, "callable does not accept type arguments";
183 GenericTypeArgumentMismatch, "HARN-TYP-012", Typ, "type argument does not satisfy the generic parameter";
184 GenericTypeArgumentArity, "HARN-TYP-013", Typ, "generic call has the wrong number of type arguments";
185 TypeParameterArity, "HARN-TYP-014", Typ, "declaration has the wrong number of type parameters";
186 WhereConstraintMismatch, "HARN-TYP-015", Typ, "type argument does not satisfy a where-clause constraint";
187 IterableExpected, "HARN-TYP-016", Typ, "expression must be iterable";
188 InvalidIndexType, "HARN-TYP-017", Typ, "subscript index type is invalid";
189 CallableExpected, "HARN-TYP-018", Typ, "expression must be callable";
190 InvalidCast, "HARN-TYP-019", Typ, "cast cannot be proven valid";
191 UnknownTypeName, "HARN-TYP-020", Typ, "type name cannot be resolved";
192 InvalidVariantUse, "HARN-TYP-021", Typ, "variant type is used in an invalid position";
193 InvalidStructLiteral, "HARN-TYP-022", Typ, "struct literal is invalid";
194 InvalidEnumConstruct, "HARN-TYP-023", Typ, "enum construction is invalid";
195 InvalidPatternBinding, "HARN-TYP-024", Typ, "pattern binding is invalid for the expected type";
196 InvalidOptionalAccess, "HARN-TYP-025", Typ, "optional access is invalid for the receiver type";
197 ThrowsTypeMismatch, "HARN-TYP-026", Typ, "thrown value type is not covered by the callable's declared throws set";
198 TupleIndexOutOfBounds, "HARN-TYP-027", Typ, "constant tuple index is outside the fixed arity";
199 ParserUnexpectedToken, "HARN-PAR-001", Par, "parser found an unexpected token";
200 ParserUnexpectedEof, "HARN-PAR-002", Par, "parser reached end of file while expecting syntax";
201 ParserUnexpectedCharacter, "HARN-PAR-003", Par, "lexer found an unexpected character";
202 ParserUnterminatedString, "HARN-PAR-004", Par, "string literal is unterminated";
203 ParserUnterminatedBlockComment, "HARN-PAR-005", Par, "block comment is unterminated";
204 ParserIntegerLiteralOutOfRange, "HARN-PAR-006", Par, "integer literal is out of range for int (i64)";
205 CompilerError, "HARN-CMP-001", Cmp, "the program failed to compile to bytecode";
206 UndefinedVariable, "HARN-NAM-001", Nam, "variable name cannot be resolved";
207 UndefinedFunction, "HARN-NAM-002", Nam, "function name cannot be resolved";
208 UnknownAttribute, "HARN-NAM-003", Nam, "attribute name is not recognized";
209 UnknownField, "HARN-NAM-004", Nam, "field name does not exist on the target type";
210 UnknownMethod, "HARN-NAM-005", Nam, "method name does not exist on the receiver type";
211 DuplicateArgument, "HARN-NAM-006", Nam, "argument name is duplicated";
212 UnknownOption, "HARN-NAM-007", Nam, "option key is not recognized";
213 UnknownBuiltin, "HARN-NAM-008", Nam, "builtin name cannot be resolved";
214 DeprecatedFunction, "HARN-NAM-009", Nam, "function call targets a deprecated declaration";
215 UnknownDeclaration, "HARN-NAM-010", Nam, "declaration reference cannot be resolved";
216 InvalidAttributeTarget, "HARN-NAM-011", Nam, "attribute is attached to an unsupported declaration";
217 InvalidAttributeArgument, "HARN-NAM-012", Nam, "attribute argument is invalid";
218 InvalidMainSignature, "HARN-NAM-101", Nam, "`fn main` must take an explicit `harness: Harness` parameter";
219 CapabilityPayloadInvalid, "HARN-CAP-001", Cap, "capability payload is invalid";
220 HitlMissingApprovalPolicy, "HARN-CAP-002", Cap, "human approval construct is missing policy";
221 HitlInvalidApprovalArgument, "HARN-CAP-003", Cap, "human approval argument is invalid";
222 CapabilityResultUnchecked, "HARN-CAP-004", Cap, "capability result must be checked";
223 CapabilityUnknownOperation, "HARN-CAP-005", Cap, "host capability operation is not declared";
224 CapabilityCallStaticNameRequired, "HARN-CAP-006", Cap, "host capability call must use a static operation name";
225 CapabilityBindingInvalid, "HARN-CAP-007", Cap, "tool host capability binding is invalid";
226 EffectInheritanceViolation, "HARN-CAP-301", Cap, "child agent effect set exceeds the parent's declared effects";
227 UnknownLlmOption, "HARN-LLM-001", Llm, "LLM option key is not recognized";
228 DeprecatedLlmOption, "HARN-LLM-002", Llm, "LLM option key is deprecated";
229 LlmSchemaMissing, "HARN-LLM-003", Llm, "LLM call is missing schema validation";
230 LlmSchemaInvalid, "HARN-LLM-004", Llm, "LLM schema option is invalid";
231 LlmProviderIdentityBranch, "HARN-LLM-005", Llm, "prompt branches on provider identity instead of capability flags";
232 LlmToolFormatCompositionInvalid, "HARN-LLM-006", Llm, "provider, model, and tool format form a known-unsafe composition";
233 OrchestrationArity, "HARN-ORC-001", Orc, "orchestration construct has invalid arity";
234 OrchestrationType, "HARN-ORC-002", Orc, "orchestration construct argument has invalid type";
235 AgentDefinitionInvalid, "HARN-ORC-003", Orc, "agent declaration is invalid";
236 WorkflowDefinitionInvalid, "HARN-ORC-004", Orc, "workflow declaration is invalid";
237 ToolDefinitionInvalid, "HARN-ORC-005", Orc, "tool declaration is invalid";
238 PipelineDefinitionInvalid, "HARN-ORC-006", Orc, "pipeline declaration is invalid";
239 InvalidSelectConstruct, "HARN-ORC-007", Orc, "select construct is invalid";
240 UnreachableCode, "HARN-ORC-008", Orc, "statement cannot be reached";
241 FlowInvariantAttributeInvalid, "HARN-ORC-009", Orc, "Flow invariant attribute set is invalid";
242 ExecutionTargetMissing, "HARN-ORC-010", Orc, "execution target path cannot be found";
243 SelfDeadlockDetected, "HARN-ORC-011", Orc, "a self-deadlock acquire would block forever";
244 WaitForGraphDeadlockDetected, "HARN-ORC-012", Orc, "a wait-for graph cycle would block forever";
245 DeprecatedStdlibSymbol, "HARN-STD-001", Std, "stdlib symbol has been renamed or deprecated";
246 StdlibUsageInvalid, "HARN-STD-002", Std, "stdlib call is invalid";
247 BuiltinArity, "HARN-STD-003", Std, "builtin call has invalid arity";
248 LintMissingStdlibMetadata, "HARN-STD-101", Std, "public stdlib function is missing declared metadata";
249 LintMissingStdlibReturnType, "HARN-STD-102", Std, "public stdlib function is missing an explicit return type";
250 PromptTemplateParse, "HARN-PRM-001", Prm, "prompt template cannot be parsed";
251 PromptVariantExplosion, "HARN-PRM-002", Prm, "prompt template has too many capability-aware branches";
252 PromptInjectionRisk, "HARN-PRM-003", Prm, "prompt construction risks direct injection";
253 PromptProviderIdentityBranch, "HARN-PRM-004", Prm, "prompt template branches on provider identity";
254 PromptToolSurfaceUnknown, "HARN-PRM-005", Prm, "prompt references a tool outside the declared surface";
255 PromptToolSurfaceDeferredReference, "HARN-PRM-006", Prm, "prompt references a deferred tool without tool search";
256 PromptTargetMissing, "HARN-PRM-007", Prm, "prompt or template target cannot be found";
257 ModuleImportUnresolved, "HARN-MOD-001", Mod, "module import cannot be resolved";
258 ModuleImportUnused, "HARN-MOD-002", Mod, "module import is unused";
259 ModuleImportOrder, "HARN-MOD-003", Mod, "module imports are not in canonical order";
260 ModuleExportInvalid, "HARN-MOD-004", Mod, "module export is invalid";
261 ModuleImportCollision, "HARN-MOD-005", Mod, "module imports expose colliding names";
262 ModuleReExportConflict, "HARN-MOD-006", Mod, "module re-exports conflict";
263 ModuleImportCompileFailed, "HARN-MOD-007", Mod, "imported module failed to compile";
264 ReminderUnknownOption, "HARN-RMD-001", Rmd, "reminder lifecycle option key is not recognized";
265 ReminderInvalidShape, "HARN-RMD-002", Rmd, "reminder payload shape is invalid";
266 ReminderUnsupportedUserBlockRoleHint, "HARN-RMD-003", Rmd, "retired provider-specific reminder role-hint diagnostic";
267 ReminderInfiniteDiscardable, "HARN-RMD-004", Rmd, "discardable reminder has no TTL";
268 ReminderUnknownPropagate, "HARN-RMD-005", Rmd, "reminder propagate value is not recognized";
269 ReminderProviderMalformedSpec, "HARN-RMD-006", Rmd, "reminder provider returned a malformed reminder spec";
270 ReminderProviderBloat, "HARN-RMD-007", Rmd, "too many reminder providers are enabled";
271 ReminderUnsupportedHookEvent, "HARN-RMD-008", Rmd, "hook event does not support reminder effects";
272 SuspendWorkerNotRunning, "HARN-SUS-001", Sus, "suspend_agent target worker is not running";
273 ResumeConditionsInvalid, "HARN-SUS-002", Sus, "ResumeConditions validation failed";
274 ResumeWorkerNotSuspended, "HARN-SUS-003", Sus, "resume_agent target worker is not suspended";
275 ResumeSnapshotInvalid, "HARN-SUS-004", Sus, "resume snapshot cannot be loaded or used";
276 AwaitResumptionOutsideAgentLoop, "HARN-SUS-005", Sus, "agent_await_resumption was invoked outside agent_loop structural handling";
277 ConcurrentResumeConflict, "HARN-SUS-006", Sus, "concurrent resume changed the worker before resume could complete";
278 ResumeTriggerRegistrationFailed, "HARN-SUS-007", Sus, "ResumeConditions trigger could not be registered";
279 ResumeTimeoutUnsupported, "HARN-SUS-008", Sus, "resume timeout action is unsupported";
280 ResumeInputInvalid, "HARN-SUS-009", Sus, "resume input failed agent_loop input validation";
281 ResumeWorkerClosed, "HARN-SUS-010", Sus, "closed suspended worker cannot be resumed";
282 ReplayResumeInputHashMismatch, "HARN-SUS-011", Sus, "replay resume input hash diverges from journaled suspension";
283 ReplayDrainDecisionPromptHashMismatch, "HARN-SUS-012", Sus, "replay drain decision prompt hash diverges from journaled receipt";
284 LifecycleSignatureMismatch, "HARN-SUS-013", Sus, "lifecycle receipt signed timestamp failed verification";
285 LintRenamedStdlibSymbol, "HARN-LNT-001", Lnt, "renamed stdlib symbol lint";
286 LintCyclomaticComplexity, "HARN-LNT-002", Lnt, "cyclomatic complexity lint";
287 LintNamingConvention, "HARN-LNT-003", Lnt, "naming convention lint";
288 LintEagerCollectionConversion, "HARN-LNT-004", Lnt, "eager collection conversion lint";
289 LintRedundantClone, "HARN-LNT-005", Lnt, "redundant clone lint";
290 LintLongRunningWithoutCleanup, "HARN-LNT-006", Lnt, "long-running workflow cleanup lint";
291 LintMcpToolAnnotations, "HARN-LNT-007", Lnt, "MCP tool annotations lint";
292 LintPrOpenWithoutSecretScan, "HARN-LNT-008", Lnt, "PR open without secret scan lint";
293 LintShadowVariable, "HARN-LNT-009", Lnt, "shadow variable lint";
294 LintPersonaHookTarget, "HARN-LNT-010", Lnt, "persona hook target lint";
295 LintDeadCodeAfterReturn, "HARN-LNT-011", Lnt, "dead code after return lint";
296 LintLetThenReturn, "HARN-LNT-012", Lnt, "let then return lint";
297 LintUnhandledApprovalResult, "HARN-LNT-013", Lnt, "unhandled approval result lint";
298 LintUnusedVariable, "HARN-LNT-014", Lnt, "unused variable lint";
299 LintUnusedPatternBinding, "HARN-LNT-015", Lnt, "unused pattern binding lint";
300 LintUnusedParameter, "HARN-LNT-016", Lnt, "unused parameter lint";
301 LintUnusedImport, "HARN-LNT-017", Lnt, "unused import lint";
302 LintMutableNeverReassigned, "HARN-LNT-018", Lnt, "mutable never reassigned lint";
303 LintUnusedFunction, "HARN-LNT-019", Lnt, "unused function lint";
304 LintUnusedType, "HARN-LNT-020", Lnt, "unused type lint";
305 LintPersonaBodyMustCallSteps, "HARN-LNT-021", Lnt, "persona body must call steps lint";
306 LintUndefinedFunction, "HARN-LNT-022", Lnt, "undefined function lint";
307 LintPipelineReturnType, "HARN-LNT-023", Lnt, "pipeline return type lint";
308 LintMissingHarndoc, "HARN-LNT-024", Lnt, "missing harndoc lint";
309 LintAssertOutsideTest, "HARN-LNT-025", Lnt, "assert outside test lint";
310 LintPromptInjectionRisk, "HARN-LNT-026", Lnt, "prompt injection risk lint";
311 LintConnectorEffectPolicy, "HARN-LNT-027", Lnt, "connector effect policy lint";
312 LintUnnecessaryCast, "HARN-LNT-028", Lnt, "unnecessary cast lint";
313 LintUntypedDictAccess, "HARN-LNT-029", Lnt, "untyped dict access lint";
314 LintConstantLogicalOperand, "HARN-LNT-030", Lnt, "constant logical operand lint";
315 LintPointlessComparison, "HARN-LNT-031", Lnt, "pointless comparison lint";
316 LintComparisonToBool, "HARN-LNT-032", Lnt, "comparison to bool lint";
317 LintInvalidBinaryOpLiteral, "HARN-LNT-033", Lnt, "invalid binary operator literal lint";
318 LintRedundantNilTernary, "HARN-LNT-034", Lnt, "redundant nil ternary lint";
319 LintEmptyBlock, "HARN-LNT-035", Lnt, "empty block lint";
320 LintUnnecessaryElseReturn, "HARN-LNT-036", Lnt, "unnecessary else return lint";
321 LintDuplicateMatchArm, "HARN-LNT-037", Lnt, "duplicate match arm lint";
322 LintRequireInTest, "HARN-LNT-038", Lnt, "require in test lint";
323 LintBreakOutsideLoop, "HARN-LNT-039", Lnt, "break outside loop lint";
324 LintTemplateParse, "HARN-LNT-040", Lnt, "template parse lint";
325 LintBlankLineBetweenItems, "HARN-LNT-041", Lnt, "blank line between items lint";
326 LintTrailingComma, "HARN-LNT-042", Lnt, "trailing comma lint";
327 LintUnnecessaryParentheses, "HARN-LNT-043", Lnt, "unnecessary parentheses lint";
328 LintTemplateVariantExplosion, "HARN-LNT-044", Lnt, "template variant explosion lint";
329 LintRequireFileHeader, "HARN-LNT-045", Lnt, "require file header lint";
330 LintTemplateProviderIdentityBranch, "HARN-LNT-046", Lnt, "template provider identity branch lint";
331 LintImportOrder, "HARN-LNT-047", Lnt, "import order lint";
332 LintPreferOptionalShorthand, "HARN-LNT-048", Lnt, "prefer optional shorthand lint";
333 LintLegacyDocComment, "HARN-LNT-049", Lnt, "legacy doc comment lint";
334 LintDeprecatedLlmOptions, "HARN-LNT-050", Lnt, "deprecated LLM options lint";
335 LintUnnecessarySafeNavigation, "HARN-LNT-051", Lnt, "unnecessary safe navigation lint";
336 LintAmbientClockBuiltin, "HARN-LNT-052", Lnt, "ambient clock builtin replaced by `harness.clock.*`";
337 LintAmbientStdioBuiltin, "HARN-LNT-053", Lnt, "ambient stdio builtin replaced by `harness.stdio.*`";
338 LintAmbientFsBuiltin, "HARN-LNT-054", Lnt, "ambient fs builtin replaced by `harness.fs.*`";
339 LintAmbientEnvBuiltin, "HARN-LNT-055", Lnt, "ambient env builtin replaced by `harness.env.*`";
340 LintAmbientRandomBuiltin, "HARN-LNT-056", Lnt, "ambient random builtin replaced by `harness.random.*`";
341 LintAmbientNetBuiltin, "HARN-LNT-057", Lnt, "ambient net builtin replaced by `harness.net.*`";
342 LintVacuousCondition, "HARN-LNT-058", Lnt, "if / while / guard condition is statically known to always succeed or always fail";
343 LintRuleEngine, "HARN-LNT-059", Lnt, "project rule-engine or native lint rule";
344 LintUnnormalizedOptions, "HARN-LNT-060", Lnt, "inline options dict bypasses the typed option constructors";
345 LintNilCoalesceNoop, "HARN-LNT-061", Lnt, "nil coalesce fallback is nil";
346 LintNilCoalesceUnreachableFallback, "HARN-LNT-062", Lnt, "nil coalesce fallback is unreachable";
347 LintUnnecessaryNonNullAssert, "HARN-LNT-063", Lnt, "non-null assertion `!` on an already-non-nil value";
348 LintMutableCaptureAcrossParallel, "HARN-LNT-064", Lnt, "a mutable variable captured from an enclosing scope is reassigned inside a `parallel`/`spawn` body, so concurrent branches share one cell and race";
349 LintNilCoalesceSelfFallback, "HARN-LNT-065", Lnt, "nil coalesce fallback repeats the left identifier";
350 LintDiscardedPureResult, "HARN-LNT-066", Lnt, "the result of a pure collection method is discarded, so the call has no effect on the receiver";
351 LintMissingPublicApiType, "HARN-LNT-067", Lnt, "public callable parameter or return is missing an explicit type";
352 LintTemplateUnknownFilter, "HARN-LNT-068", Lnt, "prompt template names a filter the engine does not implement";
353 LintBroadHarnessParameter, "HARN-LNT-069", Lnt, "helper accepts root Harness but uses only narrow capability handles";
354 LintHomogeneousPositionalApi, "HARN-LNT-070", Lnt, "public API has too many same-typed positional parameters";
355 LintAmbientHarnessMethod, "HARN-LNT-071", Lnt, "global builtin has moved to a Harness capability method";
356 SandboxCapabilityDenied, "HARN-CAP-201", Cap, "harness capability denied by active sandbox profile";
357 FormatterParseFailed, "HARN-FMT-001", Fmt, "formatter could not parse the source";
358 FormatterWouldReformat, "HARN-FMT-002", Fmt, "source is not in canonical format";
359 FormatterTrailingComma, "HARN-FMT-003", Fmt, "formatter normalized trailing comma layout";
360 ImportResolutionFailed, "HARN-IMP-001", Imp, "import target cannot be resolved";
361 ImportSymbolMissing, "HARN-IMP-002", Imp, "imported symbol does not exist";
362 ImportCycle, "HARN-IMP-003", Imp, "import graph contains a cycle";
363 ImmutableAssignment, "HARN-OWN-001", Own, "immutable binding is reassigned";
364 MutableNeverReassigned, "HARN-OWN-002", Own, "mutable binding is never reassigned";
365 OwnershipEscape, "HARN-OWN-003", Own, "owned value escapes its valid scope";
366 BoundaryValueUnvalidated, "HARN-OWN-004", Own, "unvalidated boundary value is used directly";
367 RescueOutsideFunction, "HARN-RCV-001", Rcv, "rescue construct is outside a function body";
368 TryOutsideFunction, "HARN-RCV-002", Rcv, "try construct is outside a function body";
369 InvalidRescueConstruct, "HARN-RCV-003", Rcv, "rescue construct is invalid";
370 NonExhaustiveMatch, "HARN-MAT-001", Mat, "match expression is not exhaustive";
371 DuplicateMatchArm, "HARN-MAT-002", Mat, "match expression contains a duplicate arm";
372 InvalidMatchPattern, "HARN-MAT-003", Mat, "match pattern is invalid";
373 PoolBackpressureFull, "HARN-POL-001", Pol, "pool backpressure rejected a submit";
374 PoolFailFastFull, "HARN-POL-002", Pol, "fail-fast pool has no immediate capacity";
375 ConstEvalDisallowedExpression, "HARN-MET-001", Met, "expression is not permitted in a const initializer";
376 ConstEvalStepLimit, "HARN-CST-001", Cst, "const initializer exceeded the step budget";
377 ConstEvalRecursionLimit, "HARN-CST-002", Cst, "const initializer exceeded the recursion depth budget";
378 ConstEvalSandboxViolation, "HARN-CST-003", Cst, "const initializer attempted a sandboxed capability";
379 ConstEvalRuntimeError, "HARN-CST-004", Cst, "const initializer raised a runtime error during evaluation";
380}
381impl Code {
382 pub const fn registry() -> &'static [RegistryEntry] {
383 REGISTRY
384 }
385
386 pub const fn related(self) -> &'static [Code] {
391 match self {
392 Code::TypeMismatch => &[
395 Code::AssignmentTypeMismatch,
396 Code::ArgumentTypeMismatch,
397 Code::ReturnTypeMismatch,
398 Code::VariableTypeMismatch,
399 Code::FieldTypeMismatch,
400 ],
401 Code::AssignmentTypeMismatch => &[Code::TypeMismatch, Code::VariableTypeMismatch],
402 Code::ArgumentTypeMismatch => &[Code::TypeMismatch, Code::GenericTypeArgumentMismatch],
403 Code::ReturnTypeMismatch => &[Code::TypeMismatch, Code::ClosureReturnTypeMismatch],
404 Code::VariableTypeMismatch => &[Code::TypeMismatch, Code::AssignmentTypeMismatch],
405 Code::ClosureReturnTypeMismatch => &[Code::ReturnTypeMismatch],
406 Code::FieldTypeMismatch => &[Code::TypeMismatch, Code::InvalidStructLiteral],
407 Code::MethodTypeMismatch => &[Code::TypeMismatch, Code::CallableExpected],
408 Code::GenericTypeArgumentUnsupported => &[
410 Code::GenericTypeArgumentMismatch,
411 Code::GenericTypeArgumentArity,
412 ],
413 Code::GenericTypeArgumentMismatch => &[
414 Code::GenericTypeArgumentArity,
415 Code::WhereConstraintMismatch,
416 ],
417 Code::GenericTypeArgumentArity => {
418 &[Code::GenericTypeArgumentMismatch, Code::TypeParameterArity]
419 }
420 Code::TypeParameterArity => &[Code::GenericTypeArgumentArity],
421 Code::WhereConstraintMismatch => &[Code::GenericTypeArgumentMismatch],
422 Code::UndefinedVariable => &[Code::UndefinedFunction, Code::UnknownDeclaration],
424 Code::UndefinedFunction => &[Code::UnknownBuiltin, Code::UnknownDeclaration],
425 Code::UnknownField => &[Code::UnknownMethod, Code::InvalidStructLiteral],
426 Code::UnknownMethod => &[Code::UnknownField, Code::CallableExpected],
427 Code::UnknownAttribute => {
428 &[Code::InvalidAttributeArgument, Code::InvalidAttributeTarget]
429 }
430 Code::InvalidAttributeArgument => {
431 &[Code::UnknownAttribute, Code::InvalidAttributeTarget]
432 }
433 Code::InvalidAttributeTarget => {
434 &[Code::UnknownAttribute, Code::InvalidAttributeArgument]
435 }
436 Code::LlmSchemaMissing => &[Code::LlmSchemaInvalid, Code::UnknownLlmOption],
438 Code::LlmSchemaInvalid => &[Code::LlmSchemaMissing, Code::UnknownLlmOption],
439 Code::UnknownLlmOption => &[Code::DeprecatedLlmOption, Code::LlmSchemaInvalid],
440 Code::DeprecatedLlmOption => &[Code::UnknownLlmOption],
441 Code::LlmProviderIdentityBranch => &[Code::PromptProviderIdentityBranch],
442 Code::PromptTemplateParse => &[Code::PromptTargetMissing],
444 Code::PromptInjectionRisk => &[Code::LintPromptInjectionRisk],
445 Code::PromptProviderIdentityBranch => &[
446 Code::LlmProviderIdentityBranch,
447 Code::LintTemplateProviderIdentityBranch,
448 ],
449 Code::PromptVariantExplosion => &[Code::LintTemplateVariantExplosion],
450 Code::CapabilityResultUnchecked => {
452 &[Code::RescueOutsideFunction, Code::TryOutsideFunction]
453 }
454 Code::CapabilityUnknownOperation => &[Code::CapabilityCallStaticNameRequired],
455 Code::EffectInheritanceViolation => &[
456 Code::CapabilityPayloadInvalid,
457 Code::CapabilityBindingInvalid,
458 ],
459 Code::RescueOutsideFunction => {
461 &[Code::TryOutsideFunction, Code::InvalidRescueConstruct]
462 }
463 Code::TryOutsideFunction => &[Code::RescueOutsideFunction],
464 Code::NonExhaustiveMatch => &[Code::InvalidMatchPattern, Code::DuplicateMatchArm],
465 Code::DuplicateMatchArm => &[Code::NonExhaustiveMatch, Code::LintDuplicateMatchArm],
466 Code::ModuleImportUnresolved => {
468 &[Code::ImportResolutionFailed, Code::ImportSymbolMissing]
469 }
470 Code::ModuleImportUnused => &[Code::LintUnusedImport],
471 Code::ImportResolutionFailed => {
472 &[Code::ModuleImportUnresolved, Code::ImportSymbolMissing]
473 }
474 Code::ImportCycle => &[Code::ImportResolutionFailed],
475 Code::SuspendWorkerNotRunning => {
477 &[Code::ResumeWorkerNotSuspended, Code::ResumeWorkerClosed]
478 }
479 Code::ResumeConditionsInvalid => &[
480 Code::ResumeTriggerRegistrationFailed,
481 Code::ResumeTimeoutUnsupported,
482 ],
483 Code::ResumeWorkerNotSuspended => &[
484 Code::SuspendWorkerNotRunning,
485 Code::ConcurrentResumeConflict,
486 ],
487 Code::ResumeSnapshotInvalid => &[Code::ResumeWorkerNotSuspended],
488 Code::AwaitResumptionOutsideAgentLoop => &[Code::ResumeConditionsInvalid],
489 Code::ConcurrentResumeConflict => {
490 &[Code::ResumeWorkerNotSuspended, Code::ResumeWorkerClosed]
491 }
492 Code::ResumeTriggerRegistrationFailed => &[
493 Code::ResumeConditionsInvalid,
494 Code::ResumeTimeoutUnsupported,
495 ],
496 Code::ResumeTimeoutUnsupported => &[
497 Code::ResumeConditionsInvalid,
498 Code::ResumeTriggerRegistrationFailed,
499 ],
500 Code::ResumeInputInvalid => &[Code::ResumeWorkerNotSuspended],
501 Code::ResumeWorkerClosed => &[
502 Code::ResumeWorkerNotSuspended,
503 Code::ConcurrentResumeConflict,
504 ],
505 Code::ReminderUnknownOption => {
509 &[Code::ReminderInvalidShape, Code::ReminderUnknownPropagate]
510 }
511 Code::ReminderInvalidShape => {
512 &[Code::ReminderUnknownOption, Code::ReminderUnknownPropagate]
513 }
514 Code::ReminderUnknownPropagate => {
515 &[Code::ReminderUnknownOption, Code::ReminderInvalidShape]
516 }
517 Code::ReminderProviderMalformedSpec => &[Code::ReminderInvalidShape],
518 Code::ReminderProviderBloat => &[Code::ReminderInfiniteDiscardable],
519 Code::ReminderUnsupportedHookEvent => &[Code::ReminderProviderMalformedSpec],
520 Code::ImmutableAssignment => &[Code::MutableNeverReassigned],
522 Code::MutableNeverReassigned => &[Code::LintMutableNeverReassigned],
523 Code::LintDeprecatedLlmOptions => &[Code::DeprecatedLlmOption, Code::UnknownLlmOption],
525 Code::LintUnnormalizedOptions => {
526 &[Code::LintDeprecatedLlmOptions, Code::LintUntypedDictAccess]
527 }
528 Code::LintPromptInjectionRisk => &[Code::PromptInjectionRisk],
529 Code::LintTemplateVariantExplosion => &[Code::PromptVariantExplosion],
530 Code::LintTemplateProviderIdentityBranch => &[Code::PromptProviderIdentityBranch],
531 Code::LintRenamedStdlibSymbol => &[Code::DeprecatedStdlibSymbol],
532 Code::LintAmbientClockBuiltin
533 | Code::LintAmbientStdioBuiltin
534 | Code::LintAmbientFsBuiltin
535 | Code::LintAmbientEnvBuiltin
536 | Code::LintAmbientRandomBuiltin
537 | Code::LintAmbientNetBuiltin => {
538 &[Code::InvalidMainSignature, Code::LintRenamedStdlibSymbol]
539 }
540 Code::SandboxCapabilityDenied => &[Code::CapabilityPayloadInvalid],
541 Code::LintMutableNeverReassigned => &[Code::MutableNeverReassigned],
542 Code::LintUnusedImport => &[Code::ModuleImportUnused],
543 Code::LintDuplicateMatchArm => &[Code::DuplicateMatchArm],
544 _ => &[],
545 }
546 }
547}
548
549impl fmt::Display for Code {
550 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
551 f.write_str(self.as_str())
552 }
553}
554
555#[derive(Debug, Clone, Copy, PartialEq, Eq)]
557pub struct ParseCodeError;
558
559impl fmt::Display for ParseCodeError {
560 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
561 f.write_str("unknown Harn diagnostic code")
562 }
563}
564
565impl std::error::Error for ParseCodeError {}
566
567impl FromStr for Code {
568 type Err = ParseCodeError;
569
570 fn from_str(value: &str) -> Result<Self, Self::Err> {
571 Code::ALL
572 .iter()
573 .copied()
574 .find(|code| code.as_str() == value)
575 .ok_or(ParseCodeError)
576 }
577}
578
579#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
589pub enum RepairSafety {
590 FormatOnly,
593 BehaviorPreserving,
596 ScopeLocal,
600 SurfaceChanging,
603 CapabilityChanging,
607 NeedsHuman,
611}
612
613impl RepairSafety {
614 pub const ALL: &'static [RepairSafety] = &[
615 RepairSafety::FormatOnly,
616 RepairSafety::BehaviorPreserving,
617 RepairSafety::ScopeLocal,
618 RepairSafety::SurfaceChanging,
619 RepairSafety::CapabilityChanging,
620 RepairSafety::NeedsHuman,
621 ];
622
623 pub const fn as_str(self) -> &'static str {
627 match self {
628 RepairSafety::FormatOnly => "format-only",
629 RepairSafety::BehaviorPreserving => "behavior-preserving",
630 RepairSafety::ScopeLocal => "scope-local",
631 RepairSafety::SurfaceChanging => "surface-changing",
632 RepairSafety::CapabilityChanging => "capability-changing",
633 RepairSafety::NeedsHuman => "needs-human",
634 }
635 }
636
637 pub const fn is_at_most(self, ceiling: RepairSafety) -> bool {
641 (self as u8) <= (ceiling as u8)
642 }
643}
644
645impl fmt::Display for RepairSafety {
646 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
647 f.write_str(self.as_str())
648 }
649}
650
651#[derive(Debug, Clone, Copy, PartialEq, Eq)]
653pub struct ParseRepairSafetyError;
654
655impl fmt::Display for ParseRepairSafetyError {
656 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
657 f.write_str("unknown Harn repair-safety class")
658 }
659}
660
661impl std::error::Error for ParseRepairSafetyError {}
662
663impl FromStr for RepairSafety {
664 type Err = ParseRepairSafetyError;
665
666 fn from_str(value: &str) -> Result<Self, Self::Err> {
667 RepairSafety::ALL
668 .iter()
669 .copied()
670 .find(|safety| safety.as_str() == value)
671 .ok_or(ParseRepairSafetyError)
672 }
673}
674
675#[derive(Debug, Clone, PartialEq, Eq, Hash)]
681pub struct RepairId(std::borrow::Cow<'static, str>);
682
683impl RepairId {
684 pub const fn from_static(s: &'static str) -> Self {
685 RepairId(std::borrow::Cow::Borrowed(s))
686 }
687
688 pub fn from_owned(s: String) -> Self {
689 RepairId(std::borrow::Cow::Owned(s))
690 }
691
692 pub fn as_str(&self) -> &str {
693 &self.0
694 }
695}
696
697impl fmt::Display for RepairId {
698 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
699 f.write_str(&self.0)
700 }
701}
702
703#[derive(Debug, Clone)]
711pub struct Repair {
712 pub id: RepairId,
713 pub summary: String,
714 pub safety: RepairSafety,
715}
716
717impl Repair {
718 pub fn from_template(template: &RepairTemplate) -> Self {
719 Repair {
720 id: RepairId::from_static(template.id),
721 summary: template.summary.to_string(),
722 safety: template.safety,
723 }
724 }
725}
726
727#[derive(Debug, Clone, Copy)]
734pub struct RepairTemplate {
735 pub id: &'static str,
736 pub summary: &'static str,
737 pub safety: RepairSafety,
738}
739
740impl Code {
741 pub const fn repair_template(self) -> Option<&'static RepairTemplate> {
744 match self {
745 Code::TypeMismatch
747 | Code::ReturnTypeMismatch
748 | Code::AssignmentTypeMismatch
749 | Code::ArgumentTypeMismatch
750 | Code::VariableTypeMismatch
751 | Code::ClosureReturnTypeMismatch
752 | Code::FieldTypeMismatch
753 | Code::MethodTypeMismatch
754 | Code::InvalidIndexType => Some(&REPAIR_INSERT_EXPLICIT_CONVERSION),
755 Code::StringInterpolationRewrite => Some(&REPAIR_REWRITE_STRING_INTERPOLATION),
756 Code::UnknownTypeName => Some(&REPAIR_IMPORTS_FIX_PATH),
757 Code::InvalidCast => Some(&REPAIR_CASTS_REMOVE_UNCHECKED),
758
759 Code::UndefinedVariable
761 | Code::UndefinedFunction
762 | Code::UnknownField
763 | Code::UnknownMethod
764 | Code::UnknownBuiltin
765 | Code::UnknownDeclaration => Some(&REPAIR_BINDINGS_RENAME_TO_CLOSEST),
766 Code::InvalidMainSignature => Some(&REPAIR_BINDINGS_THREAD_HARNESS_NEEDS_PARAM),
767 Code::DeprecatedFunction => Some(&REPAIR_STDLIB_MIGRATE_RENAMED),
768 Code::ModuleImportUnresolved | Code::ImportResolutionFailed => {
769 Some(&REPAIR_IMPORTS_FIX_PATH)
770 }
771 Code::ModuleImportUnused => Some(&REPAIR_IMPORTS_REMOVE_UNUSED),
772 Code::ModuleImportOrder => Some(&REPAIR_IMPORTS_REORDER),
773
774 Code::CapabilityResultUnchecked => Some(&REPAIR_ERRORS_CHECK_OR_RESCUE),
776 Code::CapabilityBindingInvalid => Some(&REPAIR_MANUAL_REVIEW_CAPABILITY),
777 Code::EffectInheritanceViolation => Some(&REPAIR_POLICY_NARROW_CHILD_EFFECTS),
778 Code::RescueOutsideFunction | Code::TryOutsideFunction => {
779 Some(&REPAIR_ERRORS_WRAP_IN_FN)
780 }
781
782 Code::DeprecatedLlmOption => Some(&REPAIR_LLM_MIGRATE_DEPRECATED_OPTION),
784 Code::LlmSchemaMissing => Some(&REPAIR_LLM_ADD_SCHEMA),
785 Code::LlmProviderIdentityBranch | Code::PromptProviderIdentityBranch => {
786 Some(&REPAIR_LLM_USE_CAPABILITY_FLAG)
787 }
788 Code::PromptInjectionRisk => Some(&REPAIR_PROMPTS_ESCAPE_INJECTION),
789 Code::PromptToolSurfaceUnknown | Code::PromptToolSurfaceDeferredReference => {
790 Some(&REPAIR_PROMPTS_ADD_TOOL_TO_SURFACE)
791 }
792 Code::PromptVariantExplosion => Some(&REPAIR_MANUAL_NEEDS_HUMAN),
793
794 Code::DeprecatedStdlibSymbol => Some(&REPAIR_STDLIB_MIGRATE_RENAMED),
796 Code::LintMissingStdlibMetadata => Some(&REPAIR_DOC_ADD_STDLIB_METADATA),
797
798 Code::ImmutableAssignment => Some(&REPAIR_BINDINGS_MAKE_MUTABLE),
800 Code::MutableNeverReassigned => Some(&REPAIR_BINDINGS_MAKE_IMMUTABLE),
801
802 Code::NonExhaustiveMatch => Some(&REPAIR_MATCH_ADD_MISSING_ARMS),
804 Code::DuplicateMatchArm => Some(&REPAIR_MATCH_REMOVE_DUPLICATE_ARM),
805
806 Code::UnreachableCode => Some(&REPAIR_DEAD_CODE_REMOVE),
808
809 Code::FormatterWouldReformat | Code::FormatterTrailingComma => {
811 Some(&REPAIR_FORMAT_REFORMAT)
812 }
813
814 Code::LintUnusedVariable
816 | Code::LintUnusedPatternBinding
817 | Code::LintUnusedParameter => Some(&REPAIR_BINDINGS_RENAME_UNUSED),
818 Code::LintUnusedImport => Some(&REPAIR_IMPORTS_REMOVE_UNUSED),
819 Code::LintUnusedFunction | Code::LintUnusedType => {
820 Some(&REPAIR_DECLARATIONS_REMOVE_UNUSED)
821 }
822 Code::LintMutableNeverReassigned => Some(&REPAIR_BINDINGS_MAKE_IMMUTABLE),
823 Code::LintImportOrder => Some(&REPAIR_IMPORTS_REORDER),
824 Code::LintBlankLineBetweenItems
825 | Code::LintTrailingComma
826 | Code::LintUnnecessaryParentheses
827 | Code::LintRequireFileHeader => Some(&REPAIR_FORMAT_REFORMAT),
828 Code::LintLegacyDocComment => Some(&REPAIR_DOC_COMMENT_MIGRATE),
829 Code::LintEmptyBlock => Some(&REPAIR_BLOCK_REMOVE_EMPTY),
830 Code::LintUnnecessaryElseReturn | Code::LintLetThenReturn => {
831 Some(&REPAIR_CONTROL_FLOW_FLATTEN)
832 }
833 Code::LintNilCoalesceNoop
834 | Code::LintNilCoalesceSelfFallback
835 | Code::LintRedundantNilTernary
836 | Code::LintUnnecessarySafeNavigation
837 | Code::LintUnnecessaryNonNullAssert
838 | Code::LintPreferOptionalShorthand
839 | Code::LintComparisonToBool
840 | Code::LintPointlessComparison
841 | Code::LintConstantLogicalOperand => Some(&REPAIR_EXPRESSION_SIMPLIFY),
842 Code::LintUnnecessaryCast => Some(&REPAIR_CASTS_REMOVE_REDUNDANT),
843 Code::LintRedundantClone => Some(&REPAIR_CLONE_REMOVE_REDUNDANT),
844 Code::LintEagerCollectionConversion => Some(&REPAIR_COLLECTION_PREFER_LAZY),
845 Code::LintDeadCodeAfterReturn => Some(&REPAIR_DEAD_CODE_REMOVE),
846 Code::LintRenamedStdlibSymbol => Some(&REPAIR_STDLIB_MIGRATE_RENAMED),
847 Code::LintAmbientClockBuiltin => Some(&REPAIR_BINDINGS_THREAD_HARNESS_CLOCK),
848 Code::LintAmbientFsBuiltin => Some(&REPAIR_BINDINGS_THREAD_HARNESS_FS),
849 Code::LintAmbientEnvBuiltin => Some(&REPAIR_BINDINGS_THREAD_HARNESS_ENV),
850 Code::LintAmbientRandomBuiltin => Some(&REPAIR_BINDINGS_THREAD_HARNESS_RANDOM),
851 Code::LintAmbientNetBuiltin => Some(&REPAIR_BINDINGS_THREAD_HARNESS_NET),
852 Code::LintAmbientStdioBuiltin => Some(&REPAIR_BINDINGS_THREAD_HARNESS),
853 Code::LintAmbientHarnessMethod => Some(&REPAIR_BINDINGS_THREAD_HARNESS_METHOD),
854 Code::LintBroadHarnessParameter => Some(&REPAIR_BINDINGS_ATTENUATE_HARNESS),
855 Code::LintDeprecatedLlmOptions => Some(&REPAIR_LLM_MIGRATE_DEPRECATED_OPTION),
856 Code::LintTemplateProviderIdentityBranch => Some(&REPAIR_LLM_USE_CAPABILITY_FLAG),
857 Code::LintPromptInjectionRisk => Some(&REPAIR_PROMPTS_ESCAPE_INJECTION),
858 Code::LintShadowVariable => Some(&REPAIR_BINDINGS_RENAME_SHADOW),
859 Code::LintNamingConvention => Some(&REPAIR_STYLE_RENAME_TO_CONVENTION),
860 Code::LintUnhandledApprovalResult => Some(&REPAIR_ERRORS_CHECK_OR_RESCUE),
861 Code::LintMissingHarndoc => Some(&REPAIR_DOC_ADD_HARNDOC),
862 Code::LintDuplicateMatchArm => Some(&REPAIR_MATCH_REMOVE_DUPLICATE_ARM),
863 Code::LintUntypedDictAccess => Some(&REPAIR_TYPES_ADD_SHAPE_ANNOTATION),
864 Code::LintUnnormalizedOptions => Some(&REPAIR_TYPES_ADD_SHAPE_ANNOTATION),
865 Code::LintMcpToolAnnotations => Some(&REPAIR_MANUAL_NEEDS_HUMAN),
866 Code::LintTemplateVariantExplosion | Code::LintLongRunningWithoutCleanup => {
867 Some(&REPAIR_MANUAL_NEEDS_HUMAN)
868 }
869
870 _ => None,
874 }
875 }
876}
877
878const REPAIR_INSERT_EXPLICIT_CONVERSION: RepairTemplate = RepairTemplate {
893 id: "casts/insert-explicit-conversion",
894 summary: "Insert an explicit conversion or correct the operand type",
895 safety: RepairSafety::ScopeLocal,
896};
897
898const REPAIR_REWRITE_STRING_INTERPOLATION: RepairTemplate = RepairTemplate {
899 id: "style/string-interpolation",
900 summary: "Rewrite string concatenation as an interpolation literal",
901 safety: RepairSafety::BehaviorPreserving,
902};
903
904const REPAIR_CASTS_REMOVE_UNCHECKED: RepairTemplate = RepairTemplate {
905 id: "casts/remove-unchecked",
906 summary: "Remove the unchecked cast or guard it with a type test",
907 safety: RepairSafety::ScopeLocal,
908};
909
910const REPAIR_CASTS_REMOVE_REDUNDANT: RepairTemplate = RepairTemplate {
911 id: "casts/remove-redundant",
912 summary: "Remove the redundant cast",
913 safety: RepairSafety::BehaviorPreserving,
914};
915
916const REPAIR_BINDINGS_RENAME_TO_CLOSEST: RepairTemplate = RepairTemplate {
917 id: "bindings/rename-to-closest",
918 summary: "Rename to the closest in-scope identifier",
919 safety: RepairSafety::ScopeLocal,
920};
921
922const REPAIR_BINDINGS_MAKE_MUTABLE: RepairTemplate = RepairTemplate {
923 id: "bindings/make-mutable",
924 summary: "Declare the binding with `let` so it can be reassigned",
925 safety: RepairSafety::ScopeLocal,
926};
927
928const REPAIR_BINDINGS_MAKE_IMMUTABLE: RepairTemplate = RepairTemplate {
929 id: "bindings/make-immutable",
930 summary: "Declare the never-reassigned binding with `const` instead of `let`",
931 safety: RepairSafety::BehaviorPreserving,
932};
933
934const REPAIR_BINDINGS_RENAME_UNUSED: RepairTemplate = RepairTemplate {
935 id: "bindings/rename-unused",
936 summary: "Use the `_` discard binding for an unused binding",
937 safety: RepairSafety::BehaviorPreserving,
938};
939
940const REPAIR_BINDINGS_RENAME_SHADOW: RepairTemplate = RepairTemplate {
941 id: "bindings/rename-shadow",
942 summary: "Rename the shadowing binding to a distinct name",
943 safety: RepairSafety::ScopeLocal,
944};
945
946const REPAIR_BINDINGS_THREAD_HARNESS: RepairTemplate = RepairTemplate {
947 id: "bindings/thread-harness",
948 summary: "Thread the existing `harness` binding through local helper calls and replace the ambient stdio builtin with `harness.stdio.*`",
949 safety: RepairSafety::ScopeLocal,
950};
951
952const REPAIR_BINDINGS_THREAD_HARNESS_NEEDS_PARAM: RepairTemplate = RepairTemplate {
953 id: "bindings/thread-harness-needs-param",
954 summary: "Add a `harness: Harness` parameter where the stdio capability handle is required and update local callers",
955 safety: RepairSafety::SurfaceChanging,
956};
957
958const REPAIR_BINDINGS_THREAD_HARNESS_METHOD: RepairTemplate = RepairTemplate {
959 id: "bindings/thread-harness-method",
960 summary: "Replace the ambient runtime builtin with its typed `harness.*` method and thread authority through local callers",
961 safety: RepairSafety::ScopeLocal,
962};
963
964const REPAIR_BINDINGS_THREAD_HARNESS_CLOCK: RepairTemplate = RepairTemplate {
965 id: "bindings/thread-harness-clock",
966 summary: "Replace the ambient clock builtin with the corresponding `harness.clock.*` method",
967 safety: RepairSafety::ScopeLocal,
968};
969
970const REPAIR_BINDINGS_THREAD_HARNESS_FS: RepairTemplate = RepairTemplate {
971 id: "bindings/thread-harness-fs",
972 summary: "Replace the ambient fs builtin with the corresponding `harness.fs.*` method",
973 safety: RepairSafety::ScopeLocal,
974};
975
976const REPAIR_BINDINGS_THREAD_HARNESS_ENV: RepairTemplate = RepairTemplate {
977 id: "bindings/thread-harness-env",
978 summary: "Replace the ambient env builtin with the corresponding `harness.env.*` method",
979 safety: RepairSafety::ScopeLocal,
980};
981
982const REPAIR_BINDINGS_THREAD_HARNESS_RANDOM: RepairTemplate = RepairTemplate {
983 id: "bindings/thread-harness-random",
984 summary: "Replace the ambient random builtin with the corresponding `harness.random.*` method",
985 safety: RepairSafety::ScopeLocal,
986};
987
988const REPAIR_BINDINGS_THREAD_HARNESS_NET: RepairTemplate = RepairTemplate {
989 id: "bindings/thread-harness-net",
990 summary: "Replace the ambient net builtin with the corresponding `harness.net.*` method",
991 safety: RepairSafety::ScopeLocal,
992};
993
994const REPAIR_DECLARATIONS_REMOVE_UNUSED: RepairTemplate = RepairTemplate {
995 id: "declarations/remove-unused",
996 summary: "Remove the unused declaration",
997 safety: RepairSafety::SurfaceChanging,
998};
999
1000const REPAIR_IMPORTS_FIX_PATH: RepairTemplate = RepairTemplate {
1001 id: "imports/fix-path",
1002 summary: "Replace the import path with a resolvable target",
1003 safety: RepairSafety::ScopeLocal,
1004};
1005
1006const REPAIR_IMPORTS_REMOVE_UNUSED: RepairTemplate = RepairTemplate {
1007 id: "imports/remove-unused",
1008 summary: "Remove the unused import",
1009 safety: RepairSafety::BehaviorPreserving,
1010};
1011
1012const REPAIR_IMPORTS_REORDER: RepairTemplate = RepairTemplate {
1013 id: "imports/reorder",
1014 summary: "Reorder imports into canonical grouping",
1015 safety: RepairSafety::FormatOnly,
1016};
1017
1018const REPAIR_ERRORS_CHECK_OR_RESCUE: RepairTemplate = RepairTemplate {
1019 id: "errors/check-or-rescue",
1020 summary: "Check the result or wrap the call in a `rescue` block",
1021 safety: RepairSafety::ScopeLocal,
1022};
1023
1024const REPAIR_ERRORS_WRAP_IN_FN: RepairTemplate = RepairTemplate {
1025 id: "errors/wrap-in-fn",
1026 summary: "Move the construct inside a function body",
1027 safety: RepairSafety::SurfaceChanging,
1028};
1029
1030const REPAIR_MATCH_ADD_MISSING_ARMS: RepairTemplate = RepairTemplate {
1031 id: "match/add-missing-arms",
1032 summary: "Add arms covering the missing variants",
1033 safety: RepairSafety::ScopeLocal,
1034};
1035
1036const REPAIR_MATCH_REMOVE_DUPLICATE_ARM: RepairTemplate = RepairTemplate {
1037 id: "match/remove-duplicate-arm",
1038 summary: "Remove the duplicated match arm",
1039 safety: RepairSafety::BehaviorPreserving,
1040};
1041
1042const REPAIR_FORMAT_REFORMAT: RepairTemplate = RepairTemplate {
1043 id: "format/reformat",
1044 summary: "Apply canonical formatting",
1045 safety: RepairSafety::FormatOnly,
1046};
1047
1048const REPAIR_DOC_COMMENT_MIGRATE: RepairTemplate = RepairTemplate {
1049 id: "doc/migrate-comment-style",
1050 summary: "Migrate the legacy comment to canonical doc syntax",
1051 safety: RepairSafety::FormatOnly,
1052};
1053
1054const REPAIR_DOC_ADD_HARNDOC: RepairTemplate = RepairTemplate {
1055 id: "doc/add-harndoc",
1056 summary: "Add a `///` doc comment describing this declaration",
1057 safety: RepairSafety::BehaviorPreserving,
1058};
1059
1060const REPAIR_DOC_ADD_STDLIB_METADATA: RepairTemplate = RepairTemplate {
1061 id: "doc/add-stdlib-metadata",
1062 summary: "Add `@effects` and `@errors` fields to the stdlib function's doc block",
1063 safety: RepairSafety::BehaviorPreserving,
1064};
1065
1066const REPAIR_BLOCK_REMOVE_EMPTY: RepairTemplate = RepairTemplate {
1067 id: "blocks/remove-empty",
1068 summary: "Remove the empty block or fill in an explicit body",
1069 safety: RepairSafety::ScopeLocal,
1070};
1071
1072const REPAIR_CONTROL_FLOW_FLATTEN: RepairTemplate = RepairTemplate {
1073 id: "control-flow/flatten",
1074 summary: "Flatten the unnecessary control flow construct",
1075 safety: RepairSafety::BehaviorPreserving,
1076};
1077
1078const REPAIR_EXPRESSION_SIMPLIFY: RepairTemplate = RepairTemplate {
1079 id: "expressions/simplify",
1080 summary: "Simplify the expression to its canonical form",
1081 safety: RepairSafety::BehaviorPreserving,
1082};
1083
1084const REPAIR_CLONE_REMOVE_REDUNDANT: RepairTemplate = RepairTemplate {
1085 id: "clones/remove-redundant",
1086 summary: "Remove the redundant clone",
1087 safety: RepairSafety::BehaviorPreserving,
1088};
1089
1090const REPAIR_COLLECTION_PREFER_LAZY: RepairTemplate = RepairTemplate {
1091 id: "collections/prefer-lazy",
1092 summary: "Replace the eager collection step with a lazy variant",
1093 safety: RepairSafety::ScopeLocal,
1094};
1095
1096const REPAIR_DEAD_CODE_REMOVE: RepairTemplate = RepairTemplate {
1097 id: "control-flow/remove-dead",
1098 summary: "Remove the unreachable code",
1099 safety: RepairSafety::BehaviorPreserving,
1100};
1101
1102const REPAIR_STDLIB_MIGRATE_RENAMED: RepairTemplate = RepairTemplate {
1103 id: "stdlib/migrate-renamed",
1104 summary: "Rename the call to the renamed stdlib symbol",
1105 safety: RepairSafety::ScopeLocal,
1106};
1107
1108const REPAIR_BINDINGS_ATTENUATE_HARNESS: RepairTemplate = RepairTemplate {
1109 id: "bindings/attenuate-harness",
1110 summary: "Replace the root Harness parameter with the single capability the helper uses",
1111 safety: RepairSafety::SurfaceChanging,
1112};
1113
1114const REPAIR_LLM_MIGRATE_DEPRECATED_OPTION: RepairTemplate = RepairTemplate {
1115 id: "llm/migrate-deprecated-option",
1116 summary: "Replace the deprecated option with its supported equivalent",
1117 safety: RepairSafety::ScopeLocal,
1118};
1119
1120const REPAIR_LLM_ADD_SCHEMA: RepairTemplate = RepairTemplate {
1121 id: "llm/add-schema",
1122 summary: "Add a typed output schema to the LLM call",
1123 safety: RepairSafety::SurfaceChanging,
1124};
1125
1126const REPAIR_LLM_USE_CAPABILITY_FLAG: RepairTemplate = RepairTemplate {
1127 id: "llm/use-capability-flag",
1128 summary: "Branch on a capability flag instead of provider identity",
1129 safety: RepairSafety::CapabilityChanging,
1130};
1131
1132const REPAIR_PROMPTS_ESCAPE_INJECTION: RepairTemplate = RepairTemplate {
1133 id: "prompts/escape-injection",
1134 summary: "Pass the untrusted input through a structured placeholder",
1135 safety: RepairSafety::ScopeLocal,
1136};
1137
1138const REPAIR_PROMPTS_ADD_TOOL_TO_SURFACE: RepairTemplate = RepairTemplate {
1139 id: "prompts/add-tool-to-surface",
1140 summary: "Add the referenced tool to the declared tool surface",
1141 safety: RepairSafety::SurfaceChanging,
1142};
1143
1144const REPAIR_STYLE_RENAME_TO_CONVENTION: RepairTemplate = RepairTemplate {
1145 id: "style/rename-to-convention",
1146 summary: "Rename to match the casing convention for this kind",
1147 safety: RepairSafety::SurfaceChanging,
1148};
1149
1150const REPAIR_TYPES_ADD_SHAPE_ANNOTATION: RepairTemplate = RepairTemplate {
1151 id: "types/add-shape-annotation",
1152 summary: "Annotate the dict with a concrete shape type",
1153 safety: RepairSafety::SurfaceChanging,
1154};
1155
1156const REPAIR_MANUAL_REVIEW_CAPABILITY: RepairTemplate = RepairTemplate {
1157 id: "manual/review-capability-binding",
1158 summary: "Review the capability binding; the fix is not mechanical",
1159 safety: RepairSafety::NeedsHuman,
1160};
1161
1162const REPAIR_POLICY_NARROW_CHILD_EFFECTS: RepairTemplate = RepairTemplate {
1163 id: "policy/narrow-child-effects",
1164 summary: "Narrow the child agent's effects to a subset of the parent's, or widen the parent's declared effects",
1165 safety: RepairSafety::SurfaceChanging,
1166};
1167
1168const REPAIR_MANUAL_NEEDS_HUMAN: RepairTemplate = RepairTemplate {
1169 id: "manual/needs-human",
1170 summary: "Plan a human-led change; auto-apply is not safe here",
1171 safety: RepairSafety::NeedsHuman,
1172};
1173
1174pub const REPAIR_REGISTRY: &[&RepairTemplate] = &[
1177 &REPAIR_INSERT_EXPLICIT_CONVERSION,
1178 &REPAIR_REWRITE_STRING_INTERPOLATION,
1179 &REPAIR_CASTS_REMOVE_UNCHECKED,
1180 &REPAIR_CASTS_REMOVE_REDUNDANT,
1181 &REPAIR_BINDINGS_RENAME_TO_CLOSEST,
1182 &REPAIR_BINDINGS_MAKE_MUTABLE,
1183 &REPAIR_BINDINGS_MAKE_IMMUTABLE,
1184 &REPAIR_BINDINGS_RENAME_UNUSED,
1185 &REPAIR_BINDINGS_RENAME_SHADOW,
1186 &REPAIR_BINDINGS_THREAD_HARNESS,
1187 &REPAIR_BINDINGS_THREAD_HARNESS_NEEDS_PARAM,
1188 &REPAIR_BINDINGS_THREAD_HARNESS_METHOD,
1189 &REPAIR_BINDINGS_THREAD_HARNESS_CLOCK,
1190 &REPAIR_BINDINGS_THREAD_HARNESS_FS,
1191 &REPAIR_BINDINGS_THREAD_HARNESS_ENV,
1192 &REPAIR_BINDINGS_THREAD_HARNESS_RANDOM,
1193 &REPAIR_BINDINGS_THREAD_HARNESS_NET,
1194 &REPAIR_DECLARATIONS_REMOVE_UNUSED,
1195 &REPAIR_IMPORTS_FIX_PATH,
1196 &REPAIR_IMPORTS_REMOVE_UNUSED,
1197 &REPAIR_IMPORTS_REORDER,
1198 &REPAIR_ERRORS_CHECK_OR_RESCUE,
1199 &REPAIR_ERRORS_WRAP_IN_FN,
1200 &REPAIR_MATCH_ADD_MISSING_ARMS,
1201 &REPAIR_MATCH_REMOVE_DUPLICATE_ARM,
1202 &REPAIR_FORMAT_REFORMAT,
1203 &REPAIR_DOC_COMMENT_MIGRATE,
1204 &REPAIR_DOC_ADD_HARNDOC,
1205 &REPAIR_DOC_ADD_STDLIB_METADATA,
1206 &REPAIR_BLOCK_REMOVE_EMPTY,
1207 &REPAIR_CONTROL_FLOW_FLATTEN,
1208 &REPAIR_EXPRESSION_SIMPLIFY,
1209 &REPAIR_CLONE_REMOVE_REDUNDANT,
1210 &REPAIR_COLLECTION_PREFER_LAZY,
1211 &REPAIR_DEAD_CODE_REMOVE,
1212 &REPAIR_STDLIB_MIGRATE_RENAMED,
1213 &REPAIR_BINDINGS_ATTENUATE_HARNESS,
1214 &REPAIR_LLM_MIGRATE_DEPRECATED_OPTION,
1215 &REPAIR_LLM_ADD_SCHEMA,
1216 &REPAIR_LLM_USE_CAPABILITY_FLAG,
1217 &REPAIR_PROMPTS_ESCAPE_INJECTION,
1218 &REPAIR_PROMPTS_ADD_TOOL_TO_SURFACE,
1219 &REPAIR_STYLE_RENAME_TO_CONVENTION,
1220 &REPAIR_TYPES_ADD_SHAPE_ANNOTATION,
1221 &REPAIR_MANUAL_REVIEW_CAPABILITY,
1222 &REPAIR_MANUAL_NEEDS_HUMAN,
1223 &REPAIR_POLICY_NARROW_CHILD_EFFECTS,
1224];
1225
1226#[cfg(test)]
1227mod tests {
1228 use super::{Category, Code, ParseRepairSafetyError, RepairSafety, REPAIR_REGISTRY};
1229 use std::collections::HashSet;
1230 use std::str::FromStr;
1231
1232 #[test]
1233 fn parses_registered_code() {
1234 assert_eq!(Code::from_str("HARN-TYP-014"), Ok(Code::TypeParameterArity));
1235 }
1236
1237 #[test]
1238 fn registry_has_unique_identifiers() {
1239 let mut seen = HashSet::new();
1240 for entry in Code::registry() {
1241 assert!(
1242 seen.insert(entry.identifier),
1243 "duplicate diagnostic code {}",
1244 entry.identifier
1245 );
1246 assert_eq!(entry.code.as_str(), entry.identifier);
1247 assert_eq!(entry.code.category(), entry.category);
1248 let expected_prefix = format!("HARN-{}-", entry.category);
1249 assert!(entry.identifier.starts_with(&expected_prefix));
1250 let suffix = entry.identifier.trim_start_matches(&expected_prefix);
1251 assert_eq!(suffix.len(), 3);
1252 assert!(suffix.chars().all(|ch| ch.is_ascii_digit()));
1253 assert!(!entry.summary.is_empty());
1254 }
1255 assert!(Code::registry().len() >= 40);
1256 }
1257
1258 #[test]
1259 fn every_category_is_populated() {
1260 for category in Category::ALL {
1261 assert!(
1262 Code::registry()
1263 .iter()
1264 .any(|entry| entry.category == *category),
1265 "missing diagnostic code category {category}"
1266 );
1267 }
1268 }
1269
1270 #[test]
1271 fn every_code_has_non_empty_explanation() {
1272 for entry in Code::registry() {
1273 let body = entry.code.explanation();
1274 assert!(
1275 !body.trim().is_empty(),
1276 "diagnostic code {} has an empty explanation file",
1277 entry.identifier
1278 );
1279 assert!(
1280 body.contains(entry.identifier),
1281 "explanation for {} should reference its identifier",
1282 entry.identifier
1283 );
1284 }
1285 }
1286
1287 #[test]
1288 fn related_codes_are_registered_and_non_self() {
1289 for entry in Code::registry() {
1290 for &other in entry.code.related() {
1291 assert_ne!(
1292 other, entry.code,
1293 "{} lists itself as a related code",
1294 entry.identifier
1295 );
1296 assert!(
1297 Code::registry().iter().any(|e| e.code == other),
1298 "{} lists unregistered related code {}",
1299 entry.identifier,
1300 other
1301 );
1302 }
1303 }
1304 }
1305
1306 #[test]
1307 fn repair_safety_string_roundtrip() {
1308 for safety in RepairSafety::ALL {
1309 let parsed = RepairSafety::from_str(safety.as_str()).unwrap();
1310 assert_eq!(parsed, *safety);
1311 assert_eq!(parsed.to_string(), safety.as_str());
1312 }
1313 assert_eq!(
1314 RepairSafety::from_str("not-a-safety-class"),
1315 Err(ParseRepairSafetyError)
1316 );
1317 }
1318
1319 #[test]
1320 fn repair_safety_ordering_is_monotonic_low_to_high() {
1321 let order = RepairSafety::ALL;
1325 for window in order.windows(2) {
1326 assert!(
1327 window[0] < window[1],
1328 "{:?} should be safer than {:?}",
1329 window[0],
1330 window[1]
1331 );
1332 assert!(window[0].is_at_most(window[1]));
1333 assert!(!window[1].is_at_most(window[0]));
1334 }
1335 }
1336
1337 #[test]
1338 fn repair_registry_has_at_least_twenty_entries() {
1339 assert!(
1340 REPAIR_REGISTRY.len() >= 20,
1341 "expected ≥20 repair templates, found {}",
1342 REPAIR_REGISTRY.len()
1343 );
1344 }
1345
1346 #[test]
1347 fn repair_ids_are_kebab_case_namespaced_and_unique() {
1348 let mut seen = HashSet::new();
1349 for template in REPAIR_REGISTRY {
1350 assert!(
1351 seen.insert(template.id),
1352 "duplicate repair id {}",
1353 template.id
1354 );
1355 let (namespace, leaf) = template.id.split_once('/').unwrap_or_else(|| {
1356 panic!(
1357 "repair id `{}` is missing `<namespace>/` prefix",
1358 template.id
1359 )
1360 });
1361 assert!(
1362 !namespace.is_empty() && !leaf.is_empty(),
1363 "repair id `{}` has empty namespace or leaf",
1364 template.id
1365 );
1366 for ch in template.id.chars() {
1367 assert!(
1368 ch.is_ascii_lowercase() || ch.is_ascii_digit() || ch == '-' || ch == '/',
1369 "repair id `{}` has non-kebab character {ch:?}",
1370 template.id
1371 );
1372 }
1373 assert!(
1374 !template.summary.is_empty(),
1375 "repair {} has empty summary",
1376 template.id
1377 );
1378 let first = template.summary.chars().next().unwrap();
1380 assert!(
1381 first.is_ascii_uppercase(),
1382 "repair {} summary `{}` should start with a capital",
1383 template.id,
1384 template.summary
1385 );
1386 }
1387 }
1388
1389 #[test]
1390 fn manual_namespace_is_needs_human() {
1391 for template in REPAIR_REGISTRY {
1392 if let Some(("manual", _)) = template.id.split_once('/') {
1393 assert_eq!(
1394 template.safety,
1395 RepairSafety::NeedsHuman,
1396 "manual/* repair {} must be NeedsHuman",
1397 template.id
1398 );
1399 }
1400 }
1401 }
1402
1403 #[test]
1404 fn known_codes_carry_expected_safety_class() {
1405 let expected: &[(Code, RepairSafety, &str)] = &[
1409 (
1410 Code::FormatterWouldReformat,
1411 RepairSafety::FormatOnly,
1412 "format/reformat",
1413 ),
1414 (
1415 Code::ModuleImportUnused,
1416 RepairSafety::BehaviorPreserving,
1417 "imports/remove-unused",
1418 ),
1419 (
1420 Code::ImmutableAssignment,
1421 RepairSafety::ScopeLocal,
1422 "bindings/make-mutable",
1423 ),
1424 (
1425 Code::LintUnusedFunction,
1426 RepairSafety::SurfaceChanging,
1427 "declarations/remove-unused",
1428 ),
1429 (
1430 Code::LlmProviderIdentityBranch,
1431 RepairSafety::CapabilityChanging,
1432 "llm/use-capability-flag",
1433 ),
1434 (
1435 Code::PromptVariantExplosion,
1436 RepairSafety::NeedsHuman,
1437 "manual/needs-human",
1438 ),
1439 (
1440 Code::NonExhaustiveMatch,
1441 RepairSafety::ScopeLocal,
1442 "match/add-missing-arms",
1443 ),
1444 (
1445 Code::LintAmbientClockBuiltin,
1446 RepairSafety::ScopeLocal,
1447 "bindings/thread-harness-clock",
1448 ),
1449 (
1450 Code::LintAmbientStdioBuiltin,
1451 RepairSafety::ScopeLocal,
1452 "bindings/thread-harness",
1453 ),
1454 (
1455 Code::InvalidMainSignature,
1456 RepairSafety::SurfaceChanging,
1457 "bindings/thread-harness-needs-param",
1458 ),
1459 ];
1460 for (code, safety, repair_id) in expected {
1461 let template = code
1462 .repair_template()
1463 .unwrap_or_else(|| panic!("{code} should have a repair template"));
1464 assert_eq!(template.safety, *safety, "{code} safety class drifted");
1465 assert_eq!(template.id, *repair_id, "{code} repair id drifted");
1466 }
1467 }
1468
1469 #[test]
1470 fn repair_templates_cover_at_least_twenty_codes() {
1471 let covered = Code::ALL
1472 .iter()
1473 .filter(|code| code.repair_template().is_some())
1474 .count();
1475 assert!(
1476 covered >= 20,
1477 "expected ≥20 codes with a repair template, found {covered}"
1478 );
1479 }
1480
1481 #[test]
1482 fn every_registered_repair_is_referenced_by_some_code() {
1483 let referenced: HashSet<&'static str> = Code::ALL
1484 .iter()
1485 .filter_map(|code| code.repair_template())
1486 .map(|template| template.id)
1487 .collect();
1488 for template in REPAIR_REGISTRY {
1489 assert!(
1490 referenced.contains(template.id),
1491 "repair {} is in REPAIR_REGISTRY but no Code maps to it",
1492 template.id
1493 );
1494 }
1495 }
1496
1497 #[test]
1498 fn every_referenced_repair_template_is_in_registry() {
1499 let registered: HashSet<&'static str> =
1500 REPAIR_REGISTRY.iter().map(|template| template.id).collect();
1501 for code in Code::ALL {
1502 let Some(template) = code.repair_template() else {
1503 continue;
1504 };
1505 assert!(
1506 registered.contains(template.id),
1507 "repair {} (used by {}) is missing from REPAIR_REGISTRY",
1508 template.id,
1509 code
1510 );
1511 }
1512 }
1513}