Iter 6: deps hardening, multi-diagnose, DESIGN audit

- ail deps now filters builtins, fn params, and let/match-pattern
  bindings. New value_names() in ailang-check::builtins is the single
  source of truth shared with the typechecker install path. walk_term
  threads a scope set; qualified `prefix.def` refs pass through.
- check_in_workspace returns Vec<CheckError>; check_workspace
  accumulates body diagnostics across defs and modules. Pass-1
  (top-level symbol table) and per-module type-def setup stay
  fail-fast — corrupt env would taint later diagnostics.
- DESIGN.md "What the MVP is NOT" was lying (ADTs, strings landed
  in Iter 2/3). Renamed to "What is not (yet) supported" and split
  into "not yet" + supported/smoke-tested. JOURNAL Iter 6 entry
  records the architecture self-check.

Tests: 47 green (was 44). +2 deps filter tests in e2e,
+1 multi-diagnose test in ailang-check workspace integration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-07 12:31:20 +02:00
parent efd209779c
commit 1a448309fa
7 changed files with 393 additions and 42 deletions
+67
View File
@@ -349,6 +349,73 @@ fn describe_workspace_resolves_qualified_name() {
);
}
/// Guards Iter 6: `ail deps` no longer surfaces built-ins, params, or
/// let-bindings as edges. The single-module `sum` def in `sum.ail.json`
/// uses `+`, `-`, `==`, the param `n`, and the recursive call `sum`.
/// Only the recursive call must remain.
#[test]
fn deps_filters_builtins_params_locals() {
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let workspace = Path::new(manifest_dir).parent().unwrap().parent().unwrap();
let src = workspace.join("examples").join("sum.ail.json");
let output = Command::new(ail_bin())
.args(["deps", src.to_str().unwrap(), "--of", "sum", "--json"])
.output()
.expect("ail deps failed to run");
assert!(
output.status.success(),
"ail deps exited non-zero; stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
let stdout = String::from_utf8(output.stdout).expect("stdout utf8");
let v: serde_json::Value = serde_json::from_str(stdout.trim()).expect("valid JSON");
let arr = v.as_array().expect("array of {name, refs}");
let entry = arr
.iter()
.find(|e| e["name"].as_str() == Some("sum"))
.expect("sum entry present");
let refs: Vec<&str> = entry["refs"]
.as_array()
.expect("refs array")
.iter()
.filter_map(|x| x.as_str())
.collect();
assert_eq!(
refs,
vec!["sum"],
"expected only the recursive call `sum`; got {refs:?}"
);
}
/// Guards Iter 6 in workspace mode: edges into built-ins (`ws_lib.+`)
/// and into the params (`ws_lib.a`, `ws_lib.b`) of `ws_lib.add` must be
/// gone after the deps refactor.
#[test]
fn deps_workspace_filters_builtins_and_params() {
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let workspace = Path::new(manifest_dir).parent().unwrap().parent().unwrap();
let entry = workspace.join("examples").join("ws_main.ail.json");
let output = Command::new(ail_bin())
.args(["deps", entry.to_str().unwrap(), "--workspace", "--json"])
.output()
.expect("ail deps --workspace failed to run");
assert!(output.status.success());
let stdout = String::from_utf8(output.stdout).expect("stdout utf8");
let v: serde_json::Value = serde_json::from_str(stdout.trim()).expect("valid JSON");
let edges = v["edges"].as_array().expect("edges array");
for e in edges {
let to_def = e.get("to_def").and_then(|x| x.as_str()).unwrap_or("");
assert_ne!(to_def, "+", "built-in `+` must not appear: {e}");
assert_ne!(to_def, "a", "param `a` must not appear: {e}");
assert_ne!(to_def, "b", "param `b` must not appear: {e}");
}
}
/// Guards Iter 5d: `ail deps --workspace` contains a cross-module edge
/// `ws_main.main -> ws_lib.add`, because `ws_main` calls `ws_lib.add(2,3)`.
#[test]