diff --git a/dsl/gunbc/ci_gate.dag b/dsl/gunbc/ci_gate.dag index 63c043f94cf..be45c367e5e 100644 --- a/dsl/gunbc/ci_gate.dag +++ b/dsl/gunbc/ci_gate.dag @@ -9,4 +9,5 @@ type Gate | GeneratedArtifactDriftGate | SourceRootIngestGate | RegenVerifyGate + | SelfHostRealizedComparisonGate | EmitDeterminismGate diff --git a/dsl/gunbc/ci_spec.dag b/dsl/gunbc/ci_spec.dag index f587a46cbb6..43e6bd76bda 100644 --- a/dsl/gunbc/ci_spec.dag +++ b/dsl/gunbc/ci_spec.dag @@ -4,7 +4,7 @@ import gunbc.ci_gate { Gate, RustMonolithGate, EmitHostGate, LayeringImportsGate, ExtdepsExternalAuthorityGate, DslCompileCleanGate, GeneratedArtifactDriftGate, - SourceRootIngestGate, RegenVerifyGate, EmitDeterminismGate + SourceRootIngestGate, RegenVerifyGate, SelfHostRealizedComparisonGate, EmitDeterminismGate } import gunbc.ci_layer_roots { witness_layer_roots, diff --git a/dsl/gunbc/commit_workflow.dag b/dsl/gunbc/commit_workflow.dag index 2bf99fad4e2..acbb7ebf53d 100644 --- a/dsl/gunbc/commit_workflow.dag +++ b/dsl/gunbc/commit_workflow.dag @@ -4,7 +4,7 @@ import gunbc.ci_gate { Gate, RustMonolithGate, EmitHostGate, LayeringImportsGate, ExtdepsExternalAuthorityGate, DslCompileCleanGate, GeneratedArtifactDriftGate, - SourceRootIngestGate, RegenVerifyGate, EmitDeterminismGate + SourceRootIngestGate, RegenVerifyGate, SelfHostRealizedComparisonGate, EmitDeterminismGate } import gunbc.generated_artifact { GeneratedArtifact, CiYamlArtifact, GithooksPrePushArtifact, artifact_path @@ -72,6 +72,10 @@ data commit_gate_roster: List = [ check: CommitSpecGate { gate: RegenVerifyGate }, surfaces: [GithubActionsCiJob] }, + CommitCheckEnrollment { + check: CommitSpecGate { gate: SelfHostRealizedComparisonGate }, + surfaces: [GithubActionsCiJob] + }, CommitCheckEnrollment { check: CommitSpecGate { gate: EmitDeterminismGate }, surfaces: [GithubActionsCiJob] @@ -140,6 +144,7 @@ fn gate_eq(a: Gate, b: Gate) -> Bool { GeneratedArtifactDriftGate => match b { GeneratedArtifactDriftGate => true _ => false } SourceRootIngestGate => match b { SourceRootIngestGate => true _ => false } RegenVerifyGate => match b { RegenVerifyGate => true _ => false } + SelfHostRealizedComparisonGate => match b { SelfHostRealizedComparisonGate => true _ => false } EmitDeterminismGate => match b { EmitDeterminismGate => true _ => false } } } diff --git a/dsl/gunbc/merge_admission.dag b/dsl/gunbc/merge_admission.dag index ffbde3bac2d..54f50ecb194 100644 --- a/dsl/gunbc/merge_admission.dag +++ b/dsl/gunbc/merge_admission.dag @@ -15,7 +15,7 @@ import gunbc.ci_gate { Gate, RustMonolithGate, EmitHostGate, LayeringImportsGate, ExtdepsExternalAuthorityGate, DslCompileCleanGate, GeneratedArtifactDriftGate, - SourceRootIngestGate, RegenVerifyGate, EmitDeterminismGate, + SourceRootIngestGate, RegenVerifyGate, SelfHostRealizedComparisonGate, EmitDeterminismGate, } import std.content_hash { content_hash_atom, content_hash_combine, content_hash_tagged } import std.disposition { Disposition, Scaffold, SingleAuthority } @@ -74,6 +74,7 @@ fn gate_content_hash(g: Gate) -> ContentHash { GeneratedArtifactDriftGate => content_hash_atom(value: "GeneratedArtifactDriftGate") SourceRootIngestGate => content_hash_atom(value: "SourceRootIngestGate") RegenVerifyGate => content_hash_atom(value: "RegenVerifyGate") + SelfHostRealizedComparisonGate => content_hash_atom(value: "SelfHostRealizedComparisonGate") EmitDeterminismGate => content_hash_atom(value: "EmitDeterminismGate") } } diff --git a/dsl/tools/ci_gates.dag b/dsl/tools/ci_gates.dag index 6315e217067..e085db564af 100644 --- a/dsl/tools/ci_gates.dag +++ b/dsl/tools/ci_gates.dag @@ -5,7 +5,7 @@ import gunbc.ci_spec { gunbc_ci_gates, CiSpec, Gate, RustMonolithGate, EmitHostGate, LayeringImportsGate, ExtdepsExternalAuthorityGate, DslCompileCleanGate, GeneratedArtifactDriftGate, - SourceRootIngestGate, RegenVerifyGate, EmitDeterminismGate + SourceRootIngestGate, RegenVerifyGate, SelfHostRealizedComparisonGate, EmitDeterminismGate } import tools.rust_gates_ci { run_rust_monolith_gate } import tools.emit_host_gate { run_emit_host_ci_gate } @@ -32,6 +32,9 @@ fn run_spec_gate(g: Gate) -> ProcessExit { reason: "SourceRootIngestGate: run via ci_floor_plan v2.workflow.source_root_ingest_gate (slice 3)" ) RegenVerifyGate => run_regen_verify_gate() + SelfHostRealizedComparisonGate => exit_failure( + reason: "SelfHostRealizedComparisonGate: run via ci_floor_plan v2.workflow.self_host_realized_comparison_gate" + ) EmitDeterminismGate => run_emit_determinism_gate() } } diff --git a/dsl/tools/regen_verify_transport.dag b/dsl/tools/regen_verify_transport.dag index 6a37252e8f6..841e7ce13f7 100644 --- a/dsl/tools/regen_verify_transport.dag +++ b/dsl/tools/regen_verify_transport.dag @@ -7,6 +7,10 @@ import extdeps.languages.bash.program { lit, var_ref, command, serialize_bash } import tools.host_prelude { ensure_regen_stage0_built } +import tools.self_host_realized_comparison_transport { + realized_comparison_stable_dir, + realized_comparison_roster_manifest_path +} import std.disposition { Disposition, Scaffold, RealizationDispatch } import std.decl_ref { DeclarationRef, WholeDeclaration } @@ -17,7 +21,12 @@ fn regen_verify_program() -> ShellProgram { set_e: true, statements: concat(ensure_regen_stage0_built(), [ command(words: [lit(text: "cd"), root]), - command(words: [regen, lit(text: "--verify")]) + command(words: [ + regen, + lit(text: "--emit-fresh"), lit(text: realized_comparison_stable_dir), + lit(text: "--write-manifest"), lit(text: realized_comparison_roster_manifest_path), + lit(text: "--verify") + ]) ]) } } diff --git a/dsl/tools/self_host_realized_comparison_transport.dag b/dsl/tools/self_host_realized_comparison_transport.dag new file mode 100644 index 00000000000..3c45e581f61 --- /dev/null +++ b/dsl/tools/self_host_realized_comparison_transport.dag @@ -0,0 +1,46 @@ +module tools.self_host_realized_comparison_transport + +import extdeps.shell +import extdeps.languages.bash.program { + ShellProgram, ShellStmt, + lit, var_ref, command, serialize_bash +} +import tools.host_prelude { ensure_regen_stage0_built } +import std.disposition { Disposition, Scaffold, RealizationDispatch } +import std.decl_ref { DeclarationRef, WholeDeclaration } + +data realized_comparison_stable_dir: String = "target/v2-emit-fresh-realize" +data realized_comparison_emitted_src_root: String = "target/v2-emit-fresh-realize/src" +data realized_comparison_committed_src_root: String = "src/v1/stage0/src" +data realized_comparison_roster_manifest_path: String = "target/v2-emit-fresh-realize/roster_manifest.txt" + +data realized_comparison_ci_subset_roster: List = ["lib.rs", "v1_rt.rs"] + +fn realized_comparison_program() -> ShellProgram { + let regen = var_ref(name: "REGEN_STAGE0") + ShellProgram { + set_e: true, + statements: concat(ensure_regen_stage0_built(), [ + command(words: [lit(text: "cd"), var_ref(name: "ROOT")]), + command(words: [ + regen, + lit(text: "--emit-fresh"), lit(text: realized_comparison_stable_dir), + lit(text: "--write-manifest"), lit(text: realized_comparison_roster_manifest_path) + ]) + ]) + } +} + +data self_host_realized_comparison_transport_disposition: Disposition = Scaffold { + dissolves_to: RealizationDispatch, + bind: DeclarationRef { + module_path: "extdeps.languages.bash.program", + decl_name: "serialize_bash", + field: WholeDeclaration + } +} + +fn run_self_host_realized_comparison_emit() -> Bool { + let result = shell.Exec.Run(script: serialize_bash(p: realized_comparison_program())) + result.success +} diff --git a/src/v1/stage0/src/bin/regen_stage0.rs b/src/v1/stage0/src/bin/regen_stage0.rs index 604a09c39c8..3c84abc6e0b 100644 --- a/src/v1/stage0/src/bin/regen_stage0.rs +++ b/src/v1/stage0/src/bin/regen_stage0.rs @@ -155,6 +155,127 @@ fn main() -> ExitCode { } } +struct VerifyFinishInput<'a> { + receipt_path: &'a Path, + workspace: &'a Path, + manifest_dir: &'a Path, + stage0_src: &'a Path, + fresh_dir: &'a Path, + verify_only: bool, + generated_file_count: usize, + emitted_file_count: usize, + phases: &'a mut Vec, + run_started: Instant, + preserve_fresh_dir: bool, +} + +fn finish_verify_checks(input: VerifyFinishInput<'_>) -> Result<(), String> { + let VerifyFinishInput { + receipt_path, + workspace, + manifest_dir, + stage0_src, + fresh_dir, + verify_only, + generated_file_count, + emitted_file_count, + phases, + run_started, + preserve_fresh_dir, + } = input; + let fresh_src = fresh_dir.join("src"); + let verify_result = time_phase(phases, "verify_stage0_matches", || { + verify_stage0_matches(stage0_src, &fresh_src) + }); + if let Err(message) = verify_result { + let changed_generated_files = changed_registered_outputs(&fresh_src, stage0_src)?; + write_bootstrap_timing_receipt(BootstrapTimingReceiptInput { + path: receipt_path, + workspace, + manifest_dir, + verify_only, + status: "failed_stage0_stale", + generated_file_count, + emitted_file_count, + phases: std::mem::take(phases), + elapsed_ms: elapsed_ms(run_started), + changed_generated_files, + })?; + if !preserve_fresh_dir { + let _ = fs::remove_dir_all(fresh_dir); + } + return Err(message); + } + if let Err(message) = time_phase(phases, "verify_stage0_split_crate_boundaries", || { + verify_stage0_split_crate_boundaries(workspace) + }) { + write_bootstrap_timing_receipt(BootstrapTimingReceiptInput { + path: receipt_path, + workspace, + manifest_dir, + verify_only, + status: "failed_stage0_split_crate_stale", + generated_file_count, + emitted_file_count, + phases: std::mem::take(phases), + elapsed_ms: elapsed_ms(run_started), + changed_generated_files: Vec::new(), + })?; + if !preserve_fresh_dir { + let _ = fs::remove_dir_all(fresh_dir); + } + return Err(message); + } + if let Err(message) = time_phase(phases, "verify_workspace_members", || { + verify_workspace_members(workspace) + }) { + write_bootstrap_timing_receipt(BootstrapTimingReceiptInput { + path: receipt_path, + workspace, + manifest_dir, + verify_only, + status: "failed_workspace_members_stale", + generated_file_count, + emitted_file_count, + phases: std::mem::take(phases), + elapsed_ms: elapsed_ms(run_started), + changed_generated_files: Vec::new(), + })?; + if !preserve_fresh_dir { + let _ = fs::remove_dir_all(fresh_dir); + } + return Err(message); + } + write_bootstrap_timing_receipt(BootstrapTimingReceiptInput { + path: receipt_path, + workspace, + manifest_dir, + verify_only, + status: if preserve_fresh_dir { + "completed_emit_fresh_verify" + } else { + "completed" + }, + generated_file_count, + emitted_file_count, + phases: std::mem::take(phases), + elapsed_ms: elapsed_ms(run_started), + changed_generated_files: Vec::new(), + })?; + if !preserve_fresh_dir { + let _ = fs::remove_dir_all(fresh_dir); + } + if preserve_fresh_dir { + println!( + "regen_stage0 --emit-fresh --verify: committed stage0 matches fresh self-compile; artifacts at {}", + fresh_dir.display() + ); + } else { + println!("regen_stage0 --verify: committed stage0 matches fresh self-compile."); + } + Ok(()) +} + fn run() -> Result<(), String> { let run_started = Instant::now(); let args: Vec = env::args().skip(1).collect(); @@ -168,22 +289,44 @@ fn run() -> Result<(), String> { // surface as cargo errors. Build it to measure them; the committed seed is // never touched. let mut emit_fresh: Option = None; - let verify_only = match args.as_slice() { - [] => false, - [flag] if flag == "--verify" => true, - [flag, dir] if flag == "--emit-fresh" => { - emit_fresh = Some(PathBuf::from(dir)); - false - } - unexpected => { - return Err(format!( - "regen_stage0: unexpected arguments: {unexpected:?}\n\ - Usage: regen_stage0 [--verify | --emit-fresh ]\n\ - Omit flags to write stage0; pass exactly `--verify` to check without writing;\n\ - pass `--emit-fresh ` to assemble the faithful emitted crate into and stop." - )); + let mut write_manifest: Option = None; + let mut verify_only = false; + let mut index = 0usize; + while index < args.len() { + match args[index].as_str() { + "--verify" => { + verify_only = true; + index += 1; + } + "--emit-fresh" => { + let dir = args + .get(index + 1) + .ok_or_else(|| "regen_stage0: --emit-fresh requires ".to_string())?; + emit_fresh = Some(PathBuf::from(dir)); + index += 2; + } + "--write-manifest" => { + let path = args + .get(index + 1) + .ok_or_else(|| "regen_stage0: --write-manifest requires ".to_string())?; + write_manifest = Some(PathBuf::from(path)); + index += 2; + } + unexpected => { + return Err(format!( + "regen_stage0: unexpected argument: {unexpected:?}\n\ + Usage: regen_stage0 [--verify | --emit-fresh [--write-manifest ] [--verify]]\n\ + Omit flags to write stage0; pass `--verify` to check without writing;\n\ + pass `--emit-fresh ` to assemble the faithful emitted crate into and stop;\n\ + add `--write-manifest ` to also write the GENERATED_STAGE0_FILES roster there;\n\ + combine `--emit-fresh` with `--verify` to leave the assembled crate in place after checking." + )); + } } - }; + } + if write_manifest.is_some() && emit_fresh.is_none() { + return Err("regen_stage0: --write-manifest requires --emit-fresh".to_string()); + } assert_registry_is_partitioned()?; let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); @@ -220,7 +363,31 @@ fn run() -> Result<(), String> { })?; if emit_fresh.is_some() { - // Non-destructive: leave the assembled crate in place for cargo build. + if let Some(manifest_path) = &write_manifest { + let content = GENERATED_STAGE0_FILES.join("\n"); + fs::write(manifest_path, content) + .map_err(|e| format!("write roster manifest {}: {e}", manifest_path.display()))?; + println!( + "regen_stage0 --write-manifest: wrote {} file entries to {}", + GENERATED_STAGE0_FILES.len(), + manifest_path.display() + ); + } + if verify_only { + return finish_verify_checks(VerifyFinishInput { + receipt_path: &receipt_path, + workspace: &workspace, + manifest_dir: &manifest_dir, + stage0_src: &stage0_src, + fresh_dir: &fresh_dir, + verify_only, + generated_file_count: GENERATED_STAGE0_FILES.len(), + emitted_file_count: emitted.len(), + phases: &mut phases, + run_started, + preserve_fresh_dir: true, + }); + } write_bootstrap_timing_receipt(BootstrapTimingReceiptInput { path: &receipt_path, workspace: &workspace, @@ -241,80 +408,19 @@ fn run() -> Result<(), String> { } if verify_only { - let verify_result = time_phase(&mut phases, "verify_stage0_matches", || { - verify_stage0_matches(&stage0_src, &fresh_dir.join("src")) - }); - if let Err(message) = verify_result { - let changed_generated_files = - changed_registered_outputs(&fresh_dir.join("src"), &stage0_src)?; - write_bootstrap_timing_receipt(BootstrapTimingReceiptInput { - path: &receipt_path, - workspace: &workspace, - manifest_dir: &manifest_dir, - verify_only, - status: "failed_stage0_stale", - generated_file_count: GENERATED_STAGE0_FILES.len(), - emitted_file_count: emitted.len(), - phases, - elapsed_ms: elapsed_ms(run_started), - changed_generated_files, - })?; - let _ = fs::remove_dir_all(&fresh_dir); - return Err(message); - } - if let Err(message) = - time_phase(&mut phases, "verify_stage0_split_crate_boundaries", || { - verify_stage0_split_crate_boundaries(&workspace) - }) - { - write_bootstrap_timing_receipt(BootstrapTimingReceiptInput { - path: &receipt_path, - workspace: &workspace, - manifest_dir: &manifest_dir, - verify_only, - status: "failed_stage0_split_crate_stale", - generated_file_count: GENERATED_STAGE0_FILES.len(), - emitted_file_count: emitted.len(), - phases, - elapsed_ms: elapsed_ms(run_started), - changed_generated_files: Vec::new(), - })?; - let _ = fs::remove_dir_all(&fresh_dir); - return Err(message); - } - if let Err(message) = time_phase(&mut phases, "verify_workspace_members", || { - verify_workspace_members(&workspace) - }) { - write_bootstrap_timing_receipt(BootstrapTimingReceiptInput { - path: &receipt_path, - workspace: &workspace, - manifest_dir: &manifest_dir, - verify_only, - status: "failed_workspace_members_stale", - generated_file_count: GENERATED_STAGE0_FILES.len(), - emitted_file_count: emitted.len(), - phases, - elapsed_ms: elapsed_ms(run_started), - changed_generated_files: Vec::new(), - })?; - let _ = fs::remove_dir_all(&fresh_dir); - return Err(message); - } - write_bootstrap_timing_receipt(BootstrapTimingReceiptInput { - path: &receipt_path, + return finish_verify_checks(VerifyFinishInput { + receipt_path: &receipt_path, workspace: &workspace, manifest_dir: &manifest_dir, + stage0_src: &stage0_src, + fresh_dir: &fresh_dir, verify_only, - status: "completed", generated_file_count: GENERATED_STAGE0_FILES.len(), emitted_file_count: emitted.len(), - phases, - elapsed_ms: elapsed_ms(run_started), - changed_generated_files: Vec::new(), - })?; - let _ = fs::remove_dir_all(&fresh_dir); - println!("regen_stage0 --verify: committed stage0 matches fresh self-compile."); - return Ok(()); + phases: &mut phases, + run_started, + preserve_fresh_dir: false, + }); } let changed_generated_files = changed_registered_outputs(&fresh_dir.join("src"), &stage0_src)?; diff --git a/src/v2/compiler/self_host.dag b/src/v2/compiler/self_host.dag index 830406684c2..ddbd9de1c07 100644 --- a/src/v2/compiler/self_host.dag +++ b/src/v2/compiler/self_host.dag @@ -1,7 +1,8 @@ module v2.compiler.self_host -import extdeps.communication.medium { Medium } -import v2.std.algebra { fold_list } +import extdeps.communication.medium { Lossless, Medium } +import extdeps.filesystem.filesystem_io +import v2.std.algebra { filter, fold_list } import v2.std.collection { List } import v2.std.logic { Bool } import v2.std.diagnostic { @@ -267,6 +268,56 @@ fn stage_emission_equality( } } +fn boundary_tag_bytes(filename: String, content: String) -> String { + concat(filename, concat("\n", content)) +} + +fn read_roster_manifest(manifest_path: String) -> List { + let raw = filesystem_read(path: manifest_path).content + filter(xs: raw.split("\n"), predicate: fn(s) { !string_is_empty(s: s) }) +} + +fn fold_side_bytes(root: String, roster: List) -> String { + fold_list( + xs: roster, + empty: "", + cons: fn(acc, filename) { + let path = concat(root, concat("/", filename)) + let content = filesystem_read(path: path).content + concat(acc, boundary_tag_bytes(filename: filename, content: content)) + } + ) +} + +fn source_models_from_roster_and_roots( + committed_root: String, + emitted_root: String, + roster: List +) -> SourceModels { + SourceModels { + stage1_committed_seed_source: Medium { + carried: fold_side_bytes(root: committed_root, roster: roster), + fidelity: Lossless + }, + stage2_regen_emitted_source: Medium { + carried: fold_side_bytes(root: emitted_root, roster: roster), + fidelity: Lossless + } + } +} + +fn source_models_from_manifest_and_roots( + committed_root: String, + emitted_root: String, + manifest_path: String +) -> SourceModels { + source_models_from_roster_and_roots( + committed_root: committed_root, + emitted_root: emitted_root, + roster: read_roster_manifest(manifest_path: manifest_path) + ) +} + fn self_host_fixed_point_validate( stage1_candidate: CoreNode, source_models: SourceModels, diff --git a/src/v2/lens/realization_vocabulary_containment.dag b/src/v2/lens/realization_vocabulary_containment.dag index aa680ccbd48..716eac48ad1 100644 --- a/src/v2/lens/realization_vocabulary_containment.dag +++ b/src/v2/lens/realization_vocabulary_containment.dag @@ -45,9 +45,11 @@ data realization_vocab_exception_roster: List = [ "dsl/tools/host_prelude.dag", "dsl/tools/layering_imports_transport.dag", "dsl/tools/regen_verify_transport.dag", + "dsl/tools/self_host_realized_comparison_transport.dag", "dsl/tools/v1_dag_parse_transport.dag", "src/v2/workflow/compiler_closure_ingest_transport.dag", "src/v2/workflow/floor_diff_observe.dag", + "src/v2/workflow/self_host_realized_comparison_gate.dag", "src/v2/workflow/source_root_ingest_transport.dag" ] diff --git a/src/v2/test/claim/self_host_realized_comparison_floor_test.dag b/src/v2/test/claim/self_host_realized_comparison_floor_test.dag new file mode 100644 index 00000000000..c2efaf42d07 --- /dev/null +++ b/src/v2/test/claim/self_host_realized_comparison_floor_test.dag @@ -0,0 +1,80 @@ +module v2.test.self_host_realized_comparison_floor + +import extdeps.communication.medium { Lossless, Medium } +import v2.compiler.self_host { + fixed_point_scaffold, + fold_side_bytes, + SourceModels +} +import v2.std.logic { Bool } +import v2.std.node { + Atom, + Node, + TypeNode, + SyntheticOccurrence +} +import v2.std.witness { Holds, Violates } + +data floor_committed_root: String = "src/v1/stage0/src" + +fn floor_seed_leaf() -> Node { + Node { + kind: TypeNode { connective: Atom { identity: ^floor_seed_leaf } }, + children: [], + occurrence_id: SyntheticOccurrence + } +} + +test fn self_host_realized_comparison_committed_bytes_stage1_known_fragment_holds() -> Bool { + let folded = fold_side_bytes(root: floor_committed_root, roster: ["lib.rs"]) + string_contains(folded, "Generated by v1 compiler") +} + +test fn self_host_realized_comparison_boundary_tag_contains_filename_holds() -> Bool { + let folded = fold_side_bytes(root: floor_committed_root, roster: ["lib.rs"]) + string_contains(folded, "lib.rs") +} + +test fn self_host_realized_comparison_generated_file_change_flips_verdict_holds() -> Bool { + let fold_lib = fold_side_bytes(root: floor_committed_root, roster: ["lib.rs"]) + let fold_vrt = fold_side_bytes(root: floor_committed_root, roster: ["v1_rt.rs"]) + fold_lib != fold_vrt +} + +test fn self_host_realized_comparison_hand_maintained_excluded_by_roster_holds() -> Bool { + let fold_generated = fold_side_bytes(root: floor_committed_root, roster: ["lib.rs"]) + let fold_hand_maintained = fold_side_bytes(root: floor_committed_root, roster: ["cli_run.rs"]) + string_contains(fold_generated, "lib.rs\n") + && string_contains(fold_hand_maintained, "cli_run.rs\n") + && !string_contains(fold_generated, "cli_run.rs\n") +} + +test fn self_host_realized_comparison_drift_goes_violates_holds() -> Bool { + let drifted = SourceModels { + stage1_committed_seed_source: Medium { carried: "self_host_fixture_seed_v1", fidelity: Lossless }, + stage2_regen_emitted_source: Medium { carried: "self_host_fixture_seed_v1_DRIFTED", fidelity: Lossless } + } + match fixed_point_scaffold( + stage1_node: floor_seed_leaf(), + stage2_node: floor_seed_leaf(), + source_models: drifted + ) { + Holds { value: _ } => false + Violates { diagnostic: _ } => true + } +} + +test fn self_host_realized_comparison_equal_pair_closes_holds() -> Bool { + let equal = SourceModels { + stage1_committed_seed_source: Medium { carried: "self_host_fixture_seed_v1", fidelity: Lossless }, + stage2_regen_emitted_source: Medium { carried: "self_host_fixture_seed_v1", fidelity: Lossless } + } + match fixed_point_scaffold( + stage1_node: floor_seed_leaf(), + stage2_node: floor_seed_leaf(), + source_models: equal + ) { + Holds { value: _ } => true + Violates { diagnostic: _ } => false + } +} diff --git a/src/v2/workflow/ci_floor_plan.dag b/src/v2/workflow/ci_floor_plan.dag index 39b4a290842..69d73f99bdd 100644 --- a/src/v2/workflow/ci_floor_plan.dag +++ b/src/v2/workflow/ci_floor_plan.dag @@ -5,7 +5,7 @@ import gunbc.ci_spec { gunbc_ci_spec, CiSpec, Gate, RustMonolithGate, EmitHostGate, LayeringImportsGate, ExtdepsExternalAuthorityGate, DslCompileCleanGate, GeneratedArtifactDriftGate, - SourceRootIngestGate, RegenVerifyGate, EmitDeterminismGate + SourceRootIngestGate, RegenVerifyGate, SelfHostRealizedComparisonGate, EmitDeterminismGate } import v2.std.algebra { length, list_snoc_item, skip, zip_map } import v2.std.collection { List } @@ -68,6 +68,8 @@ data floor_gate_witness_entry: String = "dsl/tools/floor_effect_gate_witness.dag data floor_source_root_ingest_gate_entry: String = "src/v2/workflow/source_root_ingest_gate.dag" +data floor_self_host_realized_comparison_gate_entry: String = "src/v2/workflow/self_host_realized_comparison_gate.dag" + data floor_corpus_node: Node = Node { kind: TypeNode { connective: Atom { identity: ^gunbc_floor_corpus } }, children: [] @@ -83,6 +85,7 @@ fn gate_node(g: Gate) -> Node { GeneratedArtifactDriftGate => Node { kind: TypeNode { connective: Atom { identity: ^gunbc_floor_gate_generated_artifact } }, children: [] } SourceRootIngestGate => Node { kind: TypeNode { connective: Atom { identity: ^gunbc_floor_gate_sourceingest } }, children: [] } RegenVerifyGate => Node { kind: TypeNode { connective: Atom { identity: ^gunbc_floor_gate_regen_verify } }, children: [] } + SelfHostRealizedComparisonGate => Node { kind: TypeNode { connective: Atom { identity: ^gunbc_floor_gate_self_host_realized_comparison } }, children: [] } EmitDeterminismGate => Node { kind: TypeNode { connective: Atom { identity: ^gunbc_floor_gate_emit_determinism } }, children: [] } } } @@ -117,6 +120,12 @@ fn gate_runnable_profile(g: Gate) -> RunnableResourceProfile { spawns_host_compiler: true, memory: runnable_memory_substantial_sum(left: per_shard, right: host_compiler) ) + SelfHostRealizedComparisonGate => + runnable_resource_profile( + heavy_whole_tree_resolve: false, + spawns_host_compiler: false, + memory: per_shard + ) EmitDeterminismGate => runnable_resource_profile( heavy_whole_tree_resolve: true, @@ -151,6 +160,11 @@ fn gate_runnable(g: Gate) -> Runnable { profile: profile } RegenVerifyGate => RunnableSingleClaim { entry: floor_gate_witness_entry, function: "regen_verify_gate_passes", profile: profile } + SelfHostRealizedComparisonGate => RunnableSingleClaim { + entry: floor_self_host_realized_comparison_gate_entry, + function: "self_host_realized_comparison_gate_passes", + profile: profile + } EmitDeterminismGate => RunnableSingleClaim { entry: floor_gate_witness_entry, function: "emit_determinism_gate_passes", profile: profile } } } @@ -285,10 +299,32 @@ fn floor_data_dependencies(spec: CiSpec) -> List { ) } +fn floor_regen_emit_artifact_resource_edges(spec: CiSpec) -> List { + let has_self_host = fold(spec.gates, init: false, f: fn(acc, g) { + match g { + SelfHostRealizedComparisonGate => true + _ => acc + } + }) + if has_self_host { + [ + floor_resource_depends_on( + source: gate_node(g: RegenVerifyGate), + dependent: gate_node(g: SelfHostRealizedComparisonGate) + ) + ] + } else { + [] + } +} + fn floor_resource_dependencies(spec: CiSpec) -> List { concat( floor_corpus_exclusion_resource_edges(spec: spec), - floor_heavy_resolve_chain_resource_edges(spec: spec) + concat( + floor_heavy_resolve_chain_resource_edges(spec: spec), + floor_regen_emit_artifact_resource_edges(spec: spec) + ) ) } diff --git a/src/v2/workflow/self_host_realized_comparison_claims.dag b/src/v2/workflow/self_host_realized_comparison_claims.dag new file mode 100644 index 00000000000..f6bac626a69 --- /dev/null +++ b/src/v2/workflow/self_host_realized_comparison_claims.dag @@ -0,0 +1,57 @@ +module v2.workflow.self_host_realized_comparison_claims + +import v2.std.diagnostic { Accepted, Rejected } +import v2.std.logic { Bool } +import v2.std.node { + Atom, + Node, + TypeNode, + SyntheticOccurrence +} +import v2.compiler.self_host { + fold_side_bytes, + self_host_fixed_point_validate, + source_models_from_roster_and_roots +} +import tools.self_host_realized_comparison_transport { + realized_comparison_committed_src_root, + realized_comparison_emitted_src_root, + realized_comparison_ci_subset_roster +} + +fn realized_comparison_fixture_leaf() -> Node { + Node { + kind: TypeNode { connective: Atom { identity: ^realized_comparison_fixture_leaf } }, + children: [], + occurrence_id: SyntheticOccurrence + } +} + +func self_host_realized_comparison_reads_real_bytes_both_sides_holds() -> Bool { + let stage1_folded = fold_side_bytes( + root: realized_comparison_committed_src_root, + roster: ["lib.rs"] + ) + let stage2_folded = fold_side_bytes( + root: realized_comparison_emitted_src_root, + roster: ["lib.rs"] + ) + string_contains(stage1_folded, "Generated by v1 compiler") + && string_contains(stage2_folded, "Generated by v1 compiler") +} + +func self_host_realized_comparison_staleness_gate_holds() -> Bool { + let models = source_models_from_roster_and_roots( + committed_root: realized_comparison_committed_src_root, + emitted_root: realized_comparison_emitted_src_root, + roster: realized_comparison_ci_subset_roster + ) + match self_host_fixed_point_validate( + stage1_candidate: realized_comparison_fixture_leaf(), + source_models: models, + reference_stage1: realized_comparison_fixture_leaf() + ) { + Accepted { value: _, diagnostics: _ } => true + Rejected { diagnostics: _ } => false + } +} diff --git a/src/v2/workflow/self_host_realized_comparison_gate.dag b/src/v2/workflow/self_host_realized_comparison_gate.dag new file mode 100644 index 00000000000..e28444544a1 --- /dev/null +++ b/src/v2/workflow/self_host_realized_comparison_gate.dag @@ -0,0 +1,67 @@ +module v2.workflow.self_host_realized_comparison_gate + +import extdeps.shell +import gunbc.ci_layer_roots { witness_layer_roots } +import std.logic { Bool } +import extdeps.languages.bash.program { + ShellProgram, ShellStmt, ShellWord, + lit, var_ref, command, serialize_bash +} +import std.disposition { Disposition, Scaffold, RealizationDispatch, Terminal } +import std.decl_ref { DeclarationRef, WholeDeclaration } +import tools.host_prelude { ensure_gunbc_built } + +data srfc_gate_test_entry: String = "src/v2/workflow/self_host_realized_comparison_claims.dag" +data srfc_staleness_gate_witness: String = "self_host_realized_comparison_staleness_gate_holds" +data srfc_provenance_witness: String = "self_host_realized_comparison_reads_real_bytes_both_sides_holds" + +data self_host_realized_comparison_gate_interim: Disposition = Terminal { + reason: "INTERIM gate: subset-roster fold (lib.rs,v1_rt.rs) dodges Peano digest wall; emit is full 89-file via RegenVerifyGate shared --emit-fresh. Dissolves when source_text_code_unit_digest is host-grounded and fold widens to full GENERATED_STAGE0_FILES roster." +} + +data self_host_realized_comparison_gate_disposition: Disposition = Scaffold { + dissolves_to: RealizationDispatch, + bind: DeclarationRef { + module_path: "extdeps.languages.bash.program", + decl_name: "serialize_bash", + field: WholeDeclaration + } +} + +fn srfc_source_root_flags() -> List { + fold(witness_layer_roots, init: [], f: (acc, r) => concat(acc, [lit(text: "--source-root"), lit(text: r)])) +} + +fn srfc_gunbc_claim_run(function: String) -> ShellStmt { + command(words: concat( + concat([var_ref(name: "GUNBC"), lit(text: "run")], srfc_source_root_flags()), + [ + lit(text: "--entry"), lit(text: srfc_gate_test_entry), + lit(text: "--function"), lit(text: function), + lit(text: "--claim-run") + ] + )) +} + +fn realized_comparison_gate_program() -> ShellProgram { + ShellProgram { + set_e: true, + statements: concat( + ensure_gunbc_built(), + [ + command(words: [lit(text: "cd"), var_ref(name: "ROOT")]), + srfc_gunbc_claim_run(function: srfc_provenance_witness), + srfc_gunbc_claim_run(function: srfc_staleness_gate_witness) + ] + ) + } +} + +func run_self_host_realized_comparison_gate() -> Bool { + let result = shell.Exec.Run(script: serialize_bash(p: realized_comparison_gate_program())) + result.success +} + +func self_host_realized_comparison_gate_passes() -> Bool { + run_self_host_realized_comparison_gate() +}