From 39f1634adcc2eaa31b6d6c161a69dc2171fb5fa0 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 13 Jul 2026 20:31:31 +0000 Subject: [PATCH 1/5] WIP: corpus OOM --- src/v1/03_normalize.dag | 10 ++++++---- src/v1/compile.dag | 12 +++++++----- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/v1/03_normalize.dag b/src/v1/03_normalize.dag index 6a62c9089c4..5268e5d2b1c 100644 --- a/src/v1/03_normalize.dag +++ b/src/v1/03_normalize.dag @@ -55,11 +55,13 @@ fn check_bare_containers(n: Node, module_name: String, source_indices: Map d) } +fn normalize_module_diagnostics(m: ResolvedModule, source_indices: Map) -> List { + let items = module_items(n: m.module) + flat_map(items, item => check_bare_containers(n: item, module_name: authored_name_at(source_indices: source_indices, node: m.module), source_indices: source_indices)) +} + fn normalize_graph(graph: ModuleGraph, source_indices: Map) -> NormalizeResult { - let diags = flat_map(graph.modules, m => { - let items = module_items(n: m.module) - flat_map(items, item => check_bare_containers(n: item, module_name: authored_name_at(source_indices: source_indices, node: m.module), source_indices: source_indices)) - }) + let diags = flat_map(graph.modules, m => normalize_module_diagnostics(m: m, source_indices: source_indices)) NormalizeResult { graph: graph, diagnostics: diags diff --git a/src/v1/compile.dag b/src/v1/compile.dag index 8c4b91a9cde..5db70a6e7d6 100644 --- a/src/v1/compile.dag +++ b/src/v1/compile.dag @@ -93,14 +93,16 @@ fn build_recursion_context(typed: ResolvedGraph) -> RecursionContext { RecursionContext {} } +fn module_ownership_proofs(m: TypedModule) -> List { + m.items |> filter(item => item.body != Absent) |> map(item => + analyze_ownership(func_name: authored_name_at(source_indices: m.type_env.source_indices, node: item), params: item.params, body: item.body.value, si: m.type_env.source_indices) + ) +} + fn extract_ownership_proofs(typed: ResolvedGraph) -> List { typed.modules |> filter(m => m.items |> any(item => item.body != Absent)) - |> flat_map(m => - m.items |> filter(item => item.body != Absent) |> map(item => - analyze_ownership(func_name: authored_name_at(source_indices: m.type_env.source_indices, node: item), params: item.params, body: item.body.value, si: m.type_env.source_indices) - ) - ) + |> flat_map(m => module_ownership_proofs(m: m)) } fn ownership_diagnostics(proofs: List) -> List { From ae879235398c520911ef64d36e25bd294ac0beb9 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 13 Jul 2026 20:41:57 +0000 Subject: [PATCH 2/5] WIP: corpus OOM --- src/v1/03_normalize.dag | 10 ++++------ src/v1/compile.dag | 12 +++++------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/v1/03_normalize.dag b/src/v1/03_normalize.dag index 5268e5d2b1c..6a62c9089c4 100644 --- a/src/v1/03_normalize.dag +++ b/src/v1/03_normalize.dag @@ -55,13 +55,11 @@ fn check_bare_containers(n: Node, module_name: String, source_indices: Map d) } -fn normalize_module_diagnostics(m: ResolvedModule, source_indices: Map) -> List { - let items = module_items(n: m.module) - flat_map(items, item => check_bare_containers(n: item, module_name: authored_name_at(source_indices: source_indices, node: m.module), source_indices: source_indices)) -} - fn normalize_graph(graph: ModuleGraph, source_indices: Map) -> NormalizeResult { - let diags = flat_map(graph.modules, m => normalize_module_diagnostics(m: m, source_indices: source_indices)) + let diags = flat_map(graph.modules, m => { + let items = module_items(n: m.module) + flat_map(items, item => check_bare_containers(n: item, module_name: authored_name_at(source_indices: source_indices, node: m.module), source_indices: source_indices)) + }) NormalizeResult { graph: graph, diagnostics: diags diff --git a/src/v1/compile.dag b/src/v1/compile.dag index 5db70a6e7d6..8c4b91a9cde 100644 --- a/src/v1/compile.dag +++ b/src/v1/compile.dag @@ -93,16 +93,14 @@ fn build_recursion_context(typed: ResolvedGraph) -> RecursionContext { RecursionContext {} } -fn module_ownership_proofs(m: TypedModule) -> List { - m.items |> filter(item => item.body != Absent) |> map(item => - analyze_ownership(func_name: authored_name_at(source_indices: m.type_env.source_indices, node: item), params: item.params, body: item.body.value, si: m.type_env.source_indices) - ) -} - fn extract_ownership_proofs(typed: ResolvedGraph) -> List { typed.modules |> filter(m => m.items |> any(item => item.body != Absent)) - |> flat_map(m => module_ownership_proofs(m: m)) + |> flat_map(m => + m.items |> filter(item => item.body != Absent) |> map(item => + analyze_ownership(func_name: authored_name_at(source_indices: m.type_env.source_indices, node: item), params: item.params, body: item.body.value, si: m.type_env.source_indices) + ) + ) } fn ownership_diagnostics(proofs: List) -> List { From ba4225396024ca04d9e12b80134f67953e6f88a1 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 13 Jul 2026 20:52:28 +0000 Subject: [PATCH 3/5] WIP: corpus OOM --- src/v1/03_normalize.dag | 10 +-- src/v1/compile.dag | 12 ++-- src/v1/stage0/src/cli_run.rs | 81 ++++++++++++++++++++-- src/v1/stage0/src/v1_compiler_compile.rs | 57 ++++++++------- src/v1/stage0/src/v1_compiler_normalize.rs | 47 +++++++------ 5 files changed, 141 insertions(+), 66 deletions(-) diff --git a/src/v1/03_normalize.dag b/src/v1/03_normalize.dag index 6a62c9089c4..5268e5d2b1c 100644 --- a/src/v1/03_normalize.dag +++ b/src/v1/03_normalize.dag @@ -55,11 +55,13 @@ fn check_bare_containers(n: Node, module_name: String, source_indices: Map d) } +fn normalize_module_diagnostics(m: ResolvedModule, source_indices: Map) -> List { + let items = module_items(n: m.module) + flat_map(items, item => check_bare_containers(n: item, module_name: authored_name_at(source_indices: source_indices, node: m.module), source_indices: source_indices)) +} + fn normalize_graph(graph: ModuleGraph, source_indices: Map) -> NormalizeResult { - let diags = flat_map(graph.modules, m => { - let items = module_items(n: m.module) - flat_map(items, item => check_bare_containers(n: item, module_name: authored_name_at(source_indices: source_indices, node: m.module), source_indices: source_indices)) - }) + let diags = flat_map(graph.modules, m => normalize_module_diagnostics(m: m, source_indices: source_indices)) NormalizeResult { graph: graph, diagnostics: diags diff --git a/src/v1/compile.dag b/src/v1/compile.dag index 8c4b91a9cde..5db70a6e7d6 100644 --- a/src/v1/compile.dag +++ b/src/v1/compile.dag @@ -93,14 +93,16 @@ fn build_recursion_context(typed: ResolvedGraph) -> RecursionContext { RecursionContext {} } +fn module_ownership_proofs(m: TypedModule) -> List { + m.items |> filter(item => item.body != Absent) |> map(item => + analyze_ownership(func_name: authored_name_at(source_indices: m.type_env.source_indices, node: item), params: item.params, body: item.body.value, si: m.type_env.source_indices) + ) +} + fn extract_ownership_proofs(typed: ResolvedGraph) -> List { typed.modules |> filter(m => m.items |> any(item => item.body != Absent)) - |> flat_map(m => - m.items |> filter(item => item.body != Absent) |> map(item => - analyze_ownership(func_name: authored_name_at(source_indices: m.type_env.source_indices, node: item), params: item.params, body: item.body.value, si: m.type_env.source_indices) - ) - ) + |> flat_map(m => module_ownership_proofs(m: m)) } fn ownership_diagnostics(proofs: List) -> List { diff --git a/src/v1/stage0/src/cli_run.rs b/src/v1/stage0/src/cli_run.rs index acac4e1edd5..5cda5388ae0 100644 --- a/src/v1/stage0/src/cli_run.rs +++ b/src/v1/stage0/src/cli_run.rs @@ -2695,6 +2695,18 @@ pub struct MultiEntryIndex { ), >, >, + // Per-module memo for the two per-entry recomputations the resolve-split receipt + // priced at ~15% of whole-corpus resolve (normalize 8% + ownership 7% — the + // "Track A denomination receipt", docs/plans/v1-run-stability-throughline.md §1). + // Normalize diagnostics are a pure function of the parsed module node + // (v1.compiler.normalize.normalize_module_diagnostics) and ownership diagnostics + // a pure function of the typed module (v1.compiler.compile.module_ownership_proofs) + // — both stable per declaring-file path within one process, the same in-process + // source-stability contract parse_cache and typed_module_cache already ride + // (module_source_identity walls name↔file divergence loud). Keyed by the module's + // declaring file (span.file, the parse_cache key's own identity). + normalize_diag_cache: RefCell>>>>, + ownership_diag_cache: RefCell>>>>, } // Once-per-node resolve receipt (union-resolve minimum-upper-bound contract, §6.2 of @@ -2758,6 +2770,8 @@ pub fn build_multi_entry_index(source_roots: &[String]) -> MultiEntryIndex { typed_module_cache: RefCell::new(HashMap::new()), module_source_identity: RefCell::new(HashMap::new()), resolved_graph_memo: RefCell::new(HashMap::new()), + normalize_diag_cache: RefCell::new(HashMap::new()), + ownership_diag_cache: RefCell::new(HashMap::new()), } } @@ -2777,6 +2791,8 @@ fn build_v1_attribution_multi_entry_index() -> MultiEntryIndex { typed_module_cache: RefCell::new(HashMap::new()), module_source_identity: RefCell::new(HashMap::new()), resolved_graph_memo: RefCell::new(HashMap::new()), + normalize_diag_cache: RefCell::new(HashMap::new()), + ownership_diag_cache: RefCell::new(HashMap::new()), } } @@ -3019,21 +3035,47 @@ fn resolve_entry_with_parse_cache( resolve_stage_slot_add(|s| s.resolve += resolve_started.elapsed().as_nanos()); let normalize_started = std::time::Instant::now(); - let norm = v1_compiler_normalize::normalize_graph(graph.clone(), source_indices.clone()); + // Per-module memo (normalize_diag_cache): normalize is diagnostics-only — the + // authority passes the graph through unchanged (v1.compiler.normalize + // `NormalizeResult { graph: graph, .. }`) — and its per-module row + // `normalize_module_diagnostics` is a pure function of the parsed module node, + // so an entry pays only for modules this process has not normalized before + // (resolve-split receipt: normalize was 8% of whole-corpus resolve, recomputed + // per entry at zero marginal information). + let mut norm_diag_vec: im_rc::Vector> = im_rc::Vector::new(); + for m in graph.modules.iter() { + let key = m.module.span.file.clone(); + let cached = index.normalize_diag_cache.borrow().get(&key).cloned(); + let module_diags = match cached { + Some(hit) => hit, + None => { + let computed = v1_compiler_normalize::normalize_module_diagnostics( + m.clone(), + source_indices.clone(), + ); + index + .normalize_diag_cache + .borrow_mut() + .insert(key, computed.clone()); + computed + } + }; + norm_diag_vec.extend(module_diags.iter().cloned()); + } + let norm_diags = Rc::new(norm_diag_vec); - if norm - .diagnostics + if norm_diags .iter() .any(|d| is_error_diagnostic(d.diagnostic.clone())) { - return Err(format_error_nodes(&norm.diagnostics, &source_indices)); + return Err(format_error_nodes(&norm_diags, &source_indices)); } resolve_stage_slot_add(|s| s.normalize += normalize_started.elapsed().as_nanos()); set_phase(FloorPhase::Typecheck, entry_file); let reconcile_started = std::time::Instant::now(); let typed = reconcile_with_typed_cache( - norm.graph.clone(), + graph.clone(), source_indices.clone(), global_table, &index.typed_module_cache, @@ -3064,8 +3106,33 @@ fn resolve_entry_with_parse_cache( } let ownership_started = std::time::Instant::now(); - let ownership = v1_compiler_compile::extract_ownership_proofs(typed.clone()); - let ownership_diags = v1_compiler_compile::ownership_diagnostics(ownership); + // Per-module memo (ownership_diag_cache): ownership proofs are a pure per-module + // map (v1.compiler.compile `module_ownership_proofs`; the authority's graph fold + // is exactly this row flat_mapped in module order) and `ownership_diagnostics` + // distributes over per-module concatenation, so the diagnostic list assembled in + // `typed.modules` order is identical to the graph-grain computation — a module + // with no bodied items contributes the same empty row the authority's filter + // skips. First-touch per module; the per-entry graph-grain rerun (7% of + // whole-corpus resolve in the resolve-split receipt) collapses to cache reads. + let mut ownership_diag_vec: im_rc::Vector> = im_rc::Vector::new(); + for m in typed.modules.iter() { + let key = m.module.span.file.clone(); + let cached = index.ownership_diag_cache.borrow().get(&key).cloned(); + let module_diags = match cached { + Some(hit) => hit, + None => { + let proofs = v1_compiler_compile::module_ownership_proofs(m.clone()); + let computed = v1_compiler_compile::ownership_diagnostics(proofs); + index + .ownership_diag_cache + .borrow_mut() + .insert(key, computed.clone()); + computed + } + }; + ownership_diag_vec.extend(module_diags.iter().cloned()); + } + let ownership_diags = Rc::new(ownership_diag_vec); if ownership_diags .iter() .any(|d| is_error_diagnostic(d.diagnostic.clone())) diff --git a/src/v1/stage0/src/v1_compiler_compile.rs b/src/v1/stage0/src/v1_compiler_compile.rs index 4065f10f509..2b24d37e818 100644 --- a/src/v1/stage0/src/v1_compiler_compile.rs +++ b/src/v1/stage0/src/v1_compiler_compile.rs @@ -191,6 +191,32 @@ pub fn build_recursion_context(typed: Rc) -> RecursionContext { RecursionContext {} } +pub fn module_ownership_proofs(m: Rc) -> Rc>> { + Rc::new({ + let mut __result = Vec::new(); + for item in Rc::new({ + let mut __result = Vec::new(); + for item in m.items.clone().iter().cloned() { + if (item.body.clone() != None) { + __result.push(item); + } + } + __result + }) + .iter() + .cloned() + { + __result.push(analyze_ownership( + authored_name_at(m.type_env.clone().source_indices.clone(), item.clone()), + item.params.clone(), + item.body.clone().clone().unwrap(), + m.type_env.clone().source_indices.clone(), + )); + } + __result + }) +} + pub fn extract_ownership_proofs(typed: Rc) -> Rc>> { Rc::new({ let mut __result = Vec::new(); @@ -215,36 +241,7 @@ pub fn extract_ownership_proofs(typed: Rc) -> Rc, + source_indices: Rc>>, +) -> Rc>> { + { + let items = module_items(m.module.clone()); + Rc::new({ + let mut __result = Vec::new(); + for item in items.clone().iter().cloned() { + __result.extend( + (*check_bare_containers( + item.clone(), + authored_name_at(source_indices.clone(), m.module.clone()), + source_indices.clone(), + )) + .iter() + .cloned(), + ); + } + __result + }) + } +} + pub fn normalize_graph( graph: Rc, source_indices: Rc>>, @@ -186,26 +210,9 @@ pub fn normalize_graph( let mut __result = Vec::new(); for m in graph.modules.clone().iter().cloned() { __result.extend( - (*{ - let items = module_items(m.module.clone()); - Rc::new({ - let mut __result = Vec::new(); - for item in items.clone().iter().cloned() { - __result.extend( - (*check_bare_containers( - item.clone(), - authored_name_at(source_indices.clone(), m.module.clone()), - source_indices.clone(), - )) - .iter() - .cloned(), - ); - } - __result - }) - }) - .iter() - .cloned(), + (*normalize_module_diagnostics(m.clone(), source_indices.clone())) + .iter() + .cloned(), ); } __result From d9e74e99810165aea97075bcb5315775d872c050 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 13 Jul 2026 21:02:58 +0000 Subject: [PATCH 4/5] WIP: corpus OOM --- src/v1/stage0/src/cli_run.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/v1/stage0/src/cli_run.rs b/src/v1/stage0/src/cli_run.rs index 5cda5388ae0..53f61ab0568 100644 --- a/src/v1/stage0/src/cli_run.rs +++ b/src/v1/stage0/src/cli_run.rs @@ -11967,6 +11967,13 @@ pub fn module_declaration_facts(pool_roots: &[String]) -> Vec i64 { pub fn non_fold_residue_unrostered_count() -> i64 { let roster: std::collections::BTreeSet<&str> = NON_FOLD_RESIDUE_ROSTER.iter().copied().collect(); + for s in nfr_build_report().sites.iter() { + if !roster.contains(s.as_str()) { + eprintln!("[nfr-unrostered] {s}"); + } + } nfr_build_report() .sites .iter() From 32ae0073cc1abd4ac170f03acbd7677544baa42e Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 13 Jul 2026 21:05:56 +0000 Subject: [PATCH 5/5] nfr roster backfill: #6533 frontier_blocker_class_matches landed unrostered (whole-corpus red; selection gap, third instance of #6530's masking class) Co-Authored-By: Claude Fable 5 --- src/v1/stage0/src/cli_run.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/v1/stage0/src/cli_run.rs b/src/v1/stage0/src/cli_run.rs index 53f61ab0568..e48db5bc619 100644 --- a/src/v1/stage0/src/cli_run.rs +++ b/src/v1/stage0/src/cli_run.rs @@ -11967,13 +11967,13 @@ pub fn module_declaration_facts(pool_roots: &[String]) -> Vec i64 { pub fn non_fold_residue_unrostered_count() -> i64 { let roster: std::collections::BTreeSet<&str> = NON_FOLD_RESIDUE_ROSTER.iter().copied().collect(); - for s in nfr_build_report().sites.iter() { - if !roster.contains(s.as_str()) { - eprintln!("[nfr-unrostered] {s}"); - } - } nfr_build_report() .sites .iter()