Files
AILang/docs/journals/2026-05-14-iter-pd.2.md
T
Brummel 008d18bb18 iter pd.2: surface owns prelude embed; pd.1 shim retired
Moved PRELUDE_AIL const + parse_prelude fn to ailang-surface;
re-exported from surface's lib.rs. surface::load_workspace rewritten
3-line shim-call → 7-line composition: load_modules_with → reserved-name
check → inject parse_prelude → build_workspace(&["prelude"]).

Five deletions in core: PRELUDE_JSON, load_prelude, load_workspace_with
(the pd.1 shim), the top-level cfg(test) load_one fn, and the
cfg(test) crate::load_module import. Production literal-"prelude" count
in crates/ailang-core/src/ is now zero. Cross-form-identity preflight
PASSED at module_hash 3abe0d3fa3c11c99 — parse(prelude.ail) ≡
deserialize(prelude.ail.json), so pd.3's deletion of the JSON is safe.

Three new pin tests in ailang-surface/tests/: prelude_parse_pin (succeeds
+ min defs); prelude_module_hash_pin (cross-form-identity + literal-hex
anchor); prelude_injection_pin (inject + reservation).

Plan deviations (recorded in journal): switched the regression-pin
fixture from non-existent examples/hello.ail.json to hello.ail; relocated
10 in-mod core tests to crates/ailang-core/tests/workspace_pin.rs
because the dev-dep cycle (core ↔ surface) prevents in-mod aliasing in
the lib-test target (form-a.1 T5 precedent); preserved load_one
production-symbol-deletion by adding a test-mod-private load_one helper
in core's mod tests for the 2 pd.1-introduced tests still consuming it.

cargo test --workspace 573 green (+9 net from pd.1 baseline).
bench/cross_lang.py + bench/compile_check.py exit 0; bench/check.py
exit 1 with established noise envelope (bench_list_sum.bump_s, 13th
consecutive observation; pd.2 is workspace-loader-only, no codegen
touch — baseline pristine).
2026-05-14 12:57:27 +02:00

9.8 KiB

iter pd.2 — surface owns prelude embed; pd.1 shim retired

Date: 2026-05-14 Started from: 116157a2b8 Status: DONE Tasks completed: 6 of 6

Summary

pd.2 moves the prelude embed (PRELUDE_AIL const + parse_prelude fn) and the prelude-injection step (with the ReservedModuleName reservation) out of ailang-core into ailang-surface, rewires surface::load_workspace from the 3-line shim-call into the 7-line composition (load_modules_with → reserved-name check → inject parse_prelude → build_workspace(&["prelude"])), and retires the pd.1 compatibility shim core::load_workspace_with along with load_one, load_prelude, PRELUDE_JSON, and the #[cfg(test)] use crate::load_module; import. Cross-form-identity preflight (hash-equality between parse(prelude.ail) and serde_json::from_str(prelude.ail.json)) PASSES at hash 3abe0d3fa3c11c99 — the load-bearing assumption that justifies pd.3's deletion of prelude.ail.json is verified. Production literal-"prelude" count in core/src/ is now zero. Test count: 573 green (from the pd.1 baseline of 564, +9 net = 3 new surface pins [parse + hash + injection] + 6 tempdir/fixture relocations to workspace_pin.rs minus 6 in-mod placeholders that became comments; in-mod test deletion of the pd.1 shim test is offset by the load_modules_with_custom_loader_is_called rename + 2 pd.1 tests that now use an inline test-private load_one helper). bench/cross_lang.py

  • bench/compile_check.py exit 0; bench/check.py exit 1 with the 12-audit noise envelope (the bench_list_sum.bump_s + tail-latency oscillation that has been observed in audits since audit-cma; pd.2 is a workspace-loader refactor with zero codegen / runtime touch). pd.3 will delete examples/prelude.ail.json plus the prelude_ail_and_json_parse_to_identical_module preflight, the include_str! carve-out in crates/ail/src/main.rs:472-484, the carve_out_inventory.rs §C4(b) row, the form-a §C4(b) spec text, and the migrate-bare-cross-module-refs defensive include.

