# Slovo Diagnostics

This document defines the `1.0.0-beta.13` beta policy for Slovo machine
diagnostics. It documents the existing `slovo.diagnostic` version `1` schema,
the relationship between the S-expression and JSON encodings, the current
golden diagnostic code catalog, and the migration rules for changing machine
diagnostic fields or codes.

This is a diagnostics policy and catalog slice only. It does not add source
language syntax, runtime behavior, stdlib APIs, ABI/layout guarantees, LSP,
watch mode, SARIF, daemon protocols, stable Markdown output, or a stable
`1.0.0` diagnostics freeze.

## Schema Identity

The machine diagnostic schema name is `slovo.diagnostic`.

The current schema version is `1`.

The S-expression form uses a root `(diagnostic ...)` object:

```text
(diagnostic
  (schema slovo.diagnostic)
  (version 1)
  ...)
```

The JSON form uses a single JSON object:

```json
{"schema":"slovo.diagnostic","version":1}
```

The schema name and version belong to the machine contract. Changing either one
requires an explicit migration note, release-note entry, and matching Glagol
snapshot or JSON fixture updates.

## Encodings

S-expression diagnostics and JSON diagnostics are two encodings of the same
diagnostic data model. They must not diverge semantically.

The default human diagnostics path may print human-readable prose and then the
S-expression machine form for source diagnostics. Human prose, spacing,
excerpts, hints, and surrounding text remain beta-flexible unless a later
release freezes them explicitly. Tools should consume the machine form, not the
human rendering.

`--json-diagnostics` emits JSON diagnostics on stderr. Each diagnostic is one
complete JSON object on one line. There is no JSON array wrapper, no pretty
printing, and no required trailing summary object for source failures. Consumers
should parse stderr as newline-delimited JSON objects and should treat each
line as an independent diagnostic record.

JSON and S-expression escaping must preserve the same string values. JSON uses
standard JSON string escaping. S-expression strings use the Glagol diagnostic
string escaping convention already used in golden `.diag` fixtures.

## Fields

Every source-attached compiler diagnostic must include these machine fields:

| Field | S-expression | JSON | Meaning |
| --- | --- | --- | --- |
| Schema | `(schema slovo.diagnostic)` | `"schema":"slovo.diagnostic"` | Diagnostic schema name. |
| Version | `(version 1)` | `"version":1` | Diagnostic schema version. |
| Severity | `(severity error)` | `"severity":"error"` | Diagnostic severity. |
| Code | `(code TypeMismatch)` | `"code":"TypeMismatch"` | Stable PascalCase diagnostic code for the condition. |
| Message | `(message "...")` | `"message":"..."` | Concise beta-flexible human message for the machine record. |
| Source file | `(file "...")` | `"file":"..."` | Source identity used by the invocation or project loader. |
| Span | `(span ...)` | `"span":{...}` | Primary source span and line/column range. |

Optional fields may appear when the compiler has precise data:

| Field | S-expression | JSON | Meaning |
| --- | --- | --- | --- |
| Expected | `(expected "...")` | `"expected":"..."` | Expected type, arity, value, or form. |
| Found | `(found "...")` | `"found":"..."` | Found type, arity, value, or form. |
| Hint | `(hint "...")` | `"hint":"..."` | Safe repair or orientation hint. |
| Related spans | repeated `(related ...)` | `"related":[...]` | Secondary source locations tied to the primary diagnostic. |

Optional fields are additive when they do not replace an existing machine field
or change an existing diagnostic code. Removing an optional field from an
existing golden fixture is a migration-level change unless the release note
states that the previous field was incorrect.

## Severity

Current source diagnostics use severity `error`.

JSON source-less or invocation-level messages may use `error` for failures and
`note` for informational tool output such as a machine-readable test-run
summary. A `note` is not by itself a source failure; consumers should still use
the process exit code and artifact manifest success field to determine command
success.

Adding a new severity value is an additive schema change only when old
consumers can safely ignore or display it. Reclassifying an existing failing
diagnostic from `error` to another severity is migration-level.

## Source And Range Semantics

`file` is the source identity reported by the compiler path, project loader, or
workspace loader. It is not a promise of absolute path normalization, URI
formatting, registry identity, or editor document identity.

Primary and related byte spans are zero-based and half-open: `start` is the
first byte included, and `end` is the first byte after the highlighted source.

Line and column ranges are one-based and derived from the original source text.
Columns are byte columns within the original UTF-8 source line; a tab counts as
one input byte. The end column is the first byte column after the highlighted
range on the end line.

Diagnostic locations are derived from source input, not formatter output,
lowered IR, generated C, LLVM IR, native object code, or runtime stack traces.

## Related Spans

Related spans identify secondary source locations such as an original
declaration, previous duplicate, or conflicting arm. A related span never
replaces the primary span.

In S-expression diagnostics, each related location is a repeated
`(related (span ...))` form. In JSON diagnostics, related locations are objects
inside the `related` array. Each related object has its own `file` and `span`;
it may also carry a `message` string.

