diff --git a/.llm/BETA_13_DIAGNOSTIC_CATALOG_AND_SCHEMA_POLICY.md b/.llm/BETA_13_DIAGNOSTIC_CATALOG_AND_SCHEMA_POLICY.md new file mode 100644 index 0000000..42c607b --- /dev/null +++ b/.llm/BETA_13_DIAGNOSTIC_CATALOG_AND_SCHEMA_POLICY.md @@ -0,0 +1,66 @@ +# 1.0.0-beta.13 Diagnostic Catalog And Schema Policy + +Status: release scope for `1.0.0-beta.13`. + +`1.0.0-beta.13` is a docs/tooling-only policy slice for the existing +diagnostic surface. It keeps the `1.0.0-beta` source language, typed core, +runtime, standard library, compiler CLI, diagnostic output shape, ABI/layout, +and compiler-known runtime names unchanged. + +## Scope + +- Add `docs/language/DIAGNOSTICS.md` as the beta policy for + `slovo.diagnostic` version `1`. +- Document the S-expression and JSON relationship. +- Document required and optional fields, severity/source/range/related-span + semantics, JSON-line discipline, source-less diagnostics, and + artifact-manifest diagnostic metadata. +- Define diagnostic compatibility and migration classes. +- Inventory the current diagnostic codes covered by + `compiler/tests/diagnostics_contract.rs` and the matching `.diag` snapshots. +- Update README, language roadmap, language spec, release notes, migration + policy, and post-beta roadmap to introduce beta13 and link the diagnostics + policy. + +## Acceptance + +- `docs/language/DIAGNOSTICS.md` names schema `slovo.diagnostic` and version + `1`. +- The document is clear that S-expression diagnostics and JSON diagnostics are + encodings of the same data model. +- JSON diagnostics are documented as one object per line on stderr, without an + array wrapper or pretty-printing requirement. +- Source-less diagnostics are documented without inventing fake source spans. +- Artifact manifests are documented as carrying diagnostic schema version, + diagnostic encoding, and diagnostic stream metadata, not a second diagnostic + schema. +- Human prose is documented as beta-flexible, while machine fields, codes, + schema/version markers, ranges, JSON-line discipline, and golden fixture + shape are compatibility-sensitive. +- The code catalog is concise and derived from the current golden diagnostics + contract. + +## Explicit Non-Scope + +- no source-language syntax change +- no typed-core, lowering, runtime, stdlib, or ABI/layout change +- no diagnostic-output shape change +- no LSP server, watch mode, SARIF, daemon protocol, or debug adapter +- no stable Markdown schema +- no stable `1.0.0` diagnostics freeze +- no source-map, DWARF, or LLVM debug metadata contract +- no compiler-emitted diagnostic catalog artifact +- no release publication, tag, push, or version publication work + +## Expected Controller Verification + +- Review the policy for consistency with `compiler/src/diag.rs`, + `compiler/src/main.rs`, `compiler/tests/diagnostics_contract.rs`, and the + current `.diag` snapshots. +- Confirm the catalog count matches the current contract: 358 snapshots and + 114 unique diagnostic codes. +- Run lightweight docs checks: + - `git diff --check -- README.md docs/POST_BETA_ROADMAP.md docs/language/DIAGNOSTICS.md docs/language/MIGRATION_POLICY.md docs/language/SPEC-v1.md docs/language/ROADMAP.md docs/language/RELEASE_NOTES.md .llm/BETA_13_DIAGNOSTIC_CATALOG_AND_SCHEMA_POLICY.md` + - run an `rg` check for stale beta12-only current-stage phrasing across the + same touched docs +- Do not commit, tag, push, or run release publication from this worker scope. diff --git a/.llm/reviews/BETA_13_RELEASE_REVIEW.md b/.llm/reviews/BETA_13_RELEASE_REVIEW.md new file mode 100644 index 0000000..3ef28a3 --- /dev/null +++ b/.llm/reviews/BETA_13_RELEASE_REVIEW.md @@ -0,0 +1,64 @@ +# 1.0.0-beta.13 Release Review + +Status: ready for publication after controller release gate. + +## Verdict + +No blockers after controller catalog-count reconciliation. + +## Findings + +No blocking findings remain. + +Controller reconciliation note: the initial review used `rg -o -h ...` for the +catalog count. On the local ripgrep version, `-h` prints help instead of +meaning "no filename", so that command mixed help text with diagnostic codes. +Using `--no-filename` gives the correct inventory: 358 `.diag` snapshots and +114 unique `(code ...)` values. The Current Golden Catalog table matches that +114-code snapshot set. + +## Scope Checked + +- Public docs in scope: `README.md`, `docs/POST_BETA_ROADMAP.md`, + `docs/language/DIAGNOSTICS.md`, `docs/language/MIGRATION_POLICY.md`, + `docs/language/SPEC-v1.md`, `docs/language/ROADMAP.md`, + `docs/language/RELEASE_NOTES.md`, `docs/compiler/ROADMAP.md`, + `docs/compiler/RELEASE_NOTES.md`, `docs/language/STDLIB_API.md`, and + `.llm/BETA_13_DIAGNOSTIC_CATALOG_AND_SCHEMA_POLICY.md`. +- Compiler/tooling in scope: `compiler/src/diag.rs`, `compiler/src/main.rs`, + `compiler/tests/diagnostics_schema_beta13.rs`, + `compiler/tests/diagnostics_contract.rs`, `scripts/release-gate.sh`, + `compiler/Cargo.toml`, and `compiler/Cargo.lock`. +- The compiler diff keeps diagnostic output shape limited to centralizing + `slovo.diagnostic` schema name/version constants and reusing the version in + artifact manifest diagnostics metadata. +- `docs/language/DIAGNOSTICS.md` describes the current S-expression and JSON + diagnostic shape, newline-delimited JSON stderr behavior, source-less JSON + `file:null` and `span:null`, artifact-manifest diagnostic metadata, + compatibility/migration classes, and explicit deferrals. +- Release-facing stage wording consistently points at `1.0.0-beta.13` as the + current stage and post-beta13 work as the next scope. Focused private-path + and stale current-stage beta12 checks found no matches. + +## Verification Commands And Results + +- `cargo fmt --check`: passed. +- `cargo test --test diagnostics_schema_beta13`: passed, 3 tests. +- `cargo test --test diagnostics_contract current_negative_cases_match_machine_diagnostic_snapshots`: + passed, 1 test. +- `git diff --check`: passed. +- `rg -o 'snapshot: "\\.\\./tests/[^"]+\\.diag"' compiler/tests/diagnostics_contract.rs | wc -l`: + `358`. +- `rg --files tests | rg '\\.diag$' | wc -l`: `358`. +- `rg --no-filename -o '\\(code [A-Za-z][A-Za-z0-9]*\\)' tests/*.diag | sed -E 's/^\\(code ([^)]+)\\)$/\\1/' | sort -u | wc -l`: + `114`. +- Comparing the unique snapshot code set with the Current Golden Catalog code + set produced no differences, confirming the catalog table itself matches the + 114-code snapshot set. +- Focused `rg` check for private/local paths and stale current-stage beta12 + release wording in the scoped public docs produced no matches. + +- `./scripts/release-gate.sh`: passed after the beta13 release commit, + including docs/API freshness, the focused beta13 diagnostics schema test, + `cargo fmt --check`, full `cargo test`, ignored promotion gates, binary + smoke, and LLVM smoke. diff --git a/README.md b/README.md index d89081c..f936ac0 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This repository is the canonical public monorepo for the language design, standard library source, compiler, runtime, examples, benchmarks, and technical documents. -Current release: `1.0.0-beta.12`. +Current release: `1.0.0-beta.13`. ## Repository Layout @@ -24,7 +24,7 @@ scripts/ local release and document tooling ## Beta Scope -`1.0.0-beta.12` keeps the `1.0.0-beta` language baseline, includes the +`1.0.0-beta.13` keeps the `1.0.0-beta` language baseline, includes the `1.0.0-beta.1` tooling/install hardening slice, the `1.0.0-beta.2` runtime/resource foundation bundle, the `1.0.0-beta.3` standard-library stabilization bundle, the `1.0.0-beta.4` language-usability diagnostics @@ -35,8 +35,9 @@ alias foundation, the `1.0.0-beta.9` collection alias unification and generic reservation slice, the `1.0.0-beta.10` developer-experience API discovery slice, and the `1.0.0-beta.11` local package API documentation slice, plus the `1.0.0-beta.12` concrete vector query and prefix parity -slice. The language baseline supports practical local -command-line, file, and loopback-network programs with: +slice, and the `1.0.0-beta.13` diagnostic catalog and schema policy slice. +The language baseline supports practical local command-line, file, and +loopback-network programs with: - modules, explicit imports, packages, and local workspaces - `new`, `check`, `fmt`, `test`, `doc`, `symbols`, `build`, `run`, and @@ -65,23 +66,30 @@ types, non-export filtering, and module-local alias normalization. The `1.0.0-beta.12` vector parity slice adds source-authored helper coverage only: `std.vec_i64` gains `count_of`, `starts_with`, `without_prefix`, `ends_with`, and `without_suffix`, while `std.vec_f64` gains `count_of`. +The `1.0.0-beta.13` diagnostics slice documents the beta +`slovo.diagnostic` version `1` policy in +[`docs/language/DIAGNOSTICS.md`](docs/language/DIAGNOSTICS.md): the +S-expression/JSON relationship, required and optional fields, JSON-line +discipline, source-less diagnostics, manifest diagnostic metadata, +compatibility and migration classes, and the current golden diagnostic code +catalog. Still deferred before stable: executable generics, maps/sets, broad package registry semantics, stable Markdown schema, stable stdlib/API compatibility freeze, DNS/TLS/async networking, LSP/watch guarantees, SARIF and daemon -protocols, diagnostics schema policy, re-exports/globs/hierarchical modules, -mutable vectors, slice/view APIs, iterators, new compiler-known runtime names, -stable ABI and layout, performance claims, and runtime changes for generic -collections. +protocols, stable `1.0.0` diagnostics freeze, re-exports/globs/hierarchical +modules, mutable vectors, slice/view APIs, iterators, new compiler-known +runtime names, stable ABI and layout, performance claims, and runtime changes +for generic collections. -The next likely language slice after `1.0.0-beta.12` should continue from the -developer-experience, diagnostics, and reserved generic/collection lanes -without claiming executable generics, maps, sets, traits, inference, -monomorphization, iterators, ABI stability, runtime changes, LSP/watch -protocols, registry semantics, stable Markdown schema, or a -standard-library/API compatibility freeze, mutable vectors, slice/view APIs, -new runtime names, or performance claims until the contract and gates are -explicit. +The next likely language slice after `1.0.0-beta.13` should continue from the +developer-experience, package, and reserved generic/collection lanes without +claiming executable generics, maps, sets, traits, inference, monomorphization, +iterators, ABI stability, runtime changes, LSP/watch protocols, SARIF/daemon +protocols, registry semantics, stable Markdown schema, a stable `1.0.0` +diagnostics freeze, standard-library/API compatibility freeze, mutable +vectors, slice/view APIs, new runtime names, or performance claims until the +contract and gates are explicit. ## Build And Test @@ -307,10 +315,28 @@ maps/sets, iterators, mutable vectors, slice/view APIs, new runtime helper names, stable stdlib API freeze, and broader collection abstractions remain deferred. +## 1.0.0-beta.13 Diagnostic Catalog And Schema Policy + +The `1.0.0-beta.13` release is a docs/tooling policy update for the existing +diagnostic surface. It adds +[`docs/language/DIAGNOSTICS.md`](docs/language/DIAGNOSTICS.md) as the beta +policy for `slovo.diagnostic` version `1`. + +The policy documents the S-expression and JSON encodings, required and optional +machine fields, severity/source/range/related-span semantics, JSON-line +discipline, source-less diagnostics, artifact-manifest diagnostic metadata, +compatibility and migration classes, and the current code catalog covered by +the golden diagnostics contract. + +This release does not change the source language, runtime, stdlib API, +diagnostic output shape, compiler CLI, LSP/watch behavior, SARIF/daemon +protocols, stable Markdown schema, or stable `1.0.0` diagnostics freeze. + ## Documentation - [Language Manifest](docs/language/MANIFEST.md) - [Language Specification](docs/language/SPEC-v1.md) +- [Diagnostics Policy](docs/language/DIAGNOSTICS.md) - [Local Package And Workspace Guide](docs/language/PACKAGES.md) - [Standard Library API Catalog](docs/language/STDLIB_API.md) - [Compiler Manifest](docs/compiler/GLAGOL_COMPILER_MANIFEST.md) diff --git a/compiler/Cargo.lock b/compiler/Cargo.lock index c7eaaaa..7117418 100644 --- a/compiler/Cargo.lock +++ b/compiler/Cargo.lock @@ -4,4 +4,4 @@ version = 3 [[package]] name = "glagol" -version = "1.0.0-beta.12" +version = "1.0.0-beta.13" diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml index 7614452..d91c758 100644 --- a/compiler/Cargo.toml +++ b/compiler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "glagol" -version = "1.0.0-beta.12" +version = "1.0.0-beta.13" edition = "2021" description = "Glagol, the first compiler for the Slovo language" license = "MIT OR Apache-2.0" diff --git a/compiler/src/diag.rs b/compiler/src/diag.rs index 3de2b2d..bc75edd 100644 --- a/compiler/src/diag.rs +++ b/compiler/src/diag.rs @@ -1,5 +1,8 @@ use crate::token::Span; +pub const DIAGNOSTIC_SCHEMA_NAME: &str = "slovo.diagnostic"; +pub const DIAGNOSTIC_SCHEMA_VERSION: u32 = 1; + #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Severity { Error, @@ -184,8 +187,8 @@ impl Diagnostic { ) -> String { let source = source_for(&self.file).unwrap_or(""); let mut parts = vec![ - " (schema slovo.diagnostic)".to_string(), - " (version 1)".to_string(), + format!(" (schema {})", DIAGNOSTIC_SCHEMA_NAME), + format!(" (version {})", DIAGNOSTIC_SCHEMA_VERSION), format!(" (severity {})", self.severity.as_str()), format!(" (code {})", self.code), format!(" (message {})", render_string(&self.message)), @@ -310,8 +313,8 @@ fn render_json_diagnostic<'a>( related: impl Iterator>, ) -> String { let mut fields = vec![ - "\"schema\":\"slovo.diagnostic\"".to_string(), - "\"version\":1".to_string(), + format!("\"schema\":{}", render_json_string(DIAGNOSTIC_SCHEMA_NAME)), + format!("\"version\":{}", DIAGNOSTIC_SCHEMA_VERSION), format!("\"severity\":{}", render_json_string(severity)), format!("\"code\":{}", render_json_string(code)), format!("\"message\":{}", render_json_string(message)), diff --git a/compiler/src/main.rs b/compiler/src/main.rs index 56d5078..3cfb8d6 100644 --- a/compiler/src/main.rs +++ b/compiler/src/main.rs @@ -1977,7 +1977,10 @@ fn render_manifest( " (success {})\n", if success { "true" } else { "false" } )); - out.push_str(" (diagnostics-schema-version 1)\n"); + out.push_str(&format!( + " (diagnostics-schema-version {})\n", + diag::DIAGNOSTIC_SCHEMA_VERSION + )); out.push_str(&format!( " (diagnostics-encoding {})\n", match diagnostics { diff --git a/compiler/tests/diagnostics_schema_beta13.rs b/compiler/tests/diagnostics_schema_beta13.rs new file mode 100644 index 0000000..9ea943b --- /dev/null +++ b/compiler/tests/diagnostics_schema_beta13.rs @@ -0,0 +1,486 @@ +use std::{ + ffi::OsStr, + fs, + path::{Path, PathBuf}, + process::{Command, Output}, + sync::atomic::{AtomicUsize, Ordering}, +}; + +static NEXT_FIXTURE_ID: AtomicUsize = AtomicUsize::new(0); + +#[test] +fn sexpr_diagnostics_keep_v1_schema_across_source_pipelines() { + let cases = [ + SourceDiagnosticCase { + name: "parse", + args: &["check"], + source: "(module main", + code: "UnclosedList", + }, + SourceDiagnosticCase { + name: "check", + args: &["check"], + source: r#" +(module main) + +(fn id ((value i32)) -> i32 + value) + +(fn main () -> i32 + (id true)) +"#, + code: "TypeMismatch", + }, + SourceDiagnosticCase { + name: "fmt", + args: &["fmt"], + source: r#" +(module main) ; comments stay outside formatter input + +(fn main () -> i32 + 0) +"#, + code: "UnsupportedFormatterComment", + }, + SourceDiagnosticCase { + name: "test", + args: &["test"], + source: r#" +(module main) + +(test "false case" + false) +"#, + code: "TestFailed", + }, + ]; + + for case in cases { + let fixture = write_fixture(case.name, case.source); + let mut args = case.args.iter().map(OsStr::new).collect::>(); + args.push(fixture.as_os_str()); + + let output = run_glagol(args); + + assert_exit_code(case.name, &output, 1); + assert_stdout_empty(case.name, &output); + assert_sexpr_diagnostic_schema(case.name, &output, case.code, 1); + } + + let project = write_project( + "sexpr-project", + "(module main)\n\n(import missing (value))\n", + ); + let output = run_glagol(["check".as_ref(), project.as_os_str()]); + + assert_exit_code("project", &output, 1); + assert_stdout_empty("project", &output); + assert_sexpr_diagnostic_schema("project", &output, "MissingImport", 1); +} + +#[test] +fn json_diagnostics_keep_v1_schema_across_policy_boundaries() { + let cases = [ + SourceDiagnosticCase { + name: "json-parse", + args: &["--json-diagnostics", "check"], + source: "(module main", + code: "UnclosedList", + }, + SourceDiagnosticCase { + name: "json-check", + args: &["--json-diagnostics", "check"], + source: r#" +(module main) + +(fn id ((value i32)) -> i32 + value) + +(fn main () -> i32 + (id true)) +"#, + code: "TypeMismatch", + }, + SourceDiagnosticCase { + name: "json-fmt", + args: &["--json-diagnostics", "fmt"], + source: r#" +(module main) ; comments stay outside formatter input + +(fn main () -> i32 + 0) +"#, + code: "UnsupportedFormatterComment", + }, + SourceDiagnosticCase { + name: "json-test", + args: &["--json-diagnostics", "test"], + source: r#" +(module main) + +(test "false case" + false) +"#, + code: "TestFailed", + }, + ]; + + for case in cases { + let fixture = write_fixture(case.name, case.source); + let mut args = case.args.iter().map(OsStr::new).collect::>(); + args.push(fixture.as_os_str()); + + let output = run_glagol(args); + + assert_exit_code(case.name, &output, 1); + assert_stdout_empty(case.name, &output); + assert_json_diagnostic_schema(case.name, &output, case.code, JsonSource::Source); + } + + let project = write_project( + "json-project", + "(module main)\n\n(import missing (value))\n", + ); + let output = run_glagol([ + "--json-diagnostics".as_ref(), + "check".as_ref(), + project.as_os_str(), + ]); + + assert_exit_code("json project", &output, 1); + assert_stdout_empty("json project", &output); + assert_json_diagnostic_schema("json project", &output, "MissingImport", JsonSource::Source); + + let usage_manifest = temp_path("json-usage", "manifest.slo"); + let usage = run_glagol([ + "--json-diagnostics".as_ref(), + "--manifest".as_ref(), + usage_manifest.as_os_str(), + ]); + + assert_exit_code("json usage", &usage, 2); + assert_stdout_empty("json usage", &usage); + assert_json_diagnostic_schema("json usage", &usage, "UsageError", JsonSource::SourceLess); + assert_manifest_schema_fields(&read_manifest(&usage_manifest), "json"); + + let toolchain_fixture = write_fixture( + "json-toolchain", + "(module main)\n\n(fn main () -> i32\n 0)\n", + ); + let toolchain_manifest = temp_path("json-toolchain", "manifest.slo"); + let output_path = temp_path("json-toolchain", "bin"); + let missing_clang = temp_path("json-toolchain", "not-a-clang"); + let toolchain = Command::new(compiler_path()) + .arg("--json-diagnostics") + .arg("build") + .arg(&toolchain_fixture) + .arg("-o") + .arg(&output_path) + .arg("--manifest") + .arg(&toolchain_manifest) + .env("GLAGOL_CLANG", &missing_clang) + .env_remove("GLAGOL_RUNTIME_C") + .env_remove("SLOVO_RUNTIME_C") + .output() + .unwrap_or_else(|err| panic!("run glagol build: {}", err)); + + assert_exit_code("json toolchain", &toolchain, 3); + assert_stdout_empty("json toolchain", &toolchain); + assert_json_diagnostic_schema( + "json toolchain", + &toolchain, + "ToolchainUnavailable", + JsonSource::SourceLess, + ); + assert_manifest_schema_fields(&read_manifest(&toolchain_manifest), "json"); +} + +#[test] +fn project_failure_manifests_record_schema_encoding_and_count_deterministically() { + let project = write_project( + "manifest-project", + "(module main)\n\n(import missing (value))\n", + ); + + let first_sexpr = run_project_failure_manifest(&project, "sexpr", false); + let second_sexpr = run_project_failure_manifest(&project, "sexpr-repeat", false); + assert_manifest_schema_fields(&first_sexpr, "sexpr"); + assert_project_diagnostics_count(&first_sexpr, 1); + assert_eq!( + project_block(&first_sexpr), + project_block(&second_sexpr), + "S-expression failure manifest project block drifted" + ); + + let first_json = run_project_failure_manifest(&project, "json", true); + let second_json = run_project_failure_manifest(&project, "json-repeat", true); + assert_manifest_schema_fields(&first_json, "json"); + assert_project_diagnostics_count(&first_json, 1); + assert_eq!( + project_block(&first_json), + project_block(&second_json), + "JSON failure manifest project block drifted" + ); +} + +struct SourceDiagnosticCase { + name: &'static str, + args: &'static [&'static str], + source: &'static str, + code: &'static str, +} + +#[derive(Copy, Clone)] +enum JsonSource { + Source, + SourceLess, +} + +fn run_project_failure_manifest(project: &Path, name: &str, json: bool) -> String { + let manifest = temp_path(name, "manifest.slo"); + let output = if json { + run_glagol([ + "--json-diagnostics".as_ref(), + "check".as_ref(), + "--manifest".as_ref(), + manifest.as_os_str(), + project.as_os_str(), + ]) + } else { + run_glagol([ + "check".as_ref(), + "--manifest".as_ref(), + manifest.as_os_str(), + project.as_os_str(), + ]) + }; + + assert_exit_code(name, &output, 1); + assert_stdout_empty(name, &output); + read_manifest(&manifest) +} + +fn assert_sexpr_diagnostic_schema( + context: &str, + output: &Output, + expected_code: &str, + expected_count: usize, +) { + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + !stderr.trim().is_empty(), + "{} did not emit diagnostics", + context + ); + assert!( + !stderr + .lines() + .all(|line| line.starts_with('{') && line.ends_with('}')), + "{} unexpectedly emitted JSON diagnostics:\n{}", + context, + stderr + ); + assert_eq!( + stderr.matches("(diagnostic\n").count(), + expected_count, + "{} diagnostic block count drifted:\n{}", + context, + stderr + ); + assert_eq!( + stderr.matches(" (schema slovo.diagnostic)\n").count(), + expected_count, + "{} diagnostic schema name drifted:\n{}", + context, + stderr + ); + assert_eq!( + stderr.matches(" (version 1)\n").count(), + expected_count, + "{} diagnostic schema version drifted:\n{}", + context, + stderr + ); + assert!( + stderr.contains(" (severity error)\n") + && stderr.contains(&format!(" (code {})\n", expected_code)) + && stderr.contains(" (file ") + && stderr.contains(" (span\n"), + "{} S-expression diagnostic missed required structural fields:\n{}", + context, + stderr + ); +} + +fn assert_json_diagnostic_schema( + context: &str, + output: &Output, + expected_code: &str, + source: JsonSource, +) { + let stderr = String::from_utf8_lossy(&output.stderr); + let lines = stderr + .lines() + .filter(|line| !line.trim().is_empty()) + .collect::>(); + assert!(!lines.is_empty(), "{} did not emit diagnostics", context); + + for line in &lines { + assert!( + line.starts_with('{') && line.ends_with('}'), + "{} emitted non-JSON diagnostic text:\n{}", + context, + stderr + ); + assert!( + line.contains(r#""schema":"slovo.diagnostic""#) + && line.contains(r#""version":1"#) + && line.contains(r#""severity":"error""#) + && line.contains(r#""message":"#) + && line.contains(r#""file":"#) + && line.contains(r#""span":"#), + "{} JSON diagnostic missed required schema fields:\n{}", + context, + line + ); + } + + assert!( + lines + .iter() + .any(|line| line.contains(&format!(r#""code":"{}""#, expected_code))), + "{} JSON diagnostics did not include code `{}`:\n{}", + context, + expected_code, + stderr + ); + + match source { + JsonSource::Source => assert!( + lines + .iter() + .any(|line| line.contains(r#""span":{"byte_start":"#)), + "{} JSON diagnostics should include a concrete source span:\n{}", + context, + stderr + ), + JsonSource::SourceLess => assert!( + lines + .iter() + .any(|line| line.contains(r#""file":null"#) && line.contains(r#""span":null"#)), + "{} JSON diagnostics should be source-less:\n{}", + context, + stderr + ), + } +} + +fn assert_manifest_schema_fields(manifest: &str, encoding: &str) { + assert!( + manifest.contains(" (schema slovo.artifact-manifest)\n") + && manifest.contains(" (version 1)\n") + && manifest.contains(" (diagnostics-schema-version 1)\n") + && manifest.contains(&format!(" (diagnostics-encoding {})\n", encoding)) + && manifest.contains("slovo.diagnostic"), + "manifest diagnostic schema fields drifted:\n{}", + manifest + ); +} + +fn assert_project_diagnostics_count(manifest: &str, expected: usize) { + assert!( + manifest.contains(&format!(" (diagnostics_count {})\n", expected)), + "manifest diagnostics count drifted:\n{}", + manifest + ); +} + +fn project_block(manifest: &str) -> &str { + let start = manifest + .find(" (project\n") + .expect("manifest did not contain project block"); + manifest[start..] + .strip_suffix("\n)\n") + .expect("manifest did not end with artifact-manifest close") +} + +fn run_glagol(args: I) -> Output +where + I: IntoIterator, + S: AsRef, +{ + Command::new(compiler_path()) + .args(args) + .output() + .unwrap_or_else(|err| panic!("run glagol: {}", err)) +} + +fn compiler_path() -> &'static str { + env!("CARGO_BIN_EXE_glagol") +} + +fn write_fixture(name: &str, source: &str) -> PathBuf { + let path = temp_path(name, "slo"); + fs::write(&path, source).unwrap_or_else(|err| panic!("write `{}`: {}", path.display(), err)); + path +} + +fn write_project(name: &str, main_source: &str) -> PathBuf { + let root = temp_dir(name); + let source_root = root.join("src"); + fs::create_dir_all(&source_root) + .unwrap_or_else(|err| panic!("create `{}`: {}", source_root.display(), err)); + fs::write( + root.join("slovo.toml"), + "[project]\nname = \"beta13\"\nsource_root = \"src\"\nentry = \"main\"\n", + ) + .unwrap_or_else(|err| panic!("write project manifest: {}", err)); + fs::write(source_root.join("main.slo"), main_source) + .unwrap_or_else(|err| panic!("write main source: {}", err)); + root +} + +fn read_manifest(path: &Path) -> String { + fs::read_to_string(path).unwrap_or_else(|err| panic!("read `{}`: {}", path.display(), err)) +} + +fn temp_path(name: &str, extension: &str) -> PathBuf { + let mut path = std::env::temp_dir(); + let id = NEXT_FIXTURE_ID.fetch_add(1, Ordering::Relaxed); + path.push(format!( + "glagol-beta13-{}-{}-{}.{}", + std::process::id(), + id, + name, + extension, + )); + path +} + +fn temp_dir(name: &str) -> PathBuf { + let mut path = std::env::temp_dir(); + let id = NEXT_FIXTURE_ID.fetch_add(1, Ordering::Relaxed); + path.push(format!( + "glagol-beta13-{}-{}-{}", + std::process::id(), + id, + name, + )); + path +} + +fn assert_exit_code(context: &str, output: &Output, expected: i32) { + assert_eq!( + output.status.code(), + Some(expected), + "{} exit code mismatch\nstdout:\n{}\nstderr:\n{}", + context, + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); +} + +fn assert_stdout_empty(context: &str, output: &Output) { + let stdout = String::from_utf8_lossy(&output.stdout); + assert!(stdout.is_empty(), "{} wrote stdout:\n{}", context, stdout); +} diff --git a/docs/POST_BETA_ROADMAP.md b/docs/POST_BETA_ROADMAP.md index 30dfa5b..cfa2b01 100644 --- a/docs/POST_BETA_ROADMAP.md +++ b/docs/POST_BETA_ROADMAP.md @@ -348,6 +348,37 @@ Why twelfth: concrete vectors are already broad enough that parity gaps create surprising differences, and source-authored helpers can close those gaps without committing to generic collection design. +### 13. Diagnostic Catalog And Schema Policy + +Goal: document the existing diagnostic machine contract before larger tooling +or editor-facing slices depend on it. + +Work: + +- add [`docs/language/DIAGNOSTICS.md`](language/DIAGNOSTICS.md) as the beta + `slovo.diagnostic` version `1` policy +- document the S-expression and JSON encodings, required and optional fields, + severity/source/range/related-span semantics, JSON-line discipline, + source-less diagnostics, and artifact-manifest diagnostic metadata +- classify diagnostic changes as clarifying, additive, or migration-level, + with human prose remaining beta-flexible unless machine fields, schema + markers, codes, or golden fixture shape change intentionally +- inventory the current diagnostic codes covered by + `compiler/tests/diagnostics_contract.rs` and the matching `.diag` snapshots +- keep LSP/watch, SARIF, daemon protocols, stable Markdown schema, stable + `1.0.0` diagnostics freeze, and runtime/source-language changes out of scope + +Released in `1.0.0-beta.13`: +[`docs/language/DIAGNOSTICS.md`](language/DIAGNOSTICS.md) now defines the beta +diagnostic schema policy and catalogs the current golden diagnostic codes. The +release is documentation/tooling policy only; it does not change Glagol +diagnostic output, the source language, runtime, stdlib/API surface, or +ABI/layout behavior. + +Why thirteenth: diagnostics already have a machine schema and broad golden +coverage, but the compatibility policy and current code inventory need one +central reference before future tooling or migration work builds on them. + ## Stable `1.0.0` Gate Slovo should not become stable until all of these are true: diff --git a/docs/compiler/RELEASE_NOTES.md b/docs/compiler/RELEASE_NOTES.md index cc373be..bc18023 100644 --- a/docs/compiler/RELEASE_NOTES.md +++ b/docs/compiler/RELEASE_NOTES.md @@ -10,11 +10,45 @@ integration/readiness release, not the first real beta. ## Unreleased -Next scoped Glagol work is expected to continue after the `1.0.0-beta.12` -concrete vector query and prefix parity update. +Next scoped Glagol work is expected to continue after the `1.0.0-beta.13` +diagnostic catalog and schema policy hardening update. No unreleased compiler scope is committed here yet. +## 1.0.0-beta.13 + +Release label: `1.0.0-beta.13` + +Release date: 2026-05-22 + +Release state: diagnostic catalog and schema policy hardening update + +### Summary + +The beta.13 compiler-side contract is tooling/docs-only diagnostics policy +hardening. It keeps emitted diagnostic machine shapes stable while making the +schema name/version a single compiler constant source and gating the current +policy with focused structural tests. + +- Bump the `glagol` compiler package version to `1.0.0-beta.13`. +- Centralize the `slovo.diagnostic` schema name and version used by + S-expression rendering and newline-delimited JSON diagnostics, and use the + same schema version in artifact manifest diagnostics metadata. +- Add focused `diagnostics_schema_beta13` coverage for parse, check, + formatter, test-runner, project, source-less usage, toolchain, and manifest + diagnostic policy across S-expression and `--json-diagnostics` outputs. +- Require artifact manifests to keep recording diagnostics schema version, + diagnostics encoding, and project diagnostic counts deterministically. +- Run the focused beta.13 diagnostics schema test in `scripts/release-gate.sh` + before the full compiler test suite. + +### Explicit Deferrals + +This release does not implement LSP, watch mode, SARIF, daemon protocols, +stable human diagnostic text, a stable Markdown schema, generic collections, +generic vectors, maps, sets, runtime changes, ABI changes, source-language +expansion, standard-library/API expansion, or performance claims. + ## 1.0.0-beta.12 Release label: `1.0.0-beta.12` diff --git a/docs/compiler/ROADMAP.md b/docs/compiler/ROADMAP.md index b349110..5662f32 100644 --- a/docs/compiler/ROADMAP.md +++ b/docs/compiler/ROADMAP.md @@ -22,8 +22,8 @@ general-purpose beta release. A Glagol feature is done only when it has parser/lowerer support, checker behavior, diagnostics for invalid forms, backend behavior or explicit unsupported diagnostics, and tests. -Current stage: `1.0.0-beta.12`, released on 2026-05-22 as a concrete vector -query and prefix parity update. It keeps the `1.0.0-beta` +Current stage: `1.0.0-beta.13`, released on 2026-05-22 as a diagnostic +catalog and schema policy hardening update. It keeps the `1.0.0-beta` language/compiler support baseline and includes the `1.0.0-beta.1` tooling hardening release, the `1.0.0-beta.2` runtime/resource foundation release, the `1.0.0-beta.3` standard-library stabilization release, @@ -51,15 +51,19 @@ normalization. The beta.12 stdlib/helper parity slice adds source-authored coverage for `std.vec_i64.count_of`, `std.vec_i64.starts_with`, `std.vec_i64.without_prefix`, `std.vec_i64.ends_with`, `std.vec_i64.without_suffix`, and `std.vec_f64.count_of` without changing -source-language runtime behavior. +source-language runtime behavior. The beta.13 tooling/docs-only diagnostics +slice centralizes the `slovo.diagnostic` schema name/version constants and +adds structural gates for S-expression diagnostics, `--json-diagnostics`, and +artifact-manifest diagnostics metadata without changing emitted machine +diagnostic shape. -Next stage target: post-`1.0.0-beta.12` developer-experience, -diagnostics-schema, package, and collection/generic planning. Generic vectors, -maps, sets, generic stdlib dispatch, runtime collection changes, collection -unification, stable Markdown schema, LSP/watch protocols, SARIF/daemon -protocols, re-exports/globs/hierarchical modules, diagnostics schema policy, -registry semantics, mutable vectors, slice/view APIs, new runtime names, -iterators, performance claims, ABI/layout stability, and a stable stdlib/API +Next stage target: post-`1.0.0-beta.13` developer-experience, package, and +collection/generic planning. Generic vectors, generic collections, maps, sets, +generic stdlib dispatch, runtime collection changes, collection unification, +stable human diagnostic text, stable Markdown schema, LSP/watch protocols, +SARIF/daemon protocols, re-exports/globs/hierarchical modules, registry +semantics, mutable vectors, slice/view APIs, new runtime names, iterators, +performance claims, ABI/layout stability, and a stable stdlib/API compatibility freeze remain unimplemented until a later scoped contract promotes them explicitly. diff --git a/docs/language/DIAGNOSTICS.md b/docs/language/DIAGNOSTICS.md new file mode 100644 index 0000000..501e83b --- /dev/null +++ b/docs/language/DIAGNOSTICS.md @@ -0,0 +1,253 @@ +# 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. + +## 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` diff --git a/docs/language/MIGRATION_POLICY.md b/docs/language/MIGRATION_POLICY.md index 14c0572..e2a7bee 100644 --- a/docs/language/MIGRATION_POLICY.md +++ b/docs/language/MIGRATION_POLICY.md @@ -46,10 +46,18 @@ Slovo must not silently repurpose an old supported form with new meaning. ## Diagnostic And Tooling Changes -Diagnostic code or machine-shape changes require a documented schema migration -and matching Glagol snapshot updates. Formatter changes require before/after -fixture coverage so agents can see whether the change is additive, -clarifying, or migration-level. +[`docs/language/DIAGNOSTICS.md`](DIAGNOSTICS.md) is the beta policy for +`slovo.diagnostic` version `1`. Human-readable diagnostic prose remains +beta-flexible, but machine fields, schema/version markers, diagnostic codes, +source/span/range semantics, JSON-line discipline, related-span shape, and +artifact-manifest diagnostic metadata are compatibility-sensitive. + +Diagnostic code or machine-shape changes require the compatibility class +defined in [`docs/language/DIAGNOSTICS.md`](DIAGNOSTICS.md), a documented +schema or catalog migration when the change is migration-level, and matching +Glagol snapshot updates. Formatter changes require before/after fixture +coverage so agents can see whether the change is additive, clarifying, or +migration-level. Native executable output, package layout, stable ABI/layout promises, stable standard-runtime printing APIs, and raw-memory/FFI contracts remain outside the diff --git a/docs/language/RELEASE_NOTES.md b/docs/language/RELEASE_NOTES.md index 63f203a..3ba1267 100644 --- a/docs/language/RELEASE_NOTES.md +++ b/docs/language/RELEASE_NOTES.md @@ -8,7 +8,7 @@ Historical `exp-*` releases listed here are experimental maturity milestones. The pushed tag `v2.0.0-beta.1` is historical. It is now documented as an experimental integration/readiness release, not as a beta maturity claim. -The current release is `1.0.0-beta.12`, published on 2026-05-22. It keeps the +The current release is `1.0.0-beta.13`, published on 2026-05-22. It keeps the `1.0.0-beta` language surface, includes the first post-beta tooling/install hardening bundle from `1.0.0-beta.1`, and adds the first runtime/resource foundation bundle from `1.0.0-beta.2` plus the first standard-library @@ -22,12 +22,44 @@ collection alias unification and generic reservation slice from `1.0.0-beta.9`, the first developer-experience API discovery slice from `1.0.0-beta.10`, and the local package API documentation extension from `1.0.0-beta.11`, plus the concrete vector query and prefix parity slice from -`1.0.0-beta.12`. +`1.0.0-beta.12`, and the diagnostic catalog and schema policy slice from +`1.0.0-beta.13`. ## Unreleased No unreleased language scope is committed here yet. +## 1.0.0-beta.13 + +Release label: `1.0.0-beta.13` + +Release name: Diagnostic Catalog And Schema Policy + +Release date: 2026-05-22 + +Status: released beta documentation/tooling policy update on the +`1.0.0-beta` language baseline. + +`1.0.0-beta.13` documents the existing diagnostic machine contract without +changing the source language, runtime, standard library, CLI, or diagnostic +output shape: + +- Adds [`docs/language/DIAGNOSTICS.md`](DIAGNOSTICS.md) as the beta + `slovo.diagnostic` version `1` policy. +- Documents the S-expression and JSON relationship, required and optional + fields, severity/source/range/related-span semantics, JSON-line discipline, + source-less diagnostics, and artifact-manifest diagnostic metadata. +- Defines compatibility and migration classes for diagnostic machine fields + and codes. Human-readable prose remains beta-flexible unless a release + intentionally changes machine fields, schema/version markers, codes, or + golden fixture shape. +- Inventories the 114 current diagnostic codes covered by the 358-snapshot + golden diagnostics contract in `compiler/tests/diagnostics_contract.rs`. + +This release does not add LSP/watch behavior, SARIF, daemon protocols, stable +Markdown schema, stable `1.0.0` diagnostics freeze, source-language/runtime +changes, stdlib/API changes, or ABI/layout promises. + ## 1.0.0-beta.12 Release label: `1.0.0-beta.12` diff --git a/docs/language/ROADMAP.md b/docs/language/ROADMAP.md index 69f44ca..10c9a99 100644 --- a/docs/language/ROADMAP.md +++ b/docs/language/ROADMAP.md @@ -10,10 +10,10 @@ Long-horizon planning lives in release train from the historical `v2.0.0-beta.1` tag toward and beyond the first real general-purpose beta Slovo contract. -Current stage: `1.0.0-beta.12`, released on 2026-05-22 as a post-beta concrete -vector query and prefix parity update. It keeps the `1.0.0-beta` language -contract and -includes the `1.0.0-beta.1` tooling hardening release, the `1.0.0-beta.2` +Current stage: `1.0.0-beta.13`, released on 2026-05-22 as a post-beta +diagnostic catalog and schema policy update. It keeps the `1.0.0-beta` +language contract and includes the `1.0.0-beta.1` tooling hardening release, +the `1.0.0-beta.2` runtime/resource foundation release, the `1.0.0-beta.3` standard-library stabilization release, the `1.0.0-beta.4` language-usability diagnostics release, the `1.0.0-beta.5` package/workspace discipline release, and a narrow @@ -31,23 +31,25 @@ exported struct fields, exported enum variants/payload types, non-export filtering, and module-local alias normalization, plus source-authored concrete vector helper parity: `std.vec_i64` gains `count_of`, `starts_with`, `without_prefix`, `ends_with`, and `without_suffix`, and `std.vec_f64` gains -`count_of`. JSON parsing, recursive JSON values, executable generics, generic -aliases, -parameterized aliases, cross-module alias visibility, maps/sets, traits, -inference, monomorphization, iterators, runtime changes for generic +`count_of`, plus [`docs/language/DIAGNOSTICS.md`](DIAGNOSTICS.md) as the beta +`slovo.diagnostic` version `1` policy and current golden diagnostic code +catalog. JSON parsing, recursive JSON values, executable generics, generic +aliases, parameterized aliases, cross-module alias visibility, maps/sets, +traits, inference, monomorphization, iterators, runtime changes for generic collections, DNS, TLS, UDP, async IO, non-loopback binding, HTTP frameworks, rich host-error ADTs, stable ABI/layout, stable Markdown schema, stable stdlib/API compatibility freeze, LSP/watch, SARIF/daemon protocols, -diagnostics schema policy, re-exports/globs/hierarchical modules, mutable -vectors, slice/view APIs, new runtime names, performance claims, and package -registry semantics remain deferred. +stable `1.0.0` diagnostics freeze, re-exports/globs/hierarchical modules, +mutable vectors, slice/view APIs, new runtime names, performance claims, and +package registry semantics remain deferred. -Next stage target: continue after `1.0.0-beta.12` from developer-experience, -diagnostics, package, and reserved generic/map/set planning without claiming -executable generics, an LSP/watch protocol, stable Markdown schema, registry -semantics, or stable standard-library/API compatibility freeze, mutable -vectors, slice/view APIs, new runtime names, performance claims, maps/sets, or -iterators until the exact scope is frozen from the manifest and roadmap. +Next stage target: continue after `1.0.0-beta.13` from developer-experience, +package, and reserved generic/map/set planning without claiming executable +generics, an LSP/watch protocol, SARIF/daemon protocol, stable Markdown +schema, registry semantics, stable `1.0.0` diagnostics freeze, or stable +standard-library/API compatibility freeze, mutable vectors, slice/view APIs, +new runtime names, performance claims, maps/sets, or iterators until the exact +scope is frozen from the manifest and roadmap. The final experimental precursor scope is `exp-125`, defined in `.llm/EXP_125_UNSIGNED_U32_U64_NUMERIC_AND_STDLIB_BREADTH_ALPHA.md`. Its @@ -3015,8 +3017,9 @@ exp-20 f64 numeric primitive alpha decisions: ## Phase 8: Improve v1 Tooling Contracts -- [x] Define `slovo.diagnostic` version `1` as the stable v1 machine - diagnostic schema. +- [x] Define `slovo.diagnostic` version `1` as the current v1 machine + diagnostic schema, with beta policy and catalog details in + [`docs/language/DIAGNOSTICS.md`](DIAGNOSTICS.md). - [x] Define the v1 direction for preserving source spans through LLVM IR emission while deferring stable debug metadata and source-map files. - [x] Define `slovo.artifact-manifest` version `1` for compiler outputs and diff --git a/docs/language/SPEC-v1.md b/docs/language/SPEC-v1.md index e936ed7..b8726bf 100644 --- a/docs/language/SPEC-v1.md +++ b/docs/language/SPEC-v1.md @@ -4,7 +4,8 @@ Status: living beta contract for `1.0.0-beta`, with the post-beta `1.0.0-beta.1` tooling/install update, `1.0.0-beta.2` runtime/resource foundation update, `1.0.0-beta.10` developer-experience API discovery update, `1.0.0-beta.11` local package API documentation update, and `1.0.0-beta.12` -concrete vector query and prefix parity update. The language contract integrates +concrete vector query and prefix parity update, and `1.0.0-beta.13` +diagnostic catalog and schema policy update. The language contract integrates promoted language slices through `exp-125` and the historical publication baseline through `exp-123`. `1.0.0-beta` is the first real general-purpose beta release. `exp-125` completed the unsigned numeric and stdlib breadth @@ -148,6 +149,14 @@ Current v1 release surface and explicit experimental targets: executable generics, maps/sets, iterators, mutable vectors, slice/view APIs, new runtime names, ABI/layout stability, performance claims, or a stable stdlib API freeze +- `1.0.0-beta.13` diagnostic catalog and schema policy target: + [`docs/language/DIAGNOSTICS.md`](DIAGNOSTICS.md) documents the existing + `slovo.diagnostic` version `1` beta policy, S-expression/JSON relationship, + required and optional fields, JSON-line discipline, source-less diagnostics, + artifact-manifest diagnostic metadata, compatibility/migration classes, and + current golden diagnostic code catalog; this is docs/tooling policy only, not + a source-language/runtime change, LSP/watch contract, SARIF/daemon protocol, + stable Markdown schema, or stable `1.0.0` diagnostics freeze - `exp-1` owned runtime strings: compiler-known `std.string.concat` accepts two `string` values and returns an immutable runtime-owned `string`; existing string equality, length, printing, locals, parameters, returns, and calls work @@ -1345,6 +1354,31 @@ dispatch, maps, sets, iterators, mutable vectors, slice/view APIs, new runtime names, stable ABI/layout promises, performance claims, or a stable stdlib API freeze. +### 4.4.10 Post-Beta Diagnostic Catalog And Schema Policy + +Status: released in `1.0.0-beta.13` as a docs/tooling policy update for the +existing diagnostic surface. + +`1.0.0-beta.13` adds +[`docs/language/DIAGNOSTICS.md`](DIAGNOSTICS.md) as the central beta policy for +`slovo.diagnostic` version `1`. The document records the S-expression and JSON +encodings, source-attached and source-less diagnostic field rules, +severity/source/range/related-span semantics, JSON-line discipline, +artifact-manifest diagnostic metadata, diagnostic compatibility classes, and +the current golden diagnostic code catalog. + +The source language, typed core, runtime, standard library, compiler-known +runtime names, ABI/layout behavior, CLI behavior, and diagnostic output shape +are unchanged by this target. Human-readable diagnostic prose remains +beta-flexible; schema names, versions, machine fields, diagnostic codes, source +span/range semantics, JSON-line discipline, and golden fixture shape change +only intentionally through the documented migration policy. + +This target explicitly does not add LSP/watch behavior, SARIF output, daemon +protocols, stable Markdown schema, stable `1.0.0` diagnostics freeze, +source-map/debug-metadata contracts, localized diagnostic text, automated +machine fix-its, or a compiler-emitted diagnostic catalog artifact. + ## 4.5 v2.0.0-beta.1 Experimental Integration Readiness Status: current experimental Slovo-side release contract, released 2026-05-17. @@ -5479,10 +5513,14 @@ requirement. Slovo v1 keeps the v0 rule that diagnostics have both human-readable and machine-readable forms generated from one compiler diagnostic object. Human -rendering may choose layout and surrounding prose, but the machine form is a -stable tool API. +rendering may choose layout and surrounding prose. The machine form is the +tooling contract, with beta compatibility and migration rules defined by the +diagnostics policy rather than by human text. -The v1 machine diagnostic schema is `slovo.diagnostic` version `1`. +The v1 machine diagnostic schema is `slovo.diagnostic` version `1`. As of +`1.0.0-beta.13`, the detailed beta schema policy, JSON-line discipline, +source-less diagnostic policy, compatibility classes, and current code catalog +live in [`docs/language/DIAGNOSTICS.md`](DIAGNOSTICS.md). Every machine diagnostic emitted for a v1 compiler boundary must include: @@ -5544,8 +5582,10 @@ Diagnostic locations are derived from original source text, not from formatter output. The schema version belongs in every machine diagnostic and every golden -diagnostic fixture. Future machine-shape changes require a documented schema -migration and updated Glagol snapshots. +diagnostic fixture. Human-readable diagnostic prose remains beta-flexible, but +future machine-shape or code changes require the compatibility class defined in +[`docs/language/DIAGNOSTICS.md`](DIAGNOSTICS.md) and updated Glagol snapshots +when the current golden contract changes. ### 9.2 Artifact Manifest Schema diff --git a/docs/language/STDLIB_API.md b/docs/language/STDLIB_API.md index 4b7e24a..ccfb8e5 100644 --- a/docs/language/STDLIB_API.md +++ b/docs/language/STDLIB_API.md @@ -6,7 +6,7 @@ Do not edit this file by hand. ## Stability Tiers - `beta-supported`: exported from `lib/std` and covered by source-search, promotion, or facade gates in the current beta line. -- `experimental`: not used for exported `lib/std` helpers in `1.0.0-beta.12`; future releases may mark new helpers this way before they graduate. +- `experimental`: not used for exported `lib/std` helpers in `1.0.0-beta.13`; future releases may mark new helpers this way before they graduate. - `internal`: helper names that are not exported from their module; they are intentionally omitted from this catalog. The catalog is a beta API discovery aid, not a stable `1.0.0` standard-library freeze. diff --git a/scripts/release-gate.sh b/scripts/release-gate.sh index e9064da..668c129 100755 --- a/scripts/release-gate.sh +++ b/scripts/release-gate.sh @@ -63,6 +63,7 @@ fi cd "${compiler_dir}" cargo fmt --check +cargo test --test diagnostics_schema_beta13 # Full cargo test includes unignored integration gates such as dx_v1_7, # beta_v2_0_0_beta_1, and beta_1_0_0. cargo test