diff --git a/dag/tools/namespace_import_closure_behavioral_transport.dag b/dag/tools/namespace_import_closure_behavioral_transport.dag index e75a2cc98b1..bdc58c5730b 100644 --- a/dag/tools/namespace_import_closure_behavioral_transport.dag +++ b/dag/tools/namespace_import_closure_behavioral_transport.dag @@ -14,7 +14,7 @@ data nic_discriminating_control_note: String = "DESIGN section 5 discriminating data nic_scaffold_dissolution_trigger: String = "SCAFFOLD dissolve-on: nic_provider_source / nic_provider_missing_names_source / nic_consumer_source are hand-authored fixture module sources (String carriers), the meta-harness around the receipt rather than a forked emit path — the emit itself is the real gunbc binary on real bytes. They are embedded rather than committed as .dag files on purpose: the consumer deliberately references a sibling WITHOUT importing it, so committing it under a discovered source root would feed a deliberately-unresolved module to the whole-tree compile-clean gate. DISSOLVES WHEN the step-2 typed refusal lands in the resolver (v1 04_resolve: UnlistedImportUse promoted to an error, or the resolver emitting the use-line intent directly), which deletes the reference_derived_use_lines pass and this receipt with it." -data nic_provider_source: String = "module witness.pilot.emit_provider\n\ntype PilotColor = PilotRed | PilotGreen | PilotBlue\n\nfn pilot_provider_flag() -> Bool \{ true \}\n\nfn pilot_provider_default() -> PilotColor \{ PilotRed \}\n" +data nic_provider_source: String = "module witness.pilot.emit_provider\n\ntype PilotColor = PilotRed | PilotGreen | PilotBlue\n\ntype PilotWidget = PilotWidgetA | PilotWidgetB\n\nfn pilot_provider_flag() -> Bool \{ true \}\n\nfn pilot_provider_default() -> PilotColor \{ PilotRed \}\n\nfn pilot_provider_widget_default() -> PilotWidget \{ PilotWidgetA \}\n" data nic_provider_missing_names_source: String = "module witness.pilot.emit_provider\n\ntype PilotUnrelated = PilotUnrelatedA | PilotUnrelatedB\n\nfn pilot_unrelated_flag() -> Bool \{ false \}\n" @@ -22,6 +22,8 @@ data nic_consumer_source: String = "module witness.pilot.emit_consumer\n\nfn pil data nic_consumer_partial_import_source: String = "module witness.pilot.emit_consumer_partial\n\nimport witness.pilot.emit_provider { PilotColor }\n\nfn pilot_consumer_partial_default() -> PilotColor \{ pilot_provider_default() \}\n" +data nic_consumer_partial_import_type_source: String = "module witness.pilot.emit_consumer_partial_type\n\nimport witness.pilot.emit_provider { PilotColor }\n\nfn pilot_consumer_partial_type_default() -> PilotWidget \{ pilot_provider_widget_default() \}\n" + data nic_scratch_under_workspace_root_note: String = "The fixture SOURCE tree is mktemp'd under the workspace root (target/, already gitignored), not under /tmp, because the v1 CLI's repo-path grant refuses a source path outside the process workspace root (cli_run repo_relative_path_normalized, modeled by gunbc.cli_run_repo_grant) — a bare mktemp -d source root panics the emit. The OUTPUT dir has no such constraint and stays a plain mktemp. target/ rather than a fixed path so concurrent runs cannot collide and so a mid-run auto-commit can never capture the fixture (.gitignore is itself a generated artifact — hand-editing it drifts the artifact gate)." fn nic_emitted_crate_builds(root: String, gunbc_bin: String, provider_source: String, consumer_source: String, consumer_rel: String) -> Bool { @@ -87,6 +89,13 @@ fn nic_behavioral_receipt_holds() -> Bool { consumer_source: nic_consumer_partial_import_source, consumer_rel: "/witness/pilot/emit_consumer_partial.dag" ) + let partial_import_type_arm = nic_emitted_crate_builds( + root: root.path, + gunbc_bin: gunbc_bin, + provider_source: nic_provider_source, + consumer_source: nic_consumer_partial_import_type_source, + consumer_rel: "/witness/pilot/emit_consumer_partial_type.dag" + ) let red_arm = nic_emitted_crate_builds( root: root.path, gunbc_bin: gunbc_bin, @@ -95,5 +104,5 @@ fn nic_behavioral_receipt_holds() -> Bool { consumer_rel: "/witness/pilot/emit_consumer.dag" ) - gunbc_built && green_arm && partial_import_arm && (red_arm == false) + gunbc_built && green_arm && partial_import_arm && partial_import_type_arm && (red_arm == false) } diff --git a/src/v1/05_emit_rust.dag b/src/v1/05_emit_rust.dag index bdac85c069d..1f289102d75 100644 --- a/src/v1/05_emit_rust.dag +++ b/src/v1/05_emit_rust.dag @@ -107,7 +107,8 @@ import v1.compiler.infer_emit_info { lookup_emit_type_summary, lookup_emit_type_decl, empty_emit_graph_info, emit_info_with_fn_type_context, is_enum_in_summaries, find_variant_parent, is_known_variant, - variant_belongs_to_enum, variant_summary_key + variant_belongs_to_enum, variant_summary_key, + collect_type_node_import_surface_names } import v1.compiler.resolve { get_exported_names } import v1.compiler.infer_resolve { resolve_node, lookup_unit_variant_phantom_type, is_width_nat_type_literal } @@ -2050,6 +2051,21 @@ fn collect_value_ref_names(n: Node, source_indices: Map) - concat(self_name, concat(list_fields, opt_fields)) } +fn collect_item_type_surface_names(item: Node, source_indices: Map) -> List { + let from_ann = match item.type_annotation { + Present { value: t } => collect_type_node_import_surface_names(n: t, source_indices: source_indices) + Absent => [] + } + let from_params = item.params |> flat_map(p => + collect_type_node_import_surface_names(n: param_node_type_expr(n: p), source_indices: source_indices) + ) + let from_inferred = match item.inferred { + Present { value: Resolved { node: rt } } => collect_type_node_import_surface_names(n: rt, source_indices: source_indices) + _ => [] + } + concat(from_ann, concat(from_params, from_inferred)) +} + fn imported_names_in_use_line(line: String) -> List { if contains(line, "use ") == false { [] @@ -2063,12 +2079,13 @@ fn imported_names_in_use_line(line: String) -> List { } } -data reference_derived_use_lines_note: String = "emit_import_closure_root (§5). emit_imports wires a per-module use-line only for names in an authored import list. Namespace-only resolution (post-PR 6848) references cross-module names WITHOUT importing them, so the ref is KNOWN but the use-line is declined (advisory UnlistedImportUse, is_error_diagnostic=false) — a §5 fail-open (⊤-as-ignorance) that emits invalid Rust (E0422/E0433/E0425 downstream). This pass derives the missing use-lines from the SAME resolver signal, split by reference kind onto its precise authority (§2 Realization: one closure, two consumers): (1) TYPE refs come from the resolver's UnlistedImportUse diagnostics (04_resolve.dag resolve_node, masked && not-in-SVN at type positions) threaded through ResolvedGraph.diagnostics — zero-drift by construction, the resolver already applied its SVN mask AT RESOLVE TIME; (2) VALUE-position refs come from collect_value_ref_names, a NARROW walk that structurally excludes the type over-collection classes (container heads, field labels, deep-inferred type names): fn/data refs (FunctionValueBinding ExprVar + ExprCall callee names) AND record-literal type constructions (ExprRecordLit type name + its parent_enum) — the latter matter because a GENERIC user type constructed as `T{..}` (e.g. RealizedStep) is grounded by resolve_node (masked flips false into the defining-module descent) so it NEVER fires UnlistedImportUse, yet its bare `T` still needs a use-line. Registry cross-module resolve + is_known_variant fallback keep variant constructors routed through their parent's import. NOTE the SVN authority is resolve-time-only: env.source_visible_names is built in 04_infer's unresolved_env and consumed by resolve_node, but is NOT persisted onto TypedModule.type_env (emit reads empty_map), so emit MUST NOT re-apply an SVN filter — it would be a no-op that (worse, when non-empty) diverges from the resolve-time mask. The union is instead already-imported filtered (a name already carried by an authored import / prelude / carrier use-line is skipped — this is what keeps a fully-imported SEED module zero-drift: its refs are all in an import line) and kernel filtered (no E0252 against the runtime prelude), then cross-module registry-resolved (a same-module or local ref never registry-resolves cross-module, so it is skipped for free), then reuses emit_specific_import_block for variant/reexport correctness with a §5 direct-emit fallback (arm (c): the name resolved via registry to provider). A candidate that registry-resolves to nothing is left for the step-2 typed refusal (dotted-render #6934 residue falls here); it never fabricates a use-line. SCOPE (emit_module_full): TYPE unlisted names (arm 1) run ONLY for import-free modules — the namespace-resolution case the post-PR-6848 regression is about. VALUE refs (arm 2) run for import-bearing modules ONLY when corpus_repr_is_faithful (FaithfulFreeMonoid / v2 namespace corpus): a partial-import namespace module (e.g. v2.std.node_query importing Outcome but calling outcome_with_diagnostics) must synthesize the missing fn-value use-line without re-deriving type imports that emit_imports already owns. HostNative import-bearing modules (v1 seed) get [] — running the value walk there adds spurious/wrong use-lines (registry homonyms like kernel_span/is_type_variable) and breaks zero-drift seed regen. This is where UnlistedImportUse already fires exactly for types on import-free modules; fn-value closure extends the same derivation to partial-import FaithfulFreeMonoid modules." +data reference_derived_use_lines_note: String = "emit_import_closure_root (§5). emit_imports wires a per-module use-line only for names in an authored import list. Namespace-only resolution (post-PR 6848) references cross-module names WITHOUT importing them, so the ref is KNOWN but the use-line is declined (advisory UnlistedImportUse, is_error_diagnostic=false) — a §5 fail-open (⊤-as-ignorance) that emits invalid Rust (E0422/E0433/E0425 downstream). This pass derives the missing use-lines from the SAME resolver signal, split by reference kind onto its precise authority (§2 Realization: one closure, two consumers): (1) TYPE refs come from the resolver's UnlistedImportUse diagnostics (04_resolve.dag resolve_node, masked && not-in-SVN at type positions) threaded through ResolvedGraph.diagnostics — zero-drift by construction, the resolver already applied its SVN mask AT RESOLVE TIME; (2) VALUE-position refs come from collect_value_ref_names, a NARROW walk that structurally excludes the type over-collection classes (container heads, field labels, deep-inferred type names): fn/data refs (FunctionValueBinding ExprVar + ExprCall callee names) AND record-literal type constructions (ExprRecordLit type name + its parent_enum) — the latter matter because a GENERIC user type constructed as `T{..}` (e.g. RealizedStep) is grounded by resolve_node (masked flips false into the defining-module descent) so it NEVER fires UnlistedImportUse, yet its bare `T` still needs a use-line; (3) TYPE-surface refs on item signatures come from collect_type_node_import_surface_names (04_emit_info.dag single authority) over each item's type_annotation, param types, and inferred Resolved return/signature — covering masked-at-resolve TYPE positions (e.g. partial-import return type PilotWidget) that never enter UnlistedImportUse and are not ExprVar/ExprCall harvests. Registry cross-module resolve + is_known_variant fallback keep variant constructors routed through their parent's import. NOTE the SVN authority is resolve-time-only: env.source_visible_names is built in 04_infer's unresolved_env and consumed by resolve_node, but is NOT persisted onto TypedModule.type_env (emit reads empty_map), so emit MUST NOT re-apply an SVN filter — it would be a no-op that (worse, when non-empty) diverges from the resolve-time mask. The union is instead already-imported filtered (a name already carried by an authored import / prelude / carrier use-line is skipped — this is what keeps a fully-imported SEED module zero-drift: its refs are all in an import line) and kernel filtered (no E0252 against the runtime prelude), then cross-module registry-resolved (a same-module or local ref never registry-resolves cross-module, so it is skipped for free), then reuses emit_specific_import_block for variant/reexport correctness with a §5 direct-emit fallback (arm (c): the name resolved via registry to provider). A candidate that registry-resolves to nothing is left for the step-2 typed refusal (dotted-render #6934 residue falls here); it never fabricates a use-line. SCOPE (emit_module_full): import-free modules run the full union (TYPE unlisted + VALUE refs) — the namespace-resolution case the post-PR-6848 regression is about. Import-bearing modules run reference_derived_use_lines ONLY when corpus_repr_is_faithful (FaithfulFreeMonoid / v2 namespace corpus): a partial-import namespace module (e.g. v2.std.node_query importing Outcome but calling outcome_with_diagnostics, or importing Outcome but annotating NamedEdgeTargetLookup) must synthesize BOTH the missing fn-value use-line AND the missing type use-line without duplicating names emit_imports already owns. HostNative import-bearing modules (v1 seed) get [] — running the walk there adds spurious/wrong use-lines (registry homonyms like kernel_span/is_type_variable) and breaks zero-drift seed regen." fn reference_derived_use_lines(items: List, unlisted_type_names: List, this_module_name: String, registry: Map, emit_info: EmitGraphInfo, local_type_names: List, already_imported_names: List, export_sets: Map>, typed_modules: List, source_indices: Map, module_index: ModuleIndex) -> List { { let value_names = unique_strings(items: items |> flat_map(item => collect_value_ref_names(n: item, source_indices: source_indices))) - let candidates = unique_strings(items: concat(unlisted_type_names, value_names)) + let type_surface_names = unique_strings(items: items |> flat_map(item => collect_item_type_surface_names(item: item, source_indices: source_indices))) + let candidates = unique_strings(items: concat(concat(unlisted_type_names, value_names), type_surface_names)) let already = already_imported_names |> fold(init: empty_map(), f: (acc, nm) => map_insert(acc, nm, true)) let unlisted = candidates |> flat_map(name => if emit_map_has(m: already, key: name) || is_kernel_type(name: name) { @@ -2171,7 +2188,7 @@ fn emit_module_full(typed_module: TypedModule, registry: Map, } else if corpus_repr_is_faithful(corpus_repr: emit_info.corpus_repr) { reference_derived_use_lines( items: typed_module.items, - unlisted_type_names: [], + unlisted_type_names: unlisted_type_names, this_module_name: authored_name(env: scope.type_env, node: m), registry: registry, emit_info: emit_info, diff --git a/src/v1/stage0/src/std_coercion.rs b/src/v1/stage0/src/std_coercion.rs index b0f4558fb7e..d2cfa9dcfe1 100644 --- a/src/v1/stage0/src/std_coercion.rs +++ b/src/v1/stage0/src/std_coercion.rs @@ -1,6 +1,7 @@ // Generated by v1 compiler -- do not edit. // Source module: std.coercion +pub use crate::std_types::List; use crate::v1_rt; use crate::v1_rt::Witness; use crate::v1_rt::Witness::{Holds, Violates}; diff --git a/src/v1/stage0/src/std_http_path.rs b/src/v1/stage0/src/std_http_path.rs index 46846103351..72e9f9ab16e 100644 --- a/src/v1/stage0/src/std_http_path.rs +++ b/src/v1/stage0/src/std_http_path.rs @@ -2,6 +2,7 @@ // Source module: std.http_path use self::UrlPathToken::*; +pub use crate::std_types::List; use crate::v1_rt; use crate::v1_rt::Witness; use crate::v1_rt::Witness::{Holds, Violates}; diff --git a/src/v1/stage0/src/std_syntax.rs b/src/v1/stage0/src/std_syntax.rs index f1e630736d2..8ec3bae13c1 100644 --- a/src/v1/stage0/src/std_syntax.rs +++ b/src/v1/stage0/src/std_syntax.rs @@ -6,6 +6,7 @@ use self::BinOp::*; use self::BodyKind::*; use self::ItemFormKind::*; use self::LiteralValue::*; +pub use crate::std_types::List; use crate::v1_rt; use crate::v1_rt::Witness; use crate::v1_rt::Witness::{Holds, Violates}; diff --git a/src/v1/stage0/src/v1_compiler_emit_rust.rs b/src/v1/stage0/src/v1_compiler_emit_rust.rs index 8f698513863..0c24e69cecd 100644 --- a/src/v1/stage0/src/v1_compiler_emit_rust.rs +++ b/src/v1/stage0/src/v1_compiler_emit_rust.rs @@ -59,9 +59,9 @@ pub use crate::v1_compiler_infer::{ use crate::v1_compiler_infer_emit_info::RustCorpusRepr::{FaithfulFreeMonoid, HostNative}; use crate::v1_compiler_infer_emit_info::TypeRepr::{EnumRepr, StructRepr}; pub use crate::v1_compiler_infer_emit_info::{ - emit_info_with_fn_type_context, empty_emit_graph_info, find_variant_parent, - is_enum_in_summaries, is_known_variant, lookup_emit_type_decl, lookup_emit_type_summary, - variant_belongs_to_enum, variant_summary_key, + collect_type_node_import_surface_names, emit_info_with_fn_type_context, empty_emit_graph_info, + find_variant_parent, is_enum_in_summaries, is_known_variant, lookup_emit_type_decl, + lookup_emit_type_summary, variant_belongs_to_enum, variant_summary_key, }; pub use crate::v1_compiler_infer_emit_info::{ EmitGraphInfo, RustCorpusRepr, TypeRepr, TypeSummary, @@ -4638,6 +4638,42 @@ pub fn collect_value_ref_names( }) } +pub fn collect_item_type_surface_names( + item: Rc, + source_indices: Rc>>, +) -> Rc> { + { + let from_ann = match item.type_annotation.clone() { + Some(t) => collect_type_node_import_surface_names(t.clone(), source_indices.clone()), + None => Rc::new(vec![]), + }; + let from_params = Rc::new({ + let mut __result = Vec::new(); + for p in item.params.clone().iter().cloned() { + __result.extend( + (*collect_type_node_import_surface_names( + param_node_type_expr(p.clone()), + source_indices.clone(), + )) + .iter() + .cloned(), + ); + } + __result + }); + let from_inferred = match item.inferred.clone().as_deref().cloned() { + Some(InferredNode::Resolved { node: rt, .. }) => { + collect_type_node_import_surface_names(rt.clone(), source_indices.clone()) + } + _ => Rc::new(vec![]), + }; + v1_rt::concat( + from_ann.clone(), + v1_rt::concat(from_params.clone(), from_inferred.clone()), + ) + } +} + pub fn imported_names_in_use_line(line: String) -> Rc> { if (v1_rt::contains(line.clone(), "use ".to_string()) == false) { Rc::new(vec![]) @@ -4702,7 +4738,7 @@ pub fn imported_names_in_use_line(line: String) -> Rc> { pub fn reference_derived_use_lines_note() -> String { thread_local! { static CACHED: String = { - "emit_import_closure_root (§5). emit_imports wires a per-module use-line only for names in an authored import list. Namespace-only resolution (post-PR 6848) references cross-module names WITHOUT importing them, so the ref is KNOWN but the use-line is declined (advisory UnlistedImportUse, is_error_diagnostic=false) — a §5 fail-open (⊤-as-ignorance) that emits invalid Rust (E0422/E0433/E0425 downstream). This pass derives the missing use-lines from the SAME resolver signal, split by reference kind onto its precise authority (§2 Realization: one closure, two consumers): (1) TYPE refs come from the resolver's UnlistedImportUse diagnostics (04_resolve.dag resolve_node, masked && not-in-SVN at type positions) threaded through ResolvedGraph.diagnostics — zero-drift by construction, the resolver already applied its SVN mask AT RESOLVE TIME; (2) VALUE-position refs come from collect_value_ref_names, a NARROW walk that structurally excludes the type over-collection classes (container heads, field labels, deep-inferred type names): fn/data refs (FunctionValueBinding ExprVar + ExprCall callee names) AND record-literal type constructions (ExprRecordLit type name + its parent_enum) — the latter matter because a GENERIC user type constructed as `T{..}` (e.g. RealizedStep) is grounded by resolve_node (masked flips false into the defining-module descent) so it NEVER fires UnlistedImportUse, yet its bare `T` still needs a use-line. Registry cross-module resolve + is_known_variant fallback keep variant constructors routed through their parent's import. NOTE the SVN authority is resolve-time-only: env.source_visible_names is built in 04_infer's unresolved_env and consumed by resolve_node, but is NOT persisted onto TypedModule.type_env (emit reads empty_map), so emit MUST NOT re-apply an SVN filter — it would be a no-op that (worse, when non-empty) diverges from the resolve-time mask. The union is instead already-imported filtered (a name already carried by an authored import / prelude / carrier use-line is skipped — this is what keeps a fully-imported SEED module zero-drift: its refs are all in an import line) and kernel filtered (no E0252 against the runtime prelude), then cross-module registry-resolved (a same-module or local ref never registry-resolves cross-module, so it is skipped for free), then reuses emit_specific_import_block for variant/reexport correctness with a §5 direct-emit fallback (arm (c): the name resolved via registry to provider). A candidate that registry-resolves to nothing is left for the step-2 typed refusal (dotted-render #6934 residue falls here); it never fabricates a use-line. SCOPE (emit_module_full): TYPE unlisted names (arm 1) run ONLY for import-free modules — the namespace-resolution case the post-PR-6848 regression is about. VALUE refs (arm 2) run for import-bearing modules ONLY when corpus_repr_is_faithful (FaithfulFreeMonoid / v2 namespace corpus): a partial-import namespace module (e.g. v2.std.node_query importing Outcome but calling outcome_with_diagnostics) must synthesize the missing fn-value use-line without re-deriving type imports that emit_imports already owns. HostNative import-bearing modules (v1 seed) get [] — running the value walk there adds spurious/wrong use-lines (registry homonyms like kernel_span/is_type_variable) and breaks zero-drift seed regen. This is where UnlistedImportUse already fires exactly for types on import-free modules; fn-value closure extends the same derivation to partial-import FaithfulFreeMonoid modules.".to_string() + "emit_import_closure_root (§5). emit_imports wires a per-module use-line only for names in an authored import list. Namespace-only resolution (post-PR 6848) references cross-module names WITHOUT importing them, so the ref is KNOWN but the use-line is declined (advisory UnlistedImportUse, is_error_diagnostic=false) — a §5 fail-open (⊤-as-ignorance) that emits invalid Rust (E0422/E0433/E0425 downstream). This pass derives the missing use-lines from the SAME resolver signal, split by reference kind onto its precise authority (§2 Realization: one closure, two consumers): (1) TYPE refs come from the resolver's UnlistedImportUse diagnostics (04_resolve.dag resolve_node, masked && not-in-SVN at type positions) threaded through ResolvedGraph.diagnostics — zero-drift by construction, the resolver already applied its SVN mask AT RESOLVE TIME; (2) VALUE-position refs come from collect_value_ref_names, a NARROW walk that structurally excludes the type over-collection classes (container heads, field labels, deep-inferred type names): fn/data refs (FunctionValueBinding ExprVar + ExprCall callee names) AND record-literal type constructions (ExprRecordLit type name + its parent_enum) — the latter matter because a GENERIC user type constructed as `T{..}` (e.g. RealizedStep) is grounded by resolve_node (masked flips false into the defining-module descent) so it NEVER fires UnlistedImportUse, yet its bare `T` still needs a use-line; (3) TYPE-surface refs on item signatures come from collect_type_node_import_surface_names (04_emit_info.dag single authority) over each item's type_annotation, param types, and inferred Resolved return/signature — covering masked-at-resolve TYPE positions (e.g. partial-import return type PilotWidget) that never enter UnlistedImportUse and are not ExprVar/ExprCall harvests. Registry cross-module resolve + is_known_variant fallback keep variant constructors routed through their parent's import. NOTE the SVN authority is resolve-time-only: env.source_visible_names is built in 04_infer's unresolved_env and consumed by resolve_node, but is NOT persisted onto TypedModule.type_env (emit reads empty_map), so emit MUST NOT re-apply an SVN filter — it would be a no-op that (worse, when non-empty) diverges from the resolve-time mask. The union is instead already-imported filtered (a name already carried by an authored import / prelude / carrier use-line is skipped — this is what keeps a fully-imported SEED module zero-drift: its refs are all in an import line) and kernel filtered (no E0252 against the runtime prelude), then cross-module registry-resolved (a same-module or local ref never registry-resolves cross-module, so it is skipped for free), then reuses emit_specific_import_block for variant/reexport correctness with a §5 direct-emit fallback (arm (c): the name resolved via registry to provider). A candidate that registry-resolves to nothing is left for the step-2 typed refusal (dotted-render #6934 residue falls here); it never fabricates a use-line. SCOPE (emit_module_full): import-free modules run the full union (TYPE unlisted + VALUE refs) — the namespace-resolution case the post-PR-6848 regression is about. Import-bearing modules run reference_derived_use_lines ONLY when corpus_repr_is_faithful (FaithfulFreeMonoid / v2 namespace corpus): a partial-import namespace module (e.g. v2.std.node_query importing Outcome but calling outcome_with_diagnostics, or importing Outcome but annotating NamedEdgeTargetLookup) must synthesize BOTH the missing fn-value use-line AND the missing type use-line without duplicating names emit_imports already owns. HostNative import-bearing modules (v1 seed) get [] — running the walk there adds spurious/wrong use-lines (registry homonyms like kernel_span/is_type_variable) and breaks zero-drift seed regen.".to_string() }; } CACHED.with(|c: &String| c.clone()) @@ -4733,9 +4769,20 @@ pub fn reference_derived_use_lines( } __result })); + let type_surface_names = unique_strings(Rc::new({ + let mut __result = Vec::new(); + for item in items.clone().iter().cloned() { + __result.extend( + (*collect_item_type_surface_names(item.clone(), source_indices.clone())) + .iter() + .cloned(), + ); + } + __result + })); let candidates = unique_strings(v1_rt::concat( - unlisted_type_names.clone(), - value_names.clone(), + v1_rt::concat(unlisted_type_names.clone(), value_names.clone()), + type_surface_names.clone(), )); let already = already_imported_names.clone().iter().cloned().fold( v1_rt::rc_empty_map::(), @@ -5041,7 +5088,7 @@ pub fn emit_module_full( if corpus_repr_is_faithful(emit_info.corpus_repr.clone()) { reference_derived_use_lines( typed_module.items.clone(), - Rc::new(vec![]), + unlisted_type_names.clone(), authored_name(scope.type_env.clone(), m.clone()), registry.clone(), emit_info.clone(),