Files
AILang/docs/journals/2026-05-14-iter-pd.1.md
T
Brummel ffa80326a3 iter pd.1: core API split — load_modules_with + build_workspace + implicit_imports threading
Carved load_workspace_with's inline DFS+inject+validate block into two
new public fns: load_modules_with (DFS only) and build_workspace
(validation + registry, takes implicit_imports: &[&str]). Threaded the
new parameter through validate_canonical_type_names + check_class_ref
+ check_type_con_name; the four hardcoded literal-"prelude" fallback
blocks in the diagnostic helpers became iter-loops over implicit_imports.
load_workspace_with survives as a 3-line shim composing the new fns +
the still-in-place prelude inject, so surface (ailang-surface) and all
downstream crates compile + test unchanged. Retired pub fn
load_workspace (zero production callers; 9 in-mod test callers
migrated to load_workspace_with(&entry, load_one); load_one + the
load_module import gated under cfg(test) since they're now test-only).
Production literal-"prelude" count in workspace.rs dropped 8 -> 4 (all
4 in the shim's inject path; none in the diagnostic helpers, which
was the spec's pd.1 target).

pd.2 will move the prelude embed + the inject step into ailang-surface
and retire the shim; pd.3 will delete examples/prelude.ail.json.

cargo test --workspace green; bench/check.py + compile_check.py +
cross_lang.py all exit 0.
2026-05-14 12:25:12 +02:00

6.3 KiB

iter pd.1 — core API split (load_modules_with + build_workspace) — surface unchanged via shim

Date: 2026-05-14 Started from: 61167a4ef0 Status: DONE Tasks completed: 6 of 6

Summary

pd.1 carved out two new public fns in crates/ailang-core/src/workspace.rs (load_modules_with for DFS-only loading, build_workspace for validation + registry construction with a caller-supplied implicit_imports: &[&str]), threaded the new parameter through validate_canonical_type_names / check_class_ref / check_type_con_name, and replaced the four hardcoded literal-"prelude" fallback blocks in the diagnostic helpers with a parameterised iter-loop over implicit_imports. The surviving load_workspace_with collapses from a ~70-line inline DFS+inject+validate block to a 3-line composition (load_modules_with → caller-side prelude inject → build_workspace). The legacy JSON-only wrapper pub fn load_workspace retired with zero production callers (only doc-comment references and 9 in-mod test callers; the latter migrated to load_workspace_with(&entry, load_one)). Surface (ailang-surface) and all downstream crates compile + test unchanged because the shim preserves today's load_workspace_with semantics; the regression-pin test asserts the prelude module hash matches load_prelude() post-refit. Production-code literal-"prelude" count in workspace.rs dropped from 8 to exactly the 4 expected sites (all in the shim, none in the diagnostic helpers). pd.2 will move the prelude-embed + the inject step into ailang-surface and retire the shim; pd.3 will delete examples/prelude.ail.json.

Per-task notes

  • iter pd.1.1: added pub fn load_modules_with above load_workspace_with (extracted DFS pre-amble verbatim from the old inline body); RED test load_modules_with_returns_modules_without_prelude proves the new fn returns modules without auto-injecting prelude. First-shot GREEN.
  • iter pd.1.2: added pub fn build_workspace next to load_modules_with (validation + registry construction; takes pre-assembled modules-map and implicit_imports: &[&str]). RED test build_workspace_accepts_assembled_modules_and_runs_validation ends the task RED-on-purpose per the plan (the inner call to validate_canonical_type_names doesn't have the parameter yet); Task 3 lands the green.
  • iter pd.1.3: added implicit_imports: &[&str] parameter to validate_canonical_type_names, check_class_ref, check_type_con_name; threaded it through 6 internal call sites in validate_canonical_type_names (2 in the type-walk + 4 in the class-ref match-arms); rewrote the two literal-"prelude" fallback blocks in the diagnostic helpers as iter-loops over implicit_imports. RED test implicit_imports_arg_drives_prelude_fallback_in_diagnostics exercises both branches (with-prelude includes prelude.Eq candidate; without-prelude excludes it). Migrated 18 in-mod test callers + 1 production callsite (the still-inline shim body) to &["prelude"]. Task 2's test now passes. New helpers module_with_bare_classref and module_with_class_def use the existing serde_json::from_value(json!(...)) pattern (the rest of mod tests' convention) rather than the plan's struct-literal style (which would have failed against actual ClassDef.superclass: Option<...> field shape).
  • iter pd.1.4: replaced load_workspace_with's ~70-line body with the 3-line shim composition; doc-comment rewritten to describe the shim role + retain the ext-cli.1 cycle-avoidance rationale on the loader-injection point. Pin test load_workspace_with_shim_preserves_today_semantics asserts prelude-module hash equality with load_prelude() post-refit.
  • iter pd.1.5: deleted pub fn load_workspace; removed it from lib.rs re-export; rewrote the workspace.rs module rustdoc example
    • 3 lib.rs //! references to point at load_workspace_with; hoisted the orphaned "Algorithm: DFS over imports" doc-comment block onto load_modules_with (where it now belongs). Migrated 9 in-mod test callers from load_workspace(&entry) to load_workspace_with(&entry, load_one). Required #[cfg(test)] gating of fn load_one and the load_module import (now consumed only by the test mod) to silence dead-code warnings.
  • iter pd.1.6: verification — cargo test --workspace all green (every crate's tests pass); bench/check.py + bench/compile_check.py
    • bench/cross_lang.py all exit 0; production literal-"prelude" count in workspace.rs is exactly 4 (the four expected shim sites at lines 553/555/558/560 — all in load_workspace_with body — none in the diagnostic helpers).

Concerns

  • pd.1.2 ends RED-on-purpose per the plan. This is intentional and documented in the plan, but if a future iter ever bisects through this plan the workspace WILL NOT compile at the Task 2 boundary — Task 3 must land in the same Boss commit. The Boss-side commit shape for pd.1 is therefore "one cohesive iter commit" not "per-task commits".
  • pd.1.5 needed an unanticipated #[cfg(test)] gating step on the load_one fn + the load_module import. The plan didn't anticipate this because it framed the load_workspace retirement as "doc + lib cleanup only", but load_one was the only consumer of those imports in production. Recording so a future planner adds an "if removing a caller, check whether its callees become test-only" check.

Known debt

  • pd.2 will move prelude-embed (PRELUDE_JSON const + load_prelude fn) into ailang-surface and rewire surface's load_workspace to call load_modules_with + build_workspace directly, retiring the shim. Out of scope for pd.1 by spec.
  • pd.3 will delete examples/prelude.ail.json. Out of scope for pd.1.
  • The latency.implicit_at_rc improvement cluster appearing in bench/check.py (4 metrics regressed, 2 improved) reappears for the 4th audit in a row; cause not localised. pd.1 is a workspace.rs refactor with zero codegen / runtime touch, so cannot be the source. Baseline left pristine (consistent with audit-eob and earlier decisions).

Files touched

  • crates/ailang-core/src/workspace.rs (extraction + threading + shim refit + dead-fn delete + test-mod migration)
  • crates/ailang-core/src/lib.rs (re-export drop + 3 rustdoc updates)

Stats

bench/orchestrator-stats/2026-05-14-iter-pd.1.json