From 2b7537054a5b84b8b3007254b210f2dc226a685d Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Fri, 9 Oct 2026 01:18:14 +0000 Subject: [PATCH 1/6] Delete the separate policy Strict-prep; project the policy view from the gate subject. Policy is a subset of the gate keep-set, so a second prepared copy was authored duplication. Assemble bootstraps list-literal rosters, one gate prepare runs, then claim_scope_for v2.workflow.required_floor identity-joins; a missing policy module refuses EntryModuleOutsidePreparedSubject rather than re-preparing. Co-authored-by: Cursor --- .../src/cli_run/required_floor_runner.rs | 336 ++++++++++++++++-- 1 file changed, 311 insertions(+), 25 deletions(-) diff --git a/src/v1/stage0/src/cli_run/required_floor_runner.rs b/src/v1/stage0/src/cli_run/required_floor_runner.rs index 415badd7f77..964d6cb4766 100644 --- a/src/v1/stage0/src/cli_run/required_floor_runner.rs +++ b/src/v1/stage0/src/cli_run/required_floor_runner.rs @@ -5644,20 +5644,40 @@ pub fn required_floor_nominal_subject_seeds( required_floor_nominal_subject_seeds_from_corpus(&corpus, gate_entry_index) } -/// The seed fold over a corpus the CALLER read. `run_required_floor` is the least common ancestor -/// of this prepare and the gate prepare below it, so it reads once and lends to both; the wrapper -/// above stays for callers with only one demand (the lane resolution census). +/// Bootstrap seed lists from policy SOURCE (list literals), not a second Strict-prep. +/// Assemble uses these; after the one gate prepare, [`required_floor_nominal_subject_seeds_from_prepared`] +/// evals the same functions and identity-joins. A computed roster that is not a list literal +/// refuses here rather than silently re-preparing policy. pub fn required_floor_nominal_subject_seeds_from_corpus( corpus: &crate::cli_run::SourceCorpusRead, - gate_entry_index: &MultiEntryIndex, + _gate_entry_index: &MultiEntryIndex, ) -> Result { - let policy_seed = [REQUIRED_FLOOR_POLICY_MODULE.to_string()]; - let (policy_prepared, _) = crate::cli_run::prepare_repository_from_corpus( - corpus, - &floor_prepared_subject_exclusions(), - Some((gate_entry_index, &[], &policy_seed)), - )?; - let policy_scope = claim_scope_for(&policy_prepared, REQUIRED_FLOOR_POLICY_MODULE)?; + required_floor_nominal_subject_seeds_from_policy_source(corpus) +} + +fn required_floor_nominal_subject_seeds_from_policy_source( + corpus: &crate::cli_run::SourceCorpusRead, +) -> Result { + let policy_src = corpus_module_source(corpus, REQUIRED_FLOOR_POLICY_MODULE)?; + let required_gate_prefixes = dag_fn_string_list_literal(&policy_src, "required_gate_prefixes")?; + let required_gate_authored_modules = + dag_fn_string_list_literal(&policy_src, "required_gate_authored_modules")?; + let wet_src = corpus_module_source(corpus, "v2.workflow.local_repo_wet_terminal")?; + let local_repo_wet_schedule_rows = wet_entry_module_stubs(&wet_src); + Ok(RequiredFloorNominalSubjectSeeds { + required_gate_prefixes, + required_gate_authored_modules, + local_repo_wet_schedule_rows, + }) +} + +/// Project the policy view from the ONE prepared gate subject. `claim_scope_for` refuses +/// `EntryModuleOutsidePreparedSubject` if `v2.workflow.required_floor` is not in the graph. +/// There is no second prepare and no fallback. +pub fn required_floor_nominal_subject_seeds_from_prepared( + prepared: &crate::cli_run::PreparedRepository, +) -> Result { + let policy_scope = claim_scope_for(prepared, REQUIRED_FLOOR_POLICY_MODULE)?; let policy_frame = evaluation_frame( &policy_scope, v1_interpreter::ExecutionMode::Hermetic, @@ -5668,16 +5688,25 @@ pub fn required_floor_nominal_subject_seeds_from_corpus( &policy_frame, "v2.workflow.required_floor.required_gate_prefixes", )?; - // THE GATE'S SECOND SELECTOR KIND (`v2.workflow.required_floor` - // `required_gate_authored_modules`, `RequiredGateSelector`): authored module NAMES, - // matched at segment boundaries -- the module-seed rule, not the prefix rule. They join - // the module seeds, so preparation and site disposition admit them under one rule by - // construction. let required_gate_authored_modules = floor_decode_module_prefix_roster( &policy_frame, "v2.workflow.required_floor.required_gate_authored_modules", )?; let local_repo_wet_schedule_rows = local_repo_wet_schedule(&policy_frame)?; + for row in &local_repo_wet_schedule_rows { + if !prepared + .graph + .modules + .iter() + .any(|m| m.type_env.module_path == row.entry_module) + { + return Err(format!( + "CLAIM-SCOPE REFUSAL cause=EntryModuleOutsidePreparedSubject \ + module={} — wet-schedule entry is not in the prepared gate subject", + row.entry_module + )); + } + } Ok(RequiredFloorNominalSubjectSeeds { required_gate_prefixes, required_gate_authored_modules, @@ -5685,6 +5714,137 @@ pub fn required_floor_nominal_subject_seeds_from_corpus( }) } +fn corpus_module_source( + corpus: &crate::cli_run::SourceCorpusRead, + module_path: &str, +) -> Result { + corpus + .inventory + .iter() + .find(|row| row.module_path == module_path) + .map(|row| row.source.content.clone()) + .ok_or_else(|| { + format!("REQUIRED-FLOOR REFUSAL cause=PolicySourceMissing module={module_path}") + }) +} + +fn dag_fn_string_list_literal(source: &str, fn_name: &str) -> Result, String> { + let needle = format!("fn {fn_name}("); + let fn_at = source.find(&needle).ok_or_else(|| { + format!( + "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — no function" + ) + })?; + let after = &source[fn_at..]; + if !after.contains("List") { + return Err(format!( + "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — not List" + )); + } + let brack = after.find('[').ok_or_else(|| { + format!( + "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — body is not a list literal" + ) + })?; + let body = &after[brack + 1..]; + let mut out: Vec = Vec::new(); + let mut buf = String::new(); + let mut in_str = false; + let mut escaped = false; + let mut line_comment = false; + let chars = body.chars(); + for c in chars { + if line_comment { + if c == '\n' { + line_comment = false; + } + continue; + } + if in_str { + if escaped { + buf.push(c); + escaped = false; + continue; + } + if c == '\\' { + escaped = true; + continue; + } + if c == '"' { + in_str = false; + out.push(std::mem::take(&mut buf)); + continue; + } + buf.push(c); + continue; + } + if c == '/' { + line_comment = true; + continue; + } + if c == '"' { + in_str = true; + continue; + } + if c == ']' { + return Ok(out); + } + if c.is_whitespace() || c == ',' { + continue; + } + return Err(format!( + "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — leftover {c:?}" + )); + } + Err(format!( + "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — unclosed list" + )) +} + +fn wet_entry_module_stubs(wet_source: &str) -> Vec { + let mut rows = Vec::new(); + let mut seen = std::collections::HashSet::new(); + let key = "module_path: \""; + let mut rest = wet_source; + while let Some(at) = rest.find(key) { + let after = &rest[at + key.len()..]; + if let Some(end) = after.find('"') { + let module = &after[..end]; + if seen.insert(module.to_string()) { + rows.push(LocalRepoWetScheduledRow { + identity: String::new(), + entry: String::new(), + entry_module: module.to_string(), + function: String::new(), + premise: None, + }); + } + rest = &after[end + 1..]; + } else { + break; + } + } + rows +} + +fn policy_roster_identity_join( + bootstrap: &[String], + projected: &[String], + what: &str, +) -> Result<(), String> { + let left: std::collections::BTreeSet<&str> = bootstrap.iter().map(String::as_str).collect(); + let right: std::collections::BTreeSet<&str> = projected.iter().map(String::as_str).collect(); + let dropped: Vec<&str> = left.difference(&right).copied().collect(); + let added: Vec<&str> = right.difference(&left).copied().collect(); + if dropped.is_empty() && added.is_empty() { + return Ok(()); + } + Err(format!( + "REQUIRED-FLOOR REFUSAL cause=PolicyRosterProjectionMismatch what={what} \ + dropped={dropped:?} added={added:?}" + )) +} + /// The nominal MODULE seeds (`v2.workflow.floor_subject_seed` `PreparedSubjectSeedGround`, /// matched at segment boundaries): the floor's own runtime authorities, the gate's authored /// modules, and the wet schedule's entry modules. The prefix seeds travel separately because @@ -8396,13 +8556,9 @@ pub fn run_required_floor( floor_seam("changed-witness-planning"); // ONE CORPUS READ FOR BOTH PREPARES, CARRIED FROM THE ANCESTOR THAT OWNS BOTH DEMANDS. // - // This function prepares TWO subjects -- the policy closure through - // `required_floor_nominal_subject_seeds_from_corpus`, then the gate closure below -- and both - // call sites pass identical source roots, identical exclusions and this same - // `gate_entry_index`, differing ONLY in their closure seeds. Each prepare used to begin with - // its own `build_module_index(source_roots)`, which is not memoised: it walked every root and - // `read_to_string`d every `.dag` file in the corpus. So the floor read and indexed the whole - // corpus twice, on every run, for two questions that differ in their seeds and in nothing else. + // This function prepares ONE subject -- the gate closure. Policy rosters are bootstrapped + // from list literals for assemble, then projected with `claim_scope_for` after that prepare + // and identity-joined. A second Strict-prep of the policy subset is authored duplication. // // That is DESIGN §2's authored duplication rather than a cache obligation, and §2 names the // repair: when several demands share a least common ancestor, CARRY the first value. This is @@ -8482,7 +8638,7 @@ pub fn run_required_floor( let RequiredFloorNominalSubjectSeeds { required_gate_prefixes, required_gate_authored_modules, - local_repo_wet_schedule_rows, + mut local_repo_wet_schedule_rows, } = required_floor_nominal_subject_seeds_from_corpus(&floor_corpus, &gate_entry_index)?; // THE FLOOR'S OWN AUTHORITIES ARE ALWAYS IN THE SUBJECT: the floor evaluates its rosters // (expected red, route gap, cost debt, the gate itself) in a frame over the prepared graph, @@ -8866,6 +9022,28 @@ pub fn run_required_floor( )), )?; drop(gate_entry_index); + let projected = required_floor_nominal_subject_seeds_from_prepared(&prepared)?; + policy_roster_identity_join( + &required_gate_prefixes, + &projected.required_gate_prefixes, + "required_gate_prefixes", + )?; + policy_roster_identity_join( + &required_gate_authored_modules, + &projected.required_gate_authored_modules, + "required_gate_authored_modules", + )?; + let boot_wet: Vec = local_repo_wet_schedule_rows + .iter() + .map(|r| r.entry_module.clone()) + .collect(); + let proj_wet: Vec = projected + .local_repo_wet_schedule_rows + .iter() + .map(|r| r.entry_module.clone()) + .collect(); + policy_roster_identity_join(&boot_wet, &proj_wet, "local_repo_wet_schedule.entry_module")?; + local_repo_wet_schedule_rows = projected.local_repo_wet_schedule_rows; floor_seam("prepared-subject-warm"); floor_retained_census("prepared-subject-warm", Some(&prepared.graph)); // THE FULL INDEX THE DISCOVERY AUTHORITY WILL JUDGE, captured here because the prepared @@ -15284,7 +15462,7 @@ fn broken(s: Signal) -> Int {\n s.no_such_field\n}\n"; let index = build_multi_entry_index(&roots); let prepared = prepare_repository_closure( &roots, - &floor_prepared_subject_exclusions(), + &super::floor_prepared_subject_exclusions(), Some((&index, &[], &seeds)), ); // THE DISCRIMINATOR: without the exclusion rows the same seed list refuses on the probe. @@ -19404,4 +19582,112 @@ mod floor_base_tree_tests { assert_eq!(admitted(&added), vec!["t.new".to_string()]); std::fs::remove_dir_all(&dir).ok(); } + + #[test] + fn policy_roster_list_literals_parse_from_source() { + let policy = std::fs::read_to_string("src/v2/workflow/required_floor.dag") + .or_else(|_| { + std::fs::read_to_string( + super::super::process_workspace_root() + .join("src/v2/workflow/required_floor.dag"), + ) + }) + .expect("policy source"); + let prefixes = + super::dag_fn_string_list_literal(&policy, "required_gate_prefixes").expect("prefixes"); + let authored = super::dag_fn_string_list_literal(&policy, "required_gate_authored_modules") + .expect("authored"); + assert!( + prefixes.contains(&"test.claim.infer_".to_string()), + "dropped prefix: {prefixes:?}" + ); + assert!( + authored + .iter() + .any(|m| m == "test.claim.runner_capacity_plan_witness"), + "dropped authored module: {authored:?}" + ); + let wet = std::fs::read_to_string("src/v2/workflow/local_repo_wet_terminal.dag") + .or_else(|_| { + std::fs::read_to_string( + super::super::process_workspace_root() + .join("src/v2/workflow/local_repo_wet_terminal.dag"), + ) + }) + .expect("wet source"); + let stubs = super::wet_entry_module_stubs(&wet); + assert!( + stubs.iter().any(|r| r.entry_module.contains("test.claim.")), + "wet extract dropped every test.claim member: {stubs:?}" + ); + } + + #[test] + fn policy_roster_identity_join_reds_on_drop_or_add() { + let err = super::policy_roster_identity_join( + &["a".into(), "b".into()], + &["b".into(), "c".into()], + "probe", + ) + .expect_err("join must refuse"); + assert!(err.contains("PolicyRosterProjectionMismatch"), "{err}"); + assert!(err.contains("dropped=[\"a\"]"), "{err}"); + assert!(err.contains("added=[\"c\"]"), "{err}"); + super::policy_roster_identity_join(&["a".into()], &["a".into()], "probe").expect("equal"); + } + + #[test] + #[ignore = "live-corpus: one gate Strict-prep then identity-join policy members"] + fn projected_policy_scope_members_match_policy_keep_set() { + let roots = crate::cli_run::witness_gates::witness_layer_roots(); + let corpus = crate::cli_run::read_source_corpus_once(&roots); + let index = crate::cli_run::entry_resolve::try_process_shared_index(&roots).expect("index"); + let boot = super::required_floor_nominal_subject_seeds_from_corpus(&corpus, index.as_ref()) + .expect("bootstrap seeds"); + let module_seeds = super::required_floor_nominal_closure_module_seeds( + &boot.required_gate_authored_modules, + &boot.local_repo_wet_schedule_rows, + ); + let (prepared, _) = crate::cli_run::prepare_repository_from_corpus( + &corpus, + &super::floor_prepared_subject_exclusions(), + Some((index.as_ref(), &boot.required_gate_prefixes, &module_seeds)), + ) + .expect("one gate prepare"); + let projected = super::required_floor_nominal_subject_seeds_from_prepared(&prepared) + .expect("project policy"); + super::policy_roster_identity_join( + &boot.required_gate_prefixes, + &projected.required_gate_prefixes, + "required_gate_prefixes", + ) + .expect("prefix join"); + super::policy_roster_identity_join( + &boot.required_gate_authored_modules, + &projected.required_gate_authored_modules, + "required_gate_authored_modules", + ) + .expect("authored join"); + let policy_keep = crate::cli_run::assemble_prepared_subject_from_corpus( + &corpus, + &super::floor_prepared_subject_exclusions(), + Some(( + index.as_ref(), + &[], + &[super::super::REQUIRED_FLOOR_POLICY_MODULE.to_string()], + )), + ) + .expect("policy assemble"); + let keep: Vec = policy_keep + .inventory + .iter() + .map(|r| r.module_path.clone()) + .collect(); + let scope = + crate::cli_run::claim_scope_for(&prepared, super::super::REQUIRED_FLOOR_POLICY_MODULE) + .expect("policy in gate"); + let members: Vec = (*scope.scope_order).clone(); + super::policy_roster_identity_join(&keep, &members, "policy_scope_members") + .expect("policy member join"); + } } From 99c562f2969217e6e1649fe61a967054e0f1cea6 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Fri, 9 Oct 2026 01:39:01 +0000 Subject: [PATCH 2/6] Join live policy projection on roster identities, not assemble keep vs claim-scope. The live control compared both-closure keep to scope_order; those are different sets. Identity-join prefixes, authored modules, and wet entry_modules, and require the policy module in the projected claim scope. Co-authored-by: Cursor --- .../src/cli_run/required_floor_runner.rs | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/v1/stage0/src/cli_run/required_floor_runner.rs b/src/v1/stage0/src/cli_run/required_floor_runner.rs index 964d6cb4766..9d8cc7094be 100644 --- a/src/v1/stage0/src/cli_run/required_floor_runner.rs +++ b/src/v1/stage0/src/cli_run/required_floor_runner.rs @@ -19668,26 +19668,27 @@ mod floor_base_tree_tests { "required_gate_authored_modules", ) .expect("authored join"); - let policy_keep = crate::cli_run::assemble_prepared_subject_from_corpus( - &corpus, - &super::floor_prepared_subject_exclusions(), - Some(( - index.as_ref(), - &[], - &[super::super::REQUIRED_FLOOR_POLICY_MODULE.to_string()], - )), - ) - .expect("policy assemble"); - let keep: Vec = policy_keep - .inventory + let boot_wet: Vec = boot + .local_repo_wet_schedule_rows + .iter() + .map(|r| r.entry_module.clone()) + .collect(); + let proj_wet: Vec = projected + .local_repo_wet_schedule_rows .iter() - .map(|r| r.module_path.clone()) + .map(|r| r.entry_module.clone()) .collect(); + super::policy_roster_identity_join(&boot_wet, &proj_wet, "wet_entry_module") + .expect("wet join"); let scope = crate::cli_run::claim_scope_for(&prepared, super::super::REQUIRED_FLOOR_POLICY_MODULE) .expect("policy in gate"); - let members: Vec = (*scope.scope_order).clone(); - super::policy_roster_identity_join(&keep, &members, "policy_scope_members") - .expect("policy member join"); + assert!( + scope + .scope_order + .iter() + .any(|m| m == super::super::REQUIRED_FLOOR_POLICY_MODULE), + "projection dropped the policy module from the claim scope" + ); } } From 8dc65fbf5cc97b3bb1540f93a5a29fa6cc24705f Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Fri, 9 Oct 2026 02:24:50 +0000 Subject: [PATCH 3/6] Declare the list-literal seed parse as bootstrap over a real eval/prepare cycle. Eval of the policy rosters needs a prepared graph that already contains the policy module; the gate prepare's keep-set is computed from those rosters. Keep the parse, refuse any form it cannot read, and let the floor's post-prepare eval plus identity join remain the pairing inhabitance. Co-authored-by: Cursor --- .../src/cli_run/required_floor_runner.rs | 173 +++++++++++++++--- 1 file changed, 143 insertions(+), 30 deletions(-) diff --git a/src/v1/stage0/src/cli_run/required_floor_runner.rs b/src/v1/stage0/src/cli_run/required_floor_runner.rs index 9d8cc7094be..3b2c5749853 100644 --- a/src/v1/stage0/src/cli_run/required_floor_runner.rs +++ b/src/v1/stage0/src/cli_run/required_floor_runner.rs @@ -5645,9 +5645,26 @@ pub fn required_floor_nominal_subject_seeds( } /// Bootstrap seed lists from policy SOURCE (list literals), not a second Strict-prep. -/// Assemble uses these; after the one gate prepare, [`required_floor_nominal_subject_seeds_from_prepared`] -/// evals the same functions and identity-joins. A computed roster that is not a list literal -/// refuses here rather than silently re-preparing policy. +/// +/// THIS IS A REAL ORDERING CYCLE, NOT A CONVENIENCE. `claim_scope_for` / eval of +/// `required_gate_prefixes` needs a prepared graph that already contains +/// `v2.workflow.required_floor`. The gate prepare's assemble keep-set is computed FROM those +/// prefixes. Eval-first therefore requires a prior prepare of a subject that includes the policy +/// module without using the prefixes — which is the separate policy Strict-prep this change +/// deletes. A second prepare, even a tiny one, is that route again. So the seed lists are read +/// here as list literals, assemble/prepare once, then eval on that graph is the authority and +/// [`policy_roster_identity_join`] refuses any difference. +/// +/// Retirement: ingest those declarations as the SAME values eval produces (compiler-derived +/// constant fold / AST of the function body), not a byte walk of `.dag` text. Until then this +/// parse is bootstrap; a form it cannot read REFUSES (`PolicyRosterNotAListLiteral`), it never +/// skips or falls back to a second prepare. +/// +/// Assemble uses the bootstrap; after the one gate prepare, +/// [`required_floor_nominal_subject_seeds_from_prepared`] evals the same functions and +/// identity-joins. That eval on the floor is the inhabitance claim: deleting the projection +/// fails to compile `run_required_floor`; deleting the join leaves wet rows as empty stubs and +/// the wet schedule is not the evalled roster. pub fn required_floor_nominal_subject_seeds_from_corpus( corpus: &crate::cli_run::SourceCorpusRead, _gate_entry_index: &MultiEntryIndex, @@ -5663,7 +5680,7 @@ fn required_floor_nominal_subject_seeds_from_policy_source( let required_gate_authored_modules = dag_fn_string_list_literal(&policy_src, "required_gate_authored_modules")?; let wet_src = corpus_module_source(corpus, "v2.workflow.local_repo_wet_terminal")?; - let local_repo_wet_schedule_rows = wet_entry_module_stubs(&wet_src); + let local_repo_wet_schedule_rows = wet_entry_module_stubs(&wet_src)?; Ok(RequiredFloorNominalSubjectSeeds { required_gate_prefixes, required_gate_authored_modules, @@ -5728,7 +5745,7 @@ fn corpus_module_source( }) } -fn dag_fn_string_list_literal(source: &str, fn_name: &str) -> Result, String> { +fn dag_fn_body<'a>(source: &'a str, fn_name: &str) -> Result<&'a str, String> { let needle = format!("fn {fn_name}("); let fn_at = source.find(&needle).ok_or_else(|| { format!( @@ -5736,24 +5753,93 @@ fn dag_fn_string_list_literal(source: &str, fn_name: &str) -> Result ) })?; let after = &source[fn_at..]; - if !after.contains("List") { + let sig_end = after.find('{').ok_or_else(|| { + format!( + "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — no function body" + ) + })?; + let sig = &after[..sig_end]; + if !sig.contains("List") { return Err(format!( - "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — not List" + "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — signature is not List" )); } - let brack = after.find('[').ok_or_else(|| { + let body_start = fn_at + sig_end + 1; + let rest = &source[body_start..]; + let mut depth = 1i32; + let mut in_str = false; + let mut escaped = false; + let mut slash = false; + let mut line_comment = false; + for (i, c) in rest.char_indices() { + if line_comment { + if c == '\n' { + line_comment = false; + } + continue; + } + if in_str { + if escaped { + escaped = false; + continue; + } + if c == '\\' { + escaped = true; + continue; + } + if c == '"' { + in_str = false; + } + continue; + } + if slash { + slash = false; + if c == '/' { + line_comment = true; + continue; + } + return Err(format!( + "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — leftover '/'" + )); + } + match c { + '/' => slash = true, + '"' => in_str = true, + '{' => depth += 1, + '}' => { + depth -= 1; + if depth == 0 { + return Ok(&rest[..i]); + } + } + _ => {} + } + } + Err(format!( + "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — unclosed function body" + )) +} + +fn dag_fn_string_list_literal(source: &str, fn_name: &str) -> Result, String> { + let body = dag_fn_body(source, fn_name)?; + let brack = body.find('[').ok_or_else(|| { format!( "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — body is not a list literal" ) })?; - let body = &after[brack + 1..]; + if body[..brack].chars().any(|c| !c.is_whitespace()) { + return Err(format!( + "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — body is not a list literal" + )); + } + let list = &body[brack + 1..]; let mut out: Vec = Vec::new(); let mut buf = String::new(); let mut in_str = false; let mut escaped = false; + let mut slash = false; let mut line_comment = false; - let chars = body.chars(); - for c in chars { + for c in list.chars() { if line_comment { if c == '\n' { line_comment = false; @@ -5778,8 +5864,18 @@ fn dag_fn_string_list_literal(source: &str, fn_name: &str) -> Result buf.push(c); continue; } + if slash { + slash = false; + if c == '/' { + line_comment = true; + continue; + } + return Err(format!( + "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — leftover '/'" + )); + } if c == '/' { - line_comment = true; + slash = true; continue; } if c == '"' { @@ -5801,30 +5897,40 @@ fn dag_fn_string_list_literal(source: &str, fn_name: &str) -> Result )) } -fn wet_entry_module_stubs(wet_source: &str) -> Vec { +fn wet_entry_module_stubs(wet_source: &str) -> Result, String> { let mut rows = Vec::new(); let mut seen = std::collections::HashSet::new(); let key = "module_path: \""; let mut rest = wet_source; while let Some(at) = rest.find(key) { let after = &rest[at + key.len()..]; - if let Some(end) = after.find('"') { - let module = &after[..end]; - if seen.insert(module.to_string()) { - rows.push(LocalRepoWetScheduledRow { - identity: String::new(), - entry: String::new(), - entry_module: module.to_string(), - function: String::new(), - premise: None, - }); - } - rest = &after[end + 1..]; - } else { - break; + let Some(end) = after.find('"') else { + return Err( + "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn=local_repo_wet_schedule \ + — unclosed module_path string" + .to_string(), + ); + }; + let module = &after[..end]; + if seen.insert(module.to_string()) { + rows.push(LocalRepoWetScheduledRow { + identity: String::new(), + entry: String::new(), + entry_module: module.to_string(), + function: String::new(), + premise: None, + }); } + rest = &after[end + 1..]; + } + if rows.is_empty() { + return Err( + "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn=local_repo_wet_schedule \ + — no module_path string literals" + .to_string(), + ); } - rows + Ok(rows) } fn policy_roster_identity_join( @@ -19615,11 +19721,18 @@ mod floor_base_tree_tests { ) }) .expect("wet source"); - let stubs = super::wet_entry_module_stubs(&wet); + let stubs = super::wet_entry_module_stubs(&wet).expect("wet stubs"); assert!( stubs.iter().any(|r| r.entry_module.contains("test.claim.")), "wet extract dropped every test.claim member: {stubs:?}" ); + let computed = concat!( + "fn required_gate_prefixes() -> List { map(xs, x => x) }\n", + "fn other() -> List { [\"later\"] }\n" + ); + let err = super::dag_fn_string_list_literal(computed, "required_gate_prefixes") + .expect_err("computed body must refuse"); + assert!(err.contains("PolicyRosterNotAListLiteral"), "{err}"); } #[test] @@ -19637,7 +19750,7 @@ mod floor_base_tree_tests { } #[test] - #[ignore = "live-corpus: one gate Strict-prep then identity-join policy members"] + #[ignore = "one-off measurement; pairing inhabitance is run_required_floor after the one gate prepare"] fn projected_policy_scope_members_match_policy_keep_set() { let roots = crate::cli_run::witness_gates::witness_layer_roots(); let corpus = crate::cli_run::read_source_corpus_once(&roots); From 82a7f5bd0abf0e0b89563ea96aa76a475c21a4d1 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Fri, 9 Oct 2026 02:36:05 +0000 Subject: [PATCH 4/6] Bound wet bootstrap to the schedule function and stop fabricating schedule rows. review 78189: the wet grep walked every module_path in the file and minted empty identity/entry/function fields. Seed modules now come only from local_repo_wet_schedule's body; the wet lane runs the evalled rows after the one gate prepare. Co-authored-by: Cursor --- .../src/cli_run/required_floor_runner.rs | 202 ++++++++++-------- .../required_lane_resolution_census.rs | 2 +- src/v1/stage0/src/target_invocation_host.rs | 2 +- 3 files changed, 120 insertions(+), 86 deletions(-) diff --git a/src/v1/stage0/src/cli_run/required_floor_runner.rs b/src/v1/stage0/src/cli_run/required_floor_runner.rs index 3b2c5749853..efe9ad0d26f 100644 --- a/src/v1/stage0/src/cli_run/required_floor_runner.rs +++ b/src/v1/stage0/src/cli_run/required_floor_runner.rs @@ -5626,7 +5626,8 @@ pub(crate) fn floor_stream_enabled() -> bool { pub struct RequiredFloorNominalSubjectSeeds { pub required_gate_prefixes: Vec, pub required_gate_authored_modules: Vec, - pub(crate) local_repo_wet_schedule_rows: Vec, + /// Entry modules of `local_repo_wet_schedule` only — seed identities, not fabricated schedule rows. + pub(crate) local_repo_wet_seed_modules: Vec, } /// THE LANE'S SCHEDULE IS DECODED HERE, IN THE POLICY CLOSURE, AND NOT LATER -- because its @@ -5680,20 +5681,27 @@ fn required_floor_nominal_subject_seeds_from_policy_source( let required_gate_authored_modules = dag_fn_string_list_literal(&policy_src, "required_gate_authored_modules")?; let wet_src = corpus_module_source(corpus, "v2.workflow.local_repo_wet_terminal")?; - let local_repo_wet_schedule_rows = wet_entry_module_stubs(&wet_src)?; + let local_repo_wet_seed_modules = wet_schedule_seed_modules(&wet_src)?; Ok(RequiredFloorNominalSubjectSeeds { required_gate_prefixes, required_gate_authored_modules, - local_repo_wet_schedule_rows, + local_repo_wet_seed_modules, }) } /// Project the policy view from the ONE prepared gate subject. `claim_scope_for` refuses /// `EntryModuleOutsidePreparedSubject` if `v2.workflow.required_floor` is not in the graph. -/// There is no second prepare and no fallback. +/// There is no second prepare and no fallback. The evalled wet schedule rows are returned +/// beside the seed lists so the wet lane does not run on bootstrap stubs. pub fn required_floor_nominal_subject_seeds_from_prepared( prepared: &crate::cli_run::PreparedRepository, -) -> Result { +) -> Result< + ( + RequiredFloorNominalSubjectSeeds, + Vec, + ), + String, +> { let policy_scope = claim_scope_for(prepared, REQUIRED_FLOOR_POLICY_MODULE)?; let policy_frame = evaluation_frame( &policy_scope, @@ -5709,8 +5717,8 @@ pub fn required_floor_nominal_subject_seeds_from_prepared( &policy_frame, "v2.workflow.required_floor.required_gate_authored_modules", )?; - let local_repo_wet_schedule_rows = local_repo_wet_schedule(&policy_frame)?; - for row in &local_repo_wet_schedule_rows { + let wet_rows = local_repo_wet_schedule(&policy_frame)?; + for row in &wet_rows { if !prepared .graph .modules @@ -5724,11 +5732,21 @@ pub fn required_floor_nominal_subject_seeds_from_prepared( )); } } - Ok(RequiredFloorNominalSubjectSeeds { - required_gate_prefixes, - required_gate_authored_modules, - local_repo_wet_schedule_rows, - }) + let mut local_repo_wet_seed_modules: Vec = Vec::new(); + let mut seen = std::collections::HashSet::new(); + for row in &wet_rows { + if seen.insert(row.entry_module.clone()) { + local_repo_wet_seed_modules.push(row.entry_module.clone()); + } + } + Ok(( + RequiredFloorNominalSubjectSeeds { + required_gate_prefixes, + required_gate_authored_modules, + local_repo_wet_seed_modules, + }, + wet_rows, + )) } fn corpus_module_source( @@ -5758,12 +5776,6 @@ fn dag_fn_body<'a>(source: &'a str, fn_name: &str) -> Result<&'a str, String> { "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — no function body" ) })?; - let sig = &after[..sig_end]; - if !sig.contains("List") { - return Err(format!( - "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — signature is not List" - )); - } let body_start = fn_at + sig_end + 1; let rest = &source[body_start..]; let mut depth = 1i32; @@ -5821,6 +5833,22 @@ fn dag_fn_body<'a>(source: &'a str, fn_name: &str) -> Result<&'a str, String> { } fn dag_fn_string_list_literal(source: &str, fn_name: &str) -> Result, String> { + let needle = format!("fn {fn_name}("); + let fn_at = source.find(&needle).ok_or_else(|| { + format!( + "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — no function" + ) + })?; + let sig_end = source[fn_at..].find('{').ok_or_else(|| { + format!( + "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — no function body" + ) + })?; + if !source[fn_at..fn_at + sig_end].contains("List") { + return Err(format!( + "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — signature is not List" + )); + } let body = dag_fn_body(source, fn_name)?; let brack = body.find('[').ok_or_else(|| { format!( @@ -5897,40 +5925,54 @@ fn dag_fn_string_list_literal(source: &str, fn_name: &str) -> Result )) } -fn wet_entry_module_stubs(wet_source: &str) -> Result, String> { - let mut rows = Vec::new(); +fn wet_schedule_seed_modules(wet_source: &str) -> Result, String> { + let body = dag_fn_body(wet_source, "local_repo_wet_schedule")?; + let mut modules = Vec::new(); let mut seen = std::collections::HashSet::new(); let key = "module_path: \""; - let mut rest = wet_source; - while let Some(at) = rest.find(key) { - let after = &rest[at + key.len()..]; - let Some(end) = after.find('"') else { - return Err( - "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn=local_repo_wet_schedule \ - — unclosed module_path string" - .to_string(), - ); - }; - let module = &after[..end]; - if seen.insert(module.to_string()) { - rows.push(LocalRepoWetScheduledRow { - identity: String::new(), - entry: String::new(), - entry_module: module.to_string(), - function: String::new(), - premise: None, - }); + let rest = body; + let mut line_comment = false; + let mut i = 0; + let bytes = rest.as_bytes(); + while i < rest.len() { + if line_comment { + if rest.as_bytes()[i] == b'\n' { + line_comment = false; + } + i += 1; + continue; } - rest = &after[end + 1..]; + if i + 1 < bytes.len() && bytes[i] == b'/' && bytes[i + 1] == b'/' { + line_comment = true; + i += 2; + continue; + } + if rest[i..].starts_with(key) { + let after = &rest[i + key.len()..]; + let Some(end) = after.find('"') else { + return Err( + "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn=local_repo_wet_schedule \ + — unclosed module_path string" + .to_string(), + ); + }; + let module = &after[..end]; + if seen.insert(module.to_string()) { + modules.push(module.to_string()); + } + i += key.len() + end + 1; + continue; + } + i += 1; } - if rows.is_empty() { + if modules.is_empty() { return Err( "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn=local_repo_wet_schedule \ — no module_path string literals" .to_string(), ); } - Ok(rows) + Ok(modules) } fn policy_roster_identity_join( @@ -5957,23 +5999,16 @@ fn policy_roster_identity_join( /// they are matched by a different rule. pub(crate) fn required_floor_nominal_closure_module_seeds( required_gate_authored_modules: &[String], - local_repo_wet_schedule_rows: &[LocalRepoWetScheduledRow], + local_repo_wet_seed_modules: &[String], ) -> Vec { REQUIRED_FLOOR_RUNTIME_AUTHORITY_MODULES .iter() .map(|m| m.to_string()) .chain(required_gate_authored_modules.iter().cloned()) - .chain( - local_repo_wet_schedule_rows - .iter() - .map(|row| row.entry_module.clone()), - ) - .chain( - local_repo_wet_schedule_rows - .iter() - .any(|row| row.premise.is_some()) - .then(|| LOCAL_REPO_WET_PREMISE_READBACK_MODULE.to_string()), - ) + .chain(local_repo_wet_seed_modules.iter().cloned()) + .chain(std::iter::once( + LOCAL_REPO_WET_PREMISE_READBACK_MODULE.to_string(), + )) .collect() } @@ -8744,7 +8779,7 @@ pub fn run_required_floor( let RequiredFloorNominalSubjectSeeds { required_gate_prefixes, required_gate_authored_modules, - mut local_repo_wet_schedule_rows, + local_repo_wet_seed_modules, } = required_floor_nominal_subject_seeds_from_corpus(&floor_corpus, &gate_entry_index)?; // THE FLOOR'S OWN AUTHORITIES ARE ALWAYS IN THE SUBJECT: the floor evaluates its rosters // (expected red, route gap, cost debt, the gate itself) in a frame over the prepared graph, @@ -9093,7 +9128,7 @@ pub fn run_required_floor( }; let closure_module_seeds: Vec = required_floor_nominal_closure_module_seeds( &required_gate_authored_modules, - &local_repo_wet_schedule_rows, + &local_repo_wet_seed_modules, ) .into_iter() .chain(checker_module_seeds) @@ -9128,7 +9163,8 @@ pub fn run_required_floor( )), )?; drop(gate_entry_index); - let projected = required_floor_nominal_subject_seeds_from_prepared(&prepared)?; + let (projected, local_repo_wet_schedule_rows) = + required_floor_nominal_subject_seeds_from_prepared(&prepared)?; policy_roster_identity_join( &required_gate_prefixes, &projected.required_gate_prefixes, @@ -9139,17 +9175,11 @@ pub fn run_required_floor( &projected.required_gate_authored_modules, "required_gate_authored_modules", )?; - let boot_wet: Vec = local_repo_wet_schedule_rows - .iter() - .map(|r| r.entry_module.clone()) - .collect(); - let proj_wet: Vec = projected - .local_repo_wet_schedule_rows - .iter() - .map(|r| r.entry_module.clone()) - .collect(); - policy_roster_identity_join(&boot_wet, &proj_wet, "local_repo_wet_schedule.entry_module")?; - local_repo_wet_schedule_rows = projected.local_repo_wet_schedule_rows; + policy_roster_identity_join( + &local_repo_wet_seed_modules, + &projected.local_repo_wet_seed_modules, + "local_repo_wet_schedule.entry_module", + )?; floor_seam("prepared-subject-warm"); floor_retained_census("prepared-subject-warm", Some(&prepared.graph)); // THE FULL INDEX THE DISCOVERY AUTHORITY WILL JUDGE, captured here because the prepared @@ -19721,9 +19751,9 @@ mod floor_base_tree_tests { ) }) .expect("wet source"); - let stubs = super::wet_entry_module_stubs(&wet).expect("wet stubs"); + let stubs = super::wet_schedule_seed_modules(&wet).expect("wet seeds"); assert!( - stubs.iter().any(|r| r.entry_module.contains("test.claim.")), + stubs.iter().any(|m| m.contains("test.claim.")), "wet extract dropped every test.claim member: {stubs:?}" ); let computed = concat!( @@ -19733,6 +19763,16 @@ mod floor_base_tree_tests { let err = super::dag_fn_string_list_literal(computed, "required_gate_prefixes") .expect_err("computed body must refuse"); assert!(err.contains("PolicyRosterNotAListLiteral"), "{err}"); + let wet_scoped = concat!( + "fn local_repo_wet_schedule() -> List {\n", + " [ WetScheduledClaim { identity: WitnessIdentity { module_path: \"in.schedule\", function: \"f\" } } ]\n", + "}\n", + "fn local_repo_wet_premise_roster() -> List {\n", + " [ WetPremisedClaim { identity: WitnessIdentity { module_path: \"only.premise\", function: \"g\" } } ]\n", + "}\n", + ); + let scoped = super::wet_schedule_seed_modules(wet_scoped).expect("scoped wet"); + assert_eq!(scoped, vec!["in.schedule".to_string()]); } #[test] @@ -19759,7 +19799,7 @@ mod floor_base_tree_tests { .expect("bootstrap seeds"); let module_seeds = super::required_floor_nominal_closure_module_seeds( &boot.required_gate_authored_modules, - &boot.local_repo_wet_schedule_rows, + &boot.local_repo_wet_seed_modules, ); let (prepared, _) = crate::cli_run::prepare_repository_from_corpus( &corpus, @@ -19767,7 +19807,7 @@ mod floor_base_tree_tests { Some((index.as_ref(), &boot.required_gate_prefixes, &module_seeds)), ) .expect("one gate prepare"); - let projected = super::required_floor_nominal_subject_seeds_from_prepared(&prepared) + let (projected, _) = super::required_floor_nominal_subject_seeds_from_prepared(&prepared) .expect("project policy"); super::policy_roster_identity_join( &boot.required_gate_prefixes, @@ -19781,18 +19821,12 @@ mod floor_base_tree_tests { "required_gate_authored_modules", ) .expect("authored join"); - let boot_wet: Vec = boot - .local_repo_wet_schedule_rows - .iter() - .map(|r| r.entry_module.clone()) - .collect(); - let proj_wet: Vec = projected - .local_repo_wet_schedule_rows - .iter() - .map(|r| r.entry_module.clone()) - .collect(); - super::policy_roster_identity_join(&boot_wet, &proj_wet, "wet_entry_module") - .expect("wet join"); + super::policy_roster_identity_join( + &boot.local_repo_wet_seed_modules, + &projected.local_repo_wet_seed_modules, + "wet_entry_module", + ) + .expect("wet join"); let scope = crate::cli_run::claim_scope_for(&prepared, super::super::REQUIRED_FLOOR_POLICY_MODULE) .expect("policy in gate"); diff --git a/src/v1/stage0/src/cli_run/required_lane_resolution_census.rs b/src/v1/stage0/src/cli_run/required_lane_resolution_census.rs index 0dedbd60c6f..40e5c058c4f 100644 --- a/src/v1/stage0/src/cli_run/required_lane_resolution_census.rs +++ b/src/v1/stage0/src/cli_run/required_lane_resolution_census.rs @@ -69,7 +69,7 @@ pub fn required_floor_nominal_subject_module_identities( }; let module_seeds = required_floor_nominal_closure_module_seeds( &seeds.required_gate_authored_modules, - &seeds.local_repo_wet_schedule_rows, + &seeds.local_repo_wet_seed_modules, ); let subject = match assemble_prepared_subject_closure( source_roots, diff --git a/src/v1/stage0/src/target_invocation_host.rs b/src/v1/stage0/src/target_invocation_host.rs index 30c0268b5f5..bea6adbf472 100644 --- a/src/v1/stage0/src/target_invocation_host.rs +++ b/src/v1/stage0/src/target_invocation_host.rs @@ -2833,7 +2833,7 @@ fn run_typed_graph_exclusive_bytes_floor_subject() -> InvocationOutcome { cli_run::required_floor_nominal_subject_seeds_from_corpus(&corpus, &gate_entry_index)?; let module_seeds = cli_run::required_floor_nominal_closure_module_seeds( &seeds.required_gate_authored_modules, - &seeds.local_repo_wet_schedule_rows, + &seeds.local_repo_wet_seed_modules, ); // THE FLOOR'S OWN SUBJECT, COMPILED AS ITS STRICT PREPARE COMPILES IT -- before the prepared // repository drops the typecheck caches -- because the floor's peak is inside that compile, From 23068594eac244fddd67dd3087bcb25df0b1e845 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Fri, 9 Oct 2026 05:21:54 +0000 Subject: [PATCH 5/6] Refuse unreadable bootstrap roster forms and admit the policy projection as one composition. The census consumes the source parse with no join, so a trailing list operation or a computed wet member beside a literal one must refuse rather than skip. Pairing goes through required_floor_admit_policy_projection so a disagreeing eval fires PolicyRosterProjectionMismatch before wet rows run. Co-authored-by: Cursor --- .../src/cli_run/required_floor_runner.rs | 513 +++++++++++++----- 1 file changed, 371 insertions(+), 142 deletions(-) diff --git a/src/v1/stage0/src/cli_run/required_floor_runner.rs b/src/v1/stage0/src/cli_run/required_floor_runner.rs index efe9ad0d26f..65e44be24a9 100644 --- a/src/v1/stage0/src/cli_run/required_floor_runner.rs +++ b/src/v1/stage0/src/cli_run/required_floor_runner.rs @@ -5662,10 +5662,13 @@ pub fn required_floor_nominal_subject_seeds( /// skips or falls back to a second prepare. /// /// Assemble uses the bootstrap; after the one gate prepare, -/// [`required_floor_nominal_subject_seeds_from_prepared`] evals the same functions and -/// identity-joins. That eval on the floor is the inhabitance claim: deleting the projection -/// fails to compile `run_required_floor`; deleting the join leaves wet rows as empty stubs and -/// the wet schedule is not the evalled roster. +/// [`required_floor_admit_policy_projection`] identity-joins bootstrap vs eval. The census +/// (`required_floor_nominal_subject_module_identities`) consumes the bootstrap with no join, so +/// the reader is all-or-refuse: a form it cannot read REFUSES (`PolicyRosterNotAListLiteral`), +/// it never skips a member or a trailing operation. Pairing: a disagreeing projection through +/// that admit function fires `PolicyRosterProjectionMismatch` before wet rows reach execution; +/// deleting the join reds that negative. The evalled wet rows are a separate return and are +/// not emptied by deleting the join. pub fn required_floor_nominal_subject_seeds_from_corpus( corpus: &crate::cli_run::SourceCorpusRead, _gate_entry_index: &MultiEntryIndex, @@ -5832,51 +5835,162 @@ fn dag_fn_body<'a>(source: &'a str, fn_name: &str) -> Result<&'a str, String> { )) } +struct DagSrc<'a> { + s: &'a str, + i: usize, +} + +impl<'a> DagSrc<'a> { + fn new(s: &'a str) -> Self { + Self { s, i: 0 } + } + fn rest(&self) -> &'a str { + &self.s[self.i..] + } + fn peek(&self) -> Option { + self.rest().chars().next() + } + fn bump(&mut self) -> Option { + let c = self.peek()?; + self.i += c.len_utf8(); + Some(c) + } + fn skip_ws_comments(&mut self) { + loop { + match self.peek() { + Some(c) if c.is_whitespace() => { + self.bump(); + } + Some('/') => { + let rest = self.rest(); + if rest.starts_with("//") { + while let Some(c) = self.peek() { + self.bump(); + if c == '\n' { + break; + } + } + } else { + return; + } + } + _ => return, + } + } + } + fn starts_with(&self, lit: &str) -> bool { + self.rest().starts_with(lit) + } + fn eat(&mut self, lit: &str) -> bool { + if self.starts_with(lit) { + self.i += lit.len(); + true + } else { + false + } + } + fn parse_string(&mut self) -> Result { + if !self.eat("\"") { + return Err("expected string literal".to_string()); + } + let mut buf = String::new(); + let mut escaped = false; + while let Some(c) = self.bump() { + if escaped { + buf.push(c); + escaped = false; + continue; + } + if c == '\\' { + escaped = true; + continue; + } + if c == '"' { + return Ok(buf); + } + buf.push(c); + } + Err("unclosed string".to_string()) + } + fn parse_ident(&mut self) -> Result { + let mut buf = String::new(); + match self.peek() { + Some(c) if c.is_ascii_alphabetic() || c == '_' => { + buf.push(c); + self.bump(); + } + _ => return Err("expected identifier".to_string()), + } + while let Some(c) = self.peek() { + if c.is_ascii_alphanumeric() || c == '_' { + buf.push(c); + self.bump(); + } else { + break; + } + } + Ok(buf) + } +} + +fn roster_refuse(fn_name: &str, why: &str) -> String { + format!("REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — {why}") +} + fn dag_fn_string_list_literal(source: &str, fn_name: &str) -> Result, String> { let needle = format!("fn {fn_name}("); - let fn_at = source.find(&needle).ok_or_else(|| { - format!( - "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — no function" - ) - })?; - let sig_end = source[fn_at..].find('{').ok_or_else(|| { - format!( - "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — no function body" - ) - })?; + let fn_at = source + .find(&needle) + .ok_or_else(|| roster_refuse(fn_name, "no function"))?; + let sig_end = source[fn_at..] + .find('{') + .ok_or_else(|| roster_refuse(fn_name, "no function body"))?; if !source[fn_at..fn_at + sig_end].contains("List") { - return Err(format!( - "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — signature is not List" - )); + return Err(roster_refuse(fn_name, "signature is not List")); } let body = dag_fn_body(source, fn_name)?; - let brack = body.find('[').ok_or_else(|| { - format!( - "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — body is not a list literal" - ) - })?; - if body[..brack].chars().any(|c| !c.is_whitespace()) { - return Err(format!( - "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — body is not a list literal" - )); + let mut src = DagSrc::new(body); + src.skip_ws_comments(); + if !src.eat("[") { + return Err(roster_refuse(fn_name, "body is not a list literal")); } - let list = &body[brack + 1..]; let mut out: Vec = Vec::new(); - let mut buf = String::new(); - let mut in_str = false; - let mut escaped = false; - let mut slash = false; - let mut line_comment = false; - for c in list.chars() { - if line_comment { - if c == '\n' { - line_comment = false; - } + loop { + src.skip_ws_comments(); + if src.eat("]") { + break; + } + if src.peek() == Some('"') { + out.push(src.parse_string().map_err(|e| roster_refuse(fn_name, &e))?); + src.skip_ws_comments(); + src.eat(","); continue; } + return Err(roster_refuse( + fn_name, + "list member is not a string literal", + )); + } + src.skip_ws_comments(); + if src.peek().is_some() { + return Err(roster_refuse( + fn_name, + "trailing operation after the list literal", + )); + } + Ok(out) +} + +fn skip_braced_value(src: &mut DagSrc<'_>, fn_name: &str) -> Result<(), String> { + let mut depth = 1i32; + let mut in_str = false; + let mut escaped = false; + while depth > 0 { + let c = src + .bump() + .ok_or_else(|| roster_refuse(fn_name, "unclosed brace"))?; if in_str { if escaped { - buf.push(c); escaped = false; continue; } @@ -5886,91 +6000,163 @@ fn dag_fn_string_list_literal(source: &str, fn_name: &str) -> Result } if c == '"' { in_str = false; - out.push(std::mem::take(&mut buf)); - continue; } - buf.push(c); continue; } - if slash { - slash = false; - if c == '/' { - line_comment = true; - continue; + match c { + '"' => in_str = true, + '{' => depth += 1, + '}' => depth -= 1, + '/' if src.peek() == Some('/') => { + while let Some(cc) = src.bump() { + if cc == '\n' { + break; + } + } } - return Err(format!( - "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — leftover '/'" - )); + _ => {} } - if c == '/' { - slash = true; - continue; + } + Ok(()) +} + +fn skip_wet_field_value(src: &mut DagSrc<'_>, fn_name: &str) -> Result<(), String> { + src.skip_ws_comments(); + if src.peek() == Some('"') { + src.parse_string().map_err(|e| roster_refuse(fn_name, &e))?; + return Ok(()); + } + src.parse_ident() + .map_err(|_| roster_refuse(fn_name, "unreadable field value"))?; + src.skip_ws_comments(); + if src.eat("(") { + return Err(roster_refuse(fn_name, "computed field value")); + } + if src.eat("{") { + skip_braced_value(src, fn_name)?; + } + Ok(()) +} + +fn parse_wet_witness_identity(src: &mut DagSrc<'_>, fn_name: &str) -> Result { + src.skip_ws_comments(); + if !src.eat("WitnessIdentity") { + return Err(roster_refuse( + fn_name, + "identity is not a WitnessIdentity literal", + )); + } + src.skip_ws_comments(); + if !src.eat("{") { + return Err(roster_refuse(fn_name, "identity record unopened")); + } + let mut module_path = None; + loop { + src.skip_ws_comments(); + if src.eat("}") { + break; } - if c == '"' { - in_str = true; - continue; + let field = src + .parse_ident() + .map_err(|_| roster_refuse(fn_name, "unreadable identity field"))?; + src.skip_ws_comments(); + if !src.eat(":") { + return Err(roster_refuse(fn_name, "identity field missing ':'")); + } + src.skip_ws_comments(); + match field.as_str() { + "module_path" | "function" => { + let v = src.parse_string().map_err(|_| { + roster_refuse(fn_name, "identity field is not a string literal") + })?; + if field == "module_path" { + module_path = Some(v); + } + } + _ => { + return Err(roster_refuse( + fn_name, + &format!("unknown identity field {field}"), + )); + } } - if c == ']' { - return Ok(out); + src.skip_ws_comments(); + src.eat(","); + } + module_path.ok_or_else(|| roster_refuse(fn_name, "identity carries no module_path literal")) +} + +fn parse_wet_scheduled_claim(src: &mut DagSrc<'_>, fn_name: &str) -> Result { + src.skip_ws_comments(); + if !src.eat("WetScheduledClaim") { + return Err(roster_refuse( + fn_name, + "list member is not a WetScheduledClaim literal", + )); + } + src.skip_ws_comments(); + if !src.eat("{") { + return Err(roster_refuse(fn_name, "WetScheduledClaim record unopened")); + } + let mut module_path = None; + loop { + src.skip_ws_comments(); + if src.eat("}") { + break; } - if c.is_whitespace() || c == ',' { - continue; + let field = src + .parse_ident() + .map_err(|_| roster_refuse(fn_name, "unreadable WetScheduledClaim field"))?; + src.skip_ws_comments(); + if !src.eat(":") { + return Err(roster_refuse( + fn_name, + "WetScheduledClaim field missing ':'", + )); } - return Err(format!( - "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — leftover {c:?}" - )); + if field == "identity" { + module_path = Some(parse_wet_witness_identity(src, fn_name)?); + } else { + skip_wet_field_value(src, fn_name)?; + } + src.skip_ws_comments(); + src.eat(","); } - Err(format!( - "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn={fn_name} — unclosed list" - )) + module_path + .ok_or_else(|| roster_refuse(fn_name, "WetScheduledClaim carries no identity.module_path")) } fn wet_schedule_seed_modules(wet_source: &str) -> Result, String> { - let body = dag_fn_body(wet_source, "local_repo_wet_schedule")?; + let fn_name = "local_repo_wet_schedule"; + let body = dag_fn_body(wet_source, fn_name)?; + let mut src = DagSrc::new(body); + src.skip_ws_comments(); + if !src.eat("[") { + return Err(roster_refuse(fn_name, "body is not a list literal")); + } let mut modules = Vec::new(); let mut seen = std::collections::HashSet::new(); - let key = "module_path: \""; - let rest = body; - let mut line_comment = false; - let mut i = 0; - let bytes = rest.as_bytes(); - while i < rest.len() { - if line_comment { - if rest.as_bytes()[i] == b'\n' { - line_comment = false; - } - i += 1; - continue; - } - if i + 1 < bytes.len() && bytes[i] == b'/' && bytes[i + 1] == b'/' { - line_comment = true; - i += 2; - continue; + loop { + src.skip_ws_comments(); + if src.eat("]") { + break; } - if rest[i..].starts_with(key) { - let after = &rest[i + key.len()..]; - let Some(end) = after.find('"') else { - return Err( - "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn=local_repo_wet_schedule \ - — unclosed module_path string" - .to_string(), - ); - }; - let module = &after[..end]; - if seen.insert(module.to_string()) { - modules.push(module.to_string()); - } - i += key.len() + end + 1; - continue; + let module = parse_wet_scheduled_claim(&mut src, fn_name)?; + if seen.insert(module.clone()) { + modules.push(module); } - i += 1; + src.skip_ws_comments(); + src.eat(","); + } + src.skip_ws_comments(); + if src.peek().is_some() { + return Err(roster_refuse( + fn_name, + "trailing operation after the list literal", + )); } if modules.is_empty() { - return Err( - "REQUIRED-FLOOR REFUSAL cause=PolicyRosterNotAListLiteral fn=local_repo_wet_schedule \ - — no module_path string literals" - .to_string(), - ); + return Err(roster_refuse(fn_name, "no WetScheduledClaim members")); } Ok(modules) } @@ -5993,6 +6179,31 @@ fn policy_roster_identity_join( )) } +/// The composition `run_required_floor` calls after the one gate prepare. Wet rows from +/// [`required_floor_nominal_subject_seeds_from_prepared`] must not reach execution until this +/// returns. Deleting these joins reds a disagreeing projection; an equal pair stays green. +fn required_floor_admit_policy_projection( + bootstrap: &RequiredFloorNominalSubjectSeeds, + projected: &RequiredFloorNominalSubjectSeeds, +) -> Result<(), String> { + policy_roster_identity_join( + &bootstrap.required_gate_prefixes, + &projected.required_gate_prefixes, + "required_gate_prefixes", + )?; + policy_roster_identity_join( + &bootstrap.required_gate_authored_modules, + &projected.required_gate_authored_modules, + "required_gate_authored_modules", + )?; + policy_roster_identity_join( + &bootstrap.local_repo_wet_seed_modules, + &projected.local_repo_wet_seed_modules, + "local_repo_wet_schedule.entry_module", + )?; + Ok(()) +} + /// The nominal MODULE seeds (`v2.workflow.floor_subject_seed` `PreparedSubjectSeedGround`, /// matched at segment boundaries): the floor's own runtime authorities, the gate's authored /// modules, and the wet schedule's entry modules. The prefix seeds travel separately because @@ -8776,11 +8987,11 @@ pub fn run_required_floor( // THE NOMINAL SEEDS -- gate prefixes, gate authored modules, the wet schedule -- come from // the one producer the resolution census also reads, so what the floor prepares on a run // that touches nothing and what the census reports as reached are the same fact. - let RequiredFloorNominalSubjectSeeds { - required_gate_prefixes, - required_gate_authored_modules, - local_repo_wet_seed_modules, - } = required_floor_nominal_subject_seeds_from_corpus(&floor_corpus, &gate_entry_index)?; + let bootstrap_seeds = + required_floor_nominal_subject_seeds_from_corpus(&floor_corpus, &gate_entry_index)?; + let required_gate_prefixes = bootstrap_seeds.required_gate_prefixes.clone(); + let required_gate_authored_modules = bootstrap_seeds.required_gate_authored_modules.clone(); + let local_repo_wet_seed_modules = bootstrap_seeds.local_repo_wet_seed_modules.clone(); // THE FLOOR'S OWN AUTHORITIES ARE ALWAYS IN THE SUBJECT: the floor evaluates its rosters // (expected red, route gap, cost debt, the gate itself) in a frame over the prepared graph, // and a gate roster that happened not to reach `v2.workflow.required_floor` refused with @@ -9165,21 +9376,7 @@ pub fn run_required_floor( drop(gate_entry_index); let (projected, local_repo_wet_schedule_rows) = required_floor_nominal_subject_seeds_from_prepared(&prepared)?; - policy_roster_identity_join( - &required_gate_prefixes, - &projected.required_gate_prefixes, - "required_gate_prefixes", - )?; - policy_roster_identity_join( - &required_gate_authored_modules, - &projected.required_gate_authored_modules, - "required_gate_authored_modules", - )?; - policy_roster_identity_join( - &local_repo_wet_seed_modules, - &projected.local_repo_wet_seed_modules, - "local_repo_wet_schedule.entry_module", - )?; + required_floor_admit_policy_projection(&bootstrap_seeds, &projected)?; floor_seam("prepared-subject-warm"); floor_retained_census("prepared-subject-warm", Some(&prepared.graph)); // THE FULL INDEX THE DISCOVERY AUTHORITY WILL JUDGE, captured here because the prepared @@ -19773,6 +19970,30 @@ mod floor_base_tree_tests { ); let scoped = super::wet_schedule_seed_modules(wet_scoped).expect("scoped wet"); assert_eq!(scoped, vec!["in.schedule".to_string()]); + let skip = "fn required_gate_prefixes() -> List { [\"a\", \"b\"].skip(n: 1) }\n"; + let err = super::dag_fn_string_list_literal(skip, "required_gate_prefixes") + .expect_err("post-list op must refuse"); + assert!(err.contains("trailing operation"), "{err}"); + let spaced = concat!( + "fn local_repo_wet_schedule() -> List {\n", + " [ WetScheduledClaim { identity: WitnessIdentity { module_path : \"spaced.path\", function: \"f\" } } ]\n", + "}\n", + ); + assert_eq!( + super::wet_schedule_seed_modules(spaced).expect("spaced colon"), + vec!["spaced.path".to_string()] + ); + let mixed = concat!( + "fn local_repo_wet_schedule() -> List {\n", + " [\n", + " WetScheduledClaim { identity: WitnessIdentity { module_path: \"readable.path\", function: \"f\" } },\n", + " WetScheduledClaim { identity: WitnessIdentity { module_path: concat(\"a\", \"b\"), function: \"g\" } }\n", + " ]\n", + "}\n", + ); + let err = + super::wet_schedule_seed_modules(mixed).expect_err("computed member beside literal"); + assert!(err.contains("PolicyRosterNotAListLiteral"), "{err}"); } #[test] @@ -19789,6 +20010,31 @@ mod floor_base_tree_tests { super::policy_roster_identity_join(&["a".into()], &["a".into()], "probe").expect("equal"); } + #[test] + fn required_floor_admit_policy_projection_reds_on_disagreeing_projection() { + fn seeds( + prefixes: &[&str], + authored: &[&str], + wet: &[&str], + ) -> super::RequiredFloorNominalSubjectSeeds { + super::RequiredFloorNominalSubjectSeeds { + required_gate_prefixes: prefixes.iter().map(|s| (*s).to_string()).collect(), + required_gate_authored_modules: authored.iter().map(|s| (*s).to_string()).collect(), + local_repo_wet_seed_modules: wet.iter().map(|s| (*s).to_string()).collect(), + } + } + let bootstrap = seeds(&["p.a"], &["m.a"], &["w.a"]); + super::required_floor_admit_policy_projection(&bootstrap, &bootstrap).expect("equal"); + let disagree = seeds(&["p.a"], &["m.a"], &["w.b"]); + let err = super::required_floor_admit_policy_projection(&bootstrap, &disagree) + .expect_err("disagreeing wet projection"); + assert!(err.contains("PolicyRosterProjectionMismatch"), "{err}"); + assert!( + err.contains("local_repo_wet_schedule.entry_module"), + "{err}" + ); + } + #[test] #[ignore = "one-off measurement; pairing inhabitance is run_required_floor after the one gate prepare"] fn projected_policy_scope_members_match_policy_keep_set() { @@ -19809,24 +20055,7 @@ mod floor_base_tree_tests { .expect("one gate prepare"); let (projected, _) = super::required_floor_nominal_subject_seeds_from_prepared(&prepared) .expect("project policy"); - super::policy_roster_identity_join( - &boot.required_gate_prefixes, - &projected.required_gate_prefixes, - "required_gate_prefixes", - ) - .expect("prefix join"); - super::policy_roster_identity_join( - &boot.required_gate_authored_modules, - &projected.required_gate_authored_modules, - "required_gate_authored_modules", - ) - .expect("authored join"); - super::policy_roster_identity_join( - &boot.local_repo_wet_seed_modules, - &projected.local_repo_wet_seed_modules, - "wet_entry_module", - ) - .expect("wet join"); + super::required_floor_admit_policy_projection(&boot, &projected).expect("admit"); let scope = crate::cli_run::claim_scope_for(&prepared, super::super::REQUIRED_FLOOR_POLICY_MODULE) .expect("policy in gate"); From 90ba3b5b015f5b92991e7f1584f10d8754ad7748 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Fri, 9 Oct 2026 15:41:04 +0000 Subject: [PATCH 6/6] Decode bootstrap string literals by the .dag escape table. The census consumes those identities with no join, so dropping the backslash introducer could silently rename a module. Decode exactly dag_string_escapes / numeric forms and refuse any other escape. Co-authored-by: Cursor --- .../src/cli_run/required_floor_runner.rs | 97 ++++++++++++++++--- 1 file changed, 85 insertions(+), 12 deletions(-) diff --git a/src/v1/stage0/src/cli_run/required_floor_runner.rs b/src/v1/stage0/src/cli_run/required_floor_runner.rs index 65e44be24a9..ab2ffd8b70c 100644 --- a/src/v1/stage0/src/cli_run/required_floor_runner.rs +++ b/src/v1/stage0/src/cli_run/required_floor_runner.rs @@ -5889,28 +5889,85 @@ impl<'a> DagSrc<'a> { false } } + fn parse_hex_digit(&mut self) -> Result { + let c = self + .bump() + .ok_or_else(|| "truncated hex escape".to_string())?; + match c { + '0'..='9' => Ok(c as u32 - '0' as u32), + 'a'..='f' => Ok(c as u32 - 'a' as u32 + 10), + 'A'..='F' => Ok(c as u32 - 'A' as u32 + 10), + _ => Err("non-hex digit in numeric string escape".to_string()), + } + } + fn push_unicode_scalar(buf: &mut String, cp: u32) -> Result<(), String> { + char::from_u32(cp) + .map(|ch| buf.push(ch)) + .ok_or_else(|| "numeric string escape is not a Unicode scalar".to_string()) + } + /// Decode a `.dag` string by `extdeps.languages.dag` `dag_string_escapes` / + /// `dag_string_decode_step`: `\"` `\\` `\0` `\n` `\r` `\t` `\{` `\}` `\xHH` `\u{H..H}`. + /// Any other escape, a trailing backslash, or a malformed numeric form refuses. fn parse_string(&mut self) -> Result { if !self.eat("\"") { return Err("expected string literal".to_string()); } let mut buf = String::new(); - let mut escaped = false; - while let Some(c) = self.bump() { - if escaped { - buf.push(c); - escaped = false; - continue; + loop { + let c = self.bump().ok_or_else(|| "unclosed string".to_string())?; + if c == '"' { + return Ok(buf); } - if c == '\\' { - escaped = true; + if c != '\\' { + buf.push(c); continue; } - if c == '"' { - return Ok(buf); + let e = self + .bump() + .ok_or_else(|| "trailing string escape".to_string())?; + match e { + '"' | '\\' | '{' | '}' => buf.push(e), + '0' => buf.push('\0'), + 'n' => buf.push('\n'), + 'r' => buf.push('\r'), + 't' => buf.push('\t'), + 'x' => { + let hi = self.parse_hex_digit()?; + let lo = self.parse_hex_digit()?; + Self::push_unicode_scalar(&mut buf, (hi << 4) | lo)?; + } + 'u' => { + if !self.eat("{") { + return Err("malformed unicode string escape".to_string()); + } + let mut digits = 0u32; + let mut value = 0u32; + loop { + match self.peek() { + Some('}') => { + self.bump(); + break; + } + Some(_) => { + digits += 1; + if digits > 6 { + return Err("unicode string escape is too long".to_string()); + } + value = (value << 4) | self.parse_hex_digit()?; + } + None => return Err("unclosed unicode string escape".to_string()), + } + } + if digits == 0 { + return Err("empty unicode string escape".to_string()); + } + Self::push_unicode_scalar(&mut buf, value)?; + } + _ => { + return Err(format!("unknown string escape \\{e}")); + } } - buf.push(c); } - Err("unclosed string".to_string()) } fn parse_ident(&mut self) -> Result { let mut buf = String::new(); @@ -19994,6 +20051,22 @@ mod floor_base_tree_tests { let err = super::wet_schedule_seed_modules(mixed).expect_err("computed member beside literal"); assert!(err.contains("PolicyRosterNotAListLiteral"), "{err}"); + let escaped_bs = "fn required_gate_prefixes() -> List { [\"a\\\\b\"] }\n"; + assert_eq!( + super::dag_fn_string_list_literal(escaped_bs, "required_gate_prefixes").expect("\\\\"), + vec!["a\\b".to_string()] + ); + let escaped_quote = "fn required_gate_prefixes() -> List { [\"a\\\"b\"] }\n"; + assert_eq!( + super::dag_fn_string_list_literal(escaped_quote, "required_gate_prefixes") + .expect("escaped quote"), + vec!["a\"b".to_string()] + ); + let unknown = "fn required_gate_prefixes() -> List { [\"a\\qb\"] }\n"; + let err = super::dag_fn_string_list_literal(unknown, "required_gate_prefixes") + .expect_err("unknown escape"); + assert!(err.contains("PolicyRosterNotAListLiteral"), "{err}"); + assert!(err.contains("unknown string escape"), "{err}"); } #[test]