# iter pd.1 — core API split (load_modules_with + build_workspace) — surface unchanged via shim **Date:** 2026-05-14 **Started from:** 61167a4ef0ade5dd65dadfbdde1a44b65c6693ca **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`