diff --git a/dag/tools/self_host_curated_seed_linked_harness.dag b/dag/tools/self_host_curated_seed_linked_harness.dag index c15ce4f42ad..812d0b684e9 100644 --- a/dag/tools/self_host_curated_seed_linked_harness.dag +++ b/dag/tools/self_host_curated_seed_linked_harness.dag @@ -23,7 +23,7 @@ data cssl_std_bridge_scaffold_dissolution_trigger: String = "dissolve-on: dag/to data cssl_emit_artifact_sanitize_scaffold_debt: String = "SCAFFOLD — probe-side string scrub in cssl_seed_linked_closure_assembly.rs on emitted v2_std_witness.rs (Witness import/enum collision with v1_rt). The v2_std_integer.rs arm (blind Int128..UInt8 line-range strip) was REMOVED 2026-07-18: real-entry reproduction against the production cssl_seed_linked_behavioral_receipt path (04_infer.dag full closure, primary-precedence pool, rebuilt cssl_assemble binary) found the emit source already writes exactly one copy of each struct — the strip was silently deleting the only legitimate copy, not a duplicate; its dissolve-on trigger had already fired with no live rustc symptom to mark it (a corrupting no-op, latent breakage for any future Int128..UInt8 consumer). Same class as scripts/lib/apply_std_seed_link_assembly.sh measurement patches. Remaining scrub: dissolve-on #6775 emitter Rc/Optional coherence greens the Witness collision at emit source; delete sanitize_emitter_artifact_in_place when curated cargo probes pass without scrub (countable via CSSL_ASSEMBLE: sanitize scaffold applied stderr line)." -data cssl_closure_assembly_note: String = "Generic closure assembly (arm 2 compiler re-export + arm 3 shared std-bridge + arm 4 dag/std emit-retain + arm 5 v2_lens/peripheral emit-retain + arm 6 bootstrap_inline skip + typed Refused). Dep manifest = gunbc-emitted src/lib.rs pub mod list. Entry module bytes hash-locked. Hand shim_lib_rel/shim_writes = optional fast-path (body_producer); skipped when non-empty." +data cssl_closure_assembly_note: String = "Generic closure assembly: dep manifest = gunbc-emitted src/lib.rs pub mod list (structural closure-membership authority). Entry module bytes hash-locked. Emit-retain rule: any module listed in the emitted closure manifest with a matching emitted src sibling .rs is emit-retained — seed lib.rs pub mod presence is external-oracle only and must NOT trigger seed-replace (seed stubs may lack gunbc type surface). Hand shim_lib_rel/shim_writes = optional fast-path override (body_producer / behavioral receipts)." data cssl_medium_as_string_scaffold_debt: String = "SCAFFOLD — tracked debt: Cargo.toml TOML markup authored via concat string literals in cssl_v1_compiled_cargo_* (same class as slb_cargo_toml / ssuv_cargo_toml / peh_cargo_toml). Probe-only throwaway measurement — not gate-side substrate. dissolve-on: extdeps cargo manifest grammar rows + bash-emit #5828 OR harness consolidation; until then one concat producer per variant ([[bin]] witness vs [lib] probe), consumed only via tools.self_host_curated_probe_cargo." diff --git a/scripts/lib/apply_std_seed_link_assembly.sh b/scripts/lib/apply_std_seed_link_assembly.sh index c182d40a96e..332801c80ec 100755 --- a/scripts/lib/apply_std_seed_link_assembly.sh +++ b/scripts/lib/apply_std_seed_link_assembly.sh @@ -79,9 +79,8 @@ apply_std_seed_link_assembly() { _sanitize_emitter_artifact_in_place "$src_dir/$mod.rs" ;; v2_compiler_*|extdeps_*|v1_compiler_*) - if _seed_mod_exists "$mod"; then - _write_seed_reexport_shim "$src_dir/$mod.rs" "$mod" - fi + # Closure manifest membership → emit-retain; seed pub mod is not a replace trigger. + _sanitize_emitter_artifact_in_place "$src_dir/$mod.rs" ;; v2_std_*|v1_rt|v2_extdeps_*) _sanitize_emitter_artifact_in_place "$src_dir/$mod.rs" diff --git a/src/v1/stage0/src/cssl_seed_linked_closure_assembly.rs b/src/v1/stage0/src/cssl_seed_linked_closure_assembly.rs index 5d4845e902a..7c53451d44d 100644 --- a/src/v1/stage0/src/cssl_seed_linked_closure_assembly.rs +++ b/src/v1/stage0/src/cssl_seed_linked_closure_assembly.rs @@ -106,6 +106,24 @@ fn write_compiler_seed_reexport(dest: &Path, seed_mod: &str) -> Result<(), Assem Ok(()) } +/// Membership in the gunbc-emitted closure: module listed in emitted `src/lib.rs` +/// and backed by an emitted `src/{module}.rs`. The assembly loop only visits +/// such modules; `seed lib.rs` `pub mod` presence is an external-oracle fact and +/// must not trigger seed-replace for closure members (seed stubs may lack +/// gunbc-emitted type surface — e.g. `ResolvedTree` in `v2_compiler_resolve`). +fn is_emitted_closure_member(emitted_lib_rs: &Path, module: &str, dest: &Path) -> bool { + dest.is_file() + && parse_closure_mods(emitted_lib_rs) + .map(|mods| mods.iter().any(|m| m == module)) + .unwrap_or(false) +} + +fn is_compiler_family_module(module: &str) -> bool { + module.starts_with("v2_compiler_") + || module.starts_with("extdeps_") + || module.starts_with("v1_compiler_") +} + /// SCAFFOLD — see `cssl_emit_artifact_sanitize_scaffold_debt` in /// `dag/tools/self_host_curated_seed_linked_harness.dag`. String-scrub on emitted /// `v2_std_witness.rs` only; dissolve-on #6775 emitter defects. The `v2_std_integer.rs` @@ -196,14 +214,13 @@ pub fn assemble_seed_linked_closure( }); } - if module.starts_with("v2_compiler_") - || module.starts_with("extdeps_") - || module.starts_with("v1_compiler_") - { - if seed_has_pub_mod(&seed_lib_rs, &module)? { + if is_compiler_family_module(&module) { + if is_emitted_closure_member(&emitted_lib_rs, &module, &dest) { + // Structural: closure manifest membership → emit-retain (gunbc surface). + sanitize_emitter_artifact_in_place(&dest)?; + } else if seed_has_pub_mod(&seed_lib_rs, &module)? { write_compiler_seed_reexport(&dest, &module)?; } else { - // Former RefusedDep arm → emit-retain; cargo is the refusal surface. sanitize_emitter_artifact_in_place(&dest)?; } continue; @@ -324,6 +341,111 @@ mod tests { assert!(kept.contains("emitted std_error_primitives")); } + #[test] + fn closure_compiler_mod_emit_retained_when_seed_also_has_pub_mod() { + let root = temp_fixture_root(); + let out = root.join("out"); + let src = out.join("src"); + fs::create_dir_all(&src).expect("src"); + fs::write( + out.join("src/lib.rs"), + "pub mod v2_compiler_resolve;\npub mod v2_compiler_infer;\n", + ) + .expect("lib"); + fs::write( + src.join("v2_compiler_resolve.rs"), + "pub struct ResolvedTree;\n", + ) + .expect("resolve"); + fs::write(src.join("v2_compiler_infer.rs"), "// entry\n").expect("infer"); + let seed_src = root.join("seed/src"); + fs::create_dir_all(&seed_src).expect("seed dir"); + fs::write( + seed_src.join("lib.rs"), + "pub mod v2_compiler_resolve;\npub mod v2_compiler_infer;\n", + ) + .expect("seed lib"); + fs::write(seed_src.join("v2_compiler_resolve.rs"), "// seed stub\n").expect("seed"); + let repo = root.join("repo"); + fs::create_dir_all(repo.join("src/v1/stage0/src")).expect("seed path"); + fs::copy( + seed_src.join("lib.rs"), + repo.join("src/v1/stage0/src/lib.rs"), + ) + .expect("copy"); + let dag = repo.join("src/v2/compiler/04_infer.dag"); + fs::create_dir_all(dag.parent().unwrap()).expect("dag dir"); + fs::write(&dag, "module v2.compiler.infer\n").expect("dag"); + let bridge = repo.join("dag/tools/self_host_std_bridge_shims"); + fs::create_dir_all(&bridge).expect("bridge"); + assemble_seed_linked_closure(&out, &dag, &repo, &bridge).expect("assemble"); + let kept = fs::read_to_string(src.join("v2_compiler_resolve.rs")).expect("read"); + assert!( + kept.contains("pub struct ResolvedTree"), + "closure member must stay emit-retained, not seed-shimmed" + ); + assert!(!kept.contains("pub use v1_compiler::")); + } + + #[test] + fn deliberate_red_control_broken_closure_dep_surfaces_at_cargo() { + let root = temp_fixture_root(); + let out = root.join("out"); + let src = out.join("src"); + fs::create_dir_all(&src).expect("src"); + fs::write( + out.join("src/lib.rs"), + "pub mod v2_compiler_tokenize;\npub mod v2_compiler_resolve;\n", + ) + .expect("lib"); + fs::write(src.join("v2_compiler_tokenize.rs"), "// entry\n").expect("entry"); + fs::write( + src.join("v2_compiler_resolve.rs"), + "pub fn broken_syntax(] {}\n", + ) + .expect("broken dep"); + let seed_src = root.join("seed/src"); + fs::create_dir_all(&seed_src).expect("seed dir"); + fs::write( + seed_src.join("lib.rs"), + "pub mod v2_compiler_resolve;\npub mod v2_compiler_tokenize;\n", + ) + .expect("seed lib"); + let repo = root.join("repo"); + fs::create_dir_all(repo.join("src/v1/stage0/src")).expect("seed path"); + fs::copy( + seed_src.join("lib.rs"), + repo.join("src/v1/stage0/src/lib.rs"), + ) + .expect("copy"); + let dag = repo.join("src/v2/compiler/01_tokenize.dag"); + fs::create_dir_all(dag.parent().unwrap()).expect("dag dir"); + fs::write(&dag, "module v2.compiler.tokenize\n").expect("dag"); + let bridge = repo.join("dag/tools/self_host_std_bridge_shims"); + fs::create_dir_all(&bridge).expect("bridge"); + assemble_seed_linked_closure(&out, &dag, &repo, &bridge).expect("assemble"); + let kept = fs::read_to_string(src.join("v2_compiler_resolve.rs")).expect("read"); + assert!( + kept.contains("broken_syntax"), + "emit-retain must not mask broken dep bytes" + ); + fs::write( + out.join("Cargo.toml"), + "[package]\nname = \"red_control\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\npath = \"src/lib.rs\"\n", + ) + .expect("cargo"); + let status = std::process::Command::new("cargo") + .args(["build", "--lib"]) + .current_dir(&out) + .env("RUSTC_WRAPPER", "") + .status() + .expect("cargo"); + assert!( + !status.success(), + "deliberate-red: broken closure dep must refuse cargo" + ); + } + #[test] fn witness_generic_enum_sanitize_strips_v1_rt_import() { let root = temp_fixture_root();