From 8558f59f3add459ddc5168136125b167dd6f8069 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sat, 18 Jul 2026 21:11:29 +0000 Subject: [PATCH 1/4] =?UTF-8?q?WIP:=20Module=20identity=20Phase=200(b):=20?= =?UTF-8?q?the=20admission=20invariant=20=E2=80=94=20every=20witness=20row?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v2/std/witness_admission.dag | 39 ++++++ src/v2/workflow/witness_admission.dag | 174 ++++++++++++++++++++++++++ 2 files changed, 213 insertions(+) create mode 100644 src/v2/std/witness_admission.dag create mode 100644 src/v2/workflow/witness_admission.dag diff --git a/src/v2/std/witness_admission.dag b/src/v2/std/witness_admission.dag new file mode 100644 index 00000000000..4f047f626f7 --- /dev/null +++ b/src/v2/std/witness_admission.dag @@ -0,0 +1,39 @@ +module v2.std.witness_admission + +import v2.std.collection { List } +import v2.std.logic { Bool } +import v2.std.text { String } + +data witness_admission_authority_note: String = "Phase 0(b) admission invariant (docs/plans/module-identity-storage-binding-design.md): every enrolled witness row names an executing consumer — discovery rows run per selection; SelfEmitted receipts run on the falsifier wet cadence; known-red quarantined rows run on a probe cadence expecting red (greening is a counted un-quarantine event); operator-ruled OFFLINE rows carry an explicit local-recipe consumer. Enrolled-with-zero-consumers is a typed refusal, never silent. Dissolves when witness_exclusion_substrings rows are replaced by derived consumer classification." + +type WitnessConsumerCadence + = DiscoverySelection + | FalsifierSelfHostWet + | BinWitnessWet + | QuarantineProbeExpectRed + | OfflineLocalRecipe + | FixtureExplicitRoster + | NoConsumer + +type WitnessAdmissionRefusal + = WitnessAdmissionHolds + | UnexecutedDeferredWitness { entry: String, function: String } + +fn witness_consumer_cadence_is_live(cadence: WitnessConsumerCadence) -> Bool { + match cadence { + NoConsumer => false + DiscoverySelection + | FalsifierSelfHostWet + | BinWitnessWet + | QuarantineProbeExpectRed + | OfflineLocalRecipe + | FixtureExplicitRoster => true + } +} + +fn witness_admission_refusal_is_holds(refusal: WitnessAdmissionRefusal) -> Bool { + match refusal { + WitnessAdmissionHolds => true + UnexecutedDeferredWitness { entry: _, function: _ } => false + } +} diff --git a/src/v2/workflow/witness_admission.dag b/src/v2/workflow/witness_admission.dag new file mode 100644 index 00000000000..ace7b1787da --- /dev/null +++ b/src/v2/workflow/witness_admission.dag @@ -0,0 +1,174 @@ +module v2.workflow.witness_admission + +import gunbc.ci_layer_roots { + witness_exclusion_substrings, + bin_witness_wet_entries, + known_red_probe_entries, + witness_admission_offline_exclusion_substrings, + witness_admission_fixture_exclusion_substrings +} +import v2.compiler.self_host.wet_receipt_enrollment { falsifier_self_host_wet_entries } +import std.realization_schedule { + ScheduleWitnessEntry, + schedule_witness_entry_eq +} +import v2.std.algebra { any, contains, fold_list, length, list_snoc_item } +import v2.std.collection { List } +import v2.std.logic { Bool } +import v2.std.text { String } +import v2.std.witness_admission { + WitnessConsumerCadence, + WitnessAdmissionRefusal, + DiscoverySelection, + FalsifierSelfHostWet, + BinWitnessWet, + QuarantineProbeExpectRed, + OfflineLocalRecipe, + FixtureExplicitRoster, + NoConsumer, + WitnessAdmissionHolds, + UnexecutedDeferredWitness, + witness_consumer_cadence_is_live, + witness_admission_refusal_is_holds +} + +data witness_admission_plan_doc_provenance: String = "bind: docs/plans/module-identity-storage-binding-design.md" + +fn schedule_contains_entry( + xs: List, + entry: String, + function: String +) -> Bool { + any( + xs: xs, + predicate: fn(w) { + schedule_witness_entry_eq( + a: w, + b: ScheduleWitnessEntry { entry: entry, function: function, kind: w.kind } + ) + } + ) +} + +fn path_contains_substring(path: String, sub: String) -> Bool { + string_contains(s: path, pattern: sub) +} + +fn path_matches_any_substring(path: String, subs: List) -> Bool { + any(xs: subs, predicate: fn(sub) { path_contains_substring(path: path, sub: sub) }) +} + +fn consumer_for_explicit_rosters(entry: String, function: String) -> WitnessConsumerCadence { + if schedule_contains_entry( + xs: falsifier_self_host_wet_entries(), + entry: entry, + function: function + ) { + FalsifierSelfHostWet + } else if schedule_contains_entry( + xs: bin_witness_wet_entries, + entry: entry, + function: function + ) { + BinWitnessWet + } else if schedule_contains_entry( + xs: known_red_probe_entries, + entry: entry, + function: function + ) { + QuarantineProbeExpectRed + } else { + NoConsumer + } +} + +fn witness_consumer_cadence_for_row( + entry: String, + function: String, + excluded_from_discovery: Bool +) -> WitnessConsumerCadence { + let explicit = consumer_for_explicit_rosters(entry: entry, function: function) + match explicit { + NoConsumer => {} + _ => return explicit + } + if !excluded_from_discovery { + DiscoverySelection + } else if path_matches_any_substring( + path: entry, + subs: witness_admission_offline_exclusion_substrings + ) { + OfflineLocalRecipe + } else if path_matches_any_substring( + path: entry, + subs: witness_admission_fixture_exclusion_substrings + ) { + FixtureExplicitRoster + } else { + NoConsumer + } +} + +fn witness_admission_refusal_for_row( + entry: String, + function: String, + excluded_from_discovery: Bool +) -> WitnessAdmissionRefusal { + if witness_consumer_cadence_is_live( + cadence: witness_consumer_cadence_for_row( + entry: entry, + function: function, + excluded_from_discovery: excluded_from_discovery + ) + ) { + WitnessAdmissionHolds + } else { + UnexecutedDeferredWitness { entry: entry, function: function } + } +} + +fn witness_row_excluded_from_discovery(entry: String) -> Bool { + path_matches_any_substring(path: entry, subs: witness_exclusion_substrings) +} + +fn witness_admission_holds_for_row(entry: String, function: String) -> Bool { + witness_admission_refusal_is_holds( + refusal: witness_admission_refusal_for_row( + entry: entry, + function: function, + excluded_from_discovery: witness_row_excluded_from_discovery(entry: entry) + ) + ) +} + +fn witness_admission_manifest_entry_key(entry: String, function: String) -> String { + concat(entry, "::", function) +} + +fn witness_admission_explicit_consumer_manifest() -> List { + fold_list( + xs: concat( + concat(falsifier_self_host_wet_entries(), bin_witness_wet_entries), + known_red_probe_entries + ), + empty: [], + cons: fn(acc, w) { + list_snoc_item( + xs: acc, + item: witness_admission_manifest_entry_key(entry: w.entry, function: w.function) + ) + } + ) +} + +fn witness_admission_manifest_covers_row(entry: String, function: String) -> Bool { + contains( + xs: witness_admission_explicit_consumer_manifest(), + item: witness_admission_manifest_entry_key(entry: entry, function: function), + eq: string_eq + ) +} + +fn string_eq(a: String, b: String) -> Bool { + a == b +} From 371669ed9f0d90541fbc8e27a315d466a502e240 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sat, 18 Jul 2026 21:21:48 +0000 Subject: [PATCH 2/4] =?UTF-8?q?WIP:=20Module=20identity=20Phase=200(b):=20?= =?UTF-8?q?the=20admission=20invariant=20=E2=80=94=20every=20witness=20row?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dag/gunbc/ci_layer_roots.dag | 54 ++++- src/v1/stage0/src/cli_run.rs | 211 +++++++++++++++++++ src/v2/test/claim/witness_admission_test.dag | 115 ++++++++++ src/v2/workflow/ci_floor_plan.dag | 43 +++- src/v2/workflow/witness_admission.dag | 15 +- 5 files changed, 423 insertions(+), 15 deletions(-) create mode 100644 src/v2/test/claim/witness_admission_test.dag diff --git a/dag/gunbc/ci_layer_roots.dag b/dag/gunbc/ci_layer_roots.dag index 2b65641b2c5..4869f6071dd 100644 --- a/dag/gunbc/ci_layer_roots.dag +++ b/dag/gunbc/ci_layer_roots.dag @@ -1,6 +1,6 @@ module gunbc.ci_layer_roots -import std.realization_schedule { ScheduleWitnessEntry, ExecutionWitnessKind } +import std.realization_schedule { ScheduleWitnessEntry, ExecutionWitnessKind, CorpusWitnessKind } import gunbc.cli_invoke { source_roots_shell_flags, SourceRootBare, @@ -79,6 +79,56 @@ data witness_exclusion_substrings: List = [ "host_build_cache_provision_real_execution_witness_test.dag" ] +data witness_admission_offline_exclusion_substrings: List = [ + "test/claim/execution/", + "claim/complexity/accumulator_copy_roster_gate", + "claim/identity_captured_navigation/roster_gate", + "test/claim/enforcement/", + "enforcement_live_witness_test.dag", + "lever_a_local_receipt_witness", + "host_effect_apply_witness_test.dag", + "deploy_access_privilege_witness_test.dag", + "ci_deploy_observed_wet_test.dag", + "random_bytes_csprng_witness_test.dag", + "test/manual/", + "test/claim/manual/", + "test/claim/long/", + "behavioral_witness_test.dag", + "ci_exclusion_proof_test.dag", + "impossible_bug", + "artifact_store_fs_witness_test.dag", + "emit_source_store_test.dag", + "glob_discovery", + "host_discovered_owned_data_manifest.dag", + "host_source_root_ingest_manifest.dag", + "program_assembly/real_ingest_test.dag", + "self_host/compiler_closure_emit_from_ingest_test.dag", + "unified_test_claim_substrate_equivalence.dag" +] + +data witness_admission_fixture_exclusion_substrings: List = [ + "test/fixture/floor_skip/" +] + +data witness_admission_offline_note: String = "Phase 0(b) admission invariant (module-identity-storage-binding): operator-ruled OFFLINE exclusion substrings whose witness rows carry OfflineLocalRecipe consumers — documented local recipes in the paired exclusion notes (wet_integration_offline_note, enforcement_coverage_exclusion_note, accumulator_copy_roster_gate_exclusion_note, identity_captured_navigation_roster_gate_exclusion_note, lever_a_local_receipt_witness_exclusion_note, long_lane_exclusion_note). Dissolves when each row graduates to a scheduled CI consumer or deletes." + +data witness_admission_fixture_note: String = "Phase 0(b): floor_skip control fixtures excluded at dir grain — consumers are explicit rosters in floor_skip_discovery_witness, not the discovery corpus. Dissolves when fixture-home policy changes." + +fn probe_red(entry: String, f: String) -> ScheduleWitnessEntry { + ScheduleWitnessEntry { entry: entry, function: f, kind: CorpusWitnessKind } +} + +data known_red_probe_note: String = "Phase 0(b) quarantine probe cadence (module-identity-storage-binding): known-red frontier witnesses excluded from per-PR discovery that EXPECT RED on main — the falsifier probe batch runs them SelectionApplied; greening is a counted un-quarantine event (dissolve-on in known_red_frontier_note per row). Distinct from falsifier_self_host_wet (green-only SelfEmitted receipts)." + +data known_red_probe_entries: List = [ + probe_red(entry: "src/v2/test/claim/emit/logic_ground_truth_test.dag", f: "logic_complement_truth_table"), + probe_red(entry: "src/v2/test/claim/emit/logic_ground_truth_test.dag", f: "logic_meet_truth_table"), + probe_red(entry: "src/v2/test/claim/emit/logic_ground_truth_test.dag", f: "logic_join_truth_table"), + probe_red(entry: "src/v2/test/claim/emit/logic_ground_truth_test.dag", f: "logic_truth_tables_all"), + probe_red(entry: "src/v2/test/claim/manual/english_emit_add_test.dag", f: "english_emit_add_emit_ingest_round_trip_holds"), + probe_red(entry: "src/v2/test/claim/manual/english_emit_add_test.dag", f: "english_emit_add_prose_holds") +] + data long_lane_exclusion_note: String = "test/claim/long/ is the LONG-test home (operator 5-second rule, 2026-07-12; authority + enforcement: gunbc_ci_fast_lane_rule_note / gunbc_ci_fast_lane_witness_eval_budget in v2.workflow.ci_floor_plan): a witness whose own eval reaches 5s does not run in per-PR discovery — the executor's cooperative eval deadline REDs it by name (EvalBudgetExceeded), and the fix is moving its file here (dag/test/claim/long/, src/v2/test/claim/long/), where discovery excludes it at dir grain and it runs via the local recipe (claim_batch --source-root dag --source-root src/v2 --entry --functions ) or the scheduled lane's future enrollment. First residents (receipted): s1_closure_receipt_test (whole-closure re-parse through the interpreted v2 frontend, 90+min eval, the run-29183446733 wedge class) and self_host_module_emit_derisk_test (~31s/witness). Unlike the roster exclusions above, membership here is a HOME, not a per-file ruling: the 5s deadline makes an over-budget fast-lane witness unwritable-in-place, so the roster cannot silently grow stale (§5 construction over validation). Dissolve-on: per-witness declared cost envelopes (witness-cost-locality admission law) subsume the dir grain." data wet_integration_offline_note: String = "Four wet-integration entries above (host_effect_apply, deploy_access_privilege, ci_deploy_observed_wet, random_bytes_csprng) are OFFLINE per the hermetic-floor posture (operator 2026-07-11: integration-style witnesses must not run regularly; CI runs hermetic/mocked behavior checks). Each performs genuinely live host effects the hermetic envelope refuses by construction: host_effect_apply drives real LocalShell/SSH host effects (the 133s row atop the slow-witness census), deploy_access_privilege applies privileged shell mutations through fixture actors, ci_deploy_observed_wet shells whoami + sudo -n, random_bytes_csprng reads host entropy. Hermetic duals stay discovered where they exist (the ci_deploy split keeps the pure half; deploy_access_check(access, actor) is the mock-shaped dual). Local recipe: claim_batch --wet --source-root dag --source-root src/v2 --entry per file. Dissolve-on: the scheduled (nightly) lane un-darkens and admits wet integration rows under its own budget — then these four enroll there as declared execution rows and the exclusion rows delete." @@ -124,6 +174,8 @@ data bin_witness_wet_entries: List = [ bin_wet(entry: "dag/test/claim/typed_witness_invocation_test.dag", f: "typed_run_witness_bin_unbuildable_red_control_holds"), bin_wet(entry: "dag/test/claim/typed_witness_invocation_test.dag", f: "typed_is_nonempty_positive_holds"), bin_wet(entry: "dag/test/claim/typed_witness_invocation_test.dag", f: "typed_is_nonempty_zero_byte_red_control_holds"), + bin_wet(entry: "dag/test/claim/typed_witness_invocation_test.dag", f: "typed_gunbc_cli_run_claim_holds"), + bin_wet(entry: "dag/test/claim/typed_witness_invocation_test.dag", f: "typed_claim_executor_verify_build_artifacts_red_control_holds"), bin_wet(entry: "dag/test/claim/v1_dag_parse_witness_test.dag", f: "v1_dag_parse_witnesses"), bin_wet(entry: "src/v2/test/claim/auth_declared_but_unwired_witness_test.dag", f: "auth_declared_but_unwired_witness_keystone_holds"), bin_wet(entry: "src/v2/test/claim/bootstrap_test.dag", f: "bootstrap_witness_keystone_holds"), diff --git a/src/v1/stage0/src/cli_run.rs b/src/v1/stage0/src/cli_run.rs index a1599dfb7d3..22aac1bcf91 100644 --- a/src/v1/stage0/src/cli_run.rs +++ b/src/v1/stage0/src/cli_run.rs @@ -1010,6 +1010,12 @@ const CI_LAYER_ROOTS_AUTHORITY_REL: &str = "dag/gunbc/ci_layer_roots.dag"; const WITNESS_LAYER_ROOTS_DATA_NAME: &str = "witness_layer_roots"; const WITNESS_DISCOVERY_SCAN_DIRS_DATA_NAME: &str = "witness_discovery_scan_dirs"; const WITNESS_EXCLUSION_SUBSTRINGS_DATA_NAME: &str = "witness_exclusion_substrings"; +const WITNESS_ADMISSION_OFFLINE_EXCLUSION_SUBSTRINGS_DATA_NAME: &str = + "witness_admission_offline_exclusion_substrings"; +const WITNESS_ADMISSION_FIXTURE_EXCLUSION_SUBSTRINGS_DATA_NAME: &str = + "witness_admission_fixture_exclusion_substrings"; +const WET_RECEIPT_ENROLLMENT_AUTHORITY_REL: &str = + "src/v2/compiler/self_host/wet_receipt_enrollment.dag"; const WHOLE_TREE_STRICT_RESOLVE_EXCLUSION_SUBSTRINGS_DATA_NAME: &str = "whole_tree_strict_resolve_exclusion_substrings"; @@ -7328,6 +7334,13 @@ pub struct DeferredDiscoveryRow { pub reads_live_tree: bool, } +/// Phase 0(b) admission invariant refusal — an excluded witness row with zero executing consumers. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct UnexecutedDeferredWitness { + pub entry: String, + pub function: String, +} + #[derive(Debug)] pub struct DiscoverySummary { pub total: usize, @@ -7706,6 +7719,165 @@ pub fn collect_deferred_discovery_rows( Ok(out) } +fn witness_admission_offline_exclusion_substrings() -> Vec { + static PATTERNS: OnceLock> = OnceLock::new(); + PATTERNS + .get_or_init(|| { + string_list_data_from_ci_layer_roots_source( + ci_layer_roots_authority_content(), + WITNESS_ADMISSION_OFFLINE_EXCLUSION_SUBSTRINGS_DATA_NAME, + ) + }) + .clone() +} + +fn witness_admission_fixture_exclusion_substrings() -> Vec { + static PATTERNS: OnceLock> = OnceLock::new(); + PATTERNS + .get_or_init(|| { + string_list_data_from_ci_layer_roots_source( + ci_layer_roots_authority_content(), + WITNESS_ADMISSION_FIXTURE_EXCLUSION_SUBSTRINGS_DATA_NAME, + ) + }) + .clone() +} + +fn path_matches_any_substring(path: &str, subs: &[String]) -> bool { + subs.iter().any(|sub| path.contains(sub.as_str())) +} + +fn witness_admission_manifest_key(entry: &str, function: &str) -> String { + format!("{entry}::{function}") +} + +fn witness_admission_entry_function_keys_from_source(content: &str) -> Vec { + let mut keys = Vec::new(); + let mut push_pair = |entry: &str, function: &str| { + let key = witness_admission_manifest_key(entry, function); + if !keys.iter().any(|k| k == &key) { + keys.push(key); + } + }; + let mut scan_call_style = |prefix: &str| { + let mut search_from = 0; + while let Some(rel) = content[search_from..].find(prefix) { + let start = search_from + rel + prefix.len(); + let Some((entry, after_entry)) = content[start..].split_once('"') else { + break; + }; + let fn_marker = if let Some(pos) = after_entry.find("f: \"") { + ("f: \"", pos) + } else if let Some(pos) = after_entry.find("function: \"") { + ("function: \"", pos) + } else { + search_from = start + entry.len() + 1; + continue; + }; + let fn_start = fn_marker.1 + fn_marker.0.len(); + if let Some((function, _)) = after_entry[fn_start..].split_once('"') { + push_pair(entry, function); + } + search_from = start + entry.len() + 1; + } + }; + scan_call_style("bin_wet(entry: \""); + scan_call_style("probe_red(entry: \""); + scan_call_style("self_host_wet_entry(\n entry: \""); + scan_call_style("self_host_wet_entry(entry: \""); + let mut search_from = 0; + while let Some(rel) = content[search_from..].find("entry: \"") { + let start = search_from + rel + "entry: \"".len(); + let Some((entry, after_entry)) = content[start..].split_once('"') else { + break; + }; + if let Some(fn_rel) = after_entry.find("function: \"") { + let fn_start = fn_rel + "function: \"".len(); + if let Some((function, _)) = after_entry[fn_start..].split_once('"') { + push_pair(entry, function); + } + } + search_from = start + entry.len() + 1; + } + keys.sort(); + keys +} + +fn witness_admission_explicit_consumer_keys() -> Vec { + static KEYS: OnceLock> = OnceLock::new(); + KEYS.get_or_init(|| { + let mut keys = witness_admission_entry_function_keys_from_source( + ci_layer_roots_authority_content(), + ); + let wet = std::fs::read_to_string( + workspace_root().join(WET_RECEIPT_ENROLLMENT_AUTHORITY_REL), + ) + .unwrap_or_else(|e| { + panic!( + "witness admission: failed to read {}: {e}", + WET_RECEIPT_ENROLLMENT_AUTHORITY_REL + ) + }); + for key in witness_admission_entry_function_keys_from_source(&wet) { + if !keys.iter().any(|k| k == &key) { + keys.push(key); + } + } + keys.sort(); + keys + }) + .clone() +} + +/// Phase 0(b): every deferred witness row must name an executing consumer (explicit roster, +/// offline local recipe, or fixture explicit roster). Returns orphans — enrolled, zero consumers. +pub fn collect_unexecuted_deferred_witnesses( + deferred_rows: &[DeferredDiscoveryRow], +) -> Vec { + let explicit = witness_admission_explicit_consumer_keys(); + let offline = witness_admission_offline_exclusion_substrings(); + let fixture = witness_admission_fixture_exclusion_substrings(); + let mut orphans = Vec::new(); + for row in deferred_rows { + let key = witness_admission_manifest_key(&row.entry, &row.function); + if explicit.iter().any(|k| k == &key) { + continue; + } + if path_matches_any_substring(&row.entry, &offline) + || path_matches_any_substring(&row.entry, &fixture) + { + continue; + } + orphans.push(UnexecutedDeferredWitness { + entry: row.entry.clone(), + function: row.function.clone(), + }); + } + orphans +} + +fn refuse_unexecuted_deferred_witnesses(orphans: &[UnexecutedDeferredWitness]) -> Result<(), String> { + if orphans.is_empty() { + return Ok(()); + } + let mut lines: Vec = orphans + .iter() + .take(8) + .map(|o| format!("{} ({})", o.function, o.entry)) + .collect(); + if orphans.len() > 8 { + lines.push(format!("… and {} more orphan row(s)", orphans.len() - 8)); + } + Err(format!( + "WITNESS ADMISSION REFUSAL cause=UnexecutedDeferredWitness count={} — enrolled \ + witness row(s) excluded from discovery name zero executing consumers (Phase 0(b) \ + admission invariant); each excluded row must be on falsifier_self_host_wet, \ + bin_witness_wet, known_red_probe, offline, or fixture explicit roster: {}", + orphans.len(), + lines.join("; ") + )) +} + fn eprintln_deferred_discovery_rows(rows: &[DeferredDiscoveryRow]) { if rows.is_empty() { return; @@ -9927,6 +10099,8 @@ pub fn run_discovery_corpus_with_options( } else { collect_deferred_discovery_rows(source_roots, &options.exclude_substrings)? }; + let admission_orphans = collect_unexecuted_deferred_witnesses(&deferred_rows); + refuse_unexecuted_deferred_witnesses(&admission_orphans)?; eprintln_deferred_discovery_rows(&deferred_rows); set_phase(FloorPhase::Discovery, "discovery-roster"); let selection_enabled = options.node_frontier_selection != NodeFrontierSelectionMode::Off; @@ -12573,6 +12747,43 @@ mod node_frontier_plumbing_controls { ); } + // Phase 0(b) admission invariant: every deferred witness row names an executing consumer. + #[test] + fn witness_admission_deferred_rows_have_consumers() { + let ws = workspace_root(); + std::env::set_current_dir(&ws).expect("chdir workspace"); + let roots = setup_roots(&ws); + let excludes = super::witness_exclusion_substrings(); + let deferred = + super::collect_deferred_discovery_rows(&roots, &excludes).expect("deferred scan"); + let orphans = super::collect_unexecuted_deferred_witnesses(&deferred); + super::refuse_unexecuted_deferred_witnesses(&orphans) + .unwrap_or_else(|e| panic!("live deferred corpus must admit every row: {e}")); + let normalize = deferred + .iter() + .find(|r| r.function == "self_host_03_normalize_behavioral_receipt_holds") + .expect("03_normalize behavioral receipt must be deferred from discovery"); + assert!( + normalize.entry.contains("self_host_03_normalize_behavioral_witness_test"), + "03_normalize receipt entry: got {}", + normalize.entry + ); + } + + #[test] + fn witness_admission_orphan_synthetic_row_refuses() { + let orphan = super::DeferredDiscoveryRow { + entry: "dag/test/claim/synthetic_orphan_admission_witness_test.dag".to_string(), + function: "synthetic_orphan_no_consumer_holds".to_string(), + exclude_reason: "synthetic_orphan_admission_witness_test.dag".to_string(), + reads_live_tree: false, + }; + let orphans = super::collect_unexecuted_deferred_witnesses(&[orphan]); + assert_eq!(orphans.len(), 1); + let err = super::refuse_unexecuted_deferred_witnesses(&orphans).expect_err("orphan"); + assert!(err.contains("UnexecutedDeferredWitness")); + } + // Phase 0 receipt (module-identity-storage-binding): the 03_normalize behavioral // witness declares a hermetic import closure but carries string-carried path deps via // tools.self_host_03_normalize_behavioral_transport — derived host-reading must fire. diff --git a/src/v2/test/claim/witness_admission_test.dag b/src/v2/test/claim/witness_admission_test.dag new file mode 100644 index 00000000000..e58e3d3f54c --- /dev/null +++ b/src/v2/test/claim/witness_admission_test.dag @@ -0,0 +1,115 @@ +module v2.test.claim.witness_admission_test + +import v2.workflow.witness_admission { + witness_consumer_cadence_for_row, + witness_admission_holds_for_row, + witness_admission_explicit_consumer_manifest, + witness_admission_manifest_covers_row +} +import v2.std.witness_admission { + WitnessConsumerCadence, + FalsifierSelfHostWet, + BinWitnessWet, + QuarantineProbeExpectRed, + OfflineLocalRecipe, + DiscoverySelection, + NoConsumer, + witness_consumer_cadence_is_live +} +import v2.std.algebra { length } +import v2.std.logic { Bool } +import v2.std.text { String } + +data normalize_entry: String = "dag/test/claim/self_host_03_normalize_behavioral_witness_test.dag" +data normalize_fn: String = "self_host_03_normalize_behavioral_receipt_holds" + +data synthetic_orphan_entry: String = "dag/test/claim/synthetic_orphan_admission_witness_test.dag" +data synthetic_orphan_fn: String = "synthetic_orphan_no_consumer_holds" + +fn witness_03_normalize_falsifier_wet_consumer_holds() -> Bool { + match witness_consumer_cadence_for_row( + entry: normalize_entry, + function: normalize_fn, + excluded_from_discovery: true + ) { + FalsifierSelfHostWet => true + _ => false + } +} + +fn witness_03_normalize_manifest_covers_holds() -> Bool { + witness_admission_manifest_covers_row(entry: normalize_entry, function: normalize_fn) +} + +fn witness_synthetic_orphan_has_no_consumer_holds() -> Bool { + match witness_consumer_cadence_for_row( + entry: synthetic_orphan_entry, + function: synthetic_orphan_fn, + excluded_from_discovery: true + ) { + NoConsumer => true + _ => false + } +} + +fn witness_synthetic_orphan_admission_red_holds() -> Bool { + !witness_admission_holds_for_row( + entry: synthetic_orphan_entry, + function: synthetic_orphan_fn + ) +} + +fn witness_known_red_probe_consumer_holds() -> Bool { + witness_consumer_cadence_is_live( + cadence: witness_consumer_cadence_for_row( + entry: "src/v2/test/claim/emit/logic_ground_truth_test.dag", + function: "logic_complement_truth_table", + excluded_from_discovery: true + ) + ) + && match witness_consumer_cadence_for_row( + entry: "src/v2/test/claim/emit/logic_ground_truth_test.dag", + function: "logic_complement_truth_table", + excluded_from_discovery: true + ) { + QuarantineProbeExpectRed => true + _ => false + } +} + +fn witness_offline_execution_lane_consumer_holds() -> Bool { + match witness_consumer_cadence_for_row( + entry: "src/v2/test/claim/execution/synthetic_optin_exec_witness_test.dag", + function: "synthetic_optin_exec_witness", + excluded_from_discovery: true + ) { + OfflineLocalRecipe => true + _ => false + } +} + +fn witness_discovery_row_consumer_holds() -> Bool { + match witness_consumer_cadence_for_row( + entry: "src/v2/test/claim/effect_reach_test.dag", + function: "effect_reach_path_data_init_derived_host_reading_holds", + excluded_from_discovery: false + ) { + DiscoverySelection => true + _ => false + } +} + +fn witness_explicit_manifest_nonempty_holds() -> Bool { + length(xs: witness_admission_explicit_consumer_manifest()) > 0 +} + +test fn witness_admission_invariant_holds() -> Bool { + witness_03_normalize_falsifier_wet_consumer_holds() + && witness_03_normalize_manifest_covers_holds() + && witness_synthetic_orphan_has_no_consumer_holds() + && witness_synthetic_orphan_admission_red_holds() + && witness_known_red_probe_consumer_holds() + && witness_offline_execution_lane_consumer_holds() + && witness_discovery_row_consumer_holds() + && witness_explicit_manifest_nonempty_holds() +} diff --git a/src/v2/workflow/ci_floor_plan.dag b/src/v2/workflow/ci_floor_plan.dag index 78eb2518bb3..dbc55a5e568 100644 --- a/src/v2/workflow/ci_floor_plan.dag +++ b/src/v2/workflow/ci_floor_plan.dag @@ -1,7 +1,7 @@ module v2.workflow.ci_floor_plan import std.execution_mode { Hermetic, Wet } -import gunbc.ci_layer_roots { witness_layer_roots, witness_exclusion_substrings, witness_discovery_scan_dirs, bin_witness_wet_entries } +import gunbc.ci_layer_roots { witness_layer_roots, witness_exclusion_substrings, witness_discovery_scan_dirs, bin_witness_wet_entries, known_red_probe_entries } import v2.compiler.self_host.wet_receipt_enrollment { falsifier_self_host_wet_entries } import gunbc.ci_spec { gunbc_ci_spec, gunbc_ci_regen_spec, CiSpec, @@ -13,7 +13,7 @@ import gunbc.ci_gate { SourceRootIngestGate, RegenVerifyGate, SelfHostReadsRealBytesGate, SelfHostStalenessGate } -import v2.std.algebra { filter, length, list_snoc_item, skip, zip_map } +import v2.std.algebra { concat, filter, length, list_snoc_item, skip, zip_map } import v2.std.collection { List, Optional, Absent, Present } import v2.std.dependency { DataDependsOn, ResourceDependsOn, DependencyView } import v2.std.node { Atom, Node, TypeNode, SyntheticOccurrence } @@ -597,6 +597,29 @@ fn falsifier_self_host_wet_batch_enrolled() -> Bool { length(xs: falsifier_self_host_wet_entries()) > 0 } +data falsifier_known_red_probe_batch_note: String = "Nightly falsifier batch 3 (Phase 0(b) admission invariant): the declared Hermetic probe lane for known-red quarantined witnesses (gunbc.ci_layer_roots known_red_probe_entries — see known_red_probe_note). Runs SelectionApplied after the wet self-host batch; EXPECT RED on main so greening is a counted un-quarantine event, never a hand discovery. Distinct from falsifier_self_host_wet (green-only SelfEmitted receipts)." + +fn falsifier_known_red_probe_batch_enrolled() -> Bool { + length(xs: known_red_probe_entries) > 0 +} + +fn gunbc_falsifier_known_red_probe_batch() -> Runnable { + RunnableDiscoveryBatch { + source_roots: corpus_source_roots(), + scan_dirs: [], + explicit_entries: known_red_probe_entries, + node_frontier_selection: SelectionApplied, + exclude_substrings: [], + discovery_scope_dirs: [], + profile: runnable_resource_profile( + heavy_whole_tree_resolve: false, + spawns_host_compiler: false, + memory: runnable_memory_substantial(), + execution_mode: Hermetic + ) + } +} + fn gunbc_falsifier_self_host_wet_batch() -> Runnable { RunnableDiscoveryBatch { source_roots: corpus_source_roots(), @@ -615,14 +638,18 @@ fn gunbc_falsifier_self_host_wet_batch() -> Runnable { } fn gunbc_falsifier_batches() -> List> { - if falsifier_self_host_wet_batch_enrolled() { - [ - [gunbc_falsifier_predict_only_batch()], - [gunbc_falsifier_self_host_wet_batch()] - ] + let predict = [gunbc_falsifier_predict_only_batch()] + let wet = if falsifier_self_host_wet_batch_enrolled() { + [[gunbc_falsifier_self_host_wet_batch()]] } else { - [[gunbc_falsifier_predict_only_batch()]] + [] + } + let probe = if falsifier_known_red_probe_batch_enrolled() { + [[gunbc_falsifier_known_red_probe_batch()]] + } else { + [] } + concat(concat(predict, wet), probe) } fn gunbc_ci_floor_batches() -> List> { diff --git a/src/v2/workflow/witness_admission.dag b/src/v2/workflow/witness_admission.dag index ace7b1787da..42499c0850e 100644 --- a/src/v2/workflow/witness_admission.dag +++ b/src/v2/workflow/witness_admission.dag @@ -34,6 +34,14 @@ import v2.std.witness_admission { data witness_admission_plan_doc_provenance: String = "bind: docs/plans/module-identity-storage-binding-design.md" +fn schedule_row_matches( + w: ScheduleWitnessEntry, + entry: String, + function: String +) -> Bool { + (w.entry == entry) && (w.function == function) +} + fn schedule_contains_entry( xs: List, entry: String, @@ -41,12 +49,7 @@ fn schedule_contains_entry( ) -> Bool { any( xs: xs, - predicate: fn(w) { - schedule_witness_entry_eq( - a: w, - b: ScheduleWitnessEntry { entry: entry, function: function, kind: w.kind } - ) - } + predicate: fn(w) { schedule_row_matches(w: w, entry: entry, function: function) } ) } From c5225a927e5ae5b41421fa28783d808d186534a7 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sat, 18 Jul 2026 21:32:20 +0000 Subject: [PATCH 3/4] =?UTF-8?q?WIP:=20Module=20identity=20Phase=200(b):=20?= =?UTF-8?q?the=20admission=20invariant=20=E2=80=94=20every=20witness=20row?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dag/gunbc/ci_layer_roots.dag | 1 + src/v1/stage0/src/cli_run.rs | 30 +++++++------ src/v2/std/witness_admission.dag | 12 ++--- .../test/claim/ci_floor_plan_witness_test.dag | 45 +++++++++++++++---- src/v2/workflow/ci_floor_plan.dag | 31 ++++++++----- src/v2/workflow/witness_admission.dag | 38 ++++++++-------- 6 files changed, 98 insertions(+), 59 deletions(-) diff --git a/dag/gunbc/ci_layer_roots.dag b/dag/gunbc/ci_layer_roots.dag index 4869f6071dd..4063cf06a11 100644 --- a/dag/gunbc/ci_layer_roots.dag +++ b/dag/gunbc/ci_layer_roots.dag @@ -60,6 +60,7 @@ data witness_exclusion_substrings: List = [ "test/claim/diagnostics_test.dag", "effects_rest_transport_parse_witness_test.dag", "floor_skip_discovery_witness_test.dag", + "synthetic_orphan_admission_witness_test.dag", "interp_recorded_fixture_witness_test.dag", "test/claim/parse_test.dag", "v1_dag_parse_witness_test.dag", diff --git a/src/v1/stage0/src/cli_run.rs b/src/v1/stage0/src/cli_run.rs index 22aac1bcf91..06a10f6f947 100644 --- a/src/v1/stage0/src/cli_run.rs +++ b/src/v1/stage0/src/cli_run.rs @@ -7806,18 +7806,16 @@ fn witness_admission_entry_function_keys_from_source(content: &str) -> Vec Vec { static KEYS: OnceLock> = OnceLock::new(); KEYS.get_or_init(|| { - let mut keys = witness_admission_entry_function_keys_from_source( - ci_layer_roots_authority_content(), - ); - let wet = std::fs::read_to_string( - workspace_root().join(WET_RECEIPT_ENROLLMENT_AUTHORITY_REL), - ) - .unwrap_or_else(|e| { - panic!( - "witness admission: failed to read {}: {e}", - WET_RECEIPT_ENROLLMENT_AUTHORITY_REL - ) - }); + let mut keys = + witness_admission_entry_function_keys_from_source(ci_layer_roots_authority_content()); + let wet = + std::fs::read_to_string(workspace_root().join(WET_RECEIPT_ENROLLMENT_AUTHORITY_REL)) + .unwrap_or_else(|e| { + panic!( + "witness admission: failed to read {}: {e}", + WET_RECEIPT_ENROLLMENT_AUTHORITY_REL + ) + }); for key in witness_admission_entry_function_keys_from_source(&wet) { if !keys.iter().any(|k| k == &key) { keys.push(key); @@ -7856,7 +7854,9 @@ pub fn collect_unexecuted_deferred_witnesses( orphans } -fn refuse_unexecuted_deferred_witnesses(orphans: &[UnexecutedDeferredWitness]) -> Result<(), String> { +fn refuse_unexecuted_deferred_witnesses( + orphans: &[UnexecutedDeferredWitness], +) -> Result<(), String> { if orphans.is_empty() { return Ok(()); } @@ -12764,7 +12764,9 @@ mod node_frontier_plumbing_controls { .find(|r| r.function == "self_host_03_normalize_behavioral_receipt_holds") .expect("03_normalize behavioral receipt must be deferred from discovery"); assert!( - normalize.entry.contains("self_host_03_normalize_behavioral_witness_test"), + normalize + .entry + .contains("self_host_03_normalize_behavioral_witness_test"), "03_normalize receipt entry: got {}", normalize.entry ); diff --git a/src/v2/std/witness_admission.dag b/src/v2/std/witness_admission.dag index 4f047f626f7..4c4366e3415 100644 --- a/src/v2/std/witness_admission.dag +++ b/src/v2/std/witness_admission.dag @@ -22,12 +22,12 @@ type WitnessAdmissionRefusal fn witness_consumer_cadence_is_live(cadence: WitnessConsumerCadence) -> Bool { match cadence { NoConsumer => false - DiscoverySelection - | FalsifierSelfHostWet - | BinWitnessWet - | QuarantineProbeExpectRed - | OfflineLocalRecipe - | FixtureExplicitRoster => true + DiscoverySelection => true + FalsifierSelfHostWet => true + BinWitnessWet => true + QuarantineProbeExpectRed => true + OfflineLocalRecipe => true + FixtureExplicitRoster => true } } diff --git a/src/v2/test/claim/ci_floor_plan_witness_test.dag b/src/v2/test/claim/ci_floor_plan_witness_test.dag index d540f78628b..058f93649c1 100644 --- a/src/v2/test/claim/ci_floor_plan_witness_test.dag +++ b/src/v2/test/claim/ci_floor_plan_witness_test.dag @@ -25,8 +25,9 @@ import v2.workflow.ci_floor_plan { gunbc_falsifier_self_host_wet_receipt_wall_budget_ms, gunbc_falsifier_self_host_wet_interp_eval_budget_ms, gunbc_ci_fast_lane_eval_budget_ms, + falsifier_known_red_probe_batch_enrolled, } -import gunbc.ci_layer_roots { witness_layer_roots, witness_discovery_scan_dirs } +import gunbc.ci_layer_roots { witness_layer_roots, witness_discovery_scan_dirs, known_red_probe_entries } import v2.compiler.self_host.wet_receipt_enrollment { falsifier_self_host_wet_entries } import gunbc.ci_spec { gunbc_ci_spec, ci_spec_with_witness_entries, ci_spec_with_discovery_scan_dirs, CiSpec, EmitHostGate, LayeringImportsGate, RegenVerifyGate } import std.realization_schedule { @@ -357,6 +358,23 @@ fn falsifier_self_host_wet_batch_holds(r: Runnable) -> Bool { } } +fn falsifier_known_red_probe_batch_holds(r: Runnable) -> Bool { + match r { + RunnableDiscoveryBatch { source_roots: _, scan_dirs: dirs, explicit_entries: entries, node_frontier_selection: mode, exclude_substrings: _, discovery_scope_dirs: _, profile: p } => + match mode { + SelectionApplied => true + SelectionPredictOnly => false + SelectionOff => false + } + && (length(xs: dirs) == 0) + && (length(xs: entries) == length(xs: known_red_probe_entries)) + && discovery_batch_declares_hermetic(r: r) + && (p.spawns_host_compiler == false) + RunnableSingleClaim { entry: _, function: _, profile: _ } => false + RunnableKernelWorkload { fused_op_count: _ } => false + } +} + fn falsifier_self_host_wet_budget_declared_holds() -> Bool { gunbc_falsifier_self_host_wet_receipt_wall_budget_ms() == 600000 && gunbc_falsifier_self_host_wet_interp_eval_budget_ms() == 120000 @@ -365,18 +383,27 @@ fn falsifier_self_host_wet_budget_declared_holds() -> Bool { fn falsifier_plan_structure_holds() -> Bool { let batches = gunbc_falsifier_batches() - if length(xs: falsifier_self_host_wet_entries()) > 0 { + let predict_ok = (batches.first().length() == 1) + && falsifier_predict_only_batch_holds(r: batches.first().first()) + let wet_enrolled = length(xs: falsifier_self_host_wet_entries()) > 0 + let probe_enrolled = falsifier_known_red_probe_batch_enrolled() + let expected_batches = 1 + (if wet_enrolled { 1 } else { 0 }) + (if probe_enrolled { 1 } else { 0 }) + let wet_ok = if wet_enrolled { let wet_batch = skip(xs: batches, n: 1).first() - (length(xs: batches) == 2) - && (batches.first().length() == 1) - && falsifier_predict_only_batch_holds(r: batches.first().first()) - && (wet_batch.length() == 1) + (wet_batch.length() == 1) && falsifier_self_host_wet_batch_holds(r: wet_batch.first()) } else { - (length(xs: batches) == 1) - && (batches.first().length() == 1) - && falsifier_predict_only_batch_holds(r: batches.first().first()) + true + } + let probe_ok = if probe_enrolled { + let probe_offset = if wet_enrolled { 2 } else { 1 } + let probe_batch = skip(xs: batches, n: probe_offset).first() + (probe_batch.length() == 1) + && falsifier_known_red_probe_batch_holds(r: probe_batch.first()) + } else { + true } + (length(xs: batches) == expected_batches) && predict_ok && wet_ok && probe_ok } test fn ci_floor_plan_witnesses() -> Bool { diff --git a/src/v2/workflow/ci_floor_plan.dag b/src/v2/workflow/ci_floor_plan.dag index dbc55a5e568..bfb3378c1ea 100644 --- a/src/v2/workflow/ci_floor_plan.dag +++ b/src/v2/workflow/ci_floor_plan.dag @@ -13,7 +13,7 @@ import gunbc.ci_gate { SourceRootIngestGate, RegenVerifyGate, SelfHostReadsRealBytesGate, SelfHostStalenessGate } -import v2.std.algebra { concat, filter, length, list_snoc_item, skip, zip_map } +import v2.std.algebra { filter, length, list_snoc_item, skip, zip_map } import v2.std.collection { List, Optional, Absent, Present } import v2.std.dependency { DataDependsOn, ResourceDependsOn, DependencyView } import v2.std.node { Atom, Node, TypeNode, SyntheticOccurrence } @@ -638,18 +638,27 @@ fn gunbc_falsifier_self_host_wet_batch() -> Runnable { } fn gunbc_falsifier_batches() -> List> { - let predict = [gunbc_falsifier_predict_only_batch()] - let wet = if falsifier_self_host_wet_batch_enrolled() { - [[gunbc_falsifier_self_host_wet_batch()]] - } else { - [] - } - let probe = if falsifier_known_red_probe_batch_enrolled() { - [[gunbc_falsifier_known_red_probe_batch()]] + let wet_enrolled = falsifier_self_host_wet_batch_enrolled() + let probe_enrolled = falsifier_known_red_probe_batch_enrolled() + if wet_enrolled && probe_enrolled { + [ + [gunbc_falsifier_predict_only_batch()], + [gunbc_falsifier_self_host_wet_batch()], + [gunbc_falsifier_known_red_probe_batch()] + ] + } else if wet_enrolled { + [ + [gunbc_falsifier_predict_only_batch()], + [gunbc_falsifier_self_host_wet_batch()] + ] + } else if probe_enrolled { + [ + [gunbc_falsifier_predict_only_batch()], + [gunbc_falsifier_known_red_probe_batch()] + ] } else { - [] + [[gunbc_falsifier_predict_only_batch()]] } - concat(concat(predict, wet), probe) } fn gunbc_ci_floor_batches() -> List> { diff --git a/src/v2/workflow/witness_admission.dag b/src/v2/workflow/witness_admission.dag index 42499c0850e..5615a10b81e 100644 --- a/src/v2/workflow/witness_admission.dag +++ b/src/v2/workflow/witness_admission.dag @@ -12,7 +12,7 @@ import std.realization_schedule { ScheduleWitnessEntry, schedule_witness_entry_eq } -import v2.std.algebra { any, contains, fold_list, length, list_snoc_item } +import v2.std.algebra { any, contains, fold_list, length, list_snoc_item, Empty } import v2.std.collection { List } import v2.std.logic { Bool } import v2.std.text { String } @@ -92,23 +92,23 @@ fn witness_consumer_cadence_for_row( ) -> WitnessConsumerCadence { let explicit = consumer_for_explicit_rosters(entry: entry, function: function) match explicit { - NoConsumer => {} - _ => return explicit - } - if !excluded_from_discovery { - DiscoverySelection - } else if path_matches_any_substring( - path: entry, - subs: witness_admission_offline_exclusion_substrings - ) { - OfflineLocalRecipe - } else if path_matches_any_substring( - path: entry, - subs: witness_admission_fixture_exclusion_substrings - ) { - FixtureExplicitRoster - } else { - NoConsumer + NoConsumer => + if !excluded_from_discovery { + DiscoverySelection + } else if path_matches_any_substring( + path: entry, + subs: witness_admission_offline_exclusion_substrings + ) { + OfflineLocalRecipe + } else if path_matches_any_substring( + path: entry, + subs: witness_admission_fixture_exclusion_substrings + ) { + FixtureExplicitRoster + } else { + NoConsumer + } + _ => explicit } } @@ -154,7 +154,7 @@ fn witness_admission_explicit_consumer_manifest() -> List { concat(falsifier_self_host_wet_entries(), bin_witness_wet_entries), known_red_probe_entries ), - empty: [], + empty: Empty, cons: fn(acc, w) { list_snoc_item( xs: acc, From 878558e3b886c868eda3c0fe2b7edb424aaf0d75 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sat, 18 Jul 2026 21:56:29 +0000 Subject: [PATCH 4/4] =?UTF-8?q?WIP:=20Module=20identity=20Phase=200(b):=20?= =?UTF-8?q?the=20admission=20invariant=20=E2=80=94=20every=20witness=20row?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v1/stage0/src/cli_run.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/v1/stage0/src/cli_run.rs b/src/v1/stage0/src/cli_run.rs index 06a10f6f947..dd7080ff531 100644 --- a/src/v1/stage0/src/cli_run.rs +++ b/src/v1/stage0/src/cli_run.rs @@ -7751,6 +7751,11 @@ fn witness_admission_manifest_key(entry: &str, function: &str) -> String { format!("{entry}::{function}") } +// 🟡 dissolve-on: witness_admission_explicit_consumer_manifest — replace this hand-rolled +// entry/function literal scan with the `.dag`-authoritative manifest from +// v2.workflow.witness_admission (or a Node-tree builtin over the roster +// ScheduleWitnessEntry rows). False-positive matches widen the orphan gate (excuse orphans), +// never refuse — interim seed debt, same class as string_list_data_from_ci_layer_roots_source. fn witness_admission_entry_function_keys_from_source(content: &str) -> Vec { let mut keys = Vec::new(); let mut push_pair = |entry: &str, function: &str| {