From 197ea9609e025978b52a0620ac61292131e351c2 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 27 Aug 2026 17:12:33 +0000 Subject: [PATCH 01/21] Surface #9439's two failing dispositions as typed diagnostics, and split variant delegation out of registry-absent REBUILT ON MAIN'S CONSTRUCTION. gunbc#9439 landed the per-candidate disposition twelve hours ago -- CandidateSurvived / CandidateOwnModule / CandidateRegistryAbsent / CandidateExportProofFailed, with a census and a row at (module, name, disposition) grain. My branch carried an independently written classifier with the same four arms; it is DELETED rather than merged, because two authorities for one decision is the section 3 violation and theirs is on main. WHAT #9439 DID NOT DO, by its own note: the census 'is NOT SURFACED during an ordinary build'. So the two FAILING arms still vanished from the emission -- no diagnostic, no location, nothing a build reports. That is the empty-observation narrow: the emitter answers 'this name is not part of the interface' where the truth is 'I could not prove that it was', and DESIGN rates a narrow strictly worse than the widen section 5 forbids, because a widen is merely expensive and a narrow is silently uncovered. reference_derived_row_diagnostics is a THIRD projection of rows that already exist, beside the use-lines and the census. Nothing re-derives the disposition, so the emitted crate cannot move. TWO diagnostics, not one carrying a cause (ruling: warm-hawk-909 via smart-ram-730). Registry-absent is fixed by AUTHORING AN IMPORT; export-proof-failed is fixed by making the emitter able to prove an export the provider already holds. Opposite remedies, different owners, different populations, potentially different reachability. DESIGN 4b files one row per class. THE FIFTH ARM, and it exists because it was MEASURED. A census of the failing arms over the regen seed closure returned a population dominated by bare VARIANT names -- Absent, Cons, Eq, ExprCall, Bind -- all landing in CandidateRegistryAbsent, because the registry holds declarations and a variant is not one. #9439 filters candidates by `already` and is_kernel_type only, with no variant filter ahead of the disposition, so its registry-absent column counts mostly names already correctly bound. THE ARM CARRIES ITS PARENT, and that is the design rather than a detail. The tempting shape -- one arm meaning 'variants need no import' -- is FALSE and reproduces the same conflation one level down: a variant whose parent is declared here is bound by the module's own use-glob and owes nothing, while a variant whose parent lives elsewhere needs that PARENT imported. Opposite remedies. So the arm claims the obligation is DELEGATED and names the delegate; the parent then answers for itself as its own candidate row. Objection raised by smart-ram-730, who was right that my first shape repeated the defect it was fixing. NOT ESTABLISHED, and said so on the carrier rather than assumed: delegation is sound only if every parent named by a routed row is itself a candidate. That check is mechanical and is the arm's next rung. Witness: #9439's census witness extended -- the fifth arm's discriminating RED (red against the four-arm classifier, green against five), a positive control that a non-variant still answers registry-absent, and three tests over the diagnostics (only the two failing arms produce any, both advisory, opposite remedies in the text). Co-Authored-By: Claude Opus 5 (1M context) --- src/v1/00_core.dag | 16 ++++ src/v1/05_emit_rust.dag | 95 ++++++++++++++++++- ...erived_disposition_census_witness_test.dag | 94 +++++++++++++++++- 3 files changed, 196 insertions(+), 9 deletions(-) diff --git a/src/v1/00_core.dag b/src/v1/00_core.dag index d7167cc1afc..698ae46366a 100644 --- a/src/v1/00_core.dag +++ b/src/v1/00_core.dag @@ -211,6 +211,8 @@ type CompilerDiagnostic | DeclaredTypeNotInhabited { position: String, expected: String, got: String, span: SourceSpan } | DeclaredTypeInhabitanceUndecided { position: String, reason: String, span: SourceSpan } | UnlistedImportUse { name: String, span: SourceSpan } + | ReferenceDerivedImportProviderUnknown { name: String, referencing_module: String, span: SourceSpan } + | ReferenceDerivedImportExportUnproven { name: String, referencing_module: String, provider_module: String, span: SourceSpan } | UnlistedVariantValueUse { name: String, span: SourceSpan } | AmbiguousReference { name: String, candidates: List, span: SourceSpan } | DataReferenceVisibilityBudgetExceeded { name: String, span: SourceSpan } @@ -364,6 +366,8 @@ fn diagnostic_to_span(d: CompilerDiagnostic) -> SourceSpan { DeclaredTypeNotInhabited { position: _, expected: _, got: _, span: s } => s DeclaredTypeInhabitanceUndecided { position: _, reason: _, span: s } => s UnlistedImportUse { name: _, span: s } => s + ReferenceDerivedImportProviderUnknown { name: _, referencing_module: _, span: s } => s + ReferenceDerivedImportExportUnproven { name: _, referencing_module: _, provider_module: _, span: s } => s UnlistedVariantValueUse { name: _, span: s } => s AmbiguousReference { name: _, candidates: _, span: s } => s DataReferenceVisibilityBudgetExceeded { name: _, span: s } => s @@ -448,6 +452,10 @@ fn diagnostic_to_message(d: CompilerDiagnostic) -> String { DeclaredTypeInhabitanceUndecided { position: pos, reason: r, span: _ } => concat("declared-type inhabitance is undecidable at the ", pos, " (", r, "): the modeled facts do not settle whether the produced value inhabits its declared type, so no verdict is asserted in either direction") UnlistedImportUse { name: n, span: _ } => concat("unlisted import use '", n, "' (referenced but not in any import's name list)") + ReferenceDerivedImportProviderUnknown { name: n, referencing_module: rm, span: _ } => + concat("no provider is present for '", n, "', referenced by module '", rm, "': the compile closure carries no module that declares the name, so no use-line can be synthesized and the emitted Rust references a name it never imported. The remedy is to AUTHOR AN IMPORT -- closure membership is driven by imports, so a name the module never imported is a name the closure was never asked to supply") + ReferenceDerivedImportExportUnproven { name: n, referencing_module: rm, provider_module: p, span: _ } => + concat("the provider of '", n, "' is present but its export could not be established: module '", rm, "' references the name, the registry maps it to '", p, "', and that module's transitive export surface does not establish it. The remedy is NOT an import -- the provider is already in the closure -- it is that the emitter cannot prove an export the provider may well hold") UnlistedVariantValueUse { name: n, span: _ } => concat("unlisted variant value use '", n, "' (a coproduct arm referenced but not in any import's name list)") AmbiguousReference { name: n, candidates: cs, span: _ } => concat("ambiguous reference '", n, "': ", to_string(value: cs |> count), " candidates: ", join(cs, separator: ", "), " — qualify by containment path, alias, or rename") DataReferenceVisibilityBudgetExceeded { name: n, span: _ } => @@ -507,6 +515,8 @@ data diagnostic_frontier_occurrence_key_note: String = "The canonical accessor f fn is_error_diagnostic(d: CompilerDiagnostic) -> Bool { match d { UnlistedImportUse { name: _, span: _ } => false + ReferenceDerivedImportProviderUnknown { name: _, referencing_module: _, span: _ } => false + ReferenceDerivedImportExportUnproven { name: _, referencing_module: _, provider_module: _, span: _ } => false UnlistedVariantValueUse { name: _, span: _ } => false MethodExistenceFrontierAdmitted { method: _, receiver_type: _, trigger: _, span: _ } => false ReceiverTypeUnestablished { method: _, span: _ } => false @@ -516,6 +526,8 @@ fn is_error_diagnostic(d: CompilerDiagnostic) -> Bool { } } +data reference_derived_import_refusal_severity_note: String = "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). These two diagnostics are the SURFACING half of the disposition #9439 landed: that change factored the emitter's per-candidate import decision into a typed disposition and COUNTED it, and its own note records that the census is not surfaced during an ordinary build. A census nothing surfaces is a count, not a refusal -- the failure arms still vanished from the emission, which is the empty-observation narrow DESIGN names (the emitter answering 'this name is not part of the interface' where the truth is 'I could not prove that it was'), and DESIGN rates a narrow strictly worse than the widen section 5 forbids because a widen is merely expensive and a narrow is silently uncovered. TWO DIAGNOSTICS, NOT ONE CARRYING A CAUSE (ruling: warm-hawk-909, compiler direction, relayed through smart-ram-730, 2026-08-27). The question is upstream of severity: are these one error class? They are not. ReferenceDerivedImportProviderUnknown is CandidateRegistryAbsent -- the closure carries no provider for the name -- and its remedy is to AUTHOR AN IMPORT, since closure membership is driven by imports and a name the module never imported is one the closure was never asked to supply. ReferenceDerivedImportExportUnproven is CandidateExportProofFailed -- the provider is present and its export could not be established -- and its remedy is the opposite: not an import at all, but making the emitter able to prove an export the provider may already hold. Different mechanism, different remedy, different owner, different population, potentially different reachability; one symbol over two opposite remedies would be the state-space conflation DESIGN names, and DESIGN 4b files one row per class, so two classes carry two diagnostics and two independent is_error_diagnostic booleans. BOTH ARE ADVISORY TODAY and blocking nowhere. FLIP CONDITION, and it is strictly stronger than a count: an arm flips to blocking when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient -- an observation of zero on an arm that has never produced anything is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check that is then cited as coverage, which DESIGN 4b rates worse than absent. Reachability is judged against what a FIXTURE may author, never against what the accepted corpus contains. ProviderUnknown's reachability is established: std.algebra under the src/v2/std/node.dag closure references Optional and empty_map it never imports (deep-ant-102, measured). ExportUnproven's is established by nothing yet, so if its population returns zero the MISSING FIXTURE is that class's next-rung trigger rather than a clearance to block. ProviderUnknown's roster is NOT new work: it is UnlistedImportUse clause (b) seen from the emitter side, so its trigger points at the EXISTING family-closure-SVN burndown recorded in v1.compiler.resolve resolve_node_bounded_masked_boundary and must not become a second burndown with its own schedule. Whether either flips is warm-hawk-909's call, not this module's." + data where_refinement_deferral_reason_scaffold_note: String = "owner: v1.compiler.infer (00_core diagnostic partition). lane: where-refinement literal-wall enforcement. interim: WhereRefinementUnenforced.reason is a closed-string sum enrolled in is_where_refinement_unenforced_advisory_reason; any unlisted reason fails closed blocking. bound: only the five deferral strings emitted by 04_infer today; classifier/eval/equivalence arms must change together until coproduct lands. dissolve-on: feature:where-refinement-predicate-coproduct (WhereRefinementDeferralReason coproduct on the diagnostic carrier)." fn is_interpreter_blocking_diagnostic(d: CompilerDiagnostic) -> Bool { @@ -529,6 +541,8 @@ fn is_interpreter_blocking_diagnostic(d: CompilerDiagnostic) -> Bool { ReceiverTypeUnestablished { method: _, span: _ } => false ServiceConfigReferenceJudgmentDeferred { field: _, referenced_name: _, trigger: _, span: _ } => false DeclaredTypeInhabitanceUndecided { position: _, reason: _, span: _ } => false + ReferenceDerivedImportProviderUnknown { name: _, referencing_module: _, span: _ } => false + ReferenceDerivedImportExportUnproven { name: _, referencing_module: _, provider_module: _, span: _ } => false _ => true } } @@ -541,6 +555,8 @@ fn is_discovery_corpus_advisory_typecheck_diagnostic(d: CompilerDiagnostic) -> B ReceiverTypeUnestablished { method: _, span: _ } => true ServiceConfigReferenceJudgmentDeferred { field: _, referenced_name: _, trigger: _, span: _ } => true DeclaredTypeInhabitanceUndecided { position: _, reason: _, span: _ } => true + ReferenceDerivedImportProviderUnknown { name: _, referencing_module: _, span: _ } => true + ReferenceDerivedImportExportUnproven { name: _, referencing_module: _, provider_module: _, span: _ } => true WhereRefinementUnenforced { predicate: _, formal_type: _, reason: r, span: _ } => is_where_refinement_unenforced_advisory_reason(reason: r) _ => false diff --git a/src/v1/05_emit_rust.dag b/src/v1/05_emit_rust.dag index 49e43408c99..3535df25d0d 100644 --- a/src/v1/05_emit_rust.dag +++ b/src/v1/05_emit_rust.dag @@ -19,7 +19,7 @@ import v1.std.core { MatchBoundBinding, CallSemantics, PlainCallSemantics, LookupCallSemantics, FunctionValueCallSemantics, MethodSemantics, PlainMethodSemantics, AlgebraMethodSemantics, ServiceMethodSemantics, MatchPattern, field_binding_pattern, TextFile, ErrorNode, make_error_node, InternalError, - UnlistedImportUse, AmbiguousAnonymousRecordLiteral, AmbiguousReference, DataReferenceVisibilityBudgetExceeded, ParameterDefaultFormNotAdmitted, SourceSpan, resource_use_resource, UnaryOpKind, StringPart, Text, + UnlistedImportUse, ReferenceDerivedImportProviderUnknown, ReferenceDerivedImportExportUnproven, AmbiguousAnonymousRecordLiteral, AmbiguousReference, DataReferenceVisibilityBudgetExceeded, ParameterDefaultFormNotAdmitted, SourceSpan, resource_use_resource, UnaryOpKind, StringPart, Text, Interpolation, transport_base_url, transport_has_auth, transport_auth_token, transport_auth_header_name, transport_headers, transport_env, transport_method, transport_path_template, transport_query, transport_request_body, transport_stdin, @@ -2705,7 +2705,9 @@ fn emit_rust(typed: ResolvedGraph) -> EmitResult { return EmitResult { files: [], diagnostics: anonymous_record_diags } } let test_projections = ctx.test_projections - let module_files = typed.modules |> map(tm => emit_module_full_with_dispositions(typed_module: tm, ctx: ctx, typed_modules: typed.modules).file) + let module_emissions = typed.modules |> map(tm => emit_module_full_with_dispositions(typed_module: tm, ctx: ctx, typed_modules: typed.modules)) + let module_files = module_emissions |> map(e => e.file) + let import_refusals = module_emissions |> flat_map(e => e.import_refusals) let test_files = typed.modules |> map(tm => emit_test_file( module_name: authored_name_at(source_indices: tm.type_env.source_indices, node: tm.module), @@ -2741,7 +2743,7 @@ fn emit_rust(typed: ResolvedGraph) -> EmitResult { let emitted_files = concat([cargo, lib_file, main_file], all_mod_files, compiler_tests_file, test_files) let population_manifest = emit_emitted_population_manifest(files: emitted_files) let files = concat(emitted_files, [population_manifest]) - EmitResult { files: files, diagnostics: [] } + EmitResult { files: files, diagnostics: import_refusals } } // THE EMITTER DECLARES THE POPULATION IT PRODUCED. This is the one place in the repository @@ -3677,9 +3679,34 @@ fn collect_item_realized_surface_names(item: Node, shared_types: Set, so // CandidateSurvived and CandidateExportProofFailed both carry the provider module because both are // facts ABOUT a provider -- one proven to export the symbol, one not -- and a census that could not // name the provider could not tell anyone where to look. +// CandidateVariantDelegatedToParent is the fifth arm and it exists BECAUSE IT WAS MEASURED, not +// because it was reasoned. The first census of the two failing arms over the regen seed closure +// returned a population dominated by bare VARIANT names -- Absent, Cons, Eq, ExprCall, Bind -- every +// one of them landing in CandidateRegistryAbsent, because the bare-name registry holds DECLARATIONS +// and a variant is not one. Classifying them there answers "I could not find a provider for this" +// where the truth is "this name never had a provider of its own", which is the +// not-applicable-rendered-as-malformed class, and it made the registry-absent column count mostly +// names whose binding is already correct. +// +// THE ARM CARRIES ITS PARENT, AND THAT IS THE WHOLE DESIGN. The tempting shape -- one arm meaning +// "variants need no import" -- is FALSE and reproduces the very conflation this arm repairs, one +// level down: a variant whose parent enum is declared HERE is bound by the module's own +// `use self::E::*` and owes nothing, while a variant whose parent lives in another module needs that +// PARENT imported. Opposite remedies. So the arm does not claim "nothing is owed"; it claims the +// obligation is DELEGATED, and names the delegate. The parent then answers for itself as its own +// candidate row -- filtered as CandidateOwnModule when local, judged by the registry and the export +// wall when not -- so the two cases separate where they are decidable instead of being asserted here. +// +// WHAT IS NOT ESTABLISHED, stated rather than assumed: this delegation is sound only if every parent +// named by a routed row is ITSELF a candidate. collect_value_ref_names harvests a record literal's +// parent_enum and expand_variant_payload_struct_imports expands payload structs, but no invariant +// says that covers every reference form. The check is mechanical and is the arm's next rung: no +// parent_enum may appear in a routed row without appearing as a row of its own. Until that is +// measured, a routed row is a claim about WHERE the obligation went, never that it was discharged. type ReferenceDerivedCandidateDisposition = CandidateSurvived { provider_module: String } | CandidateOwnModule + | CandidateVariantDelegatedToParent { parent_enum: String } | CandidateRegistryAbsent | CandidateExportProofFailed { provider_module: String } @@ -3698,7 +3725,13 @@ type ReferenceDerivedCandidateRow { // emitted before; the census takes the same rows and counts them. Byte-identity of the emitted crate // is therefore STRUCTURAL rather than asserted: the emitted use-lines cannot move unless this // function's answer moves, because there is no second copy of the decision to drift from. -fn reference_derived_candidate_disposition(name: String, this_module_name: String, registry: Map, export_sets: Map>, typed_modules: List, source_indices: Map, module_index: ModuleIndex) -> ReferenceDerivedCandidateDisposition { +fn reference_derived_candidate_disposition(name: String, this_module_name: String, registry: Map, export_sets: Map>, typed_modules: List, source_indices: Map, module_index: ModuleIndex, type_summaries: Map, variant_to_enum: Map) -> ReferenceDerivedCandidateDisposition { + if is_known_variant(type_summaries: type_summaries, name: name) { + match map_get(variant_to_enum, name) { + Present { value: parent } => CandidateVariantDelegatedToParent { parent_enum: parent } + Absent => CandidateRegistryAbsent + } + } else { match map_get(registry, name) { Present { value: info } => if info.module_name == this_module_name { @@ -3710,6 +3743,54 @@ fn reference_derived_candidate_disposition(name: String, this_module_name: Strin } Absent => CandidateRegistryAbsent } + } +} + +// THE SURFACING HALF. #9439 factored this decision into a typed disposition and COUNTED it; its own +// note records that the census "is NOT SURFACED during an ordinary build". So the two FAILING arms +// still vanished from the emission -- no diagnostic, no location, nothing a build could report -- +// which is the empty-observation narrow DESIGN names: the emitter answers "this name is not part of +// the interface" where the truth is "I could not prove that it was", and a narrow is strictly worse +// than the widen section 5 forbids because a widen is merely expensive and a narrow is silently +// uncovered. Downstream the emitted crate references a name it never imported and rustc says +// E0422/E0425/E0433 far from the cause; upstream the deficit's frequency is zero by construction, +// so the wall's own gaps never rank for repair. +// +// This is a THIRD projection of rows that already exist -- beside the use-lines and the census -- and +// not a second decision. Nothing here re-derives the disposition, so the emitted crate cannot move. +// +// TWO diagnostics rather than one carrying a cause, because the arms are two error classes and not +// one: registry-absent is fixed by AUTHORING AN IMPORT, export-proof-failed is fixed by making the +// emitter able to prove an export the provider already holds. Opposite remedies, different owners, +// different populations, potentially different reachability. DESIGN 4b files one row per class. +// +// LOCATION GRAIN, stated rather than implied: the referencing MODULE's declaration span, because a +// row carries (module, name, disposition) and the reference SITES are reduced to bare names by the +// candidate walk before any disposition is assigned. Carrying the originating span on the row is the +// named next rung. +fn reference_derived_row_diagnostics(rows: List, module_span: SourceSpan) -> List { + rows |> flat_map(r => + match r.disposition { + CandidateRegistryAbsent => [make_error_node( + diagnostic: ReferenceDerivedImportProviderUnknown { + name: r.name, + referencing_module: r.module_name, + span: module_span + }, + module_name: r.module_name + )] + CandidateExportProofFailed { provider_module: p } => [make_error_node( + diagnostic: ReferenceDerivedImportExportUnproven { + name: r.name, + referencing_module: r.module_name, + provider_module: p, + span: module_span + }, + module_name: r.module_name + )] + _ => [] + } + ) } fn reference_derived_candidate_survived(disposition: ReferenceDerivedCandidateDisposition) -> Bool { @@ -3726,6 +3807,7 @@ fn reference_derived_disposition_name(disposition: ReferenceDerivedCandidateDisp match disposition { CandidateSurvived { provider_module: _ } => "survived" CandidateOwnModule => "own-module" + CandidateVariantDelegatedToParent { parent_enum: _ } => "variant-delegated-to-parent" CandidateRegistryAbsent => "registry-absent" CandidateExportProofFailed { provider_module: _ } => "export-proof-failed" } @@ -3765,6 +3847,7 @@ type ReferenceDerivedCensus { candidates: Int survived: Int own_module: Int + variant_delegated_to_parent: Int registry_absent: Int export_proof_failed: Int } @@ -3774,6 +3857,7 @@ fn reference_derived_census(rows: List) -> Referen candidates: rows |> count, survived: rows |> filter(r => reference_derived_disposition_name(disposition: r.disposition) == "survived") |> count, own_module: rows |> filter(r => reference_derived_disposition_name(disposition: r.disposition) == "own-module") |> count, + variant_delegated_to_parent: rows |> filter(r => reference_derived_disposition_name(disposition: r.disposition) == "variant-delegated-to-parent") |> count, registry_absent: rows |> filter(r => reference_derived_disposition_name(disposition: r.disposition) == "registry-absent") |> count, export_proof_failed: rows |> filter(r => reference_derived_disposition_name(disposition: r.disposition) == "export-proof-failed") |> count } @@ -3818,7 +3902,7 @@ fn reference_derived_use_line_plan(items: List, unlisted_type_names: List< [ReferenceDerivedCandidateRow { module_name: this_module_name, name: name, - disposition: reference_derived_candidate_disposition(name: name, this_module_name: this_module_name, registry: registry, export_sets: export_sets, typed_modules: typed_modules, source_indices: source_indices, module_index: module_index) + disposition: reference_derived_candidate_disposition(name: name, this_module_name: this_module_name, registry: registry, export_sets: export_sets, typed_modules: typed_modules, source_indices: source_indices, module_index: module_index, type_summaries: emit_info.type_summaries, variant_to_enum: emit_info.variant_to_enum) }] } ) @@ -3868,6 +3952,7 @@ fn reference_derived_use_lines(items: List, unlisted_type_names: List + import_refusals: List } fn emit_module_full_with_dispositions(typed_module: TypedModule, ctx: EmitRustContext, typed_modules: List) -> ModuleEmission { diff --git a/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag b/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag index 9e8d1b76e1a..6c1ee571dcc 100644 --- a/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag +++ b/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag @@ -6,7 +6,9 @@ import v1.compiler.emit_rust { CandidateOwnModule, CandidateRegistryAbsent, CandidateExportProofFailed, + CandidateVariantDelegatedToParent, ReferenceDerivedCandidateRow, + reference_derived_row_diagnostics, ReferenceDerivedCensus, reference_derived_candidate_disposition, reference_derived_disposition_name, @@ -16,6 +18,10 @@ import v1.compiler.emit_rust { build_module_index } import v1.compiler.infer_items { ItemInfo, FnItem } +import v1.std.core { + ErrorNode, SourceSpan, no_span, diagnostic_to_message, is_error_diagnostic +} +import v1.compiler.emit_info { TypeSummary, EnumRepr } import v1.compiler.compile { SourceFile, compile_to_resolved } data reference_derived_disposition_census_witness_note: String = "THE ROUTE HALF OF THIS FILE IS ABSENT AND THE REASON IS A WALL RATHER THAN A CHOICE, stated here because it is the first thing a reader of a five-row file should know: the end-to-end pair this file was designed around -- a two-module manifest whose consumer calls a peer fn in VALUE position with no import, against a control differing by one authored import line -- was authored and DOES NOT RUN. A nested compile is not evaluable in the interpreter: compile_to_resolved over an authored source vector refuses with NoSuchField Node.ident during the nested pipeline, measured 2026-08-27 on three separate subjects. That is not a property of this change; it is why the two nested-compile instruments the corpus already has (gunbc.compile_diagnostic_census and tools.multi_module_compile_fixture) both route through a HOST BUILTIN rather than calling the pipeline from .dag. The rows are not retained in a form that cannot execute, because a probe that always errors is not a red -- it is an inert artifact that would be counted as coverage. NEXT-RUNG TRIGGER FOR THE ROUTE: a nested-compile builtin returning the resolved graph, the shape tools.multi_module_compile_fixture already has, widened to carry the emitter reference-derived rows.\n\nWHERE THIS RUNS, STATED FIRST AND WITHOUT FLATTERY. THIS WITNESS IS NOT RUN BY CI. gunbc.ci_layer_roots witness_discovery_scan_dirs is [dag/test/claim, src/v2/test/claim/manual, src/v2/test/claim/emit] -- src/v1/tests/claim is not among them -- and the required run invokes claim_executor with --source-root dag --source-root src/v2, which could not resolve this file's import of v1.compiler.emit_rust even if discovery reached it. The scoped batch that once ran this directory died in the 2026-08-15 floor cut; gunbc.ci_layer_roots v1_claim_scoped_witness_batch_deleted_note records that and states the entries here have no executing consumer. The roster its sibling checkpoint_identity_keying_witness_test claims to be enrolled in, v1_claim_scoped_witness_entries, is not declared anywhere in dag/gunbc/ci_layer_roots.dag -- verified by grep in both directions rather than taken from the note. THE FIVE ROWS BELOW WERE RUN DIRECTLY ON 2026-08-27 AND ALL FIVE PASS. Direct execution is evidence the assertions HOLD, never evidence that CI runs them, and conflating those two is the rung inflation this directory has now recorded twice.\n\nWHAT THE SUBJECT IS. Since PR 6848 a cross-module name resolves whether or not it is imported. At TYPE positions the resolver says so advisorily (UnlistedImportUse, is_error_diagnostic false); at VALUE positions it says nothing at all, and the emitter silently synthesizes the use-line the author did not write. That synthesis is unchanged here and deliberately so. What changed is that the per-candidate decision behind it stopped being an inline flat_map whose three non-surviving arms all returned the empty list, and became a four-armed coproduct one census can count.\n\nWHY BOTH HALVES ARE HERE. The four unit rows discriminate the DECISION: each names a distinct arm, and two of them differ ONLY in whether the provider's export set proves the symbol, so no constant, disabled mechanism or name-keyed rule satisfies them together. The end-to-end rows discriminate the CENSUS ROUTE: a two-module manifest compiled through compile_to_resolved, whose consumer references a peer's fn in VALUE position with no import, against a control differing by exactly one authored import line. The unlisted arm must produce a survived row naming peer_answer; the control must produce no row for that name at all, because an already-provided name never reaches the decision. Neither can pass vacuously -- a census that always reported zero fails the first, and one that reported every reference fails the second.\n\nTHE RED IS AUTHORABLE AT THE FIXTURE BOUNDARY, which is what makes this a probe rather than a decoration. No Accepted corpus module can be relied on to hold a value-position unlisted use on demand, but a fixture manifest can author one directly, and this file does. DESIGN 4b: a check whose RED cannot be produced anywhere it runs is permanently green by construction and worse than absent, because it gets cited as coverage. What this file lacks is a RUNNER, not a reachable red.\n\nWHAT THIS DOES NOT ESTABLISH. Nothing about the SIZE of the live population -- these are fixtures; NO PRODUCER FOR A REAL FIGURE EXISTS YET, and this file does not name one: the only route from .dag to a corpus census is a nested compile, which the interpreter refuses, so any figure quoted today names nothing that can produce it. And nothing about the names the census never sees: a candidate already provided by an authored import, the prelude, a carrier use-line or the module's own declarations is filtered before the decision, so every count is a LOWER BOUND and the gap is unmeasured. Quote it as at least N.\n\nNEXT-RUNG TRIGGER FOR THIS FILE'S EVIDENCE, which is NOT the same trigger as the class's and must not be folded into it: one prepared subject spanning dag, src/v1 and src/v2 -- the restoration gunbc.ci_layer_roots already names for the deleted batch -- at which point these rows execute and this first paragraph deletes. The CLASS's own trigger (the value-position case joining the fail-closed wall, retiring the census and the synthesis together) lives on v1.compiler.emit_rust reference_derived_census_rung_note. Either can land first; neither closes the other." @@ -49,7 +55,9 @@ fn fixture_disposition(registry: Map, export_sets: Map Bool { export_sets: fixture_export_sets(module_name: "fixture.provider", name: "PeerName"), typed_modules: [], source_indices: empty_map(), - module_index: build_module_index(modules: []) + module_index: build_module_index(modules: []), + type_summaries: empty_map(), + variant_to_enum: empty_map() ) == CandidateSurvived { provider_module: "fixture.provider" } } @@ -84,12 +94,88 @@ test fn census_counts_each_arm_separately() -> Bool { ReferenceDerivedCandidateRow { module_name: "m", name: "a", disposition: CandidateSurvived { provider_module: "p" } }, ReferenceDerivedCandidateRow { module_name: "m", name: "b", disposition: CandidateOwnModule }, ReferenceDerivedCandidateRow { module_name: "m", name: "c", disposition: CandidateRegistryAbsent }, - ReferenceDerivedCandidateRow { module_name: "m", name: "d", disposition: CandidateExportProofFailed { provider_module: "p" } } + ReferenceDerivedCandidateRow { module_name: "m", name: "d", disposition: CandidateExportProofFailed { provider_module: "p" } }, + ReferenceDerivedCandidateRow { module_name: "m", name: "e", disposition: CandidateVariantDelegatedToParent { parent_enum: "E" } } ] let census = reference_derived_census(rows: rows) - census.candidates == 4 + census.candidates == 5 && census.survived == 1 && census.own_module == 1 + && census.variant_delegated_to_parent == 1 && census.registry_absent == 1 && census.export_proof_failed == 1 } + +data variant_arm_red_note: String = "THE DISCRIMINATING RED FOR THE FIFTH ARM, and it is the one this file most needs. Before the arm existed a bare variant name reached the registry lookup, missed, and answered registry-absent -- so the fixture below is RED against the four-arm classifier and green against the five-arm one, which is what makes it evidence rather than decoration. The positive control beside it is the census row above: a name that is NOT a known variant still answers registry-absent, so the arm narrows the class rather than emptying it." + +fn fixture_variant_type_summaries() -> Map { + map_insert(empty_map(), "E", TypeSummary { + name: "E", + repr: EnumRepr { unit_only: true }, + field_summaries: empty_map(), + field_type_map: empty_map(), + field_import_surface_names: [], + variant_name_set: map_insert(empty_map(), "V", true), + generic_param_names: [], + has_fn_fields: false + }) +} + +test fn known_variant_is_delegated_to_its_parent_not_registry_absent() -> Bool { + reference_derived_candidate_disposition( + name: "V", + this_module_name: "fixture.consumer", + registry: empty_map(), + export_sets: empty_map(), + typed_modules: [], + source_indices: empty_map(), + module_index: build_module_index(modules: []), + type_summaries: fixture_variant_type_summaries(), + variant_to_enum: map_insert(empty_map(), "V", "E") + ) == CandidateVariantDelegatedToParent { parent_enum: "E" } +} + +test fn non_variant_name_still_answers_registry_absent() -> Bool { + reference_derived_disposition_name(disposition: reference_derived_candidate_disposition( + name: "NotAVariant", + this_module_name: "fixture.consumer", + registry: empty_map(), + export_sets: empty_map(), + typed_modules: [], + source_indices: empty_map(), + module_index: build_module_index(modules: []), + type_summaries: fixture_variant_type_summaries(), + variant_to_enum: map_insert(empty_map(), "V", "E") + )) == "registry-absent" +} + +test fn only_the_two_failing_arms_produce_diagnostics() -> Bool { + let rows = [ + ReferenceDerivedCandidateRow { module_name: "m", name: "a", disposition: CandidateSurvived { provider_module: "p" } }, + ReferenceDerivedCandidateRow { module_name: "m", name: "b", disposition: CandidateOwnModule }, + ReferenceDerivedCandidateRow { module_name: "m", name: "e", disposition: CandidateVariantDelegatedToParent { parent_enum: "E" } }, + ReferenceDerivedCandidateRow { module_name: "m", name: "c", disposition: CandidateRegistryAbsent }, + ReferenceDerivedCandidateRow { module_name: "m", name: "d", disposition: CandidateExportProofFailed { provider_module: "p" } } + ] + reference_derived_row_diagnostics(rows: rows, module_span: no_span()) |> count == 2 +} + +test fn both_refusal_diagnostics_are_advisory_today() -> Bool { + let rows = [ + ReferenceDerivedCandidateRow { module_name: "m", name: "c", disposition: CandidateRegistryAbsent }, + ReferenceDerivedCandidateRow { module_name: "m", name: "d", disposition: CandidateExportProofFailed { provider_module: "p" } } + ] + reference_derived_row_diagnostics(rows: rows, module_span: no_span()) + |> all(e => !is_error_diagnostic(d: e.diagnostic)) +} + +test fn the_two_refusals_carry_different_remedies() -> Bool { + let rows = [ + ReferenceDerivedCandidateRow { module_name: "m", name: "c", disposition: CandidateRegistryAbsent }, + ReferenceDerivedCandidateRow { module_name: "m", name: "d", disposition: CandidateExportProofFailed { provider_module: "p" } } + ] + let msgs = reference_derived_row_diagnostics(rows: rows, module_span: no_span()) + |> map(e => diagnostic_to_message(d: e.diagnostic)) + (msgs |> any(m => string_contains(s: m, pattern: "AUTHOR AN IMPORT"))) + && (msgs |> any(m => string_contains(s: m, pattern: "NOT an import"))) +} From 4a512cc5254a51b44ba08a6e46315b537d7b2915 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 27 Aug 2026 17:19:53 +0000 Subject: [PATCH 02/21] Fix two refusals from the first gen-0 compile: ModuleEmission field and the TypeSummary import path Both caught by execution rather than review: the emitter refused with 2 hard diagnostics before writing any candidate tree. - emit_module_full built a ModuleEmission without import_refusals after the type gained the field. - the census witness imported TypeSummary/EnumRepr from v1.compiler.emit_info, which is not the module's declared name; it is v1.compiler.infer_emit_info. Co-Authored-By: Claude Opus 5 (1M context) --- src/v1/05_emit_rust.dag | 3 ++- .../reference_derived_disposition_census_witness_test.dag | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/v1/05_emit_rust.dag b/src/v1/05_emit_rust.dag index 3535df25d0d..e51f73a9d52 100644 --- a/src/v1/05_emit_rust.dag +++ b/src/v1/05_emit_rust.dag @@ -4084,7 +4084,8 @@ fn emit_module_full(typed_module: TypedModule, registry: Map, coproduct_wire_contract_validation_section, items_str, phantom_section, "\n") ModuleEmission { file: TextFile { path: concat(rust_source_root(), filename, rust_source_ext()), content: content }, - reference_rows: reference_plan.rows + reference_rows: reference_plan.rows, + import_refusals: reference_derived_row_diagnostics(rows: reference_plan.rows, module_span: m.span) } } diff --git a/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag b/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag index 6c1ee571dcc..a1a505c8d48 100644 --- a/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag +++ b/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag @@ -21,7 +21,7 @@ import v1.compiler.infer_items { ItemInfo, FnItem } import v1.std.core { ErrorNode, SourceSpan, no_span, diagnostic_to_message, is_error_diagnostic } -import v1.compiler.emit_info { TypeSummary, EnumRepr } +import v1.compiler.infer_emit_info { TypeSummary, EnumRepr } import v1.compiler.compile { SourceFile, compile_to_resolved } data reference_derived_disposition_census_witness_note: String = "THE ROUTE HALF OF THIS FILE IS ABSENT AND THE REASON IS A WALL RATHER THAN A CHOICE, stated here because it is the first thing a reader of a five-row file should know: the end-to-end pair this file was designed around -- a two-module manifest whose consumer calls a peer fn in VALUE position with no import, against a control differing by one authored import line -- was authored and DOES NOT RUN. A nested compile is not evaluable in the interpreter: compile_to_resolved over an authored source vector refuses with NoSuchField Node.ident during the nested pipeline, measured 2026-08-27 on three separate subjects. That is not a property of this change; it is why the two nested-compile instruments the corpus already has (gunbc.compile_diagnostic_census and tools.multi_module_compile_fixture) both route through a HOST BUILTIN rather than calling the pipeline from .dag. The rows are not retained in a form that cannot execute, because a probe that always errors is not a red -- it is an inert artifact that would be counted as coverage. NEXT-RUNG TRIGGER FOR THE ROUTE: a nested-compile builtin returning the resolved graph, the shape tools.multi_module_compile_fixture already has, widened to carry the emitter reference-derived rows.\n\nWHERE THIS RUNS, STATED FIRST AND WITHOUT FLATTERY. THIS WITNESS IS NOT RUN BY CI. gunbc.ci_layer_roots witness_discovery_scan_dirs is [dag/test/claim, src/v2/test/claim/manual, src/v2/test/claim/emit] -- src/v1/tests/claim is not among them -- and the required run invokes claim_executor with --source-root dag --source-root src/v2, which could not resolve this file's import of v1.compiler.emit_rust even if discovery reached it. The scoped batch that once ran this directory died in the 2026-08-15 floor cut; gunbc.ci_layer_roots v1_claim_scoped_witness_batch_deleted_note records that and states the entries here have no executing consumer. The roster its sibling checkpoint_identity_keying_witness_test claims to be enrolled in, v1_claim_scoped_witness_entries, is not declared anywhere in dag/gunbc/ci_layer_roots.dag -- verified by grep in both directions rather than taken from the note. THE FIVE ROWS BELOW WERE RUN DIRECTLY ON 2026-08-27 AND ALL FIVE PASS. Direct execution is evidence the assertions HOLD, never evidence that CI runs them, and conflating those two is the rung inflation this directory has now recorded twice.\n\nWHAT THE SUBJECT IS. Since PR 6848 a cross-module name resolves whether or not it is imported. At TYPE positions the resolver says so advisorily (UnlistedImportUse, is_error_diagnostic false); at VALUE positions it says nothing at all, and the emitter silently synthesizes the use-line the author did not write. That synthesis is unchanged here and deliberately so. What changed is that the per-candidate decision behind it stopped being an inline flat_map whose three non-surviving arms all returned the empty list, and became a four-armed coproduct one census can count.\n\nWHY BOTH HALVES ARE HERE. The four unit rows discriminate the DECISION: each names a distinct arm, and two of them differ ONLY in whether the provider's export set proves the symbol, so no constant, disabled mechanism or name-keyed rule satisfies them together. The end-to-end rows discriminate the CENSUS ROUTE: a two-module manifest compiled through compile_to_resolved, whose consumer references a peer's fn in VALUE position with no import, against a control differing by exactly one authored import line. The unlisted arm must produce a survived row naming peer_answer; the control must produce no row for that name at all, because an already-provided name never reaches the decision. Neither can pass vacuously -- a census that always reported zero fails the first, and one that reported every reference fails the second.\n\nTHE RED IS AUTHORABLE AT THE FIXTURE BOUNDARY, which is what makes this a probe rather than a decoration. No Accepted corpus module can be relied on to hold a value-position unlisted use on demand, but a fixture manifest can author one directly, and this file does. DESIGN 4b: a check whose RED cannot be produced anywhere it runs is permanently green by construction and worse than absent, because it gets cited as coverage. What this file lacks is a RUNNER, not a reachable red.\n\nWHAT THIS DOES NOT ESTABLISH. Nothing about the SIZE of the live population -- these are fixtures; NO PRODUCER FOR A REAL FIGURE EXISTS YET, and this file does not name one: the only route from .dag to a corpus census is a nested compile, which the interpreter refuses, so any figure quoted today names nothing that can produce it. And nothing about the names the census never sees: a candidate already provided by an authored import, the prelude, a carrier use-line or the module's own declarations is filtered before the decision, so every count is a LOWER BOUND and the gap is unmeasured. Quote it as at least N.\n\nNEXT-RUNG TRIGGER FOR THIS FILE'S EVIDENCE, which is NOT the same trigger as the class's and must not be folded into it: one prepared subject spanning dag, src/v1 and src/v2 -- the restoration gunbc.ci_layer_roots already names for the deleted batch -- at which point these rows execute and this first paragraph deletes. The CLASS's own trigger (the value-position case joining the fail-closed wall, retiring the census and the synthesis together) lives on v1.compiler.emit_rust reference_derived_census_rung_note. Either can land first; neither closes the other." From 52bbf221c1806186c54196f6c2386ddc28e62e6a Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 27 Aug 2026 17:33:45 +0000 Subject: [PATCH 03/21] Install the regenerated mirrors and the cli_run arms they make compilable The seed mirror is a TWO-FILE change for a new 00_core coproduct variant -- declaration in v1_std_core.rs, exhaustive match arms in cli_run.rs -- and the two are circularly ordered: the arms name variants the committed mirror does not carry, so generation 0 stops building the moment they land alone. rustc reports E0004 against the CONSUMING file, which points away from the missing half. Mirrors regenerated by generation 0 from the edited .dag; three files drifted, including the emitted census witness. Co-Authored-By: Claude Opus 5 (1M context) --- src/v1/stage0/src/v1_compiler_emit_rust.rs | 176 +++++++++----- src/v1/stage0/src/v1_std_core.rs | 30 +++ ...derived_disposition_census_witness_test.rs | 224 +++++++++++++++++- 3 files changed, 374 insertions(+), 56 deletions(-) diff --git a/src/v1/stage0/src/v1_compiler_emit_rust.rs b/src/v1/stage0/src/v1_compiler_emit_rust.rs index 35e18cd883f..135334e8e95 100644 --- a/src/v1/stage0/src/v1_compiler_emit_rust.rs +++ b/src/v1/stage0/src/v1_compiler_emit_rust.rs @@ -177,7 +177,8 @@ use crate::v1_std_core::CallSemantics::{ use crate::v1_std_core::Cardinality::{CardOptional, Required}; use crate::v1_std_core::CompilerDiagnostic::{ AmbiguousAnonymousRecordLiteral, AmbiguousReference, DataReferenceVisibilityBudgetExceeded, - InternalError, ParameterDefaultFormNotAdmitted, UnlistedImportUse, + InternalError, ParameterDefaultFormNotAdmitted, ReferenceDerivedImportExportUnproven, + ReferenceDerivedImportProviderUnknown, UnlistedImportUse, }; use crate::v1_std_core::Connective::{Conj, Disj, NoConnective}; use crate::v1_std_core::ExprData::{ @@ -5500,18 +5501,28 @@ pub fn emit_rust(typed: Rc) -> Rc { }); } let test_projections = ctx.test_projections.clone(); - let module_files = Rc::new({ + let module_emissions = Rc::new({ let mut __result = Vec::new(); for tm in typed.modules.clone().iter().cloned() { - __result.push( - emit_module_full_with_dispositions( - tm.clone(), - ctx.clone(), - typed.modules.clone(), - ) - .file - .clone(), - ); + __result.push(emit_module_full_with_dispositions( + tm.clone(), + ctx.clone(), + typed.modules.clone(), + )); + } + __result + }); + let module_files = Rc::new({ + let mut __result = Vec::new(); + for e in module_emissions.iter().cloned() { + __result.push(e.file.clone()); + } + __result + }); + let import_refusals = Rc::new({ + let mut __result = Vec::new(); + for e in module_emissions.iter().cloned() { + __result.extend((*e.import_refusals.clone()).iter().cloned()); } __result }); @@ -5631,7 +5642,7 @@ pub fn emit_rust(typed: Rc) -> Rc { ); Rc::new(EmitResult { files: files.clone(), - diagnostics: Rc::new(vec![]), + diagnostics: import_refusals.clone(), }) } } @@ -7746,29 +7757,10 @@ pub fn collect_item_realized_surface_names( pub enum ReferenceDerivedCandidateDisposition { CandidateSurvived { provider_module: String }, CandidateOwnModule, + CandidateVariantDelegatedToParent { parent_enum: String }, CandidateRegistryAbsent, CandidateExportProofFailed { provider_module: String }, } -impl ReferenceDerivedCandidateDisposition { - pub fn provider_module(&self) -> String { - match self { - ReferenceDerivedCandidateDisposition::CandidateSurvived { - provider_module: __val, - .. - } => __val.clone(), - ReferenceDerivedCandidateDisposition::CandidateOwnModule => { - panic!("no provider_module on unit variant") - } - ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent => { - panic!("no provider_module on unit variant") - } - ReferenceDerivedCandidateDisposition::CandidateExportProofFailed { - provider_module: __val, - .. - } => __val.clone(), - } - } -} #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct ReferenceDerivedCandidateRow { @@ -7785,36 +7777,90 @@ pub fn reference_derived_candidate_disposition( typed_modules: Rc>>, source_indices: Rc>>, module_index: Rc, + type_summaries: Rc>>, + variant_to_enum: Rc>, ) -> Rc { - match v1_rt::map_get(®istry, name.clone()) { - Some(info) => { - if (info.module_name.clone() == this_module_name.clone()) { - Rc::new(ReferenceDerivedCandidateDisposition::CandidateOwnModule) - } else { - if provider_proven_exports_symbol( - name.clone(), - info.module_name.clone(), - export_sets.clone(), - typed_modules.clone(), - source_indices.clone(), - module_index.clone(), - ) { - Rc::new(ReferenceDerivedCandidateDisposition::CandidateSurvived { - provider_module: info.module_name.clone(), - }) + if is_known_variant(type_summaries.clone(), name.clone()) { + match v1_rt::map_get(&variant_to_enum, name.clone()) { + Some(parent) => Rc::new( + ReferenceDerivedCandidateDisposition::CandidateVariantDelegatedToParent { + parent_enum: parent.clone(), + }, + ), + None => Rc::new(ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent), + } + } else { + match v1_rt::map_get(®istry, name.clone()) { + Some(info) => { + if (info.module_name.clone() == this_module_name.clone()) { + Rc::new(ReferenceDerivedCandidateDisposition::CandidateOwnModule) } else { - Rc::new( - ReferenceDerivedCandidateDisposition::CandidateExportProofFailed { + if provider_proven_exports_symbol( + name.clone(), + info.module_name.clone(), + export_sets.clone(), + typed_modules.clone(), + source_indices.clone(), + module_index.clone(), + ) { + Rc::new(ReferenceDerivedCandidateDisposition::CandidateSurvived { provider_module: info.module_name.clone(), - }, - ) + }) + } else { + Rc::new( + ReferenceDerivedCandidateDisposition::CandidateExportProofFailed { + provider_module: info.module_name.clone(), + }, + ) + } } } + None => Rc::new(ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent), } - None => Rc::new(ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent), } } +pub fn reference_derived_row_diagnostics( + rows: Rc>>, + module_span: Rc, +) -> Rc>> { + Rc::new({ + let mut __result = Vec::new(); + for r in rows.iter().cloned() { + __result.extend( + (*match (*r.disposition.clone()).clone() { + ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent => { + Rc::new(vec![make_error_node( + Rc::new(CompilerDiagnostic::ReferenceDerivedImportProviderUnknown { + name: r.name.clone(), + referencing_module: r.module_name.clone(), + span: module_span.clone(), + }), + r.module_name.clone(), + )]) + } + ReferenceDerivedCandidateDisposition::CandidateExportProofFailed { + provider_module: p, + .. + } => Rc::new(vec![make_error_node( + Rc::new(CompilerDiagnostic::ReferenceDerivedImportExportUnproven { + name: r.name.clone(), + referencing_module: r.module_name.clone(), + provider_module: p.clone(), + span: module_span.clone(), + }), + r.module_name.clone(), + )]), + _ => Rc::new(vec![]), + }) + .iter() + .cloned(), + ); + } + __result + }) +} + pub fn reference_derived_candidate_survived( disposition: Rc, ) -> bool { @@ -7834,6 +7880,10 @@ pub fn reference_derived_disposition_name( provider_module: _, .. } => "survived".to_string(), ReferenceDerivedCandidateDisposition::CandidateOwnModule => "own-module".to_string(), + ReferenceDerivedCandidateDisposition::CandidateVariantDelegatedToParent { + parent_enum: _, + .. + } => "variant-delegated-to-parent".to_string(), ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent => { "registry-absent".to_string() } @@ -7849,6 +7899,7 @@ pub struct ReferenceDerivedCensus { pub candidates: i64, pub survived: i64, pub own_module: i64, + pub variant_delegated_to_parent: i64, pub registry_absent: i64, pub export_proof_failed: i64, } @@ -7882,6 +7933,18 @@ pub fn reference_derived_census( __result }) .len() as i64), + variant_delegated_to_parent: (Rc::new({ + let mut __result = Vec::new(); + for r in rows.iter().cloned() { + if (reference_derived_disposition_name(r.disposition.clone()) + == "variant-delegated-to-parent".to_string()) + { + __result.push(r); + } + } + __result + }) + .len() as i64), registry_absent: (Rc::new({ let mut __result = Vec::new(); for r in rows.iter().cloned() { @@ -8129,6 +8192,8 @@ pub fn reference_derived_use_line_plan( typed_modules.clone(), source_indices.clone(), module_index.clone(), + emit_info.type_summaries.clone(), + emit_info.variant_to_enum.clone(), ), })]) }) @@ -8329,6 +8394,7 @@ pub fn reference_derived_use_lines( pub struct ModuleEmission { pub file: Rc, pub reference_rows: Rc>>, + pub import_refusals: Rc>>, } pub fn emit_module_full_with_dispositions( @@ -8736,6 +8802,10 @@ pub fn emit_module_full( content: content.clone(), }), reference_rows: reference_plan.rows.clone(), + import_refusals: reference_derived_row_diagnostics( + reference_plan.rows.clone(), + m.span.clone(), + ), }) } } diff --git a/src/v1/stage0/src/v1_std_core.rs b/src/v1/stage0/src/v1_std_core.rs index 940a3b3d314..f0fbcfbaa10 100644 --- a/src/v1/stage0/src/v1_std_core.rs +++ b/src/v1/stage0/src/v1_std_core.rs @@ -554,6 +554,17 @@ pub enum CompilerDiagnostic { name: String, span: Rc, }, + ReferenceDerivedImportProviderUnknown { + name: String, + referencing_module: String, + span: Rc, + }, + ReferenceDerivedImportExportUnproven { + name: String, + referencing_module: String, + provider_module: String, + span: Rc, + }, UnlistedVariantValueUse { name: String, span: Rc, @@ -729,6 +740,8 @@ pub fn diagnostic_to_span(d: Rc) -> Rc { CompilerDiagnostic::DeclaredTypeNotInhabited { span: s, .. } => s.clone(), CompilerDiagnostic::DeclaredTypeInhabitanceUndecided { span: s, .. } => s.clone(), CompilerDiagnostic::UnlistedImportUse { span: s, .. } => s.clone(), + CompilerDiagnostic::ReferenceDerivedImportProviderUnknown { span: s, .. } => s.clone(), + CompilerDiagnostic::ReferenceDerivedImportExportUnproven { span: s, .. } => s.clone(), CompilerDiagnostic::UnlistedVariantValueUse { span: s, .. } => s.clone(), CompilerDiagnostic::AmbiguousReference { span: s, .. } => s.clone(), CompilerDiagnostic::DataReferenceVisibilityBudgetExceeded { span: s, .. } => s.clone(), @@ -787,6 +800,8 @@ pub fn diagnostic_to_message(d: Rc) -> String { CompilerDiagnostic::DeclaredTypeNotInhabited { position: pos, expected: e, got: g, .. } => v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("value does not inhabit its declared type at the ".to_string(), pos.clone()), ": declared '".to_string()), e.clone()), "', produced '".to_string()), g.clone()), "'".to_string()), CompilerDiagnostic::DeclaredTypeInhabitanceUndecided { position: pos, reason: r, .. } => v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("declared-type inhabitance is undecidable at the ".to_string(), pos.clone()), " (".to_string()), r.clone()), "): the modeled facts do not settle whether the produced value inhabits its declared type, so no verdict is asserted in either direction".to_string()), CompilerDiagnostic::UnlistedImportUse { name: n, .. } => v1_rt::concat(v1_rt::concat("unlisted import use '".to_string(), n.clone()), "' (referenced but not in any import's name list)".to_string()), + CompilerDiagnostic::ReferenceDerivedImportProviderUnknown { name: n, referencing_module: rm, .. } => v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("no provider is present for '".to_string(), n.clone()), "', referenced by module '".to_string()), rm.clone()), "': the compile closure carries no module that declares the name, so no use-line can be synthesized and the emitted Rust references a name it never imported. The remedy is to AUTHOR AN IMPORT -- closure membership is driven by imports, so a name the module never imported is a name the closure was never asked to supply".to_string()), + CompilerDiagnostic::ReferenceDerivedImportExportUnproven { name: n, referencing_module: rm, provider_module: p, .. } => v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("the provider of '".to_string(), n.clone()), "' is present but its export could not be established: module '".to_string()), rm.clone()), "' references the name, the registry maps it to '".to_string()), p.clone()), "', and that module's transitive export surface does not establish it. The remedy is NOT an import -- the provider is already in the closure -- it is that the emitter cannot prove an export the provider may well hold".to_string()), CompilerDiagnostic::UnlistedVariantValueUse { name: n, .. } => v1_rt::concat(v1_rt::concat("unlisted variant value use '".to_string(), n.clone()), "' (a coproduct arm referenced but not in any import's name list)".to_string()), CompilerDiagnostic::AmbiguousReference { name: n, candidates: cs, .. } => v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("ambiguous reference '".to_string(), n.clone()), "': ".to_string()), ((cs.clone().len() as i64)).to_string()), " candidates: ".to_string()), cs.clone().join(&", ".to_string())), " — qualify by containment path, alias, or rename".to_string()), CompilerDiagnostic::DataReferenceVisibilityBudgetExceeded { name: n, .. } => v1_rt::concat(v1_rt::concat("visible declarations of '".to_string(), n.clone()), "' could not be enumerated: the import re-export walk exceeded its depth bound, so no verdict is asserted about how many declarations answer to the name".to_string()), @@ -862,6 +877,8 @@ pub fn diagnostic_frontier_occurrence_key_note() -> String { pub fn is_error_diagnostic(d: Rc) -> bool { match (*d.clone()).clone() { CompilerDiagnostic::UnlistedImportUse { .. } => false, + CompilerDiagnostic::ReferenceDerivedImportProviderUnknown { .. } => false, + CompilerDiagnostic::ReferenceDerivedImportExportUnproven { .. } => false, CompilerDiagnostic::UnlistedVariantValueUse { .. } => false, CompilerDiagnostic::MethodExistenceFrontierAdmitted { .. } => false, CompilerDiagnostic::ReceiverTypeUnestablished { .. } => false, @@ -872,6 +889,15 @@ pub fn is_error_diagnostic(d: Rc) -> bool { } } +pub fn reference_derived_import_refusal_severity_note() -> String { + thread_local! { + static CACHED: String = { + "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). These two diagnostics are the SURFACING half of the disposition #9439 landed: that change factored the emitter's per-candidate import decision into a typed disposition and COUNTED it, and its own note records that the census is not surfaced during an ordinary build. A census nothing surfaces is a count, not a refusal -- the failure arms still vanished from the emission, which is the empty-observation narrow DESIGN names (the emitter answering 'this name is not part of the interface' where the truth is 'I could not prove that it was'), and DESIGN rates a narrow strictly worse than the widen section 5 forbids because a widen is merely expensive and a narrow is silently uncovered. TWO DIAGNOSTICS, NOT ONE CARRYING A CAUSE (ruling: warm-hawk-909, compiler direction, relayed through smart-ram-730, 2026-08-27). The question is upstream of severity: are these one error class? They are not. ReferenceDerivedImportProviderUnknown is CandidateRegistryAbsent -- the closure carries no provider for the name -- and its remedy is to AUTHOR AN IMPORT, since closure membership is driven by imports and a name the module never imported is one the closure was never asked to supply. ReferenceDerivedImportExportUnproven is CandidateExportProofFailed -- the provider is present and its export could not be established -- and its remedy is the opposite: not an import at all, but making the emitter able to prove an export the provider may already hold. Different mechanism, different remedy, different owner, different population, potentially different reachability; one symbol over two opposite remedies would be the state-space conflation DESIGN names, and DESIGN 4b files one row per class, so two classes carry two diagnostics and two independent is_error_diagnostic booleans. BOTH ARE ADVISORY TODAY and blocking nowhere. FLIP CONDITION, and it is strictly stronger than a count: an arm flips to blocking when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient -- an observation of zero on an arm that has never produced anything is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check that is then cited as coverage, which DESIGN 4b rates worse than absent. Reachability is judged against what a FIXTURE may author, never against what the accepted corpus contains. ProviderUnknown's reachability is established: std.algebra under the src/v2/std/node.dag closure references Optional and empty_map it never imports (deep-ant-102, measured). ExportUnproven's is established by nothing yet, so if its population returns zero the MISSING FIXTURE is that class's next-rung trigger rather than a clearance to block. ProviderUnknown's roster is NOT new work: it is UnlistedImportUse clause (b) seen from the emitter side, so its trigger points at the EXISTING family-closure-SVN burndown recorded in v1.compiler.resolve resolve_node_bounded_masked_boundary and must not become a second burndown with its own schedule. Whether either flips is warm-hawk-909's call, not this module's.".to_string() + }; + } + CACHED.with(|c: &String| c.clone()) +} + pub fn where_refinement_deferral_reason_scaffold_note() -> String { thread_local! { static CACHED: String = { @@ -893,6 +919,8 @@ pub fn is_interpreter_blocking_diagnostic(d: Rc) -> bool { CompilerDiagnostic::ReceiverTypeUnestablished { .. } => false, CompilerDiagnostic::ServiceConfigReferenceJudgmentDeferred { .. } => false, CompilerDiagnostic::DeclaredTypeInhabitanceUndecided { .. } => false, + CompilerDiagnostic::ReferenceDerivedImportProviderUnknown { .. } => false, + CompilerDiagnostic::ReferenceDerivedImportExportUnproven { .. } => false, _ => true, } } @@ -905,6 +933,8 @@ pub fn is_discovery_corpus_advisory_typecheck_diagnostic(d: Rc true, CompilerDiagnostic::ServiceConfigReferenceJudgmentDeferred { .. } => true, CompilerDiagnostic::DeclaredTypeInhabitanceUndecided { .. } => true, + CompilerDiagnostic::ReferenceDerivedImportProviderUnknown { .. } => true, + CompilerDiagnostic::ReferenceDerivedImportExportUnproven { .. } => true, CompilerDiagnostic::WhereRefinementUnenforced { reason: r, .. } => { is_where_refinement_unenforced_advisory_reason(r.clone()) } diff --git a/src/v1/stage0/src/v1_tests_claim_reference_derived_disposition_census_witness_test.rs b/src/v1/stage0/src/v1_tests_claim_reference_derived_disposition_census_witness_test.rs index e91a0c67a9a..7a8c08f3913 100644 --- a/src/v1/stage0/src/v1_tests_claim_reference_derived_disposition_census_witness_test.rs +++ b/src/v1/stage0/src/v1_tests_claim_reference_derived_disposition_census_witness_test.rs @@ -1,24 +1,31 @@ // Generated by v1 compiler -- do not edit. // Source module: v1.tests.claim.reference_derived_disposition_census_witness_test +pub use crate::std_types::SourceSpan; pub use crate::v1_compiler_compile::compile_to_resolved; pub use crate::v1_compiler_compile::SourceFile; use crate::v1_compiler_emit_rust::ReferenceDerivedCandidateDisposition::{ CandidateExportProofFailed, CandidateOwnModule, CandidateRegistryAbsent, CandidateSurvived, + CandidateVariantDelegatedToParent, }; pub use crate::v1_compiler_emit_rust::{ build_module_index, emit_rust_reference_derived_census, emit_rust_reference_derived_rows, reference_derived_candidate_disposition, reference_derived_census, - reference_derived_disposition_name, + reference_derived_disposition_name, reference_derived_row_diagnostics, }; pub use crate::v1_compiler_emit_rust::{ ReferenceDerivedCandidateDisposition, ReferenceDerivedCandidateRow, ReferenceDerivedCensus, }; +use crate::v1_compiler_infer_emit_info::TypeRepr::EnumRepr; +pub use crate::v1_compiler_infer_emit_info::{TypeRepr, TypeSummary}; use crate::v1_compiler_infer_items::ItemKind::FnItem; pub use crate::v1_compiler_infer_items::{ItemInfo, ItemKind}; use crate::v1_rt; use crate::v1_rt::{VecCompat, VecJoin}; -pub use crate::v1_std_core::{NewlineIndex, Node}; +use crate::v1_std_core::CompilerDiagnostic::*; +pub use crate::v1_std_core::ErrorNode; +pub use crate::v1_std_core::{diagnostic_to_message, is_error_diagnostic, no_span}; +pub use crate::v1_std_core::{CompilerDiagnostic, FieldSummary, NewlineIndex, Node}; use crate::NonEmptyBTreeSet; use crate::NonEmptyVec; use im::{vector as vec, HashMap, OrdSet as BTreeSet, Vector as Vec}; @@ -77,6 +84,8 @@ pub fn fixture_disposition( Rc::new(vec![]), v1_rt::rc_empty_map::>(), build_module_index(Rc::new(vec![])), + v1_rt::rc_empty_map::>(), + v1_rt::rc_empty_map::(), )) } @@ -110,6 +119,8 @@ pub fn cross_module_candidate_with_export_proof_survives() -> bool { Rc::new(vec![]), v1_rt::rc_empty_map::>(), build_module_index(Rc::new(vec![])), + v1_rt::rc_empty_map::>(), + v1_rt::rc_empty_map::(), ) == Rc::new(ReferenceDerivedCandidateDisposition::CandidateSurvived { provider_module: "fixture.provider".to_string(), })) @@ -144,11 +155,218 @@ pub fn census_counts_each_arm_separately() -> bool { }, ), }), + Rc::new(ReferenceDerivedCandidateRow { + module_name: "m".to_string(), + name: "e".to_string(), + disposition: Rc::new( + ReferenceDerivedCandidateDisposition::CandidateVariantDelegatedToParent { + parent_enum: "E".to_string(), + }, + ), + }), ]); let census = reference_derived_census(rows.clone()); - (((((census.candidates.clone() == 4) && (census.survived.clone() == 1)) + ((((((census.candidates.clone() == 5) && (census.survived.clone() == 1)) && (census.own_module.clone() == 1)) + && (census.variant_delegated_to_parent.clone() == 1)) && (census.registry_absent.clone() == 1)) && (census.export_proof_failed.clone() == 1)) } } + +pub fn variant_arm_red_note() -> String { + thread_local! { + static CACHED: String = { + "THE DISCRIMINATING RED FOR THE FIFTH ARM, and it is the one this file most needs. Before the arm existed a bare variant name reached the registry lookup, missed, and answered registry-absent -- so the fixture below is RED against the four-arm classifier and green against the five-arm one, which is what makes it evidence rather than decoration. The positive control beside it is the census row above: a name that is NOT a known variant still answers registry-absent, so the arm narrows the class rather than emptying it.".to_string() + }; + } + CACHED.with(|c: &String| c.clone()) +} + +pub fn fixture_variant_type_summaries() -> Rc>> { + v1_rt::rc_map_insert( + v1_rt::rc_empty_map::>(), + "E".to_string(), + Rc::new(TypeSummary { + name: "E".to_string(), + repr: Rc::new(TypeRepr::EnumRepr { unit_only: true }), + field_summaries: v1_rt::rc_empty_map::>(), + field_type_map: v1_rt::rc_empty_map::(), + field_import_surface_names: Rc::new(vec![]), + variant_name_set: v1_rt::rc_map_insert( + v1_rt::rc_empty_map::(), + "V".to_string(), + true, + ), + generic_param_names: Rc::new(vec![]), + has_fn_fields: false, + }), + ) +} + +pub fn known_variant_is_delegated_to_its_parent_not_registry_absent() -> bool { + (reference_derived_candidate_disposition( + "V".to_string(), + "fixture.consumer".to_string(), + v1_rt::rc_empty_map::>(), + v1_rt::rc_empty_map::>>(), + Rc::new(vec![]), + v1_rt::rc_empty_map::>(), + build_module_index(Rc::new(vec![])), + fixture_variant_type_summaries(), + v1_rt::rc_map_insert( + v1_rt::rc_empty_map::(), + "V".to_string(), + "E".to_string(), + ), + ) == Rc::new( + ReferenceDerivedCandidateDisposition::CandidateVariantDelegatedToParent { + parent_enum: "E".to_string(), + }, + )) +} + +pub fn non_variant_name_still_answers_registry_absent() -> bool { + (reference_derived_disposition_name(reference_derived_candidate_disposition( + "NotAVariant".to_string(), + "fixture.consumer".to_string(), + v1_rt::rc_empty_map::>(), + v1_rt::rc_empty_map::>>(), + Rc::new(vec![]), + v1_rt::rc_empty_map::>(), + build_module_index(Rc::new(vec![])), + fixture_variant_type_summaries(), + v1_rt::rc_map_insert( + v1_rt::rc_empty_map::(), + "V".to_string(), + "E".to_string(), + ), + )) == "registry-absent".to_string()) +} + +pub fn only_the_two_failing_arms_produce_diagnostics() -> bool { + { + let rows = Rc::new(vec![ + Rc::new(ReferenceDerivedCandidateRow { + module_name: "m".to_string(), + name: "a".to_string(), + disposition: Rc::new(ReferenceDerivedCandidateDisposition::CandidateSurvived { + provider_module: "p".to_string(), + }), + }), + Rc::new(ReferenceDerivedCandidateRow { + module_name: "m".to_string(), + name: "b".to_string(), + disposition: Rc::new(ReferenceDerivedCandidateDisposition::CandidateOwnModule), + }), + Rc::new(ReferenceDerivedCandidateRow { + module_name: "m".to_string(), + name: "e".to_string(), + disposition: Rc::new( + ReferenceDerivedCandidateDisposition::CandidateVariantDelegatedToParent { + parent_enum: "E".to_string(), + }, + ), + }), + Rc::new(ReferenceDerivedCandidateRow { + module_name: "m".to_string(), + name: "c".to_string(), + disposition: Rc::new(ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent), + }), + Rc::new(ReferenceDerivedCandidateRow { + module_name: "m".to_string(), + name: "d".to_string(), + disposition: Rc::new( + ReferenceDerivedCandidateDisposition::CandidateExportProofFailed { + provider_module: "p".to_string(), + }, + ), + }), + ]); + ((reference_derived_row_diagnostics(rows.clone(), no_span()).len() as i64) == 2) + } +} + +pub fn both_refusal_diagnostics_are_advisory_today() -> bool { + { + let rows = Rc::new(vec![ + Rc::new(ReferenceDerivedCandidateRow { + module_name: "m".to_string(), + name: "c".to_string(), + disposition: Rc::new(ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent), + }), + Rc::new(ReferenceDerivedCandidateRow { + module_name: "m".to_string(), + name: "d".to_string(), + disposition: Rc::new( + ReferenceDerivedCandidateDisposition::CandidateExportProofFailed { + provider_module: "p".to_string(), + }, + ), + }), + ]); + { + let mut __all = true; + for e in reference_derived_row_diagnostics(rows.clone(), no_span()) + .iter() + .cloned() + { + if !(!is_error_diagnostic(e.diagnostic.clone())) { + __all = false; + break; + } + } + __all + } + } +} + +pub fn the_two_refusals_carry_different_remedies() -> bool { + { + let rows = Rc::new(vec![ + Rc::new(ReferenceDerivedCandidateRow { + module_name: "m".to_string(), + name: "c".to_string(), + disposition: Rc::new(ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent), + }), + Rc::new(ReferenceDerivedCandidateRow { + module_name: "m".to_string(), + name: "d".to_string(), + disposition: Rc::new( + ReferenceDerivedCandidateDisposition::CandidateExportProofFailed { + provider_module: "p".to_string(), + }, + ), + }), + ]); + let msgs = Rc::new({ + let mut __result = Vec::new(); + for e in reference_derived_row_diagnostics(rows.clone(), no_span()) + .iter() + .cloned() + { + __result.push(diagnostic_to_message(e.diagnostic.clone())); + } + __result + }); + ({ + let mut __found = false; + for m in msgs.iter().cloned() { + if v1_rt::string_contains(&m, "AUTHOR AN IMPORT".to_string()) { + __found = true; + break; + } + } + __found + } && { + let mut __found = false; + for m in msgs.iter().cloned() { + if v1_rt::string_contains(&m, "NOT an import".to_string()) { + __found = true; + break; + } + } + __found + }) + } +} From 9972267d87c342b6fad5d90168148647d52b625c Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 27 Aug 2026 17:34:05 +0000 Subject: [PATCH 04/21] Add the two cli_run match arms the regenerated mirror makes compilable Co-Authored-By: Claude Opus 5 (1M context) --- src/v1/stage0/src/cli_run.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/v1/stage0/src/cli_run.rs b/src/v1/stage0/src/cli_run.rs index d988d194e52..78a4ef05b57 100644 --- a/src/v1/stage0/src/cli_run.rs +++ b/src/v1/stage0/src/cli_run.rs @@ -5424,6 +5424,12 @@ pub fn compile_clean_diagnostic_histogram_key(d: &Rc) -> (String, Str "ServiceConfigReferenceJudgmentDeferred" } CompilerDiagnostic::UnlistedVariantValueUse { .. } => "UnlistedVariantValueUse", + CompilerDiagnostic::ReferenceDerivedImportProviderUnknown { .. } => { + "ReferenceDerivedImportProviderUnknown" + } + CompilerDiagnostic::ReferenceDerivedImportExportUnproven { .. } => { + "ReferenceDerivedImportExportUnproven" + } }; let name = match d.diagnostic.as_ref() { CompilerDiagnostic::UnresolvedImport { module_path, .. } => module_path.clone(), @@ -5467,6 +5473,8 @@ pub fn compile_clean_diagnostic_histogram_key(d: &Rc) -> (String, Str CompilerDiagnostic::DeclaredTypeNotInhabited { position, .. } => position.clone(), CompilerDiagnostic::DeclaredTypeInhabitanceUndecided { position, .. } => position.clone(), CompilerDiagnostic::UnlistedImportUse { name, .. } => name.clone(), + CompilerDiagnostic::ReferenceDerivedImportProviderUnknown { name, .. } => name.clone(), + CompilerDiagnostic::ReferenceDerivedImportExportUnproven { name, .. } => name.clone(), CompilerDiagnostic::UnlistedVariantValueUse { name, .. } => name.clone(), CompilerDiagnostic::AmbiguousReference { name, .. } => name.clone(), CompilerDiagnostic::DataReferenceVisibilityBudgetExceeded { name, .. } => name.clone(), From 5b358fdb2a3a1acdf8fa14369b73d49c9eaf7890 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 27 Aug 2026 18:24:10 +0000 Subject: [PATCH 05/21] Surface only the export-proof arm as a diagnostic: the registry-absent population is target-language tokens THE CENSUS RAN, on the rebuilt construction, over the regen seed closure (claim_executor --required-regen --source-root dag --source-root src/v2, whose subject is regen_input_sources grown to a joint fixpoint over import, dotted-reference and bare-reference edges). Two arms instrumented, generation 1: export-proof-failed 0 registry-absent 473 rows, 63 distinct names, 110 modules AND READING THE 473 DECIDED THE DESIGN. The top of that population is Vec 88, bool 73, Option 50, i64 48, empty_map 33, then BTreeSet, Fn, fn, u8, serde_json, '_' and '-'. Those are RUST TARGET-LANGUAGE tokens, proposed by the candidate walk's emitted-source arm, which tokenizes the module's own emitted Rust and offers every identifier in it. No .dag provider can ever supply 'bool'. Registry-absent is therefore the CORRECT disposition for them and a diagnostic would be a false report in nearly every row -- printed on every build, in every module, forever. So that arm stays a census column. Its trigger is not a burndown of the 473: it is that the candidate walk stop proposing target-language vocabulary, after which the diagnostic can be wired with no other change. ReferenceDerivedImportProviderUnknown stays DECLARED and is produced by nothing. The class is real and its shape is settled; only its input is not yet clean. THE FLIP CONDITION IS NOW MET FOR ExportUnproven, both halves: population zero over a named closure, AND a discriminating RED authorable -- and authored -- at the fixture boundary, which is what separates 'observed zero' from 'cannot fire'. Whether it flips is warm-hawk-909's call. A REFUTED PREDICTION, recorded because it was decision-relevant and was asked for before either arm flips: registry-absent was expected to overlap heavily with UnlistedImportUse, both being described as 'referenced but never imported'. Measured on one run -- 63 registry-absent names, 36 UnlistedImportUse names, INTERSECTION ZERO. UnlistedImportUse names .dag types masked at resolve time; registry-absent is dominated by Rust tokens that never reached the resolver. They are not two views of one population, so ProviderUnknown's trigger does NOT point at the family-closure-SVN burndown as this carrier previously assumed. Witness gains registry_absent_produces_no_diagnostic, which fails the moment someone wires that arm -- the wall that keeps the false reports out. Co-Authored-By: Claude Opus 5 (1M context) --- src/v1/00_core.dag | 2 +- src/v1/05_emit_rust.dag | 52 +++++++++++-------- ...erived_disposition_census_witness_test.dag | 25 +++++---- 3 files changed, 47 insertions(+), 32 deletions(-) diff --git a/src/v1/00_core.dag b/src/v1/00_core.dag index 698ae46366a..ebfed1e7c82 100644 --- a/src/v1/00_core.dag +++ b/src/v1/00_core.dag @@ -526,7 +526,7 @@ fn is_error_diagnostic(d: CompilerDiagnostic) -> Bool { } } -data reference_derived_import_refusal_severity_note: String = "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). These two diagnostics are the SURFACING half of the disposition #9439 landed: that change factored the emitter's per-candidate import decision into a typed disposition and COUNTED it, and its own note records that the census is not surfaced during an ordinary build. A census nothing surfaces is a count, not a refusal -- the failure arms still vanished from the emission, which is the empty-observation narrow DESIGN names (the emitter answering 'this name is not part of the interface' where the truth is 'I could not prove that it was'), and DESIGN rates a narrow strictly worse than the widen section 5 forbids because a widen is merely expensive and a narrow is silently uncovered. TWO DIAGNOSTICS, NOT ONE CARRYING A CAUSE (ruling: warm-hawk-909, compiler direction, relayed through smart-ram-730, 2026-08-27). The question is upstream of severity: are these one error class? They are not. ReferenceDerivedImportProviderUnknown is CandidateRegistryAbsent -- the closure carries no provider for the name -- and its remedy is to AUTHOR AN IMPORT, since closure membership is driven by imports and a name the module never imported is one the closure was never asked to supply. ReferenceDerivedImportExportUnproven is CandidateExportProofFailed -- the provider is present and its export could not be established -- and its remedy is the opposite: not an import at all, but making the emitter able to prove an export the provider may already hold. Different mechanism, different remedy, different owner, different population, potentially different reachability; one symbol over two opposite remedies would be the state-space conflation DESIGN names, and DESIGN 4b files one row per class, so two classes carry two diagnostics and two independent is_error_diagnostic booleans. BOTH ARE ADVISORY TODAY and blocking nowhere. FLIP CONDITION, and it is strictly stronger than a count: an arm flips to blocking when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient -- an observation of zero on an arm that has never produced anything is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check that is then cited as coverage, which DESIGN 4b rates worse than absent. Reachability is judged against what a FIXTURE may author, never against what the accepted corpus contains. ProviderUnknown's reachability is established: std.algebra under the src/v2/std/node.dag closure references Optional and empty_map it never imports (deep-ant-102, measured). ExportUnproven's is established by nothing yet, so if its population returns zero the MISSING FIXTURE is that class's next-rung trigger rather than a clearance to block. ProviderUnknown's roster is NOT new work: it is UnlistedImportUse clause (b) seen from the emitter side, so its trigger points at the EXISTING family-closure-SVN burndown recorded in v1.compiler.resolve resolve_node_bounded_masked_boundary and must not become a second burndown with its own schedule. Whether either flips is warm-hawk-909's call, not this module's." +data reference_derived_import_refusal_severity_note: String = "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). ONE OF THESE TWO DIAGNOSTICS IS PRODUCED TODAY AND THE OTHER IS NOT, and which is which was decided by measurement. ReferenceDerivedImportExportUnproven is emitted from CandidateExportProofFailed: the provider is present in the closure and its export could not be established, and the remedy is NOT an import -- the provider is already here -- but making the emitter able to prove an export it may already hold. ReferenceDerivedImportProviderUnknown is DECLARED HERE AND PRODUCED BY NOTHING: its arm, CandidateRegistryAbsent, was instrumented and measured over the regen seed closure and returned 473 rows over 63 names across 110 modules whose top entries are Vec, bool, Option, i64, empty_map, BTreeSet, Fn, fn, u8, serde_json -- Rust TARGET-LANGUAGE tokens proposed by the candidate walk's emitted-source arm, which tokenizes the module's own emitted Rust. No .dag provider can supply `bool`, so a diagnostic there would be a false report in nearly every row, printed on every build. The variant is kept declared because the class is real and its shape is settled; what is missing is a candidate walk that does not propose target-language vocabulary, and that is the trigger for wiring it, not a burndown of the 473. THE TWO ARE SEPARATE DIAGNOSTICS RATHER THAN ONE WITH A CAUSE FIELD (ruling: warm-hawk-909, compiler direction, relayed through smart-ram-730, 2026-08-27), because the question is upstream of severity: registry-absent is fixed by AUTHORING AN IMPORT and export-proof-failed is fixed by proving an export that already exists -- opposite remedies, different owners, different populations, different reachability -- and one symbol over two opposite remedies is the state-space conflation DESIGN names; 4b files one row per class. That the two turned out to need different TREATMENT, not merely different severity, is the strongest evidence the split was right. BOTH ARE ADVISORY. FLIP CONDITION, strictly stronger than a count: an arm flips to blocking when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient -- an observation of zero on an arm that has never fired is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check cited as coverage, which 4b rates worse than absent. FOR ExportUnproven BOTH HALVES NOW HOLD: the population is zero over the regen seed closure (claim_executor --required-regen --source-root dag --source-root src/v2, whose subject is regen_input_sources grown to a joint fixpoint over import, dotted-reference and bare-reference edges), and the RED is authorable and AUTHORED at the fixture boundary in v1.tests.claim.reference_derived_disposition_census_witness_test, which builds a CandidateExportProofFailed row and asserts the diagnostic. Whether it flips is warm-hawk-909's call, not this module's. A REFUTED PREDICTION, recorded because it was decision-relevant and someone asked for it before either arm flipped: registry-absent was expected to overlap heavily with UnlistedImportUse, both being described as 'referenced but never imported'. Measured on one run: 63 registry-absent names, 36 UnlistedImportUse names, INTERSECTION ZERO. UnlistedImportUse names .dag types masked at resolve time; registry-absent is dominated by Rust tokens that never reached the resolver. They are not two views of one population, and ProviderUnknown's trigger therefore does NOT point at the family-closure-SVN burndown as was previously assumed here." data where_refinement_deferral_reason_scaffold_note: String = "owner: v1.compiler.infer (00_core diagnostic partition). lane: where-refinement literal-wall enforcement. interim: WhereRefinementUnenforced.reason is a closed-string sum enrolled in is_where_refinement_unenforced_advisory_reason; any unlisted reason fails closed blocking. bound: only the five deferral strings emitted by 04_infer today; classifier/eval/equivalence arms must change together until coproduct lands. dissolve-on: feature:where-refinement-predicate-coproduct (WhereRefinementDeferralReason coproduct on the diagnostic carrier)." diff --git a/src/v1/05_emit_rust.dag b/src/v1/05_emit_rust.dag index e51f73a9d52..b8026cb0e61 100644 --- a/src/v1/05_emit_rust.dag +++ b/src/v1/05_emit_rust.dag @@ -3746,23 +3746,41 @@ fn reference_derived_candidate_disposition(name: String, this_module_name: Strin } } -// THE SURFACING HALF. #9439 factored this decision into a typed disposition and COUNTED it; its own -// note records that the census "is NOT SURFACED during an ordinary build". So the two FAILING arms -// still vanished from the emission -- no diagnostic, no location, nothing a build could report -- -// which is the empty-observation narrow DESIGN names: the emitter answers "this name is not part of -// the interface" where the truth is "I could not prove that it was", and a narrow is strictly worse -// than the widen section 5 forbids because a widen is merely expensive and a narrow is silently -// uncovered. Downstream the emitted crate references a name it never imported and rustc says -// E0422/E0425/E0433 far from the cause; upstream the deficit's frequency is zero by construction, -// so the wall's own gaps never rank for repair. +// THE SURFACING HALF, AND IT SURFACES EXACTLY ONE OF THE TWO FAILING ARMS. #9439 factored this +// decision into a typed disposition and COUNTED it; its own note records that the census "is NOT +// SURFACED during an ordinary build". So both failing arms still vanished from the emission -- no +// diagnostic, no location, nothing a build could report -- which is the empty-observation narrow +// DESIGN names: the emitter answers "this name is not part of the interface" where the truth is "I +// could not prove that it was". Downstream the emitted crate references a name it never imported and +// rustc says E0422/E0425/E0433 far from the cause; upstream the deficit's frequency is zero by +// construction, so the wall's own gaps never rank for repair. // // This is a THIRD projection of rows that already exist -- beside the use-lines and the census -- and // not a second decision. Nothing here re-derives the disposition, so the emitted crate cannot move. // -// TWO diagnostics rather than one carrying a cause, because the arms are two error classes and not -// one: registry-absent is fixed by AUTHORING AN IMPORT, export-proof-failed is fixed by making the -// emitter able to prove an export the provider already holds. Opposite remedies, different owners, -// different populations, potentially different reachability. DESIGN 4b files one row per class. +// ONLY CandidateExportProofFailed BECOMES A DIAGNOSTIC, AND THAT IS A MEASUREMENT RESULT RATHER THAN +// A DESIGN PREFERENCE. Both arms were instrumented and run over the regen seed closure (the census +// invocation is recorded on the carrier note). Export-proof-failed returned ZERO. Registry-absent +// returned 473 rows over 63 distinct names across 110 modules, and reading those names shows the +// population is not what the arm's name suggests: the top of it is Vec 88, bool 73, Option 50, i64 +// 48, empty_map 33, with BTreeSet, Fn, fn, u8, serde_json, `_` and `-` behind them. Those are RUST +// TARGET-LANGUAGE tokens, proposed by the candidate walk's emitted-source arm, which tokenizes the +// module's own emitted Rust and offers every identifier in it. No `.dag` provider can ever supply +// `bool`, so registry-absent is the correct disposition and a DIAGNOSTIC would be a false report in +// every one of those rows -- printed on every build, in every module, forever. +// +// So registry-absent stays a census column and does not become a diagnostic. Its next-rung trigger is +// NOT a burndown: it is that the candidate walk stop proposing target-language vocabulary, at which +// point the arm's population becomes the `.dag` names it was always meant to name and the diagnostic +// can be added without changing anything else here. +// +// A PREDICTION WAS REFUTED HERE AND IT IS RECORDED BECAUSE IT WAS DECISION-RELEVANT. Registry-absent +// was expected to overlap heavily with UnlistedImportUse -- both said to mean "referenced but never +// imported" -- and if it did not, one of the two mechanisms was not what anyone thought. Measured on +// the same run: 63 registry-absent names, 36 UnlistedImportUse names, INTERSECTION ZERO. The +// mechanisms have near-disjoint vocabularies, because UnlistedImportUse names `.dag` types masked at +// resolve time while registry-absent is dominated by Rust tokens that never passed through the +// resolver at all. They are not two views of one population. // // LOCATION GRAIN, stated rather than implied: the referencing MODULE's declaration span, because a // row carries (module, name, disposition) and the reference SITES are reduced to bare names by the @@ -3771,14 +3789,6 @@ fn reference_derived_candidate_disposition(name: String, this_module_name: Strin fn reference_derived_row_diagnostics(rows: List, module_span: SourceSpan) -> List { rows |> flat_map(r => match r.disposition { - CandidateRegistryAbsent => [make_error_node( - diagnostic: ReferenceDerivedImportProviderUnknown { - name: r.name, - referencing_module: r.module_name, - span: module_span - }, - module_name: r.module_name - )] CandidateExportProofFailed { provider_module: p } => [make_error_node( diagnostic: ReferenceDerivedImportExportUnproven { name: r.name, diff --git a/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag b/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag index a1a505c8d48..59c353ad827 100644 --- a/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag +++ b/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag @@ -149,7 +149,7 @@ test fn non_variant_name_still_answers_registry_absent() -> Bool { )) == "registry-absent" } -test fn only_the_two_failing_arms_produce_diagnostics() -> Bool { +test fn only_export_proof_failed_produces_a_diagnostic() -> Bool { let rows = [ ReferenceDerivedCandidateRow { module_name: "m", name: "a", disposition: CandidateSurvived { provider_module: "p" } }, ReferenceDerivedCandidateRow { module_name: "m", name: "b", disposition: CandidateOwnModule }, @@ -157,25 +157,30 @@ test fn only_the_two_failing_arms_produce_diagnostics() -> Bool { ReferenceDerivedCandidateRow { module_name: "m", name: "c", disposition: CandidateRegistryAbsent }, ReferenceDerivedCandidateRow { module_name: "m", name: "d", disposition: CandidateExportProofFailed { provider_module: "p" } } ] - reference_derived_row_diagnostics(rows: rows, module_span: no_span()) |> count == 2 + reference_derived_row_diagnostics(rows: rows, module_span: no_span()) |> count == 1 } -test fn both_refusal_diagnostics_are_advisory_today() -> Bool { +test fn the_export_unproven_diagnostic_is_advisory_today() -> Bool { let rows = [ - ReferenceDerivedCandidateRow { module_name: "m", name: "c", disposition: CandidateRegistryAbsent }, ReferenceDerivedCandidateRow { module_name: "m", name: "d", disposition: CandidateExportProofFailed { provider_module: "p" } } ] reference_derived_row_diagnostics(rows: rows, module_span: no_span()) |> all(e => !is_error_diagnostic(d: e.diagnostic)) } -test fn the_two_refusals_carry_different_remedies() -> Bool { +data registry_absent_not_surfaced_note: String = "The RED that keeps registry-absent OUT of the diagnostic stream. Measured over the regen seed closure, that arm returns 473 rows over 63 names dominated by Rust target-language tokens -- Vec, bool, Option, i64, empty_map -- which the candidate walk proposes by tokenizing the module's own emitted Rust and for which no .dag provider can ever exist. Wiring it would print a false report on every build. This test fails the moment someone adds that arm to reference_derived_row_diagnostics, which is the intended wall until the candidate walk stops proposing target-language vocabulary." + +test fn registry_absent_produces_no_diagnostic() -> Bool { + let rows = [ + ReferenceDerivedCandidateRow { module_name: "m", name: "bool", disposition: CandidateRegistryAbsent } + ] + reference_derived_row_diagnostics(rows: rows, module_span: no_span()) |> count == 0 +} + +test fn the_export_unproven_message_names_its_own_remedy() -> Bool { let rows = [ - ReferenceDerivedCandidateRow { module_name: "m", name: "c", disposition: CandidateRegistryAbsent }, ReferenceDerivedCandidateRow { module_name: "m", name: "d", disposition: CandidateExportProofFailed { provider_module: "p" } } ] - let msgs = reference_derived_row_diagnostics(rows: rows, module_span: no_span()) - |> map(e => diagnostic_to_message(d: e.diagnostic)) - (msgs |> any(m => string_contains(s: m, pattern: "AUTHOR AN IMPORT"))) - && (msgs |> any(m => string_contains(s: m, pattern: "NOT an import"))) + reference_derived_row_diagnostics(rows: rows, module_span: no_span()) + |> all(e => string_contains(s: diagnostic_to_message(d: e.diagnostic), pattern: "NOT an import")) } From 56be069b7e91c1dca4ead1ba37514c755c22165b Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 27 Aug 2026 18:28:22 +0000 Subject: [PATCH 06/21] Flip ExportUnproven to blocking (approved), both halves of the condition met warm-hawk-909 approved the flip: population zero over a named closure AND a discriminating RED authorable -- and authored -- at the fixture boundary. The second half is what separates a real wall that is quiet from an arm that has never fired, and only the first is a count. ReferenceDerivedImportExportUnproven leaves the advisory arms of is_error_diagnostic, is_interpreter_blocking_diagnostic and the discovery-corpus advisory set; the default blocking arm now carries it. ProviderUnknown stays advisory and stays produced by nothing. The emission still produces its files beside a blocking diagnostic rather than returning none: production precedes adjudication, so the candidate tree survives the refusal and the refusal is what stops the line. The carrier now records why ProviderUnknown must stay unwired in the strongest available form, which is not that its rows are unfixable: Vec/bool/Option/i64 are EVIDENCE THE CANDIDATE FILTER IS WRONG, and wiring a permanently-false report onto nearly every build is worse than shipping nothing because IT TRAINS READERS TO IGNORE THE CHANNEL. A diagnostic nobody reads is worth less than an absent one, because the absent one is honest about its coverage. Witness updated: the export-unproven diagnostic is asserted blocking rather than advisory. Co-Authored-By: Claude Opus 5 (1M context) --- src/v1/00_core.dag | 5 +---- .../reference_derived_disposition_census_witness_test.dag | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/v1/00_core.dag b/src/v1/00_core.dag index ebfed1e7c82..6b3c7ee4a42 100644 --- a/src/v1/00_core.dag +++ b/src/v1/00_core.dag @@ -516,7 +516,6 @@ fn is_error_diagnostic(d: CompilerDiagnostic) -> Bool { match d { UnlistedImportUse { name: _, span: _ } => false ReferenceDerivedImportProviderUnknown { name: _, referencing_module: _, span: _ } => false - ReferenceDerivedImportExportUnproven { name: _, referencing_module: _, provider_module: _, span: _ } => false UnlistedVariantValueUse { name: _, span: _ } => false MethodExistenceFrontierAdmitted { method: _, receiver_type: _, trigger: _, span: _ } => false ReceiverTypeUnestablished { method: _, span: _ } => false @@ -526,7 +525,7 @@ fn is_error_diagnostic(d: CompilerDiagnostic) -> Bool { } } -data reference_derived_import_refusal_severity_note: String = "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). ONE OF THESE TWO DIAGNOSTICS IS PRODUCED TODAY AND THE OTHER IS NOT, and which is which was decided by measurement. ReferenceDerivedImportExportUnproven is emitted from CandidateExportProofFailed: the provider is present in the closure and its export could not be established, and the remedy is NOT an import -- the provider is already here -- but making the emitter able to prove an export it may already hold. ReferenceDerivedImportProviderUnknown is DECLARED HERE AND PRODUCED BY NOTHING: its arm, CandidateRegistryAbsent, was instrumented and measured over the regen seed closure and returned 473 rows over 63 names across 110 modules whose top entries are Vec, bool, Option, i64, empty_map, BTreeSet, Fn, fn, u8, serde_json -- Rust TARGET-LANGUAGE tokens proposed by the candidate walk's emitted-source arm, which tokenizes the module's own emitted Rust. No .dag provider can supply `bool`, so a diagnostic there would be a false report in nearly every row, printed on every build. The variant is kept declared because the class is real and its shape is settled; what is missing is a candidate walk that does not propose target-language vocabulary, and that is the trigger for wiring it, not a burndown of the 473. THE TWO ARE SEPARATE DIAGNOSTICS RATHER THAN ONE WITH A CAUSE FIELD (ruling: warm-hawk-909, compiler direction, relayed through smart-ram-730, 2026-08-27), because the question is upstream of severity: registry-absent is fixed by AUTHORING AN IMPORT and export-proof-failed is fixed by proving an export that already exists -- opposite remedies, different owners, different populations, different reachability -- and one symbol over two opposite remedies is the state-space conflation DESIGN names; 4b files one row per class. That the two turned out to need different TREATMENT, not merely different severity, is the strongest evidence the split was right. BOTH ARE ADVISORY. FLIP CONDITION, strictly stronger than a count: an arm flips to blocking when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient -- an observation of zero on an arm that has never fired is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check cited as coverage, which 4b rates worse than absent. FOR ExportUnproven BOTH HALVES NOW HOLD: the population is zero over the regen seed closure (claim_executor --required-regen --source-root dag --source-root src/v2, whose subject is regen_input_sources grown to a joint fixpoint over import, dotted-reference and bare-reference edges), and the RED is authorable and AUTHORED at the fixture boundary in v1.tests.claim.reference_derived_disposition_census_witness_test, which builds a CandidateExportProofFailed row and asserts the diagnostic. Whether it flips is warm-hawk-909's call, not this module's. A REFUTED PREDICTION, recorded because it was decision-relevant and someone asked for it before either arm flipped: registry-absent was expected to overlap heavily with UnlistedImportUse, both being described as 'referenced but never imported'. Measured on one run: 63 registry-absent names, 36 UnlistedImportUse names, INTERSECTION ZERO. UnlistedImportUse names .dag types masked at resolve time; registry-absent is dominated by Rust tokens that never reached the resolver. They are not two views of one population, and ProviderUnknown's trigger therefore does NOT point at the family-closure-SVN burndown as was previously assumed here." +data reference_derived_import_refusal_severity_note: String = "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). TWO DIAGNOSTICS, ONE WIRED AND BLOCKING, ONE DECLARED AND PRODUCED BY NOTHING -- and which is which was decided by measurement, not by preference. ReferenceDerivedImportExportUnproven is emitted from CandidateExportProofFailed: the provider is present in the closure and its export could not be established, and the remedy is NOT an import -- the provider is already here -- but making the emitter able to prove an export it may already hold. IT IS BLOCKING (warm-hawk-909, compiler direction, 2026-08-27), under a flip condition strictly stronger than a count: an arm flips when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient, because an observation of zero on an arm that has never fired is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check cited as coverage, which 4b rates worse than absent. BOTH HALVES HOLD HERE: the population is zero over the regen seed closure (claim_executor --required-regen --source-root dag --source-root src/v2, whose subject is regen_input_sources grown to a joint fixpoint over import, dotted-reference and bare-reference edges), and the RED is authorable and AUTHORED at the fixture boundary in v1.tests.claim.reference_derived_disposition_census_witness_test, which builds a CandidateExportProofFailed row and asserts the diagnostic. The emission still produces its files beside a blocking diagnostic rather than returning none: production precedes adjudication, so the candidate tree survives the refusal and the refusal is what stops the line. ReferenceDerivedImportProviderUnknown is DECLARED HERE AND PRODUCED BY NOTHING. Its arm, CandidateRegistryAbsent, was instrumented and measured over the same closure and returned 473 rows over 63 names across 110 modules whose top entries are Vec 88, bool 73, Option 50, i64 48, empty_map 33, then BTreeSet, Fn, fn, u8, serde_json. Those are RUST TARGET-LANGUAGE TOKENS, admitted by the candidate walk's third disjunct, which offers any identifier appearing in the module's own emitted Rust. No .dag provider can supply `bool`, so registry-absent is the CORRECT disposition for them -- the rows are not a roster to burn down, they are EVIDENCE THE CANDIDATE FILTER IS WRONG. Wiring a diagnostic to that input would print a permanently-false report on nearly every build, and the reason that is worse than shipping nothing is that IT TRAINS READERS TO IGNORE THE CHANNEL: a diagnostic nobody reads is worth less than an absent one, because the absent one is honest about its coverage. The variant stays declared because the class is real and its shape is settled; the trigger for wiring it is that the candidate walk stop proposing target-language vocabulary. A witness asserts zero diagnostics from that arm and fails the moment anyone wires it. THE TWO ARE SEPARATE DIAGNOSTICS RATHER THAN ONE WITH A CAUSE FIELD, because the question is upstream of severity: registry-absent is fixed by authoring an import, export-proof-failed by proving an export that already exists -- opposite remedies, different owners, different populations, different reachability -- and 4b files one row per class. That the two turned out to need different TREATMENT rather than merely different severity is the strongest evidence the split was right. A REFUTED PREDICTION, recorded rather than quietly repointed. Registry-absent was predicted to overlap heavily with UnlistedImportUse, both being described as 'referenced but never imported', and the prediction was made because closure membership is driven by imports. Measured on one run: 63 registry-absent names, 36 UnlistedImportUse names, INTERSECTION ZERO. UnlistedImportUse names .dag types masked at RESOLVE time; registry-absent is dominated by Rust tokens that never reached the resolver. The reasoning was wrong at the MECHANISM, not merely the magnitude. ProviderUnknown's trigger therefore does NOT point at the family-closure-SVN burndown as this carrier previously stated, and the falsification is recorded here rather than the trigger being silently repointed -- a repointed trigger would inherit the credibility of a prediction that just died, so whatever that trigger eventually becomes is argued fresh." data where_refinement_deferral_reason_scaffold_note: String = "owner: v1.compiler.infer (00_core diagnostic partition). lane: where-refinement literal-wall enforcement. interim: WhereRefinementUnenforced.reason is a closed-string sum enrolled in is_where_refinement_unenforced_advisory_reason; any unlisted reason fails closed blocking. bound: only the five deferral strings emitted by 04_infer today; classifier/eval/equivalence arms must change together until coproduct lands. dissolve-on: feature:where-refinement-predicate-coproduct (WhereRefinementDeferralReason coproduct on the diagnostic carrier)." @@ -542,7 +541,6 @@ fn is_interpreter_blocking_diagnostic(d: CompilerDiagnostic) -> Bool { ServiceConfigReferenceJudgmentDeferred { field: _, referenced_name: _, trigger: _, span: _ } => false DeclaredTypeInhabitanceUndecided { position: _, reason: _, span: _ } => false ReferenceDerivedImportProviderUnknown { name: _, referencing_module: _, span: _ } => false - ReferenceDerivedImportExportUnproven { name: _, referencing_module: _, provider_module: _, span: _ } => false _ => true } } @@ -556,7 +554,6 @@ fn is_discovery_corpus_advisory_typecheck_diagnostic(d: CompilerDiagnostic) -> B ServiceConfigReferenceJudgmentDeferred { field: _, referenced_name: _, trigger: _, span: _ } => true DeclaredTypeInhabitanceUndecided { position: _, reason: _, span: _ } => true ReferenceDerivedImportProviderUnknown { name: _, referencing_module: _, span: _ } => true - ReferenceDerivedImportExportUnproven { name: _, referencing_module: _, provider_module: _, span: _ } => true WhereRefinementUnenforced { predicate: _, formal_type: _, reason: r, span: _ } => is_where_refinement_unenforced_advisory_reason(reason: r) _ => false diff --git a/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag b/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag index 59c353ad827..09932adc7bb 100644 --- a/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag +++ b/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag @@ -160,12 +160,12 @@ test fn only_export_proof_failed_produces_a_diagnostic() -> Bool { reference_derived_row_diagnostics(rows: rows, module_span: no_span()) |> count == 1 } -test fn the_export_unproven_diagnostic_is_advisory_today() -> Bool { +test fn the_export_unproven_diagnostic_is_blocking() -> Bool { let rows = [ ReferenceDerivedCandidateRow { module_name: "m", name: "d", disposition: CandidateExportProofFailed { provider_module: "p" } } ] reference_derived_row_diagnostics(rows: rows, module_span: no_span()) - |> all(e => !is_error_diagnostic(d: e.diagnostic)) + |> all(e => is_error_diagnostic(d: e.diagnostic)) } data registry_absent_not_surfaced_note: String = "The RED that keeps registry-absent OUT of the diagnostic stream. Measured over the regen seed closure, that arm returns 473 rows over 63 names dominated by Rust target-language tokens -- Vec, bool, Option, i64, empty_map -- which the candidate walk proposes by tokenizing the module's own emitted Rust and for which no .dag provider can ever exist. Wiring it would print a false report on every build. This test fails the moment someone adds that arm to reference_derived_row_diagnostics, which is the intended wall until the candidate walk stops proposing target-language vocabulary." From 8ee2a04ca1729ca997048220caeb37b2c2753c38 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 27 Aug 2026 18:38:37 +0000 Subject: [PATCH 07/21] Correct the producer attribution, and make the diagnostic match wildcard-free TWO FIXES, both from clever-boar-140, who owns the coproduct. 1. MECHANISM ATTRIBUTION WAS WRONG IN MY CARRIER. I wrote that the candidate filter's emitted-source disjunct 'offers any identifier appearing in the emitted Rust'. It cannot: that disjunct is an admission GATE over an already proposed candidate list. The PRODUCER of Vec/bool/i64 is collect_item_realized_surface_names -- rust_identifier_tokens over render_rust_type -- which emits target-language SPELLINGS by construction, and reference_is_host_realized_builtin misses them because it is keyed on .dag vocabulary (is_container_type reads std.types container_type_arity), so Vec and BTreeSet, the Rust spellings of List and Set, pass a filter that exists precisely to remove host-realized names. Verified both halves against the code before taking the correction. It matters because it moves where a repair goes: narrowing the gate would delete genuine emitter-attested candidates and leave the real source untouched. 2. THE DIAGNOSTIC MATCH HAD A WILDCARD, which is the defect this change exists to repair, in the change itself. The coproduct now carries THREE not-applicable arms against two genuine drops, so a '_' makes the next drop arm silent by default -- total at the level examined, blind one level down. Every non-reporting disposition is now enumerated, so a sixth arm fails to compile here instead of inheriting 'produces no diagnostic'. Co-Authored-By: Claude Opus 5 (1M context) --- src/v1/00_core.dag | 2 +- src/v1/05_emit_rust.dag | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/v1/00_core.dag b/src/v1/00_core.dag index 6b3c7ee4a42..b32298836df 100644 --- a/src/v1/00_core.dag +++ b/src/v1/00_core.dag @@ -525,7 +525,7 @@ fn is_error_diagnostic(d: CompilerDiagnostic) -> Bool { } } -data reference_derived_import_refusal_severity_note: String = "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). TWO DIAGNOSTICS, ONE WIRED AND BLOCKING, ONE DECLARED AND PRODUCED BY NOTHING -- and which is which was decided by measurement, not by preference. ReferenceDerivedImportExportUnproven is emitted from CandidateExportProofFailed: the provider is present in the closure and its export could not be established, and the remedy is NOT an import -- the provider is already here -- but making the emitter able to prove an export it may already hold. IT IS BLOCKING (warm-hawk-909, compiler direction, 2026-08-27), under a flip condition strictly stronger than a count: an arm flips when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient, because an observation of zero on an arm that has never fired is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check cited as coverage, which 4b rates worse than absent. BOTH HALVES HOLD HERE: the population is zero over the regen seed closure (claim_executor --required-regen --source-root dag --source-root src/v2, whose subject is regen_input_sources grown to a joint fixpoint over import, dotted-reference and bare-reference edges), and the RED is authorable and AUTHORED at the fixture boundary in v1.tests.claim.reference_derived_disposition_census_witness_test, which builds a CandidateExportProofFailed row and asserts the diagnostic. The emission still produces its files beside a blocking diagnostic rather than returning none: production precedes adjudication, so the candidate tree survives the refusal and the refusal is what stops the line. ReferenceDerivedImportProviderUnknown is DECLARED HERE AND PRODUCED BY NOTHING. Its arm, CandidateRegistryAbsent, was instrumented and measured over the same closure and returned 473 rows over 63 names across 110 modules whose top entries are Vec 88, bool 73, Option 50, i64 48, empty_map 33, then BTreeSet, Fn, fn, u8, serde_json. Those are RUST TARGET-LANGUAGE TOKENS, admitted by the candidate walk's third disjunct, which offers any identifier appearing in the module's own emitted Rust. No .dag provider can supply `bool`, so registry-absent is the CORRECT disposition for them -- the rows are not a roster to burn down, they are EVIDENCE THE CANDIDATE FILTER IS WRONG. Wiring a diagnostic to that input would print a permanently-false report on nearly every build, and the reason that is worse than shipping nothing is that IT TRAINS READERS TO IGNORE THE CHANNEL: a diagnostic nobody reads is worth less than an absent one, because the absent one is honest about its coverage. The variant stays declared because the class is real and its shape is settled; the trigger for wiring it is that the candidate walk stop proposing target-language vocabulary. A witness asserts zero diagnostics from that arm and fails the moment anyone wires it. THE TWO ARE SEPARATE DIAGNOSTICS RATHER THAN ONE WITH A CAUSE FIELD, because the question is upstream of severity: registry-absent is fixed by authoring an import, export-proof-failed by proving an export that already exists -- opposite remedies, different owners, different populations, different reachability -- and 4b files one row per class. That the two turned out to need different TREATMENT rather than merely different severity is the strongest evidence the split was right. A REFUTED PREDICTION, recorded rather than quietly repointed. Registry-absent was predicted to overlap heavily with UnlistedImportUse, both being described as 'referenced but never imported', and the prediction was made because closure membership is driven by imports. Measured on one run: 63 registry-absent names, 36 UnlistedImportUse names, INTERSECTION ZERO. UnlistedImportUse names .dag types masked at RESOLVE time; registry-absent is dominated by Rust tokens that never reached the resolver. The reasoning was wrong at the MECHANISM, not merely the magnitude. ProviderUnknown's trigger therefore does NOT point at the family-closure-SVN burndown as this carrier previously stated, and the falsification is recorded here rather than the trigger being silently repointed -- a repointed trigger would inherit the credibility of a prediction that just died, so whatever that trigger eventually becomes is argued fresh." +data reference_derived_import_refusal_severity_note: String = "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). TWO DIAGNOSTICS, ONE WIRED AND BLOCKING, ONE DECLARED AND PRODUCED BY NOTHING -- and which is which was decided by measurement, not by preference. ReferenceDerivedImportExportUnproven is emitted from CandidateExportProofFailed: the provider is present in the closure and its export could not be established, and the remedy is NOT an import -- the provider is already here -- but making the emitter able to prove an export it may already hold. IT IS BLOCKING (warm-hawk-909, compiler direction, 2026-08-27), under a flip condition strictly stronger than a count: an arm flips when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient, because an observation of zero on an arm that has never fired is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check cited as coverage, which 4b rates worse than absent. BOTH HALVES HOLD HERE: the population is zero over the regen seed closure (claim_executor --required-regen --source-root dag --source-root src/v2, whose subject is regen_input_sources grown to a joint fixpoint over import, dotted-reference and bare-reference edges), and the RED is authorable and AUTHORED at the fixture boundary in v1.tests.claim.reference_derived_disposition_census_witness_test, which builds a CandidateExportProofFailed row and asserts the diagnostic. The emission still produces its files beside a blocking diagnostic rather than returning none: production precedes adjudication, so the candidate tree survives the refusal and the refusal is what stops the line. ReferenceDerivedImportProviderUnknown is DECLARED HERE AND PRODUCED BY NOTHING. Its arm, CandidateRegistryAbsent, was instrumented and measured over the same closure and returned 473 rows over 63 names across 110 modules whose top entries are Vec 88, bool 73, Option 50, i64 48, empty_map 33, then BTreeSet, Fn, fn, u8, serde_json. Those are RUST TARGET-LANGUAGE TOKENS, and the PRODUCER is collect_item_realized_surface_names, which is rust_identifier_tokens over render_rust_type and therefore emits target-language SPELLINGS by construction (correction from clever-boar-140, who owns the coproduct: an earlier revision of this note blamed the candidate filter's emitted-source disjunct, but that disjunct is an admission GATE over an already-proposed candidate list and cannot itself propose anything -- narrowing it would delete genuine emitter-attested candidates and leave the actual source untouched). reference_is_host_realized_builtin does not catch them because it is keyed on .dag vocabulary -- is_container_type reads std.types container_type_arity -- so Vec and BTreeSet, the RUST spellings of List and Set, pass a filter that exists precisely to remove host-realized names. No .dag provider can supply `bool`, so registry-absent is the CORRECT disposition for them -- the rows are not a roster to burn down, they are EVIDENCE THE CANDIDATE FILTER IS WRONG. Wiring a diagnostic to that input would print a permanently-false report on nearly every build, and the reason that is worse than shipping nothing is that IT TRAINS READERS TO IGNORE THE CHANNEL: a diagnostic nobody reads is worth less than an absent one, because the absent one is honest about its coverage. The variant stays declared because the class is real and its shape is settled; the trigger for wiring it is that the candidate walk stop proposing target-language vocabulary. A witness asserts zero diagnostics from that arm and fails the moment anyone wires it. THE TWO ARE SEPARATE DIAGNOSTICS RATHER THAN ONE WITH A CAUSE FIELD, because the question is upstream of severity: registry-absent is fixed by authoring an import, export-proof-failed by proving an export that already exists -- opposite remedies, different owners, different populations, different reachability -- and 4b files one row per class. That the two turned out to need different TREATMENT rather than merely different severity is the strongest evidence the split was right. A REFUTED PREDICTION, recorded rather than quietly repointed. Registry-absent was predicted to overlap heavily with UnlistedImportUse, both being described as 'referenced but never imported', and the prediction was made because closure membership is driven by imports. Measured on one run: 63 registry-absent names, 36 UnlistedImportUse names, INTERSECTION ZERO. UnlistedImportUse names .dag types masked at RESOLVE time; registry-absent is dominated by Rust tokens that never reached the resolver. The reasoning was wrong at the MECHANISM, not merely the magnitude. ProviderUnknown's trigger therefore does NOT point at the family-closure-SVN burndown as this carrier previously stated, and the falsification is recorded here rather than the trigger being silently repointed -- a repointed trigger would inherit the credibility of a prediction that just died, so whatever that trigger eventually becomes is argued fresh." data where_refinement_deferral_reason_scaffold_note: String = "owner: v1.compiler.infer (00_core diagnostic partition). lane: where-refinement literal-wall enforcement. interim: WhereRefinementUnenforced.reason is a closed-string sum enrolled in is_where_refinement_unenforced_advisory_reason; any unlisted reason fails closed blocking. bound: only the five deferral strings emitted by 04_infer today; classifier/eval/equivalence arms must change together until coproduct lands. dissolve-on: feature:where-refinement-predicate-coproduct (WhereRefinementDeferralReason coproduct on the diagnostic carrier)." diff --git a/src/v1/05_emit_rust.dag b/src/v1/05_emit_rust.dag index b8026cb0e61..c6a35bcd6f3 100644 --- a/src/v1/05_emit_rust.dag +++ b/src/v1/05_emit_rust.dag @@ -3782,6 +3782,12 @@ fn reference_derived_candidate_disposition(name: String, this_module_name: Strin // resolve time while registry-absent is dominated by Rust tokens that never passed through the // resolver at all. They are not two views of one population. // +// NO WILDCARD ARM, DELIBERATELY. Every non-reporting disposition is enumerated, so a SIXTH arm added +// to the coproduct fails to compile here rather than silently inheriting "produces no diagnostic". +// That matters because the coproduct now holds three not-applicable arms against two genuine drops, +// and a `_` would make the next drop arm silent by default -- total at the level examined, blind one +// level down, which is the shape this whole change exists to repair. +// // LOCATION GRAIN, stated rather than implied: the referencing MODULE's declaration span, because a // row carries (module, name, disposition) and the reference SITES are reduced to bare names by the // candidate walk before any disposition is assigned. Carrying the originating span on the row is the @@ -3798,7 +3804,10 @@ fn reference_derived_row_diagnostics(rows: List, m }, module_name: r.module_name )] - _ => [] + CandidateSurvived { provider_module: _ } => [] + CandidateOwnModule => [] + CandidateVariantDelegatedToParent { parent_enum: _ } => [] + CandidateRegistryAbsent => [] } ) } From e954b7376739ad7abe699bc34a5efde04ad48603 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 27 Aug 2026 19:02:19 +0000 Subject: [PATCH 08/21] Correct the registry-absent characterisation: the 473 is MIXED, not all target vocabulary I generalised from the head of a sorted list -- the rule I had derived from the '-' rows this morning and then broke on the population I derived it from. deep-ant-102 pushed back on the census SCOPE and smart-ram-730 relayed it; both halves verified against the code before taking it. THE TRAP: the census invocation passes --source-root dag --source-root src/v2, but the SUBJECT is regen_input_sources, whose roots are SeedV1 and DagCorpus (cli_run regen_source_roots) and exclude src/v2 entirely -- stage0 IS the v1 seed and a seed reaching into src/v2 would depend on the successor it bootstraps toward. The source-root FLAGS and the regen SUBJECT are not the same thing. So registry-absent conflates two classes: (a) EXTINGUISHED Vec, bool, i64, BTreeSet -- no .dag declaration exists or can; render_rust_type minted the spelling. (b) OUT-OF-CLOSURE empty_map 33 (v2.std.collection), Optional 25 with Present 47 and Absent 13 (v2.std.optional) -- REAL .dag names whose providers exist and were not selected in. Verified by reading both declarations. Class (b) is precisely the closure-conditioned population this change exists to surface, sitting inside rows I had written off as unfixable. The proportion is UNMEASURED -- about twelve of 63 names were examined -- and the carrier says so rather than inferring it. WHAT DOES NOT CHANGE: the flip, which rests on an authored fixture RED and not on the zero; and keeping ProviderUnknown unwired, which is justified by class (a) alone -- a permanently-false report on those rows trains readers to ignore the channel whether or not part of the population is movable. WHAT CHANGES: registry-absent is ENTRY-RELATIVE, not a fixed target, and its trigger is now that class (a) leave the arm rather than that the whole population be dismissed. Co-Authored-By: Claude Opus 5 (1M context) --- src/v1/00_core.dag | 2 +- src/v1/05_emit_rust.dag | 35 ++++++++++++++++++++++++----------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/v1/00_core.dag b/src/v1/00_core.dag index b32298836df..1d855f27b2f 100644 --- a/src/v1/00_core.dag +++ b/src/v1/00_core.dag @@ -525,7 +525,7 @@ fn is_error_diagnostic(d: CompilerDiagnostic) -> Bool { } } -data reference_derived_import_refusal_severity_note: String = "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). TWO DIAGNOSTICS, ONE WIRED AND BLOCKING, ONE DECLARED AND PRODUCED BY NOTHING -- and which is which was decided by measurement, not by preference. ReferenceDerivedImportExportUnproven is emitted from CandidateExportProofFailed: the provider is present in the closure and its export could not be established, and the remedy is NOT an import -- the provider is already here -- but making the emitter able to prove an export it may already hold. IT IS BLOCKING (warm-hawk-909, compiler direction, 2026-08-27), under a flip condition strictly stronger than a count: an arm flips when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient, because an observation of zero on an arm that has never fired is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check cited as coverage, which 4b rates worse than absent. BOTH HALVES HOLD HERE: the population is zero over the regen seed closure (claim_executor --required-regen --source-root dag --source-root src/v2, whose subject is regen_input_sources grown to a joint fixpoint over import, dotted-reference and bare-reference edges), and the RED is authorable and AUTHORED at the fixture boundary in v1.tests.claim.reference_derived_disposition_census_witness_test, which builds a CandidateExportProofFailed row and asserts the diagnostic. The emission still produces its files beside a blocking diagnostic rather than returning none: production precedes adjudication, so the candidate tree survives the refusal and the refusal is what stops the line. ReferenceDerivedImportProviderUnknown is DECLARED HERE AND PRODUCED BY NOTHING. Its arm, CandidateRegistryAbsent, was instrumented and measured over the same closure and returned 473 rows over 63 names across 110 modules whose top entries are Vec 88, bool 73, Option 50, i64 48, empty_map 33, then BTreeSet, Fn, fn, u8, serde_json. Those are RUST TARGET-LANGUAGE TOKENS, and the PRODUCER is collect_item_realized_surface_names, which is rust_identifier_tokens over render_rust_type and therefore emits target-language SPELLINGS by construction (correction from clever-boar-140, who owns the coproduct: an earlier revision of this note blamed the candidate filter's emitted-source disjunct, but that disjunct is an admission GATE over an already-proposed candidate list and cannot itself propose anything -- narrowing it would delete genuine emitter-attested candidates and leave the actual source untouched). reference_is_host_realized_builtin does not catch them because it is keyed on .dag vocabulary -- is_container_type reads std.types container_type_arity -- so Vec and BTreeSet, the RUST spellings of List and Set, pass a filter that exists precisely to remove host-realized names. No .dag provider can supply `bool`, so registry-absent is the CORRECT disposition for them -- the rows are not a roster to burn down, they are EVIDENCE THE CANDIDATE FILTER IS WRONG. Wiring a diagnostic to that input would print a permanently-false report on nearly every build, and the reason that is worse than shipping nothing is that IT TRAINS READERS TO IGNORE THE CHANNEL: a diagnostic nobody reads is worth less than an absent one, because the absent one is honest about its coverage. The variant stays declared because the class is real and its shape is settled; the trigger for wiring it is that the candidate walk stop proposing target-language vocabulary. A witness asserts zero diagnostics from that arm and fails the moment anyone wires it. THE TWO ARE SEPARATE DIAGNOSTICS RATHER THAN ONE WITH A CAUSE FIELD, because the question is upstream of severity: registry-absent is fixed by authoring an import, export-proof-failed by proving an export that already exists -- opposite remedies, different owners, different populations, different reachability -- and 4b files one row per class. That the two turned out to need different TREATMENT rather than merely different severity is the strongest evidence the split was right. A REFUTED PREDICTION, recorded rather than quietly repointed. Registry-absent was predicted to overlap heavily with UnlistedImportUse, both being described as 'referenced but never imported', and the prediction was made because closure membership is driven by imports. Measured on one run: 63 registry-absent names, 36 UnlistedImportUse names, INTERSECTION ZERO. UnlistedImportUse names .dag types masked at RESOLVE time; registry-absent is dominated by Rust tokens that never reached the resolver. The reasoning was wrong at the MECHANISM, not merely the magnitude. ProviderUnknown's trigger therefore does NOT point at the family-closure-SVN burndown as this carrier previously stated, and the falsification is recorded here rather than the trigger being silently repointed -- a repointed trigger would inherit the credibility of a prediction that just died, so whatever that trigger eventually becomes is argued fresh." +data reference_derived_import_refusal_severity_note: String = "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). TWO DIAGNOSTICS, ONE WIRED AND BLOCKING, ONE DECLARED AND PRODUCED BY NOTHING -- and which is which was decided by measurement, not by preference. ReferenceDerivedImportExportUnproven is emitted from CandidateExportProofFailed: the provider is present in the closure and its export could not be established, and the remedy is NOT an import -- the provider is already here -- but making the emitter able to prove an export it may already hold. IT IS BLOCKING (warm-hawk-909, compiler direction, 2026-08-27), under a flip condition strictly stronger than a count: an arm flips when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient, because an observation of zero on an arm that has never fired is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check cited as coverage, which 4b rates worse than absent. BOTH HALVES HOLD HERE: the population is zero over the regen seed closure (claim_executor --required-regen --source-root dag --source-root src/v2, whose subject is regen_input_sources grown to a joint fixpoint over import, dotted-reference and bare-reference edges), and the RED is authorable and AUTHORED at the fixture boundary in v1.tests.claim.reference_derived_disposition_census_witness_test, which builds a CandidateExportProofFailed row and asserts the diagnostic. The emission still produces its files beside a blocking diagnostic rather than returning none: production precedes adjudication, so the candidate tree survives the refusal and the refusal is what stops the line. ReferenceDerivedImportProviderUnknown is DECLARED HERE AND PRODUCED BY NOTHING. Its arm, CandidateRegistryAbsent, was instrumented and measured over the same closure and returned 473 rows over 63 names across 110 modules. THAT POPULATION IS MIXED, AND AN EARLIER REVISION OF THIS NOTE CALLED IT ALL TARGET VOCABULARY -- corrected 2026-08-27 after deep-ant-102 pushed back on the census SCOPE. Two classes are conflated inside it. (a) EXTINGUISHED -- Vec 88, bool 73, i64 48, BTreeSet: no .dag declaration exists anywhere and none can, because the PRODUCER is collect_item_realized_surface_names, which is rust_identifier_tokens over render_rust_type and so emits target-language SPELLINGS by construction (attribution corrected by clever-boar-140, who owns the coproduct: the candidate filter's emitted-source disjunct is an admission GATE over an already-proposed list and cannot itself propose anything, so narrowing it would delete genuine emitter-attested candidates and leave the real source untouched). reference_is_host_realized_builtin misses these because it is keyed on .dag vocabulary -- is_container_type reads std.types container_type_arity -- so Vec and BTreeSet, the RUST spellings of List and Set, pass a filter that exists precisely to remove host-realized names. (b) OUT-OF-CLOSURE -- empty_map 33 (declared in v2.std.collection) and Optional 25 with its variants Present 47 and Absent 13 (declared in v2.std.optional): REAL .dag names whose providers exist and were not selected in. THE PROPORTION BETWEEN (a) AND (b) IS UNMEASURED and must not be inferred from these examples; roughly twelve of the 63 names were examined. WHY (b) IS THERE AT ALL is a trap worth carrying: the census invocation passes --source-root dag --source-root src/v2, but the SUBJECT is regen_input_sources, whose roots are SeedV1 and DagCorpus and exclude src/v2 entirely -- stage0 IS the v1 seed, and a seed reaching into src/v2 would depend on the successor it bootstraps toward. The source-root FLAGS and the regen SUBJECT are not the same thing. So class (b) is precisely the closure-conditioned population this change exists to make visible, sitting inside the rows that had been written off, and registry-absent is ENTRY-RELATIVE rather than a fixed target: a reader who takes the number as stable has made the denominator error. THE 473 IS NOT A ROSTER OF UNFIXABLE ROWS. Wiring a diagnostic to it would still print a permanently-false report on every class-(a) row -- which is why the arm stays unwired even though part of the population is movable -- and the reason that is worse than shipping nothing is that IT TRAINS READERS TO IGNORE THE CHANNEL: a diagnostic nobody reads is worth less than an absent one, because the absent one is honest about its coverage. The variant stays declared because the class is real and its shape is settled; the trigger for wiring it is that class (a) leave the arm -- the candidate walk ceasing to propose target-language vocabulary -- after which the residue is class (b) and is exactly the closure-conditioned population worth reporting. A witness asserts zero diagnostics from that arm and fails the moment anyone wires it. THE TWO ARE SEPARATE DIAGNOSTICS RATHER THAN ONE WITH A CAUSE FIELD, because the question is upstream of severity: registry-absent is fixed by authoring an import, export-proof-failed by proving an export that already exists -- opposite remedies, different owners, different populations, different reachability -- and 4b files one row per class. That the two turned out to need different TREATMENT rather than merely different severity is the strongest evidence the split was right. A REFUTED PREDICTION, recorded rather than quietly repointed. Registry-absent was predicted to overlap heavily with UnlistedImportUse, both being described as 'referenced but never imported', and the prediction was made because closure membership is driven by imports. Measured on one run: 63 registry-absent names, 36 UnlistedImportUse names, INTERSECTION ZERO. UnlistedImportUse names .dag types masked at RESOLVE time; registry-absent is dominated by Rust tokens that never reached the resolver. The reasoning was wrong at the MECHANISM, not merely the magnitude. ProviderUnknown's trigger therefore does NOT point at the family-closure-SVN burndown as this carrier previously stated, and the falsification is recorded here rather than the trigger being silently repointed -- a repointed trigger would inherit the credibility of a prediction that just died, so whatever that trigger eventually becomes is argued fresh." data where_refinement_deferral_reason_scaffold_note: String = "owner: v1.compiler.infer (00_core diagnostic partition). lane: where-refinement literal-wall enforcement. interim: WhereRefinementUnenforced.reason is a closed-string sum enrolled in is_where_refinement_unenforced_advisory_reason; any unlisted reason fails closed blocking. bound: only the five deferral strings emitted by 04_infer today; classifier/eval/equivalence arms must change together until coproduct lands. dissolve-on: feature:where-refinement-predicate-coproduct (WhereRefinementDeferralReason coproduct on the diagnostic carrier)." diff --git a/src/v1/05_emit_rust.dag b/src/v1/05_emit_rust.dag index c6a35bcd6f3..60de7528a64 100644 --- a/src/v1/05_emit_rust.dag +++ b/src/v1/05_emit_rust.dag @@ -3761,18 +3761,31 @@ fn reference_derived_candidate_disposition(name: String, this_module_name: Strin // ONLY CandidateExportProofFailed BECOMES A DIAGNOSTIC, AND THAT IS A MEASUREMENT RESULT RATHER THAN // A DESIGN PREFERENCE. Both arms were instrumented and run over the regen seed closure (the census // invocation is recorded on the carrier note). Export-proof-failed returned ZERO. Registry-absent -// returned 473 rows over 63 distinct names across 110 modules, and reading those names shows the -// population is not what the arm's name suggests: the top of it is Vec 88, bool 73, Option 50, i64 -// 48, empty_map 33, with BTreeSet, Fn, fn, u8, serde_json, `_` and `-` behind them. Those are RUST -// TARGET-LANGUAGE tokens, proposed by the candidate walk's emitted-source arm, which tokenizes the -// module's own emitted Rust and offers every identifier in it. No `.dag` provider can ever supply -// `bool`, so registry-absent is the correct disposition and a DIAGNOSTIC would be a false report in -// every one of those rows -- printed on every build, in every module, forever. +// returned 473 rows over 63 distinct names across 110 modules -- and that population is MIXED, which +// an earlier revision of this comment got wrong by generalising from the head of a sorted list: // -// So registry-absent stays a census column and does not become a diagnostic. Its next-rung trigger is -// NOT a burndown: it is that the candidate walk stop proposing target-language vocabulary, at which -// point the arm's population becomes the `.dag` names it was always meant to name and the diagnostic -// can be added without changing anything else here. +// (a) EXTINGUISHED Vec 88, bool 73, i64 48, BTreeSet -- no .dag declaration exists or can. The +// producer is collect_item_realized_surface_names, rust_identifier_tokens over +// render_rust_type, which emits target-language SPELLINGS by construction; +// reference_is_host_realized_builtin misses them because it is keyed on .dag +// vocabulary, so Vec and BTreeSet -- the Rust spellings of List and Set -- +// slip a filter built to remove exactly this. +// (b) OUT-OF-CLOSURE empty_map 33, Optional 25, Present 47, Absent 13 -- REAL .dag declarations +// in v2.std.collection and v2.std.optional whose providers were not selected +// into the measured closure. Movable, and precisely the closure-conditioned +// class this change exists to surface. +// +// The proportion between them is UNMEASURED. Class (b) is present because the census invocation's +// --source-root flags are NOT the regen SUBJECT: regen_input_sources is rooted at SeedV1 and +// DagCorpus and excludes src/v2 entirely, because stage0 IS the v1 seed and a seed reaching into +// src/v2 would depend on the successor it bootstraps toward. So registry-absent is ENTRY-RELATIVE, +// not a fixed target, and a reader treating the count as stable has made the denominator error. +// +// The arm stays unwired anyway, and the reason is (a) rather than the whole population: a diagnostic +// there would be permanently false on every class-(a) row, printed on every build in every module, +// and that is worse than shipping nothing because IT TRAINS READERS TO IGNORE THE CHANNEL -- an +// absent diagnostic is at least honest about its coverage. The trigger for wiring it is that class +// (a) LEAVE the arm; the residue is then class (b), which is worth reporting. // // A PREDICTION WAS REFUTED HERE AND IT IS RECORDED BECAUSE IT WAS DECISION-RELEVANT. Registry-absent // was expected to overlap heavily with UnlistedImportUse -- both said to mean "referenced but never From d4f8fd9559297399085ddfb39e885049c9063684 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 27 Aug 2026 19:08:35 +0000 Subject: [PATCH 09/21] Row-count and distinct-name point opposite ways: state both, quote neither as a proportion Refinement from smart-ram-730 and clever-boar-140. My carrier said 'roughly twelve of the 63 names were examined' -- a figure I inherited rather than verified. What is actually established: BY ROW immovable names dominate: Vec 88 + bool 73 + Option 50 + i64 48 of 473. Verified here -- none of Vec, bool, i64, BTreeSet, Fn, u8, serde_json has any .dag declaration in the tree. BY DISTINCT NAME the examined sample is 4 of 63 and ALL FOUR ARE MOVABLE. Those point opposite ways, and the structure is why the population was misread twice: high-count immovable names at the head, movable names in the tail with small counts. empty_map's 33 rows were the THIRD-LARGEST count and were still classified as target vocabulary on the first pass. Fifty-nine names remain unexamined by anyone, and the carrier now forbids quoting either figure as a proportion. Also records the variant half, which is structural rather than incidental: Present and Absent are Optional's VARIANTS, so a consumer key reading a declaration index's field alone reports no-provider-anywhere for every variant name in the corpus. The key must read declared UNION variants. That reading is clever-boar-140's, recorded as attribution rather than as something this module verified. Co-Authored-By: Claude Opus 5 (1M context) --- src/v1/00_core.dag | 2 +- src/v1/05_emit_rust.dag | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/v1/00_core.dag b/src/v1/00_core.dag index 1d855f27b2f..0daa5d20837 100644 --- a/src/v1/00_core.dag +++ b/src/v1/00_core.dag @@ -525,7 +525,7 @@ fn is_error_diagnostic(d: CompilerDiagnostic) -> Bool { } } -data reference_derived_import_refusal_severity_note: String = "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). TWO DIAGNOSTICS, ONE WIRED AND BLOCKING, ONE DECLARED AND PRODUCED BY NOTHING -- and which is which was decided by measurement, not by preference. ReferenceDerivedImportExportUnproven is emitted from CandidateExportProofFailed: the provider is present in the closure and its export could not be established, and the remedy is NOT an import -- the provider is already here -- but making the emitter able to prove an export it may already hold. IT IS BLOCKING (warm-hawk-909, compiler direction, 2026-08-27), under a flip condition strictly stronger than a count: an arm flips when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient, because an observation of zero on an arm that has never fired is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check cited as coverage, which 4b rates worse than absent. BOTH HALVES HOLD HERE: the population is zero over the regen seed closure (claim_executor --required-regen --source-root dag --source-root src/v2, whose subject is regen_input_sources grown to a joint fixpoint over import, dotted-reference and bare-reference edges), and the RED is authorable and AUTHORED at the fixture boundary in v1.tests.claim.reference_derived_disposition_census_witness_test, which builds a CandidateExportProofFailed row and asserts the diagnostic. The emission still produces its files beside a blocking diagnostic rather than returning none: production precedes adjudication, so the candidate tree survives the refusal and the refusal is what stops the line. ReferenceDerivedImportProviderUnknown is DECLARED HERE AND PRODUCED BY NOTHING. Its arm, CandidateRegistryAbsent, was instrumented and measured over the same closure and returned 473 rows over 63 names across 110 modules. THAT POPULATION IS MIXED, AND AN EARLIER REVISION OF THIS NOTE CALLED IT ALL TARGET VOCABULARY -- corrected 2026-08-27 after deep-ant-102 pushed back on the census SCOPE. Two classes are conflated inside it. (a) EXTINGUISHED -- Vec 88, bool 73, i64 48, BTreeSet: no .dag declaration exists anywhere and none can, because the PRODUCER is collect_item_realized_surface_names, which is rust_identifier_tokens over render_rust_type and so emits target-language SPELLINGS by construction (attribution corrected by clever-boar-140, who owns the coproduct: the candidate filter's emitted-source disjunct is an admission GATE over an already-proposed list and cannot itself propose anything, so narrowing it would delete genuine emitter-attested candidates and leave the real source untouched). reference_is_host_realized_builtin misses these because it is keyed on .dag vocabulary -- is_container_type reads std.types container_type_arity -- so Vec and BTreeSet, the RUST spellings of List and Set, pass a filter that exists precisely to remove host-realized names. (b) OUT-OF-CLOSURE -- empty_map 33 (declared in v2.std.collection) and Optional 25 with its variants Present 47 and Absent 13 (declared in v2.std.optional): REAL .dag names whose providers exist and were not selected in. THE PROPORTION BETWEEN (a) AND (b) IS UNMEASURED and must not be inferred from these examples; roughly twelve of the 63 names were examined. WHY (b) IS THERE AT ALL is a trap worth carrying: the census invocation passes --source-root dag --source-root src/v2, but the SUBJECT is regen_input_sources, whose roots are SeedV1 and DagCorpus and exclude src/v2 entirely -- stage0 IS the v1 seed, and a seed reaching into src/v2 would depend on the successor it bootstraps toward. The source-root FLAGS and the regen SUBJECT are not the same thing. So class (b) is precisely the closure-conditioned population this change exists to make visible, sitting inside the rows that had been written off, and registry-absent is ENTRY-RELATIVE rather than a fixed target: a reader who takes the number as stable has made the denominator error. THE 473 IS NOT A ROSTER OF UNFIXABLE ROWS. Wiring a diagnostic to it would still print a permanently-false report on every class-(a) row -- which is why the arm stays unwired even though part of the population is movable -- and the reason that is worse than shipping nothing is that IT TRAINS READERS TO IGNORE THE CHANNEL: a diagnostic nobody reads is worth less than an absent one, because the absent one is honest about its coverage. The variant stays declared because the class is real and its shape is settled; the trigger for wiring it is that class (a) leave the arm -- the candidate walk ceasing to propose target-language vocabulary -- after which the residue is class (b) and is exactly the closure-conditioned population worth reporting. A witness asserts zero diagnostics from that arm and fails the moment anyone wires it. THE TWO ARE SEPARATE DIAGNOSTICS RATHER THAN ONE WITH A CAUSE FIELD, because the question is upstream of severity: registry-absent is fixed by authoring an import, export-proof-failed by proving an export that already exists -- opposite remedies, different owners, different populations, different reachability -- and 4b files one row per class. That the two turned out to need different TREATMENT rather than merely different severity is the strongest evidence the split was right. A REFUTED PREDICTION, recorded rather than quietly repointed. Registry-absent was predicted to overlap heavily with UnlistedImportUse, both being described as 'referenced but never imported', and the prediction was made because closure membership is driven by imports. Measured on one run: 63 registry-absent names, 36 UnlistedImportUse names, INTERSECTION ZERO. UnlistedImportUse names .dag types masked at RESOLVE time; registry-absent is dominated by Rust tokens that never reached the resolver. The reasoning was wrong at the MECHANISM, not merely the magnitude. ProviderUnknown's trigger therefore does NOT point at the family-closure-SVN burndown as this carrier previously stated, and the falsification is recorded here rather than the trigger being silently repointed -- a repointed trigger would inherit the credibility of a prediction that just died, so whatever that trigger eventually becomes is argued fresh." +data reference_derived_import_refusal_severity_note: String = "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). TWO DIAGNOSTICS, ONE WIRED AND BLOCKING, ONE DECLARED AND PRODUCED BY NOTHING -- and which is which was decided by measurement, not by preference. ReferenceDerivedImportExportUnproven is emitted from CandidateExportProofFailed: the provider is present in the closure and its export could not be established, and the remedy is NOT an import -- the provider is already here -- but making the emitter able to prove an export it may already hold. IT IS BLOCKING (warm-hawk-909, compiler direction, 2026-08-27), under a flip condition strictly stronger than a count: an arm flips when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient, because an observation of zero on an arm that has never fired is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check cited as coverage, which 4b rates worse than absent. BOTH HALVES HOLD HERE: the population is zero over the regen seed closure (claim_executor --required-regen --source-root dag --source-root src/v2, whose subject is regen_input_sources grown to a joint fixpoint over import, dotted-reference and bare-reference edges), and the RED is authorable and AUTHORED at the fixture boundary in v1.tests.claim.reference_derived_disposition_census_witness_test, which builds a CandidateExportProofFailed row and asserts the diagnostic. The emission still produces its files beside a blocking diagnostic rather than returning none: production precedes adjudication, so the candidate tree survives the refusal and the refusal is what stops the line. ReferenceDerivedImportProviderUnknown is DECLARED HERE AND PRODUCED BY NOTHING. Its arm, CandidateRegistryAbsent, was instrumented and measured over the same closure and returned 473 rows over 63 names across 110 modules. THAT POPULATION IS MIXED, AND AN EARLIER REVISION OF THIS NOTE CALLED IT ALL TARGET VOCABULARY -- corrected 2026-08-27 after deep-ant-102 pushed back on the census SCOPE. Two classes are conflated inside it. (a) EXTINGUISHED -- Vec 88, bool 73, i64 48, BTreeSet: no .dag declaration exists anywhere and none can, because the PRODUCER is collect_item_realized_surface_names, which is rust_identifier_tokens over render_rust_type and so emits target-language SPELLINGS by construction (attribution corrected by clever-boar-140, who owns the coproduct: the candidate filter's emitted-source disjunct is an admission GATE over an already-proposed list and cannot itself propose anything, so narrowing it would delete genuine emitter-attested candidates and leave the real source untouched). reference_is_host_realized_builtin misses these because it is keyed on .dag vocabulary -- is_container_type reads std.types container_type_arity -- so Vec and BTreeSet, the RUST spellings of List and Set, pass a filter that exists precisely to remove host-realized names. (b) OUT-OF-CLOSURE -- empty_map 33 (v2.std.collection), Optional 25 (v2.std.optional), and its VARIANTS Present 47 and Absent 13: REAL .dag names whose providers exist and were not selected in. The variant half is not a detail: a consumer key that reads a declaration index's `declared` field alone reports no-provider-anywhere for every variant name in the corpus, because a variant is not a top-level declaration and such an index holds variants in a separate field -- so the key must read declared UNION variants, and two of these four names are the instance that proves it (found by clever-boar-140 reading v1_compiler.declaration_index; the reading is theirs and is recorded here as attribution, not as something this module verified). THE PROPORTION IS UNMEASURED IN BOTH DIRECTIONS, and the two ways of counting point OPPOSITE ways, which is the whole reason this population was misread twice. BY ROW the immovable names dominate: Vec 88 + bool 73 + Option 50 + i64 48 out of 473, each verified to have no .dag declaration anywhere. BY DISTINCT NAME the examined sample is 4 of 63 and ALL FOUR ARE MOVABLE. High-count immovable names at the head, movable names in the tail with small counts -- which is exactly the structure that hid the signal, and exactly why reading a sorted head answered the wrong question: empty_map's 33 rows were the third-largest count and were still classified as target vocabulary on the first pass. FIFTY-NINE NAMES ARE UNEXAMINED BY ANYONE. Neither the row-dominance nor the 4-of-4 may be quoted as a proportion; both are directional facts about different denominators. WHY (b) IS THERE AT ALL is a trap worth carrying: the census invocation passes --source-root dag --source-root src/v2, but the SUBJECT is regen_input_sources, whose roots are SeedV1 and DagCorpus and exclude src/v2 entirely -- stage0 IS the v1 seed, and a seed reaching into src/v2 would depend on the successor it bootstraps toward. The source-root FLAGS and the regen SUBJECT are not the same thing. So class (b) is precisely the closure-conditioned population this change exists to make visible, sitting inside the rows that had been written off, and registry-absent is ENTRY-RELATIVE rather than a fixed target: a reader who takes the number as stable has made the denominator error. THE 473 IS NOT A ROSTER OF UNFIXABLE ROWS. Wiring a diagnostic to it would still print a permanently-false report on every class-(a) row -- which is why the arm stays unwired even though part of the population is movable -- and the reason that is worse than shipping nothing is that IT TRAINS READERS TO IGNORE THE CHANNEL: a diagnostic nobody reads is worth less than an absent one, because the absent one is honest about its coverage. The variant stays declared because the class is real and its shape is settled; the trigger for wiring it is that class (a) leave the arm -- the candidate walk ceasing to propose target-language vocabulary -- after which the residue is class (b) and is exactly the closure-conditioned population worth reporting. A witness asserts zero diagnostics from that arm and fails the moment anyone wires it. THE TWO ARE SEPARATE DIAGNOSTICS RATHER THAN ONE WITH A CAUSE FIELD, because the question is upstream of severity: registry-absent is fixed by authoring an import, export-proof-failed by proving an export that already exists -- opposite remedies, different owners, different populations, different reachability -- and 4b files one row per class. That the two turned out to need different TREATMENT rather than merely different severity is the strongest evidence the split was right. A REFUTED PREDICTION, recorded rather than quietly repointed. Registry-absent was predicted to overlap heavily with UnlistedImportUse, both being described as 'referenced but never imported', and the prediction was made because closure membership is driven by imports. Measured on one run: 63 registry-absent names, 36 UnlistedImportUse names, INTERSECTION ZERO. UnlistedImportUse names .dag types masked at RESOLVE time; registry-absent is dominated by Rust tokens that never reached the resolver. The reasoning was wrong at the MECHANISM, not merely the magnitude. ProviderUnknown's trigger therefore does NOT point at the family-closure-SVN burndown as this carrier previously stated, and the falsification is recorded here rather than the trigger being silently repointed -- a repointed trigger would inherit the credibility of a prediction that just died, so whatever that trigger eventually becomes is argued fresh." data where_refinement_deferral_reason_scaffold_note: String = "owner: v1.compiler.infer (00_core diagnostic partition). lane: where-refinement literal-wall enforcement. interim: WhereRefinementUnenforced.reason is a closed-string sum enrolled in is_where_refinement_unenforced_advisory_reason; any unlisted reason fails closed blocking. bound: only the five deferral strings emitted by 04_infer today; classifier/eval/equivalence arms must change together until coproduct lands. dissolve-on: feature:where-refinement-predicate-coproduct (WhereRefinementDeferralReason coproduct on the diagnostic carrier)." diff --git a/src/v1/05_emit_rust.dag b/src/v1/05_emit_rust.dag index 60de7528a64..075978f4147 100644 --- a/src/v1/05_emit_rust.dag +++ b/src/v1/05_emit_rust.dag @@ -3770,12 +3770,16 @@ fn reference_derived_candidate_disposition(name: String, this_module_name: Strin // reference_is_host_realized_builtin misses them because it is keyed on .dag // vocabulary, so Vec and BTreeSet -- the Rust spellings of List and Set -- // slip a filter built to remove exactly this. -// (b) OUT-OF-CLOSURE empty_map 33, Optional 25, Present 47, Absent 13 -- REAL .dag declarations -// in v2.std.collection and v2.std.optional whose providers were not selected -// into the measured closure. Movable, and precisely the closure-conditioned -// class this change exists to surface. +// (b) OUT-OF-CLOSURE empty_map 33, Optional 25, and Optional's VARIANTS Present 47 and Absent 13 +// -- REAL .dag declarations in v2.std.collection and v2.std.optional whose +// providers were not selected into the measured closure. Movable, and +// precisely the closure-conditioned class this change exists to surface. // -// The proportion between them is UNMEASURED. Class (b) is present because the census invocation's +// The proportion is UNMEASURED IN BOTH DIRECTIONS, and the two ways of counting point OPPOSITE ways. +// BY ROW the immovable names dominate (88 + 73 + 50 + 48 of 473). BY DISTINCT NAME the examined +// sample is 4 of 63 and all four are movable. High counts at the immovable head, movable names in the +// tail with small counts -- the structure that hid the signal, and the reason a sorted head answered +// the wrong question here twice. Fifty-nine names are unexamined. Neither figure is a proportion. Class (b) is present because the census invocation's // --source-root flags are NOT the regen SUBJECT: regen_input_sources is rooted at SeedV1 and // DagCorpus and excludes src/v2 entirely, because stage0 IS the v1 seed and a seed reaching into // src/v2 would depend on the successor it bootstraps toward. So registry-absent is ENTRY-RELATIVE, From 5221756e7a9292ced026c4638ec7e1f58ba4b3e4 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 27 Aug 2026 19:09:10 +0000 Subject: [PATCH 10/21] Record the structural variant fact and how it composes with the fifth arm's ordering clever-boar-140 withdrew the corroboration I objected to and settled the question from the producer instead: build_item_info emits one ItemInfo per TOP-LEVEL ITEM, no arm descends into a coproduct's children, and the single item_registry insert is keyed on that name -- so a variant name is absent from the registry under EVERY closure, not merely this one. Verified against both sites before recording it. THE TWO FACTS COMPOSE, and the composition explains why Present and Absent sit under OUT-OF-CLOSURE rather than under EXTINGUISHED: this PR's fifth arm is tested BEFORE the registry lookup, so a variant whose parent is IN closure is delegated and never reaches the registry. The registry's structural inability to hold variants surfaces only when the parent is OUT of closure and type_summaries cannot recognise the name as a variant at all. That is precisely why my four names could not discriminate the variant-set reading from the closure reading, and why the structural argument was needed rather than the sample. Co-Authored-By: Claude Opus 5 (1M context) --- src/v1/00_core.dag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v1/00_core.dag b/src/v1/00_core.dag index 0daa5d20837..f8924ef4098 100644 --- a/src/v1/00_core.dag +++ b/src/v1/00_core.dag @@ -525,7 +525,7 @@ fn is_error_diagnostic(d: CompilerDiagnostic) -> Bool { } } -data reference_derived_import_refusal_severity_note: String = "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). TWO DIAGNOSTICS, ONE WIRED AND BLOCKING, ONE DECLARED AND PRODUCED BY NOTHING -- and which is which was decided by measurement, not by preference. ReferenceDerivedImportExportUnproven is emitted from CandidateExportProofFailed: the provider is present in the closure and its export could not be established, and the remedy is NOT an import -- the provider is already here -- but making the emitter able to prove an export it may already hold. IT IS BLOCKING (warm-hawk-909, compiler direction, 2026-08-27), under a flip condition strictly stronger than a count: an arm flips when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient, because an observation of zero on an arm that has never fired is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check cited as coverage, which 4b rates worse than absent. BOTH HALVES HOLD HERE: the population is zero over the regen seed closure (claim_executor --required-regen --source-root dag --source-root src/v2, whose subject is regen_input_sources grown to a joint fixpoint over import, dotted-reference and bare-reference edges), and the RED is authorable and AUTHORED at the fixture boundary in v1.tests.claim.reference_derived_disposition_census_witness_test, which builds a CandidateExportProofFailed row and asserts the diagnostic. The emission still produces its files beside a blocking diagnostic rather than returning none: production precedes adjudication, so the candidate tree survives the refusal and the refusal is what stops the line. ReferenceDerivedImportProviderUnknown is DECLARED HERE AND PRODUCED BY NOTHING. Its arm, CandidateRegistryAbsent, was instrumented and measured over the same closure and returned 473 rows over 63 names across 110 modules. THAT POPULATION IS MIXED, AND AN EARLIER REVISION OF THIS NOTE CALLED IT ALL TARGET VOCABULARY -- corrected 2026-08-27 after deep-ant-102 pushed back on the census SCOPE. Two classes are conflated inside it. (a) EXTINGUISHED -- Vec 88, bool 73, i64 48, BTreeSet: no .dag declaration exists anywhere and none can, because the PRODUCER is collect_item_realized_surface_names, which is rust_identifier_tokens over render_rust_type and so emits target-language SPELLINGS by construction (attribution corrected by clever-boar-140, who owns the coproduct: the candidate filter's emitted-source disjunct is an admission GATE over an already-proposed list and cannot itself propose anything, so narrowing it would delete genuine emitter-attested candidates and leave the real source untouched). reference_is_host_realized_builtin misses these because it is keyed on .dag vocabulary -- is_container_type reads std.types container_type_arity -- so Vec and BTreeSet, the RUST spellings of List and Set, pass a filter that exists precisely to remove host-realized names. (b) OUT-OF-CLOSURE -- empty_map 33 (v2.std.collection), Optional 25 (v2.std.optional), and its VARIANTS Present 47 and Absent 13: REAL .dag names whose providers exist and were not selected in. The variant half is not a detail: a consumer key that reads a declaration index's `declared` field alone reports no-provider-anywhere for every variant name in the corpus, because a variant is not a top-level declaration and such an index holds variants in a separate field -- so the key must read declared UNION variants, and two of these four names are the instance that proves it (found by clever-boar-140 reading v1_compiler.declaration_index; the reading is theirs and is recorded here as attribution, not as something this module verified). THE PROPORTION IS UNMEASURED IN BOTH DIRECTIONS, and the two ways of counting point OPPOSITE ways, which is the whole reason this population was misread twice. BY ROW the immovable names dominate: Vec 88 + bool 73 + Option 50 + i64 48 out of 473, each verified to have no .dag declaration anywhere. BY DISTINCT NAME the examined sample is 4 of 63 and ALL FOUR ARE MOVABLE. High-count immovable names at the head, movable names in the tail with small counts -- which is exactly the structure that hid the signal, and exactly why reading a sorted head answered the wrong question: empty_map's 33 rows were the third-largest count and were still classified as target vocabulary on the first pass. FIFTY-NINE NAMES ARE UNEXAMINED BY ANYONE. Neither the row-dominance nor the 4-of-4 may be quoted as a proportion; both are directional facts about different denominators. WHY (b) IS THERE AT ALL is a trap worth carrying: the census invocation passes --source-root dag --source-root src/v2, but the SUBJECT is regen_input_sources, whose roots are SeedV1 and DagCorpus and exclude src/v2 entirely -- stage0 IS the v1 seed, and a seed reaching into src/v2 would depend on the successor it bootstraps toward. The source-root FLAGS and the regen SUBJECT are not the same thing. So class (b) is precisely the closure-conditioned population this change exists to make visible, sitting inside the rows that had been written off, and registry-absent is ENTRY-RELATIVE rather than a fixed target: a reader who takes the number as stable has made the denominator error. THE 473 IS NOT A ROSTER OF UNFIXABLE ROWS. Wiring a diagnostic to it would still print a permanently-false report on every class-(a) row -- which is why the arm stays unwired even though part of the population is movable -- and the reason that is worse than shipping nothing is that IT TRAINS READERS TO IGNORE THE CHANNEL: a diagnostic nobody reads is worth less than an absent one, because the absent one is honest about its coverage. The variant stays declared because the class is real and its shape is settled; the trigger for wiring it is that class (a) leave the arm -- the candidate walk ceasing to propose target-language vocabulary -- after which the residue is class (b) and is exactly the closure-conditioned population worth reporting. A witness asserts zero diagnostics from that arm and fails the moment anyone wires it. THE TWO ARE SEPARATE DIAGNOSTICS RATHER THAN ONE WITH A CAUSE FIELD, because the question is upstream of severity: registry-absent is fixed by authoring an import, export-proof-failed by proving an export that already exists -- opposite remedies, different owners, different populations, different reachability -- and 4b files one row per class. That the two turned out to need different TREATMENT rather than merely different severity is the strongest evidence the split was right. A REFUTED PREDICTION, recorded rather than quietly repointed. Registry-absent was predicted to overlap heavily with UnlistedImportUse, both being described as 'referenced but never imported', and the prediction was made because closure membership is driven by imports. Measured on one run: 63 registry-absent names, 36 UnlistedImportUse names, INTERSECTION ZERO. UnlistedImportUse names .dag types masked at RESOLVE time; registry-absent is dominated by Rust tokens that never reached the resolver. The reasoning was wrong at the MECHANISM, not merely the magnitude. ProviderUnknown's trigger therefore does NOT point at the family-closure-SVN burndown as this carrier previously stated, and the falsification is recorded here rather than the trigger being silently repointed -- a repointed trigger would inherit the credibility of a prediction that just died, so whatever that trigger eventually becomes is argued fresh." +data reference_derived_import_refusal_severity_note: String = "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). TWO DIAGNOSTICS, ONE WIRED AND BLOCKING, ONE DECLARED AND PRODUCED BY NOTHING -- and which is which was decided by measurement, not by preference. ReferenceDerivedImportExportUnproven is emitted from CandidateExportProofFailed: the provider is present in the closure and its export could not be established, and the remedy is NOT an import -- the provider is already here -- but making the emitter able to prove an export it may already hold. IT IS BLOCKING (warm-hawk-909, compiler direction, 2026-08-27), under a flip condition strictly stronger than a count: an arm flips when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient, because an observation of zero on an arm that has never fired is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check cited as coverage, which 4b rates worse than absent. BOTH HALVES HOLD HERE: the population is zero over the regen seed closure (claim_executor --required-regen --source-root dag --source-root src/v2, whose subject is regen_input_sources grown to a joint fixpoint over import, dotted-reference and bare-reference edges), and the RED is authorable and AUTHORED at the fixture boundary in v1.tests.claim.reference_derived_disposition_census_witness_test, which builds a CandidateExportProofFailed row and asserts the diagnostic. The emission still produces its files beside a blocking diagnostic rather than returning none: production precedes adjudication, so the candidate tree survives the refusal and the refusal is what stops the line. ReferenceDerivedImportProviderUnknown is DECLARED HERE AND PRODUCED BY NOTHING. Its arm, CandidateRegistryAbsent, was instrumented and measured over the same closure and returned 473 rows over 63 names across 110 modules. THAT POPULATION IS MIXED, AND AN EARLIER REVISION OF THIS NOTE CALLED IT ALL TARGET VOCABULARY -- corrected 2026-08-27 after deep-ant-102 pushed back on the census SCOPE. Two classes are conflated inside it. (a) EXTINGUISHED -- Vec 88, bool 73, i64 48, BTreeSet: no .dag declaration exists anywhere and none can, because the PRODUCER is collect_item_realized_surface_names, which is rust_identifier_tokens over render_rust_type and so emits target-language SPELLINGS by construction (attribution corrected by clever-boar-140, who owns the coproduct: the candidate filter's emitted-source disjunct is an admission GATE over an already-proposed list and cannot itself propose anything, so narrowing it would delete genuine emitter-attested candidates and leave the real source untouched). reference_is_host_realized_builtin misses these because it is keyed on .dag vocabulary -- is_container_type reads std.types container_type_arity -- so Vec and BTreeSet, the RUST spellings of List and Set, pass a filter that exists precisely to remove host-realized names. (b) OUT-OF-CLOSURE -- empty_map 33 (v2.std.collection), Optional 25 (v2.std.optional), and its VARIANTS Present 47 and Absent 13: REAL .dag names whose providers exist and were not selected in. The variant half is not a detail: a consumer key that reads a declaration index's `declared` field alone reports no-provider-anywhere for every variant name in the corpus, because a variant is not a top-level declaration and such an index holds variants in a separate field -- so the key must read declared UNION variants, and two of these four names are the instance that proves it (clever-boar-140, who then settled it STRUCTURALLY rather than from this sample: build_item_info in v1.compiler.infer produces one ItemInfo per TOP-LEVEL ITEM and no arm descends into a coproduct's children, and the single item_registry insert is keyed on that name, so a variant name is absent from the registry under EVERY closure -- verified here against both sites). THAT FACT AND THIS ARM'S ORDERING COMPOSE, and the composition is why these two names appear under (b) rather than under (a): CandidateVariantDelegatedToParent is tested BEFORE the registry lookup, so a variant whose parent is IN closure is delegated and never reaches the registry at all, and the registry's structural inability to hold variants surfaces only when the parent is OUT of closure and type_summaries therefore cannot recognise the name as a variant. So Present and Absent are registry-absent HERE because Optional was out of closure, and would be delegated with it in -- which is exactly why this sample cannot discriminate the two readings, and why the structural argument above was needed instead. THE PROPORTION IS UNMEASURED IN BOTH DIRECTIONS, and the two ways of counting point OPPOSITE ways, which is the whole reason this population was misread twice. BY ROW the immovable names dominate: Vec 88 + bool 73 + Option 50 + i64 48 out of 473, each verified to have no .dag declaration anywhere. BY DISTINCT NAME the examined sample is 4 of 63 and ALL FOUR ARE MOVABLE. High-count immovable names at the head, movable names in the tail with small counts -- which is exactly the structure that hid the signal, and exactly why reading a sorted head answered the wrong question: empty_map's 33 rows were the third-largest count and were still classified as target vocabulary on the first pass. FIFTY-NINE NAMES ARE UNEXAMINED BY ANYONE. Neither the row-dominance nor the 4-of-4 may be quoted as a proportion; both are directional facts about different denominators. WHY (b) IS THERE AT ALL is a trap worth carrying: the census invocation passes --source-root dag --source-root src/v2, but the SUBJECT is regen_input_sources, whose roots are SeedV1 and DagCorpus and exclude src/v2 entirely -- stage0 IS the v1 seed, and a seed reaching into src/v2 would depend on the successor it bootstraps toward. The source-root FLAGS and the regen SUBJECT are not the same thing. So class (b) is precisely the closure-conditioned population this change exists to make visible, sitting inside the rows that had been written off, and registry-absent is ENTRY-RELATIVE rather than a fixed target: a reader who takes the number as stable has made the denominator error. THE 473 IS NOT A ROSTER OF UNFIXABLE ROWS. Wiring a diagnostic to it would still print a permanently-false report on every class-(a) row -- which is why the arm stays unwired even though part of the population is movable -- and the reason that is worse than shipping nothing is that IT TRAINS READERS TO IGNORE THE CHANNEL: a diagnostic nobody reads is worth less than an absent one, because the absent one is honest about its coverage. The variant stays declared because the class is real and its shape is settled; the trigger for wiring it is that class (a) leave the arm -- the candidate walk ceasing to propose target-language vocabulary -- after which the residue is class (b) and is exactly the closure-conditioned population worth reporting. A witness asserts zero diagnostics from that arm and fails the moment anyone wires it. THE TWO ARE SEPARATE DIAGNOSTICS RATHER THAN ONE WITH A CAUSE FIELD, because the question is upstream of severity: registry-absent is fixed by authoring an import, export-proof-failed by proving an export that already exists -- opposite remedies, different owners, different populations, different reachability -- and 4b files one row per class. That the two turned out to need different TREATMENT rather than merely different severity is the strongest evidence the split was right. A REFUTED PREDICTION, recorded rather than quietly repointed. Registry-absent was predicted to overlap heavily with UnlistedImportUse, both being described as 'referenced but never imported', and the prediction was made because closure membership is driven by imports. Measured on one run: 63 registry-absent names, 36 UnlistedImportUse names, INTERSECTION ZERO. UnlistedImportUse names .dag types masked at RESOLVE time; registry-absent is dominated by Rust tokens that never reached the resolver. The reasoning was wrong at the MECHANISM, not merely the magnitude. ProviderUnknown's trigger therefore does NOT point at the family-closure-SVN burndown as this carrier previously stated, and the falsification is recorded here rather than the trigger being silently repointed -- a repointed trigger would inherit the credibility of a prediction that just died, so whatever that trigger eventually becomes is argued fresh." data where_refinement_deferral_reason_scaffold_note: String = "owner: v1.compiler.infer (00_core diagnostic partition). lane: where-refinement literal-wall enforcement. interim: WhereRefinementUnenforced.reason is a closed-string sum enrolled in is_where_refinement_unenforced_advisory_reason; any unlisted reason fails closed blocking. bound: only the five deferral strings emitted by 04_infer today; classifier/eval/equivalence arms must change together until coproduct lands. dissolve-on: feature:where-refinement-predicate-coproduct (WhereRefinementDeferralReason coproduct on the diagnostic carrier)." From af77d3a5dcf17a26d726bcf56b010c0d47f20038 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 27 Aug 2026 19:33:03 +0000 Subject: [PATCH 11/21] Regenerate the three drifted mirrors against the final head Generation 1 reaches first_generation_equal=true; export_unproven=0 and provider_unknown=0 (unwired) on the regen seed closure. Co-Authored-By: Claude Opus 5 (1M context) --- src/v1/stage0/src/v1_compiler_emit_rust.rs | 23 ++-- src/v1/stage0/src/v1_std_core.rs | 5 +- ...derived_disposition_census_witness_test.rs | 111 ++++++++---------- 3 files changed, 65 insertions(+), 74 deletions(-) diff --git a/src/v1/stage0/src/v1_compiler_emit_rust.rs b/src/v1/stage0/src/v1_compiler_emit_rust.rs index 135334e8e95..2a09774910f 100644 --- a/src/v1/stage0/src/v1_compiler_emit_rust.rs +++ b/src/v1/stage0/src/v1_compiler_emit_rust.rs @@ -7829,16 +7829,6 @@ pub fn reference_derived_row_diagnostics( for r in rows.iter().cloned() { __result.extend( (*match (*r.disposition.clone()).clone() { - ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent => { - Rc::new(vec![make_error_node( - Rc::new(CompilerDiagnostic::ReferenceDerivedImportProviderUnknown { - name: r.name.clone(), - referencing_module: r.module_name.clone(), - span: module_span.clone(), - }), - r.module_name.clone(), - )]) - } ReferenceDerivedCandidateDisposition::CandidateExportProofFailed { provider_module: p, .. @@ -7851,7 +7841,18 @@ pub fn reference_derived_row_diagnostics( }), r.module_name.clone(), )]), - _ => Rc::new(vec![]), + ReferenceDerivedCandidateDisposition::CandidateSurvived { + provider_module: _, + .. + } => Rc::new(vec![]), + ReferenceDerivedCandidateDisposition::CandidateOwnModule => Rc::new(vec![]), + ReferenceDerivedCandidateDisposition::CandidateVariantDelegatedToParent { + parent_enum: _, + .. + } => Rc::new(vec![]), + ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent => { + Rc::new(vec![]) + } }) .iter() .cloned(), diff --git a/src/v1/stage0/src/v1_std_core.rs b/src/v1/stage0/src/v1_std_core.rs index f0fbcfbaa10..8e5f970200b 100644 --- a/src/v1/stage0/src/v1_std_core.rs +++ b/src/v1/stage0/src/v1_std_core.rs @@ -878,7 +878,6 @@ pub fn is_error_diagnostic(d: Rc) -> bool { match (*d.clone()).clone() { CompilerDiagnostic::UnlistedImportUse { .. } => false, CompilerDiagnostic::ReferenceDerivedImportProviderUnknown { .. } => false, - CompilerDiagnostic::ReferenceDerivedImportExportUnproven { .. } => false, CompilerDiagnostic::UnlistedVariantValueUse { .. } => false, CompilerDiagnostic::MethodExistenceFrontierAdmitted { .. } => false, CompilerDiagnostic::ReceiverTypeUnestablished { .. } => false, @@ -892,7 +891,7 @@ pub fn is_error_diagnostic(d: Rc) -> bool { pub fn reference_derived_import_refusal_severity_note() -> String { thread_local! { static CACHED: String = { - "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). These two diagnostics are the SURFACING half of the disposition #9439 landed: that change factored the emitter's per-candidate import decision into a typed disposition and COUNTED it, and its own note records that the census is not surfaced during an ordinary build. A census nothing surfaces is a count, not a refusal -- the failure arms still vanished from the emission, which is the empty-observation narrow DESIGN names (the emitter answering 'this name is not part of the interface' where the truth is 'I could not prove that it was'), and DESIGN rates a narrow strictly worse than the widen section 5 forbids because a widen is merely expensive and a narrow is silently uncovered. TWO DIAGNOSTICS, NOT ONE CARRYING A CAUSE (ruling: warm-hawk-909, compiler direction, relayed through smart-ram-730, 2026-08-27). The question is upstream of severity: are these one error class? They are not. ReferenceDerivedImportProviderUnknown is CandidateRegistryAbsent -- the closure carries no provider for the name -- and its remedy is to AUTHOR AN IMPORT, since closure membership is driven by imports and a name the module never imported is one the closure was never asked to supply. ReferenceDerivedImportExportUnproven is CandidateExportProofFailed -- the provider is present and its export could not be established -- and its remedy is the opposite: not an import at all, but making the emitter able to prove an export the provider may already hold. Different mechanism, different remedy, different owner, different population, potentially different reachability; one symbol over two opposite remedies would be the state-space conflation DESIGN names, and DESIGN 4b files one row per class, so two classes carry two diagnostics and two independent is_error_diagnostic booleans. BOTH ARE ADVISORY TODAY and blocking nowhere. FLIP CONDITION, and it is strictly stronger than a count: an arm flips to blocking when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient -- an observation of zero on an arm that has never produced anything is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check that is then cited as coverage, which DESIGN 4b rates worse than absent. Reachability is judged against what a FIXTURE may author, never against what the accepted corpus contains. ProviderUnknown's reachability is established: std.algebra under the src/v2/std/node.dag closure references Optional and empty_map it never imports (deep-ant-102, measured). ExportUnproven's is established by nothing yet, so if its population returns zero the MISSING FIXTURE is that class's next-rung trigger rather than a clearance to block. ProviderUnknown's roster is NOT new work: it is UnlistedImportUse clause (b) seen from the emitter side, so its trigger points at the EXISTING family-closure-SVN burndown recorded in v1.compiler.resolve resolve_node_bounded_masked_boundary and must not become a second burndown with its own schedule. Whether either flips is warm-hawk-909's call, not this module's.".to_string() + "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). TWO DIAGNOSTICS, ONE WIRED AND BLOCKING, ONE DECLARED AND PRODUCED BY NOTHING -- and which is which was decided by measurement, not by preference. ReferenceDerivedImportExportUnproven is emitted from CandidateExportProofFailed: the provider is present in the closure and its export could not be established, and the remedy is NOT an import -- the provider is already here -- but making the emitter able to prove an export it may already hold. IT IS BLOCKING (warm-hawk-909, compiler direction, 2026-08-27), under a flip condition strictly stronger than a count: an arm flips when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient, because an observation of zero on an arm that has never fired is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check cited as coverage, which 4b rates worse than absent. BOTH HALVES HOLD HERE: the population is zero over the regen seed closure (claim_executor --required-regen --source-root dag --source-root src/v2, whose subject is regen_input_sources grown to a joint fixpoint over import, dotted-reference and bare-reference edges), and the RED is authorable and AUTHORED at the fixture boundary in v1.tests.claim.reference_derived_disposition_census_witness_test, which builds a CandidateExportProofFailed row and asserts the diagnostic. The emission still produces its files beside a blocking diagnostic rather than returning none: production precedes adjudication, so the candidate tree survives the refusal and the refusal is what stops the line. ReferenceDerivedImportProviderUnknown is DECLARED HERE AND PRODUCED BY NOTHING. Its arm, CandidateRegistryAbsent, was instrumented and measured over the same closure and returned 473 rows over 63 names across 110 modules. THAT POPULATION IS MIXED, AND AN EARLIER REVISION OF THIS NOTE CALLED IT ALL TARGET VOCABULARY -- corrected 2026-08-27 after deep-ant-102 pushed back on the census SCOPE. Two classes are conflated inside it. (a) EXTINGUISHED -- Vec 88, bool 73, i64 48, BTreeSet: no .dag declaration exists anywhere and none can, because the PRODUCER is collect_item_realized_surface_names, which is rust_identifier_tokens over render_rust_type and so emits target-language SPELLINGS by construction (attribution corrected by clever-boar-140, who owns the coproduct: the candidate filter's emitted-source disjunct is an admission GATE over an already-proposed list and cannot itself propose anything, so narrowing it would delete genuine emitter-attested candidates and leave the real source untouched). reference_is_host_realized_builtin misses these because it is keyed on .dag vocabulary -- is_container_type reads std.types container_type_arity -- so Vec and BTreeSet, the RUST spellings of List and Set, pass a filter that exists precisely to remove host-realized names. (b) OUT-OF-CLOSURE -- empty_map 33 (v2.std.collection), Optional 25 (v2.std.optional), and its VARIANTS Present 47 and Absent 13: REAL .dag names whose providers exist and were not selected in. The variant half is not a detail: a consumer key that reads a declaration index's `declared` field alone reports no-provider-anywhere for every variant name in the corpus, because a variant is not a top-level declaration and such an index holds variants in a separate field -- so the key must read declared UNION variants, and two of these four names are the instance that proves it (clever-boar-140, who then settled it STRUCTURALLY rather than from this sample: build_item_info in v1.compiler.infer produces one ItemInfo per TOP-LEVEL ITEM and no arm descends into a coproduct's children, and the single item_registry insert is keyed on that name, so a variant name is absent from the registry under EVERY closure -- verified here against both sites). THAT FACT AND THIS ARM'S ORDERING COMPOSE, and the composition is why these two names appear under (b) rather than under (a): CandidateVariantDelegatedToParent is tested BEFORE the registry lookup, so a variant whose parent is IN closure is delegated and never reaches the registry at all, and the registry's structural inability to hold variants surfaces only when the parent is OUT of closure and type_summaries therefore cannot recognise the name as a variant. So Present and Absent are registry-absent HERE because Optional was out of closure, and would be delegated with it in -- which is exactly why this sample cannot discriminate the two readings, and why the structural argument above was needed instead. THE PROPORTION IS UNMEASURED IN BOTH DIRECTIONS, and the two ways of counting point OPPOSITE ways, which is the whole reason this population was misread twice. BY ROW the immovable names dominate: Vec 88 + bool 73 + Option 50 + i64 48 out of 473, each verified to have no .dag declaration anywhere. BY DISTINCT NAME the examined sample is 4 of 63 and ALL FOUR ARE MOVABLE. High-count immovable names at the head, movable names in the tail with small counts -- which is exactly the structure that hid the signal, and exactly why reading a sorted head answered the wrong question: empty_map's 33 rows were the third-largest count and were still classified as target vocabulary on the first pass. FIFTY-NINE NAMES ARE UNEXAMINED BY ANYONE. Neither the row-dominance nor the 4-of-4 may be quoted as a proportion; both are directional facts about different denominators. WHY (b) IS THERE AT ALL is a trap worth carrying: the census invocation passes --source-root dag --source-root src/v2, but the SUBJECT is regen_input_sources, whose roots are SeedV1 and DagCorpus and exclude src/v2 entirely -- stage0 IS the v1 seed, and a seed reaching into src/v2 would depend on the successor it bootstraps toward. The source-root FLAGS and the regen SUBJECT are not the same thing. So class (b) is precisely the closure-conditioned population this change exists to make visible, sitting inside the rows that had been written off, and registry-absent is ENTRY-RELATIVE rather than a fixed target: a reader who takes the number as stable has made the denominator error. THE 473 IS NOT A ROSTER OF UNFIXABLE ROWS. Wiring a diagnostic to it would still print a permanently-false report on every class-(a) row -- which is why the arm stays unwired even though part of the population is movable -- and the reason that is worse than shipping nothing is that IT TRAINS READERS TO IGNORE THE CHANNEL: a diagnostic nobody reads is worth less than an absent one, because the absent one is honest about its coverage. The variant stays declared because the class is real and its shape is settled; the trigger for wiring it is that class (a) leave the arm -- the candidate walk ceasing to propose target-language vocabulary -- after which the residue is class (b) and is exactly the closure-conditioned population worth reporting. A witness asserts zero diagnostics from that arm and fails the moment anyone wires it. THE TWO ARE SEPARATE DIAGNOSTICS RATHER THAN ONE WITH A CAUSE FIELD, because the question is upstream of severity: registry-absent is fixed by authoring an import, export-proof-failed by proving an export that already exists -- opposite remedies, different owners, different populations, different reachability -- and 4b files one row per class. That the two turned out to need different TREATMENT rather than merely different severity is the strongest evidence the split was right. A REFUTED PREDICTION, recorded rather than quietly repointed. Registry-absent was predicted to overlap heavily with UnlistedImportUse, both being described as 'referenced but never imported', and the prediction was made because closure membership is driven by imports. Measured on one run: 63 registry-absent names, 36 UnlistedImportUse names, INTERSECTION ZERO. UnlistedImportUse names .dag types masked at RESOLVE time; registry-absent is dominated by Rust tokens that never reached the resolver. The reasoning was wrong at the MECHANISM, not merely the magnitude. ProviderUnknown's trigger therefore does NOT point at the family-closure-SVN burndown as this carrier previously stated, and the falsification is recorded here rather than the trigger being silently repointed -- a repointed trigger would inherit the credibility of a prediction that just died, so whatever that trigger eventually becomes is argued fresh.".to_string() }; } CACHED.with(|c: &String| c.clone()) @@ -920,7 +919,6 @@ pub fn is_interpreter_blocking_diagnostic(d: Rc) -> bool { CompilerDiagnostic::ServiceConfigReferenceJudgmentDeferred { .. } => false, CompilerDiagnostic::DeclaredTypeInhabitanceUndecided { .. } => false, CompilerDiagnostic::ReferenceDerivedImportProviderUnknown { .. } => false, - CompilerDiagnostic::ReferenceDerivedImportExportUnproven { .. } => false, _ => true, } } @@ -934,7 +932,6 @@ pub fn is_discovery_corpus_advisory_typecheck_diagnostic(d: Rc true, CompilerDiagnostic::DeclaredTypeInhabitanceUndecided { .. } => true, CompilerDiagnostic::ReferenceDerivedImportProviderUnknown { .. } => true, - CompilerDiagnostic::ReferenceDerivedImportExportUnproven { .. } => true, CompilerDiagnostic::WhereRefinementUnenforced { reason: r, .. } => { is_where_refinement_unenforced_advisory_reason(r.clone()) } diff --git a/src/v1/stage0/src/v1_tests_claim_reference_derived_disposition_census_witness_test.rs b/src/v1/stage0/src/v1_tests_claim_reference_derived_disposition_census_witness_test.rs index 7a8c08f3913..1e4e21f4dad 100644 --- a/src/v1/stage0/src/v1_tests_claim_reference_derived_disposition_census_witness_test.rs +++ b/src/v1/stage0/src/v1_tests_claim_reference_derived_disposition_census_witness_test.rs @@ -244,7 +244,7 @@ pub fn non_variant_name_still_answers_registry_absent() -> bool { )) == "registry-absent".to_string()) } -pub fn only_the_two_failing_arms_produce_diagnostics() -> bool { +pub fn only_export_proof_failed_produces_a_diagnostic() -> bool { { let rows = Rc::new(vec![ Rc::new(ReferenceDerivedCandidateRow { @@ -283,35 +283,28 @@ pub fn only_the_two_failing_arms_produce_diagnostics() -> bool { ), }), ]); - ((reference_derived_row_diagnostics(rows.clone(), no_span()).len() as i64) == 2) + ((reference_derived_row_diagnostics(rows.clone(), no_span()).len() as i64) == 1) } } -pub fn both_refusal_diagnostics_are_advisory_today() -> bool { +pub fn the_export_unproven_diagnostic_is_blocking() -> bool { { - let rows = Rc::new(vec![ - Rc::new(ReferenceDerivedCandidateRow { - module_name: "m".to_string(), - name: "c".to_string(), - disposition: Rc::new(ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent), - }), - Rc::new(ReferenceDerivedCandidateRow { - module_name: "m".to_string(), - name: "d".to_string(), - disposition: Rc::new( - ReferenceDerivedCandidateDisposition::CandidateExportProofFailed { - provider_module: "p".to_string(), - }, - ), - }), - ]); + let rows = Rc::new(vec![Rc::new(ReferenceDerivedCandidateRow { + module_name: "m".to_string(), + name: "d".to_string(), + disposition: Rc::new( + ReferenceDerivedCandidateDisposition::CandidateExportProofFailed { + provider_module: "p".to_string(), + }, + ), + })]); { let mut __all = true; for e in reference_derived_row_diagnostics(rows.clone(), no_span()) .iter() .cloned() { - if !(!is_error_diagnostic(e.diagnostic.clone())) { + if !(is_error_diagnostic(e.diagnostic.clone())) { __all = false; break; } @@ -321,52 +314,52 @@ pub fn both_refusal_diagnostics_are_advisory_today() -> bool { } } -pub fn the_two_refusals_carry_different_remedies() -> bool { +pub fn registry_absent_not_surfaced_note() -> String { + thread_local! { + static CACHED: String = { + "The RED that keeps registry-absent OUT of the diagnostic stream. Measured over the regen seed closure, that arm returns 473 rows over 63 names dominated by Rust target-language tokens -- Vec, bool, Option, i64, empty_map -- which the candidate walk proposes by tokenizing the module's own emitted Rust and for which no .dag provider can ever exist. Wiring it would print a false report on every build. This test fails the moment someone adds that arm to reference_derived_row_diagnostics, which is the intended wall until the candidate walk stops proposing target-language vocabulary.".to_string() + }; + } + CACHED.with(|c: &String| c.clone()) +} + +pub fn registry_absent_produces_no_diagnostic() -> bool { { - let rows = Rc::new(vec![ - Rc::new(ReferenceDerivedCandidateRow { - module_name: "m".to_string(), - name: "c".to_string(), - disposition: Rc::new(ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent), - }), - Rc::new(ReferenceDerivedCandidateRow { - module_name: "m".to_string(), - name: "d".to_string(), - disposition: Rc::new( - ReferenceDerivedCandidateDisposition::CandidateExportProofFailed { - provider_module: "p".to_string(), - }, - ), - }), - ]); - let msgs = Rc::new({ - let mut __result = Vec::new(); + let rows = Rc::new(vec![Rc::new(ReferenceDerivedCandidateRow { + module_name: "m".to_string(), + name: "bool".to_string(), + disposition: Rc::new(ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent), + })]); + ((reference_derived_row_diagnostics(rows.clone(), no_span()).len() as i64) == 0) + } +} + +pub fn the_export_unproven_message_names_its_own_remedy() -> bool { + { + let rows = Rc::new(vec![Rc::new(ReferenceDerivedCandidateRow { + module_name: "m".to_string(), + name: "d".to_string(), + disposition: Rc::new( + ReferenceDerivedCandidateDisposition::CandidateExportProofFailed { + provider_module: "p".to_string(), + }, + ), + })]); + { + let mut __all = true; for e in reference_derived_row_diagnostics(rows.clone(), no_span()) .iter() .cloned() { - __result.push(diagnostic_to_message(e.diagnostic.clone())); - } - __result - }); - ({ - let mut __found = false; - for m in msgs.iter().cloned() { - if v1_rt::string_contains(&m, "AUTHOR AN IMPORT".to_string()) { - __found = true; - break; - } - } - __found - } && { - let mut __found = false; - for m in msgs.iter().cloned() { - if v1_rt::string_contains(&m, "NOT an import".to_string()) { - __found = true; + if !(v1_rt::string_contains( + &diagnostic_to_message(e.diagnostic.clone()), + "NOT an import".to_string(), + )) { + __all = false; break; } } - __found - }) + __all + } } } From 157cef3e751dc32e4474981ae6ea831f5eb6a134 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 27 Aug 2026 19:36:40 +0000 Subject: [PATCH 12/21] A variant whose parent is unresolvable gets its own arm, not registry-absent's name Found in review by clever-boar-140, who owns the coproduct: the Absent arm of the parent lookup handed back CandidateRegistryAbsent. Reaching it means THIS IS KNOWN TO BE A VARIANT and its parent is not established -- while registry-absent says the bare-name REGISTRY holds no entry, a statement about a different map, reached by a different route, with a different remedy. A reader auditing that row would go looking at the registry, where the missing fact does not live. Two states distinguishable at the point of collapse, collapsed anyway -- the same shape my wildcard removal one function down exists to prevent, left standing one function up. CHECKING ITS REACHABILITY SURFACED THE SHARPER HALF. derive_variant_to_enum inserts the EMPTY STRING as the parent when one variant name appears in two enums. So the lookup answers Present with a parent naming nothing, and the arm I wrote would have produced CandidateVariantDelegatedToParent { parent_enum: "" } -- a delegation to no one, wearing the very payload that was supposed to make the arm honest. That is the top-as-ignorance shape inside the fix for a conflation. That case is REACHABLE; variant-name collision across coproducts is real enough that the compiler carries a VariantCollision diagnostic for it. The Absent case is not, while both this predicate and the parent map derive from the same EnumRepr summaries -- and it routes to the new arm anyway, because a quiet guard should say what it means rather than borrow another arm's name. CandidateVariantParentUnresolved, with a census column, an enumerated no-diagnostic arm, and two witnesses: a fixture authoring the collision directly (RED against the arm-less form, which delegated to the empty string), and one asserting the disposition never reports as registry-absent. Co-Authored-By: Claude Opus 5 (1M context) --- src/v1/05_emit_rust.dag | 29 +++++++++++++- ...erived_disposition_census_witness_test.dag | 40 ++++++++++++++++++- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/v1/05_emit_rust.dag b/src/v1/05_emit_rust.dag index 075978f4147..89114497c5e 100644 --- a/src/v1/05_emit_rust.dag +++ b/src/v1/05_emit_rust.dag @@ -3697,6 +3697,24 @@ fn collect_item_realized_surface_names(item: Node, shared_types: Set, so // candidate row -- filtered as CandidateOwnModule when local, judged by the registry and the export // wall when not -- so the two cases separate where they are decidable instead of being asserted here. // +// THE PARENT MAY BE UNRESOLVABLE, AND THAT IS ITS OWN ARM RATHER THAN A COLLAPSE INTO +// CandidateRegistryAbsent. Reaching the unresolvable case means: THIS IS KNOWN TO BE A VARIANT and +// its parent is not established. Registry-absent says something else entirely -- that the bare-name +// REGISTRY holds no entry -- which is a statement about a different map, reached by a different +// route, with a different remedy, and a reader auditing it would go looking at the registry where +// the missing fact does not live. Two states distinguishable at the point of collapse must not be +// collapsed (found in review by clever-boar-140, who owns the coproduct). +// +// Checking its reachability surfaced the sharper half. derive_variant_to_enum inserts the EMPTY +// STRING as the parent when one variant name appears in two enums -- an ambiguity sentinel -- so the +// lookup answers Present with a parent that names nothing, and the naive arm would have produced +// CandidateVariantDelegatedToParent { parent_enum: "" }: a delegation to no one, which is the +// top-as-ignorance shape wearing the payload that was supposed to make this arm honest. That case is +// REACHABLE (variant-name collision across coproducts is real enough that the compiler carries a +// VariantCollision diagnostic for it), unlike the Absent case, which cannot occur while both this +// predicate and the parent map derive from the same EnumRepr summaries. Both route here, because a +// quiet guard should still say what it means rather than borrow another arm's name. +// // WHAT IS NOT ESTABLISHED, stated rather than assumed: this delegation is sound only if every parent // named by a routed row is ITSELF a candidate. collect_value_ref_names harvests a record literal's // parent_enum and expand_variant_payload_struct_imports expands payload structs, but no invariant @@ -3707,6 +3725,7 @@ type ReferenceDerivedCandidateDisposition = CandidateSurvived { provider_module: String } | CandidateOwnModule | CandidateVariantDelegatedToParent { parent_enum: String } + | CandidateVariantParentUnresolved | CandidateRegistryAbsent | CandidateExportProofFailed { provider_module: String } @@ -3728,8 +3747,10 @@ type ReferenceDerivedCandidateRow { fn reference_derived_candidate_disposition(name: String, this_module_name: String, registry: Map, export_sets: Map>, typed_modules: List, source_indices: Map, module_index: ModuleIndex, type_summaries: Map, variant_to_enum: Map) -> ReferenceDerivedCandidateDisposition { if is_known_variant(type_summaries: type_summaries, name: name) { match map_get(variant_to_enum, name) { - Present { value: parent } => CandidateVariantDelegatedToParent { parent_enum: parent } - Absent => CandidateRegistryAbsent + Present { value: parent } => + if parent == "" { CandidateVariantParentUnresolved } + else { CandidateVariantDelegatedToParent { parent_enum: parent } } + Absent => CandidateVariantParentUnresolved } } else { match map_get(registry, name) { @@ -3824,6 +3845,7 @@ fn reference_derived_row_diagnostics(rows: List, m CandidateSurvived { provider_module: _ } => [] CandidateOwnModule => [] CandidateVariantDelegatedToParent { parent_enum: _ } => [] + CandidateVariantParentUnresolved => [] CandidateRegistryAbsent => [] } ) @@ -3844,6 +3866,7 @@ fn reference_derived_disposition_name(disposition: ReferenceDerivedCandidateDisp CandidateSurvived { provider_module: _ } => "survived" CandidateOwnModule => "own-module" CandidateVariantDelegatedToParent { parent_enum: _ } => "variant-delegated-to-parent" + CandidateVariantParentUnresolved => "variant-parent-unresolved" CandidateRegistryAbsent => "registry-absent" CandidateExportProofFailed { provider_module: _ } => "export-proof-failed" } @@ -3884,6 +3907,7 @@ type ReferenceDerivedCensus { survived: Int own_module: Int variant_delegated_to_parent: Int + variant_parent_unresolved: Int registry_absent: Int export_proof_failed: Int } @@ -3894,6 +3918,7 @@ fn reference_derived_census(rows: List) -> Referen survived: rows |> filter(r => reference_derived_disposition_name(disposition: r.disposition) == "survived") |> count, own_module: rows |> filter(r => reference_derived_disposition_name(disposition: r.disposition) == "own-module") |> count, variant_delegated_to_parent: rows |> filter(r => reference_derived_disposition_name(disposition: r.disposition) == "variant-delegated-to-parent") |> count, + variant_parent_unresolved: rows |> filter(r => reference_derived_disposition_name(disposition: r.disposition) == "variant-parent-unresolved") |> count, registry_absent: rows |> filter(r => reference_derived_disposition_name(disposition: r.disposition) == "registry-absent") |> count, export_proof_failed: rows |> filter(r => reference_derived_disposition_name(disposition: r.disposition) == "export-proof-failed") |> count } diff --git a/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag b/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag index 09932adc7bb..3fb01efad69 100644 --- a/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag +++ b/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag @@ -7,6 +7,7 @@ import v1.compiler.emit_rust { CandidateRegistryAbsent, CandidateExportProofFailed, CandidateVariantDelegatedToParent, + CandidateVariantParentUnresolved, ReferenceDerivedCandidateRow, reference_derived_row_diagnostics, ReferenceDerivedCensus, @@ -95,13 +96,15 @@ test fn census_counts_each_arm_separately() -> Bool { ReferenceDerivedCandidateRow { module_name: "m", name: "b", disposition: CandidateOwnModule }, ReferenceDerivedCandidateRow { module_name: "m", name: "c", disposition: CandidateRegistryAbsent }, ReferenceDerivedCandidateRow { module_name: "m", name: "d", disposition: CandidateExportProofFailed { provider_module: "p" } }, - ReferenceDerivedCandidateRow { module_name: "m", name: "e", disposition: CandidateVariantDelegatedToParent { parent_enum: "E" } } + ReferenceDerivedCandidateRow { module_name: "m", name: "e", disposition: CandidateVariantDelegatedToParent { parent_enum: "E" } }, + ReferenceDerivedCandidateRow { module_name: "m", name: "f", disposition: CandidateVariantParentUnresolved } ] let census = reference_derived_census(rows: rows) - census.candidates == 5 + census.candidates == 6 && census.survived == 1 && census.own_module == 1 && census.variant_delegated_to_parent == 1 + && census.variant_parent_unresolved == 1 && census.registry_absent == 1 && census.export_proof_failed == 1 } @@ -135,6 +138,39 @@ test fn known_variant_is_delegated_to_its_parent_not_registry_absent() -> Bool { ) == CandidateVariantDelegatedToParent { parent_enum: "E" } } +data ambiguous_parent_red_note: String = "THE REACHABLE HALF of the unresolved-parent arm, and the reason it is not merely a tidier name. derive_variant_to_enum inserts the EMPTY STRING as the parent when one variant name appears in two enums, so the lookup answers Present with a parent naming nothing. Without this arm that produced CandidateVariantDelegatedToParent { parent_enum: \"\" } -- a delegation to no one, wearing the payload that was supposed to make delegation honest. This fixture authors the collision directly and is RED against the arm-less form." + +fn fixture_colliding_variant_type_summaries() -> Map { + map_insert(fixture_variant_type_summaries(), "F", TypeSummary { + name: "F", + repr: EnumRepr { unit_only: true }, + field_summaries: empty_map(), + field_type_map: empty_map(), + field_import_surface_names: [], + variant_name_set: map_insert(empty_map(), "V", true), + generic_param_names: [], + has_fn_fields: false + }) +} + +test fn a_variant_whose_parent_is_ambiguous_is_not_delegated_to_nothing() -> Bool { + reference_derived_candidate_disposition( + name: "V", + this_module_name: "fixture.consumer", + registry: empty_map(), + export_sets: empty_map(), + typed_modules: [], + source_indices: empty_map(), + module_index: build_module_index(modules: []), + type_summaries: fixture_colliding_variant_type_summaries(), + variant_to_enum: map_insert(empty_map(), "V", "") + ) == CandidateVariantParentUnresolved +} + +test fn an_unresolved_parent_is_not_reported_as_registry_absent() -> Bool { + reference_derived_disposition_name(disposition: CandidateVariantParentUnresolved) != "registry-absent" +} + test fn non_variant_name_still_answers_registry_absent() -> Bool { reference_derived_disposition_name(disposition: reference_derived_candidate_disposition( name: "NotAVariant", From b331e24cf7d426e05a73014f20e845a59c761643 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 27 Aug 2026 20:01:36 +0000 Subject: [PATCH 13/21] Regenerate the stage0 mirrors for the sixth disposition arm The .dag gained CandidateVariantParentUnresolved and the variant_parent_unresolved census column after the previous regen, so the committed mirrors carried five arms where the authority declares six. Review 56923 reported this as two findings, and they are one: it names a behavioral defect (the Rust dispatch collapses the ambiguity-sentinel and Absent cases) whose cause is structural (the arm is not declared in that file at all). The distinction matters for the remedy -- a collapse is fixed by descending a match, an absence only by regen -- so anyone reading the first finding literally would have searched for an arm to split in a file whose type has no sixth arm to split. Emitted by generation 0 from 157cef3e751; the two drifted files are exactly the two the sixth arm touches. v1_std_core.rs does not drift, which is the expected result: the arm is a 05_emit_rust disposition, not a 00_core diagnostic variant. --- src/v1/stage0/src/v1_compiler_emit_rust.rs | 38 +++++++++-- ...derived_disposition_census_witness_test.rs | 66 ++++++++++++++++++- 2 files changed, 96 insertions(+), 8 deletions(-) diff --git a/src/v1/stage0/src/v1_compiler_emit_rust.rs b/src/v1/stage0/src/v1_compiler_emit_rust.rs index 2a09774910f..d38ed546af8 100644 --- a/src/v1/stage0/src/v1_compiler_emit_rust.rs +++ b/src/v1/stage0/src/v1_compiler_emit_rust.rs @@ -7758,6 +7758,7 @@ pub enum ReferenceDerivedCandidateDisposition { CandidateSurvived { provider_module: String }, CandidateOwnModule, CandidateVariantDelegatedToParent { parent_enum: String }, + CandidateVariantParentUnresolved, CandidateRegistryAbsent, CandidateExportProofFailed { provider_module: String }, } @@ -7782,12 +7783,18 @@ pub fn reference_derived_candidate_disposition( ) -> Rc { if is_known_variant(type_summaries.clone(), name.clone()) { match v1_rt::map_get(&variant_to_enum, name.clone()) { - Some(parent) => Rc::new( - ReferenceDerivedCandidateDisposition::CandidateVariantDelegatedToParent { - parent_enum: parent.clone(), - }, - ), - None => Rc::new(ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent), + Some(parent) => { + if (parent.clone() == "".to_string()) { + Rc::new(ReferenceDerivedCandidateDisposition::CandidateVariantParentUnresolved) + } else { + Rc::new( + ReferenceDerivedCandidateDisposition::CandidateVariantDelegatedToParent { + parent_enum: parent.clone(), + }, + ) + } + } + None => Rc::new(ReferenceDerivedCandidateDisposition::CandidateVariantParentUnresolved), } } else { match v1_rt::map_get(®istry, name.clone()) { @@ -7850,6 +7857,9 @@ pub fn reference_derived_row_diagnostics( parent_enum: _, .. } => Rc::new(vec![]), + ReferenceDerivedCandidateDisposition::CandidateVariantParentUnresolved => { + Rc::new(vec![]) + } ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent => { Rc::new(vec![]) } @@ -7885,6 +7895,9 @@ pub fn reference_derived_disposition_name( parent_enum: _, .. } => "variant-delegated-to-parent".to_string(), + ReferenceDerivedCandidateDisposition::CandidateVariantParentUnresolved => { + "variant-parent-unresolved".to_string() + } ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent => { "registry-absent".to_string() } @@ -7901,6 +7914,7 @@ pub struct ReferenceDerivedCensus { pub survived: i64, pub own_module: i64, pub variant_delegated_to_parent: i64, + pub variant_parent_unresolved: i64, pub registry_absent: i64, pub export_proof_failed: i64, } @@ -7946,6 +7960,18 @@ pub fn reference_derived_census( __result }) .len() as i64), + variant_parent_unresolved: (Rc::new({ + let mut __result = Vec::new(); + for r in rows.iter().cloned() { + if (reference_derived_disposition_name(r.disposition.clone()) + == "variant-parent-unresolved".to_string()) + { + __result.push(r); + } + } + __result + }) + .len() as i64), registry_absent: (Rc::new({ let mut __result = Vec::new(); for r in rows.iter().cloned() { diff --git a/src/v1/stage0/src/v1_tests_claim_reference_derived_disposition_census_witness_test.rs b/src/v1/stage0/src/v1_tests_claim_reference_derived_disposition_census_witness_test.rs index 1e4e21f4dad..c590f65c74d 100644 --- a/src/v1/stage0/src/v1_tests_claim_reference_derived_disposition_census_witness_test.rs +++ b/src/v1/stage0/src/v1_tests_claim_reference_derived_disposition_census_witness_test.rs @@ -6,7 +6,7 @@ pub use crate::v1_compiler_compile::compile_to_resolved; pub use crate::v1_compiler_compile::SourceFile; use crate::v1_compiler_emit_rust::ReferenceDerivedCandidateDisposition::{ CandidateExportProofFailed, CandidateOwnModule, CandidateRegistryAbsent, CandidateSurvived, - CandidateVariantDelegatedToParent, + CandidateVariantDelegatedToParent, CandidateVariantParentUnresolved, }; pub use crate::v1_compiler_emit_rust::{ build_module_index, emit_rust_reference_derived_census, emit_rust_reference_derived_rows, @@ -164,11 +164,19 @@ pub fn census_counts_each_arm_separately() -> bool { }, ), }), + Rc::new(ReferenceDerivedCandidateRow { + module_name: "m".to_string(), + name: "f".to_string(), + disposition: Rc::new( + ReferenceDerivedCandidateDisposition::CandidateVariantParentUnresolved, + ), + }), ]); let census = reference_derived_census(rows.clone()); - ((((((census.candidates.clone() == 5) && (census.survived.clone() == 1)) + (((((((census.candidates.clone() == 6) && (census.survived.clone() == 1)) && (census.own_module.clone() == 1)) && (census.variant_delegated_to_parent.clone() == 1)) + && (census.variant_parent_unresolved.clone() == 1)) && (census.registry_absent.clone() == 1)) && (census.export_proof_failed.clone() == 1)) } @@ -226,6 +234,60 @@ pub fn known_variant_is_delegated_to_its_parent_not_registry_absent() -> bool { )) } +pub fn ambiguous_parent_red_note() -> String { + thread_local! { + static CACHED: String = { + "THE REACHABLE HALF of the unresolved-parent arm, and the reason it is not merely a tidier name. derive_variant_to_enum inserts the EMPTY STRING as the parent when one variant name appears in two enums, so the lookup answers Present with a parent naming nothing. Without this arm that produced CandidateVariantDelegatedToParent { parent_enum: \"\" } -- a delegation to no one, wearing the payload that was supposed to make delegation honest. This fixture authors the collision directly and is RED against the arm-less form.".to_string() + }; + } + CACHED.with(|c: &String| c.clone()) +} + +pub fn fixture_colliding_variant_type_summaries() -> Rc>> { + v1_rt::rc_map_insert( + fixture_variant_type_summaries(), + "F".to_string(), + Rc::new(TypeSummary { + name: "F".to_string(), + repr: Rc::new(TypeRepr::EnumRepr { unit_only: true }), + field_summaries: v1_rt::rc_empty_map::>(), + field_type_map: v1_rt::rc_empty_map::(), + field_import_surface_names: Rc::new(vec![]), + variant_name_set: v1_rt::rc_map_insert( + v1_rt::rc_empty_map::(), + "V".to_string(), + true, + ), + generic_param_names: Rc::new(vec![]), + has_fn_fields: false, + }), + ) +} + +pub fn a_variant_whose_parent_is_ambiguous_is_not_delegated_to_nothing() -> bool { + (reference_derived_candidate_disposition( + "V".to_string(), + "fixture.consumer".to_string(), + v1_rt::rc_empty_map::>(), + v1_rt::rc_empty_map::>>(), + Rc::new(vec![]), + v1_rt::rc_empty_map::>(), + build_module_index(Rc::new(vec![])), + fixture_colliding_variant_type_summaries(), + v1_rt::rc_map_insert( + v1_rt::rc_empty_map::(), + "V".to_string(), + "".to_string(), + ), + ) == Rc::new(ReferenceDerivedCandidateDisposition::CandidateVariantParentUnresolved)) +} + +pub fn an_unresolved_parent_is_not_reported_as_registry_absent() -> bool { + (reference_derived_disposition_name(Rc::new( + ReferenceDerivedCandidateDisposition::CandidateVariantParentUnresolved, + )) != "registry-absent".to_string()) +} + pub fn non_variant_name_still_answers_registry_absent() -> bool { (reference_derived_disposition_name(reference_derived_candidate_disposition( "NotAVariant".to_string(), From 132f1ee2241fe85f7ac8da234430c725c228e63d Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 27 Aug 2026 20:43:56 +0000 Subject: [PATCH 14/21] Demote the export-proof refusal to advisory: its first execution over a second closure returned 87, not 0 The flip to blocking was approved on a measurement of the regen seed closure, where CandidateExportProofFailed's population is zero. The required build lane also runs a v2-emission phase over a different closure -- entry:src/v2/compiler/00_compile.dag, 169 modules -- and there the population is 87. Run 33111325404 at b331e24cf7d refused that phase and reddened the required check. The flip condition was "population over a NAMED CLOSURE is zero AND a discriminating RED is authorable". Both halves held and the conclusion was still wrong: the zero was a property of the closure, not of the arm, and a per-closure zero licenses nothing about another closure. That is the denominator error this same carrier already names one clause down for the sibling arm -- registry-absent is entry-relative rather than a fixed target -- and the reasoning was available for this arm and not applied to it. The bounded sequence ruled for this work said count == 0 flips and count > 0 is the burndown roster at identity grain. The terminal event has fired with the second answer, so 87 is the roster and the arm stays advisory until it burns down. What the brief asked for is unchanged: the diagnostic is still typed, located and counted, so the silent omission is closed. Severity decides whether the line stops, not whether the omission is visible. The falsification is recorded in the carrier rather than the trigger being repointed, and the corrected condition is stated: the population must be zero over every closure a required phase compiles. --- src/v1/00_core.dag | 5 ++++- .../reference_derived_disposition_census_witness_test.dag | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/v1/00_core.dag b/src/v1/00_core.dag index f8924ef4098..e9196adfdc0 100644 --- a/src/v1/00_core.dag +++ b/src/v1/00_core.dag @@ -516,6 +516,7 @@ fn is_error_diagnostic(d: CompilerDiagnostic) -> Bool { match d { UnlistedImportUse { name: _, span: _ } => false ReferenceDerivedImportProviderUnknown { name: _, referencing_module: _, span: _ } => false + ReferenceDerivedImportExportUnproven { name: _, referencing_module: _, provider_module: _, span: _ } => false UnlistedVariantValueUse { name: _, span: _ } => false MethodExistenceFrontierAdmitted { method: _, receiver_type: _, trigger: _, span: _ } => false ReceiverTypeUnestablished { method: _, span: _ } => false @@ -525,7 +526,7 @@ fn is_error_diagnostic(d: CompilerDiagnostic) -> Bool { } } -data reference_derived_import_refusal_severity_note: String = "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). TWO DIAGNOSTICS, ONE WIRED AND BLOCKING, ONE DECLARED AND PRODUCED BY NOTHING -- and which is which was decided by measurement, not by preference. ReferenceDerivedImportExportUnproven is emitted from CandidateExportProofFailed: the provider is present in the closure and its export could not be established, and the remedy is NOT an import -- the provider is already here -- but making the emitter able to prove an export it may already hold. IT IS BLOCKING (warm-hawk-909, compiler direction, 2026-08-27), under a flip condition strictly stronger than a count: an arm flips when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient, because an observation of zero on an arm that has never fired is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check cited as coverage, which 4b rates worse than absent. BOTH HALVES HOLD HERE: the population is zero over the regen seed closure (claim_executor --required-regen --source-root dag --source-root src/v2, whose subject is regen_input_sources grown to a joint fixpoint over import, dotted-reference and bare-reference edges), and the RED is authorable and AUTHORED at the fixture boundary in v1.tests.claim.reference_derived_disposition_census_witness_test, which builds a CandidateExportProofFailed row and asserts the diagnostic. The emission still produces its files beside a blocking diagnostic rather than returning none: production precedes adjudication, so the candidate tree survives the refusal and the refusal is what stops the line. ReferenceDerivedImportProviderUnknown is DECLARED HERE AND PRODUCED BY NOTHING. Its arm, CandidateRegistryAbsent, was instrumented and measured over the same closure and returned 473 rows over 63 names across 110 modules. THAT POPULATION IS MIXED, AND AN EARLIER REVISION OF THIS NOTE CALLED IT ALL TARGET VOCABULARY -- corrected 2026-08-27 after deep-ant-102 pushed back on the census SCOPE. Two classes are conflated inside it. (a) EXTINGUISHED -- Vec 88, bool 73, i64 48, BTreeSet: no .dag declaration exists anywhere and none can, because the PRODUCER is collect_item_realized_surface_names, which is rust_identifier_tokens over render_rust_type and so emits target-language SPELLINGS by construction (attribution corrected by clever-boar-140, who owns the coproduct: the candidate filter's emitted-source disjunct is an admission GATE over an already-proposed list and cannot itself propose anything, so narrowing it would delete genuine emitter-attested candidates and leave the real source untouched). reference_is_host_realized_builtin misses these because it is keyed on .dag vocabulary -- is_container_type reads std.types container_type_arity -- so Vec and BTreeSet, the RUST spellings of List and Set, pass a filter that exists precisely to remove host-realized names. (b) OUT-OF-CLOSURE -- empty_map 33 (v2.std.collection), Optional 25 (v2.std.optional), and its VARIANTS Present 47 and Absent 13: REAL .dag names whose providers exist and were not selected in. The variant half is not a detail: a consumer key that reads a declaration index's `declared` field alone reports no-provider-anywhere for every variant name in the corpus, because a variant is not a top-level declaration and such an index holds variants in a separate field -- so the key must read declared UNION variants, and two of these four names are the instance that proves it (clever-boar-140, who then settled it STRUCTURALLY rather than from this sample: build_item_info in v1.compiler.infer produces one ItemInfo per TOP-LEVEL ITEM and no arm descends into a coproduct's children, and the single item_registry insert is keyed on that name, so a variant name is absent from the registry under EVERY closure -- verified here against both sites). THAT FACT AND THIS ARM'S ORDERING COMPOSE, and the composition is why these two names appear under (b) rather than under (a): CandidateVariantDelegatedToParent is tested BEFORE the registry lookup, so a variant whose parent is IN closure is delegated and never reaches the registry at all, and the registry's structural inability to hold variants surfaces only when the parent is OUT of closure and type_summaries therefore cannot recognise the name as a variant. So Present and Absent are registry-absent HERE because Optional was out of closure, and would be delegated with it in -- which is exactly why this sample cannot discriminate the two readings, and why the structural argument above was needed instead. THE PROPORTION IS UNMEASURED IN BOTH DIRECTIONS, and the two ways of counting point OPPOSITE ways, which is the whole reason this population was misread twice. BY ROW the immovable names dominate: Vec 88 + bool 73 + Option 50 + i64 48 out of 473, each verified to have no .dag declaration anywhere. BY DISTINCT NAME the examined sample is 4 of 63 and ALL FOUR ARE MOVABLE. High-count immovable names at the head, movable names in the tail with small counts -- which is exactly the structure that hid the signal, and exactly why reading a sorted head answered the wrong question: empty_map's 33 rows were the third-largest count and were still classified as target vocabulary on the first pass. FIFTY-NINE NAMES ARE UNEXAMINED BY ANYONE. Neither the row-dominance nor the 4-of-4 may be quoted as a proportion; both are directional facts about different denominators. WHY (b) IS THERE AT ALL is a trap worth carrying: the census invocation passes --source-root dag --source-root src/v2, but the SUBJECT is regen_input_sources, whose roots are SeedV1 and DagCorpus and exclude src/v2 entirely -- stage0 IS the v1 seed, and a seed reaching into src/v2 would depend on the successor it bootstraps toward. The source-root FLAGS and the regen SUBJECT are not the same thing. So class (b) is precisely the closure-conditioned population this change exists to make visible, sitting inside the rows that had been written off, and registry-absent is ENTRY-RELATIVE rather than a fixed target: a reader who takes the number as stable has made the denominator error. THE 473 IS NOT A ROSTER OF UNFIXABLE ROWS. Wiring a diagnostic to it would still print a permanently-false report on every class-(a) row -- which is why the arm stays unwired even though part of the population is movable -- and the reason that is worse than shipping nothing is that IT TRAINS READERS TO IGNORE THE CHANNEL: a diagnostic nobody reads is worth less than an absent one, because the absent one is honest about its coverage. The variant stays declared because the class is real and its shape is settled; the trigger for wiring it is that class (a) leave the arm -- the candidate walk ceasing to propose target-language vocabulary -- after which the residue is class (b) and is exactly the closure-conditioned population worth reporting. A witness asserts zero diagnostics from that arm and fails the moment anyone wires it. THE TWO ARE SEPARATE DIAGNOSTICS RATHER THAN ONE WITH A CAUSE FIELD, because the question is upstream of severity: registry-absent is fixed by authoring an import, export-proof-failed by proving an export that already exists -- opposite remedies, different owners, different populations, different reachability -- and 4b files one row per class. That the two turned out to need different TREATMENT rather than merely different severity is the strongest evidence the split was right. A REFUTED PREDICTION, recorded rather than quietly repointed. Registry-absent was predicted to overlap heavily with UnlistedImportUse, both being described as 'referenced but never imported', and the prediction was made because closure membership is driven by imports. Measured on one run: 63 registry-absent names, 36 UnlistedImportUse names, INTERSECTION ZERO. UnlistedImportUse names .dag types masked at RESOLVE time; registry-absent is dominated by Rust tokens that never reached the resolver. The reasoning was wrong at the MECHANISM, not merely the magnitude. ProviderUnknown's trigger therefore does NOT point at the family-closure-SVN burndown as this carrier previously stated, and the falsification is recorded here rather than the trigger being silently repointed -- a repointed trigger would inherit the credibility of a prediction that just died, so whatever that trigger eventually becomes is argued fresh." +data reference_derived_import_refusal_severity_note: String = "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). TWO DIAGNOSTICS, ONE WIRED AND BLOCKING, ONE DECLARED AND PRODUCED BY NOTHING -- and which is which was decided by measurement, not by preference. ReferenceDerivedImportExportUnproven is emitted from CandidateExportProofFailed: the provider is present in the closure and its export could not be established, and the remedy is NOT an import -- the provider is already here -- but making the emitter able to prove an export it may already hold. IT IS ADVISORY, AND IT WAS BLOCKING FOR ONE CI RUN -- THE FLIP WAS APPROVED, EXECUTED, AND REFUTED BY ITS OWN FIRST EXECUTION OVER A SECOND CLOSURE, recorded here rather than repointed because a trigger that survives its own falsification inherits credibility a dead prediction no longer has. THE FLIP CONDITION WAS: an arm flips when its population over a NAMED CLOSURE is zero AND a discriminating RED is authorable. Both halves held and the conclusion was still wrong, which is the finding. The named closure was the regen seed closure (SeedV1 + DagCorpus), where the population is zero. The required build lane ALSO runs a v2-emission phase over a DIFFERENT closure -- entry:src/v2/compiler/00_compile.dag, 169 modules -- and there the population is EIGHTY-SEVEN, measured on run 33111325404 at b331e24cf7d, which refused the phase and reddened the required check. So the zero was a property of THE CLOSURE and not of the arm, and flipping globally on a single-closure measurement is the denominator error this very carrier already names one clause down for the sibling arm (registry-absent is ENTRY-RELATIVE rather than a fixed target). The same reasoning was available for this arm and was not applied to it. THE CORRECTED CONDITION, which is what the flip should have required: the population must be zero over EVERY closure a required phase compiles, not over one named closure -- a per-closure zero is a fact about that entry and licenses nothing about another. The bounded sequence ruled for this work said count == 0 flips and count > 0 IS THE BURNDOWN ROSTER AT IDENTITY GRAIN; the terminal event has now fired with the second answer, so the 87 is that roster and the arm stays advisory until it burns down. The population is NOT a stable number and must not be quoted as one: it is 0 over the seed closure and 87 over the v2 compiler entry, and no closure has been enumerated beyond those two. THE DIAGNOSTIC IS STILL TYPED, LOCATED AND COUNTED, which is the whole of what the brief asked -- the silent omission is closed either way; severity decides whether the line stops, not whether the omission is visible. A sample of what it reports, and the reason 87 is a real roster rather than noise: std.determinism references std.perturbation.PerturbationVerdict whose provider is in the closure with its export unestablished, and v2.lens.complexity_accumulator_copy.analyze accounts for a large share of the rest against providers that are likewise present. Under the previous condition: an arm flips when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient, because an observation of zero on an arm that has never fired is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check cited as coverage, which 4b rates worse than absent. BOTH HALVES HELD AT THE TIME: the population is zero over the regen seed closure (claim_executor --required-regen --source-root dag --source-root src/v2, whose subject is regen_input_sources grown to a joint fixpoint over import, dotted-reference and bare-reference edges), and the RED is authorable and AUTHORED at the fixture boundary in v1.tests.claim.reference_derived_disposition_census_witness_test, which builds a CandidateExportProofFailed row and asserts the diagnostic. The emission still produces its files beside the diagnostic rather than returning none: production precedes adjudication, so the candidate tree survives the refusal and the refusal is what stops the line. ReferenceDerivedImportProviderUnknown is DECLARED HERE AND PRODUCED BY NOTHING. Its arm, CandidateRegistryAbsent, was instrumented and measured over the same closure and returned 473 rows over 63 names across 110 modules. THAT POPULATION IS MIXED, AND AN EARLIER REVISION OF THIS NOTE CALLED IT ALL TARGET VOCABULARY -- corrected 2026-08-27 after deep-ant-102 pushed back on the census SCOPE. Two classes are conflated inside it. (a) EXTINGUISHED -- Vec 88, bool 73, i64 48, BTreeSet: no .dag declaration exists anywhere and none can, because the PRODUCER is collect_item_realized_surface_names, which is rust_identifier_tokens over render_rust_type and so emits target-language SPELLINGS by construction (attribution corrected by clever-boar-140, who owns the coproduct: the candidate filter's emitted-source disjunct is an admission GATE over an already-proposed list and cannot itself propose anything, so narrowing it would delete genuine emitter-attested candidates and leave the real source untouched). reference_is_host_realized_builtin misses these because it is keyed on .dag vocabulary -- is_container_type reads std.types container_type_arity -- so Vec and BTreeSet, the RUST spellings of List and Set, pass a filter that exists precisely to remove host-realized names. (b) OUT-OF-CLOSURE -- empty_map 33 (v2.std.collection), Optional 25 (v2.std.optional), and its VARIANTS Present 47 and Absent 13: REAL .dag names whose providers exist and were not selected in. The variant half is not a detail: a consumer key that reads a declaration index's `declared` field alone reports no-provider-anywhere for every variant name in the corpus, because a variant is not a top-level declaration and such an index holds variants in a separate field -- so the key must read declared UNION variants, and two of these four names are the instance that proves it (clever-boar-140, who then settled it STRUCTURALLY rather than from this sample: build_item_info in v1.compiler.infer produces one ItemInfo per TOP-LEVEL ITEM and no arm descends into a coproduct's children, and the single item_registry insert is keyed on that name, so a variant name is absent from the registry under EVERY closure -- verified here against both sites). THAT FACT AND THIS ARM'S ORDERING COMPOSE, and the composition is why these two names appear under (b) rather than under (a): CandidateVariantDelegatedToParent is tested BEFORE the registry lookup, so a variant whose parent is IN closure is delegated and never reaches the registry at all, and the registry's structural inability to hold variants surfaces only when the parent is OUT of closure and type_summaries therefore cannot recognise the name as a variant. So Present and Absent are registry-absent HERE because Optional was out of closure, and would be delegated with it in -- which is exactly why this sample cannot discriminate the two readings, and why the structural argument above was needed instead. THE PROPORTION IS UNMEASURED IN BOTH DIRECTIONS, and the two ways of counting point OPPOSITE ways, which is the whole reason this population was misread twice. BY ROW the immovable names dominate: Vec 88 + bool 73 + Option 50 + i64 48 out of 473, each verified to have no .dag declaration anywhere. BY DISTINCT NAME the examined sample is 4 of 63 and ALL FOUR ARE MOVABLE. High-count immovable names at the head, movable names in the tail with small counts -- which is exactly the structure that hid the signal, and exactly why reading a sorted head answered the wrong question: empty_map's 33 rows were the third-largest count and were still classified as target vocabulary on the first pass. FIFTY-NINE NAMES ARE UNEXAMINED BY ANYONE. Neither the row-dominance nor the 4-of-4 may be quoted as a proportion; both are directional facts about different denominators. WHY (b) IS THERE AT ALL is a trap worth carrying: the census invocation passes --source-root dag --source-root src/v2, but the SUBJECT is regen_input_sources, whose roots are SeedV1 and DagCorpus and exclude src/v2 entirely -- stage0 IS the v1 seed, and a seed reaching into src/v2 would depend on the successor it bootstraps toward. The source-root FLAGS and the regen SUBJECT are not the same thing. So class (b) is precisely the closure-conditioned population this change exists to make visible, sitting inside the rows that had been written off, and registry-absent is ENTRY-RELATIVE rather than a fixed target: a reader who takes the number as stable has made the denominator error. THE 473 IS NOT A ROSTER OF UNFIXABLE ROWS. Wiring a diagnostic to it would still print a permanently-false report on every class-(a) row -- which is why the arm stays unwired even though part of the population is movable -- and the reason that is worse than shipping nothing is that IT TRAINS READERS TO IGNORE THE CHANNEL: a diagnostic nobody reads is worth less than an absent one, because the absent one is honest about its coverage. The variant stays declared because the class is real and its shape is settled; the trigger for wiring it is that class (a) leave the arm -- the candidate walk ceasing to propose target-language vocabulary -- after which the residue is class (b) and is exactly the closure-conditioned population worth reporting. A witness asserts zero diagnostics from that arm and fails the moment anyone wires it. THE TWO ARE SEPARATE DIAGNOSTICS RATHER THAN ONE WITH A CAUSE FIELD, because the question is upstream of severity: registry-absent is fixed by authoring an import, export-proof-failed by proving an export that already exists -- opposite remedies, different owners, different populations, different reachability -- and 4b files one row per class. That the two turned out to need different TREATMENT rather than merely different severity is the strongest evidence the split was right. A REFUTED PREDICTION, recorded rather than quietly repointed. Registry-absent was predicted to overlap heavily with UnlistedImportUse, both being described as 'referenced but never imported', and the prediction was made because closure membership is driven by imports. Measured on one run: 63 registry-absent names, 36 UnlistedImportUse names, INTERSECTION ZERO. UnlistedImportUse names .dag types masked at RESOLVE time; registry-absent is dominated by Rust tokens that never reached the resolver. The reasoning was wrong at the MECHANISM, not merely the magnitude. ProviderUnknown's trigger therefore does NOT point at the family-closure-SVN burndown as this carrier previously stated, and the falsification is recorded here rather than the trigger being silently repointed -- a repointed trigger would inherit the credibility of a prediction that just died, so whatever that trigger eventually becomes is argued fresh." data where_refinement_deferral_reason_scaffold_note: String = "owner: v1.compiler.infer (00_core diagnostic partition). lane: where-refinement literal-wall enforcement. interim: WhereRefinementUnenforced.reason is a closed-string sum enrolled in is_where_refinement_unenforced_advisory_reason; any unlisted reason fails closed blocking. bound: only the five deferral strings emitted by 04_infer today; classifier/eval/equivalence arms must change together until coproduct lands. dissolve-on: feature:where-refinement-predicate-coproduct (WhereRefinementDeferralReason coproduct on the diagnostic carrier)." @@ -541,6 +542,7 @@ fn is_interpreter_blocking_diagnostic(d: CompilerDiagnostic) -> Bool { ServiceConfigReferenceJudgmentDeferred { field: _, referenced_name: _, trigger: _, span: _ } => false DeclaredTypeInhabitanceUndecided { position: _, reason: _, span: _ } => false ReferenceDerivedImportProviderUnknown { name: _, referencing_module: _, span: _ } => false + ReferenceDerivedImportExportUnproven { name: _, referencing_module: _, provider_module: _, span: _ } => false _ => true } } @@ -554,6 +556,7 @@ fn is_discovery_corpus_advisory_typecheck_diagnostic(d: CompilerDiagnostic) -> B ServiceConfigReferenceJudgmentDeferred { field: _, referenced_name: _, trigger: _, span: _ } => true DeclaredTypeInhabitanceUndecided { position: _, reason: _, span: _ } => true ReferenceDerivedImportProviderUnknown { name: _, referencing_module: _, span: _ } => true + ReferenceDerivedImportExportUnproven { name: _, referencing_module: _, provider_module: _, span: _ } => true WhereRefinementUnenforced { predicate: _, formal_type: _, reason: r, span: _ } => is_where_refinement_unenforced_advisory_reason(reason: r) _ => false diff --git a/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag b/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag index 3fb01efad69..517c8b7d3e7 100644 --- a/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag +++ b/src/v1/tests/claim/reference_derived_disposition_census_witness_test.dag @@ -196,12 +196,12 @@ test fn only_export_proof_failed_produces_a_diagnostic() -> Bool { reference_derived_row_diagnostics(rows: rows, module_span: no_span()) |> count == 1 } -test fn the_export_unproven_diagnostic_is_blocking() -> Bool { +test fn the_export_unproven_diagnostic_is_advisory() -> Bool { let rows = [ ReferenceDerivedCandidateRow { module_name: "m", name: "d", disposition: CandidateExportProofFailed { provider_module: "p" } } ] reference_derived_row_diagnostics(rows: rows, module_span: no_span()) - |> all(e => is_error_diagnostic(d: e.diagnostic)) + |> all(e => !is_error_diagnostic(d: e.diagnostic)) } data registry_absent_not_surfaced_note: String = "The RED that keeps registry-absent OUT of the diagnostic stream. Measured over the regen seed closure, that arm returns 473 rows over 63 names dominated by Rust target-language tokens -- Vec, bool, Option, i64, empty_map -- which the candidate walk proposes by tokenizing the module's own emitted Rust and for which no .dag provider can ever exist. Wiring it would print a false report on every build. This test fails the moment someone adds that arm to reference_derived_row_diagnostics, which is the intended wall until the candidate walk stops proposing target-language vocabulary." From 40f6b80380dda52ddf350b3691ddeb458bc822c0 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 27 Aug 2026 20:46:01 +0000 Subject: [PATCH 15/21] Name the three required closures in the carrier rather than describing them smart-ram-730's point: 'every closure a required phase compiles' is a set that moves. It grew when #9035 added the v2-emission phase and again when that phase's subject widened from dag/std/abi.dag to the v2 pipeline root. A described set leaves a future phase addition to whoever remembers; a named one makes it visibly re-open the flip question. Also records that only two of the three are measured for this arm (0 and 87), so even the corrected condition is not currently evaluable, and that deep-ant-102 delivered this exact objection before the flip and it was acknowledged and not carried. A dropped warning and a missing insight have different remedies. --- src/v1/00_core.dag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v1/00_core.dag b/src/v1/00_core.dag index e9196adfdc0..b3a5353ae2e 100644 --- a/src/v1/00_core.dag +++ b/src/v1/00_core.dag @@ -526,7 +526,7 @@ fn is_error_diagnostic(d: CompilerDiagnostic) -> Bool { } } -data reference_derived_import_refusal_severity_note: String = "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). TWO DIAGNOSTICS, ONE WIRED AND BLOCKING, ONE DECLARED AND PRODUCED BY NOTHING -- and which is which was decided by measurement, not by preference. ReferenceDerivedImportExportUnproven is emitted from CandidateExportProofFailed: the provider is present in the closure and its export could not be established, and the remedy is NOT an import -- the provider is already here -- but making the emitter able to prove an export it may already hold. IT IS ADVISORY, AND IT WAS BLOCKING FOR ONE CI RUN -- THE FLIP WAS APPROVED, EXECUTED, AND REFUTED BY ITS OWN FIRST EXECUTION OVER A SECOND CLOSURE, recorded here rather than repointed because a trigger that survives its own falsification inherits credibility a dead prediction no longer has. THE FLIP CONDITION WAS: an arm flips when its population over a NAMED CLOSURE is zero AND a discriminating RED is authorable. Both halves held and the conclusion was still wrong, which is the finding. The named closure was the regen seed closure (SeedV1 + DagCorpus), where the population is zero. The required build lane ALSO runs a v2-emission phase over a DIFFERENT closure -- entry:src/v2/compiler/00_compile.dag, 169 modules -- and there the population is EIGHTY-SEVEN, measured on run 33111325404 at b331e24cf7d, which refused the phase and reddened the required check. So the zero was a property of THE CLOSURE and not of the arm, and flipping globally on a single-closure measurement is the denominator error this very carrier already names one clause down for the sibling arm (registry-absent is ENTRY-RELATIVE rather than a fixed target). The same reasoning was available for this arm and was not applied to it. THE CORRECTED CONDITION, which is what the flip should have required: the population must be zero over EVERY closure a required phase compiles, not over one named closure -- a per-closure zero is a fact about that entry and licenses nothing about another. The bounded sequence ruled for this work said count == 0 flips and count > 0 IS THE BURNDOWN ROSTER AT IDENTITY GRAIN; the terminal event has now fired with the second answer, so the 87 is that roster and the arm stays advisory until it burns down. The population is NOT a stable number and must not be quoted as one: it is 0 over the seed closure and 87 over the v2 compiler entry, and no closure has been enumerated beyond those two. THE DIAGNOSTIC IS STILL TYPED, LOCATED AND COUNTED, which is the whole of what the brief asked -- the silent omission is closed either way; severity decides whether the line stops, not whether the omission is visible. A sample of what it reports, and the reason 87 is a real roster rather than noise: std.determinism references std.perturbation.PerturbationVerdict whose provider is in the closure with its export unestablished, and v2.lens.complexity_accumulator_copy.analyze accounts for a large share of the rest against providers that are likewise present. Under the previous condition: an arm flips when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient, because an observation of zero on an arm that has never fired is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check cited as coverage, which 4b rates worse than absent. BOTH HALVES HELD AT THE TIME: the population is zero over the regen seed closure (claim_executor --required-regen --source-root dag --source-root src/v2, whose subject is regen_input_sources grown to a joint fixpoint over import, dotted-reference and bare-reference edges), and the RED is authorable and AUTHORED at the fixture boundary in v1.tests.claim.reference_derived_disposition_census_witness_test, which builds a CandidateExportProofFailed row and asserts the diagnostic. The emission still produces its files beside the diagnostic rather than returning none: production precedes adjudication, so the candidate tree survives the refusal and the refusal is what stops the line. ReferenceDerivedImportProviderUnknown is DECLARED HERE AND PRODUCED BY NOTHING. Its arm, CandidateRegistryAbsent, was instrumented and measured over the same closure and returned 473 rows over 63 names across 110 modules. THAT POPULATION IS MIXED, AND AN EARLIER REVISION OF THIS NOTE CALLED IT ALL TARGET VOCABULARY -- corrected 2026-08-27 after deep-ant-102 pushed back on the census SCOPE. Two classes are conflated inside it. (a) EXTINGUISHED -- Vec 88, bool 73, i64 48, BTreeSet: no .dag declaration exists anywhere and none can, because the PRODUCER is collect_item_realized_surface_names, which is rust_identifier_tokens over render_rust_type and so emits target-language SPELLINGS by construction (attribution corrected by clever-boar-140, who owns the coproduct: the candidate filter's emitted-source disjunct is an admission GATE over an already-proposed list and cannot itself propose anything, so narrowing it would delete genuine emitter-attested candidates and leave the real source untouched). reference_is_host_realized_builtin misses these because it is keyed on .dag vocabulary -- is_container_type reads std.types container_type_arity -- so Vec and BTreeSet, the RUST spellings of List and Set, pass a filter that exists precisely to remove host-realized names. (b) OUT-OF-CLOSURE -- empty_map 33 (v2.std.collection), Optional 25 (v2.std.optional), and its VARIANTS Present 47 and Absent 13: REAL .dag names whose providers exist and were not selected in. The variant half is not a detail: a consumer key that reads a declaration index's `declared` field alone reports no-provider-anywhere for every variant name in the corpus, because a variant is not a top-level declaration and such an index holds variants in a separate field -- so the key must read declared UNION variants, and two of these four names are the instance that proves it (clever-boar-140, who then settled it STRUCTURALLY rather than from this sample: build_item_info in v1.compiler.infer produces one ItemInfo per TOP-LEVEL ITEM and no arm descends into a coproduct's children, and the single item_registry insert is keyed on that name, so a variant name is absent from the registry under EVERY closure -- verified here against both sites). THAT FACT AND THIS ARM'S ORDERING COMPOSE, and the composition is why these two names appear under (b) rather than under (a): CandidateVariantDelegatedToParent is tested BEFORE the registry lookup, so a variant whose parent is IN closure is delegated and never reaches the registry at all, and the registry's structural inability to hold variants surfaces only when the parent is OUT of closure and type_summaries therefore cannot recognise the name as a variant. So Present and Absent are registry-absent HERE because Optional was out of closure, and would be delegated with it in -- which is exactly why this sample cannot discriminate the two readings, and why the structural argument above was needed instead. THE PROPORTION IS UNMEASURED IN BOTH DIRECTIONS, and the two ways of counting point OPPOSITE ways, which is the whole reason this population was misread twice. BY ROW the immovable names dominate: Vec 88 + bool 73 + Option 50 + i64 48 out of 473, each verified to have no .dag declaration anywhere. BY DISTINCT NAME the examined sample is 4 of 63 and ALL FOUR ARE MOVABLE. High-count immovable names at the head, movable names in the tail with small counts -- which is exactly the structure that hid the signal, and exactly why reading a sorted head answered the wrong question: empty_map's 33 rows were the third-largest count and were still classified as target vocabulary on the first pass. FIFTY-NINE NAMES ARE UNEXAMINED BY ANYONE. Neither the row-dominance nor the 4-of-4 may be quoted as a proportion; both are directional facts about different denominators. WHY (b) IS THERE AT ALL is a trap worth carrying: the census invocation passes --source-root dag --source-root src/v2, but the SUBJECT is regen_input_sources, whose roots are SeedV1 and DagCorpus and exclude src/v2 entirely -- stage0 IS the v1 seed, and a seed reaching into src/v2 would depend on the successor it bootstraps toward. The source-root FLAGS and the regen SUBJECT are not the same thing. So class (b) is precisely the closure-conditioned population this change exists to make visible, sitting inside the rows that had been written off, and registry-absent is ENTRY-RELATIVE rather than a fixed target: a reader who takes the number as stable has made the denominator error. THE 473 IS NOT A ROSTER OF UNFIXABLE ROWS. Wiring a diagnostic to it would still print a permanently-false report on every class-(a) row -- which is why the arm stays unwired even though part of the population is movable -- and the reason that is worse than shipping nothing is that IT TRAINS READERS TO IGNORE THE CHANNEL: a diagnostic nobody reads is worth less than an absent one, because the absent one is honest about its coverage. The variant stays declared because the class is real and its shape is settled; the trigger for wiring it is that class (a) leave the arm -- the candidate walk ceasing to propose target-language vocabulary -- after which the residue is class (b) and is exactly the closure-conditioned population worth reporting. A witness asserts zero diagnostics from that arm and fails the moment anyone wires it. THE TWO ARE SEPARATE DIAGNOSTICS RATHER THAN ONE WITH A CAUSE FIELD, because the question is upstream of severity: registry-absent is fixed by authoring an import, export-proof-failed by proving an export that already exists -- opposite remedies, different owners, different populations, different reachability -- and 4b files one row per class. That the two turned out to need different TREATMENT rather than merely different severity is the strongest evidence the split was right. A REFUTED PREDICTION, recorded rather than quietly repointed. Registry-absent was predicted to overlap heavily with UnlistedImportUse, both being described as 'referenced but never imported', and the prediction was made because closure membership is driven by imports. Measured on one run: 63 registry-absent names, 36 UnlistedImportUse names, INTERSECTION ZERO. UnlistedImportUse names .dag types masked at RESOLVE time; registry-absent is dominated by Rust tokens that never reached the resolver. The reasoning was wrong at the MECHANISM, not merely the magnitude. ProviderUnknown's trigger therefore does NOT point at the family-closure-SVN burndown as this carrier previously stated, and the falsification is recorded here rather than the trigger being silently repointed -- a repointed trigger would inherit the credibility of a prediction that just died, so whatever that trigger eventually becomes is argued fresh." +data reference_derived_import_refusal_severity_note: String = "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). TWO DIAGNOSTICS, ONE WIRED AND BLOCKING, ONE DECLARED AND PRODUCED BY NOTHING -- and which is which was decided by measurement, not by preference. ReferenceDerivedImportExportUnproven is emitted from CandidateExportProofFailed: the provider is present in the closure and its export could not be established, and the remedy is NOT an import -- the provider is already here -- but making the emitter able to prove an export it may already hold. IT IS ADVISORY, AND IT WAS BLOCKING FOR ONE CI RUN -- THE FLIP WAS APPROVED, EXECUTED, AND REFUTED BY ITS OWN FIRST EXECUTION OVER A SECOND CLOSURE, recorded here rather than repointed because a trigger that survives its own falsification inherits credibility a dead prediction no longer has. THE FLIP CONDITION WAS: an arm flips when its population over a NAMED CLOSURE is zero AND a discriminating RED is authorable. Both halves held and the conclusion was still wrong, which is the finding. The named closure was the regen seed closure (SeedV1 + DagCorpus), where the population is zero. The required build lane ALSO runs a v2-emission phase over a DIFFERENT closure -- entry:src/v2/compiler/00_compile.dag, 169 modules -- and there the population is EIGHTY-SEVEN, measured on run 33111325404 at b331e24cf7d, which refused the phase and reddened the required check. So the zero was a property of THE CLOSURE and not of the arm, and flipping globally on a single-closure measurement is the denominator error this very carrier already names one clause down for the sibling arm (registry-absent is ENTRY-RELATIVE rather than a fixed target). The same reasoning was available for this arm and was not applied to it. THE CORRECTED CONDITION, which is what the flip should have required: the population must be zero over EVERY closure a required phase compiles, not over one named closure -- a per-closure zero is a fact about that entry and licenses nothing about another. AND THE CLOSURES MUST BE NAMED HERE RATHER THAN DESCRIBED, because "every closure a required phase compiles" is a set that MOVES: it grew when #9035 added the v2-emission phase and again when that phase's subject was widened from dag/std/abi.dag to the v2 pipeline root, and it grows whenever a phase is added or an entry roster widens. A described set leaves that to whoever remembers; a named one makes a phase addition visibly re-open this question. AS OF 2026-08-27 THE SET IS THREE, each with its own authority: the regen seed closure (v1.compiler.cli_run regen_source_roots, SeedV1 + DagCorpus), the v2-emission entries (gunbc.ci_layer_roots required_v2_emission_entries), and the emit-compile entries (gunbc.ci_layer_roots required_emit_compile_entries). Only the first two have been measured for this arm -- 0 and 87 -- and the third is UNMEASURED, so even the corrected condition is not currently evaluable and any future flip must measure it rather than assume it follows the seed. This warning was delivered before the flip rather than after it: deep-ant-102 stated that the zero was 0 over that closure and not over all closures, and that with every provider selected in the proof gate is the easy case; it was acknowledged in writing and not carried into the condition. It is recorded as a dropped warning rather than as a missing insight, because the two have different remedies. The bounded sequence ruled for this work said count == 0 flips and count > 0 IS THE BURNDOWN ROSTER AT IDENTITY GRAIN; the terminal event has now fired with the second answer, so the 87 is that roster and the arm stays advisory until it burns down. The population is NOT a stable number and must not be quoted as one: it is 0 over the seed closure and 87 over the v2 compiler entry, and no closure has been enumerated beyond those two. THE DIAGNOSTIC IS STILL TYPED, LOCATED AND COUNTED, which is the whole of what the brief asked -- the silent omission is closed either way; severity decides whether the line stops, not whether the omission is visible. A sample of what it reports, and the reason 87 is a real roster rather than noise: std.determinism references std.perturbation.PerturbationVerdict whose provider is in the closure with its export unestablished, and v2.lens.complexity_accumulator_copy.analyze accounts for a large share of the rest against providers that are likewise present. Under the previous condition: an arm flips when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient, because an observation of zero on an arm that has never fired is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check cited as coverage, which 4b rates worse than absent. BOTH HALVES HELD AT THE TIME: the population is zero over the regen seed closure (claim_executor --required-regen --source-root dag --source-root src/v2, whose subject is regen_input_sources grown to a joint fixpoint over import, dotted-reference and bare-reference edges), and the RED is authorable and AUTHORED at the fixture boundary in v1.tests.claim.reference_derived_disposition_census_witness_test, which builds a CandidateExportProofFailed row and asserts the diagnostic. The emission still produces its files beside the diagnostic rather than returning none: production precedes adjudication, so the candidate tree survives the refusal and the refusal is what stops the line. ReferenceDerivedImportProviderUnknown is DECLARED HERE AND PRODUCED BY NOTHING. Its arm, CandidateRegistryAbsent, was instrumented and measured over the same closure and returned 473 rows over 63 names across 110 modules. THAT POPULATION IS MIXED, AND AN EARLIER REVISION OF THIS NOTE CALLED IT ALL TARGET VOCABULARY -- corrected 2026-08-27 after deep-ant-102 pushed back on the census SCOPE. Two classes are conflated inside it. (a) EXTINGUISHED -- Vec 88, bool 73, i64 48, BTreeSet: no .dag declaration exists anywhere and none can, because the PRODUCER is collect_item_realized_surface_names, which is rust_identifier_tokens over render_rust_type and so emits target-language SPELLINGS by construction (attribution corrected by clever-boar-140, who owns the coproduct: the candidate filter's emitted-source disjunct is an admission GATE over an already-proposed list and cannot itself propose anything, so narrowing it would delete genuine emitter-attested candidates and leave the real source untouched). reference_is_host_realized_builtin misses these because it is keyed on .dag vocabulary -- is_container_type reads std.types container_type_arity -- so Vec and BTreeSet, the RUST spellings of List and Set, pass a filter that exists precisely to remove host-realized names. (b) OUT-OF-CLOSURE -- empty_map 33 (v2.std.collection), Optional 25 (v2.std.optional), and its VARIANTS Present 47 and Absent 13: REAL .dag names whose providers exist and were not selected in. The variant half is not a detail: a consumer key that reads a declaration index's `declared` field alone reports no-provider-anywhere for every variant name in the corpus, because a variant is not a top-level declaration and such an index holds variants in a separate field -- so the key must read declared UNION variants, and two of these four names are the instance that proves it (clever-boar-140, who then settled it STRUCTURALLY rather than from this sample: build_item_info in v1.compiler.infer produces one ItemInfo per TOP-LEVEL ITEM and no arm descends into a coproduct's children, and the single item_registry insert is keyed on that name, so a variant name is absent from the registry under EVERY closure -- verified here against both sites). THAT FACT AND THIS ARM'S ORDERING COMPOSE, and the composition is why these two names appear under (b) rather than under (a): CandidateVariantDelegatedToParent is tested BEFORE the registry lookup, so a variant whose parent is IN closure is delegated and never reaches the registry at all, and the registry's structural inability to hold variants surfaces only when the parent is OUT of closure and type_summaries therefore cannot recognise the name as a variant. So Present and Absent are registry-absent HERE because Optional was out of closure, and would be delegated with it in -- which is exactly why this sample cannot discriminate the two readings, and why the structural argument above was needed instead. THE PROPORTION IS UNMEASURED IN BOTH DIRECTIONS, and the two ways of counting point OPPOSITE ways, which is the whole reason this population was misread twice. BY ROW the immovable names dominate: Vec 88 + bool 73 + Option 50 + i64 48 out of 473, each verified to have no .dag declaration anywhere. BY DISTINCT NAME the examined sample is 4 of 63 and ALL FOUR ARE MOVABLE. High-count immovable names at the head, movable names in the tail with small counts -- which is exactly the structure that hid the signal, and exactly why reading a sorted head answered the wrong question: empty_map's 33 rows were the third-largest count and were still classified as target vocabulary on the first pass. FIFTY-NINE NAMES ARE UNEXAMINED BY ANYONE. Neither the row-dominance nor the 4-of-4 may be quoted as a proportion; both are directional facts about different denominators. WHY (b) IS THERE AT ALL is a trap worth carrying: the census invocation passes --source-root dag --source-root src/v2, but the SUBJECT is regen_input_sources, whose roots are SeedV1 and DagCorpus and exclude src/v2 entirely -- stage0 IS the v1 seed, and a seed reaching into src/v2 would depend on the successor it bootstraps toward. The source-root FLAGS and the regen SUBJECT are not the same thing. So class (b) is precisely the closure-conditioned population this change exists to make visible, sitting inside the rows that had been written off, and registry-absent is ENTRY-RELATIVE rather than a fixed target: a reader who takes the number as stable has made the denominator error. THE 473 IS NOT A ROSTER OF UNFIXABLE ROWS. Wiring a diagnostic to it would still print a permanently-false report on every class-(a) row -- which is why the arm stays unwired even though part of the population is movable -- and the reason that is worse than shipping nothing is that IT TRAINS READERS TO IGNORE THE CHANNEL: a diagnostic nobody reads is worth less than an absent one, because the absent one is honest about its coverage. The variant stays declared because the class is real and its shape is settled; the trigger for wiring it is that class (a) leave the arm -- the candidate walk ceasing to propose target-language vocabulary -- after which the residue is class (b) and is exactly the closure-conditioned population worth reporting. A witness asserts zero diagnostics from that arm and fails the moment anyone wires it. THE TWO ARE SEPARATE DIAGNOSTICS RATHER THAN ONE WITH A CAUSE FIELD, because the question is upstream of severity: registry-absent is fixed by authoring an import, export-proof-failed by proving an export that already exists -- opposite remedies, different owners, different populations, different reachability -- and 4b files one row per class. That the two turned out to need different TREATMENT rather than merely different severity is the strongest evidence the split was right. A REFUTED PREDICTION, recorded rather than quietly repointed. Registry-absent was predicted to overlap heavily with UnlistedImportUse, both being described as 'referenced but never imported', and the prediction was made because closure membership is driven by imports. Measured on one run: 63 registry-absent names, 36 UnlistedImportUse names, INTERSECTION ZERO. UnlistedImportUse names .dag types masked at RESOLVE time; registry-absent is dominated by Rust tokens that never reached the resolver. The reasoning was wrong at the MECHANISM, not merely the magnitude. ProviderUnknown's trigger therefore does NOT point at the family-closure-SVN burndown as this carrier previously stated, and the falsification is recorded here rather than the trigger being silently repointed -- a repointed trigger would inherit the credibility of a prediction that just died, so whatever that trigger eventually becomes is argued fresh." data where_refinement_deferral_reason_scaffold_note: String = "owner: v1.compiler.infer (00_core diagnostic partition). lane: where-refinement literal-wall enforcement. interim: WhereRefinementUnenforced.reason is a closed-string sum enrolled in is_where_refinement_unenforced_advisory_reason; any unlisted reason fails closed blocking. bound: only the five deferral strings emitted by 04_infer today; classifier/eval/equivalence arms must change together until coproduct lands. dissolve-on: feature:where-refinement-predicate-coproduct (WhereRefinementDeferralReason coproduct on the diagnostic carrier)." From 13ea193fda4e02f6facf75e042422d2dfe6890e7 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 27 Aug 2026 20:52:18 +0000 Subject: [PATCH 16/21] Fix the carrier note's inner double quotes, which terminated the .dag string The previous commit's note embedded a quoted phrase inside a double-quoted string literal, so the parse ended mid-sentence and the module index refused the whole file. Caught by regen, five minutes into a remote build, at a byte offset 46420 that names the position and not the cause -- the error reads 'expected item declaration' because the parser was looking at prose it had fallen out of a string into. --- src/v1/00_core.dag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v1/00_core.dag b/src/v1/00_core.dag index b3a5353ae2e..0bad3a6829e 100644 --- a/src/v1/00_core.dag +++ b/src/v1/00_core.dag @@ -526,7 +526,7 @@ fn is_error_diagnostic(d: CompilerDiagnostic) -> Bool { } } -data reference_derived_import_refusal_severity_note: String = "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). TWO DIAGNOSTICS, ONE WIRED AND BLOCKING, ONE DECLARED AND PRODUCED BY NOTHING -- and which is which was decided by measurement, not by preference. ReferenceDerivedImportExportUnproven is emitted from CandidateExportProofFailed: the provider is present in the closure and its export could not be established, and the remedy is NOT an import -- the provider is already here -- but making the emitter able to prove an export it may already hold. IT IS ADVISORY, AND IT WAS BLOCKING FOR ONE CI RUN -- THE FLIP WAS APPROVED, EXECUTED, AND REFUTED BY ITS OWN FIRST EXECUTION OVER A SECOND CLOSURE, recorded here rather than repointed because a trigger that survives its own falsification inherits credibility a dead prediction no longer has. THE FLIP CONDITION WAS: an arm flips when its population over a NAMED CLOSURE is zero AND a discriminating RED is authorable. Both halves held and the conclusion was still wrong, which is the finding. The named closure was the regen seed closure (SeedV1 + DagCorpus), where the population is zero. The required build lane ALSO runs a v2-emission phase over a DIFFERENT closure -- entry:src/v2/compiler/00_compile.dag, 169 modules -- and there the population is EIGHTY-SEVEN, measured on run 33111325404 at b331e24cf7d, which refused the phase and reddened the required check. So the zero was a property of THE CLOSURE and not of the arm, and flipping globally on a single-closure measurement is the denominator error this very carrier already names one clause down for the sibling arm (registry-absent is ENTRY-RELATIVE rather than a fixed target). The same reasoning was available for this arm and was not applied to it. THE CORRECTED CONDITION, which is what the flip should have required: the population must be zero over EVERY closure a required phase compiles, not over one named closure -- a per-closure zero is a fact about that entry and licenses nothing about another. AND THE CLOSURES MUST BE NAMED HERE RATHER THAN DESCRIBED, because "every closure a required phase compiles" is a set that MOVES: it grew when #9035 added the v2-emission phase and again when that phase's subject was widened from dag/std/abi.dag to the v2 pipeline root, and it grows whenever a phase is added or an entry roster widens. A described set leaves that to whoever remembers; a named one makes a phase addition visibly re-open this question. AS OF 2026-08-27 THE SET IS THREE, each with its own authority: the regen seed closure (v1.compiler.cli_run regen_source_roots, SeedV1 + DagCorpus), the v2-emission entries (gunbc.ci_layer_roots required_v2_emission_entries), and the emit-compile entries (gunbc.ci_layer_roots required_emit_compile_entries). Only the first two have been measured for this arm -- 0 and 87 -- and the third is UNMEASURED, so even the corrected condition is not currently evaluable and any future flip must measure it rather than assume it follows the seed. This warning was delivered before the flip rather than after it: deep-ant-102 stated that the zero was 0 over that closure and not over all closures, and that with every provider selected in the proof gate is the easy case; it was acknowledged in writing and not carried into the condition. It is recorded as a dropped warning rather than as a missing insight, because the two have different remedies. The bounded sequence ruled for this work said count == 0 flips and count > 0 IS THE BURNDOWN ROSTER AT IDENTITY GRAIN; the terminal event has now fired with the second answer, so the 87 is that roster and the arm stays advisory until it burns down. The population is NOT a stable number and must not be quoted as one: it is 0 over the seed closure and 87 over the v2 compiler entry, and no closure has been enumerated beyond those two. THE DIAGNOSTIC IS STILL TYPED, LOCATED AND COUNTED, which is the whole of what the brief asked -- the silent omission is closed either way; severity decides whether the line stops, not whether the omission is visible. A sample of what it reports, and the reason 87 is a real roster rather than noise: std.determinism references std.perturbation.PerturbationVerdict whose provider is in the closure with its export unestablished, and v2.lens.complexity_accumulator_copy.analyze accounts for a large share of the rest against providers that are likewise present. Under the previous condition: an arm flips when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient, because an observation of zero on an arm that has never fired is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check cited as coverage, which 4b rates worse than absent. BOTH HALVES HELD AT THE TIME: the population is zero over the regen seed closure (claim_executor --required-regen --source-root dag --source-root src/v2, whose subject is regen_input_sources grown to a joint fixpoint over import, dotted-reference and bare-reference edges), and the RED is authorable and AUTHORED at the fixture boundary in v1.tests.claim.reference_derived_disposition_census_witness_test, which builds a CandidateExportProofFailed row and asserts the diagnostic. The emission still produces its files beside the diagnostic rather than returning none: production precedes adjudication, so the candidate tree survives the refusal and the refusal is what stops the line. ReferenceDerivedImportProviderUnknown is DECLARED HERE AND PRODUCED BY NOTHING. Its arm, CandidateRegistryAbsent, was instrumented and measured over the same closure and returned 473 rows over 63 names across 110 modules. THAT POPULATION IS MIXED, AND AN EARLIER REVISION OF THIS NOTE CALLED IT ALL TARGET VOCABULARY -- corrected 2026-08-27 after deep-ant-102 pushed back on the census SCOPE. Two classes are conflated inside it. (a) EXTINGUISHED -- Vec 88, bool 73, i64 48, BTreeSet: no .dag declaration exists anywhere and none can, because the PRODUCER is collect_item_realized_surface_names, which is rust_identifier_tokens over render_rust_type and so emits target-language SPELLINGS by construction (attribution corrected by clever-boar-140, who owns the coproduct: the candidate filter's emitted-source disjunct is an admission GATE over an already-proposed list and cannot itself propose anything, so narrowing it would delete genuine emitter-attested candidates and leave the real source untouched). reference_is_host_realized_builtin misses these because it is keyed on .dag vocabulary -- is_container_type reads std.types container_type_arity -- so Vec and BTreeSet, the RUST spellings of List and Set, pass a filter that exists precisely to remove host-realized names. (b) OUT-OF-CLOSURE -- empty_map 33 (v2.std.collection), Optional 25 (v2.std.optional), and its VARIANTS Present 47 and Absent 13: REAL .dag names whose providers exist and were not selected in. The variant half is not a detail: a consumer key that reads a declaration index's `declared` field alone reports no-provider-anywhere for every variant name in the corpus, because a variant is not a top-level declaration and such an index holds variants in a separate field -- so the key must read declared UNION variants, and two of these four names are the instance that proves it (clever-boar-140, who then settled it STRUCTURALLY rather than from this sample: build_item_info in v1.compiler.infer produces one ItemInfo per TOP-LEVEL ITEM and no arm descends into a coproduct's children, and the single item_registry insert is keyed on that name, so a variant name is absent from the registry under EVERY closure -- verified here against both sites). THAT FACT AND THIS ARM'S ORDERING COMPOSE, and the composition is why these two names appear under (b) rather than under (a): CandidateVariantDelegatedToParent is tested BEFORE the registry lookup, so a variant whose parent is IN closure is delegated and never reaches the registry at all, and the registry's structural inability to hold variants surfaces only when the parent is OUT of closure and type_summaries therefore cannot recognise the name as a variant. So Present and Absent are registry-absent HERE because Optional was out of closure, and would be delegated with it in -- which is exactly why this sample cannot discriminate the two readings, and why the structural argument above was needed instead. THE PROPORTION IS UNMEASURED IN BOTH DIRECTIONS, and the two ways of counting point OPPOSITE ways, which is the whole reason this population was misread twice. BY ROW the immovable names dominate: Vec 88 + bool 73 + Option 50 + i64 48 out of 473, each verified to have no .dag declaration anywhere. BY DISTINCT NAME the examined sample is 4 of 63 and ALL FOUR ARE MOVABLE. High-count immovable names at the head, movable names in the tail with small counts -- which is exactly the structure that hid the signal, and exactly why reading a sorted head answered the wrong question: empty_map's 33 rows were the third-largest count and were still classified as target vocabulary on the first pass. FIFTY-NINE NAMES ARE UNEXAMINED BY ANYONE. Neither the row-dominance nor the 4-of-4 may be quoted as a proportion; both are directional facts about different denominators. WHY (b) IS THERE AT ALL is a trap worth carrying: the census invocation passes --source-root dag --source-root src/v2, but the SUBJECT is regen_input_sources, whose roots are SeedV1 and DagCorpus and exclude src/v2 entirely -- stage0 IS the v1 seed, and a seed reaching into src/v2 would depend on the successor it bootstraps toward. The source-root FLAGS and the regen SUBJECT are not the same thing. So class (b) is precisely the closure-conditioned population this change exists to make visible, sitting inside the rows that had been written off, and registry-absent is ENTRY-RELATIVE rather than a fixed target: a reader who takes the number as stable has made the denominator error. THE 473 IS NOT A ROSTER OF UNFIXABLE ROWS. Wiring a diagnostic to it would still print a permanently-false report on every class-(a) row -- which is why the arm stays unwired even though part of the population is movable -- and the reason that is worse than shipping nothing is that IT TRAINS READERS TO IGNORE THE CHANNEL: a diagnostic nobody reads is worth less than an absent one, because the absent one is honest about its coverage. The variant stays declared because the class is real and its shape is settled; the trigger for wiring it is that class (a) leave the arm -- the candidate walk ceasing to propose target-language vocabulary -- after which the residue is class (b) and is exactly the closure-conditioned population worth reporting. A witness asserts zero diagnostics from that arm and fails the moment anyone wires it. THE TWO ARE SEPARATE DIAGNOSTICS RATHER THAN ONE WITH A CAUSE FIELD, because the question is upstream of severity: registry-absent is fixed by authoring an import, export-proof-failed by proving an export that already exists -- opposite remedies, different owners, different populations, different reachability -- and 4b files one row per class. That the two turned out to need different TREATMENT rather than merely different severity is the strongest evidence the split was right. A REFUTED PREDICTION, recorded rather than quietly repointed. Registry-absent was predicted to overlap heavily with UnlistedImportUse, both being described as 'referenced but never imported', and the prediction was made because closure membership is driven by imports. Measured on one run: 63 registry-absent names, 36 UnlistedImportUse names, INTERSECTION ZERO. UnlistedImportUse names .dag types masked at RESOLVE time; registry-absent is dominated by Rust tokens that never reached the resolver. The reasoning was wrong at the MECHANISM, not merely the magnitude. ProviderUnknown's trigger therefore does NOT point at the family-closure-SVN burndown as this carrier previously stated, and the falsification is recorded here rather than the trigger being silently repointed -- a repointed trigger would inherit the credibility of a prediction that just died, so whatever that trigger eventually becomes is argued fresh." +data reference_derived_import_refusal_severity_note: String = "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). TWO DIAGNOSTICS, ONE WIRED AND BLOCKING, ONE DECLARED AND PRODUCED BY NOTHING -- and which is which was decided by measurement, not by preference. ReferenceDerivedImportExportUnproven is emitted from CandidateExportProofFailed: the provider is present in the closure and its export could not be established, and the remedy is NOT an import -- the provider is already here -- but making the emitter able to prove an export it may already hold. IT IS ADVISORY, AND IT WAS BLOCKING FOR ONE CI RUN -- THE FLIP WAS APPROVED, EXECUTED, AND REFUTED BY ITS OWN FIRST EXECUTION OVER A SECOND CLOSURE, recorded here rather than repointed because a trigger that survives its own falsification inherits credibility a dead prediction no longer has. THE FLIP CONDITION WAS: an arm flips when its population over a NAMED CLOSURE is zero AND a discriminating RED is authorable. Both halves held and the conclusion was still wrong, which is the finding. The named closure was the regen seed closure (SeedV1 + DagCorpus), where the population is zero. The required build lane ALSO runs a v2-emission phase over a DIFFERENT closure -- entry:src/v2/compiler/00_compile.dag, 169 modules -- and there the population is EIGHTY-SEVEN, measured on run 33111325404 at b331e24cf7d, which refused the phase and reddened the required check. So the zero was a property of THE CLOSURE and not of the arm, and flipping globally on a single-closure measurement is the denominator error this very carrier already names one clause down for the sibling arm (registry-absent is ENTRY-RELATIVE rather than a fixed target). The same reasoning was available for this arm and was not applied to it. THE CORRECTED CONDITION, which is what the flip should have required: the population must be zero over EVERY closure a required phase compiles, not over one named closure -- a per-closure zero is a fact about that entry and licenses nothing about another. AND THE CLOSURES MUST BE NAMED HERE RATHER THAN DESCRIBED, because the phrase every-closure-a-required-phase-compiles names a set that MOVES: it grew when #9035 added the v2-emission phase and again when that phase's subject was widened from dag/std/abi.dag to the v2 pipeline root, and it grows whenever a phase is added or an entry roster widens. A described set leaves that to whoever remembers; a named one makes a phase addition visibly re-open this question. AS OF 2026-08-27 THE SET IS THREE, each with its own authority: the regen seed closure (v1.compiler.cli_run regen_source_roots, SeedV1 + DagCorpus), the v2-emission entries (gunbc.ci_layer_roots required_v2_emission_entries), and the emit-compile entries (gunbc.ci_layer_roots required_emit_compile_entries). Only the first two have been measured for this arm -- 0 and 87 -- and the third is UNMEASURED, so even the corrected condition is not currently evaluable and any future flip must measure it rather than assume it follows the seed. This warning was delivered before the flip rather than after it: deep-ant-102 stated that the zero was 0 over that closure and not over all closures, and that with every provider selected in the proof gate is the easy case; it was acknowledged in writing and not carried into the condition. It is recorded as a dropped warning rather than as a missing insight, because the two have different remedies. The bounded sequence ruled for this work said count == 0 flips and count > 0 IS THE BURNDOWN ROSTER AT IDENTITY GRAIN; the terminal event has now fired with the second answer, so the 87 is that roster and the arm stays advisory until it burns down. The population is NOT a stable number and must not be quoted as one: it is 0 over the seed closure and 87 over the v2 compiler entry, and no closure has been enumerated beyond those two. THE DIAGNOSTIC IS STILL TYPED, LOCATED AND COUNTED, which is the whole of what the brief asked -- the silent omission is closed either way; severity decides whether the line stops, not whether the omission is visible. A sample of what it reports, and the reason 87 is a real roster rather than noise: std.determinism references std.perturbation.PerturbationVerdict whose provider is in the closure with its export unestablished, and v2.lens.complexity_accumulator_copy.analyze accounts for a large share of the rest against providers that are likewise present. Under the previous condition: an arm flips when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient, because an observation of zero on an arm that has never fired is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check cited as coverage, which 4b rates worse than absent. BOTH HALVES HELD AT THE TIME: the population is zero over the regen seed closure (claim_executor --required-regen --source-root dag --source-root src/v2, whose subject is regen_input_sources grown to a joint fixpoint over import, dotted-reference and bare-reference edges), and the RED is authorable and AUTHORED at the fixture boundary in v1.tests.claim.reference_derived_disposition_census_witness_test, which builds a CandidateExportProofFailed row and asserts the diagnostic. The emission still produces its files beside the diagnostic rather than returning none: production precedes adjudication, so the candidate tree survives the refusal and the refusal is what stops the line. ReferenceDerivedImportProviderUnknown is DECLARED HERE AND PRODUCED BY NOTHING. Its arm, CandidateRegistryAbsent, was instrumented and measured over the same closure and returned 473 rows over 63 names across 110 modules. THAT POPULATION IS MIXED, AND AN EARLIER REVISION OF THIS NOTE CALLED IT ALL TARGET VOCABULARY -- corrected 2026-08-27 after deep-ant-102 pushed back on the census SCOPE. Two classes are conflated inside it. (a) EXTINGUISHED -- Vec 88, bool 73, i64 48, BTreeSet: no .dag declaration exists anywhere and none can, because the PRODUCER is collect_item_realized_surface_names, which is rust_identifier_tokens over render_rust_type and so emits target-language SPELLINGS by construction (attribution corrected by clever-boar-140, who owns the coproduct: the candidate filter's emitted-source disjunct is an admission GATE over an already-proposed list and cannot itself propose anything, so narrowing it would delete genuine emitter-attested candidates and leave the real source untouched). reference_is_host_realized_builtin misses these because it is keyed on .dag vocabulary -- is_container_type reads std.types container_type_arity -- so Vec and BTreeSet, the RUST spellings of List and Set, pass a filter that exists precisely to remove host-realized names. (b) OUT-OF-CLOSURE -- empty_map 33 (v2.std.collection), Optional 25 (v2.std.optional), and its VARIANTS Present 47 and Absent 13: REAL .dag names whose providers exist and were not selected in. The variant half is not a detail: a consumer key that reads a declaration index's `declared` field alone reports no-provider-anywhere for every variant name in the corpus, because a variant is not a top-level declaration and such an index holds variants in a separate field -- so the key must read declared UNION variants, and two of these four names are the instance that proves it (clever-boar-140, who then settled it STRUCTURALLY rather than from this sample: build_item_info in v1.compiler.infer produces one ItemInfo per TOP-LEVEL ITEM and no arm descends into a coproduct's children, and the single item_registry insert is keyed on that name, so a variant name is absent from the registry under EVERY closure -- verified here against both sites). THAT FACT AND THIS ARM'S ORDERING COMPOSE, and the composition is why these two names appear under (b) rather than under (a): CandidateVariantDelegatedToParent is tested BEFORE the registry lookup, so a variant whose parent is IN closure is delegated and never reaches the registry at all, and the registry's structural inability to hold variants surfaces only when the parent is OUT of closure and type_summaries therefore cannot recognise the name as a variant. So Present and Absent are registry-absent HERE because Optional was out of closure, and would be delegated with it in -- which is exactly why this sample cannot discriminate the two readings, and why the structural argument above was needed instead. THE PROPORTION IS UNMEASURED IN BOTH DIRECTIONS, and the two ways of counting point OPPOSITE ways, which is the whole reason this population was misread twice. BY ROW the immovable names dominate: Vec 88 + bool 73 + Option 50 + i64 48 out of 473, each verified to have no .dag declaration anywhere. BY DISTINCT NAME the examined sample is 4 of 63 and ALL FOUR ARE MOVABLE. High-count immovable names at the head, movable names in the tail with small counts -- which is exactly the structure that hid the signal, and exactly why reading a sorted head answered the wrong question: empty_map's 33 rows were the third-largest count and were still classified as target vocabulary on the first pass. FIFTY-NINE NAMES ARE UNEXAMINED BY ANYONE. Neither the row-dominance nor the 4-of-4 may be quoted as a proportion; both are directional facts about different denominators. WHY (b) IS THERE AT ALL is a trap worth carrying: the census invocation passes --source-root dag --source-root src/v2, but the SUBJECT is regen_input_sources, whose roots are SeedV1 and DagCorpus and exclude src/v2 entirely -- stage0 IS the v1 seed, and a seed reaching into src/v2 would depend on the successor it bootstraps toward. The source-root FLAGS and the regen SUBJECT are not the same thing. So class (b) is precisely the closure-conditioned population this change exists to make visible, sitting inside the rows that had been written off, and registry-absent is ENTRY-RELATIVE rather than a fixed target: a reader who takes the number as stable has made the denominator error. THE 473 IS NOT A ROSTER OF UNFIXABLE ROWS. Wiring a diagnostic to it would still print a permanently-false report on every class-(a) row -- which is why the arm stays unwired even though part of the population is movable -- and the reason that is worse than shipping nothing is that IT TRAINS READERS TO IGNORE THE CHANNEL: a diagnostic nobody reads is worth less than an absent one, because the absent one is honest about its coverage. The variant stays declared because the class is real and its shape is settled; the trigger for wiring it is that class (a) leave the arm -- the candidate walk ceasing to propose target-language vocabulary -- after which the residue is class (b) and is exactly the closure-conditioned population worth reporting. A witness asserts zero diagnostics from that arm and fails the moment anyone wires it. THE TWO ARE SEPARATE DIAGNOSTICS RATHER THAN ONE WITH A CAUSE FIELD, because the question is upstream of severity: registry-absent is fixed by authoring an import, export-proof-failed by proving an export that already exists -- opposite remedies, different owners, different populations, different reachability -- and 4b files one row per class. That the two turned out to need different TREATMENT rather than merely different severity is the strongest evidence the split was right. A REFUTED PREDICTION, recorded rather than quietly repointed. Registry-absent was predicted to overlap heavily with UnlistedImportUse, both being described as 'referenced but never imported', and the prediction was made because closure membership is driven by imports. Measured on one run: 63 registry-absent names, 36 UnlistedImportUse names, INTERSECTION ZERO. UnlistedImportUse names .dag types masked at RESOLVE time; registry-absent is dominated by Rust tokens that never reached the resolver. The reasoning was wrong at the MECHANISM, not merely the magnitude. ProviderUnknown's trigger therefore does NOT point at the family-closure-SVN burndown as this carrier previously stated, and the falsification is recorded here rather than the trigger being silently repointed -- a repointed trigger would inherit the credibility of a prediction that just died, so whatever that trigger eventually becomes is argued fresh." data where_refinement_deferral_reason_scaffold_note: String = "owner: v1.compiler.infer (00_core diagnostic partition). lane: where-refinement literal-wall enforcement. interim: WhereRefinementUnenforced.reason is a closed-string sum enrolled in is_where_refinement_unenforced_advisory_reason; any unlisted reason fails closed blocking. bound: only the five deferral strings emitted by 04_infer today; classifier/eval/equivalence arms must change together until coproduct lands. dissolve-on: feature:where-refinement-predicate-coproduct (WhereRefinementDeferralReason coproduct on the diagnostic carrier)." From a3ba9c1f25d4fad61ed9a9ccf9eb0d6acdbf13f7 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 27 Aug 2026 21:16:30 +0000 Subject: [PATCH 17/21] Regenerate the mirrors for the advisory demotion Drift is exactly the two files the change touches: v1_std_core.rs carries the three severity classifiers gaining a ReferenceDerivedImportExportUnproven arm, and the witness mirror carries the _is_blocking -> _is_advisory rename. v1_compiler_emit_rust.rs correctly does not move -- the demotion is a 00_core severity fact, not an emitter one. Emitted by generation 0 from 13ea193fda4; the run reports first_generation_equal=true after installing the candidate. --- src/v1/stage0/src/v1_std_core.rs | 5 ++++- ...laim_reference_derived_disposition_census_witness_test.rs | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/v1/stage0/src/v1_std_core.rs b/src/v1/stage0/src/v1_std_core.rs index 8e5f970200b..8de8771e911 100644 --- a/src/v1/stage0/src/v1_std_core.rs +++ b/src/v1/stage0/src/v1_std_core.rs @@ -878,6 +878,7 @@ pub fn is_error_diagnostic(d: Rc) -> bool { match (*d.clone()).clone() { CompilerDiagnostic::UnlistedImportUse { .. } => false, CompilerDiagnostic::ReferenceDerivedImportProviderUnknown { .. } => false, + CompilerDiagnostic::ReferenceDerivedImportExportUnproven { .. } => false, CompilerDiagnostic::UnlistedVariantValueUse { .. } => false, CompilerDiagnostic::MethodExistenceFrontierAdmitted { .. } => false, CompilerDiagnostic::ReceiverTypeUnestablished { .. } => false, @@ -891,7 +892,7 @@ pub fn is_error_diagnostic(d: Rc) -> bool { pub fn reference_derived_import_refusal_severity_note() -> String { thread_local! { static CACHED: String = { - "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). TWO DIAGNOSTICS, ONE WIRED AND BLOCKING, ONE DECLARED AND PRODUCED BY NOTHING -- and which is which was decided by measurement, not by preference. ReferenceDerivedImportExportUnproven is emitted from CandidateExportProofFailed: the provider is present in the closure and its export could not be established, and the remedy is NOT an import -- the provider is already here -- but making the emitter able to prove an export it may already hold. IT IS BLOCKING (warm-hawk-909, compiler direction, 2026-08-27), under a flip condition strictly stronger than a count: an arm flips when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient, because an observation of zero on an arm that has never fired is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check cited as coverage, which 4b rates worse than absent. BOTH HALVES HOLD HERE: the population is zero over the regen seed closure (claim_executor --required-regen --source-root dag --source-root src/v2, whose subject is regen_input_sources grown to a joint fixpoint over import, dotted-reference and bare-reference edges), and the RED is authorable and AUTHORED at the fixture boundary in v1.tests.claim.reference_derived_disposition_census_witness_test, which builds a CandidateExportProofFailed row and asserts the diagnostic. The emission still produces its files beside a blocking diagnostic rather than returning none: production precedes adjudication, so the candidate tree survives the refusal and the refusal is what stops the line. ReferenceDerivedImportProviderUnknown is DECLARED HERE AND PRODUCED BY NOTHING. Its arm, CandidateRegistryAbsent, was instrumented and measured over the same closure and returned 473 rows over 63 names across 110 modules. THAT POPULATION IS MIXED, AND AN EARLIER REVISION OF THIS NOTE CALLED IT ALL TARGET VOCABULARY -- corrected 2026-08-27 after deep-ant-102 pushed back on the census SCOPE. Two classes are conflated inside it. (a) EXTINGUISHED -- Vec 88, bool 73, i64 48, BTreeSet: no .dag declaration exists anywhere and none can, because the PRODUCER is collect_item_realized_surface_names, which is rust_identifier_tokens over render_rust_type and so emits target-language SPELLINGS by construction (attribution corrected by clever-boar-140, who owns the coproduct: the candidate filter's emitted-source disjunct is an admission GATE over an already-proposed list and cannot itself propose anything, so narrowing it would delete genuine emitter-attested candidates and leave the real source untouched). reference_is_host_realized_builtin misses these because it is keyed on .dag vocabulary -- is_container_type reads std.types container_type_arity -- so Vec and BTreeSet, the RUST spellings of List and Set, pass a filter that exists precisely to remove host-realized names. (b) OUT-OF-CLOSURE -- empty_map 33 (v2.std.collection), Optional 25 (v2.std.optional), and its VARIANTS Present 47 and Absent 13: REAL .dag names whose providers exist and were not selected in. The variant half is not a detail: a consumer key that reads a declaration index's `declared` field alone reports no-provider-anywhere for every variant name in the corpus, because a variant is not a top-level declaration and such an index holds variants in a separate field -- so the key must read declared UNION variants, and two of these four names are the instance that proves it (clever-boar-140, who then settled it STRUCTURALLY rather than from this sample: build_item_info in v1.compiler.infer produces one ItemInfo per TOP-LEVEL ITEM and no arm descends into a coproduct's children, and the single item_registry insert is keyed on that name, so a variant name is absent from the registry under EVERY closure -- verified here against both sites). THAT FACT AND THIS ARM'S ORDERING COMPOSE, and the composition is why these two names appear under (b) rather than under (a): CandidateVariantDelegatedToParent is tested BEFORE the registry lookup, so a variant whose parent is IN closure is delegated and never reaches the registry at all, and the registry's structural inability to hold variants surfaces only when the parent is OUT of closure and type_summaries therefore cannot recognise the name as a variant. So Present and Absent are registry-absent HERE because Optional was out of closure, and would be delegated with it in -- which is exactly why this sample cannot discriminate the two readings, and why the structural argument above was needed instead. THE PROPORTION IS UNMEASURED IN BOTH DIRECTIONS, and the two ways of counting point OPPOSITE ways, which is the whole reason this population was misread twice. BY ROW the immovable names dominate: Vec 88 + bool 73 + Option 50 + i64 48 out of 473, each verified to have no .dag declaration anywhere. BY DISTINCT NAME the examined sample is 4 of 63 and ALL FOUR ARE MOVABLE. High-count immovable names at the head, movable names in the tail with small counts -- which is exactly the structure that hid the signal, and exactly why reading a sorted head answered the wrong question: empty_map's 33 rows were the third-largest count and were still classified as target vocabulary on the first pass. FIFTY-NINE NAMES ARE UNEXAMINED BY ANYONE. Neither the row-dominance nor the 4-of-4 may be quoted as a proportion; both are directional facts about different denominators. WHY (b) IS THERE AT ALL is a trap worth carrying: the census invocation passes --source-root dag --source-root src/v2, but the SUBJECT is regen_input_sources, whose roots are SeedV1 and DagCorpus and exclude src/v2 entirely -- stage0 IS the v1 seed, and a seed reaching into src/v2 would depend on the successor it bootstraps toward. The source-root FLAGS and the regen SUBJECT are not the same thing. So class (b) is precisely the closure-conditioned population this change exists to make visible, sitting inside the rows that had been written off, and registry-absent is ENTRY-RELATIVE rather than a fixed target: a reader who takes the number as stable has made the denominator error. THE 473 IS NOT A ROSTER OF UNFIXABLE ROWS. Wiring a diagnostic to it would still print a permanently-false report on every class-(a) row -- which is why the arm stays unwired even though part of the population is movable -- and the reason that is worse than shipping nothing is that IT TRAINS READERS TO IGNORE THE CHANNEL: a diagnostic nobody reads is worth less than an absent one, because the absent one is honest about its coverage. The variant stays declared because the class is real and its shape is settled; the trigger for wiring it is that class (a) leave the arm -- the candidate walk ceasing to propose target-language vocabulary -- after which the residue is class (b) and is exactly the closure-conditioned population worth reporting. A witness asserts zero diagnostics from that arm and fails the moment anyone wires it. THE TWO ARE SEPARATE DIAGNOSTICS RATHER THAN ONE WITH A CAUSE FIELD, because the question is upstream of severity: registry-absent is fixed by authoring an import, export-proof-failed by proving an export that already exists -- opposite remedies, different owners, different populations, different reachability -- and 4b files one row per class. That the two turned out to need different TREATMENT rather than merely different severity is the strongest evidence the split was right. A REFUTED PREDICTION, recorded rather than quietly repointed. Registry-absent was predicted to overlap heavily with UnlistedImportUse, both being described as 'referenced but never imported', and the prediction was made because closure membership is driven by imports. Measured on one run: 63 registry-absent names, 36 UnlistedImportUse names, INTERSECTION ZERO. UnlistedImportUse names .dag types masked at RESOLVE time; registry-absent is dominated by Rust tokens that never reached the resolver. The reasoning was wrong at the MECHANISM, not merely the magnitude. ProviderUnknown's trigger therefore does NOT point at the family-closure-SVN burndown as this carrier previously stated, and the falsification is recorded here rather than the trigger being silently repointed -- a repointed trigger would inherit the credibility of a prediction that just died, so whatever that trigger eventually becomes is argued fresh.".to_string() + "owner: v1.compiler.emit_rust (reference_derived_use_line_plan's per-candidate disposition). TWO DIAGNOSTICS, ONE WIRED AND BLOCKING, ONE DECLARED AND PRODUCED BY NOTHING -- and which is which was decided by measurement, not by preference. ReferenceDerivedImportExportUnproven is emitted from CandidateExportProofFailed: the provider is present in the closure and its export could not be established, and the remedy is NOT an import -- the provider is already here -- but making the emitter able to prove an export it may already hold. IT IS ADVISORY, AND IT WAS BLOCKING FOR ONE CI RUN -- THE FLIP WAS APPROVED, EXECUTED, AND REFUTED BY ITS OWN FIRST EXECUTION OVER A SECOND CLOSURE, recorded here rather than repointed because a trigger that survives its own falsification inherits credibility a dead prediction no longer has. THE FLIP CONDITION WAS: an arm flips when its population over a NAMED CLOSURE is zero AND a discriminating RED is authorable. Both halves held and the conclusion was still wrong, which is the finding. The named closure was the regen seed closure (SeedV1 + DagCorpus), where the population is zero. The required build lane ALSO runs a v2-emission phase over a DIFFERENT closure -- entry:src/v2/compiler/00_compile.dag, 169 modules -- and there the population is EIGHTY-SEVEN, measured on run 33111325404 at b331e24cf7d, which refused the phase and reddened the required check. So the zero was a property of THE CLOSURE and not of the arm, and flipping globally on a single-closure measurement is the denominator error this very carrier already names one clause down for the sibling arm (registry-absent is ENTRY-RELATIVE rather than a fixed target). The same reasoning was available for this arm and was not applied to it. THE CORRECTED CONDITION, which is what the flip should have required: the population must be zero over EVERY closure a required phase compiles, not over one named closure -- a per-closure zero is a fact about that entry and licenses nothing about another. AND THE CLOSURES MUST BE NAMED HERE RATHER THAN DESCRIBED, because the phrase every-closure-a-required-phase-compiles names a set that MOVES: it grew when #9035 added the v2-emission phase and again when that phase's subject was widened from dag/std/abi.dag to the v2 pipeline root, and it grows whenever a phase is added or an entry roster widens. A described set leaves that to whoever remembers; a named one makes a phase addition visibly re-open this question. AS OF 2026-08-27 THE SET IS THREE, each with its own authority: the regen seed closure (v1.compiler.cli_run regen_source_roots, SeedV1 + DagCorpus), the v2-emission entries (gunbc.ci_layer_roots required_v2_emission_entries), and the emit-compile entries (gunbc.ci_layer_roots required_emit_compile_entries). Only the first two have been measured for this arm -- 0 and 87 -- and the third is UNMEASURED, so even the corrected condition is not currently evaluable and any future flip must measure it rather than assume it follows the seed. This warning was delivered before the flip rather than after it: deep-ant-102 stated that the zero was 0 over that closure and not over all closures, and that with every provider selected in the proof gate is the easy case; it was acknowledged in writing and not carried into the condition. It is recorded as a dropped warning rather than as a missing insight, because the two have different remedies. The bounded sequence ruled for this work said count == 0 flips and count > 0 IS THE BURNDOWN ROSTER AT IDENTITY GRAIN; the terminal event has now fired with the second answer, so the 87 is that roster and the arm stays advisory until it burns down. The population is NOT a stable number and must not be quoted as one: it is 0 over the seed closure and 87 over the v2 compiler entry, and no closure has been enumerated beyond those two. THE DIAGNOSTIC IS STILL TYPED, LOCATED AND COUNTED, which is the whole of what the brief asked -- the silent omission is closed either way; severity decides whether the line stops, not whether the omission is visible. A sample of what it reports, and the reason 87 is a real roster rather than noise: std.determinism references std.perturbation.PerturbationVerdict whose provider is in the closure with its export unestablished, and v2.lens.complexity_accumulator_copy.analyze accounts for a large share of the rest against providers that are likewise present. Under the previous condition: an arm flips when its population over a NAMED CLOSURE is zero AND A DISCRIMINATING RED IS AUTHORABLE. A zero alone is never sufficient, because an observation of zero on an arm that has never fired is indistinguishable in a count from an arm that CANNOT fire, and flipping the latter yields a permanently-green check cited as coverage, which 4b rates worse than absent. BOTH HALVES HELD AT THE TIME: the population is zero over the regen seed closure (claim_executor --required-regen --source-root dag --source-root src/v2, whose subject is regen_input_sources grown to a joint fixpoint over import, dotted-reference and bare-reference edges), and the RED is authorable and AUTHORED at the fixture boundary in v1.tests.claim.reference_derived_disposition_census_witness_test, which builds a CandidateExportProofFailed row and asserts the diagnostic. The emission still produces its files beside the diagnostic rather than returning none: production precedes adjudication, so the candidate tree survives the refusal and the refusal is what stops the line. ReferenceDerivedImportProviderUnknown is DECLARED HERE AND PRODUCED BY NOTHING. Its arm, CandidateRegistryAbsent, was instrumented and measured over the same closure and returned 473 rows over 63 names across 110 modules. THAT POPULATION IS MIXED, AND AN EARLIER REVISION OF THIS NOTE CALLED IT ALL TARGET VOCABULARY -- corrected 2026-08-27 after deep-ant-102 pushed back on the census SCOPE. Two classes are conflated inside it. (a) EXTINGUISHED -- Vec 88, bool 73, i64 48, BTreeSet: no .dag declaration exists anywhere and none can, because the PRODUCER is collect_item_realized_surface_names, which is rust_identifier_tokens over render_rust_type and so emits target-language SPELLINGS by construction (attribution corrected by clever-boar-140, who owns the coproduct: the candidate filter's emitted-source disjunct is an admission GATE over an already-proposed list and cannot itself propose anything, so narrowing it would delete genuine emitter-attested candidates and leave the real source untouched). reference_is_host_realized_builtin misses these because it is keyed on .dag vocabulary -- is_container_type reads std.types container_type_arity -- so Vec and BTreeSet, the RUST spellings of List and Set, pass a filter that exists precisely to remove host-realized names. (b) OUT-OF-CLOSURE -- empty_map 33 (v2.std.collection), Optional 25 (v2.std.optional), and its VARIANTS Present 47 and Absent 13: REAL .dag names whose providers exist and were not selected in. The variant half is not a detail: a consumer key that reads a declaration index's `declared` field alone reports no-provider-anywhere for every variant name in the corpus, because a variant is not a top-level declaration and such an index holds variants in a separate field -- so the key must read declared UNION variants, and two of these four names are the instance that proves it (clever-boar-140, who then settled it STRUCTURALLY rather than from this sample: build_item_info in v1.compiler.infer produces one ItemInfo per TOP-LEVEL ITEM and no arm descends into a coproduct's children, and the single item_registry insert is keyed on that name, so a variant name is absent from the registry under EVERY closure -- verified here against both sites). THAT FACT AND THIS ARM'S ORDERING COMPOSE, and the composition is why these two names appear under (b) rather than under (a): CandidateVariantDelegatedToParent is tested BEFORE the registry lookup, so a variant whose parent is IN closure is delegated and never reaches the registry at all, and the registry's structural inability to hold variants surfaces only when the parent is OUT of closure and type_summaries therefore cannot recognise the name as a variant. So Present and Absent are registry-absent HERE because Optional was out of closure, and would be delegated with it in -- which is exactly why this sample cannot discriminate the two readings, and why the structural argument above was needed instead. THE PROPORTION IS UNMEASURED IN BOTH DIRECTIONS, and the two ways of counting point OPPOSITE ways, which is the whole reason this population was misread twice. BY ROW the immovable names dominate: Vec 88 + bool 73 + Option 50 + i64 48 out of 473, each verified to have no .dag declaration anywhere. BY DISTINCT NAME the examined sample is 4 of 63 and ALL FOUR ARE MOVABLE. High-count immovable names at the head, movable names in the tail with small counts -- which is exactly the structure that hid the signal, and exactly why reading a sorted head answered the wrong question: empty_map's 33 rows were the third-largest count and were still classified as target vocabulary on the first pass. FIFTY-NINE NAMES ARE UNEXAMINED BY ANYONE. Neither the row-dominance nor the 4-of-4 may be quoted as a proportion; both are directional facts about different denominators. WHY (b) IS THERE AT ALL is a trap worth carrying: the census invocation passes --source-root dag --source-root src/v2, but the SUBJECT is regen_input_sources, whose roots are SeedV1 and DagCorpus and exclude src/v2 entirely -- stage0 IS the v1 seed, and a seed reaching into src/v2 would depend on the successor it bootstraps toward. The source-root FLAGS and the regen SUBJECT are not the same thing. So class (b) is precisely the closure-conditioned population this change exists to make visible, sitting inside the rows that had been written off, and registry-absent is ENTRY-RELATIVE rather than a fixed target: a reader who takes the number as stable has made the denominator error. THE 473 IS NOT A ROSTER OF UNFIXABLE ROWS. Wiring a diagnostic to it would still print a permanently-false report on every class-(a) row -- which is why the arm stays unwired even though part of the population is movable -- and the reason that is worse than shipping nothing is that IT TRAINS READERS TO IGNORE THE CHANNEL: a diagnostic nobody reads is worth less than an absent one, because the absent one is honest about its coverage. The variant stays declared because the class is real and its shape is settled; the trigger for wiring it is that class (a) leave the arm -- the candidate walk ceasing to propose target-language vocabulary -- after which the residue is class (b) and is exactly the closure-conditioned population worth reporting. A witness asserts zero diagnostics from that arm and fails the moment anyone wires it. THE TWO ARE SEPARATE DIAGNOSTICS RATHER THAN ONE WITH A CAUSE FIELD, because the question is upstream of severity: registry-absent is fixed by authoring an import, export-proof-failed by proving an export that already exists -- opposite remedies, different owners, different populations, different reachability -- and 4b files one row per class. That the two turned out to need different TREATMENT rather than merely different severity is the strongest evidence the split was right. A REFUTED PREDICTION, recorded rather than quietly repointed. Registry-absent was predicted to overlap heavily with UnlistedImportUse, both being described as 'referenced but never imported', and the prediction was made because closure membership is driven by imports. Measured on one run: 63 registry-absent names, 36 UnlistedImportUse names, INTERSECTION ZERO. UnlistedImportUse names .dag types masked at RESOLVE time; registry-absent is dominated by Rust tokens that never reached the resolver. The reasoning was wrong at the MECHANISM, not merely the magnitude. ProviderUnknown's trigger therefore does NOT point at the family-closure-SVN burndown as this carrier previously stated, and the falsification is recorded here rather than the trigger being silently repointed -- a repointed trigger would inherit the credibility of a prediction that just died, so whatever that trigger eventually becomes is argued fresh.".to_string() }; } CACHED.with(|c: &String| c.clone()) @@ -919,6 +920,7 @@ pub fn is_interpreter_blocking_diagnostic(d: Rc) -> bool { CompilerDiagnostic::ServiceConfigReferenceJudgmentDeferred { .. } => false, CompilerDiagnostic::DeclaredTypeInhabitanceUndecided { .. } => false, CompilerDiagnostic::ReferenceDerivedImportProviderUnknown { .. } => false, + CompilerDiagnostic::ReferenceDerivedImportExportUnproven { .. } => false, _ => true, } } @@ -932,6 +934,7 @@ pub fn is_discovery_corpus_advisory_typecheck_diagnostic(d: Rc true, CompilerDiagnostic::DeclaredTypeInhabitanceUndecided { .. } => true, CompilerDiagnostic::ReferenceDerivedImportProviderUnknown { .. } => true, + CompilerDiagnostic::ReferenceDerivedImportExportUnproven { .. } => true, CompilerDiagnostic::WhereRefinementUnenforced { reason: r, .. } => { is_where_refinement_unenforced_advisory_reason(r.clone()) } diff --git a/src/v1/stage0/src/v1_tests_claim_reference_derived_disposition_census_witness_test.rs b/src/v1/stage0/src/v1_tests_claim_reference_derived_disposition_census_witness_test.rs index c590f65c74d..e8732b148a8 100644 --- a/src/v1/stage0/src/v1_tests_claim_reference_derived_disposition_census_witness_test.rs +++ b/src/v1/stage0/src/v1_tests_claim_reference_derived_disposition_census_witness_test.rs @@ -349,7 +349,7 @@ pub fn only_export_proof_failed_produces_a_diagnostic() -> bool { } } -pub fn the_export_unproven_diagnostic_is_blocking() -> bool { +pub fn the_export_unproven_diagnostic_is_advisory() -> bool { { let rows = Rc::new(vec![Rc::new(ReferenceDerivedCandidateRow { module_name: "m".to_string(), @@ -366,7 +366,7 @@ pub fn the_export_unproven_diagnostic_is_blocking() -> bool { .iter() .cloned() { - if !(is_error_diagnostic(e.diagnostic.clone())) { + if !(!is_error_diagnostic(e.diagnostic.clone())) { __all = false; break; } From f360e2bcea192f669b005fd8d0c0e30d4146fc0e Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 28 Aug 2026 00:07:47 +0000 Subject: [PATCH 18/21] Re-apply the two diagnostic arms onto main's cli_run.rs instead of restoring a pre-merge copy The previous commit restored cli_run.rs wholesale from the snapshot taken before stripping the arms for gen-0. That snapshot predates main's change to record_from_module, which gained an &Rc parameter, so restoring the whole file silently reverted main's edit to a file that had auto-merged cleanly. rustc caught it as E0061. The arms are a two-hunk addition, not a file. Re-applied onto main's version beside their sibling UnlistedVariantValueUse arms. --- src/v1/stage0/src/cli_run.rs | 288 ++++++++++++++++++++++++++++++++--- 1 file changed, 265 insertions(+), 23 deletions(-) diff --git a/src/v1/stage0/src/cli_run.rs b/src/v1/stage0/src/cli_run.rs index f54f7361ab4..1ce72108082 100644 --- a/src/v1/stage0/src/cli_run.rs +++ b/src/v1/stage0/src/cli_run.rs @@ -1932,6 +1932,73 @@ pub enum CompileDiagnosticCensus { NotRunnable(String), } +thread_local! { + static COMPILE_DAG_DIAGNOSTIC_CENSUS_MEMO: std::cell::RefCell< + std::collections::HashMap, + > = std::cell::RefCell::new(std::collections::HashMap::new()); +} + +static COMPILE_DAG_DIAGNOSTIC_CENSUS_MEMO_HITS: std::sync::atomic::AtomicU64 = + std::sync::atomic::AtomicU64::new(0); +static COMPILE_DAG_DIAGNOSTIC_CENSUS_MEMO_MISSES: std::sync::atomic::AtomicU64 = + std::sync::atomic::AtomicU64::new(0); + +/// `(hits, misses)` for the `compile_dag_diagnostic_census` memo across the whole process. +/// Report-only; no consumer branches on it. Same accounting shape, and the same reason for it, +/// as [`compile_dag_rust_emit_check_memo_counts`]: a hit count with no denominator is not a +/// measurement. +pub fn compile_dag_diagnostic_census_memo_counts() -> (u64, u64) { + ( + COMPILE_DAG_DIAGNOSTIC_CENSUS_MEMO_HITS.load(std::sync::atomic::Ordering::Relaxed), + COMPILE_DAG_DIAGNOSTIC_CENSUS_MEMO_MISSES.load(std::sync::atomic::Ordering::Relaxed), + ) +} + +/// Memoized entry point for the census, mirroring [`compile_dag_rust_emit_check`]'s memo rather +/// than inventing a second caching discipline beside it (DESIGN §3 — the two builtins compile the +/// same program through the same pipeline and differ only in what they report, so they must not +/// disagree about when a compile may be reused). +/// +/// WHY THIS EXISTS, stated as the measurement that produced it rather than as a general +/// preference: `compile_dag_rust_emit_check` was memoized and this sibling was not, so a witness +/// asking two QUESTIONS about one source paid for two full compiles of it. `neither_green_source_ +/// refuses_and_neither_mis_resolves` (`test.claim.callable_candidate_ambiguity_witness`) is the +/// specimen — four census calls over two distinct sources, so half of its compiles recomputed a +/// pure function of an input already compiled in the same run. That is the DESIGN §6 +/// bare-minimum-cost class ("a proven cost-shape defect is ALWAYS fixed, regardless of the +/// realized n"), and its n stopped being small: the row reached 5437ms CPU against the 5000ms +/// `required_floor_claim_cpu_safety_limit_ms`, which is a FAIL-STOP protecting the executor and +/// explicitly "never a budget, tolerance, or target" — so the admissible repair is to stop +/// recomputing, never to raise the line. +/// +/// PURITY, and it is the whole reason for the guard: the memo is armed ONLY under the floor's +/// prepared-inventory snapshot and keyed on the source TOGETHER WITH that inventory's content +/// digest, because `build_module_path_index_from_witness_roots` reads those bytes and the census +/// is therefore a function of the corpus as well as of the source. Outside the guard there is no +/// snapshot, so a hit would be a claim about disk that nothing established — DESIGN's +/// cache-impurity rule (key on declared-input content), and the reason this is not simply a +/// `HashMap` on the source string. +pub fn compile_dag_diagnostic_census(source: &str) -> CompileDiagnosticCensus { + let Some(inventory_digest) = floor_prepared_inventory_digest() else { + return compile_dag_diagnostic_census_uncached(source); + }; + let memo_key = { + use crate::v1_rt::{atom_identity_hash, hash_combine}; + let h = atom_identity_hash(source.to_string()); + hash_combine(h, atom_identity_hash(inventory_digest)) + }; + if let Some(hit) = + COMPILE_DAG_DIAGNOSTIC_CENSUS_MEMO.with(|m| m.borrow().get(&memo_key).cloned()) + { + COMPILE_DAG_DIAGNOSTIC_CENSUS_MEMO_HITS.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + return hit; + } + COMPILE_DAG_DIAGNOSTIC_CENSUS_MEMO_MISSES.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + let census = compile_dag_diagnostic_census_uncached(source); + COMPILE_DAG_DIAGNOSTIC_CENSUS_MEMO.with(|m| m.borrow_mut().insert(memo_key, census.clone())); + census +} + /// Host realization backing the `compile_dag_diagnostic_census` builtin: compile an in-memory /// `.dag` program through the v1 pipeline to the Rust render target (the same pipeline /// [`compile_dag_rust_emit_check`] uses), and report the full per-class diagnostic census the @@ -1953,7 +2020,7 @@ pub enum CompileDiagnosticCensus { /// fail-open / `UnlistedImportUse` advisory path. Census receipts therefore must not be read as /// production compile-clean behavior for those type positions. It observes nothing about the /// interpreter's disposition of the same program and nothing about other emission targets. -pub fn compile_dag_diagnostic_census(source: &str) -> CompileDiagnosticCensus { +fn compile_dag_diagnostic_census_uncached(source: &str) -> CompileDiagnosticCensus { let compiled = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { crate::v1_rt::with_type_ref_hit_ne_bind_measure(|| { let module_index = build_module_path_index_from_witness_roots(); @@ -2407,6 +2474,42 @@ thread_local! { // not reported" — DESIGN's empty-observation narrow, ⊥-as-answer conflated with ⊥-as-ignorance. // One end-of-run receipt carrying BOTH numbers is the same information at 1/N the volume, and // it is the first form in which the ratio is readable at all. +thread_local! { + /// CPU spent, on this thread, FILLING shared memoized artifacts during the current claim. + /// + /// WHY THIS QUANTITY EXISTS AT ALL, and it is an attribution fact rather than a performance + /// one: a memoized compile is consumed by every claim that names the same source, but its + /// cost lands entirely on whichever claim happened to reach it FIRST. That makes a + /// merge-blocking per-claim ceiling a function of EXECUTION ORDER rather than of the tree — + /// the same claim is over or under the line depending on who got there first. The floor + /// already refuses that accounting for its larger shared artifacts: the three + /// `[floor-phase]` warm builds report `provenance=built-by-preparation` and are billed to + /// preparation, never to a claim. This is that same rule at a smaller grain (DESIGN §2 — one + /// concept, every scale), which is why it reuses `SharedBuildProvenance` rather than minting + /// an exemption beside it. + /// + /// IT EXEMPTS NOTHING. The cost is measured, accumulated, reported per claim and attributed + /// to the shared artifact; it stops being CHARGED to the first payer and does not stop being + /// COUNTED. A cost that vanished here would be the absorbing fallback (§5) wearing an + /// accounting label — the deficit's frequency zeroed by construction — so the receipt carries + /// marginal and fill as two columns whose sum is the claim's whole measured cost. + static SHARED_ARTIFACT_FILL_CPU_NANOS: std::cell::Cell = + const { std::cell::Cell::new(0) }; +} + +/// Accumulate CPU spent filling a shared memoized artifact. Called ONLY from a memo MISS path, +/// and only under the floor guard — outside it there is no memo, so there is no shared artifact +/// and nothing to attribute. +fn record_shared_artifact_fill_cpu(nanos: u128) { + SHARED_ARTIFACT_FILL_CPU_NANOS.with(|c| c.set(c.get().saturating_add(nanos))); +} + +/// Read the running total for this thread. The claim loop samples it either side of one claim; +/// the difference is that claim's fill. +pub fn shared_artifact_fill_cpu_nanos() -> u128 { + SHARED_ARTIFACT_FILL_CPU_NANOS.with(|c| c.get()) +} + static COMPILE_DAG_RUST_EMIT_CHECK_MEMO_HITS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0); static COMPILE_DAG_RUST_EMIT_CHECK_MEMO_MISSES: std::sync::atomic::AtomicU64 = @@ -2488,7 +2591,14 @@ pub fn compile_dag_rust_emit_check( return hit; } COMPILE_DAG_RUST_EMIT_CHECK_MEMO_MISSES.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + // A MISS FILLS A SHARED ARTIFACT. Measured on the same thread clock the claim loop enforces + // against, so the two quantities cannot drift apart, and recorded rather than subtracted here + // — the claim loop does the split, this only says how much of the cost was a fill. + let fill_started = v1_interpreter::thread_cpu_nanos(); let verdict = compile_dag_rust_emit_check_uncached(source, file_path, includes, excludes); + record_shared_artifact_fill_cpu( + v1_interpreter::thread_cpu_nanos().saturating_sub(fill_started), + ); COMPILE_DAG_RUST_EMIT_CHECK_MEMO.with(|m| m.borrow_mut().insert(memo_key, verdict)); verdict } @@ -5476,9 +5586,9 @@ pub fn compile_clean_diagnostic_histogram_key(d: &Rc) -> (String, Str CompilerDiagnostic::DeclaredTypeNotInhabited { position, .. } => position.clone(), CompilerDiagnostic::DeclaredTypeInhabitanceUndecided { position, .. } => position.clone(), CompilerDiagnostic::UnlistedImportUse { name, .. } => name.clone(), + CompilerDiagnostic::UnlistedVariantValueUse { name, .. } => name.clone(), CompilerDiagnostic::ReferenceDerivedImportProviderUnknown { name, .. } => name.clone(), CompilerDiagnostic::ReferenceDerivedImportExportUnproven { name, .. } => name.clone(), - CompilerDiagnostic::UnlistedVariantValueUse { name, .. } => name.clone(), CompilerDiagnostic::AmbiguousReference { name, .. } => name.clone(), CompilerDiagnostic::DataReferenceVisibilityBudgetExceeded { name, .. } => name.clone(), CompilerDiagnostic::ParameterDefaultFormNotAdmitted { parameter, .. } => parameter.clone(), @@ -18424,6 +18534,7 @@ pub fn run_dag_parse_sweep(workspace: &Path, roots: &[&str]) -> Result 0 { + // REPORTED, NOT ABSORBED. Printed on its own line, per claim, whenever a fill happened, + // so the quantity the ceiling stops charging is visible at the same grain it was measured + // — the difference between attributing a cost and losing one. `triggered_by` is this + // claim, which is precisely what `SharedBuildProvenance::AlreadyWarmOnEntry` records for + // the larger shared builds: every later claim reading this artifact reads it warm, and + // this line names who paid. + eprintln!( + "[floor-shared-fill] claim={function} marginal_cpu_ms={} fill_cpu_ms={} \ + measured_cpu_ms={} provenance=filled-shared-artifact triggered_by={function}", + cpu_nanos / 1_000_000, + fill_cpu_nanos / 1_000_000, + measured_cpu_nanos / 1_000_000, + ); + } let wall_nanos = started.elapsed().as_nanos(); ctx.clear_eval_deadline(); ctx.clear_wall_deadline(); @@ -32645,7 +32784,6 @@ pub fn emit_module_storage_binding_manifest( out.push_str("import std.algebra { Cons, Empty }\n"); out.push_str("import v2.std.diagnostic { ByteRange, Textual }\n"); out.push_str("import v2.std.integer { Int }\n"); - out.push_str("import v2.std.node { MintedOccurrence }\n"); out.push_str("import v2.std.provenance { FromSource, span_index_empty, span_index_record }\n"); out.push_str("import v2.std.qualified_name { qualified_name_from_string_segments }\n"); out.push_str(&emit_module_binding_source_root_import(&rows)); @@ -32703,30 +32841,25 @@ fn emit_module_binding_qualified_name(module_path: &str) -> Result String { let start = span.start.max(0); let end = span.end.max(start); let occurrence_id = start.max(1); format!( - "span_index_record(\n index: span_index_empty(),\n id: MintedOccurrence {{ id: v2.std.node.OccurrenceId {{ value: {occurrence_id} }} }},\n event: FromSource {{ locus: Textual {{ file: {file_symbol}, extent: ByteRange {{ start: {start}, end: {end} }} }} }}\n)" + "span_index_record(\n index: span_index_empty(),\n id: std.occurrence_identity.OccurrenceId {{ value: {occurrence_id} }},\n event: FromSource {{ locus: Textual {{ file: {file_symbol}, extent: ByteRange {{ start: {start}, end: {end} }} }} }}\n)" ) } @@ -43995,6 +44128,8 @@ pub struct PreparedRepository { pub modules_excluded: usize, /// The witness sites preparation found, already folded. NOT the corpus bytes. pub witness_files: Vec, + /// Admitted sources carrying zero `test fn` decls. See `PreparedSubject::test_decl_free_paths`. + pub test_decl_free_paths: Vec, } /// Shared view of one admitted source. Holds the same `Rc` preparation already @@ -44183,6 +44318,14 @@ pub struct PreparedSubject { pub sources: Vec>, pub inventory: Vec, pub witness_files: Vec, + /// Admitted sources carrying ZERO `test fn` decls, by repo-relative path. + /// + /// Preparation records the fact and judges nothing with it. Whether a path in here is a + /// VIOLATION is a policy question owned by `v2.workflow.floor_naming_hygiene` + /// (`floor_test_sidecar_suffix` / `floor_entry_is_barren_test_sidecar`), and the floor asks + /// it once, later, against the constant read from that module — so the rule keeps one home + /// and only its consumer moved. + pub test_decl_free_paths: Vec, pub subject_digest: String, pub modules_resolved: usize, pub modules_excluded: usize, @@ -44209,6 +44352,7 @@ pub fn assemble_prepared_subject( let index = build_module_index(source_roots); let total = index.len(); let mut witness_files: Vec = Vec::new(); + let mut test_decl_free_paths: Vec = Vec::new(); let mut sources: Vec> = Vec::with_capacity(total); let mut inventory: Vec = Vec::with_capacity(total); for (module_path, sf) in index.iter() { @@ -44219,8 +44363,42 @@ pub fn assemble_prepared_subject( { continue; } - if let Some(site) = witness_file_from_source(module_path, &sf.path, &sf.content) { - witness_files.push(site); + // THE DROP THAT MADE A WALL UNREACHABLE, NOW A RECORDED FACT. + // + // `witness_file_from_source` answers `None` for a file with no `test fn` decl, and this + // loop used to discard that answer. Since `run_required_floor` builds its whole roster + // from `witness_files`, a `*_test.dag` file declaring no `test fn` was not declined and + // not counted — it left the floor's universe entirely, one level ABOVE the + // `offered = routed + declined_long + declined_live` partition, which is why that line + // can be exact and still say nothing about it. + // + // The `.dag` producer has carried a wall for exactly this since it was written + // (`v2.workflow.floor_naming_hygiene` `floor_entry_is_barren_test_sidecar`, refused as + // `BarrenTestSidecar`), and it never fired, because the required floor does not take the + // path that reaches it — `invoke_floor_discovery_producer` is reachable only through + // `discover_floor_witness_roster`, which `run_required_floor` never calls. + // + // AND THE RECORDED SET USES THE RULE'S OWN VOCABULARY, NOT THIS SCANNER'S. `test data ` + // is a test decl to `floor_discovery_scan_test_decl_names`, which is what + // `floor_entry_is_barren_test_sidecar` composes, so a file declaring only `test data` + // rows is NOT barren under the rule this wall enforces — measured at 3bbd53c05a, 13 such + // files exist and refusing them here would have been this wall over-reaching into a + // different defect. That different defect is real and is declared, not fixed here: + // `witness_file_from_source` recognises `test fn ` and not `test data `, so those rows + // are dropped from the floor's roster whether or not their file carries a `test fn`. + let site = witness_file_from_source(module_path, &sf.path, &sf.content); + let declares_no_test_decl = site.is_none() + && !sf + .content + .lines() + .any(|line| line.starts_with("test data ")); + match site { + Some(site) => witness_files.push(site), + None => { + if declares_no_test_decl { + test_decl_free_paths.push(p.clone()); + } + } } inventory.push(PreparedSourceView { module_path: module_path.clone(), @@ -44233,12 +44411,14 @@ pub fn assemble_prepared_subject( } let modules_excluded = total - sources.len(); witness_files.sort_by(|a, b| a.path.cmp(&b.path)); + test_decl_free_paths.sort(); let subject_digest = subject_digest_for_closure(&sources); let modules_resolved = total - modules_excluded; Ok(PreparedSubject { sources, inventory, witness_files, + test_decl_free_paths, subject_digest, modules_resolved, modules_excluded, @@ -44267,6 +44447,7 @@ pub fn prepare_repository_once( sources, inventory, witness_files, + test_decl_free_paths, subject_digest, modules_resolved, modules_excluded, @@ -44281,6 +44462,7 @@ pub fn prepare_repository_once( modules_resolved, modules_excluded, witness_files, + test_decl_free_paths, }, inventory, )) @@ -45633,6 +45815,51 @@ fn floor_required_int(ctx: &v1_interpreter::InterpContext, func: &str) -> Result } } +/// Read one `String` constant from a `.dag` authority, by qualified name. +/// +/// The barren-sidecar rule's suffix lives in `v2.workflow.floor_naming_hygiene` +/// (`floor_test_sidecar_suffix`), and it stays there: re-spelling `"_test.dag"` in Rust would +/// fork the rule across two representations, which is the defect this whole repair is about. +fn floor_required_string( + ctx: &v1_interpreter::InterpContext, + qualified: &str, +) -> Result { + match v1_interpreter::run_in_context(ctx, qualified, false) { + Ok(v1_interpreter::Value::Str(s)) if !s.is_empty() => Ok(s.to_string()), + Ok(other) => Err(format!( + "{qualified}: expected a non-empty String, got {}", + floor_value_shape(Some(&other)) + )), + Err(e) => Err(format!("{qualified}: {e}")), + } +} + +/// The barren witnesses in the prepared subject, judged by the `.dag` rule rather than by a +/// second Rust spelling of it. +/// +/// Preparation records every admitted source with no `test fn` decl; this asks +/// `floor_naming_hygiene`'s own suffix which of them are `*_test.dag` entries — the same +/// predicate `floor_entry_is_barren_test_sidecar` composes, consumed here because THIS is the +/// path the required floor takes. +fn floor_barren_test_sidecars( + hermetic: &v1_interpreter::InterpContext, + test_decl_free_paths: &[String], +) -> Result, String> { + let suffix = floor_required_string( + hermetic, + "v2.workflow.floor_naming_hygiene.floor_test_sidecar_suffix", + )?; + Ok(test_decl_free_paths + .iter() + .filter(|path| { + path.strip_prefix("./") + .unwrap_or(path.as_str()) + .ends_with(suffix.as_str()) + }) + .cloned() + .collect()) +} + fn floor_decode_list<'a>( ctx: &v1_interpreter::InterpContext, v: Option<&'a v1_interpreter::Value>, @@ -46351,6 +46578,21 @@ pub fn run_required_floor( &hermetic, "v2.workflow.required_floor.long_home_prefixes", )?; + // THE LINE STOPS BEFORE THE PARTITION IS COMPUTED, because a barren entry is invisible to + // the partition by construction — it contributes no SITE, so `offered` cannot report it and + // `offered == routed + declined_long + declined_live` stays exact while saying nothing about + // it. A file that claims the `*_test.dag` place and enrolls nothing is a witness nobody asked + // and everybody reads as covered. + let barren_test_sidecars = + floor_barren_test_sidecars(&hermetic, &prepared.test_decl_free_paths)?; + if !barren_test_sidecars.is_empty() { + return Err(format!( + "REQUIRED-FLOOR REFUSAL cause=BarrenTestSidecar count={} — a `*_test.dag` entry must \ + declare at least one `test fn`; the required floor enrolls nothing from: {}", + barren_test_sidecars.len(), + barren_test_sidecars.join(", ") + )); + } let mut claims: Vec = Vec::new(); let mut planned_identities: HashSet = HashSet::new(); let mut long_declined = 0usize; From da0ccfc79c350811c413e03857d03afe34386677 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 28 Aug 2026 02:36:51 +0000 Subject: [PATCH 19/21] State that step 2 landed in the note that still called it future work reference_derived_use_lines_note described the PRE-CHANGE world inside the change that closes it: 'typed refusal at step-2 is future work', in a branch that lands CandidateExportProofFailed and ReferenceDerivedImportExportUnproven at seven sites each. A reader trusting the note concludes the wall does not exist in the PR that builds it. Found by smart-ram-730, who raised it from the opposite direction -- they read a note claiming the emission REFUSES and measured blocking=0 against it. That claim turned out to be on an abandoned local lineage rather than this branch, but reading my own note to answer them surfaced the inverse defect, which is mine and materially worse: a false 'it refuses' overstates a wall, a stale 'future work' denies one that is there. The clause now names the disposition and the diagnostic, and says explicitly that the emission is NOT refused -- because 'typed refusal' otherwise implies one. The diagnostic is advisory, so emission completes and produces its files beside it, measured on the required build lane at this branch: entry:src/v2/compiler/00_compile.dag emitted=175 blocking=0. What step 2 closed is the SILENCE, not the emission. Severity stays where it is owned, in v1.compiler.core reference_derived_import_refusal_severity_note. The annotation above the coproduct QUOTED the old wording. It keeps the quote, now marked as superseded, because the before-state is what motivates the arms -- but a quotation that silently tracked the edited note would cite a text that no longer exists. Its 'four things' also became six when the two variant arms landed. --- src/v1/05_emit_rust.dag | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/v1/05_emit_rust.dag b/src/v1/05_emit_rust.dag index 19b0ad25b10..b9fc9698a44 100644 --- a/src/v1/05_emit_rust.dag +++ b/src/v1/05_emit_rust.dag @@ -3446,7 +3446,7 @@ 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; (3) TYPE-surface refs on item signatures come from collect_item_emit_surface_names (collect_item_type_surface_names + collect_value_emit_type_surface_names over data bodies) over each item's type_annotation, param types, inferred Resolved return/signature, AND value-position record-literal / variant-parent-enum surfaces the emitter will qualify (e.g. UriScheme::Https in a data anchor) — 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. CONSTRUCTION WALL (ROOT 3, §5): a candidate that registry-resolves to a provider module MUST also pass provider_proven_exports_symbol (name_in_transitive_export_surface) before any use-line is synthesized — never a plausible guess from the bare-name registry alone (the v1_rt::member fabrication class: registry homonym without export proof). 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 LOCAL-DECL filtered (any name the module itself declares — local_decl_names from authored_name_at over items, plus local_type_names aliases/phantoms — is never an import candidate: the containment tree binds a bare ref to the module's own declaration before any cross-module lookup, and the bare-name registry is last-write-wins, so a dual-tree homonym — dag/std List/Map/GroupCompletion vs their src/v2/std twins — steals the registry row and would otherwise synthesize a SELF-COLLIDING pub-use, the E0255 std_dup class measured on the 2026-07-22 curated 4-module baseline), then cross-module registry-resolved (a ref the registry maps to this same module is skipped), 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 AND export-proven). A candidate that registry-resolves to nothing, or resolves but fails export proof, is left unsynthesized (typed refusal at step-2 is future work); 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. SUPERSEDED-MECHANISM, THIS CUT (eager-deer-389): the two sentences that follow describe the corpus_repr_is_faithful / HostNative gate, which NO LONGER EXISTS -- RustCorpusRepr and both its variants are deleted and the gate is gone, so import-bearing modules are no longer split by corpus mode. They are retained as the history of WHY the walk was gated at all (the spurious-use-line and seed zero-drift hazards below are real and still constrain any future gating), not as a live description; reference_use_lines_representation_invariant_note in this module carries the post-cut statement. Read them as quarry. AS WRITTEN BEFORE THE CUT: 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." +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_item_emit_surface_names (collect_item_type_surface_names + collect_value_emit_type_surface_names over data bodies) over each item's type_annotation, param types, inferred Resolved return/signature, AND value-position record-literal / variant-parent-enum surfaces the emitter will qualify (e.g. UriScheme::Https in a data anchor) — 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. CONSTRUCTION WALL (ROOT 3, §5): a candidate that registry-resolves to a provider module MUST also pass provider_proven_exports_symbol (name_in_transitive_export_surface) before any use-line is synthesized — never a plausible guess from the bare-name registry alone (the v1_rt::member fabrication class: registry homonym without export proof). 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 LOCAL-DECL filtered (any name the module itself declares — local_decl_names from authored_name_at over items, plus local_type_names aliases/phantoms — is never an import candidate: the containment tree binds a bare ref to the module's own declaration before any cross-module lookup, and the bare-name registry is last-write-wins, so a dual-tree homonym — dag/std List/Map/GroupCompletion vs their src/v2/std twins — steals the registry row and would otherwise synthesize a SELF-COLLIDING pub-use, the E0255 std_dup class measured on the 2026-07-22 curated 4-module baseline), then cross-module registry-resolved (a ref the registry maps to this same module is skipped), 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 AND export-proven). A candidate that registry-resolves to nothing, or resolves but fails export proof, is left unsynthesized, and it never fabricates a use-line. STEP 2 HAS LANDED: the reason is no longer silent. Each such candidate carries a ReferenceDerivedCandidateDisposition -- CandidateRegistryAbsent or CandidateExportProofFailed, distinct because the remedies are opposite -- and CandidateExportProofFailed emits the typed, located, counted ReferenceDerivedImportExportUnproven. THE EMISSION IS NOT REFUSED, and the distinction matters because a reader of the phrase typed refusal will otherwise expect one: the diagnostic is ADVISORY, so emission completes and produces its files beside it. Measured on the required build lane at this branch head, entry:src/v2/compiler/00_compile.dag: emitted=175 blocking=0 -- so what step 2 closed is the SILENCE, not the emission. Whether the line stops is the severity question, and v1.compiler.core reference_derived_import_refusal_severity_note owns it, including why the blocking flip was executed and then falsified by its own first run over a second closure. 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. SUPERSEDED-MECHANISM, THIS CUT (eager-deer-389): the two sentences that follow describe the corpus_repr_is_faithful / HostNative gate, which NO LONGER EXISTS -- RustCorpusRepr and both its variants are deleted and the gate is gone, so import-bearing modules are no longer split by corpus mode. They are retained as the history of WHY the walk was gated at all (the spurious-use-line and seed zero-drift hazards below are real and still constrain any future gating), not as a live description; reference_use_lines_representation_invariant_note in this module carries the post-cut statement. Read them as quarry. AS WRITTEN BEFORE THE CUT: 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 expand_variant_payload_struct_imports(names: List, type_summaries: Map, variant_to_enum: Map) -> List { names |> flat_map(n => @@ -3696,11 +3696,16 @@ fn local_coproduct_variant_names(items: List, source_indices: Map Date: Fri, 28 Aug 2026 03:01:16 +0000 Subject: [PATCH 20/21] Regenerate the emit_rust mirror for the note edit The note text is emitted into the mirror, so editing it necessarily drifts v1_compiler_emit_rust.rs. Two-generation regen at fdf4ccf1d97 plus the note commit: gen-0 first_generation_equal=false with drift in exactly that one file, install, REBUILD, gen-1 first_generation_equal=true. The single-file drift was the discriminator this run needed, not just its result. An EMPTY drift would not have been good news: it would have meant the uncommitted edit never reached the runner and the regen had measured a tree without it. Non-empty drift naming exactly the mirror of the edited file is what establishes the measurement was of the right tree, and it doubles as the parse check -- a .dag parse failure returns NO_CANDIDATE, which is how a stray quote in a note surfaced earlier on this branch. --- src/v1/stage0/src/v1_compiler_emit_rust.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v1/stage0/src/v1_compiler_emit_rust.rs b/src/v1/stage0/src/v1_compiler_emit_rust.rs index 493ee10537b..1978c7f1d7b 100644 --- a/src/v1/stage0/src/v1_compiler_emit_rust.rs +++ b/src/v1/stage0/src/v1_compiler_emit_rust.rs @@ -7482,7 +7482,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; (3) TYPE-surface refs on item signatures come from collect_item_emit_surface_names (collect_item_type_surface_names + collect_value_emit_type_surface_names over data bodies) over each item's type_annotation, param types, inferred Resolved return/signature, AND value-position record-literal / variant-parent-enum surfaces the emitter will qualify (e.g. UriScheme::Https in a data anchor) — 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. CONSTRUCTION WALL (ROOT 3, §5): a candidate that registry-resolves to a provider module MUST also pass provider_proven_exports_symbol (name_in_transitive_export_surface) before any use-line is synthesized — never a plausible guess from the bare-name registry alone (the v1_rt::member fabrication class: registry homonym without export proof). 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 LOCAL-DECL filtered (any name the module itself declares — local_decl_names from authored_name_at over items, plus local_type_names aliases/phantoms — is never an import candidate: the containment tree binds a bare ref to the module's own declaration before any cross-module lookup, and the bare-name registry is last-write-wins, so a dual-tree homonym — dag/std List/Map/GroupCompletion vs their src/v2/std twins — steals the registry row and would otherwise synthesize a SELF-COLLIDING pub-use, the E0255 std_dup class measured on the 2026-07-22 curated 4-module baseline), then cross-module registry-resolved (a ref the registry maps to this same module is skipped), 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 AND export-proven). A candidate that registry-resolves to nothing, or resolves but fails export proof, is left unsynthesized (typed refusal at step-2 is future work); 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. SUPERSEDED-MECHANISM, THIS CUT (eager-deer-389): the two sentences that follow describe the corpus_repr_is_faithful / HostNative gate, which NO LONGER EXISTS -- RustCorpusRepr and both its variants are deleted and the gate is gone, so import-bearing modules are no longer split by corpus mode. They are retained as the history of WHY the walk was gated at all (the spurious-use-line and seed zero-drift hazards below are real and still constrain any future gating), not as a live description; reference_use_lines_representation_invariant_note in this module carries the post-cut statement. Read them as quarry. AS WRITTEN BEFORE THE CUT: 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() + "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_item_emit_surface_names (collect_item_type_surface_names + collect_value_emit_type_surface_names over data bodies) over each item's type_annotation, param types, inferred Resolved return/signature, AND value-position record-literal / variant-parent-enum surfaces the emitter will qualify (e.g. UriScheme::Https in a data anchor) — 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. CONSTRUCTION WALL (ROOT 3, §5): a candidate that registry-resolves to a provider module MUST also pass provider_proven_exports_symbol (name_in_transitive_export_surface) before any use-line is synthesized — never a plausible guess from the bare-name registry alone (the v1_rt::member fabrication class: registry homonym without export proof). 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 LOCAL-DECL filtered (any name the module itself declares — local_decl_names from authored_name_at over items, plus local_type_names aliases/phantoms — is never an import candidate: the containment tree binds a bare ref to the module's own declaration before any cross-module lookup, and the bare-name registry is last-write-wins, so a dual-tree homonym — dag/std List/Map/GroupCompletion vs their src/v2/std twins — steals the registry row and would otherwise synthesize a SELF-COLLIDING pub-use, the E0255 std_dup class measured on the 2026-07-22 curated 4-module baseline), then cross-module registry-resolved (a ref the registry maps to this same module is skipped), 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 AND export-proven). A candidate that registry-resolves to nothing, or resolves but fails export proof, is left unsynthesized, and it never fabricates a use-line. STEP 2 HAS LANDED: the reason is no longer silent. Each such candidate carries a ReferenceDerivedCandidateDisposition -- CandidateRegistryAbsent or CandidateExportProofFailed, distinct because the remedies are opposite -- and CandidateExportProofFailed emits the typed, located, counted ReferenceDerivedImportExportUnproven. THE EMISSION IS NOT REFUSED, and the distinction matters because a reader of the phrase typed refusal will otherwise expect one: the diagnostic is ADVISORY, so emission completes and produces its files beside it. Measured on the required build lane at this branch head, entry:src/v2/compiler/00_compile.dag: emitted=175 blocking=0 -- so what step 2 closed is the SILENCE, not the emission. Whether the line stops is the severity question, and v1.compiler.core reference_derived_import_refusal_severity_note owns it, including why the blocking flip was executed and then falsified by its own first run over a second closure. 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. SUPERSEDED-MECHANISM, THIS CUT (eager-deer-389): the two sentences that follow describe the corpus_repr_is_faithful / HostNative gate, which NO LONGER EXISTS -- RustCorpusRepr and both its variants are deleted and the gate is gone, so import-bearing modules are no longer split by corpus mode. They are retained as the history of WHY the walk was gated at all (the spurious-use-line and seed zero-drift hazards below are real and still constrain any future gating), not as a live description; reference_use_lines_representation_invariant_note in this module carries the post-cut statement. Read them as quarry. AS WRITTEN BEFORE THE CUT: 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()) From a3347f745d57171cedb880dc1841dc5b53ff0907 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 28 Aug 2026 03:04:16 +0000 Subject: [PATCH 21/21] Seed the merge resolution from the side that compiles, then let regen decide The merge of #9551 conflicted on v1_compiler_emit_rust.rs and the generated-artifact driver refused it correctly -- path left unmerged, no markers, regeneration recipe printed. I then seeded the resolution from MAIN's side, and the two-generation regen refused to build it: error[E0599]: no variant named CandidateVariantDelegatedToParent found for enum ReferenceDerivedCandidateDisposition --> v1_tests_claim_reference_derived_disposition_census_witness_test.rs could not compile v1-compiler (lib) due to 19 previous errors Main's mirror predates this branch's dispositions and this branch's witness mirror references them, so that side cannot be a compilable gen-0 seed. THE LESSON IS ABOUT WHAT 'DO NOT PICK A SIDE' MEANS. I took it to mean the final bytes must come from regeneration, which is right, and inferred that the starting seed was therefore arbitrary, which is wrong. Gen-0 runs the COMMITTED mirror to emit the candidate, so the seed must compile -- a side that does not is not a neutral starting point, it is a broken compiler. The choice of seed is not a choice of content and it is not free either. Seeded from this branch's side instead, which is main's #9551 bytes plus the note edit -- established rather than assumed: fdf4ccf1d97 regenerated with EMPTY drift, so its mirrors already equalled main's, and 037cda8130d added only the note text on top. Regen output follows and is the authority. --- src/v1/stage0/src/v1_compiler_emit_rust.rs | 201 +++++++++++++++------ 1 file changed, 149 insertions(+), 52 deletions(-) diff --git a/src/v1/stage0/src/v1_compiler_emit_rust.rs b/src/v1/stage0/src/v1_compiler_emit_rust.rs index c742cc39066..1978c7f1d7b 100644 --- a/src/v1/stage0/src/v1_compiler_emit_rust.rs +++ b/src/v1/stage0/src/v1_compiler_emit_rust.rs @@ -181,7 +181,8 @@ use crate::v1_std_core::CallTargetIdentity::{ use crate::v1_std_core::Cardinality::{CardOptional, Required}; use crate::v1_std_core::CompilerDiagnostic::{ AmbiguousAnonymousRecordLiteral, AmbiguousReference, DataReferenceVisibilityBudgetExceeded, - InternalError, ParameterDefaultFormNotAdmitted, UnlistedImportUse, + InternalError, ParameterDefaultFormNotAdmitted, ReferenceDerivedImportExportUnproven, + ReferenceDerivedImportProviderUnknown, UnlistedImportUse, }; use crate::v1_std_core::Connective::{Conj, Disj, NoConnective}; use crate::v1_std_core::ExprData::{ @@ -5553,18 +5554,28 @@ pub fn emit_rust(typed: Rc) -> Rc { }); } let test_projections = ctx.test_projections.clone(); - let module_files = Rc::new({ + let module_emissions = Rc::new({ let mut __result = Vec::new(); for tm in typed.modules.clone().iter().cloned() { - __result.push( - emit_module_full_with_dispositions( - tm.clone(), - ctx.clone(), - typed.modules.clone(), - ) - .file - .clone(), - ); + __result.push(emit_module_full_with_dispositions( + tm.clone(), + ctx.clone(), + typed.modules.clone(), + )); + } + __result + }); + let module_files = Rc::new({ + let mut __result = Vec::new(); + for e in module_emissions.iter().cloned() { + __result.push(e.file.clone()); + } + __result + }); + let import_refusals = Rc::new({ + let mut __result = Vec::new(); + for e in module_emissions.iter().cloned() { + __result.extend((*e.import_refusals.clone()).iter().cloned()); } __result }); @@ -5686,7 +5697,7 @@ pub fn emit_rust(typed: Rc) -> Rc { ); Rc::new(EmitResult { files: files.clone(), - diagnostics: Rc::new(vec![]), + diagnostics: import_refusals.clone(), }) } } @@ -7471,7 +7482,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; (3) TYPE-surface refs on item signatures come from collect_item_emit_surface_names (collect_item_type_surface_names + collect_value_emit_type_surface_names over data bodies) over each item's type_annotation, param types, inferred Resolved return/signature, AND value-position record-literal / variant-parent-enum surfaces the emitter will qualify (e.g. UriScheme::Https in a data anchor) — 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. CONSTRUCTION WALL (ROOT 3, §5): a candidate that registry-resolves to a provider module MUST also pass provider_proven_exports_symbol (name_in_transitive_export_surface) before any use-line is synthesized — never a plausible guess from the bare-name registry alone (the v1_rt::member fabrication class: registry homonym without export proof). 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 LOCAL-DECL filtered (any name the module itself declares — local_decl_names from authored_name_at over items, plus local_type_names aliases/phantoms — is never an import candidate: the containment tree binds a bare ref to the module's own declaration before any cross-module lookup, and the bare-name registry is last-write-wins, so a dual-tree homonym — dag/std List/Map/GroupCompletion vs their src/v2/std twins — steals the registry row and would otherwise synthesize a SELF-COLLIDING pub-use, the E0255 std_dup class measured on the 2026-07-22 curated 4-module baseline), then cross-module registry-resolved (a ref the registry maps to this same module is skipped), 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 AND export-proven). A candidate that registry-resolves to nothing, or resolves but fails export proof, is left unsynthesized (typed refusal at step-2 is future work); 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. SUPERSEDED-MECHANISM, THIS CUT (eager-deer-389): the two sentences that follow describe the corpus_repr_is_faithful / HostNative gate, which NO LONGER EXISTS -- RustCorpusRepr and both its variants are deleted and the gate is gone, so import-bearing modules are no longer split by corpus mode. They are retained as the history of WHY the walk was gated at all (the spurious-use-line and seed zero-drift hazards below are real and still constrain any future gating), not as a live description; reference_use_lines_representation_invariant_note in this module carries the post-cut statement. Read them as quarry. AS WRITTEN BEFORE THE CUT: 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() + "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_item_emit_surface_names (collect_item_type_surface_names + collect_value_emit_type_surface_names over data bodies) over each item's type_annotation, param types, inferred Resolved return/signature, AND value-position record-literal / variant-parent-enum surfaces the emitter will qualify (e.g. UriScheme::Https in a data anchor) — 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. CONSTRUCTION WALL (ROOT 3, §5): a candidate that registry-resolves to a provider module MUST also pass provider_proven_exports_symbol (name_in_transitive_export_surface) before any use-line is synthesized — never a plausible guess from the bare-name registry alone (the v1_rt::member fabrication class: registry homonym without export proof). 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 LOCAL-DECL filtered (any name the module itself declares — local_decl_names from authored_name_at over items, plus local_type_names aliases/phantoms — is never an import candidate: the containment tree binds a bare ref to the module's own declaration before any cross-module lookup, and the bare-name registry is last-write-wins, so a dual-tree homonym — dag/std List/Map/GroupCompletion vs their src/v2/std twins — steals the registry row and would otherwise synthesize a SELF-COLLIDING pub-use, the E0255 std_dup class measured on the 2026-07-22 curated 4-module baseline), then cross-module registry-resolved (a ref the registry maps to this same module is skipped), 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 AND export-proven). A candidate that registry-resolves to nothing, or resolves but fails export proof, is left unsynthesized, and it never fabricates a use-line. STEP 2 HAS LANDED: the reason is no longer silent. Each such candidate carries a ReferenceDerivedCandidateDisposition -- CandidateRegistryAbsent or CandidateExportProofFailed, distinct because the remedies are opposite -- and CandidateExportProofFailed emits the typed, located, counted ReferenceDerivedImportExportUnproven. THE EMISSION IS NOT REFUSED, and the distinction matters because a reader of the phrase typed refusal will otherwise expect one: the diagnostic is ADVISORY, so emission completes and produces its files beside it. Measured on the required build lane at this branch head, entry:src/v2/compiler/00_compile.dag: emitted=175 blocking=0 -- so what step 2 closed is the SILENCE, not the emission. Whether the line stops is the severity question, and v1.compiler.core reference_derived_import_refusal_severity_note owns it, including why the blocking flip was executed and then falsified by its own first run over a second closure. 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. SUPERSEDED-MECHANISM, THIS CUT (eager-deer-389): the two sentences that follow describe the corpus_repr_is_faithful / HostNative gate, which NO LONGER EXISTS -- RustCorpusRepr and both its variants are deleted and the gate is gone, so import-bearing modules are no longer split by corpus mode. They are retained as the history of WHY the walk was gated at all (the spurious-use-line and seed zero-drift hazards below are real and still constrain any future gating), not as a live description; reference_use_lines_representation_invariant_note in this module carries the post-cut statement. Read them as quarry. AS WRITTEN BEFORE THE CUT: 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()) @@ -7864,29 +7875,11 @@ pub fn local_coproduct_variant_names( pub enum ReferenceDerivedCandidateDisposition { CandidateSurvived { provider_module: String }, CandidateOwnModule, + CandidateVariantDelegatedToParent { parent_enum: String }, + CandidateVariantParentUnresolved, CandidateRegistryAbsent, CandidateExportProofFailed { provider_module: String }, } -impl ReferenceDerivedCandidateDisposition { - pub fn provider_module(&self) -> String { - match self { - ReferenceDerivedCandidateDisposition::CandidateSurvived { - provider_module: __val, - .. - } => __val.clone(), - ReferenceDerivedCandidateDisposition::CandidateOwnModule => { - panic!("no provider_module on unit variant") - } - ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent => { - panic!("no provider_module on unit variant") - } - ReferenceDerivedCandidateDisposition::CandidateExportProofFailed { - provider_module: __val, - .. - } => __val.clone(), - } - } -} #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct ReferenceDerivedCandidateRow { @@ -7903,36 +7896,100 @@ pub fn reference_derived_candidate_disposition( typed_modules: Rc>>, source_indices: Rc>>, module_index: Rc, + type_summaries: Rc>>, + variant_to_enum: Rc>, ) -> Rc { - match v1_rt::map_get(®istry, name.clone()) { - Some(info) => { - if (info.module_name.clone() == this_module_name.clone()) { - Rc::new(ReferenceDerivedCandidateDisposition::CandidateOwnModule) - } else { - if provider_proven_exports_symbol( - name.clone(), - info.module_name.clone(), - export_sets.clone(), - typed_modules.clone(), - source_indices.clone(), - module_index.clone(), - ) { - Rc::new(ReferenceDerivedCandidateDisposition::CandidateSurvived { - provider_module: info.module_name.clone(), - }) + if crate::v1_compiler_infer_emit_info::is_known_variant(type_summaries.clone(), name.clone()) { + match v1_rt::map_get(&variant_to_enum, name.clone()) { + Some(parent) => { + if (parent.clone() == "".to_string()) { + Rc::new(ReferenceDerivedCandidateDisposition::CandidateVariantParentUnresolved) } else { Rc::new( - ReferenceDerivedCandidateDisposition::CandidateExportProofFailed { - provider_module: info.module_name.clone(), + ReferenceDerivedCandidateDisposition::CandidateVariantDelegatedToParent { + parent_enum: parent.clone(), }, ) } } + None => Rc::new(ReferenceDerivedCandidateDisposition::CandidateVariantParentUnresolved), + } + } else { + match v1_rt::map_get(®istry, name.clone()) { + Some(info) => { + if (info.module_name.clone() == this_module_name.clone()) { + Rc::new(ReferenceDerivedCandidateDisposition::CandidateOwnModule) + } else { + if provider_proven_exports_symbol( + name.clone(), + info.module_name.clone(), + export_sets.clone(), + typed_modules.clone(), + source_indices.clone(), + module_index.clone(), + ) { + Rc::new(ReferenceDerivedCandidateDisposition::CandidateSurvived { + provider_module: info.module_name.clone(), + }) + } else { + Rc::new( + ReferenceDerivedCandidateDisposition::CandidateExportProofFailed { + provider_module: info.module_name.clone(), + }, + ) + } + } + } + None => Rc::new(ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent), } - None => Rc::new(ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent), } } +pub fn reference_derived_row_diagnostics( + rows: Rc>>, + module_span: Rc, +) -> Rc>> { + Rc::new({ + let mut __result = Vec::new(); + for r in rows.iter().cloned() { + __result.extend( + (*match (*r.disposition.clone()).clone() { + ReferenceDerivedCandidateDisposition::CandidateExportProofFailed { + provider_module: p, + .. + } => Rc::new(vec![crate::v1_std_core::make_error_node( + Rc::new(CompilerDiagnostic::ReferenceDerivedImportExportUnproven { + name: r.name.clone(), + referencing_module: r.module_name.clone(), + provider_module: p.clone(), + span: module_span.clone(), + }), + r.module_name.clone(), + )]), + ReferenceDerivedCandidateDisposition::CandidateSurvived { + provider_module: _, + .. + } => Rc::new(vec![]), + ReferenceDerivedCandidateDisposition::CandidateOwnModule => Rc::new(vec![]), + ReferenceDerivedCandidateDisposition::CandidateVariantDelegatedToParent { + parent_enum: _, + .. + } => Rc::new(vec![]), + ReferenceDerivedCandidateDisposition::CandidateVariantParentUnresolved => { + Rc::new(vec![]) + } + ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent => { + Rc::new(vec![]) + } + }) + .iter() + .cloned(), + ); + } + __result + }) +} + pub fn reference_derived_candidate_survived( disposition: Rc, ) -> bool { @@ -7952,6 +8009,13 @@ pub fn reference_derived_disposition_name( provider_module: _, .. } => "survived".to_string(), ReferenceDerivedCandidateDisposition::CandidateOwnModule => "own-module".to_string(), + ReferenceDerivedCandidateDisposition::CandidateVariantDelegatedToParent { + parent_enum: _, + .. + } => "variant-delegated-to-parent".to_string(), + ReferenceDerivedCandidateDisposition::CandidateVariantParentUnresolved => { + "variant-parent-unresolved".to_string() + } ReferenceDerivedCandidateDisposition::CandidateRegistryAbsent => { "registry-absent".to_string() } @@ -7967,6 +8031,8 @@ pub struct ReferenceDerivedCensus { pub candidates: i64, pub survived: i64, pub own_module: i64, + pub variant_delegated_to_parent: i64, + pub variant_parent_unresolved: i64, pub registry_absent: i64, pub export_proof_failed: i64, } @@ -8000,6 +8066,30 @@ pub fn reference_derived_census( __result }) .len() as i64), + variant_delegated_to_parent: (Rc::new({ + let mut __result = Vec::new(); + for r in rows.iter().cloned() { + if (reference_derived_disposition_name(r.disposition.clone()) + == "variant-delegated-to-parent".to_string()) + { + __result.push(r); + } + } + __result + }) + .len() as i64), + variant_parent_unresolved: (Rc::new({ + let mut __result = Vec::new(); + for r in rows.iter().cloned() { + if (reference_derived_disposition_name(r.disposition.clone()) + == "variant-parent-unresolved".to_string()) + { + __result.push(r); + } + } + __result + }) + .len() as i64), registry_absent: (Rc::new({ let mut __result = Vec::new(); for r in rows.iter().cloned() { @@ -8259,6 +8349,8 @@ pub fn reference_derived_use_line_plan( typed_modules.clone(), source_indices.clone(), module_index.clone(), + emit_info.type_summaries.clone(), + emit_info.variant_to_enum.clone(), ), })]) }) @@ -8375,6 +8467,7 @@ pub fn reference_derived_use_lines( pub struct ModuleEmission { pub file: Rc, pub reference_rows: Rc>>, + pub import_refusals: Rc>>, } pub fn emit_module_full_with_dispositions( @@ -8814,6 +8907,10 @@ pub fn emit_module_full( content: content.clone(), }), reference_rows: reference_plan.rows.clone(), + import_refusals: reference_derived_row_diagnostics( + reference_plan.rows.clone(), + m.span.clone(), + ), }) } }