Per-task notes

  • iter pd.2.1: pub const PRELUDE_AIL: &str = include_str!("../../../examples/prelude.ail");
    • pub fn parse_prelude() -> Module added near the top of crates/ailang-surface/src/loader.rs; both re-exported from ailang-surface/src/lib.rs. RED test parse_prelude_returns_module_named_prelude_with_min_defs failed with the expected unresolved-import message, then GREEN. First-shot clean.
  • iter pd.2.2: crates/ailang-surface/tests/prelude_module_hash_pin.rs with prelude_ail_and_json_parse_to_identical_module (cross-form- identity preflight, load-bearing for pd.3) + prelude_parse_yields_canonical_hash (literal-hex anchor). Preflight PASSED first-run (the round-trip invariant from form-a.0 + form-a.1 was already exercising parse-then-print idempotency on every .ail fixture, so parse(prelude.ail) ≡ deserialize(prelude.ail.json) was not an accident). Hash 3abe0d3fa3c11c99 captured at pd.2 close.
  • iter pd.2.3: surface::load_workspace rewritten 3-line → 7-line composition; imports updated load_workspace_with → bare Workspace, WorkspaceLoadError with load_modules_with / build_workspace fully-qualified inline; module rustdoc rewritten to name the new injection point + the implicit-imports list. Regression-pin tests surface_load_workspace_injects_prelude_into_every_workspace
    • surface_load_workspace_rejects_user_module_named_prelude PASS before AND after the rewrite (semantics preserved). DONE_WITH_CONCERNS: the plan's example used examples/hello.ail.json which doesn't exist post-form-a.1 (deleted in T8); switched to examples/hello.ail.
  • iter pd.2.4: 10 in-mod tests RELOCATED to crates/ailang-core/tests/workspace_pin.rs rather than aliased per the plan's literal text — the dev-dep cycle (ailang-coreailang-surface via [dev-dependencies]) makes surface::load_workspace calls from ailang-core's lib-test crate structurally impossible (two distinct compilations of ailang-core appear in the test build, types don't match). The integration-test crate consumes both crates against the same ailang-core artefact, so the cycle never materialises — same pattern form-a.1 T5 used for similar relocations. The 11th test (load_workspace_with_custom_loader_is_called) renamed to load_modules_with_custom_loader_is_called with the closure adapted to wrap crate::load_module directly via the same Io/Schema mapping load_one had. The 12th test (load_workspace_with_shim_preserves_today_semantics) deleted outright with a forwarding comment to the surface-side hash pin.
  • iter pd.2.5: 5 deletions (PRELUDE_JSON, load_prelude, load_workspace_with, top-level load_one, #[cfg(test)] use crate::load_module;); module rustdoc reworked to two-phase composition framing; lib.rs three rustdoc sites (lines 16-21, 50-51, 120) re-pointed at ailang_surface::load_workspace. ONE plan-vs-actual deviation: the plan didn't enumerate the 2 pd.1-introduced in-mod tests at the bottom of mod tests that consume load_one directly (load_modules_with_returns_modules_without_prelude, build_workspace_accepts_assembled_modules_and_runs_validation); preserved by adding a test-mod-private load_one helper inside mod tests (production-symbol-level deletion preserved; helper is scoped only to the test module, not exported). The build_workspace... test also called load_prelude() directly; replaced inline with serde_json::from_str(include_str!(...)) of prelude.ail.json (the same one-line code load_prelude was). Zero literal-"prelude" strings in production core/src/ confirmed by grep. cargo doc warnings: 3 (unchanged from pre-pd.2 baseline; all three are dangling [load_workspace] references in the Workspace / Registry rustdoc that pd.1 left behind when it deleted load_workspace; out of scope per the implementer's no-surrounding-cleanup discipline, recorded as known debt).
  • iter pd.2.6: bench verification — cross_lang.py exit 0 (25/25 stable), compile_check.py exit 0 (24/24, "11 regressed" reported but exit code says baseline-tolerance check passes — this is the audit-form-a + clippy-sweep-named noise on the corpus's small build fixtures), check.py exit 1 with 1 regression on bench_list_sum.bump_s +13.61% (within established noise envelope; second run showed same bump_s plus a tail-latency implicit_at_rc.p99_us blip). All three are runtime/compile metrics that cannot be touched by a workspace.rs/loader.rs/lib.rs refactor (no codegen / no runtime / no typecheck path edited).

Concerns

  • pd.2.3: plan's regression-pin used examples/hello.ail.json which doesn't exist post-form-a.1 T8. Switched to examples/hello.ail. Recommend a planner check: when a plan references a .ail.json fixture, verify the fixture exists post-form-a.1 (the carve-out inventory is the right reference).
  • pd.2.4: plan's "alias to surface_load_workspace and stay in-mod" was structurally invalid because of the ailang-coreailang-surface dev-dep cycle inside the lib-test target. Relocated to tests/workspace_pin.rs instead — same pattern form-a.1 T5 used. Recommend: planner recon-check should detect when a planned in-mod test is meant to consume an external dev-dep cycle that the lib-test target can't resolve.
  • pd.2.5: plan's "delete load_one" preserved at production-symbol level (the top-level #[cfg(test)] fn load_one is gone) by introducing a test-mod-private fn load_one helper inside mod tests for the 2 pd.1 in-mod tests that still consume it. The symbol's name is preserved for clarity; its scope is now narrower than pre-pd.2.
  • pd.2.6: check.py exit 1 — established noise envelope, 13th consecutive observation pattern (audit-cma onwards). pd.2 is a workspace-loader refactor with zero codegen / runtime / typecheck touch, so cannot be the source. Baseline left pristine consistent with all prior audit decisions on this envelope.

Known debt

  • 3 cargo doc warnings on [load_workspace] references in the Workspace (line 44, 49) + Registry (line 64) rustdoc — pd.1 deleted load_workspace but didn't sweep these references. Pre- pd.2 baseline already carried them; pd.2 didn't add to the count; not within pd.2 scope (no-surrounding-cleanup discipline).
  • pd.3 work explicitly out of scope: deleting examples/prelude.ail.json, the prelude_ail_and_json_parse_to_identical_module preflight test (which depends on the JSON file's existence), the include_str! of prelude.ail.json in crates/ail/src/main.rs:472-484, the carve_out_inventory.rs §C4(b) row, the form-a §C4(b) spec text edits, and the migrate-bare-cross-module-refs defensive include.

Files touched

Modified:

  • crates/ailang-core/src/lib.rs (3 rustdoc sites updated)
  • crates/ailang-core/src/workspace.rs (5 deletions + module rustdoc reframe + 2 pd.1 in-mod test repairs + 6 in-mod test placeholders + test-mod-private load_one helper restored at narrow scope)
  • crates/ailang-core/tests/workspace_pin.rs (10 relocated tests appended + helper fn write_simple_module_json + preamble extension)
  • crates/ailang-surface/src/lib.rs (re-export of parse_prelude + PRELUDE_AIL)
  • crates/ailang-surface/src/loader.rs (PRELUDE_AIL const + parse_prelude fn added; load_workspace body 3-line → 7-line composition rewrite; module rustdoc reframed; imports updated)

New:

  • crates/ailang-surface/tests/prelude_parse_pin.rs (1 test)
  • crates/ailang-surface/tests/prelude_module_hash_pin.rs (2 tests: cross-form-identity preflight + literal-hex anchor 3abe0d3fa3c11c99)
  • crates/ailang-surface/tests/prelude_injection_pin.rs (2 tests: inject + reservation pin)

Stats

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