Add workspace default package selection
This commit is contained in:
parent
33668a0793
commit
0c612ad7fd
39
.llm/BETA_5_PACKAGE_WORKSPACE_DISCIPLINE.md
Normal file
39
.llm/BETA_5_PACKAGE_WORKSPACE_DISCIPLINE.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# Beta 5 Package And Workspace Discipline
|
||||||
|
|
||||||
|
Release label: `1.0.0-beta.5` candidate scope
|
||||||
|
|
||||||
|
Status: in progress.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This post-`1.0.0-beta.4` slice tightens local package/workspace behavior
|
||||||
|
without adding a remote registry, package solver, lockfile, or stable package
|
||||||
|
ABI promise.
|
||||||
|
|
||||||
|
## Current Work
|
||||||
|
|
||||||
|
- Add `[workspace] default_package = "name"` as an explicit build/run entry
|
||||||
|
selector for workspaces with multiple packages that contain their entry
|
||||||
|
module.
|
||||||
|
- Keep `check`, `test`, `fmt`, and `doc` project-wide over the full closed
|
||||||
|
workspace graph.
|
||||||
|
- Diagnose missing `default_package` references during workspace loading.
|
||||||
|
- Diagnose duplicate workspace members after path normalization before package
|
||||||
|
loading.
|
||||||
|
- Keep dependency resolution local-path-only and deterministic.
|
||||||
|
|
||||||
|
## Acceptance Gates
|
||||||
|
|
||||||
|
- `cargo test --test project_mode workspace_default_package`
|
||||||
|
- `cargo test --test project_mode workspace_package_boundaries`
|
||||||
|
- `cargo fmt --check`
|
||||||
|
- `git diff --check`
|
||||||
|
|
||||||
|
## Deferrals
|
||||||
|
|
||||||
|
- No remote registry.
|
||||||
|
- No lockfile.
|
||||||
|
- No semantic-version solver.
|
||||||
|
- No optional, dev, target, or feature dependencies.
|
||||||
|
- No package publishing or archive format.
|
||||||
|
- No stable package ABI/layout guarantee.
|
||||||
@ -150,6 +150,15 @@ language surface. Project/workspace build and run entry failures now use
|
|||||||
entry-specific diagnostic codes, and non-exhaustive `match` diagnostics have
|
entry-specific diagnostic codes, and non-exhaustive `match` diagnostics have
|
||||||
clearer wording with deterministic found-arm output.
|
clearer wording with deterministic found-arm output.
|
||||||
|
|
||||||
|
## Current Main: Package And Workspace Discipline
|
||||||
|
|
||||||
|
After `1.0.0-beta.4`, `main` is tracking a package/workspace discipline slice.
|
||||||
|
Local workspaces may declare `[workspace] default_package = "name"` to select
|
||||||
|
the build/run entry package when multiple packages have entry modules.
|
||||||
|
Duplicate normalized workspace members and missing default-package references
|
||||||
|
are now dedicated diagnostics. Remote registries, lockfiles, semantic-version
|
||||||
|
solving, package publishing, and stable package ABI/layout remain deferred.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
- [Language Manifest](docs/language/MANIFEST.md)
|
- [Language Manifest](docs/language/MANIFEST.md)
|
||||||
|
|||||||
@ -130,6 +130,7 @@ struct WorkspaceManifest {
|
|||||||
source: String,
|
source: String,
|
||||||
root: PathBuf,
|
root: PathBuf,
|
||||||
members: Vec<String>,
|
members: Vec<String>,
|
||||||
|
default_package: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -1194,6 +1195,7 @@ fn parse_workspace_manifest(
|
|||||||
let mut in_workspace = false;
|
let mut in_workspace = false;
|
||||||
let mut saw_workspace = false;
|
let mut saw_workspace = false;
|
||||||
let mut members = None::<Vec<String>>;
|
let mut members = None::<Vec<String>>;
|
||||||
|
let mut default_package = None::<String>;
|
||||||
|
|
||||||
for line in manifest_lines(&source) {
|
for line in manifest_lines(&source) {
|
||||||
let trimmed = line.text.trim();
|
let trimmed = line.text.trim();
|
||||||
@ -1264,6 +1266,28 @@ fn parse_workspace_manifest(
|
|||||||
.with_span(line.span),
|
.with_span(line.span),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
"default_package" => match parse_manifest_string(value.trim()) {
|
||||||
|
Some(parsed) => {
|
||||||
|
if default_package.replace(parsed).is_some() {
|
||||||
|
errors.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
&file,
|
||||||
|
"WorkspaceManifestInvalid",
|
||||||
|
"duplicate workspace manifest key `default_package`",
|
||||||
|
)
|
||||||
|
.with_span(line.span),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => errors.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
&file,
|
||||||
|
"WorkspaceManifestInvalid",
|
||||||
|
"workspace default_package must be a string",
|
||||||
|
)
|
||||||
|
.with_span(line.span),
|
||||||
|
),
|
||||||
|
},
|
||||||
other => errors.push(
|
other => errors.push(
|
||||||
Diagnostic::new(
|
Diagnostic::new(
|
||||||
&file,
|
&file,
|
||||||
@ -1292,8 +1316,22 @@ fn parse_workspace_manifest(
|
|||||||
Vec::new()
|
Vec::new()
|
||||||
});
|
});
|
||||||
|
|
||||||
for member in &members {
|
let mut normalized_members = Vec::new();
|
||||||
if normalize_workspace_member(member).is_none() {
|
let mut seen_members = BTreeMap::<String, String>::new();
|
||||||
|
for member in members {
|
||||||
|
if let Some(normalized) = normalize_workspace_member(&member) {
|
||||||
|
if let Some(original) = seen_members.insert(normalized.clone(), member.clone()) {
|
||||||
|
errors.push(Diagnostic::new(
|
||||||
|
&file,
|
||||||
|
"DuplicateWorkspaceMember",
|
||||||
|
format!(
|
||||||
|
"workspace member path `{}` duplicates `{}` after normalization",
|
||||||
|
member, original
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
normalized_members.push(normalized);
|
||||||
|
} else {
|
||||||
errors.push(Diagnostic::new(
|
errors.push(Diagnostic::new(
|
||||||
&file,
|
&file,
|
||||||
"WorkspaceMemberPathEscape",
|
"WorkspaceMemberPathEscape",
|
||||||
@ -1304,12 +1342,19 @@ fn parse_workspace_manifest(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
members = members
|
members = normalized_members;
|
||||||
.into_iter()
|
|
||||||
.filter_map(|member| normalize_workspace_member(&member))
|
|
||||||
.collect();
|
|
||||||
members.sort();
|
members.sort();
|
||||||
|
|
||||||
|
if let Some(default_package) = &default_package {
|
||||||
|
if !is_project_name(default_package) {
|
||||||
|
errors.push(Diagnostic::new(
|
||||||
|
&file,
|
||||||
|
"WorkspaceManifestInvalid",
|
||||||
|
"workspace default_package must be a package name",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
return Err(errors);
|
return Err(errors);
|
||||||
}
|
}
|
||||||
@ -1325,6 +1370,7 @@ fn parse_workspace_manifest(
|
|||||||
source,
|
source,
|
||||||
root,
|
root,
|
||||||
members,
|
members,
|
||||||
|
default_package,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2750,6 +2796,18 @@ fn validate_workspace_packages(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(default_package) = &workspace.default_package {
|
||||||
|
if !by_name.contains_key(default_package) {
|
||||||
|
diagnostics.push(Diagnostic::new(
|
||||||
|
&workspace.path.display().to_string(),
|
||||||
|
"WorkspaceDefaultPackageMissing",
|
||||||
|
format!(
|
||||||
|
"workspace default_package `{}` is not a workspace package",
|
||||||
|
default_package
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut resolved_dependencies = vec![Vec::<usize>::new(); packages.len()];
|
let mut resolved_dependencies = vec![Vec::<usize>::new(); packages.len()];
|
||||||
for (package_index, package) in packages.iter().enumerate() {
|
for (package_index, package) in packages.iter().enumerate() {
|
||||||
@ -3088,9 +3146,9 @@ fn resolve_and_check_workspace(
|
|||||||
|
|
||||||
let mut selected_build_entry_package = None;
|
let mut selected_build_entry_package = None;
|
||||||
if require_entry_wrapper {
|
if require_entry_wrapper {
|
||||||
selected_build_entry_package = select_workspace_build_entry(&packages, &module_maps);
|
match select_workspace_build_entry(&workspace, &packages, &module_maps) {
|
||||||
match selected_build_entry_package {
|
Ok(package_index) => {
|
||||||
Some(package_index) => {
|
selected_build_entry_package = Some(package_index);
|
||||||
add_workspace_entry_wrapper(
|
add_workspace_entry_wrapper(
|
||||||
&packages,
|
&packages,
|
||||||
package_index,
|
package_index,
|
||||||
@ -3099,11 +3157,7 @@ fn resolve_and_check_workspace(
|
|||||||
&mut diagnostics,
|
&mut diagnostics,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
None => diagnostics.push(Diagnostic::new(
|
Err(diagnostic) => diagnostics.push(diagnostic),
|
||||||
&workspace.path.display().to_string(),
|
|
||||||
"WorkspaceBuildAmbiguousEntryPackage",
|
|
||||||
"workspace build requires exactly one package with its entry module",
|
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
if !diagnostics.is_empty() {
|
if !diagnostics.is_empty() {
|
||||||
return Err(ProjectLoadFailure {
|
return Err(ProjectLoadFailure {
|
||||||
@ -4200,9 +4254,38 @@ fn add_entry_wrapper(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn select_workspace_build_entry(
|
fn select_workspace_build_entry(
|
||||||
|
workspace: &WorkspaceManifest,
|
||||||
packages: &[PackageUnit],
|
packages: &[PackageUnit],
|
||||||
module_maps: &[HashMap<String, usize>],
|
module_maps: &[HashMap<String, usize>],
|
||||||
) -> Option<usize> {
|
) -> Result<usize, Diagnostic> {
|
||||||
|
if let Some(default_package) = &workspace.default_package {
|
||||||
|
let Some(package_index) = packages
|
||||||
|
.iter()
|
||||||
|
.position(|package| package.manifest.name == *default_package)
|
||||||
|
else {
|
||||||
|
return Err(Diagnostic::new(
|
||||||
|
&workspace.path.display().to_string(),
|
||||||
|
"WorkspaceDefaultPackageMissing",
|
||||||
|
format!(
|
||||||
|
"workspace default_package `{}` is not a workspace package",
|
||||||
|
default_package
|
||||||
|
),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
let package = &packages[package_index];
|
||||||
|
if module_maps[package_index].contains_key(&package.manifest.entry) {
|
||||||
|
return Ok(package_index);
|
||||||
|
}
|
||||||
|
return Err(Diagnostic::new(
|
||||||
|
&package.manifest.path.display().to_string(),
|
||||||
|
"WorkspaceDefaultPackageEntryMissing",
|
||||||
|
format!(
|
||||||
|
"workspace default_package `{}` has no entry module `{}`; build/run require one selected package entry module",
|
||||||
|
default_package, package.manifest.entry
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let candidates = packages
|
let candidates = packages
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@ -4210,9 +4293,13 @@ fn select_workspace_build_entry(
|
|||||||
.map(|(index, _)| index)
|
.map(|(index, _)| index)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
if candidates.len() == 1 {
|
if candidates.len() == 1 {
|
||||||
candidates.first().copied()
|
Ok(candidates[0])
|
||||||
} else {
|
} else {
|
||||||
None
|
Err(Diagnostic::new(
|
||||||
|
&workspace.path.display().to_string(),
|
||||||
|
"WorkspaceBuildAmbiguousEntryPackage",
|
||||||
|
"workspace build requires exactly one package with its entry module or `[workspace] default_package = \"name\"`",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -587,6 +587,55 @@ fn workspace_build_requires_one_entry_package() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn workspace_default_package_selects_build_entry() {
|
||||||
|
let workspace = write_workspace(
|
||||||
|
"workspace-default-build-entry",
|
||||||
|
"[workspace]\nmembers = [\"packages/app\", \"packages/tool\"]\ndefault_package = \"app\"\n",
|
||||||
|
&[
|
||||||
|
WorkspacePackageSpec {
|
||||||
|
member: "packages/app",
|
||||||
|
manifest: "[package]\nname = \"app\"\nversion = \"0.1.0\"\n",
|
||||||
|
modules: &[("main", "(module main)\n\n(fn main () -> i32\n 0)\n")],
|
||||||
|
},
|
||||||
|
WorkspacePackageSpec {
|
||||||
|
member: "packages/tool",
|
||||||
|
manifest: "[package]\nname = \"tool\"\nversion = \"0.1.0\"\n",
|
||||||
|
modules: &[("main", "(module main)\n\n(fn main () -> i32\n 7)\n")],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
let binary = unique_path("workspace-default-build-bin");
|
||||||
|
|
||||||
|
let output = run_glagol([
|
||||||
|
"build".as_ref(),
|
||||||
|
"-o".as_ref(),
|
||||||
|
binary.as_os_str(),
|
||||||
|
workspace.as_os_str(),
|
||||||
|
]);
|
||||||
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
|
assert!(
|
||||||
|
!stderr.contains("WorkspaceBuildAmbiguousEntryPackage"),
|
||||||
|
"default package should avoid ambiguous build entry diagnostic:\n{}",
|
||||||
|
stderr
|
||||||
|
);
|
||||||
|
|
||||||
|
if output.status.code() == Some(3) {
|
||||||
|
assert_stderr_contains(
|
||||||
|
"workspace default package build toolchain",
|
||||||
|
&output,
|
||||||
|
"ToolchainUnavailable",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_success("workspace default package build", &output);
|
||||||
|
let run = Command::new(&binary)
|
||||||
|
.output()
|
||||||
|
.expect("run workspace default package build output");
|
||||||
|
assert_success("workspace default package build binary", &run);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn workspace_build_reports_entry_main_contract() {
|
fn workspace_build_reports_entry_main_contract() {
|
||||||
let missing_main = write_workspace(
|
let missing_main = write_workspace(
|
||||||
@ -776,6 +825,74 @@ fn workspace_package_boundaries_are_diagnostics() {
|
|||||||
&escape_output,
|
&escape_output,
|
||||||
"WorkspaceMemberPathEscape",
|
"WorkspaceMemberPathEscape",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let duplicate_member = write_workspace(
|
||||||
|
"workspace-duplicate-member",
|
||||||
|
"[workspace]\nmembers = [\"packages/app\", \"packages/./app\"]\n",
|
||||||
|
&[WorkspacePackageSpec {
|
||||||
|
member: "packages/app",
|
||||||
|
manifest: "[package]\nname = \"app\"\nversion = \"0.1.0\"\n",
|
||||||
|
modules: &[("main", "(module main)\n\n(fn main () -> i32\n 0)\n")],
|
||||||
|
}],
|
||||||
|
);
|
||||||
|
let duplicate_member_output = run_glagol(["check".as_ref(), duplicate_member.as_os_str()]);
|
||||||
|
assert_exit_code("duplicate workspace member", &duplicate_member_output, 1);
|
||||||
|
assert_stderr_contains(
|
||||||
|
"duplicate workspace member",
|
||||||
|
&duplicate_member_output,
|
||||||
|
"DuplicateWorkspaceMember",
|
||||||
|
);
|
||||||
|
|
||||||
|
let missing_default = write_workspace(
|
||||||
|
"workspace-missing-default-package",
|
||||||
|
"[workspace]\nmembers = [\"packages/app\"]\ndefault_package = \"tool\"\n",
|
||||||
|
&[WorkspacePackageSpec {
|
||||||
|
member: "packages/app",
|
||||||
|
manifest: "[package]\nname = \"app\"\nversion = \"0.1.0\"\n",
|
||||||
|
modules: &[("main", "(module main)\n\n(fn main () -> i32\n 0)\n")],
|
||||||
|
}],
|
||||||
|
);
|
||||||
|
let missing_default_output = run_glagol(["check".as_ref(), missing_default.as_os_str()]);
|
||||||
|
assert_exit_code("missing default package", &missing_default_output, 1);
|
||||||
|
assert_stderr_contains(
|
||||||
|
"missing default package",
|
||||||
|
&missing_default_output,
|
||||||
|
"WorkspaceDefaultPackageMissing",
|
||||||
|
);
|
||||||
|
|
||||||
|
let missing_default_entry = write_workspace(
|
||||||
|
"workspace-missing-default-entry",
|
||||||
|
"[workspace]\nmembers = [\"packages/app\", \"packages/tool\"]\ndefault_package = \"app\"\n",
|
||||||
|
&[
|
||||||
|
WorkspacePackageSpec {
|
||||||
|
member: "packages/app",
|
||||||
|
manifest: "[package]\nname = \"app\"\nversion = \"0.1.0\"\nentry = \"app\"\n",
|
||||||
|
modules: &[("main", "(module main)\n\n(fn main () -> i32\n 0)\n")],
|
||||||
|
},
|
||||||
|
WorkspacePackageSpec {
|
||||||
|
member: "packages/tool",
|
||||||
|
manifest: "[package]\nname = \"tool\"\nversion = \"0.1.0\"\n",
|
||||||
|
modules: &[("main", "(module main)\n\n(fn main () -> i32\n 0)\n")],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
let missing_default_entry_binary = unique_path("workspace-missing-default-entry-bin");
|
||||||
|
let missing_default_entry_output = run_glagol([
|
||||||
|
"build".as_ref(),
|
||||||
|
"-o".as_ref(),
|
||||||
|
missing_default_entry_binary.as_os_str(),
|
||||||
|
missing_default_entry.as_os_str(),
|
||||||
|
]);
|
||||||
|
assert_exit_code(
|
||||||
|
"missing default package entry",
|
||||||
|
&missing_default_entry_output,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
assert_stderr_contains(
|
||||||
|
"missing default package entry",
|
||||||
|
&missing_default_entry_output,
|
||||||
|
"WorkspaceDefaultPackageEntryMissing",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -131,6 +131,13 @@ Work:
|
|||||||
- add diagnostics for ambiguous package roots and dependency cycles
|
- add diagnostics for ambiguous package roots and dependency cycles
|
||||||
- keep remote registry, semver solving, and publishing out of scope
|
- keep remote registry, semver solving, and publishing out of scope
|
||||||
|
|
||||||
|
In progress after `1.0.0-beta.4`: local workspaces can declare
|
||||||
|
`default_package` to select the build/run entry package when multiple packages
|
||||||
|
have entry modules. Duplicate normalized member paths and missing default
|
||||||
|
package references are dedicated diagnostics. Lockfiles, remote registries,
|
||||||
|
semver solving, publishing, optional/dev/target dependencies, and stable
|
||||||
|
package ABI/layout remain out of scope.
|
||||||
|
|
||||||
Why fifth: stable package rules are a prerequisite for a usable public language,
|
Why fifth: stable package rules are a prerequisite for a usable public language,
|
||||||
but remote publishing can wait.
|
but remote publishing can wait.
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,14 @@ integration/readiness release, not the first real beta.
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
No unreleased changes yet.
|
- Workspace manifests may now declare
|
||||||
|
`[workspace] default_package = "name"` to choose the build/run entry package
|
||||||
|
when multiple workspace packages contain their entry module.
|
||||||
|
- Workspace loading now diagnoses duplicate member paths after normalization
|
||||||
|
with `DuplicateWorkspaceMember`.
|
||||||
|
- Workspace loading now diagnoses a missing `default_package` reference with
|
||||||
|
`WorkspaceDefaultPackageMissing`, and build/run diagnose a selected package
|
||||||
|
that lacks its entry module with `WorkspaceDefaultPackageEntryMissing`.
|
||||||
|
|
||||||
## 1.0.0-beta.4
|
## 1.0.0-beta.4
|
||||||
|
|
||||||
|
|||||||
@ -30,6 +30,12 @@ hardening release, the `1.0.0-beta.2` runtime/resource foundation release, the
|
|||||||
project/workspace `main` diagnostics, and clearer non-exhaustive `match`
|
project/workspace `main` diagnostics, and clearer non-exhaustive `match`
|
||||||
diagnostics.
|
diagnostics.
|
||||||
|
|
||||||
|
Current unreleased work is the package/workspace discipline slice. It adds
|
||||||
|
`[workspace] default_package = "name"` for deterministic build/run entry
|
||||||
|
selection in multi-entry workspaces and tightens workspace-member/default
|
||||||
|
package diagnostics while keeping registries, lockfiles, semver solving, and
|
||||||
|
publishing deferred.
|
||||||
|
|
||||||
The final experimental precursor scope is `exp-125`. Its unsigned direct-value
|
The final experimental precursor scope is `exp-125`. Its unsigned direct-value
|
||||||
flow, parse/format runtime lanes, and matching staged stdlib helper breadth
|
flow, parse/format runtime lanes, and matching staged stdlib helper breadth
|
||||||
are now absorbed into `1.0.0-beta`.
|
are now absorbed into `1.0.0-beta`.
|
||||||
|
|||||||
@ -17,7 +17,15 @@ diagnostics bundle.
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
No unreleased changes yet.
|
- Local workspace manifests may now declare
|
||||||
|
`[workspace] default_package = "name"` to make build/run entry selection
|
||||||
|
deterministic when more than one package has an entry module.
|
||||||
|
- Duplicate workspace member paths after normalization are now a dedicated
|
||||||
|
`DuplicateWorkspaceMember` diagnostic.
|
||||||
|
- Missing `default_package` references are now a dedicated
|
||||||
|
`WorkspaceDefaultPackageMissing` diagnostic. A selected default package that
|
||||||
|
lacks its configured entry module is diagnosed as
|
||||||
|
`WorkspaceDefaultPackageEntryMissing` during build/run.
|
||||||
|
|
||||||
## 1.0.0-beta.4
|
## 1.0.0-beta.4
|
||||||
|
|
||||||
|
|||||||
@ -17,6 +17,12 @@ contract and includes the `1.0.0-beta.1` tooling hardening release, the
|
|||||||
standard-library stabilization release, entry-specific project/workspace
|
standard-library stabilization release, entry-specific project/workspace
|
||||||
`main` diagnostics, and clearer non-exhaustive `match` diagnostics.
|
`main` diagnostics, and clearer non-exhaustive `match` diagnostics.
|
||||||
|
|
||||||
|
Current unreleased work is the package/workspace discipline slice. It adds
|
||||||
|
`[workspace] default_package = "name"` for deterministic build/run entry
|
||||||
|
selection in multi-entry workspaces and tightens duplicate-member/default
|
||||||
|
package diagnostics while keeping registries, lockfiles, semver solving, and
|
||||||
|
publishing deferred.
|
||||||
|
|
||||||
The final experimental precursor scope is `exp-125`, defined in
|
The final experimental precursor scope is `exp-125`, defined in
|
||||||
`.llm/EXP_125_UNSIGNED_U32_U64_NUMERIC_AND_STDLIB_BREADTH_ALPHA.md`. Its
|
`.llm/EXP_125_UNSIGNED_U32_U64_NUMERIC_AND_STDLIB_BREADTH_ALPHA.md`. Its
|
||||||
unsigned direct-value flow, parse/format runtime lanes, and matching staged
|
unsigned direct-value flow, parse/format runtime lanes, and matching staged
|
||||||
|
|||||||
@ -1011,6 +1011,32 @@ recursive filesystem operations, process handles, sockets, async/event-loop
|
|||||||
resources, rich host-error ADTs, platform-specific error codes, finalizers,
|
resources, rich host-error ADTs, platform-specific error codes, finalizers,
|
||||||
destructors, and automatic cleanup remain deferred.
|
destructors, and automatic cleanup remain deferred.
|
||||||
|
|
||||||
|
### 4.4.3 Post-Beta Package And Workspace Discipline Additions
|
||||||
|
|
||||||
|
The package/workspace discipline slice keeps the existing closed local
|
||||||
|
workspace model from exp-5 and adds one beta manifest key:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[workspace]
|
||||||
|
members = ["packages/app", "packages/tool"]
|
||||||
|
default_package = "app"
|
||||||
|
```
|
||||||
|
|
||||||
|
`default_package` is optional. When absent, `glagol build` and `glagol run`
|
||||||
|
continue to require exactly one workspace package whose configured `entry`
|
||||||
|
module exists. When present, the named package is the selected build/run entry
|
||||||
|
package. The named package must be a workspace package, and build/run require
|
||||||
|
that package to contain its configured entry module.
|
||||||
|
|
||||||
|
Workspace member paths are normalized under the workspace root before package
|
||||||
|
loading. Duplicate normalized member paths are invalid, even if they were
|
||||||
|
spelled differently in the manifest.
|
||||||
|
|
||||||
|
This slice does not add remote registries, lockfiles, semantic-version
|
||||||
|
solving, package publishing, archive formats, optional/dev/target
|
||||||
|
dependencies, feature flags, package build scripts, or stable package
|
||||||
|
ABI/layout promises.
|
||||||
|
|
||||||
## 4.5 v2.0.0-beta.1 Experimental Integration Readiness
|
## 4.5 v2.0.0-beta.1 Experimental Integration Readiness
|
||||||
|
|
||||||
Status: current experimental Slovo-side release contract, released 2026-05-17.
|
Status: current experimental Slovo-side release contract, released 2026-05-17.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user