Diagnostics
Reference for the JSON Lines diagnostics produced by sakura check --json, including record kinds, error codes, and fix impact classes.
Pre-alpha
The source-built compiler runs the core language and much of M3 on JavaScript: contracts, typed effects and failures, std handlers, permissions, LSP and MCP. M3 is still in progress; no release or stable compatibility contract exists. See the roadmap for implemented features and remaining gates.
Diagnostics are an API. Every diagnostic has a stable code, byte spans, structured expected and actual values, causal links, and fixes that declare their impact. The CLI, the LSP server, and the MCP server share one analysis library and one format.
sakura check --json
sakura test --json--json writes JSON Lines: one record per line. Human-readable output is rendered from the same data.
Record kinds
kind | Meaning |
|---|---|
diagnostic | An error, warning, or note. Shape below. |
hole | A typed hole with its context. |
obligation | A claim about the program and its status: proved, runtime_checked, tested, or trusted. |
progress | Progress of a long run. |
summary | End of run. summary.truncated is true if output hit the cap. |
The diagnostic record
The program below omits Console from greet's effect row:
fn greet(name: String) -> Unit {
Console.print("hello, {name}")
}sakura check --json would report it as one line, shown here pretty-printed. Hashes and ids are shortened.
{
"schema_version": "1.0",
"kind": "diagnostic",
"id": "d:7f3a",
"code": "V3001",
"severity": "error",
"phase": "effect",
"message": "function `greet` performs `Console`, which its signature does not allow",
"snapshot": "sha256:91c0",
"symbol_id": "hello::main::greet",
"spans": [
{ "uri": "file:///work/hello/src/main.sakura", "content_hash": "sha256:4be2",
"start_byte": 3, "end_byte": 8, "role": "primary", "label": "`greet` is declared pure" },
{ "uri": "file:///work/hello/src/main.sakura", "content_hash": "sha256:4be2",
"start_byte": 35, "end_byte": 65, "role": "cause", "label": "`Console.print` introduces `Console` here" }
],
"related_ids": [],
"expected": { "render": "{}", "row": { "labels": [], "tail": null } },
"actual": { "render": "{Console}", "row": { "labels": [{ "effect": "std::Console", "args": [] }], "tail": null } },
"obligation": null,
"counterexample": null,
"explanation_uri": "sakura://explain/V3001",
"fixes": [
{ "id": "f:01", "title": "Add `Console` to the signature of `greet`",
"applicability": "suggested", "impact": "interface_change",
"edits": [ { "span": { "uri": "file:///work/hello/src/main.sakura", "content_hash": "sha256:4be2",
"start_byte": 30, "end_byte": 30, "role": "primary", "label": "" },
"replacement": " ! {Console}" } ] }
]
}For V3001, the design specifies two fixes: add the effect to the signature (interface_change) or handle it locally with with (local). Only the first is shown.
Fields
| Field | Type | Meaning |
|---|---|---|
schema_version | string | Version of this format. "1.0" in v0. |
kind | string | "diagnostic". |
id | string | Unique per occurrence in this run. |
code | string | Stable class, such as V3001. See codes. |
severity | string | error, warning, or note. |
phase | string | lex, parse, resolve, type, effect, pattern, contract, capability, verify, package, or internal. |
message | string | One sentence naming the symbols involved in backticks. |
snapshot | string | Hash of the workspace snapshot that was checked. |
symbol_id | string or null | The symbol the diagnostic is about. |
spans | array | Source locations, each with uri, content_hash, start_byte, end_byte, role, label. |
related_ids | array | Ids of root-cause diagnostics this one cascades from. Empty for a root cause. |
expected | object or null | What was required: a render string, and a structured row for effect rows. |
actual | object or null | What was found, in the same shape. |
obligation | object or null | id, clause (for example "ensures bounded"), and status. |
counterexample | object or null | inputs and a trace, when one is known. |
explanation_uri | string | sakura://explain/<code>. Same content as sakura explain <code> and the MCP explain_diagnostic tool. |
fixes | array | Zero or more fixes. See below. |
Span role is one of primary, cause, definition, or use.
Rules
- Byte offsets are zero-based, half-open, and count UTF-8 bytes. There are no line or column numbers.
content_hashmust match the file before any edit is applied.- The edits in one fix are applied atomically and rejected if they overlap.
- Records are ordered root causes first (empty
related_ids), then by position. - Output is capped at 200 diagnostics per run, with
summary.truncated: true.
Fixes
| Field | Meaning |
|---|---|
id | Fix id, accepted by sakura fix --apply. |
title | Short imperative description. |
applicability | machine_applicable, suggested, or incomplete. |
impact | What the fix changes. See below. |
edits | List of { span, replacement }. A span with equal start and end is an insertion. |
Impact classes
impact | Changes | Suggested agent policy |
|---|---|---|
local | Code inside one body; no signature changes. | Apply if machine_applicable. |
behavior_change | What the code does, without changing its signature. | Apply only if it matches the task. |
interface_change | A signature, effect row, or error union. | Apply only if the task needs it, and report it for review. |
contract_change | A requires, ensures, or where clause. | Never apply unless a human asks. |
authority_change | Permissions or capabilities. | Never apply unless a human asks. |
Compiler fixes with interface_change, contract_change, or authority_change impact are never machine_applicable. No fix ever widens authority or weakens a contract by itself.
sakura fix lists fixes with their impact class. It applies only machine_applicable fixes unless given ids with --apply id,…, and it never applies contract_change or authority_change. --preview shows the edits without writing.
Frozen specs
sakura check --frozen-specs <symbols|all>, sakura fix, and the MCP apply_patch tool reject any patch that changes a frozen symbol's signature, effect row, error union, requires/ensures/where clauses, or any test/property body. The diagnostic is V8020 spec_frozen and names the clause. This stops an agent from passing a check by editing the spec.
Error codes
Codes are V + a phase digit + three digits for errors, W for warnings, and D for runtime defects. Codes are never reused or renumbered; retired codes stay documented. Every code has an explain page with a minimal pair: wrong program, diagnostic, fixed program.
| Phase digit | Area | Examples |
|---|---|---|
0 | Lexing and parsing | V0001 syntax error, V0007 reserved keyword, V0010 chained comparison, V0011 call-site type arguments, V0014 named argument |
1 | Resolution and modules | V1002 unresolved name (offers the import as a fix), V1004 wrong casing, V1006 item not pub or opaque constructor, V1007 wrong kind of name, V1010 unknown attribute, V1030 import cycle, V1040 assignment to a let, V1041 jump outside a loop |
2 | Types | V2031 Secret is not Show, V2032 constraint not satisfied, V2040 unused value, V2041 lambda or try body assigns an outer var, V2050 hole in build |
3 | Effects and handlers | V3001 effect not allowed, V3011 handler clauses do not match the operations, V3020 test needs a handler |
4 | Patterns | V4001 non-exhaustive match, V4002 redundant arm |
5 | Contracts and refinements | V5001 predicate outside the contract subset |
6 | Capabilities, resources, escape | V6002 capability escapes its scope, V6003 resource escapes, V6010 non-std effect in main |
7 | Tests and verification | None named in the design yet |
8 | Packages, build, frozen specs | V8020 spec frozen |
9 | Internal | Compiler bugs |
Warnings named in the design include W1002 (a local shadows an import), W4010 (catch-all _ on an open error union), W5005 (an O(n) contract on a hot path), W7001 (property generator discard budget exceeded), and W8001 (a collection update copies in a loop).
Runtime defects are not diagnostics from sakura check. They terminate the task; if main defects, the program writes one defect record to stderr and exits with status 70:
{"schema_version":"1.0","kind":"defect","code":"D002","message":"division by zero"}| Code | Raised by |
|---|---|
D001 | Int overflow |
D002 | / or % by zero |
D003 | Int beyond ±(2^53−1) on the JavaScript backend |
D004 | Float.round or Float.to_int_checked of NaN or ±∞ |
D010 | failed refinement in a checked introduction (T.from) |
D011 | a contract predicate itself defected |
D020, D021 | requires violated (caller blamed), ensures violated (callee blamed) |
D030 | Option.expect on None |
D031 | String.slice off a character boundary |
D090, D091 | runtime-internal error, host operation failed |
Exit codes
| Code | Meaning |
|---|---|
0 | OK. |
1 | Diagnostics with errors, or main ended with an uncaught Fail (stderr: error: main failed with <Show of the error>). |
2 | Usage error. |
3 | Test failures. |
70 | Defect at runtime. |
75 | Permission denied at startup. |
Next
Typed holes: writing a program one ?hole at a time.