Related messages are beta-flexible prose. The existence of a related span for a
current golden fixture is part of the machine shape for that fixture and should
change only intentionally.

## Source-Less Diagnostics

Some diagnostics describe tool invocation or usage failures rather than a
source range. Examples include invalid CLI arguments, missing inputs, and
source-loading failures before a source span is available.

JSON source-less diagnostics use:

```json
{"file":null,"span":null}
```

Source-less diagnostics must not invent dummy paths, byte offsets, or
line/column ranges. Text-mode source-less diagnostics may remain human-only in
current Glagol output; consumers that need machine-readable source-less
diagnostics should request `--json-diagnostics`.

If a future release adds S-expression source-less diagnostics, absence of a
source must be explicit and documented under `slovo.diagnostic` version `1` or
a later migrated version. It must not be encoded as fake source coordinates.

## Artifact Manifest Metadata

`slovo.artifact-manifest` records the diagnostic metadata for a tool
invocation. The relevant fields are:

- `(diagnostics-schema-version 1)`: the diagnostic schema version used by the
  invocation.
- `(diagnostics-encoding sexpr)` or `(diagnostics-encoding json)`: the encoding
  selected for diagnostics.
- `(primary-output (kind diagnostics) ...)`: the primary output kind when the
  command failed by emitting diagnostics.
- `(diagnostic_artifacts ...)`: the diagnostic stream artifact metadata for
  project/package/workspace modes.
- `(diagnostics_count N)`: the project-level count when available.

The manifest points to or records diagnostic streams. It does not define a
separate diagnostic schema, embed a parsed diagnostic catalog, freeze Markdown
documentation structure, or replace the newline discipline of JSON diagnostic
stderr.

## Compatibility And Migration

Diagnostic changes are classified as follows.

Clarifying changes:

- Rewording human-readable stderr prose.
- Rewording `message` or `hint` text without changing the diagnostic code,
  source span, field set, severity, or expected/found semantics.
- Improving documentation around an existing code or fixture.

Additive changes:

- Adding a new diagnostic code for a newly covered boundary.
- Adding a new golden fixture for a newly rejected form.
- Adding an optional field when the old fields, code, severity, source span,
  and JSON-line discipline remain valid.
- Adding related spans for a new fixture.

Migration changes:

- Renaming, removing, splitting, or merging an existing diagnostic code covered
  by golden fixtures.
- Changing required field names, schema name, schema version, span shape, range
  indexing, JSON null policy, or JSON-line discipline.
- Removing an existing optional field or related span from a golden fixture
  without documenting the correction.
- Changing a source-attached diagnostic into a source-less diagnostic, or the
  reverse, for an existing golden fixture.
- Reclassifying an existing failing diagnostic away from severity `error`.
- Changing artifact manifest diagnostic metadata fields or their meaning.

Every migration-level diagnostic change must update this document or the next
release policy, `docs/language/MIGRATION_POLICY.md` if needed,
`docs/language/RELEASE_NOTES.md`, and the matching Glagol golden snapshots or
tests.

## Current Golden Catalog

The current golden diagnostics contract is the snapshot inventory in
`compiler/tests/diagnostics_contract.rs`. As of `1.0.0-beta.13`, that contract
references 358 `.diag` snapshots under `tests/`, and those snapshots contain
114 unique diagnostic codes.

This catalog inventories those codes. It is policy-focused: messages and
fixture-specific prose remain in the snapshots.

