iter pd.3: prelude.ail.json retired — milestone prelude-decouple closed

Deleted examples/prelude.ail.json (-559 lines). The cross-form-identity
preflight test that ratified pd.3's load-bearing assumption at module
hash 3abe0d3fa3c11c99 has discharged its purpose and was removed
along with its supporting bytes; the long-term prelude_parse_yields_canonical_hash
anchor stays. The ail-CLI migrate-bare-cross-module-refs subcommand's
defensive include + lockstep skip-branch removed; the rewrite logic's
prelude-fallback capability silently retired with the synthetic insert
(known debt, doc-comment updated to reflect). One in-mod core test
relocated to crates/ailang-core/tests/workspace_pin.rs (pd.2.4 dev-dep
cycle precedent — in-mod call to ailang_surface still structurally
impossible).

carve_out_inventory.rs: 8 → 7 carve-outs, §C4(b) row dropped, header
sentence updated. form-a-default-authoring.md §C4(b) gets a "RETIRED
2026-05-14 by milestone prelude-decouple" status marker; original
historical text preserved. New prelude_decouple_carve_out_pin.rs
asserts examples/prelude.ail.json does NOT exist.

Milestone prelude-decouple closed: prelude exists on disk only as
examples/prelude.ail; ailang-core embeds zero prelude bytes; CLAUDE.md
"authors write .ail" doctrine holds without exception.

cargo test --workspace 573 green; bench/cross_lang.py +
bench/compile_check.py exit 0; bench/check.py exit 1 with the
established noise envelope (14th observation of bench_list_sum.bump_s
since audit-cma; pd.3 is filesystem + spec text + test deletion only,
no codegen / runtime / typecheck path edited — baseline pristine).

Folds in the orphan pd.2 INDEX entry that was left uncommitted in the
working tree.
This commit is contained in:
2026-05-14 13:24:40 +02:00
parent c0dd2bc334
commit 9a8d3850e7
11 changed files with 354 additions and 664 deletions
@@ -0,0 +1,29 @@
//! pd.3 (`prelude-decouple` milestone): pin that
//! `examples/prelude.ail.json` does NOT exist on disk. Surfaces
//! immediately if the file is recreated by accident or a future
//! refactor reverts the embed-source swap.
//!
//! Companion to the `examples_ail_json_inventory_matches_carve_outs`
//! test in `crates/ailang-core/tests/carve_out_inventory.rs`: that
//! one asserts the carve-out list is exactly seven; this one
//! asserts the eighth (retired) file specifically does not exist.
//! Either test detects an accidental re-introduction; this one
//! pinpoints the file by name in the error message.
use std::path::Path;
#[test]
fn prelude_ail_json_does_not_exist_on_disk() {
let path = Path::new("../../examples/prelude.ail.json");
assert!(
!path.exists(),
"examples/prelude.ail.json must NOT exist after the prelude-\
decouple milestone (closed 2026-05-14). The prelude is now \
embedded as prelude.ail in ailang-surface. If you intentionally \
re-introduced this file, you must also: (a) remove this pin, \
(b) restore the §C4 (b) entry in carve_out_inventory.rs, \
(c) revert the §C4 (b) status marker in \
docs/specs/2026-05-13-form-a-default-authoring.md, and \
(d) record the rationale in a per-iter journal."
);
}
@@ -1,46 +1,17 @@
//! pd.2 (`prelude-decouple` milestone): pin the canonical identity of
//! the parsed prelude.
//! pd.2/pd.3 (`prelude-decouple` milestone): pin the canonical
//! identity of the parsed prelude as a literal hex hash. Updates
//! intentionally on prelude-content changes (record the why in the
//! per-iter journal); accidental drift trips immediately.
//!
//! Two tests:
//! 1. `prelude_ail_and_json_parse_to_identical_module` — cross-form-
//! identity preflight. Asserts that parsing `examples/prelude.ail`
//! via `ailang_surface::parse` produces a `Module` with the same
//! `module_hash` as deserialising `examples/prelude.ail.json` via
//! `serde_json::from_str`. This is the load-bearing assumption
//! that justifies pd.3's deletion of the JSON file. pd.3 drops
//! this test along with the JSON.
//! 2. `prelude_parse_yields_canonical_hash` — long-term anchor.
//! Asserts the literal hex hash of the parsed Module. Updates
//! intentionally on prelude-content changes (with a JOURNAL entry
//! naming why); accidental drift trips immediately.
//! pd.2 also carried a `prelude_ail_and_json_parse_to_identical_module`
//! cross-form-identity preflight that compared `parse(prelude.ail)`
//! against `serde_json::from_str(prelude.ail.json)`. The preflight
//! discharged its purpose at pd.2 close (PASSED at hash
//! `3abe0d3fa3c11c99`) and was retired in pd.3 alongside the
//! `prelude.ail.json` file.
use ailang_core::workspace::module_hash;
use ailang_surface::{parse_prelude, PRELUDE_AIL};
const PRELUDE_JSON: &str = include_str!("../../../examples/prelude.ail.json");
#[test]
fn prelude_ail_and_json_parse_to_identical_module() {
// Form A path: parse prelude.ail via ailang_surface::parse.
let from_ail = parse_prelude();
// Form B path: deserialise prelude.ail.json via serde_json (the
// pre-pd.2 path; matches what `ailang_core::workspace::load_prelude`
// did before retirement).
let from_json: ailang_core::ast::Module = serde_json::from_str(PRELUDE_JSON)
.expect("examples/prelude.ail.json must deserialise");
let h_ail = module_hash(&from_ail);
let h_json = module_hash(&from_json);
assert_eq!(
h_ail, h_json,
"cross-form-identity preflight failed:\n parse(prelude.ail) hash: {}\n deserialize(prelude.ail.json) hash: {}\n\
The two parse paths produce structurally different `Module` values for the same prelude content. \
pd.3 must NOT delete prelude.ail.json until this is investigated.",
h_ail, h_json,
);
}
use ailang_surface::parse_prelude;
#[test]
fn prelude_parse_yields_canonical_hash() {
@@ -55,6 +26,3 @@ fn prelude_parse_yields_canonical_hash() {
hex below + record the why in the per-iter journal."
);
}
// PRELUDE_AIL re-exported for any future test that wants the raw bytes.
const _: &str = PRELUDE_AIL;