From b241a8a812ff2e0673f79e91cbc540426271a1cc Mon Sep 17 00:00:00 2001 From: sanjin Date: Sat, 23 May 2026 03:23:10 +0200 Subject: [PATCH] Release 1.0.0-beta.24 package manifest discipline --- ...FEST_IDENTITY_AND_DEPENDENCY_DISCIPLINE.md | 79 ++++++ .llm/reviews/BETA_24_RELEASE_REVIEW.md | 66 +++++ README.md | 28 ++- compiler/Cargo.lock | 2 +- compiler/Cargo.toml | 2 +- compiler/src/project.rs | 102 ++++++-- .../package_workspace_discipline_beta24.rs | 229 ++++++++++++++++++ docs/POST_BETA_ROADMAP.md | 10 + docs/compiler/RELEASE_NOTES.md | 31 +++ docs/compiler/ROADMAP.md | 14 +- docs/language/DIAGNOSTICS.md | 41 ++++ docs/language/PACKAGES.md | 12 + docs/language/RELEASE_NOTES.md | 32 ++- docs/language/ROADMAP.md | 19 +- docs/language/STDLIB_API.md | 2 +- scripts/release-gate.sh | 1 + 16 files changed, 636 insertions(+), 34 deletions(-) create mode 100644 .llm/BETA_24_PACKAGE_MANIFEST_IDENTITY_AND_DEPENDENCY_DISCIPLINE.md create mode 100644 .llm/reviews/BETA_24_RELEASE_REVIEW.md create mode 100644 compiler/tests/package_workspace_discipline_beta24.rs diff --git a/.llm/BETA_24_PACKAGE_MANIFEST_IDENTITY_AND_DEPENDENCY_DISCIPLINE.md b/.llm/BETA_24_PACKAGE_MANIFEST_IDENTITY_AND_DEPENDENCY_DISCIPLINE.md new file mode 100644 index 0000000..7b46a47 --- /dev/null +++ b/.llm/BETA_24_PACKAGE_MANIFEST_IDENTITY_AND_DEPENDENCY_DISCIPLINE.md @@ -0,0 +1,79 @@ +# 1.0.0-beta.24 Package Manifest Identity And Dependency Discipline + +## Scope + +`1.0.0-beta.24` is a package/workspace discipline hardening slice for local +manifest diagnostics. It tightens how package manifest identity and local +dependency tables are reported when users write ambiguous or invalid manifest +keys. + +This release changes diagnostics only. It does not change the Slovo source +language, typed core, runtime behavior, standard-library helper surface, +compiler-known runtime names, package graph semantics, ABI/layout policy, or +artifact/Markdown schema stability guarantees. + +## Contract + +The beta package model remains a closed local workspace model: + +- package manifests declare `[package]` identity metadata and optional + `[dependencies]` local path records +- dependency keys name local packages and must match the target package name +- dependency paths remain local path records under the existing workspace and + package boundary checks + +The beta24 diagnostic hardening makes these manifest errors explicit: + +- duplicate keys in package manifests report `PackageManifestInvalid` +- invalid dependency keys report `InvalidPackageDependencyName` +- duplicate dependency keys report `DuplicatePackageDependencyName` + +These diagnostics are part of the beta package/workspace discipline surface. +They do not promote a package manager, resolver, lockfile, registry, publish +flow, or stable package ABI/layout. + +## Non-Scope + +This scope does not add: + +- remote registry behavior +- lockfiles +- semantic-version solving +- package publishing +- optional, dev, target, or feature-gated dependencies +- build scripts or package archives +- stable package ABI/layout +- stable package manager behavior +- source-language syntax or semantics +- runtime behavior or runtime C capabilities +- standard-library helpers or stdlib behavior +- compiler-known `std.*` runtime names +- stable artifact-manifest or Markdown schema guarantees +- performance claims + +## Acceptance Criteria + +- `docs/language/PACKAGES.md` documents duplicate package-manifest keys, + invalid dependency keys, and duplicate dependency keys as explicit beta + diagnostics. +- Language and compiler release notes describe beta24 as package manifest + identity/dependency diagnostic hardening only. +- Language and compiler roadmaps record beta24 as a local package/workspace + diagnostics slice with all registry, lockfile, semver, publishing, + optional/dev/target dependency, ABI/layout, language, runtime, and stdlib + work deferred. +- README and the post-beta roadmap identify `1.0.0-beta.24` as the current + package/workspace discipline hardening slice. +- Glagol is versioned as `1.0.0-beta.24`. +- `compiler/tests/package_workspace_discipline_beta24.rs` covers the focused + package manifest/dependency diagnostics and a positive local dependency + workspace. +- `scripts/release-gate.sh` runs the focused beta24 test. + +## Suggested Gates + +```bash +git diff --check +cargo fmt --check +cargo test --test package_workspace_discipline_beta24 +``` diff --git a/.llm/reviews/BETA_24_RELEASE_REVIEW.md b/.llm/reviews/BETA_24_RELEASE_REVIEW.md new file mode 100644 index 0000000..1ba8774 --- /dev/null +++ b/.llm/reviews/BETA_24_RELEASE_REVIEW.md @@ -0,0 +1,66 @@ +# Beta24 Release Review + +Verdict: PASS. + +## Findings + +No blocking or non-blocking findings in the reviewed scope. + +## Scope Reviewed + +- `compiler/src/project.rs` package manifest diagnostics. +- `compiler/tests/package_workspace_discipline_beta24.rs`. +- `scripts/release-gate.sh` beta24 focused gate entry. +- `compiler/Cargo.toml` and `compiler/Cargo.lock` version bump. +- `.llm/BETA_24_PACKAGE_MANIFEST_IDENTITY_AND_DEPENDENCY_DISCIPLINE.md`. +- README, language package/diagnostic/release/roadmap docs, compiler release/roadmap docs, and post-beta roadmap updates. + +## Acceptance Checklist + +- PASS: Duplicate package manifest keys now use package-scoped diagnostics through `set_manifest_key(..., "PackageManifestInvalid", "package")` for package identity keys. See `compiler/src/project.rs:1505` through `compiler/src/project.rs:1545` and `compiler/src/project.rs:1752` through `compiler/src/project.rs:1771`. +- PASS: Invalid dependency keys are rejected before graph validation with `InvalidPackageDependencyName`. See `compiler/src/project.rs:1556` through `compiler/src/project.rs:1572`. +- PASS: Duplicate dependency keys are rejected before graph validation with `DuplicatePackageDependencyName`, and invalid/duplicate dependency records are not added to the dependency graph. See `compiler/src/project.rs:1573` through `compiler/src/project.rs:1593`. +- PASS: Existing local-path-only dependency parsing and later dependency key/name matching remain in place. See `compiler/src/project.rs:1594` through `compiler/src/project.rs:1601`, `compiler/src/project.rs:1743` through `compiler/src/project.rs:1749`, and `compiler/src/project.rs:2970` through `compiler/src/project.rs:3005`. +- PASS: Focused tests cover duplicate package keys, invalid dependency keys, duplicate dependency keys, and a clean local dependency workspace. See `compiler/tests/package_workspace_discipline_beta24.rs:11` through `compiler/tests/package_workspace_discipline_beta24.rs:109`. +- PASS: `scripts/release-gate.sh` runs the beta24 focused test before full `cargo test`. See `scripts/release-gate.sh:68` through `scripts/release-gate.sh:82`. +- PASS: The compiler package version is aligned to `1.0.0-beta.24` in both Cargo files. See `compiler/Cargo.toml:3` and `compiler/Cargo.lock:7`. +- PASS: The beta24 contract defines diagnostics-only scope, explicit non-scope, acceptance criteria, and focused gates. See `.llm/BETA_24_PACKAGE_MANIFEST_IDENTITY_AND_DEPENDENCY_DISCIPLINE.md:5` through `.llm/BETA_24_PACKAGE_MANIFEST_IDENTITY_AND_DEPENDENCY_DISCIPLINE.md:79`. +- PASS: README identifies `1.0.0-beta.24` as current and describes the package manifest/dependency diagnostic hardening scope without claiming package-manager, runtime, language, or stdlib changes. See `README.md:9` and `README.md:27` through `README.md:50`. +- PASS: `docs/language/PACKAGES.md` documents duplicate package manifest keys, invalid dependency keys, and duplicate dependency keys as beta package/workspace diagnostics. See `docs/language/PACKAGES.md:59` through `docs/language/PACKAGES.md:73` and `docs/language/PACKAGES.md:111` through `docs/language/PACKAGES.md:130`. +- PASS: `docs/language/DIAGNOSTICS.md` includes the new package/workspace diagnostic codes in the project/workspace catalog. See `docs/language/DIAGNOSTICS.md:240` through `docs/language/DIAGNOSTICS.md:279`. +- PASS: Language and compiler release notes describe beta24 as package manifest identity/dependency diagnostic hardening only. See `docs/language/RELEASE_NOTES.md:44` through `docs/language/RELEASE_NOTES.md:68` and `docs/compiler/RELEASE_NOTES.md:15` through `docs/compiler/RELEASE_NOTES.md:42`. +- PASS: Language/compiler roadmaps and post-beta roadmap mark beta24 as the current local package/workspace diagnostics slice with registry, lockfile, semver, publishing, optional/dev/target dependencies, ABI/layout, language, runtime, and stdlib work deferred. See `docs/language/ROADMAP.md:11` through `docs/language/ROADMAP.md:33`, `docs/compiler/ROADMAP.md:24` through `docs/compiler/ROADMAP.md:38`, `docs/compiler/ROADMAP.md:722` through `docs/compiler/ROADMAP.md:728`, and `docs/POST_BETA_ROADMAP.md:185` through `docs/POST_BETA_ROADMAP.md:191`. + +## Verification Commands + +Ran: + +```bash +git diff --check +cargo fmt --check +cargo test --test package_workspace_discipline_beta24 +cargo test --test project_mode workspace_package_boundaries_are_diagnostics +cargo test --test project_mode +rg -n "beta\\.23|beta23|beta\\.24|beta24|1\\.0\\.0-beta\\.2[0-9]|current|Release state|Last updated" README.md docs/language/PACKAGES.md docs/language/DIAGNOSTICS.md docs/language/RELEASE_NOTES.md docs/language/ROADMAP.md docs/compiler/RELEASE_NOTES.md docs/compiler/ROADMAP.md docs/POST_BETA_ROADMAP.md .llm/BETA_24_PACKAGE_MANIFEST_IDENTITY_AND_DEPENDENCY_DISCIPLINE.md compiler/Cargo.toml compiler/Cargo.lock scripts/release-gate.sh +``` + +Result: all executed checks passed. The focused beta24 test reported 4 passed tests. The full `project_mode` suite reported 36 passed tests. + +## Final Gate Disposition + +After this review, the controller ran the full release gate: + +```bash +./scripts/release-gate.sh +``` + +Result: PASS. The gate completed docs/catalog checks, focused beta13-beta24 +tests, full `cargo test`, ignored promotion smoke, ignored binary smoke, and +ignored LLVM smoke. + +## Residual Risks + +- No release-blocking residual risks remain. The release gate regenerated and + checked the standard-library API catalog; publication PDFs were not + regenerated because this slice does not touch paper sources, and the gate + verified that the required PDF artifacts are present. diff --git a/README.md b/README.md index 7439916..19cb2d6 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.23`. +Current release: `1.0.0-beta.24`. ## Repository Layout @@ -24,7 +24,7 @@ scripts/ local release and document tooling ## Beta Scope -`1.0.0-beta.23` keeps the `1.0.0-beta` language baseline, includes the +`1.0.0-beta.24` 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 @@ -46,7 +46,9 @@ the `1.0.0-beta.17` JSON primitive scalar parsing foundation, the `1.0.0-beta.21` JSON document scalar parsing foundation, plus the `1.0.0-beta.22` run manifest and execution report hardening slice, and the `1.0.0-beta.23` standard-library stability tier ledger and catalog alignment -slice. +slice, and the `1.0.0-beta.24` package manifest identity and local dependency +diagnostic hardening slice. + The language baseline supports practical local command-line, file, and loopback-network programs with: @@ -196,6 +198,14 @@ schema, stable Markdown schema, stable ABI/layout, or stable stdlib/API freeze changes. The generated catalog and release gate now expose and check tier metadata. +The `1.0.0-beta.24` package manifest identity and local dependency discipline +slice tightens local manifest diagnostics only: duplicate package manifest +keys, invalid dependency keys, and duplicate dependency keys are explicit beta +package/workspace diagnostics. It does not add remote registries, lockfiles, +semantic-version solving, package publishing, optional/dev/target +dependencies, stable package ABI/layout, source-language changes, runtime +changes, or standard-library changes. + Still deferred before stable: executable generics, generic aliases, maps/sets, broad package registry semantics, stable artifact-manifest schema, stable Markdown schema, stable stdlib/API compatibility freeze, DNS/TLS/async @@ -330,6 +340,18 @@ package/dependency summary, new workspace templates declare local-package rules. Remote registries, lockfiles, semantic-version solving, package publishing, and stable package ABI/layout remain deferred. +## 1.0.0-beta.24 Package Manifest Identity And Dependency Discipline + +The `1.0.0-beta.24` release keeps the local package model closed and local. It +hardens diagnostics for manifest identity/dependency mistakes only: duplicate +package manifest keys, invalid dependency keys, and duplicate dependency keys +are reported explicitly. + +This scope does not add a remote registry, lockfile, semantic-version solver, +package publishing flow, optional/dev/target dependencies, stable package +ABI/layout, source-language behavior, runtime behavior, or standard-library +behavior. + ## 1.0.0-beta.6 Networking Foundation The `1.0.0-beta.6` release adds a narrow blocking loopback TCP foundation: diff --git a/compiler/Cargo.lock b/compiler/Cargo.lock index f164ca9..b7612e4 100644 --- a/compiler/Cargo.lock +++ b/compiler/Cargo.lock @@ -4,4 +4,4 @@ version = 3 [[package]] name = "glagol" -version = "1.0.0-beta.23" +version = "1.0.0-beta.24" diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml index 3a0b760..297e2bd 100644 --- a/compiler/Cargo.toml +++ b/compiler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "glagol" -version = "1.0.0-beta.23" +version = "1.0.0-beta.24" edition = "2021" description = "Glagol, the first compiler for the Slovo language" license = "MIT OR Apache-2.0" diff --git a/compiler/src/project.rs b/compiler/src/project.rs index 008d62e..09a171b 100644 --- a/compiler/src/project.rs +++ b/compiler/src/project.rs @@ -1124,7 +1124,16 @@ fn parse_manifest(path: PathBuf, source: String) -> Result set_manifest_key(&file, &mut errors, &mut name, parsed, line.span, "name"), + "name" => set_manifest_key( + &file, + &mut errors, + &mut name, + parsed, + line.span, + "name", + "ProjectManifestInvalid", + "project", + ), "source_root" => set_manifest_key( &file, &mut errors, @@ -1132,8 +1141,19 @@ fn parse_manifest(path: PathBuf, source: String) -> Result set_manifest_key( + &file, + &mut errors, + &mut entry, + parsed, + line.span, + "entry", + "ProjectManifestInvalid", + "project", ), - "entry" => set_manifest_key(&file, &mut errors, &mut entry, parsed, line.span, "entry"), _ => errors.push( Diagnostic::new( &file, @@ -1412,6 +1432,7 @@ fn parse_package_manifest( let mut source_root = None::; let mut entry = None::; let mut dependencies = Vec::new(); + let mut dependency_keys = BTreeSet::::new(); for line in manifest_lines(&source) { let trimmed = line.text.trim(); @@ -1489,6 +1510,8 @@ fn parse_package_manifest( parsed, line.span, "name", + "PackageManifestInvalid", + "package", ), "version" => set_manifest_key( &file, @@ -1497,6 +1520,8 @@ fn parse_package_manifest( parsed, line.span, "version", + "PackageManifestInvalid", + "package", ), "source_root" => set_manifest_key( &file, @@ -1505,6 +1530,8 @@ fn parse_package_manifest( parsed, line.span, "source_root", + "PackageManifestInvalid", + "package", ), "entry" => set_manifest_key( &file, @@ -1513,6 +1540,8 @@ fn parse_package_manifest( parsed, line.span, "entry", + "PackageManifestInvalid", + "package", ), other => errors.push( Diagnostic::new( @@ -1524,21 +1553,54 @@ fn parse_package_manifest( ), } } - "dependencies" => match parse_dependency_path(value) { - Some(path) => dependencies.push(PackageDependency { - key: key.to_string(), - path, - span: line.span, - }), - None => errors.push( - Diagnostic::new( - &file, - "UnsupportedDependency", - "workspace dependencies must use `{ path = \"...\" }` local path records only", - ) - .with_span(line.span), - ), - }, + "dependencies" => { + let valid_key = if is_project_name(key) { + true + } else { + errors.push( + Diagnostic::new( + &file, + "InvalidPackageDependencyName", + format!( + "package dependency name `{}` must start with `a-z` and contain only `a-z`, `0-9`, and `-`", + key + ), + ) + .with_span(line.span), + ); + false + }; + let unique_key = if dependency_keys.insert(key.to_string()) { + true + } else { + errors.push( + Diagnostic::new( + &file, + "DuplicatePackageDependencyName", + format!("duplicate package dependency name `{}`", key), + ) + .with_span(line.span), + ); + false + }; + + match parse_dependency_path(value) { + Some(path) if valid_key && unique_key => dependencies.push(PackageDependency { + key: key.to_string(), + path, + span: line.span, + }), + Some(_) => {} + None => errors.push( + Diagnostic::new( + &file, + "UnsupportedDependency", + "workspace dependencies must use `{ path = \"...\" }` local path records only", + ) + .with_span(line.span), + ), + } + } "" | "project" => errors.push( Diagnostic::new( &file, @@ -1694,13 +1756,15 @@ fn set_manifest_key( value: String, span: Span, key: &str, + code: &'static str, + manifest_kind: &str, ) { if slot.replace(value).is_some() { errors.push( Diagnostic::new( file, - "ProjectManifestInvalid", - format!("duplicate project manifest key `{}`", key), + code, + format!("duplicate {} manifest key `{}`", manifest_kind, key), ) .with_span(span), ); diff --git a/compiler/tests/package_workspace_discipline_beta24.rs b/compiler/tests/package_workspace_discipline_beta24.rs new file mode 100644 index 0000000..96070be --- /dev/null +++ b/compiler/tests/package_workspace_discipline_beta24.rs @@ -0,0 +1,229 @@ +use std::{ + fs, + path::PathBuf, + process::{Command, Output}, + sync::atomic::{AtomicUsize, Ordering}, + time::{SystemTime, UNIX_EPOCH}, +}; + +static NEXT_WORKSPACE_ID: AtomicUsize = AtomicUsize::new(0); + +#[test] +fn duplicate_package_keys_report_package_manifest_invalid() { + let workspace = write_workspace( + "duplicate-package-key", + "[workspace]\nmembers = [\"packages/app\"]\n", + &[WorkspacePackageSpec { + member: "packages/app", + manifest: "[package]\nname = \"app\"\nname = \"other\"\nversion = \"0.1.0\"\n", + modules: &[("main", "(module main)\n\n(fn main () -> i32\n 0)\n")], + }], + ); + + let output = run_glagol([ + "--json-diagnostics".as_ref(), + "check".as_ref(), + workspace.as_os_str(), + ]); + assert_exit_code("duplicate package key", &output, 1); + assert_json_diagnostic_code("duplicate package key", &output, "PackageManifestInvalid"); + assert_json_diagnostic_code_absent("duplicate package key", &output, "ProjectManifestInvalid"); +} + +#[test] +fn invalid_dependency_key_reports_invalid_package_dependency_name() { + let workspace = write_workspace( + "invalid-dependency-key", + "[workspace]\nmembers = [\"packages/app\"]\n", + &[WorkspacePackageSpec { + member: "packages/app", + manifest: "[package]\nname = \"app\"\nversion = \"0.1.0\"\n\n[dependencies]\nBad_Name = { path = \"../util\" }\n", + modules: &[("main", "(module main)\n\n(fn main () -> i32\n 0)\n")], + }], + ); + + let output = run_glagol([ + "--json-diagnostics".as_ref(), + "check".as_ref(), + workspace.as_os_str(), + ]); + assert_exit_code("invalid dependency key", &output, 1); + assert_json_diagnostic_code( + "invalid dependency key", + &output, + "InvalidPackageDependencyName", + ); +} + +#[test] +fn duplicate_dependency_keys_report_duplicate_package_dependency_name() { + let workspace = write_workspace( + "duplicate-dependency-key", + "[workspace]\nmembers = [\"packages/app\"]\n", + &[WorkspacePackageSpec { + member: "packages/app", + manifest: "[package]\nname = \"app\"\nversion = \"0.1.0\"\n\n[dependencies]\nutil = { path = \"../util\" }\nutil = { path = \"../util-again\" }\n", + modules: &[("main", "(module main)\n\n(fn main () -> i32\n 0)\n")], + }], + ); + + let output = run_glagol([ + "--json-diagnostics".as_ref(), + "check".as_ref(), + workspace.as_os_str(), + ]); + assert_exit_code("duplicate dependency key", &output, 1); + assert_json_diagnostic_code( + "duplicate dependency key", + &output, + "DuplicatePackageDependencyName", + ); +} + +#[test] +fn valid_dependency_identity_checks_cleanly() { + let workspace = write_workspace( + "valid-dependency-identity", + "[workspace]\nmembers = [\"packages/app\", \"packages/util\"]\n", + &[ + WorkspacePackageSpec { + member: "packages/util", + manifest: "[package]\nname = \"util\"\nversion = \"0.1.0\"\n", + modules: &[( + "util", + "(module util (export answer))\n\n(fn answer () -> i32\n 42)\n", + )], + }, + WorkspacePackageSpec { + member: "packages/app", + manifest: "[package]\nname = \"app\"\nversion = \"0.1.0\"\n\n[dependencies]\nutil = { path = \"../util\" }\n", + modules: &[( + "main", + "(module main)\n\n(import util.util (answer))\n\n(fn main () -> i32\n (answer))\n", + )], + }, + ], + ); + + let output = run_glagol(["check".as_ref(), workspace.as_os_str()]); + assert_success_stdout("valid dependency identity", output, ""); +} + +struct WorkspacePackageSpec<'a> { + member: &'a str, + manifest: &'a str, + modules: &'a [(&'a str, &'a str)], +} + +fn write_workspace( + name: &str, + workspace_manifest: &str, + packages: &[WorkspacePackageSpec<'_>], +) -> PathBuf { + let root = unique_path(name); + fs::create_dir_all(&root).expect("create workspace root"); + fs::write(root.join("slovo.toml"), workspace_manifest).expect("write workspace manifest"); + + for package in packages { + let package_root = root.join(package.member); + let src = package_root.join("src"); + fs::create_dir_all(&src).expect("create workspace package src"); + fs::write(package_root.join("slovo.toml"), package.manifest) + .expect("write workspace package manifest"); + for (module, source) in package.modules { + fs::write(src.join(format!("{}.slo", module)), source) + .expect("write workspace package module"); + } + } + + root +} + +fn unique_path(name: &str) -> PathBuf { + let id = NEXT_WORKSPACE_ID.fetch_add(1, Ordering::SeqCst); + let nanos = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|duration| duration.as_nanos()) + .unwrap_or(0); + std::env::temp_dir().join(format!( + "glagol-package-workspace-discipline-beta24-{}-{}-{}-{}", + std::process::id(), + nanos, + id, + name + )) +} + +fn run_glagol(args: I) -> Output +where + I: IntoIterator, + S: AsRef, +{ + Command::new(env!("CARGO_BIN_EXE_glagol")) + .args(args) + .output() + .expect("run glagol") +} + +fn assert_success_stdout(context: &str, output: Output, expected: &str) { + assert!( + output.status.success(), + "{} failed\nstdout:\n{}\nstderr:\n{}", + context, + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + assert_eq!( + String::from_utf8_lossy(&output.stdout), + expected, + "{} stdout mismatch", + context + ); + assert!( + output.stderr.is_empty(), + "{} wrote stderr:\n{}", + context, + String::from_utf8_lossy(&output.stderr) + ); +} + +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_json_diagnostic_code(context: &str, output: &Output, expected: &str) { + let diagnostics = diagnostic_text(output); + assert!( + diagnostics.contains(&format!(r#""code":"{}""#, expected)), + "{} did not report `{}`:\n{}", + context, + expected, + diagnostics + ); +} + +fn assert_json_diagnostic_code_absent(context: &str, output: &Output, unexpected: &str) { + let diagnostics = diagnostic_text(output); + assert!( + !diagnostics.contains(&format!(r#""code":"{}""#, unexpected)), + "{} unexpectedly reported `{}`:\n{}", + context, + unexpected, + diagnostics + ); +} + +fn diagnostic_text(output: &Output) -> String { + format!( + "{}{}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ) +} diff --git a/docs/POST_BETA_ROADMAP.md b/docs/POST_BETA_ROADMAP.md index ab27869..8cceb51 100644 --- a/docs/POST_BETA_ROADMAP.md +++ b/docs/POST_BETA_ROADMAP.md @@ -182,6 +182,14 @@ local-package rules. Lockfiles, remote registries, semver solving, publishing, optional/dev/target dependencies, and stable package ABI/layout remain out of scope. +Released in `1.0.0-beta.24`: package manifest identity and dependency +diagnostics are tightened without changing the package model. Duplicate +package manifest keys, invalid dependency keys, and duplicate dependency keys +are explicit diagnostics. The slice adds no remote registry, lockfile, +semantic-version solving, package publishing, optional/dev/target +dependencies, stable package ABI/layout, source-language change, runtime +change, or standard-library change. + Why fifth: stable package rules are a prerequisite for a usable public language, but remote publishing can wait. @@ -557,6 +565,8 @@ Slovo should not become stable until all of these are true: - `lib/std` has explicit beta-supported, experimental, and internal tiers plus a later stable-tier/deprecation policy before `1.0.0` - package/workspace behavior is deterministic +- package manifest identity and dependency-key failures have explicit + diagnostics - conformance tests cover user-shaped projects - release gates are reproducible on a clean checkout - diagnostics and formatter output are stable for promoted features diff --git a/docs/compiler/RELEASE_NOTES.md b/docs/compiler/RELEASE_NOTES.md index 3621af1..2b84bc2 100644 --- a/docs/compiler/RELEASE_NOTES.md +++ b/docs/compiler/RELEASE_NOTES.md @@ -12,6 +12,37 @@ integration/readiness release, not the first real beta. No active unreleased compiler scope is documented here yet. +## 1.0.0-beta.24 + +Release label: `1.0.0-beta.24` + +Release date: 2026-05-23 + +Release state: package manifest identity and local dependency diagnostic +hardening + +### Summary + +The beta.24 compiler/tooling slice tightens local package manifest diagnostics +without changing the language, runtime, stdlib, or package graph model: + +- Bump the `glagol` compiler package version to `1.0.0-beta.24`. +- Diagnose duplicate package manifest keys explicitly. +- Diagnose invalid dependency keys explicitly. +- Diagnose duplicate dependency keys explicitly. +- Keep dependency records local-path-only and require dependency keys to match + target package names. + +### Explicit Deferrals + +This release does not implement remote registries, lockfiles, +semantic-version solving, package publishing, optional/dev/target +dependencies, feature flags, build scripts, package archives, stable package +ABI/layout, source-language syntax, runtime C capabilities, standard-library +helpers, compiler-known `std.*` runtime names, stable artifact-manifest schema +guarantees, stable Markdown schemas, LSP/watch/SARIF/daemon protocols, or +performance claims. + ## 1.0.0-beta.23 Release label: `1.0.0-beta.23` diff --git a/docs/compiler/ROADMAP.md b/docs/compiler/ROADMAP.md index b4674a7..0d0f1ab 100644 --- a/docs/compiler/ROADMAP.md +++ b/docs/compiler/ROADMAP.md @@ -21,8 +21,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.23`, released on 2026-05-23 as standard-library -stability tier ledger and catalog alignment. It keeps the +Current stage: `1.0.0-beta.24`, released on 2026-05-23 as package manifest +identity and local dependency diagnostic hardening. 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, @@ -131,6 +131,14 @@ experimental, and keeps concrete vector modules beta-supported concrete lanes without claiming generic collection stability. It adds no source-language, runtime, package, or standard-library helper surface. +The beta.24 compiler/tooling slice bumps the package version and tightens +local package manifest diagnostics. Duplicate package manifest keys, invalid +dependency keys, and duplicate dependency keys are explicit diagnostics. It +keeps the existing closed local workspace model and adds no remote registry, +lockfile, semantic-version solving, package publishing, optional/dev/target +dependencies, stable package ABI/layout, source-language behavior, runtime +behavior, or standard-library behavior. + Generic vectors, generic collections, maps, sets, generic stdlib dispatch, runtime collection changes, collection unification, stable human diagnostic text, stable artifact-manifest or Markdown schema freezes, LSP/watch @@ -714,6 +722,8 @@ Alpha is the latest compiler semantic/runtime-operation support slice. - [x] Diagnose missing packages, duplicate package names, dependency cycles, path escapes, invalid package names, invalid package versions, private visibility, and dependency key mismatches. +- [x] Diagnose duplicate package manifest keys, invalid dependency keys, and + duplicate dependency keys explicitly. - [x] Keep registries, lockfiles, semver solving, aliases/globs/re-exports, generated code, build scripts, publishing, optional/dev/target dependencies, stable package ABI, and remote dependencies deferred. diff --git a/docs/language/DIAGNOSTICS.md b/docs/language/DIAGNOSTICS.md index 501e83b..68e3f52 100644 --- a/docs/language/DIAGNOSTICS.md +++ b/docs/language/DIAGNOSTICS.md @@ -237,6 +237,47 @@ 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: diff --git a/docs/language/PACKAGES.md b/docs/language/PACKAGES.md index 03dd792..0ca4b37 100644 --- a/docs/language/PACKAGES.md +++ b/docs/language/PACKAGES.md @@ -56,6 +56,10 @@ used for solving dependency constraints. dependency paths must stay inside the workspace/package boundary after normalization and canonical path checks. +Duplicate keys in package manifests are `PackageManifestInvalid` diagnostics. +The manifest loader does not silently choose one spelling for repeated package +identity or dependency entries. + Dependencies are local path records only. The dependency key must match the target package name: @@ -63,6 +67,11 @@ target package name: mathlib = { path = "../mathlib" } ``` +Dependency keys use the same package-name shape as package identities. Invalid +dependency keys are `InvalidPackageDependencyName` diagnostics, and duplicate +dependency keys are `DuplicatePackageDependencyName` diagnostics before the +local dependency graph is accepted. + ## Imports Within a workspace, a package imports a dependency module through the package @@ -106,9 +115,12 @@ The package/workspace gate covers these user-facing error families: - missing member manifests - duplicate normalized workspace members - invalid member or dependency paths +- duplicate package manifest keys - invalid package names and versions - duplicate package names - missing local path dependencies +- invalid dependency keys +- duplicate dependency keys - dependency key/name mismatches - package dependency cycles - private cross-package imports diff --git a/docs/language/RELEASE_NOTES.md b/docs/language/RELEASE_NOTES.md index dab773a..23a149b 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.23`, published on 2026-05-23. It keeps the +The current release is `1.0.0-beta.24`, published on 2026-05-23. 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 @@ -33,12 +33,40 @@ conformance foundation from `1.0.0-beta.19`, and the string search and ASCII trim foundation from `1.0.0-beta.20`, plus the JSON document scalar parsing foundation from `1.0.0-beta.21`, and the run manifest execution-report hardening slice from `1.0.0-beta.22`, plus the standard-library stability tier -ledger and catalog alignment slice from `1.0.0-beta.23`. +ledger and catalog alignment slice from `1.0.0-beta.23`, and the package +manifest identity and local dependency diagnostic hardening slice from +`1.0.0-beta.24`. ## Unreleased No active unreleased language scope is documented here yet. +## 1.0.0-beta.24 + +Release label: `1.0.0-beta.24` + +Release name: Package Manifest Identity And Dependency Discipline + +Release date: 2026-05-23 + +Status: released beta package/workspace diagnostic hardening on the +`1.0.0-beta` language baseline. + +This release tightens diagnostics for local package manifests only. Duplicate +package manifest keys, invalid dependency keys, and duplicate dependency keys +are explicit beta package/workspace diagnostics. + +The package model remains the existing closed local workspace model: +dependencies are local path records, dependency keys must match target package +names, and package-qualified imports still resolve through the local workspace +graph. + +This release adds no remote registry, lockfile, semantic-version solving, +package publishing, optional/dev/target dependencies, stable package +ABI/layout, source-language syntax or semantics, runtime behavior, +standard-library helpers, compiler-known runtime names, stable manifest schema, +stable Markdown schema, or performance claim. + ## 1.0.0-beta.23 Release label: `1.0.0-beta.23` diff --git a/docs/language/ROADMAP.md b/docs/language/ROADMAP.md index f9e4133..7917fd0 100644 --- a/docs/language/ROADMAP.md +++ b/docs/language/ROADMAP.md @@ -8,10 +8,10 @@ Guiding rule: the manifest wins. A feature is not accepted until it has surface Long-horizon planning lives in `.llm/ROADMAP_TO_STABLE.md`. It defines the release train beyond the first real general-purpose beta Slovo contract. -Current stage: `1.0.0-beta.23`, released on 2026-05-23 as post-beta -standard-library stability tier ledger and catalog alignment. It keeps the -`1.0.0-beta` language -contract and includes the `1.0.0-beta.1` tooling hardening release, the +Current stage: `1.0.0-beta.24`, released on 2026-05-23 as post-beta +package manifest identity and local dependency diagnostic hardening. 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 @@ -29,7 +29,8 @@ scalar token parsing, `1.0.0-beta.18` JSON string token parsing, `1.0.0-beta.20` string search and ASCII trim helpers, plus `1.0.0-beta.21` JSON document scalar parsing helpers, and `1.0.0-beta.22` run manifest execution-report hardening, plus -`1.0.0-beta.23` standard-library stability tier documentation. +`1.0.0-beta.23` standard-library stability tier documentation, and +`1.0.0-beta.24` package manifest/dependency diagnostic hardening. `1.0.0-beta.16` adds `std.string` source facades and examples for `byte_at_result`, `slice_result`, `starts_with`, and `ends_with`. These helpers @@ -118,6 +119,14 @@ behavior, stable manifest schema, stable Markdown schema, stable ABI/layout, or stable stdlib/API freeze. It does update generated catalog output and the release gate so tier metadata is visible and checked. +`1.0.0-beta.24` is package/workspace diagnostic hardening, not a language, +runtime, package-manager, or stdlib behavior feature. It makes duplicate +package manifest keys, invalid dependency keys, and duplicate dependency keys +explicit diagnostics while keeping the existing closed local workspace model. +It adds no remote registry, lockfile, semantic-version solving, package +publishing, optional/dev/target dependencies, stable package ABI/layout, +source-language change, runtime change, or stdlib change. + The final experimental precursor scope is `exp-125`, defined in `.llm/EXP_125_UNSIGNED_U32_U64_NUMERIC_AND_STDLIB_BREADTH_ALPHA.md`. Its unsigned direct-value flow, parse/format runtime lanes, and matching staged diff --git a/docs/language/STDLIB_API.md b/docs/language/STDLIB_API.md index bb590a1..155fe40 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`: exported from `lib/std` in `1.0.0-beta.23`, but still has beta caveats around host behavior, resource handles, or API shape. +- `experimental`: exported from `lib/std` in `1.0.0-beta.24`, but still has beta caveats around host behavior, resource handles, or API shape. - `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 7b3a3b2..316dc8f 100755 --- a/scripts/release-gate.sh +++ b/scripts/release-gate.sh @@ -76,6 +76,7 @@ cargo test --test test_discovery_beta19 cargo test --test standard_string_search_trim_beta20 cargo test --test standard_json_document_scalar_parsing_beta21 cargo test --test run_manifest_beta22 +cargo test --test package_workspace_discipline_beta24 # 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