| Area | Current codes |
| --- | --- |
| General calls, typing, signatures, and source shape | `ArityMismatch`, `ReturnTypeMismatch`, `SingleFileMainSignature`, `TypeMismatch`, `UnclosedList`, `UnknownFunction`, `UnknownTopLevelForm`, `UnsupportedBackendFeature`, `UnsupportedUnitSignatureType` |
| Control flow and tests | `EmptyWhileBody`, `IfBranchTypeMismatch`, `IfConditionNotBool`, `MalformedIfForm`, `MalformedTestForm`, `MalformedWhileForm`, `NestedWhileUnsupported`, `TestExpressionNotBool`, `WhileBodyFormNotUnit`, `WhileConditionNotBool` |
| Literals and strings | `I64LiteralOutOfRange`, `IntegerOutOfRange`, `InvalidI64Literal`, `UnsupportedFloatLiteral`, `UnsupportedStringConcatenation`, `UnsupportedStringEscape`, `UnsupportedStringLiteral` |
| Locals, assignment, and name conflicts | `CannotAssignImmutableLocal`, `CannotAssignParameter`, `DuplicateFunction`, `DuplicateLocal`, `DuplicateTestName`, `InvalidSetTarget`, `InvalidTestName`, `LocalDeclarationInWhileBodyUnsupported`, `LocalDeclarationNotAllowed`, `LocalRedeclaresParameter`, `LocalShadowsCallable`, `ParameterShadowsCallable`, `UnknownVariable`, `UnsupportedLocalType` |
| Type aliases, generics, maps, and sets | `DuplicateTypeAlias`, `MalformedTypeAlias`, `SelfTypeAlias`, `TypeAliasCycle`, `TypeAliasNameConflict`, `UnknownTypeAliasTarget`, `UnsupportedGenericFunction`, `UnsupportedGenericStandardLibraryCall`, `UnsupportedGenericTypeAlias`, `UnsupportedGenericTypeParameter`, `UnsupportedMapType`, `UnsupportedSetType`, `UnsupportedTypeAliasTarget` |
| Structs and fields | `DuplicateStruct`, `DuplicateStructConstructorField`, `DuplicateStructField`, `EmptyStructUnsupported`, `FieldAccessOnNonStruct`, `MissingStructField`, `RecursiveStructFieldUnsupported`, `StructConstructorFieldOrderMismatch`, `UnknownStructField`, `UnknownStructType`, `UnsupportedStructFieldType` |
| Arrays and vectors | `ArrayIndexNotI32`, `ArrayIndexOutOfBounds`, `EmptyArrayUnsupported`, `IndexOnNonArray`, `MutableArrayLocalUnsupported`, `UnsupportedArrayElementType`, `UnsupportedArrayEquality`, `UnsupportedArrayPrint`, `UnsupportedVectorElementType`, `UnsupportedVectorEquality`, `ZeroLengthArrayUnsupported` |
| Options, results, and match | `DuplicateMatchArm`, `MalformedMatchPattern`, `MalformedOptionConstructor`, `MalformedResultConstructor`, `MalformedUnwrapForm`, `MatchArmTypeMismatch`, `MatchBindingCollision`, `MatchSubjectTypeMismatch`, `NonExhaustiveMatch`, `OptionObservationTypeMismatch`, `OptionUnwrapTypeMismatch`, `ResultObservationTypeMismatch`, `ResultUnwrapTypeMismatch`, `UnsupportedMatchContainer`, `UnsupportedMatchMutation`, `UnsupportedMatchPayloadType`, `UnsupportedOptionPayloadType`, `UnsupportedOptionResultEquality`, `UnsupportedOptionResultPrint`, `UnsupportedResultPayloadType` |
| Enums | `DuplicateEnum`, `DuplicateEnumVariant`, `EmptyEnumUnsupported`, `EnumSubjectMismatch`, `InvalidEnumMatchArm`, `MixedEnumPayloadTypesUnsupported`, `RecursiveEnumPayloadStructUnsupported`, `UnknownEnumConstructor`, `UnknownVariantConstructor`, `UnsupportedEnumContainer`, `UnsupportedEnumEquality`, `UnsupportedEnumOrdering`, `UnsupportedEnumPayloadType`, `UnsupportedEnumPrint`, `VariantConstructorArity` |
| Unsafe operations | `MalformedUnsafeForm`, `UnsafeRequired`, `UnsupportedUnsafeOperation` |
| Standard-library reservation boundaries | `UnsupportedStandardLibraryCall` |

Future releases may add codes or split broad codes when the release scope
requires more precise tooling behavior. Such changes must be documented using
the compatibility classes above.

## Project And Workspace Codes

Project, package, and workspace diagnostics are covered by integration tests
because they often require multiple files, manifests, or generated temporary
directories rather than a single `.slo` golden fixture. These codes still use
the same `slovo.diagnostic` version `1` schema and PascalCase code policy.

Current package/workspace loader and graph diagnostics include:

- `DependencyNameMismatch`
- `DependencyPathEscape`
- `DuplicatePackageDependencyName`
- `DuplicatePackageName`
- `DuplicateWorkspaceMember`
- `InvalidPackageDependencyName`
- `InvalidPackageName`
- `InvalidPackageVersion`
- `MissingPackageDependency`
- `MissingPackageModule`
- `PackageDependencyCycle`
- `PackageImportNotDependency`
- `PackageManifestInvalid`
- `PackageSourceReadFailed`
- `PackageSourceRootEscape`
- `PackageSourceRootMissing`
- `ProjectEntryMainInvalidSignature`
- `ProjectEntryMainMissing`
- `ProjectManifestInvalid`
- `ProjectManifestReadFailed`
- `ProjectSourceReadFailed`
- `ProjectSourceRootMissing`
- `UnsupportedDependency`
- `WorkspaceBuildAmbiguousEntryPackage`
- `WorkspaceDefaultPackageEntryMissing`
- `WorkspaceDefaultPackageMissing`
- `WorkspaceEntryMainInvalidSignature`
- `WorkspaceEntryMainMissing`
- `WorkspaceManifestInvalid`
- `WorkspaceMemberManifestMissing`
- `WorkspaceMemberPathEscape`

## Explicit Deferrals

`1.0.0-beta.13` does not define:

- a stable `1.0.0` diagnostics freeze
- LSP diagnostics, watch mode, SARIF, daemon protocols, or debug adapters
- a stable Markdown documentation schema
- stable source-map, DWARF, LLVM debug metadata, or runtime stack trace schema
- warning, lint, or suggestion taxonomies beyond the current `error` and
  source-less `note` uses
- localized diagnostic text
- machine-readable remediation edits
- a separate diagnostic catalog artifact emitted by the compiler
- stable package registry, URI, or workspace identity semantics for `file`
