diff --git a/dag/extdeps/languages/go/types.dag b/dag/extdeps/languages/go/types.dag index 0c8575c91e9..34b5ffc5366 100644 --- a/dag/extdeps/languages/go/types.dag +++ b/dag/extdeps/languages/go/types.dag @@ -24,6 +24,11 @@ data go_type_checkpoints: List = [ { dag_name: "Json", target_type: "interface{}", grounding_type: "interface{}", default_expr: "nil", is_copy: none, literal_suffix: none } ] +// NO PointwisePower ROW — see extdeps.languages.rust.types (the note above +// rust_algebra_inhabitants) for the full reasoning. PointwisePower is the open-support +// characteristic-function carrier; a finite map[...]struct{} realizes FinitePowerSet, not it. +// The fossil row's only live effect was the record-literal refusal panic, measured on the +// emitted 00_compile closure's first native run (2026-09-07). data go_algebra_inhabitants: List = [ { algebra: "FreeMonoid", @@ -40,13 +45,6 @@ data go_algebra_inhabitants: List = [ import_path: none, is_copy: none }, - { algebra: "PointwisePower", - template: "map[\{0\}]struct{}", - arity: 1, - identity_expr: "nil", - import_path: none, - is_copy: none }, - { algebra: "PartialFunction", template: "map[\{0\}]\{1\}", arity: 2, diff --git a/dag/extdeps/languages/python/types.dag b/dag/extdeps/languages/python/types.dag index adb244c0533..5aa2b545bef 100644 --- a/dag/extdeps/languages/python/types.dag +++ b/dag/extdeps/languages/python/types.dag @@ -24,6 +24,11 @@ data python_type_checkpoints: List = [ { dag_name: "Json", target_type: "dict", grounding_type: "dict", default_expr: "{}", is_copy: none, literal_suffix: none } ] +// NO PointwisePower ROW — see extdeps.languages.rust.types (the note above +// rust_algebra_inhabitants) for the full reasoning. PointwisePower is the open-support +// characteristic-function carrier; a finite set[...] realizes FinitePowerSet, not it. The fossil +// row's only live effect was the record-literal refusal panic, measured on the emitted +// 00_compile closure's first native run (2026-09-07). data python_algebra_inhabitants: List = [ { algebra: "FreeMonoid", template: "list[\{0\}]", @@ -39,13 +44,6 @@ data python_algebra_inhabitants: List = [ import_path: none, is_copy: none }, - { algebra: "PointwisePower", - template: "set[\{0\}]", - arity: 1, - identity_expr: "set()", - import_path: none, - is_copy: none }, - { algebra: "PartialFunction", template: "dict[\{0\}, \{1\}]", arity: 2, diff --git a/dag/extdeps/languages/rust/types.dag b/dag/extdeps/languages/rust/types.dag index 88b6c90a8b7..8f8d7692381 100644 --- a/dag/extdeps/languages/rust/types.dag +++ b/dag/extdeps/languages/rust/types.dag @@ -51,6 +51,23 @@ data rust_checkpoint_scalar_arity_note: String = "A TypeCheckpoint row states on // authority for how a Rust primitive is spelled is `extdeps.languages.rust.primitives` // `rust_grounding_primitives`, through its `target_name` rows. A name list here would be // a second spelling authority for one fact. +// NO PointwisePower ROW IN THIS ROSTER, BY CONSTRUCTION. PointwisePower is the OPEN-support +// power set presented as a characteristic function (std.algebra, profile OpenSupport): its only +// construction is a record literal carrying `member: fn(T) -> Bool`, and no finite enumerable +// BTreeSet can realize an open membership oracle. The row that stood here was a pre-split +// fossil — std.algebra's carrier-split note records the byte-identical "BooleanAlgebra" / +// "PointwisePower" rows that prompted the split, and the FinitePowerSet row below keeps the +// finite-set realization the fossil actually described. Its one live effect was measured on the +// emitted 00_compile closure's first native run (2026-09-07): +// keyed_container_has_target_inhabitant answered true for the record literal, so twelve +// language-model and nullable-fixpoint construction sites emitted the record-shaped-carrier +// refusal panic where the interpreted pipeline evaluates — while the type renderer, keyed on +// declaration provenance, already rendered PointwisePower structurally, so the row made the two +// emission paths disagree. Removing it routes construction to the same structural path the type +// renderer already takes (the bool_boolean_algebra pattern: Rc'd closure fields, _phantom +// filled). Regression control: +// test.claim.self_host_emitted_call_target_realization_witness_test +// w_pointwise_power_record_shape_constructs_its_structural_carrier. data rust_algebra_inhabitants: List = [ { algebra: "FreeMonoid", @@ -67,13 +84,6 @@ data rust_algebra_inhabitants: List = [ import_path: none, is_copy: false }, - { algebra: "PointwisePower", - template: "BTreeSet<\{0\}>", - arity: 1, - identity_expr: "BTreeSet::new()", - import_path: none, - is_copy: false }, - { algebra: "FinitelySupportedFunction", template: "HashMap<\{0\}, \{1\}>", arity: 2, diff --git a/dag/extdeps/languages/typescript/types.dag b/dag/extdeps/languages/typescript/types.dag index 76593826367..aacefcf5b4f 100644 --- a/dag/extdeps/languages/typescript/types.dag +++ b/dag/extdeps/languages/typescript/types.dag @@ -24,6 +24,11 @@ data ts_type_checkpoints: List = [ { dag_name: "Json", target_type: "unknown", grounding_type: "unknown", default_expr: "null", is_copy: false, literal_suffix: none } ] +// NO PointwisePower ROW — see extdeps.languages.rust.types (the note above +// rust_algebra_inhabitants) for the full reasoning. PointwisePower is the open-support +// characteristic-function carrier; a finite Set<...> realizes FinitePowerSet, not it. The fossil +// row's only live effect was the record-literal refusal panic, measured on the emitted +// 00_compile closure's first native run (2026-09-07). data ts_algebra_inhabitants: List = [ { algebra: "FreeMonoid", @@ -40,13 +45,6 @@ data ts_algebra_inhabitants: List = [ import_path: none, is_copy: false }, - { algebra: "PointwisePower", - template: "Set<\{0\}>", - arity: 1, - identity_expr: "new Set()", - import_path: none, - is_copy: false }, - { algebra: "PartialFunction", template: "Map<\{0\}, \{1\}>", arity: 2, diff --git a/dag/gunbc/commit_workflow.dag b/dag/gunbc/commit_workflow.dag index 35953767851..a5233ed93c3 100644 --- a/dag/gunbc/commit_workflow.dag +++ b/dag/gunbc/commit_workflow.dag @@ -1684,8 +1684,8 @@ data commit_gate_roster: List = [ check_fns: [ "ingested_classical_not_resolves_holds", "ingested_classical_not_arrow_has_match_holds", - "ingested_classical_not_canonical_emit_refuses_underived_holds", - "ingested_classical_not_staging_emit_refuses_underived_holds", + "ingested_classical_not_canonical_emit_accepts_holds", + "ingested_classical_not_staging_emit_accepts_holds", "ingested_classical_not_emit_accepts_holds", "ingested_classical_not_emit_octet_bridge_shape_holds", "ingested_classical_not_emit_source_arm_shape_holds" diff --git a/dag/gunbc/declined_live_tree_defect_classification.dag b/dag/gunbc/declined_live_tree_defect_classification.dag index 6d4d4ac44eb..ede544fcd69 100644 --- a/dag/gunbc/declined_live_tree_defect_classification.dag +++ b/dag/gunbc/declined_live_tree_defect_classification.dag @@ -205,8 +205,8 @@ data group_a_remaining_rows: List = [ ClassifiedRow { identity: identity(module_path: "v2.test.execution.self_host_candidate_generation", function: "candidate_generation_translate_self_emit_dag_add_slice_holds"), classification: RealDefect { subject: CompilerBehaviourRefusal }, - measured_evidence: "staged by execution: infer(dag_add_emitted_root) ACCEPTS, translate(inferred, dag_add_target_model) REFUSES with head reason infer_grounding_not_derived; identical under --hermetic and --wet, so the mode is not the discriminator", - disposition: RoutedToOwner { owner: "the v2 self-host lane -- infer_grounding_not_derived is the known v2-self-grounding-frontier class already registered in gunbc.guarantee_probe_corpus, not a new defect" } + measured_evidence: "staged by execution: infer(dag_add_emitted_root) ACCEPTS, translate(inferred, dag_add_target_model) REFUSES with head reason infer_grounding_not_derived; identical under --hermetic and --wet, so the mode is not the discriminator. REPAIR RECEIPT, 2026-09-07: the owner lane's declared-inhabitant roster-membership derivation (v2.compiler.infer infer_node_declared_in_language_inhabitants) grounds the slice's ten type-spine nodes by lookup against the dag language authority's declared-inhabitants roster, and the witness now PASSES under claim_batch --hermetic on the repairing tree (re-measured after the merge onto main at 5ee9cab5fa). The classification and the first sentence are kept verbatim as the dated record of the defect as routed; this sentence is the repair measurement.", + disposition: RepairedInThisChange }, ClassifiedRow { identity: identity(module_path: "v2.test.lens_test_migration_debt.test_migration_debt_test", function: "retained_rust_kernel_wall_holds_against_live_tree"), diff --git a/dag/gunbc/explicit_witness_admission.dag b/dag/gunbc/explicit_witness_admission.dag index bf1d09c0929..4c7498d8156 100644 --- a/dag/gunbc/explicit_witness_admission.dag +++ b/dag/gunbc/explicit_witness_admission.dag @@ -337,14 +337,6 @@ data explicit_witness_admissions: List = [ reason: "the discriminating closing validation for roadmap node namespace-reference-derived-closure, RED BY DESIGN while its six-clause production admission frontier carries typed unavailable rows naming #7515 and the Class-B pool-independence wall. The separately discovered fixture suite executes P1 and proves every clause rejects its planted wrong output; this witness imports no fixture admissions and is bound directly by namespace_reference_derived_closure_execution_contract, so zero implementation progress cannot verify as complete.", dissolution: unbound_dissolution(description: "the production candidate producer and dependency projection establish all six ReferenceDerivedClosureCapability rows by execution — this row deletes in the completing change, promoting the unchanged acceptance witness to ordinary DiscoverySelection as the permanent namespace-derived-closure regression wall") ), - known_red_probe( - entry: "src/v2/test/claim/long/direct_rust_door_production_group_test.dag", - f: "direct_rust_door_production_group_closing_expectation_holds", - kind: CorpusWitnessKind, - budget: SubstrateLongLaneEvalBudget, - reason: "THE canonical closing validation for roadmap node v2-emitter-direct-rust-door, RED BY EXECUTION. Say the location claim exactly: ONE production implementation authority, ONE production walk per group construction, ONE scheduled group construction (the second occurrence was the budget-by-batch-membership deficit, closed 2026-08-06 when the eval budget became its own declared axis; direct_rust_door_group_root_is_scheduled_exactly_once is the executing receipt), and the IN-MEMORY group locates EmissionRejected while the SCHEDULED executor receipt remains an unlocated Bool. Inside that group the observation stops at EmissionRejected, so the four artifact leaves report LeafNotReached carrying that stage while the missing-rule leaf is green. That one green leaf is what proves the red is located rather than uniform — for a consumer holding the group, not for anyone reading the falsifier receipt. It replaces three superseded rows deleted in the same change, whose fold answered SeedEmitterRequiredForWrittenSource for BOTH a rejected pipeline and a seed-dependent artifact. Not relaxable: the only edit that may green it is making the real inferred tree survive emission.", - dissolution: unbound_dissolution(description: "every leaf reaches LeafEstablished by execution — this row deletes in the completing change, promoting the unchanged group root to ordinary DiscoverySelection as the lane's permanent regression wall") - ), known_red_probe( entry: "dag/test/claim/self_host_parse_engine_hooks_behavioral_witness_test.dag", f: "self_host_parse_engine_hooks_behavioral_receipt_holds", diff --git a/dag/gunbc/guarantee_stall/roster.dag b/dag/gunbc/guarantee_stall/roster.dag index 6b97fd464fc..001b035d14c 100644 --- a/dag/gunbc/guarantee_stall/roster.dag +++ b/dag/gunbc/guarantee_stall/roster.dag @@ -29,7 +29,6 @@ import gunbc.guarantee_stall.enforcement_live_closure_gate_stall { enforcement_l import gunbc.guarantee_stall.mandatory_tag_live_corpus_stall { mandatory_tag_live_corpus_stall } import gunbc.guarantee_stall.parse_ingest_grammar_relation_stall { parse_ingest_grammar_relation_stall } import gunbc.guarantee_stall.required_regen_host_derivation_stall { required_regen_host_derivation_stall } -import gunbc.guarantee_stall.self_host_candidate_generation_add_slice_stall { self_host_candidate_generation_add_slice_stall } import gunbc.guarantee_stall.self_host_hand_authorized_cargo_toml_stall { self_host_hand_authorized_cargo_toml_stall } import gunbc.guarantee_stall.self_host_hand_authorized_driver_source_stall { self_host_hand_authorized_driver_source_stall } import gunbc.guarantee_stall.self_host_spelling_search_stall { self_host_spelling_search_stall } @@ -186,7 +185,6 @@ data all_guarantee_stalls: List = [ mandatory_tag_live_corpus_stall, parse_ingest_grammar_relation_stall, required_regen_host_derivation_stall, - self_host_candidate_generation_add_slice_stall, self_host_hand_authorized_cargo_toml_stall, self_host_hand_authorized_driver_source_stall, self_host_spelling_search_stall, diff --git a/dag/gunbc/guarantee_stall/self_host_candidate_generation_add_slice_stall.dag b/dag/gunbc/guarantee_stall/self_host_candidate_generation_add_slice_stall.dag deleted file mode 100644 index c04258004da..00000000000 --- a/dag/gunbc/guarantee_stall/self_host_candidate_generation_add_slice_stall.dag +++ /dev/null @@ -1,14 +0,0 @@ -module gunbc.guarantee_stall.self_host_candidate_generation_add_slice_stall - -import v2.std.algebra { Cons, Empty } -import gunbc.guarantee_rung { OutsideTheLadder, MechanicallyPreventable } -import gunbc.guarantee_stall { GuaranteeStall, ClimbableButUnbuilt, BoundedPopulation, climbs_when } - -data self_host_candidate_generation_add_slice_stall: GuaranteeStall = GuaranteeStall { - subject: "self-host candidate generation, translation, and emission disagree on the add slice", - current: OutsideTheLadder, - ceiling: MechanicallyPreventable, - blocker: ClimbableButUnbuilt, - population: BoundedPopulation { members: Cons { head: "v2.test.execution.self_host_candidate_generation.candidate_generation_translate_self_emit_dag_add_slice_holds", tail: Cons { head: "v2.test.execution.emit_ingest_python_same_language_round_trip.emit_ingest_python_same_language_round_trip_holds", tail: Cons { head: "v2.test.execution.emit_ingest_python_same_language_round_trip.python_same_language_source_emit_round_trip_holds", tail: Cons { head: "v2.test.execution.emit_ingest_typescript_same_language_round_trip.emit_ingest_typescript_same_language_round_trip_holds", tail: Cons { head: "v2.test.execution.emit_ingest_typescript_same_language_round_trip.typescript_same_language_source_emit_round_trip_holds", tail: Empty {} } } } } } }, - next_rung_trigger: climbs_when(capability: "candidate generation, translation, and self-emission agree on the add slice: infer derives grounding for the Arrow, Conj and Atom kinds of the add fn, so cross_language_compile stops carrying infer_grounding_not_derived over the same-language python and typescript parse fixtures") -} diff --git a/dag/gunbc/instruments/emission_entry_instrument.dag b/dag/gunbc/instruments/emission_entry_instrument.dag index d5fd2d4f5dd..50c1bb7df15 100644 --- a/dag/gunbc/instruments/emission_entry_instrument.dag +++ b/dag/gunbc/instruments/emission_entry_instrument.dag @@ -531,6 +531,19 @@ fn emission_scope_entry(sc: EmissionScope) -> EmissionScopeEntry { // recorded target TRUE BY CONSTRUCTION rather than checked afterwards, which is section 5's order // of preference; and where the triple cannot be read the half REFUSES, because an unpinned target // is exactly the state this pin exists to exclude. Found by review on gunbc#9857. +// +// THE TOOLCHAIN CHANNEL IS PINNED BY THE SAME ARGUMENT, ONE LEVEL UP. The cargo half runs with +// cwd = out_dir, a fresh mktemp directory: with no rust-toolchain.toml there, rustup resolves the +// host's DEFAULT toolchain, and a census under cargo 1.83 against one under cargo 1.93 would +// compare as equal epochs while different compilers did the measuring -- the fabricated +// comparability the target pin excludes, at the channel layer. Measured 2026-09-07 on this +// instrument: a host default of 1.83.0 met a crates.io index whose freshly published dependency +// manifests require edition2024, dependency resolution failed before any diagnostic existed, and +// the zero-diagnostic refusal fired (correctly) on an unmeasured tree. The pin is propagated by +// COPYING the repo's rust-toolchain.toml into out_dir: the file remains the sole in-repo channel +// authority (its header forbids a second pinned literal), and the copy makes the measured channel +// true by construction on any host. The gate's read_live_toolchain observes the same channel +// because every documented actuator invokes from the repository root, which the same file governs. fn measure_cargo_half( subject: EmissionMeasurementSubject, root: String, @@ -582,11 +595,18 @@ fn measure_cargo_half( path: concat(out_dir, "/Cargo.toml"), content: cssl_v1_compiled_probe_lib_cargo_toml(root: root) ) - if !manifest.success { + let channel = Filesystem.Read(path: concat(root, "/rust-toolchain.toml")) + let channel_pin = if channel.success { + Filesystem.Write(path: concat(out_dir, "/rust-toolchain.toml"), content: channel.content).success + } else { + false + } + let scaffold_incomplete = !manifest.success || !channel_pin + if scaffold_incomplete { EmissionCargoUnreached { subject: subject, stage: StageManifest, - cause: concat("could not write the probe crate manifest under ", out_dir), + cause: concat("could not write the probe crate manifest and toolchain channel pin under ", out_dir), emit_diagnostics: emit_diagnostics, emitted_files: emitted_files } diff --git a/dag/gunbc/roadmap/roadmap_authority.dag b/dag/gunbc/roadmap/roadmap_authority.dag index 756030a8755..52c6663ad64 100644 --- a/dag/gunbc/roadmap/roadmap_authority.dag +++ b/dag/gunbc/roadmap/roadmap_authority.dag @@ -548,28 +548,27 @@ fn v2_emitter_producer_provenance_execution_contract() -> WorkItemExecutionContr // chosen over the suite's other twenty-six claims because it is the one that fails if the // unrepresentability itself lapses, rather than one that checks a roster stayed in step. -// THE CANONICAL CLOSING VALIDATION for v2-emitter-direct-rust-door, rebound to -// direct_rust_door_production_group_closing_expectation_holds. The production path is now observed -// through ProvenancedRustEmissionObservation, which LOCATES the failure instead of erasing it: -// assembly ACCEPTS and inference ACCEPTS on the real add_probe source, and emission REJECTS the -// real inferred tree. That supersedes the earlier reading — carried in this note until 2026-08-05 -// and in the node first_slice — that the pieces were joined and the last step was simply not -// called; candidate_generation does call emit, and emit is where the path stops. The prior binding -// to witness_v2_emitter_direct_rust_door_closing_contract_holds is deleted along with its fold, -// whose single SeedEmitterRequiredForWrittenSource arm answered for BOTH a rejected pipeline and a -// seed-dependent artifact — a state-space conflation that made the red unlocatable. -// MissingTranslationRuleRefuses remains established through the rust_add missing-rules serialize -// refusal and is carried as the group's one green leaf, which is what proves the parent's red is -// located rather than uniform. Group ALGEBRA is discriminated by planted-observation controls at -// zero production cost (v2.test.self_host.direct_rust_door_group_algebra). State the walk claim -// exactly: ONE production implementation authority, ONE production walk per group construction, ONE -// scheduled group construction, measured by v2.test.claim.schedule_occurrence_multiplicity_test -// against the live falsifier schedule. The count was TWO until 2026-08-06: the root joined the -// long-lane batch solely to obtain an eval ceiling, because budget was a property of batch -// membership. Separating the execution-budget axis from the batch roster (std.witness_admission -// witness_admission_axis_separation_note) dropped it to one, which is what that witness now -// asserts. And state the location claim exactly: the in-memory group locates EmissionRejected; the -// scheduled executor receipt remains an unlocated Bool. +// THE CANONICAL CLOSING VALIDATION for v2-emitter-direct-rust-door, bound to +// direct_rust_door_production_group_closing_expectation_holds — GREEN BY EXECUTION since +// 2026-09-06, when the known-red admission row deleted per its own dissolution ("every leaf +// reaches LeafEstablished by execution"), promoting the unchanged group root to the lane's +// permanent regression wall. The production walk is ingest → assemble → infer → emission on the +// real add_probe source and reaches ArtifactProduced: inference derives grounding for the whole +// specimen (declared-inhabitants roster membership, binding denotation, Conj/Arrow product +// introduction, canonical-operations and grammar-productions roster membership, and +// binding-reference derivation from the enclosing arrow's domain), and emission is the +// single-produced-declaration module composition (generate_rust_module_emission_candidate over +// v2.compiler.emit_produced), because a resolved module is a module shell over named +// declarations, never a type record to record-project. All five leaves establish: source +// fidelity byte-equal to direct_rust_door_expected_source, producer identity, seed-emitter +// absence, SourceProduced qualification, and the missing-rule refusal. State the walk claim +// exactly: ONE production implementation authority, ONE production walk per group construction, +// ONE scheduled group construction, measured by +// v2.test.claim.schedule_occurrence_multiplicity_test against the live falsifier schedule. +// Group ALGEBRA remains discriminated by planted-observation controls at zero production cost +// (v2.test.self_host.direct_rust_door_group_algebra). The group's observation is in-memory; the +// written-to-disk artifact surface is mint_written_provenanced_rust_artifact, which rides the +// same module composition. fn v2_emitter_direct_rust_door_execution_contract() -> WorkItemExecutionContract { gunbc_claim_execution_contract( diff --git a/dag/gunbc/self_host_compile_phase_frontier.dag b/dag/gunbc/self_host_compile_phase_frontier.dag index 55ef8629beb..47c4ce056be 100644 --- a/dag/gunbc/self_host_compile_phase_frontier.dag +++ b/dag/gunbc/self_host_compile_phase_frontier.dag @@ -1936,7 +1936,221 @@ fn receipt_1_eced9d24_receipt() -> CompilePhaseFrontierReceipt { ) } +// Receipt 2 was produced by the same one-entry instrument (tools.emission_entry_instrument +// measure_entry_emission) in a local invocation over the branch tree at 66765317ec, the tip of the +// XL-N String-carrier lane. TWO things change at this receipt and they are recorded separately +// because the design requires it. +// +// THE EPOCH CHANGES, AND THE CAUSE IS THE INSTRUMENT, NOT THE TOOLCHAIN. The measured +// `cargo check` now PINS the host triple on the invocation (tools.emission_entry_instrument +// measure_cargo_half, found by review on gunbc#9857), where both prior receipts recorded +// TargetDefaultedToHost. comparison_inputs_same therefore answers false against receipt_1 even +// though cargo, rustc, the triple, the profile and the classification policy are all unchanged, +// and the series fold admits the transition only with a reclassified predecessor. The +// reclassification is the IDENTITY MAP on receipt_1's board: nothing about the pinning changes +// which diagnostics rustc emits for an identical closure on an identical triple, so receipt_1's +// raw population classified under this epoch is receipt_1's board unchanged. That claim is +// checkable: the live gate's comparability probe reads the same five fields. +// +// THE POPULATION MOVES 155 -> 15. The 140-identity net reduction is credited to two landed lanes, +// measured at each step: the emitter's qualified-alias peel in field position (60cbd7b697, 72 -> +// 28 on this closure) and the twelve-error source cluster on top of it (66765317ec, 28 -> 15): +// integer.dag's decimal-digits boundary converts at ingress, 01_tokenize.dag converts structural +// lexemes to the host carrier at Token/UnboundSourceAnnotation construction, target_model.dag's +// EmitSpellingEscape carrier went structural with host/structural conversion at the +// EmitSpellingQuote boundary (bash.dag's escape rows follow), and three bare Violates +// constructions route through v2.std.witness's witness_violates so the emitter reads the carrier +// from a Witness-headed position. Nine of the thirteen added identities are hop relocations -- +// the same (file, code, message) key at a new line:column after those edits -- admitted by the +// hop index, not by disposition. The remaining four sit in v2_extdeps_languages_python.rs and +// v2_extdeps_languages_typescript.rs, files with NO receipt_1 identities: both modules entered the +// emitted closure only after receipt_1's tree (the v2.compiler.compile import closure walks to +// 158 resolved modules at eced9d24c599 and 164 at 66765317ec, python and typescript absent at the +// former and present at the latter), which is exactly what ExposedByNewEmittedModule admits. +data receipt_2_66765317_parse_observation: RustfmtParseObservation = RustfmtParseObservation { + producer: "rustfmt --emit stdout --edition 2021", + files_observed: 171, + refused_files: [] +} + +data receipt_2_66765317_raw_error_diagnostic_identities: List = [ + "src/v2_std_compilers_lexing.rs:168:11 E0308 mismatched types", + "src/v2_std_qualified_name.rs:234:44 E0308 mismatched types", + "src/gunbc_v1_interpreter_primitive_surface.rs:254:75 E0599 no method named `concat` found for struct `std::string::String` in the current scope", + "src/v2_std_compilers_target_model.rs:618:12 E0308 mismatched types", + "src/v2_std_compilers_target_model.rs:622:12 E0308 mismatched types", + "src/v2_std_compilers_target_model.rs:707:37 E0308 mismatched types", + "src/v2_std_compilers_target_model.rs:2629:43 E0308 mismatched types", + "src/v2_extdeps_languages_dag.rs:536:11 E0308 mismatched types", + "src/v2_extdeps_languages_dag.rs:545:11 E0308 mismatched types", + "src/v2_extdeps_languages_python.rs:328:13 E0308 mismatched types", + "src/v2_extdeps_languages_python.rs:967:11 E0308 mismatched types", + "src/v2_extdeps_languages_typescript.rs:177:13 E0308 mismatched types", + "src/v2_extdeps_languages_typescript.rs:1597:11 E0308 mismatched types", + "src/v2_compiler_parse.rs:529:143 E0282 type annotations needed", + "src/v2_compiler_parse.rs:836:59 E0308 mismatched types" +] + +data receipt_2_66765317_phase_board_fold: PersistedPhaseBoardFold = phase_board_from_census_identities( + parse_observation: receipt_2_66765317_parse_observation, + raw_identities: receipt_2_66765317_raw_error_diagnostic_identities +) + +fn receipt_2_66765317_receipt() -> CompilePhaseFrontierReceipt { + let receipt_1 = receipt_1_eced9d24_receipt() + seal_compile_phase_receipt( + sequence: 2, + observed_at: "2026-09-07T17:16:45Z" as NonEmptyStr, + subject: CompilePhaseReceiptSubject { + source_revision: "66765317ec3c33c5f948853abfe04bff57f535b5" as CommitSha, + git_tree_object: floor_discovery_tree_id(object_id: GitSha1ObjectId { digest: Sha1Digest { hex: "739dc39ded5f4edd99d7849402b54c4fa3fa8465" } }), + emitted_artifact_identity: sha256_digest(hex: "484963a7eb74b5d96fe622cdfd1b89684314d06443547e20e1fe7374e93df763" as NonEmptyStr), + compiler_identity: sha256_digest(hex: "db954404133bb15dc2295821f9248901098c9529307004d0d0277bb3ddeeac32" as NonEmptyStr), + assembler_identity: sha256_digest(hex: "bb7f3c7684a9288c0401e4d7d63cefcd644d0f02e80330c021b6f53c4e1009a6" as NonEmptyStr), + cargo_identity: "cargo 1.93.0 (083ac5135 2025-12-15)" as NonEmptyStr, + rustc_identity: "rustc 1.93.0 (254b59607 2026-01-19); commit 254b59607d4417e9dffbc307138ae5c86280fe4c; host x86_64-unknown-linux-gnu; LLVM 21.1.8" as NonEmptyStr, + invocation: CompilePhaseInvocation { + target: "x86_64-unknown-linux-gnu", + target_selection: TargetPinnedOnInvocation, + profile: "check/dev", + features: [] + }, + classification_policy_digest: rustc_phase_classification_policy_digest, + comparison_epoch: "rustc-1.93.0-x86_64-pinned-check-dev-rustc-error-index-1.93.0" as NonEmptyStr + }, + invocation: "local-105b239b-7518-43d7-b526-a5e2ee0b610b" as NonEmptyStr, + position: PhaseBoardSuccessor { predecessor_invocation: "buildbuddy-44d90e35-ccba-41d1-9a90-928a914b26a2" as NonEmptyStr }, + previous_prefix_digest: Present { value: receipt_1.prefix_digest }, + parse_evidence: ParseReached { evidence: receipt_2_66765317_parse_observation }, + board: receipt_2_66765317_phase_board_fold.board, + raw_error_diagnostic_identities: receipt_2_66765317_raw_error_diagnostic_identities, + unplaced_identities: receipt_2_66765317_phase_board_fold.unplaced_identities, + newly_exposed: [ + NewlyExposedDiagnostic { + identity: "src/v2_extdeps_languages_python.rs:328:13 E0308 mismatched types", + cause: ExposedByNewEmittedModule { module: "src/v2_extdeps_languages_python.rs" } + }, + NewlyExposedDiagnostic { + identity: "src/v2_extdeps_languages_python.rs:967:11 E0308 mismatched types", + cause: ExposedByNewEmittedModule { module: "src/v2_extdeps_languages_python.rs" } + }, + NewlyExposedDiagnostic { + identity: "src/v2_extdeps_languages_typescript.rs:177:13 E0308 mismatched types", + cause: ExposedByNewEmittedModule { module: "src/v2_extdeps_languages_typescript.rs" } + }, + NewlyExposedDiagnostic { + identity: "src/v2_extdeps_languages_typescript.rs:1597:11 E0308 mismatched types", + cause: ExposedByNewEmittedModule { module: "src/v2_extdeps_languages_typescript.rs" } + } + ], + unadmitted_regressions: [], + regression_repairs: [], + reclassified_predecessor_board: Present { value: receipt_1.board }, + producer: PhaseBoardProducer { + module_path: "tools.emission_entry_instrument", + function: "measure_entry_emission", + entry: "src/v2/compiler/00_compile.dag" + } + ) +} + +// Receipt 3 was produced by the same one-entry instrument in a local invocation over the branch +// tree at 5ee4892b70, three commits past receipt_2's subject, and it records the XL-N lane's +// arrival: THE EMITTED CLOSURE'S CARGO CENSUS IS EMPTY. cargo check --lib on the 172-file emitted +// crate reports zero error diagnostics (coded and codeless), so the board attributes every phase +// a PhaseCount of zero and furthest_phase_reached stands at Borrowck. The fifteen removals against +// receipt_2 need no disposition -- the ratchet admits a shrinking population -- and nothing was +// added, so newly_exposed is empty and no epoch machinery is in play. +// +// THE EPOCH DOES NOT CHANGE, AND THE INSTRUMENT CHANGE UNDER THIS RECEIPT IS WHY THAT CLAIM IS +// CHECKABLE. The cargo half now copies the repo's rust-toolchain.toml into the scratch crate +// before invoking cargo (tools.emission_entry_instrument measure_cargo_half), so the measured +// channel is pinned by construction rather than by host default. Every recorded comparison field +// is nevertheless IDENTICAL to receipt_2 -- cargo 1.93.0, rustc 1.93.0, the pinned triple, the +// check/dev profile, the classification policy digest -- because receipt_2's census was already +// compiled by that toolchain (the run's target-dir rustc fingerprint records 1.93.0), and the pin +// removes the host-default resolution path that a later run fell through to. comparison_inputs_same +// therefore answers true against receipt_2, and the same-epoch arm forbids a reclassified +// predecessor, so none is carried. +// +// THE THREE LANDED REPAIRS, each measured before this receipt was taken. The emitter's +// substituted-declaration provenance guard (b21d3282cf) routes a Conj/Disj type-argument node -- +// inference substitutes the resolved declaration into a data annotation's type-argument position -- +// to its own-span provenance, so BooleanAlgebra renders the structural enum +// instead of the kernel bool that the referencing scope's #9813 shadowing answers for the bare +// leaf; that cleared the python.rs:328 / typescript.rs:177 pair. The source-side carrier batch +// (285b02eed2) converts at the construction boundaries the receipt_2 census named: lex-pattern +// text goes structural at construction in lexing.dag and the three language modules, the bool +// groundings qualify their annotation, target_model returns the host carrier out of +// target_lex_rule_literal_step and converts at its two other boundaries, qualified_name.dag and +// 02_parse.dag move their two-character lookahead and emptiness checks onto host-carrier +// operations, and row_key's concat pipeline becomes nested calls so the lowering no longer emits a +// method that does not exist. The channel pin (5ee4892b70) is the instrument repair above. +// +// ONE HONESTY NOTE ON THE ARTIFACT IDENTITY. This receipt's emitted_artifact_identity differs from +// the pre-commit validation run's over the SAME bytes-by-value closure: the known import-set +// ordering class (src/v1/05_emit_rust.dag's rostered annotation -- pure `pub use` reordering, +// normalized-identical after rustfmt) makes the raw emitted bytes run-dependent, and this +// instrument digests the raw bytes. The census the ratchet compares is order-independent and +// unaffected; the identity is recorded as this run's observation, not a reproducibility claim. +data receipt_3_5ee4892b_parse_observation: RustfmtParseObservation = RustfmtParseObservation { + producer: "rustfmt --emit stdout --edition 2021", + files_observed: 171, + refused_files: [] +} + +data receipt_3_5ee4892b_raw_error_diagnostic_identities: List = [ +] + +data receipt_3_5ee4892b_phase_board_fold: PersistedPhaseBoardFold = phase_board_from_census_identities( + parse_observation: receipt_3_5ee4892b_parse_observation, + raw_identities: receipt_3_5ee4892b_raw_error_diagnostic_identities +) + +fn receipt_3_5ee4892b_receipt() -> CompilePhaseFrontierReceipt { + let receipt_2 = receipt_2_66765317_receipt() + seal_compile_phase_receipt( + sequence: 3, + observed_at: "2026-09-07T23:07:19Z" as NonEmptyStr, + subject: CompilePhaseReceiptSubject { + source_revision: "5ee4892b709bd42fc9ba6a067cecf888ed2673f1" as CommitSha, + git_tree_object: floor_discovery_tree_id(object_id: GitSha1ObjectId { digest: Sha1Digest { hex: "07eee038259e268e1a543edddb9d3f105e2d11cf" } }), + emitted_artifact_identity: sha256_digest(hex: "83d95342004c0aa5954eaf46bf9c1cf5112473de5d38fce98d33ad2f889b6e11" as NonEmptyStr), + compiler_identity: sha256_digest(hex: "a31526e509283dc2235f470bc737f0906cd83baa2fd18fa603f0d3c573249a0b" as NonEmptyStr), + assembler_identity: sha256_digest(hex: "bb7f3c7684a9288c0401e4d7d63cefcd644d0f02e80330c021b6f53c4e1009a6" as NonEmptyStr), + cargo_identity: "cargo 1.93.0 (083ac5135 2025-12-15)" as NonEmptyStr, + rustc_identity: "rustc 1.93.0 (254b59607 2026-01-19); commit 254b59607d4417e9dffbc307138ae5c86280fe4c; host x86_64-unknown-linux-gnu; LLVM 21.1.8" as NonEmptyStr, + invocation: CompilePhaseInvocation { + target: "x86_64-unknown-linux-gnu", + target_selection: TargetPinnedOnInvocation, + profile: "check/dev", + features: [] + }, + classification_policy_digest: rustc_phase_classification_policy_digest, + comparison_epoch: "rustc-1.93.0-x86_64-pinned-check-dev-rustc-error-index-1.93.0" as NonEmptyStr + }, + invocation: "local-8b8875a1-eac3-47b1-a041-aa04fa22b2dd" as NonEmptyStr, + position: PhaseBoardSuccessor { predecessor_invocation: "local-105b239b-7518-43d7-b526-a5e2ee0b610b" as NonEmptyStr }, + previous_prefix_digest: Present { value: receipt_2.prefix_digest }, + parse_evidence: ParseReached { evidence: receipt_3_5ee4892b_parse_observation }, + board: receipt_3_5ee4892b_phase_board_fold.board, + raw_error_diagnostic_identities: receipt_3_5ee4892b_raw_error_diagnostic_identities, + unplaced_identities: receipt_3_5ee4892b_phase_board_fold.unplaced_identities, + newly_exposed: [], + unadmitted_regressions: [], + regression_repairs: [], + reclassified_predecessor_board: none, + producer: PhaseBoardProducer { + module_path: "tools.emission_entry_instrument", + function: "measure_entry_emission", + entry: "src/v2/compiler/00_compile.dag" + } + ) +} + data self_host_compile_phase_frontier_receipts: List = [ genesis_5006ddc6_receipt(), - receipt_1_eced9d24_receipt() + receipt_1_eced9d24_receipt(), + receipt_2_66765317_receipt(), + receipt_3_5ee4892b_receipt() ] diff --git a/dag/gunbc/stage0_partition_rebuild_scope.dag b/dag/gunbc/stage0_partition_rebuild_scope.dag index 76b773eb95c..38c53c5d371 100644 --- a/dag/gunbc/stage0_partition_rebuild_scope.dag +++ b/dag/gunbc/stage0_partition_rebuild_scope.dag @@ -85,6 +85,7 @@ type PartitionRebuildDecision package_closure: List executable_assembly: PartitionExecutableAssembly } + | ReleaseScopeEmpty { changed_mirrors: List } | WholeCompilerRebuildRequired { cause: WholeCompilerRebuildCause } | RebuildScopeRefused { cause: RebuildScopeRefusalCause } @@ -112,6 +113,30 @@ data rebuild_scope_shared_build_input_mirrors: List = [ "v1_compiler_workspace_members.rs" ] +// THE THIRD POSITIVELY MODELED CLASS, reached like the two above by NAMING its members, never by +// failing to name something else: emitted mirrors whose bytes cannot reach the next-pass +// executable, so their precise rebuild scope is the EMPTY set -- derived, not widened to. The +// authority for membership is the emitter itself: v1.compiler.emit_rust +// emit_compiler_tests_module emits compiler_tests.rs with every item it defines behind +// #[cfg(test)], and the round's only build invocation is `cargo build --release --bin +// claim_executor` (required_regen_host RegenConvergenceStageReceipt.build_invocation), under +// which cfg(test) is off and the file contributes no item to any release unit. THE MECHANISM IS +// ONE LEVEL FINER THAN THE MOD LINE: emit_lib_rs_from_paths declares the OUTER `mod +// compiler_tests;` ungated, so rustc does read and parse the file in a release build and the +// shell crate's fingerprint recompiles it -- measured 2026-09-08, convergence stage-2 compiled +// v1-compiler with this file as the only installed surface -- while elaborating zero items from +// it, and the produced executable was byte-identical (stage input seed digest == output seed +// digest on the same receipt). So the empty scope is a claim about the EXECUTABLE, verified by +// the digest comparison the round already performs, not a claim that nothing recompiles. A +// mirror is on this roster only while that citation holds; an unowned mirror NOT named here +// still refuses MirrorHasNoOwningPackage, so the roster cannot decay into the absorbing fallback +// this module exists to refuse. The host verifies the partition half by execution: under +// ReleaseScopeEmpty every partition package is excluded, the build still runs, and a compiled +// partition package refuses the stage. +data rebuild_scope_release_excluded_mirrors: List = [ + "compiler_tests.rs" +] + // THE EXECUTABLE THE SECOND GENERATION RUNS, stated as a typed state rather than assumed. // // IT IS ASSEMBLED NOW, and the capability its old trigger named is what changed. The host shell @@ -173,7 +198,10 @@ fn rebuild_scope_extend_with_executable( // so a probe may build them and must terminate as packages-built-executable-unassembled: it may // not feed the fixed-point receipt or the second pass. Making that a typed question the host asks // -- rather than a sentence in a comment the host is trusted to have read -- is what keeps -// "we derived the scope" from being spent as "we rebuilt from it". +// "we derived the scope" from being spent as "we rebuilt from it". ReleaseScopeEmpty is +// ACTUATABLE BECAUSE THERE IS NOTHING TO COMPILE, not because there is nothing to verify: the +// host still runs the build, and the exclusion set (every package) makes any compilation a +// located refusal rather than an unnoticed widening. fn stage0_partition_rebuild_is_actuatable(decision: PartitionRebuildDecision) -> Bool { match decision { PartitionRebuildScopeDerived { changed_mirrors: _, owning_packages: _, package_closure: _, executable_assembly: assembly } => @@ -181,6 +209,7 @@ fn stage0_partition_rebuild_is_actuatable(decision: PartitionRebuildDecision) -> PartitionAssembledExecutable { package_name: _, bin_name: _ } => true ExecutableAssemblyUnavailable { trigger: _, detail: _ } => false } + ReleaseScopeEmpty { changed_mirrors: _ } => true WholeCompilerRebuildRequired { cause: _ } => false RebuildScopeRefused { cause: _ } => false } @@ -296,6 +325,10 @@ fn rebuild_scope_changed_in_roster(changed_mirrors: List, roster: List filter(m => rebuild_scope_list_has(items: roster, item: m)) } +fn rebuild_scope_changed_outside_roster(changed_mirrors: List, roster: List) -> List { + changed_mirrors |> filter(m => !rebuild_scope_list_has(items: roster, item: m)) +} + // THE DECISION. `unlocatable` is the host's list of changed paths it could not name as a compared // mirror; a non-empty list refuses before anything else is asked, exactly as // gunbc.regen_affected_set EditedSetUnlocatable does, and for the same reason. @@ -306,6 +339,13 @@ fn rebuild_scope_changed_in_roster(changed_mirrors: List, roster: List, unlocatable: List, @@ -322,8 +362,12 @@ fn stage0_partition_rebuild_decision( } else if count(changed_mirrors) == 0 { RebuildScopeRefused { cause: NoChangedMirrorsToRebuild } } else { - let generation = rebuild_scope_changed_in_roster(changed_mirrors: changed_mirrors, roster: rebuild_scope_partition_generation_mirrors) - let shared = rebuild_scope_changed_in_roster(changed_mirrors: changed_mirrors, roster: rebuild_scope_shared_build_input_mirrors) + let release_changed = rebuild_scope_changed_outside_roster(changed_mirrors: changed_mirrors, roster: rebuild_scope_release_excluded_mirrors) + if count(release_changed) == 0 { + ReleaseScopeEmpty { changed_mirrors: changed_mirrors } + } else { + let generation = rebuild_scope_changed_in_roster(changed_mirrors: release_changed, roster: rebuild_scope_partition_generation_mirrors) + let shared = rebuild_scope_changed_in_roster(changed_mirrors: release_changed, roster: rebuild_scope_shared_build_input_mirrors) if count(generation) > 0 { WholeCompilerRebuildRequired { cause: PartitionGenerationAuthorityChanged { mirrors: generation } } } else if count(shared) > 0 { @@ -338,7 +382,7 @@ fn stage0_partition_rebuild_decision( } } } else { - match rebuild_scope_resolve_owners(changed_mirrors: changed_mirrors, rows: rows) { + match rebuild_scope_resolve_owners(changed_mirrors: release_changed, rows: rows) { OwnerRefused { cause: c } => RebuildScopeRefused { cause: c } OwnerResolved { packages: owners } => { let partition_closure = rebuild_scope_reverse_closure(seeds: owners, rows: rows) @@ -372,6 +416,7 @@ fn stage0_partition_rebuild_decision( } } } + } } } @@ -433,6 +478,8 @@ fn stage0_partition_rebuild_decision_line(decision: PartitionRebuildDecision) -> "partition-rebuild: RebuildScopeRefused NoChangedMirrorsToRebuild" RebuildScopeRefused { cause: ChangedMirrorUnlocatable { mirrors: ms, reason: r } } => concat(concat("partition-rebuild: RebuildScopeRefused ChangedMirrorUnlocatable mirrors=[", rebuild_scope_join(items: ms)), concat("] reason=", r)) + ReleaseScopeEmpty { changed_mirrors: m } => + concat("partition-rebuild: ReleaseScopeEmpty changed_mirrors=[", concat(rebuild_scope_join(items: m), "] -- every changed mirror is excluded from the release build by construction; the executable is unaffected and the build runs as verification")) RebuildScopeRefused { cause: MirrorHasNoOwningPackage { mirror: m } } => concat("partition-rebuild: RebuildScopeRefused MirrorHasNoOwningPackage mirror=", m) RebuildScopeRefused { cause: MirrorHasMultipleOwningPackages { mirror: m, packages: ps } } => @@ -477,16 +524,36 @@ fn stage0_partition_rebuild_packages_today( ) -> List { match stage0_partition_rebuild_decision_today(changed_mirrors: changed_mirrors, unlocatable: unlocatable) { PartitionRebuildScopeDerived { changed_mirrors: _, owning_packages: _, package_closure: c, executable_assembly: _ } => c + ReleaseScopeEmpty { changed_mirrors: _ } => [] WholeCompilerRebuildRequired { cause: _ } => [] RebuildScopeRefused { cause: _ } => [] } } +// THE FOURTH HOST QUESTION, and the reason the empty closure below is legal: an empty package +// list from ReleaseScopeEmpty is the derived answer, while an empty list from the refusing arms +// is no answer at all. The host distinguishes them HERE -- by the variant, never by counting -- +// so the guard that refuses an empty closure under a derived scope stays load-bearing. +fn stage0_partition_rebuild_release_scope_empty_today( + changed_mirrors: List, + unlocatable: List +) -> Bool { + match stage0_partition_rebuild_decision_today(changed_mirrors: changed_mirrors, unlocatable: unlocatable) { + ReleaseScopeEmpty { changed_mirrors: _ } => true + PartitionRebuildScopeDerived { changed_mirrors: _, owning_packages: _, package_closure: _, executable_assembly: _ } => false + WholeCompilerRebuildRequired { cause: _ } => false + RebuildScopeRefused { cause: _ } => false + } +} + // THE PACKAGES THAT MUST NOT BE COMPILED. Reported to the host as identities so a build that // touches one of them is a located refusal rather than an unnoticed widening: an exclusion no // consumer can read is not evidence of anything, and this is the exclusion the whole // change-denominated claim rests on. The emit shell is a partition package, so it appears here -// whenever the changed mirrors do not reach it. +// whenever the changed mirrors do not reach it. UNDER ReleaseScopeEmpty THE CLOSURE IS EMPTY BY +// CONSTRUCTION, so this answers EVERY package: the guarantee the host enforces is that the +// verification build compiles no partition package, and an exclusion that names every package +// is what makes a single compiled crate a located refusal. fn stage0_partition_rebuild_excluded_today( changed_mirrors: List, unlocatable: List diff --git a/dag/gunbc/v1/v1_interpreter_primitive_surface.dag b/dag/gunbc/v1/v1_interpreter_primitive_surface.dag index 6aa716dd01c..2b30c9e7037 100644 --- a/dag/gunbc/v1/v1_interpreter_primitive_surface.dag +++ b/dag/gunbc/v1/v1_interpreter_primitive_surface.dag @@ -194,11 +194,7 @@ fn shadowed_spellings(rows: List) -> List String { - row.dispatch_symbol - |> concat(":") - |> concat(row.arm.identity) - |> concat(":") - |> concat(row.authored_spelling) + concat(concat(concat(concat(row.dispatch_symbol, ":"), row.arm.identity), ":"), row.authored_spelling) } fn duplicate_row_keys(rows: List) -> List { diff --git a/dag/gunbc/witness/witness_deferral_freeze.dag b/dag/gunbc/witness/witness_deferral_freeze.dag index b299f378c9e..d0475a1fde8 100644 --- a/dag/gunbc/witness/witness_deferral_freeze.dag +++ b/dag/gunbc/witness/witness_deferral_freeze.dag @@ -549,7 +549,7 @@ data frozen_path_deferrals: List = [ entry: "src/v2/test/claim/execution/long/emit_host_classical_not_ingested_equals_eval_test.dag", functions: [ "ingested_classical_not_eval_subgraph_canonical_octet_zero_holds", - "ingested_classical_not_staging_swapped_emit_refuses_underived_holds", + "ingested_classical_not_staging_swapped_emit_accepts_holds", "emit_host_classical_not_ingested_swapped_refuses_holds", "ingested_classical_not_real_infer_holds", "ingested_classical_not_match_only_param_scrutinee_infer_refuses_holds", @@ -960,7 +960,7 @@ data frozen_path_deferrals: List = [ FrozenPathDeferral { entry: "src/v2/test/claim/long/inhabitant_neutralization_e2e_witness_test.dag", functions: [ - "inhabitant_neutralization_python_to_ts_cross_language_compile_refuses_underived_holds", + "inhabitant_neutralization_python_to_ts_cross_language_compile_round_trip_holds", "inhabitant_neutralization_python_to_go_cross_language_compile_refuses_underived_holds", "inhabitant_neutralization_python_to_go_discriminates_on_real_tree", "inhabitant_neutralization_python_to_go_holds", diff --git a/dag/std/primitive_identity.dag b/dag/std/primitive_identity.dag index 57d1bce881f..e314a2629a5 100644 --- a/dag/std/primitive_identity.dag +++ b/dag/std/primitive_identity.dag @@ -875,13 +875,15 @@ fn primitive_definition_identity(definition: PrimitiveDefinition) -> PrimitiveId definition.identity } +// The filter is let-bound, not written in the branch condition: a filter in a condition is an +// unprojectable construct (the FilterInBranchCondition wall), so the condition reads the bound +// name. fn primitive_duplicate_semantic_definition_violation( definitions: List, target: PrimitiveIdentity ) -> PrimitiveJoinViolation? { - if count( - definitions |> filter(fn(d) { primitive_identity_eq(left: d.identity, right: target) }) - ) > 1 { + let matches = definitions |> filter(fn(d) { primitive_identity_eq(left: d.identity, right: target) }) + if count(matches) > 1 { Present { value: DuplicateSemanticDefinition { identity: target } } } else { none diff --git a/dag/test/claim/self_host_compile_phase_frontier_witness_test.dag b/dag/test/claim/self_host_compile_phase_frontier_witness_test.dag index 56ffed9e2d3..da4ff46b325 100644 --- a/dag/test/claim/self_host_compile_phase_frontier_witness_test.dag +++ b/dag/test/claim/self_host_compile_phase_frontier_witness_test.dag @@ -441,15 +441,21 @@ test fn the_persisted_genesis_census_is_fully_attributed_to_its_phases() -> Bool } // The end-to-end claim, asserted on the exact string the ledger projection publishes: the standing -// the document renders may not say the emitted compiler clears typeck or borrowck while the latest -// validated receipt's census says otherwise. -test fn the_published_frontier_standing_does_not_claim_typeck_or_borrowck_passed() -> Bool { +// the document renders must agree with the latest validated receipt's census. This row was born as +// the frontier-state pin `the_published_frontier_standing_does_not_claim_typeck_or_borrowck_passed`, +// asserting the red state while the census carried typeck errors. Receipt 3's census is EMPTY -- +// the emitted closure's cargo check reports zero error diagnostics, furthest phase borrowck -- so +// per DESIGN 4b(4) the guard flips to its permanent regression control: the rendered standing must +// now claim typeck and borrowck passed, and any future receipt whose census re-enters either phase +// renders "N remaining" here and reds this row. +test fn the_published_frontier_standing_claims_typeck_and_borrowck_passed() -> Bool { match current_compile_phase_frontier_standing() { CompilePhaseFrontierValidated { latest: latest } => { let rendered = render_phase_board(board: latest.board) - !string_contains(s: rendered, pattern: "typeck PASSES") - && !string_contains(s: rendered, pattern: "borrowck PASSES") - && string_contains(s: rendered, pattern: "borrowck not yet reached") + string_contains(s: rendered, pattern: "typeck PASSES") + && string_contains(s: rendered, pattern: "borrowck PASSES") + && !string_contains(s: rendered, pattern: "not yet reached") + && !string_contains(s: rendered, pattern: "remaining") } standing => false } diff --git a/dag/test/claim/self_host_emitted_call_target_realization_witness_test.dag b/dag/test/claim/self_host_emitted_call_target_realization_witness_test.dag index 3528743b7a3..34858f9b93a 100644 --- a/dag/test/claim/self_host_emitted_call_target_realization_witness_test.dag +++ b/dag/test/claim/self_host_emitted_call_target_realization_witness_test.dag @@ -64,6 +64,185 @@ test fn w_host_map_record_shape_refuses_instead_of_constructing_a_second_carrier ) } +// THE OPEN-SUPPORT CARRIER CONSTRUCTS STRUCTURALLY, THE DISCRIMINATING RED for the fossil-row +// class. PointwisePower is the power set presented as a characteristic function (std.algebra, +// OpenSupport) — no finite BTreeSet can realize an open membership oracle — but all four target +// inhabitant rosters carried a pre-split "PointwisePower" -> finite-set row, and the construction +// arm consulted it while the type renderer (keyed on declaration provenance) did not: the type +// rendered structurally and the literal emitted the record-shaped-carrier refusal panic. Measured +// on the emitted 00_compile closure's first native run (2026-09-07): twelve such panic sites, hit +// through dag_language_model().canonical_symbols. The rows are removed; the literal now takes the +// same structural path the type renderer already took (Rc'd closure field, phantom filled — the +// bool_boolean_algebra pattern). The finite-set exclusion is spelled `BTreeSet Bool { + compile_dag_rust_emit_check( + "module test.claim.pointwise_power_record_shape_probe\nimport std.algebra { PointwisePower }\nimport std.types { Int }\nfn probe(only: Int) -> PointwisePower { PointwisePower { member: fn(n) { n == only } } }\n", + "src/test_claim_pointwise_power_record_shape_probe.rs", + ["PointwisePower {", "member:"], + ["record-shaped carrier has no realization", "BTreeSet, not BTreeSet.) +test fn w_finite_power_set_record_shape_still_refuses_while_its_row_stands() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.finite_power_set_record_shape_probe\nimport std.algebra { FinitePowerSet }\nimport std.types { Int }\nfn probe(s: FinitePowerSet) -> FinitePowerSet { FinitePowerSet { member: fn(n) { s.member(n) }, union: fn(other) { s }, intersect: fn(other) { s }, diff: fn(other) { s }, contains: fn(n) { s.contains(n) }, count: fn() { s.count() }, length: fn() { s.length() } } }\n", + "src/test_claim_finite_power_set_record_shape_probe.rs", + ["record-shaped carrier has no realization"], + ["FinitePowerSet {"] + ) +} + +// FIELDLESS-RECORD DATA UNDER THE JSON PATH, THE DISCRIMINATING RED for the second native-parity +// divergence class. A data definition of record type with no cross-refs is emitted as a +// serde_json round-trip (emit_data_value_json), and the value side spelled EVERY record as a +// JSON map -- including the zero-field record, whose emitted Rust type is a unit struct +// (emit_struct_from_children, `pub struct Marker;`). serde's unit-struct Deserialize reads +// `null` and rejects `{}`, so the emitted compiler panicked at first touch of the data: +// "invalid type: map, expected unit struct BoolEncodingFact", measured 2026-09-08 on the native +// run of the emitted 00_compile closure (v2.std.logic bool_primitive_facts). The value side now +// spells the zero-field record `null`; this row reds if the map spelling returns. +test fn w_fieldless_record_data_value_spells_null_for_the_unit_struct() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.fieldless_record_json_probe\ntype Marker {}\ntype Holder {\n marker: Marker\n}\ndata probe: Holder = Holder { marker: Marker {} }\n", + "src/test_claim_fieldless_record_json_probe.rs", + ["pub struct Marker;", "\"marker\": null"], + ["\"marker\": {}"] + ) +} + +// THE BOUNDARY CONTROL. The null spelling is the zero-field case ONLY: a record with fields +// keeps the map spelling its braced struct deserializes from. A repair that emitted `null` for +// every record literal greens the row above and reds here. +test fn w_nonempty_record_data_value_keeps_the_map_spelling() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.nonempty_record_json_probe\nimport std.types { String }\ntype Holder {\n name: String\n}\ndata probe: Holder = Holder { name: \"kept\" }\n", + "src/test_claim_nonempty_record_json_probe.rs", + ["\"name\": \"kept\""], + ["\"name\": null"] + ) +} + + +// THE VARIANT BOUNDARY OF THE JSON DATA PATH, SPELLED. A record literal with parent_enum PRESENT +// is a variant construction, and its wire spelling is the parent coproduct's declared +// VariantEncoding policy -- measured against serde 2026-09-08: the internal-tag default accepts +// only the tag map (`{"_variant": "A"}`), while `null` and the bare string are both rejected. +// This row stood as a fail-closed REFUSAL for one day, because the policy is a module-local fact +// of the parent's home module and the JSON value path carried no cross-module view of it; it +// greened the day its named trigger landed -- the closure-wide wire-spelling index +// (EmitGraphInfo.data_variant_wire_spellings, built beside type_decl_items by +// build_data_variant_wire_spellings), which this pair of rows now pins. The probe forces the JSON +// path (the Holder record nests Marker) and constructs the fieldless variant with braces; the +// bare spelling never reaches the arm (an ExprVar routes the whole value to direct construction +// via data_value_has_cross_refs). It reds if the tag map is lost, and if the former +// mis-serializations (null, or the compile_error! refusal) return. +test fn w_variant_record_lit_on_the_json_data_path_spells_the_internal_tag() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.variant_json_spelling_probe\ntype E = A | B { x: Int }\ntype Marker {}\ntype Holder {\n e: E\n m: Marker\n}\ndata probe: Holder = Holder { e: A {}, m: Marker {} }\n", + "src/test_claim_variant_json_spelling_probe.rs", + ["{\"e\": {\"_variant\": \"A\"}, \"m\": null}"], + ["compile_error!", "\"e\": null"] + ) +} + +// THE FIELD-CARRYING HALF OF THE SAME BOUNDARY. The internal-tag default spells a record variant +// as the tag map with the payload fields INLINE (`{"_variant": "B", "x": 3}`), not the bare field +// map (`{"x": 3}`) the pre-index arm produced -- measured against serde 2026-09-08: the untagged +// field map is rejected ("missing field `_variant`"). This is the shape the five real closure +// sites take (SurfaceAtomKey { atom } in v2.std.compilers.sugar, CopiedPortCitationRetained { +// combiner, migration_trigger } in v2.lens.cost.copied_port_citations), so this row is the +// discriminating control for the population the index actually serves. It reds if the tag or the +// inline fields are dropped, and if the compile_error! refusal returns. +test fn w_fielded_variant_record_lit_on_the_json_data_path_spells_tag_and_fields() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.variant_json_fielded_probe\ntype E = A | B { x: Int }\ntype Marker {}\ntype Holder {\n e: E\n m: Marker\n}\ndata probe: Holder = Holder { e: B { x: 3 }, m: Marker {} }\n", + "src/test_claim_variant_json_fielded_probe.rs", + ["{\"e\": {\"_variant\": \"B\", \"x\": 3}, \"m\": null}"], + ["compile_error!", "\"e\": {\"x\": 3}"] + ) +} + +// LOCALITY WINS OVER A FOREIGN AMBIGUITY, the third native-parity divergence class. The leaf +// index is global: a leaf two closure modules declare answers LeafAmbiguous, and the alias-RHS +// module resolver used to end at the poison marker for EVERY such reference -- including a +// reference inside one of the declaring modules itself, where source resolution binds the local +// declaration before any cross-module lookup. Measured on the native 00_compile closure build as +// twelve E0433 sites in v2_std_integer.rs, whose own `type Compose` co-resides +// with std.machine_constraints' `Compose` alias. The probe reproduces that shape exactly: a local +// parametric opaque Compose, a foreign Compose pulled in by the MachineWidth import, and an alias +// whose RHS applies the local one. It reds if the poison marker returns, and if the local +// declaration stops being the resolution. +test fn w_alias_rhs_leaf_declared_locally_shadows_foreign_declarer() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.ambiguous_leaf_locality_probe\nimport std.machine_constraints { MachineWidth }\ntype Compose\ntype Word = Compose, MachineWidth<8>>\n", + "src/test_claim_ambiguous_leaf_locality_probe.rs", + ["pub type Word = Compose<"], + ["__ambiguous_leaf__Compose"] + ) +} + +// THE QUALIFIER IS THE DISAMBIGUATOR, the fourth native-parity divergence class. A qualified type +// reference names its provider in its own spelling, so a global leaf ambiguity never attaches to +// it -- but the qualified use-line route asked the leaf index, saw LeafAmbiguous, and declined to +// synthesize any line, leaving the bare leaf unimported in the emitted file. Measured on the +// native 00_compile closure build as eight E0425/E0433 `Nat` sites in v2_lens_fact_density.rs, +// which references v2.std.nat.Nat qualified while the std.nat alias co-resides in the closure. +// +// HOW THE DOTTED SPELLING REACHES THE ROUTE constrained the probe shape, because a signature +// annotation alone does NOT produce it: the resolver binds `n: v2.std.nat.Nat` to the declaration +// node and the surface walk collects the bare leaf (measured: a signature-only probe delivered no +// dotted name). The dotted spelling enters through the VALUE surface -- a qualified value +// projection (`v2.std.nat.Zero` as match scrutinee) takes its inferred type from +// qualify_borrowed_type_names, whose borrowed view carries the qualified name, and +// annotate_pattern_parent_enums stamps that spelling as the patterns' parent_enum, which +// collect_pattern_ref_names emits. That is the fact_density chain in miniature. The record-lit +// head contributes the dotted VARIANT `v2.std.nat.Succ` to the same route, so the exclude half +// pins the E0603 boundary: the variant must still be declined (it is not a type the qualifier +// declares), or the route re-opens the sixteen-site false-import class its comment records. +// The pool admits a module only through an import edge, so +// test.fixture.nat_qualified_provider holds the v2.std.nat edge while std.nat is imported +// directly; both Nat declarers co-reside and the leaf is globally ambiguous, exactly the closure +// condition the fix answers. It reds if the qualifier stops resolving the ambiguity, and if the +// variant leaf starts being imported. +test fn w_qualified_type_reference_qualifier_disambiguates_the_leaf() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.qualified_leaf_disambiguation_probe\nimport test.fixture.nat_qualified_provider { nat_qualified_provider_touch }\nimport std.nat { nat_max }\nfn probe() -> v2.std.nat.Nat {\n match v2.std.nat.Zero {\n v2.std.nat.Zero => v2.std.nat.Zero\n v2.std.nat.Succ { prev: p } => v2.std.nat.Succ { prev: p }\n }\n}\n", + "src/test_claim_qualified_leaf_disambiguation_probe.rs", + ["pub use crate::v2_std_nat::Nat;", "pub fn probe() -> Rc"], + ["pub use crate::v2_std_nat::Succ;"] + ) +} + +// THE SECOND HOP OF CLONE-BOUND FORWARDING, the fifth native-parity divergence class. The +// call-forwarding derivation re-derived each callee's bound from the callee's SELF-derived half +// only, so a callee whose bound is itself forwarded re-derived to empty and the wrapper emitted +// bare -- declared one-hop until the specimen arrived: std.realization_measurement +// witness_measured_rollup_differs_from_predicted_zero through cost_account_time_count (itself +// forwarded from cost_time_axis_established's match-scrutinee clone), E0277 on the native +// 00_compile closure build. The probe is that chain in miniature: inner earns the bound by +// matching its Rc'd coproduct by value, middle forwards it one hop, outer needs two. The +// derivation is now transitive over the call graph (visited-set termination, the module's existing +// pattern); this row reds if the second hop is lost, and also pins the two boundary controls -- +// middle's one-hop bound must stand and wrap, which constructs rather than clones, must stay bare +// (a spurious bound tightens a signature and breaks its callers). +test fn w_clone_bound_forwards_transitively_over_two_call_hops() -> Bool { + compile_dag_rust_emit_check( + "module test.claim.clone_bound_two_hop_probe\ntype Measure { count: M }\ntype CostTimeAxis\n = CostTimeMeasured { value: Measure }\n | CostTimeUnestablished\n\ntype CostAccount {\n time: CostTimeAxis\n}\nfn inner(axis: CostTimeAxis) -> Bool {\n match axis {\n CostTimeMeasured { value: _ } => true\n CostTimeUnestablished => false\n }\n}\nfn middle(account: CostAccount) -> Bool {\n inner(axis: account.time)\n}\nfn wrap(m: Measure) -> CostAccount {\n CostAccount { time: CostTimeMeasured { value: m } }\n}\nfn outer(wall_time: Measure) -> Bool {\n middle(account: wrap(m: wall_time))\n}\n", + "src/test_claim_clone_bound_two_hop_probe.rs", + ["pub fn inner", "pub fn middle", "pub fn outer", "pub fn wrap("], + ["pub fn outer"] + ) +} + // THE PROBE IMPORTS v2.std.collection AND THEN CALLS IT BY ITS QUALIFIED NAME, which looks // redundant and is not. compile_dag_rust_emit_check compiles through the witness harness's // restricted pool (2973 modules against the corpus's 4261), and a qualified spelling alone does not diff --git a/dag/test/fixture/nat_qualified_provider.dag b/dag/test/fixture/nat_qualified_provider.dag new file mode 100644 index 00000000000..0e0d79a9c7f --- /dev/null +++ b/dag/test/fixture/nat_qualified_provider.dag @@ -0,0 +1,17 @@ +module test.fixture.nat_qualified_provider + +import v2.std.nat { Nat, nat_add } + +// Pool-edge fixture for +// test.claim.self_host_emitted_call_target_realization_witness_test's +// w_qualified_type_reference_qualifier_disambiguates_the_leaf. The mechanism under test is the +// emitter's qualified-type use-line route, which only sees a reference when the resolver leaves it +// AUTHORED -- and the resolver leaves v2.std.nat.Nat authored only in a module that does NOT import +// v2.std.nat (the v2.lens.fact_density shape measured on the 00_compile closure). The witness pool +// admits a module to the closure only through an import edge, so this fixture holds the edge: the +// probe imports this module, v2.std.nat enters the closure transitively, and the probe's own +// qualified reference stays authored. The bare import-and-reference below keeps the fixture's own +// emission clean through the authored-import route rather than the route under test. +fn nat_qualified_provider_touch(a: Nat) -> Nat { + nat_add(a: a, b: a) +} diff --git a/docs/design-rung-drops.md b/docs/design-rung-drops.md index 027e395a806..0ac1be8485a 100644 --- a/docs/design-rung-drops.md +++ b/docs/design-rung-drops.md @@ -70,15 +70,15 @@ CI — **RUNG DROP, DECLARED (2026-08-15, the floor cut).** WHAT WAS HERE: one c ### Whole-corpus compile-phase frontier receipts — latest validated row -Milestones: parse PASSES / expand PASSES / resolve PASSES / typeck 155 remaining / borrowck not yet reached +Milestones: parse PASSES / expand PASSES / resolve PASSES / typeck PASSES / borrowck PASSES -Tree: `sha1:d424154bb6fb8c40b149be07f2ddd8d61e89108e` +Tree: `sha1:07eee038259e268e1a543edddb9d3f105e2d11cf` -Invocation: `buildbuddy-44d90e35-ccba-41d1-9a90-928a914b26a2` +Invocation: `local-8b8875a1-eac3-47b1-a041-aa04fa22b2dd` -Census identity digest (self-host-compile-phase-census-canonical-identity-set-v1): `049d0a54eea70534` +Census identity digest (self-host-compile-phase-census-canonical-identity-set-v1): `4325dac985bbe897` -Error identities: 155 total = 155 coded + 0 codeless +Error identities: 0 total = 0 coded + 0 codeless Producer: `tools.emission_entry_instrument::measure_entry_emission` diff --git a/docs/plans/v2-self-host-direct-path-2026-09-06.md b/docs/plans/v2-self-host-direct-path-2026-09-06.md new file mode 100644 index 00000000000..ef0416f5ee0 --- /dev/null +++ b/docs/plans/v2-self-host-direct-path-2026-09-06.md @@ -0,0 +1,67 @@ +# The direct path to a clean v2 self-host — orientation, sequencing, autonomy (2026-09-06) + +**What this file is.** An orientation and sequencing read, written at one point in time, plus the autonomy contract under which an agent executes it. It is **not** a work-item roster and not a second authority: every fact about what remains lives in the carriers named below and is re-derived by running the instruments named below, never by reading a number off this page (DESIGN §6 — name the instrument, never transcribe its output). When this file and a carrier disagree, the carrier is right and this file is stale. + +**The authorities this file defers to:** + +- DESIGN §7 — the goal itself, and the honesty boundary around it. +- [v2-self-hosting.md](v2-self-hosting.md) — the operator-signed (2026-07-11) Weak → Strong Self Host program: four waves, dependency-gated exits, `src/v1` deleted only at the end of Wave 4. That is the plan from here to there; this file does not replace it. +- `dag/gunbc/roadmap/roadmap_authority.dag` — the node chain with per-node boundary, first slice, RED control, exclusions, handback. Projected to `ROADMAP.md`; every node requires explicit manual acceptance. +- The three self-host frontier carriers: `v2.compiler.self_host.seed_retention_frontier`, `v2.compiler.self_host.emit_coverage_frontier`, `v2.compiler.self_host.native_routing_frontier`. +- `dag/gunbc/guarantee_stall/` — the typed stall roster; `self_host_candidate_generation_add_slice_stall` is the grounding-frontier row. +- gunbc#9664 ("XL-N") — the native-closure burn-down program with go/no-go gates. + +## 1. What you actually get at the end + +In daily terms: **you edit `.dag` files, and the compiler that compiles them was itself compiled from `.dag` files.** The hand-written Rust seed is gone except a pinned, content-addressed bootstrap kernel (the wave doc sizes it at roughly 8–15k LOC); everything else the compiler does — reading source, resolving names, deriving types, translating, emitting — is emitted from the graph it compiles. + +The proof shape, per the roadmap nodes' own RED controls: + +- The generator builds itself **twice**; the second build is shown never to have reached the old compiler — run with the old one taken away, or with a receipt proving it was never called. Behaving the same is not accepted as proof of independence. +- Every self-emitted module passes the same behavioral corpus as the seed it replaced, including the deliberately-broken controls that must still fail. Byte-identity with the seed is explicitly **not** the goal (operator, 2026-07-08) — the seed's warts are not reproduced. +- Required CI runs `v2.test.*` through the native emitted crate; the interpreter survives only for `src/v1` regen until that too is gone. + +The payoff is DESIGN §7's: **language design opens up.** A new guarantee is a row, not a compiler fork; and because the substrate reads one grammar in both directions over many media, that row applies on top of an existing language through ingest — no adoption problem. This is the economic point of the whole exercise: today every improvement routes through the generator we are trying to delete. + +What you do **not** get (§7's honesty boundary): the compiler does not invent unstated intent, does not prove unmodeled external reality, and does not lift arbitrary predicates to proof. Runtime mechanisms — typed refusal, totality, rollback, budgets — remain real at honest boundaries. + +## 2. Where "here" is — by instrument + +Each line names the producer that re-derives the state. Run it; do not believe this sentence. + +- **Axis A — the real path, slice by slice.** The add slice (the `add` fn of `std.integer`) is green end-to-end on the dag path as of #10673: infer derives grounding by declared-inhabitant roster membership, and the ingest→compile round trip is byte-faithful. Instrument: `gunbc run --source-root dag --source-root src/v2 --entry src/v2/test/claim/execution/self_host_candidate_generation_stage_verdicts_test.dag --function add_slice_stage_verdicts_entry`. The **direct-rust-door** (roadmap `v2-emitter-direct-rust-door`) is **green by execution as of 2026-09-06**: the production walk reaches `ArtifactProduced`, all five leaf projections establish (source fidelity byte-equal to `direct_rust_door_expected_source`, producer identity, seed-emitter absence, `SourceProduced` qualification, missing-rule refusal), and the known-red admission row deleted per its own dissolution, promoting the group root to the lane's permanent regression wall. Instrument: `claim_batch --source-root dag --source-root src/v2 --entry src/v2/test/claim/long/direct_rust_door_production_group_test.dag --functions direct_rust_door_production_group_closing_expectation_holds` (a `long/` row — it reconstructs the pipeline per evaluation; the cost defect is declared in the test module's header). +- **The grounding frontier.** The door specimen's frontier is closed, and the parse-product family followed on 2026-09-07: the declared-inhabitant roster-membership derivation widened from the dag roster to the closed ingest set (dag, python, typescript), closing the python and typescript fixtures' frontiers and greening the four same-language round-trips. The add-slice stall's trigger fired and it retired per §4b(4). Six derivation rules now cover declared-inhabitants roster membership (language-general), binding denotation, Conj/Arrow product introduction, canonical-operations and grammar-productions roster membership, and binding references from the enclosing arrow's domain. The remaining frontier is the six node kinds with no derivation rule yet (the `node_grounding_frontier_note` in `04_infer.dag` is the standing carrier). +- **Axis B — the native closure (XL-N).** The XL-0 gate (`cargo check` = 0 on the emitted compiler closure) is **not satisfied**; the issue's own receipt table records that the closure reached complete rustc type checking for the first time at XL-0A and carries the honest error population there. gunbc#10266 (fabricated runtime panics on representation-identical casts) is filed as an independent compiler slice on this path. Instrument (the issue's own): `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag --target rust --output-dir /tmp/v2emit && cd /tmp/v2emit && cargo check --message-format short`. +- **Axis C — the seed dissolves.** `v2.compiler.self_host.seed_retention_frontier` holds every retained seed file as a declared row; `undeclared_reason_rows()` is the prioritizable debt, and the census refuses unrostered retention in three directions. `v2.compiler.self_host.emit_coverage_frontier`'s `interpreter_retained_rows()` and `v2.compiler.self_host.native_routing_frontier`'s roster cover the witness-execution axes. The first InterpreterRetained → SelfEmittedNative promotion after classical_not landed 2026-09-07: the add family's row flipped when `emit_host_native_only_add_holds` (+ its wrong-octet broken control) landed in the file-grain-enrolled native-only verdict entry — the emitted add crate's stdout pinned to its expected octet with `eval()` never called, program-side discrimination staying with the family's equals_eval primitive-five/six pair. **The emit coverage frontier closed 2026-09-08: all fifteen roster rows are SelfEmittedNative, `interpreter_retained_rows()` is empty**, each row backed by a native-only verdict arm (positive + wrong-octet broken control) in the same enrolled entry, and the `retained_via_eval_agreement` row constructor was deleted with the last flip (DESIGN §3c). This closes axis (a) — witness-body-runs-native — only; it says nothing about which engine compiled the compiler itself (axis (b), the regen-grain flip, remains operator-gated below). Instrument: `claim_batch --source-root dag --source-root src/v2 --entry src/v2/test/claim/execution/long/emit_host_native_only_verdict_test.dag` (offline wet legs — real cargo build and native run per leg). +- **Wave position.** Per the wave doc, Wave 1's four exit items all carry landed receipts; Wave 2 (first real flips — emit-surface tracks self-emit, behaviorally verify, and **replace** their v1 counterparts) is the open wave. The wave doc's header records that its roster carrier was deleted and per-module dispositions must be re-derived from the emitter — which is what the door-first sequence below does. + +## 3. The sequence from here + +Ordered by the roadmap chain; each step names its green condition as the node's own contract states it. + +1. **The door** (`v2-emitter-direct-rust-door`) — **LANDED 2026-09-06.** The loop's actual findings, for the record: not missing `TargetModel` rows. (a) The grounding frontier closed by six real derivation rules in `04_infer.dag` (see §2's frontier line). (b) Emission needed the right composition, not more rules: a resolved module is a module shell over named declarations, so the production path is `generate_rust_module_emission_candidate` — collect the produced-decl-shaped nodes, admit exactly one, emit through `v2.compiler.emit_produced` — while the raw whole-tree `generate_rust_emission_candidate` remains the fixture lane's composition with its own dissolution trigger. (c) One representation decode gap: resolution canonicalizes surface operators into canonical-operation wire nodes, so the translate value-expression projection decodes the operator position with `canonical_operation_from_wire_node` first and falls to the surface-token table only on a wire miss. Green condition met: the production observation reaches `ArtifactProduced`, all five leaf projections establish, the known-red expectation row deleted in the same change, and the group root promotes to permanent regression evidence (`v2_emitter_direct_rust_door_contract_dissolution_note`). +2. **The parse-product grounding family** (the stall's named trigger) — **LANDED 2026-09-07.** The roster-membership derivation generalized from the dag language roster to the closed ingest set (dag, python, typescript): `infer_node_declared_in_language_inhabitants` returns the declaring authority's roster root as evidence, with deep subtree membership so a declared inhabitant's leaf fact atoms derive exactly as the inhabitant node itself does. Measured: the python fixture's 19-node frontier and the typescript fixture's 28-node frontier both close to zero; all four stall-population round-trip witnesses green; the python→typescript cross-language compile accepts, byte-identical to `ts_source_text`. One §4b(4) flip: `cross_language_compile_refuses_canonical_underived_holds` → `cross_language_compile_python_to_typescript_round_trip_holds` (the pipeline-level frontier guard became the permanent regression control for the acceptance). The add-slice stall's trigger fired, so it retired per §4b(4) — removed from `all_guarantee_stalls`, row file deleted, witnesses stay enrolled. +3. **First behavioral module** (`v2-emitter-first-behavioral-module`). One already-tested module regenerated by the new generator; its existing tests — including the deliberately-broken one — pass unchanged, and the run is shown never to have reached the old generator. First-slice evidence landed 2026-09-07 at the coverage-frontier grain: the add family (fewest dependencies — integer literals plus one canonical operation) carries a native-only verdict witness, so its behavior is established by the emitted crate's own stdout with `eval()` unreachable, and its coverage row reads SelfEmittedNative. What remains for the node is the regen-grain flip — a corpus module's *production* switching generators — plus operator acceptance. +4. **The XL-N milestones, in the issue's order** — arms → 0; emitter defects C, B → 0; crate compiles; native = interpreted with every divergence typed; swap. This is Wave 4's "pipeline runs on emitted Rust" executed as a burn-down, and its rules bind: fixes land in `.dag` and reach Rust via regen, and each closed class gets a `test.claim` fixture under the required gate. Its no-go gates are real: M2 stops if a fix needs a fact the inferred tree doesn't carry (an `04_infer` modeling gap — file it, don't grind); M3 re-plans if a class needs a language capability we don't have. +5. **Native bootstrap, then fixed point** (`v2-emitter-native-bootstrap`, `v1-emitter-fixed-point`). The two-round self-build for the generator, then the same two-round test extended stage by stage to the rest of the compiler. Then `dag/gunbc/v1/v1_deletion_plan.dag` executes; the seed-retention roster drains as modules flip, and Wave 3's first obligation — a derived denominator for what the compiler consists of — is named in the wave doc. +6. **Parallel track, not a wave gate:** the import-deletion ladder (wave doc §3) — B3's migration alongside Waves 2–3, B4's grammar deletion at Wave 4. + +## 4. The autonomy contract + +An agent can execute, end to end and without supervision, the per-slice loop: run the instrument → diagnose from the typed diagnostics → land the `.dag` fix → flip each affected witness per §4b(4) with per-witness justification → narrow the roster/stall rows in the same change → commit, push, open the PR, and iterate to green CI. #10673 is the worked example of that loop. Declaring seed-retention reasons, landing derivation rules, and burning XL-N error classes are all inside it. + +The operator gates that remain, each with its repo citation: + +- **Merges.** The agent never merges; every slice pauses at a green PR for human review. +- **Roadmap node acceptance.** ROADMAP.md's header states every active row requires explicit manual acceptance; a green node is handed back, not self-accepted. +- **CI job roster growth.** Closed without operator sign-off (DESIGN *Building & checks*, 2026-09-04 ruling). XL-N's M5 swap changes what required CI runs on — it is an operator decision by construction. +- **Scaffold admission.** §5: approval is external to the diff; an author cannot self-approve a scaffold. +- **Declared rung drops.** §4b(3): the agent can author the full declaration (previous rung, temporary rung, reason, population, restoration trigger); landing it is review-gated. +- **Language-semantics rulings** that the tree cannot decide. The Atom-authority question is the template: surfaced de-jargoned, decided by the operator (2026-09-06: the namespacing rules answer it), recorded in the carrier. These are rare but real, and the agent's obligation is to stop and raise them rather than guess. + +## 5. What would make this plan wrong + +- An XL-N no-go gate firing (M2's modeling gap, M3's missing language capability) — re-plan at that gate, per the issue's own rule. +- ~~The door's emission diagnostics turning out to be an `04_infer` modeling gap rather than `TargetModel` rows~~ — discharged 2026-09-06: the door is green; the gaps were derivation rules, one emission composition, and one operator-representation decode, none of them a modeling wall. +- A Wave-2 flip surfacing behavioral divergence that re-sequences the module order — typed as emitter bug or interpreter bug either way, but the sequence absorbs it. +- This file aging. It is a position read, not a carrier; the instruments in §2 are the standing truth. diff --git a/src/v1/04_emit_info.dag b/src/v1/04_emit_info.dag index 0180e253d74..0e2e98c61ce 100644 --- a/src/v1/04_emit_info.dag +++ b/src/v1/04_emit_info.dag @@ -108,9 +108,27 @@ fn collect_type_node_import_surface_names(n: Node, source_indices: Map type_decl_items: Map + data_variant_wire_spellings: Map fn_decl_items: Map recursive_type_set: Set fielded_variants: Set @@ -150,6 +168,7 @@ fn empty_emit_graph_info() -> EmitGraphInfo { item_leaf_owner_modules: empty_map(), type_summaries: empty_map(), type_decl_items: empty_map(), + data_variant_wire_spellings: empty_map(), fn_decl_items: empty_map(), recursive_type_set: empty_set(), fielded_variants: empty_set(), @@ -177,6 +196,7 @@ fn emit_info_with_fn_type_context(emit_info: EmitGraphInfo, generic_param_names: item_leaf_owner_modules: emit_info.item_leaf_owner_modules, type_summaries: emit_info.type_summaries, type_decl_items: emit_info.type_decl_items, + data_variant_wire_spellings: emit_info.data_variant_wire_spellings, fn_decl_items: emit_info.fn_decl_items, recursive_type_set: emit_info.recursive_type_set, fielded_variants: emit_info.fielded_variants, @@ -203,6 +223,7 @@ fn emit_info_with_fn_return(emit_info: EmitGraphInfo, fn_return_type: Node?) -> item_leaf_owner_modules: emit_info.item_leaf_owner_modules, type_summaries: emit_info.type_summaries, type_decl_items: emit_info.type_decl_items, + data_variant_wire_spellings: emit_info.data_variant_wire_spellings, fn_decl_items: emit_info.fn_decl_items, recursive_type_set: emit_info.recursive_type_set, fielded_variants: emit_info.fielded_variants, @@ -234,6 +255,7 @@ fn emit_info_with_expected_type(emit_info: EmitGraphInfo, expected_type: Node?) item_leaf_owner_modules: emit_info.item_leaf_owner_modules, type_summaries: emit_info.type_summaries, type_decl_items: emit_info.type_decl_items, + data_variant_wire_spellings: emit_info.data_variant_wire_spellings, fn_decl_items: emit_info.fn_decl_items, recursive_type_set: emit_info.recursive_type_set, fielded_variants: emit_info.fielded_variants, diff --git a/src/v1/04_infer.dag b/src/v1/04_infer.dag index e78846c5e20..3abca2650f2 100644 --- a/src/v1/04_infer.dag +++ b/src/v1/04_infer.dag @@ -14006,6 +14006,7 @@ fn build_emit_graph_info(modules: List, registry: Map) -> EmitterOutcome { +// The JSON spelling of a data value must deserialize into the RUST TYPE the same declaration +// emitted, so the two sides are one agreement, not two choices. The case that forces the issue +// is the fieldless record: emit_struct_from_children renders it as a unit struct (`pub struct +// Name;`), and serde's derived unit-struct Deserialize reads `null` -- an empty map `{}` is a +// different, rejected shape ("invalid type: map, expected unit struct", measured 2026-09-08 as +// the second native-parity divergence, on v2.std.logic bool_primitive_facts). The record arm +// therefore spells the zero-field value `null`; non-empty records keep the map spelling their +// braced struct expects. +// +// THE VARIANT ARM READS THE CLOSURE-WIDE WIRE-SPELLING INDEX. A record literal with parent_enum +// PRESENT is a variant construction, and its wire spelling is the parent coproduct's declared +// VariantEncoding policy: the internal-tag default spells the tag map (`{"_variant": "A"}`, +// measured against serde 2026-09-08 -- `null` and the bare string are both rejected, and the +// field-carrying map without the tag is rejected), while StringVariant, UntaggedVariant and +// TaggedVariant each spell differently. That policy is a module-local fact of the parent's home +// module (plus its `_contracts` sibling), which this value path cannot see -- so the spelling is +// derived ONCE per (coproduct, variant) at the emission roots +// (build_data_variant_wire_spellings, beside EmitGraphInfo.type_decl_items) and carried in +// EmitGraphInfo.data_variant_wire_spellings, keyed `Parent.variant`. This arm is a lookup, never +// a re-derivation: an unindexed key or a stored DataVariantSpellingRefused fails closed (the +// caller renders compile_error!, a build-time located refusal) rather than spelling a guess. +// The bare fieldless variant never reaches here at all: it is an ExprVar, and +// data_value_has_cross_refs routes any ExprVar-bearing value to direct construction. +fn emit_data_fields_json(value: Node, source_indices: Map, variant_wire: Map) -> JsonFragmentsAccum { + value.children |> fold(init: FragmentsAccumulated { pieces: [] }, f: (acc, fld) => + accumulate_json_field( + acc: acc, + name: field_init_node_name_at(n: fld, source_indices: source_indices), + outcome: emit_data_value_json(value: field_init_node_value(n: fld), source_indices: source_indices, variant_wire: variant_wire) + ) + ) +} + +fn emit_data_value_json(value: Node, source_indices: Map, variant_wire: Map) -> EmitterOutcome { match value.expr_data { ExprLiteral { value: v } => match v { LitStr { value: s } => Emitted { json: concat("\"", escape_json_string(s: s), "\"") } @@ -465,28 +500,60 @@ fn emit_data_value_json(value: Node, source_indices: Map) } ExprListLit => { let accum = value.children |> fold(init: FragmentsAccumulated { pieces: [] }, f: (acc, e) => - accumulate_json_fragment(acc: acc, outcome: emit_data_value_json(value: e, source_indices: source_indices)) + accumulate_json_fragment(acc: acc, outcome: emit_data_value_json(value: e, source_indices: source_indices, variant_wire: variant_wire)) ) match accum { FragmentsRefused { reason: r } => Refused { reason: r } FragmentsAccumulated { pieces: ps } => Emitted { json: concat("[", ps |> join(separator: ", "), "]") } } } - ExprRecordLit { parent_enum: _ } => { - let accum = value.children |> fold(init: FragmentsAccumulated { pieces: [] }, f: (acc, fld) => - accumulate_json_field( - acc: acc, - name: field_init_node_name_at(n: fld, source_indices: source_indices), - outcome: emit_data_value_json(value: field_init_node_value(n: fld), source_indices: source_indices) - ) - ) - match accum { - FragmentsRefused { reason: r } => Refused { reason: r } - FragmentsAccumulated { pieces: ps } => Emitted { json: concat("{", ps |> join(separator: ", "), "}") } + ExprRecordLit { parent_enum: pe } => + match pe { + Present { value: parent } => + match record_lit_type_name_at(texpr: value, source_indices: source_indices) { + Absent => Refused { reason: concat("variant record literal with parent coproduct ", parent, " carries no authored head name to key the wire-spelling index") } + Present { value: head } => + let key = concat(parent, ".", qualified_last_segment(name: head)) + match map_get(variant_wire, key) { + Absent => Refused { reason: concat("no wire spelling indexed for ", key, ": the index covers every coproduct declared in the emission closure it was built from, so this parent is outside that closure") } + Present { value: spelling } => match spelling { + DataVariantSpellingRefused { reason: r } => Refused { reason: r } + DataVariantUntagged => + if (value.children |> count) == 0 { + Emitted { json: "null" } + } else { + match emit_data_fields_json(value: value, source_indices: source_indices, variant_wire: variant_wire) { + FragmentsRefused { reason: r } => Refused { reason: r } + FragmentsAccumulated { pieces: ps } => Emitted { json: concat("{", ps |> join(separator: ", "), "}") } + } + } + DataVariantBareString { tag: t } => + if (value.children |> count) == 0 { + Emitted { json: concat("\"", escape_json_string(s: t), "\"") } + } else { + Refused { reason: concat("variant ", key, " carries fields under a StringVariant wire policy, which is nullary-only; the type-side emission of the parent coproduct refuses it too") } + } + DataVariantInternalTagged { tag_field: tf, tag: t } => + let tag_piece = concat("\"", escape_json_string(s: tf), "\": \"", escape_json_string(s: t), "\"") + match emit_data_fields_json(value: value, source_indices: source_indices, variant_wire: variant_wire) { + FragmentsRefused { reason: r } => Refused { reason: r } + FragmentsAccumulated { pieces: ps } => Emitted { json: concat("{", concat([tag_piece], ps) |> join(separator: ", "), "}") } + } + } + } + } + Absent => + if (value.children |> count) == 0 { + Emitted { json: "null" } + } else { + match emit_data_fields_json(value: value, source_indices: source_indices, variant_wire: variant_wire) { + FragmentsRefused { reason: r } => Refused { reason: r } + FragmentsAccumulated { pieces: ps } => Emitted { json: concat("{", ps |> join(separator: ", "), "}") } + } + } } - } ExprVar { binding_kind: _ } => Emitted { json: concat("\"", escape_json_string(s: expr_var_name_at(texpr: value, source_indices: source_indices)), "\"") } - ExprUnaryOp { op: Neg } => match emit_data_value_json(value: unaryop_operand(texpr: value), source_indices: source_indices) { + ExprUnaryOp { op: Neg } => match emit_data_value_json(value: unaryop_operand(texpr: value), source_indices: source_indices, variant_wire: variant_wire) { Emitted { json: j } => Emitted { json: concat("-", j) } Refused { reason: r } => Refused { reason: r } } diff --git a/src/v1/05_emit_rust.dag b/src/v1/05_emit_rust.dag index 33673fa42ee..653586c9f91 100644 --- a/src/v1/05_emit_rust.dag +++ b/src/v1/05_emit_rust.dag @@ -134,7 +134,7 @@ import v1.compiler.coercion { coerce_primitive_type, is_copy, target_callable, rust_lookup_exact_binding, declaration_realizes_natively_on_rust, type_reference_realization, declaration_realization, type_realization_decision, realized_checkpoint, realization_host_numeric_spelling, - realization_is_host_numeric + realization_is_host_numeric, provenance_declares_structurally } import std.coercion { TypeDeclarationProvenance, CorpusDeclared, KernelMinted, DeclarationIdentityAbsent, @@ -361,6 +361,45 @@ fn render_rust_arrow_type_in_scope(n: Node, generic_param_names: List, s // otherwise renders `predecessor: Nat` as `Rc` at a variant record field while the // constructor passes i64 -- a layer below the field-carrier decision // rust_field_carrier_final_type projects (E0308 x2, E0369 x1). +// PEEL, DO NOT NAME, at an overlay-less QUALIFIED String-spelled alias leaf. A field authored +// `v2.std.text.String` reaches this renderer as the RESOLVED reference leaf -- no children, no +// __applied_type_args overlay (nothing in the current tree writes that channel; the readers are +// legacy), and a provenance that answers the reference's own file (type_reference_provenance's +// documented unresolved-reference shape), so every identity-keyed arm ahead of this one answers +// about the wrong declaration. Rendering the bare name can only bind where the emitted module +// declares or imports that name, and for the String spelling neither holds: kernel names are never +// overridden by imports (#9813, v1.compiler.infer overlay_skips_kernel_name), so the use-line that +// would bind the alias is dropped by import_name_resolves_to_host_realized_kernel_scalar and the +// bare token binds the prelude String -- while every value position renders the same carrier +// Rc>. That is the v2_compiler_tokenize.rs E0308 family on the XL-N board (41 of 72), +// and post-#9907 the whole cross-module population is authored QUALIFIED, which is what the +// contains(".") conjunct keys on. The fn-signature positions peel this population by consulting +// the env-resolved alias binding; this arm asks the same question of the same authority +// (closed_alias_peel_verdict) and renders the alias declaration's resolved right-hand side, so +// both positions project one realization. +// THE QUALIFIED GATE IS LOAD-BEARING, NOT A NARROWING TASTE. Inside the declaring module itself +// (src/v2/std/text.dag authors `text: String` bare) the bare terminal name is the CORRECT render: +// the emitted module carries `pub type String = Rc>;` and the bare token binds it. +// Peeling there is not merely redundant -- the local declaration's env binding resolves to a node +// whose resolved_type drops the RHS type argument, so an ungated peel rendered `Rc` +// there (E0107 x13, E0282 x2 in emitted v2_std_text.rs, measured on the probe7 emission). A +// module-local `type String = ...` referenced bare likewise keeps its bare name and binds its own +// declaration, and the kernel scalar never reaches the peel either: a bare `String` outside the +// declaring module denotes the kernel post-#9813, which the host-carrier arm ahead of this one +// answers. SCOPE: keyed to +// the String spelling because String is the kernel-named STRUCTURAL text carrier with a measured +// error population; std.integer Int and std.float Float realize NATIVELY via +// numeric_realization_declaring_modules, so peeling them would render the algebra structure +// against i64 values -- their field-position rendering is a separate class with its own evidence. +fn rust_overlayless_alias_leaf_requires_peel(env: TypeEnv, n: Node) -> Bool { + let name = authored_name_at(source_indices: env.source_indices, node: n) + n.connective == NoConnective + && (n.children |> count) == 0 + && contains(name, ".") + && qualified_last_segment(name: name) == "String" + && closed_alias_peels_zero_param(env: env, n: n) +} + fn render_rust_type_without_applied_binding(n: Node, shared_types: Set, source_indices: Map, emit_info: EmitGraphInfo) -> String { if is_host_text_carrier_type(n: n, source_indices: source_indices) { return rust_carrier_optional_wrap(n: n, rendered: "String") } if is_host_optional_carrier_type(n: n, source_indices: source_indices) { @@ -428,6 +467,11 @@ fn render_rust_type_without_applied_binding(n: Node, shared_types: Set, "_" } else if is_host_text_carrier_type(n: n, source_indices: source_indices) { rust_carrier_optional_wrap(n: n, rendered: render_rust_text_carrier(shared_types: shared_types)) + } else if rust_overlayless_alias_leaf_requires_peel(env: emit_info.fn_type_env, n: n) { + match lookup_type_for(env: emit_info.fn_type_env, node: n) { + Present { value: binding } => render_rust_type(n: resolved_type(n: binding), shared_types: shared_types, source_indices: source_indices, emit_info: emit_info) + Absent => render_node_type(n: n, target: Rust, shared_types: shared_types, source_indices: source_indices) + } } else { match realization_host_numeric_spelling(decision: type_reference_realization_in_env(n: n, dag_name: qualified_last_segment(name: tn), env: emit_info.fn_type_env, source_indices: source_indices)) { Present { value: host } => rust_carrier_optional_wrap(n: n, rendered: host) @@ -693,10 +737,28 @@ data rust_checkpoint_scalar_phantom_params_note: String = "Construction wall for // binding is still decidable from the env: the reference's leaf name resolves to one declaration // in the referencing module's scope. Accepted only when that declaration IS named by the leaf, // so an alias's right-hand side node can never stand in for the alias. +// A node that IS its own declaration carries its provenance on its own span, and the env +// bare-leaf lookup must not pre-empt it. Inference substitutes the RESOLVED declaration into a +// data annotation's type-argument position, so `BooleanAlgebra` reaches the +// emitter with the arg BEING the `type Bool = True | False` declaration itself -- Disj +// connective, True/False children, ident_span in src/v2/std/logic.dag, no Resolved wrapper +// (measured on the XL-N probe closure; the python.rs:328 / typescript.rs:177 E0308 pair on the +// self-host compile-phase frontier). The bare-leaf arm re-resolves that leaf in the REFERENCING +// module's scope, where post-#9813 a kernel-shadowed spelling answers the KERNEL declaration +// (lookup_type_by_name "Bool" binds the kernel Bool), so the structural enum rendered as host +// `bool` against a value of BooleanAlgebra. The connective is the discriminator: a +// reference node is a bare name (NoConnective), while a node carrying Conj/Disj structure IS +// the declaration, and type_reference_provenance's own-span fallback (v1.compiler.core +// declaration_provenance_of) already answers that shape correctly -- CorpusDeclared on the +// span's own file, which structural_declaration_modules_for then matches. fn type_reference_provenance_in_env(n: Node, env: TypeEnv, source_indices: Map) -> TypeDeclarationProvenance { match n.inferred { Present { value: Resolved { node: _ } } => type_reference_provenance(n: n) _ => + let node_is_own_declaration = n.connective == Conj || n.connective == Disj + if node_is_own_declaration { + type_reference_provenance(n: n) + } else { let leaf = rust_fn_sig_leaf_name(source_indices: source_indices, n: n) if leaf == "" { type_reference_provenance(n: n) } else { @@ -708,6 +770,7 @@ fn type_reference_provenance_in_env(n: Node, env: TypeEnv, source_indices: Map type_reference_provenance(n: n) } } + } } } @@ -824,13 +887,48 @@ fn rust_host_text_carrier_elem_name(n: Node, source_indices: Map String` // answered true here (its name reads as its return leaf) ahead of every Arrow arm, so the type // argument rendered `String` (E0277 x2 at v2.std.compilers.target_model). +// +// The String arm is keyed on the resolved declaration's PROVENANCE, never on the spelling alone +// (DESIGN section 3, and gunbc.recurring_failure_mode +// alias_resolution_collides_with_kernel_spelling): v2.std.text declares `type String = +// FreeMonoid`, a structural alias whose spelling collides with the kernel scalar, and the +// namespace lane (gunbc#9907) requalified the v2 corpus's text-carrier fields to +// `v2.std.text.String` on that basis. A spelling-keyed arm renders every one of those references +// as the host String while every value-position consumer renders the structure -- the E0308 +// family dominating the self-host compile-phase frontier (receipt_1_eced9d24's successor board: +// 64 E0308, 41 of them in v2_compiler_tokenize.rs). The structural roster the answer is read from +// is v1.compiler.coercion structural_declaration_modules_for, the same authority +// type_realization_decision consults, so this arm and the strict decision cannot diverge on one +// node. Kernel mints and unresolved references keep the host answer exactly as before. +// +// The FreeMonoid/List arm carries the same gate one peel later: the record-field path renders the +// RESOLVED type node (src/v1/04_types.dag resolved_type), so a field authored +// `v2.std.text.String` arrives here as the alias's right-hand side -- a `FreeMonoid` +// application whose spelling no longer mentions String at all. Keying that arm on spelling + +// Char element alone re-attributes the corpus-declared std.algebra FreeMonoid to the host text +// carrier, which is the same alias_resolution_collides_with_kernel_spelling class wearing the +// peeled spelling: the type position rendered host `String` while the value position rendered +// `Rc>` (the 41 v2_compiler_tokenize.rs E0308s on the successor board). The arm +// therefore fires only where the resolved declaration is NOT corpus-declared -- a kernel mint or +// an unresolved reference, the population the arm was authored for -- and a corpus-declared +// FreeMonoid/List over Char renders the structure, agreeing with type_realization_decision on the +// same node (measured by gunbc.instruments.carrier_realization_census as DivergesWithExactIdentity +// before this gate, Agrees after). fn is_host_text_carrier_type(n: Node, source_indices: Map) -> Bool { if n.connective == Arrow { return false } let nm = authored_name_at(source_indices: source_indices, node: n) if nm == "String" { - true + !provenance_declares_structurally(dag_name: "String", p: type_reference_provenance(n: n)) } else if nm == "FreeMonoid" || nm == "List" { - rust_host_text_carrier_elem_name(n: n, source_indices: source_indices) == "Char" + if rust_host_text_carrier_elem_name(n: n, source_indices: source_indices) != "Char" { + false + } else { + match type_reference_provenance(n: n) { + CorpusDeclared { decl_file: _ } => false + KernelMinted { minted_name: _ } => true + DeclarationIdentityAbsent => true + } + } } else { false } @@ -1089,9 +1187,18 @@ fn closed_alias_verdict_do_not_peel() -> ClosedAliasPeelVerdict { ClosedAliasHasParams } +// The String spelling guard is provenance-gated for the same reason is_host_text_carrier_type's +// String arm is: a STRUCTURAL String alias (v2.std.text, std.string_type) is a zero-parameter +// closed alias and must be eligible for ClosedAliasPeelZeroParam, or a reference carrying the +// resolved definition's type arguments renders `String` -- name plus resolved children -- +// against a Rust alias declared with no parameters (the E0107 family the host-carrier provenance +// gate surfaces in v2_std_text.rs once the String arm stops short-circuiting those references to +// the host spelling). The kernel scalar keeps the historical do-not-peel answer exactly: its +// provenance is never structurally declared, so the guard still fires for it on every path that +// reaches this verdict without an upstream host-carrier short-circuit. fn closed_alias_peel_verdict(env: TypeEnv, n: Node) -> ClosedAliasPeelVerdict { let name = authored_name_at(source_indices: env.source_indices, node: n) - if name == "String" { + if name == "String" && !provenance_declares_structurally(dag_name: "String", p: type_reference_provenance(n: n)) { return closed_alias_verdict_do_not_peel() } let binding = match lookup_type_for(env: env, node: n) { @@ -2538,6 +2645,175 @@ fn resolve_local_coproduct_wire_policy(coproduct_name: String, all_unit_variants } } +// THE ONE POLICY DECISION FOR AN EMITTED COPRODUCT, extracted so the type side and the data-path +// index ask the same question of the same authority (DESIGN section 2 -- one derivation, two call +// sites). The answer order is: a CoproductWireContract row in the coproduct's own module or its +// `_contracts` sibling (resolve_local_coproduct_wire_policy scans the concatenated context); then, +// for a nullary-only coproduct, the module's legacy `wire_contract` data item; otherwise the +// internal-tag default. The wire_items/wire_imports arguments ARE the emitting module's items and +// imports concatenated with its contracts sibling's (contracts_items_for_module / +// contracts_imports_for_module), and scoped_data_items is the module's scoped data index augmented +// with the contracts sibling's imports -- both call sites build them identically. +fn resolve_emission_coproduct_wire_policy(coproduct_name: String, item: Node, wire_items: List, wire_imports: List, wire_contract_item: Node?, scoped_data_items: Map>, source_indices: Map) -> RustEnumWireSerde { + let all_unit_variants = item.children |> all(child => child.children |> count == 0) + match resolve_local_coproduct_wire_policy(coproduct_name: coproduct_name, all_unit_variants: all_unit_variants, module_items: wire_items, imports: wire_imports, source_indices: source_indices) { + Present { value: local_policy } => local_policy + Absent => + if all_unit_variants { + match wire_contract_item { + Present { value: _ } => resolve_wire_serde_policy_for_coproduct(wire_contract_item: wire_contract_item, source_indices: source_indices, data_items: scoped_data_items) + Absent => rust_tagged_object_policy() + } + } else { + rust_tagged_object_policy() + } + } +} + +// THE TOTAL WIRE-TAG QUESTION. wire_variant_tag_for_policy answers the ATTRIBUTE question -- does +// this variant need a per-variant #[serde(rename)] -- and its `none` means "no attr needed", which +// conflates "renamed by the container's rename_all" with "as authored". The data path needs the +// TAG itself: the exact string serde places on the wire, which under a container rename_all is the +// renamed spelling and under no renaming is the authored one. The two functions read the same +// policy fields in the same order so the answers cannot drift: StripAffix style defers to the +// attribute function's computation (its none -- a failed strip the type side already refuses -- +// propagates as none here), then rename_all snake_case / SCREAMING_SNAKE_CASE, then as-authored. +fn data_path_wire_variant_tag(authored: String, policy: RustEnumWireSerde) -> String? { + if policy.error_message != none { + none + } else { + match policy.rename_style { + Present { value: _ } => wire_variant_tag_for_policy(authored: authored, policy: policy) + Absent => + if contains(policy.enum_attr, "rename_all = \"snake_case\"") { + Present { value: to_snake(name: authored) } + } else if contains(policy.enum_attr, "rename_all = \"SCREAMING_SNAKE_CASE\"") { + Present { value: to_screaming_snake(name: authored) } + } else { + Present { value: authored } + } + } + } +} + +// Projection from the Rust policy carrier to the language-general spelling the data path reads +// (the type's comment at v1.compiler.infer_emit_info DataVariantWireSpelling names why the +// projection exists). Untagged is tested FIRST: policy_is_string_variant's shape test (no +// "tag =" substring) also matches the untagged attribute, so the order is load-bearing. +fn project_data_variant_spelling(authored_variant: String, policy: RustEnumWireSerde) -> DataVariantWireSpelling { + match policy.error_message { + Present { value: message } => DataVariantSpellingRefused { reason: message } + Absent => + if policy_is_untagged(policy: policy) { + DataVariantUntagged + } else { + match data_path_wire_variant_tag(authored: authored_variant, policy: policy) { + Absent => DataVariantSpellingRefused { reason: concat("no wire tag derivable for variant ", authored_variant, " under the parent coproduct's naming policy") } + Present { value: tag } => + if policy_is_string_variant(policy: policy) { + DataVariantBareString { tag: tag } + } else { + match policy_serde_tag_field(policy: policy) { + Present { value: tag_field } => DataVariantInternalTagged { tag_field: tag_field, tag: tag } + Absent => DataVariantSpellingRefused { reason: concat("wire policy for variant ", authored_variant, " carries no tag field and is neither untagged nor string-variant") } + } + } + } + } + } +} + +fn data_variant_wire_spelling_eq(a: DataVariantWireSpelling, b: DataVariantWireSpelling) -> Bool { + match a { + DataVariantInternalTagged { tag_field: a_tf, tag: a_t } => + match b { + DataVariantInternalTagged { tag_field: b_tf, tag: b_t } => a_tf == b_tf && a_t == b_t + _ => false + } + DataVariantBareString { tag: a_t } => + match b { + DataVariantBareString { tag: b_t } => a_t == b_t + _ => false + } + DataVariantUntagged => + match b { + DataVariantUntagged => true + _ => false + } + DataVariantSpellingRefused { reason: a_r } => + match b { + DataVariantSpellingRefused { reason: b_r } => a_r == b_r + _ => false + } + } +} + +// The index is keyed by BARE coproduct and variant names because that is what a variant record +// literal carries: infer stamps parent_enum as the parent declaration's authored name +// (lookup_variant_parent_enum), never module-qualified. Two modules in one closure declaring the +// same coproduct leaf therefore share a key. Where their resolved policies agree the answer is one +// fact and either row serves; where they differ the key is POISONED with a stored refusal, so the +// data path fails closed with the cause instead of silently serving whichever module folded last. +fn insert_data_variant_spelling(index: Map, key: String, spelling: DataVariantWireSpelling, coproduct_name: String) -> Map { + match map_get(index, key) { + Absent => map_insert(index, key, spelling) + Present { value: existing } => + if data_variant_wire_spelling_eq(a: existing, b: spelling) { + index + } else { + map_insert(index, key, DataVariantSpellingRefused { reason: concat("ambiguous wire policy for coproduct ", coproduct_name, ": two modules in the emission closure declare it and their resolved policies differ, so a bare parent_enum cannot name one policy") }) + } + } +} + +// THE CLOSURE-WIDE WIRE-POLICY INDEX the variant arm of emit_data_value_json reads. Built once per +// emission root from the same inputs the type side resolves against, one row per (coproduct, +// variant) pair, beside EmitGraphInfo.type_decl_items. This is the trigger the fail-closed +// variant refusal named on 2026-09-08, landed the same day its first real population measured out: +// five sites in the 00_compile closure (SugarKey x2 in v2.std.compilers.sugar, +// CopiedPortCitationFrontierDisposition x3 in v2.lens.cost.copied_port_citations). +fn build_data_variant_wire_spellings(modules: List, data_items: Map, module_index: ModuleIndex) -> Map { + modules |> fold(init: empty_map(), f: (acc, tm) => + let si = tm.type_env.source_indices + let module_name = authored_name_at(source_indices: si, node: tm.module) + let contracts_imports = contracts_imports_for_module(module_name: module_name, typed_modules: modules, source_indices: si, module_index: module_index) + let wire_items = concat(tm.items, contracts_items_for_module(module_name: module_name, typed_modules: modules, source_indices: si, module_index: module_index)) + let wire_imports = concat(module_imports(n: tm.module), contracts_imports) + let scoped_data = augment_scoped_data_item_index_with_imports( + scoped: build_scoped_data_item_index(typed_module: tm, data_items: data_items), + imports: contracts_imports, + source_indices: si, + data_items: data_items + ) + let wire_contract_item = wire_items + |> filter(i => i.module_item_kind == ModuleItemDataValue && authored_name_at(source_indices: si, node: i) == "wire_contract") + |> first + tm.items + |> filter(item => is_type_def_item(item: item) && is_coproduct_type(n: item)) + |> fold(init: acc, f: (item_acc, item) => + let coproduct_name = authored_name_at(source_indices: si, node: item) + let policy = resolve_emission_coproduct_wire_policy( + coproduct_name: coproduct_name, + item: item, + wire_items: wire_items, + wire_imports: wire_imports, + wire_contract_item: wire_contract_item, + scoped_data_items: scoped_data, + source_indices: si + ) + item.children |> fold(init: item_acc, f: (variant_acc, variant) => + let variant_name = authored_name_at(source_indices: si, node: variant) + insert_data_variant_spelling( + index: variant_acc, + key: concat(coproduct_name, ".", variant_name), + spelling: project_data_variant_spelling(authored_variant: variant_name, policy: policy), + coproduct_name: coproduct_name + ) + ) + ) + ) +} + fn build_data_item_index(modules: List) -> Map { modules |> fold(init: empty_map(), f: (acc, tm) => let module_name = authored_name_at(source_indices: tm.type_env.source_indices, node: tm.module) @@ -3059,10 +3335,13 @@ fn build_emit_rust_context(typed: ResolvedGraph) -> EmitRustContext { type_decl_items: base_info.type_decl_items, source_indices: merged_module_source_indices(modules: typed.modules) ) + let data_items = build_data_item_index(modules: typed.modules) + let module_index = build_module_index(modules: typed.modules, leaf_owner_modules: base_info.item_leaf_owner_modules) let emit_info = EmitGraphInfo { item_leaf_owner_modules: base_info.item_leaf_owner_modules, type_summaries: base_info.type_summaries, type_decl_items: base_info.type_decl_items, + data_variant_wire_spellings: build_data_variant_wire_spellings(modules: typed.modules, data_items: data_items, module_index: module_index), fn_decl_items: base_info.fn_decl_items, recursive_type_set: base_info.recursive_type_set, fielded_variants: base_info.fielded_variants, @@ -3083,7 +3362,6 @@ fn build_emit_rust_context(typed: ResolvedGraph) -> EmitRustContext { expected_type: none } let registry = typed.item_registry - let data_items = build_data_item_index(modules: typed.modules) let workflow_funcs = collect_workflow_funcs(modules: typed.modules, registry: registry, read_only_params_index: emit_info.read_only_params_index) let workflow_default_diags = validate_workflow_param_defaults(workflow_funcs: workflow_funcs) let anonymous_record_diags = typed.modules |> flat_map(tm => tm.items |> flat_map(item => ambiguous_anonymous_record_literal_diagnostics(n: item, type_summaries: emit_info.type_summaries, source_indices: tm.type_env.source_indices, module_name: authored_name_at(source_indices: tm.type_env.source_indices, node: tm.module)))) @@ -3096,7 +3374,6 @@ fn build_emit_rust_context(typed: ResolvedGraph) -> EmitRustContext { ) let test_projections = extract_test_projections(typed: typed) let export_sets = build_module_export_sets(modules: typed.modules) - let module_index = build_module_index(modules: typed.modules, leaf_owner_modules: emit_info.item_leaf_owner_modules) let unlisted_type_names_by_module = group_unlisted_type_names(diags: typed.diagnostics) EmitRustContext { emit_info: emit_info, @@ -3709,10 +3986,13 @@ fn emit_module(typed_module: TypedModule, registry: Map) -> Te type_decl_items: base_info.type_decl_items, source_indices: merged_module_source_indices(modules: [typed_module]) ) + let data_items = build_data_item_index(modules: [typed_module]) + let module_index = build_module_index(modules: [typed_module], leaf_owner_modules: base_info.item_leaf_owner_modules) let emit_info = EmitGraphInfo { item_leaf_owner_modules: base_info.item_leaf_owner_modules, type_summaries: base_info.type_summaries, type_decl_items: base_info.type_decl_items, + data_variant_wire_spellings: build_data_variant_wire_spellings(modules: [typed_module], data_items: data_items, module_index: module_index), fn_decl_items: base_info.fn_decl_items, recursive_type_set: base_info.recursive_type_set, fielded_variants: base_info.fielded_variants, @@ -3734,8 +4014,7 @@ fn emit_module(typed_module: TypedModule, registry: Map) -> Te } let shared_types = emit_info.shared_types let export_sets = build_module_export_sets(modules: [typed_module]) - let module_index = build_module_index(modules: [typed_module], leaf_owner_modules: emit_info.item_leaf_owner_modules) - emit_module_full(typed_module: typed_module, registry: registry, emit_info: emit_info, shared_types: shared_types, svc_module_map: empty_map(), data_items: build_data_item_index(modules: [typed_module]), export_sets: export_sets, typed_modules: [typed_module], module_index: module_index, unlisted_type_names: []).file + emit_module_full(typed_module: typed_module, registry: registry, emit_info: emit_info, shared_types: shared_types, svc_module_map: empty_map(), data_items: data_items, export_sets: export_sets, typed_modules: [typed_module], module_index: module_index, unlisted_type_names: []).file } fn build_module_export_sets(modules: List) -> Map> { @@ -5019,6 +5298,17 @@ fn reference_derived_use_line_plan(items: List, unlisted_type_names: List< // the named qualifier before any line is synthesized; every other qualified name is the value // route's, and this route says nothing about it. Each decision this route does take is a census row // beside the bare-name rows -- the qualified spelling is the row's name, so the two never collide. +// +// THE QUALIFIER IS THE DISAMBIGUATOR. The leaf index is global: `LeafAmbiguous` says two modules in +// the closure declare the leaf, and this route used to decline to the ambiguous row on that answer +// alone. A QUALIFIED reference already names its provider in its own spelling, so the global +// ambiguity never attaches to it -- the question is only whether the named qualifier declares the +// leaf as a type and exports it, the same construction wall the ItemFound arm passes, now asked by +// identity lookup at the qualifier. Declining used to drop the use-line for a reference the author +// had fully resolved: measured as the eight E0425/E0433 `Nat` sites in v2_lens_fact_density.rs, +// whose `v2.std.nat.Nat` co-resides with the std.nat alias in the 00_compile closure. The ambiguous +// row still stands for the reference whose qualifier does NOT declare the leaf -- that one is +// genuinely unresolvable here. fn qualified_type_reference_rows(names: List, this_module_name: String, emit_info: EmitGraphInfo, already: Map, names_already_in_lines: List, registry: Map, export_sets: Map>, typed_modules: List, source_indices: Map, module_index: ModuleIndex) -> List { let leaves_done = names_already_in_lines |> fold(init: empty_map(), f: (acc, nm) => map_insert(acc, nm, true)) names |> flat_map(name => @@ -5031,21 +5321,33 @@ fn qualified_type_reference_rows(names: List, this_module_name: String, } else { match lookup_item_by_leaf(leaf: leaf, registry: registry, emit_info: emit_info) { ItemNotFound => [ReferenceDerivedCandidateRow { module_name: this_module_name, name: name, disposition: CandidateRegistryAbsent }] - ItemLeafAmbiguous { leaf: _ } => [ReferenceDerivedCandidateRow { module_name: this_module_name, name: name, disposition: CandidateLeafAmbiguous }] - ItemFound { info: info } => - if !(registry_row_is_type_declared_in(info: info, module_name: qualifier)) { - [] - } else if provider_proven_exports_symbol(name: leaf, provider_module: qualifier, export_sets: export_sets, typed_modules: typed_modules, source_indices: source_indices, module_index: module_index) { - [ReferenceDerivedCandidateRow { module_name: this_module_name, name: name, disposition: CandidateSurvived { provider_module: qualifier } }] - } else { - [ReferenceDerivedCandidateRow { module_name: this_module_name, name: name, disposition: CandidateExportProofFailed { provider_module: qualifier } }] + ItemLeafAmbiguous { leaf: _ } => + match lookup_item_by_identity(registry: registry, id: DeclaredCallableIdentity { owner_module_path: qualifier, decl_name: leaf }) { + Present { value: info } => + qualified_type_reference_row_for_info(name: name, leaf: leaf, qualifier: qualifier, this_module_name: this_module_name, info: info, export_sets: export_sets, typed_modules: typed_modules, source_indices: source_indices, module_index: module_index) + Absent => [ReferenceDerivedCandidateRow { module_name: this_module_name, name: name, disposition: CandidateLeafAmbiguous }] } + ItemFound { info: info } => + qualified_type_reference_row_for_info(name: name, leaf: leaf, qualifier: qualifier, this_module_name: this_module_name, info: info, export_sets: export_sets, typed_modules: typed_modules, source_indices: source_indices, module_index: module_index) } } } ) } +// The qualifier-keyed decision both lookup arms share (DESIGN section 2 -- one derivation, two +// call sites): the leaf must be a TYPE the qualifier declares, and the qualifier must prove it +// exports the symbol, before any use-line is synthesized. +fn qualified_type_reference_row_for_info(name: String, leaf: String, qualifier: String, this_module_name: String, info: ItemInfo, export_sets: Map>, typed_modules: List, source_indices: Map, module_index: ModuleIndex) -> List { + if !(registry_row_is_type_declared_in(info: info, module_name: qualifier)) { + [] + } else if provider_proven_exports_symbol(name: leaf, provider_module: qualifier, export_sets: export_sets, typed_modules: typed_modules, source_indices: source_indices, module_index: module_index) { + [ReferenceDerivedCandidateRow { module_name: this_module_name, name: name, disposition: CandidateSurvived { provider_module: qualifier } }] + } else { + [ReferenceDerivedCandidateRow { module_name: this_module_name, name: name, disposition: CandidateExportProofFailed { provider_module: qualifier } }] + } +} + // Named because `info.kind == TypeItem` is a comparison the .dag surface reads as a record literal. fn registry_row_is_type_declared_in(info: ItemInfo, module_name: String) -> Bool { if info.module_name != module_name { false } else { match info.kind { TypeItem => true _ => false } } @@ -5113,6 +5415,7 @@ fn emit_module_full(typed_module: TypedModule, registry: Map, item_leaf_owner_modules: emit_info.item_leaf_owner_modules, type_summaries: emit_info.type_summaries, type_decl_items: emit_info.type_decl_items, + data_variant_wire_spellings: emit_info.data_variant_wire_spellings, fn_decl_items: emit_info.fn_decl_items, recursive_type_set: emit_info.recursive_type_set, fielded_variants: emit_info.fielded_variants, @@ -5905,6 +6208,17 @@ fn alias_rhs_rust_qualify_module_filename(name: String, alias_module: String, im // The leaf index says which module owns the spelling; the ROW still comes from the registry, by the // identity that index just completed. Two lookups, one authority. +// +// THE INDEX IS GLOBAL AND THE RESOLUTION IS MODULE-LOCAL. `LeafAmbiguous` records that two modules +// in the closure declare the spelling; it does NOT record that the module under emission is one of +// them, and when it is, the question is not ambiguous at all: source resolution binds an +// unqualified reference to the module's own declaration before any cross-module lookup, so the +// local declaration shadows every foreign declarer of the same leaf. Answering +// `ambiguous_leaf_module_refusal` there poisons a reference the source resolved exactly -- measured +// on the 00_compile closure as twelve E0433 sites in v2_std_integer.rs, whose `Compose` is declared +// by v2.std.integer itself and shadowed nothing else. The refusal still stands when the module +// under emission has no claim: a leaf two FOREIGN modules declare has no resolution here, and the +// import-or-registry arm below still ends at the poison marker for it. fn alias_rhs_base_module_filename(name: String, alias_module: String, imports: List, source_indices: Map, scope: InferScope, registry: Map, export_sets: Map>, typed_modules: List, module_index: ModuleIndex) -> String { let local_mod = module_to_filename(name: alias_module) let owner_info = match map_get(module_index.leaf_owner_modules, name) { @@ -5924,7 +6238,11 @@ fn alias_rhs_base_module_filename(name: String, alias_module: String, imports: L alias_rhs_base_module_from_import_or_registry(name: name, imports: imports, source_indices: source_indices, registry: registry, local_mod: local_mod, export_sets: export_sets, typed_modules: typed_modules, module_index: module_index) } Absent => - alias_rhs_base_module_from_import_or_registry(name: name, imports: imports, source_indices: source_indices, registry: registry, local_mod: local_mod, export_sets: export_sets, typed_modules: typed_modules, module_index: module_index) + if has_physical_type_def_in_module_filename(rhs_name: name, mod_filename: local_mod, typed_modules: typed_modules, source_indices: source_indices, module_index: module_index) { + local_mod + } else { + alias_rhs_base_module_from_import_or_registry(name: name, imports: imports, source_indices: source_indices, registry: registry, local_mod: local_mod, export_sets: export_sets, typed_modules: typed_modules, module_index: module_index) + } } } @@ -7162,6 +7480,7 @@ fn emit_typed_item(item: Node, module_name: String, registry: Map count == 0 { emit_rust_item_refusal(item_text: item_text, reason: "service declares no operations") } else { - emit_service_def(item: item, registry: registry, shared_types: shared_types, env: env) + emit_service_def(item: item, registry: registry, shared_types: shared_types, env: env, emit_info: emit_info) } ModuleItemResource => emit_resource_def(item: item, shared_types: shared_types, env: env) @@ -7508,19 +7827,7 @@ fn emit_type_def_from_connective(item: Node, recursive_types: Set, share let type_params = emit_type_params(params: item.params, source_indices: env.source_indices) concat(rust_visibility_prefix(), "type ", item_text, type_params, " = ", render_rust_diagnostics_carrier_applied(shared_types: shared_types), ";") } else { - let all_unit_variants = item.children |> all(child => child.children |> count == 0) - let serde_policy = match resolve_local_coproduct_wire_policy(coproduct_name: item_text, all_unit_variants: all_unit_variants, module_items: module_items, imports: imports, source_indices: env.source_indices) { - Present { value: local_policy } => local_policy - Absent => - if all_unit_variants { - match wire_contract_item { - Present { value: _ } => resolve_wire_serde_policy_for_coproduct(wire_contract_item: wire_contract_item, source_indices: env.source_indices, data_items: data_items) - Absent => rust_tagged_object_policy() - } - } else { - rust_tagged_object_policy() - } - } + let serde_policy = resolve_emission_coproduct_wire_policy(coproduct_name: item_text, item: item, wire_items: module_items, wire_imports: imports, wire_contract_item: wire_contract_item, scoped_data_items: data_items, source_indices: env.source_indices) let rename_validations = variant_rename_validations_for_policy(children: item.children, env: env, serde_policy: serde_policy) let policy_validation = match serde_policy.error_message { Present { value: message } => emit_rust_compile_error_item(message: message) @@ -8419,7 +8726,8 @@ fn v1_call_forwarding_clone_bound_param_names( fn_decl_items: Map, emit_info: EmitGraphInfo, shared_types: Set, - source_indices: Map + source_indices: Map, + visited: List ) -> List { if (generic_param_names |> count) == 0 { return [] @@ -8431,7 +8739,8 @@ fn v1_call_forwarding_clone_bound_param_names( fn_decl_items: fn_decl_items, emit_info: emit_info, shared_types: shared_types, - source_indices: source_indices + source_indices: source_indices, + visited: visited ) _ => [] } @@ -8444,25 +8753,44 @@ fn v1_call_forwarding_clone_bound_param_names( fn_decl_items: fn_decl_items, emit_info: emit_info, shared_types: shared_types, - source_indices: source_indices + source_indices: source_indices, + visited: visited ) ) ) } -// One call site: resolve the callee's declaration, re-derive the callee's OWN clone bound with the +// One call site: resolve the callee's declaration, re-derive the callee's clone bound with the // same derivation emit_fn_def runs on itself (DESIGN.md S2 -- one derivation, two call sites, never // a re-implementation), then forward it onto the wrapper's params. +// +// THE DERIVATION IS THE FULL ONE, TRANSITIVE OVER THE CALL GRAPH. It was previously the +// self-derived half only (v1_fn_body_derived_clone_param_names), declared one-hop: a callee whose +// own bound is itself only FORWARDED re-derived to empty here, and the wrapper emitted bare. The +// specimen arrived in the 00_compile closure: std.realization_measurement +// witness_measured_rollup_differs_from_predicted_zero calls std.realization_schedule +// cost_account_time_count, whose S: Clone is itself forwarded from cost_time_axis_established's +// match-scrutinee clone -- two hops, so the wrapper's E0277 was the one-hop boundary firing, not a +// new class. The recursion is the same walk this module already runs, applied to the callee's body +// exactly as emit_fn_def applies it to its own. Termination is the visited set every cyclic-graph +// walk in this module already carries (name_in_transitive_export_surface): a recursive callee +// answers its self-derived half alone, which is the pre-existing reading of a cycle and stands. +// THE EQUALITY HALF BELOW STAYS ONE-HOP: no specimen has needed its second hop, and widening it +// without one is the hypothetical-arity move v1_call_forwarding_bound_wrapper_param_names refuses. fn v1_call_site_clone_forwarded_param_names( call: Node, generic_param_names: List, fn_decl_items: Map, emit_info: EmitGraphInfo, shared_types: Set, - source_indices: Map + source_indices: Map, + visited: List ) -> List { let si = source_indices let callee_name = expr_call_func_at(texpr: call, source_indices: si) + if visited |> any(v => v == callee_name) { + return [] + } match map_get(fn_decl_items, callee_name) { Absent => [] Present { value: callee_item } => @@ -8472,13 +8800,24 @@ fn v1_call_site_clone_forwarded_param_names( let callee_params = callee_item.params let callee_type_params = function_type_params(params: callee_params) let callee_generic_param_names = callee_type_params |> map(p => generic_param_name_at(n: p, source_indices: si)) - let callee_clone_param_names = v1_fn_body_derived_clone_param_names( - params: callee_params, - inferred: resolved_type(n: callee_item), - body: callee_body, - emit_info: emit_info, - shared_types: shared_types, - source_indices: si + let callee_clone_param_names = v1_union_bound_param_names( + base: v1_fn_body_derived_clone_param_names( + params: callee_params, + inferred: resolved_type(n: callee_item), + body: callee_body, + emit_info: emit_info, + shared_types: shared_types, + source_indices: si + ), + extra: v1_call_forwarding_clone_bound_param_names( + generic_param_names: callee_generic_param_names, + body: callee_body, + fn_decl_items: fn_decl_items, + emit_info: emit_info, + shared_types: shared_types, + source_indices: si, + visited: concat(visited, [callee_name]) + ) ) v1_call_forwarding_forwarded_param_names( call: call, @@ -8523,9 +8862,11 @@ fn v1_call_site_clone_forwarded_param_names( // BoundedToDirectSingleCallLambdaBody already names and is measured on its own regen, not smuggled // in beside an equality fix. // -// ONE HOP, DECLARED. A callee that earns its own bound only by forwarding two hops up is not -// bounded here in one pass; it is discovered as each intermediate fn is itself re-emitted, exactly -// as the clone half's note describes, and a specimen needing a fixpoint over the call graph is the +// ONE HOP, DECLARED -- and now the equality half alone. The Clone half above went transitive over +// the call graph when its two-hop specimen arrived (std.realization_measurement's witness wrapper +// through cost_account_time_count, named in v1_call_site_clone_forwarded_param_names). This half +// keeps the boundary: a callee that earns its own equality bound only by forwarding two hops up is +// not bounded here in one pass, and a specimen needing the same transitivity for equality is the // next-rung trigger rather than a silent gap. fn v1_call_forwarding_equality_bound_param_names( generic_param_names: List, @@ -8701,7 +9042,8 @@ fn emit_fn_def(name: String, params: List, inferred: Node, body: Node, reg fn_decl_items: emit_info.fn_decl_items, emit_info: emit_info, shared_types: shared_types, - source_indices: si + source_indices: si, + visited: [name] ) let derived_clone_param_names_with_rc_match = v1_union_bound_param_names( base: self_derived_clone_param_names, @@ -11649,6 +11991,7 @@ fn emit_rust_fold_method_call(method_call_node: Node, fold_accumulator_type: Nod item_leaf_owner_modules: emit_info.item_leaf_owner_modules, type_summaries: emit_info.type_summaries, type_decl_items: emit_info.type_decl_items, + data_variant_wire_spellings: emit_info.data_variant_wire_spellings, fn_decl_items: emit_info.fn_decl_items, recursive_type_set: emit_info.recursive_type_set, fielded_variants: emit_info.fielded_variants, @@ -11682,6 +12025,7 @@ fn emit_rust_fold_method_call(method_call_node: Node, fold_accumulator_type: Nod item_leaf_owner_modules: emit_info.item_leaf_owner_modules, type_summaries: emit_info.type_summaries, type_decl_items: emit_info.type_decl_items, + data_variant_wire_spellings: emit_info.data_variant_wire_spellings, fn_decl_items: emit_info.fn_decl_items, recursive_type_set: emit_info.recursive_type_set, fielded_variants: emit_info.fielded_variants, @@ -13830,13 +14174,14 @@ fn emit_service_def( item: Node, registry: Map, shared_types: Set, - env: TypeEnv + env: TypeEnv, + emit_info: EmitGraphInfo ) -> String { let safe_name = sanitize_service_name(name: authored_name(env: env, node: item)) let fallback_transport = service_fallback_transport(item: item) let op_children = item.children let struct_def = emit_service_struct(name: safe_name, fallback_transport: fallback_transport, op_children: op_children, service_item: item, source_indices: env.source_indices) - let impl_block = emit_service_impl(name: safe_name, transport: fallback_transport, op_children: op_children, registry: registry, shared_types: shared_types, env: env, service_item: item) + let impl_block = emit_service_impl(name: safe_name, transport: fallback_transport, op_children: op_children, registry: registry, shared_types: shared_types, env: env, service_item: item, emit_info: emit_info) concat(struct_def, "\n\n", impl_block) } @@ -13857,11 +14202,11 @@ fn emit_service_config_fields(fallback_transport: Node, op_children: List, else { decls |> join(separator: "") } } -fn emit_service_impl(name: String, transport: Node, op_children: List, registry: Map, shared_types: Set, env: TypeEnv, service_item: Node) -> String { +fn emit_service_impl(name: String, transport: Node, op_children: List, registry: Map, shared_types: Set, env: TypeEnv, service_item: Node, emit_info: EmitGraphInfo) -> String { let depth = 0 let new_method = emit_service_new_method(name: name, fallback_transport: transport, op_children: op_children, service_item: service_item, source_indices: env.source_indices) let method_strs = op_children |> map(op_node => - emit_operation_method(service_name: name, transport: transport, op_node: op_node, registry: registry, depth: depth + 1, shared_types: shared_types, env: env, service_item: service_item) + emit_operation_method(service_name: name, transport: transport, op_node: op_node, registry: registry, depth: depth + 1, shared_types: shared_types, env: env, service_item: service_item, emit_info: emit_info) ) let all_methods = concat([new_method], method_strs) let methods_str = all_methods |> join(separator: "\n\n") @@ -13944,7 +14289,7 @@ fn emit_modifier_doc_from_props(properties: List, source_indices: Map, depth: Int, shared_types: Set, env: TypeEnv, service_item: Node) -> String { +fn emit_operation_method(service_name: String, transport: Node, op_node: Node, registry: Map, depth: Int, shared_types: Set, env: TypeEnv, service_item: Node, emit_info: EmitGraphInfo) -> String { let op_text = authored_name(env: env, node: op_node) let input_params = op_node.params |> map(p => concat(emit_ident(name: param_node_name_at(n: p, source_indices: env.source_indices), target: Rust), rust_items().param_type_sep, emit_rust_param_type(n: param_node_type_expr(n: p), generic_param_names: [], shared_types: shared_types, source_indices: env.source_indices, variant_to_enum: empty_map(), env: env, enclosing_returns_arrow: false)) @@ -13960,7 +14305,7 @@ fn emit_operation_method(service_name: String, transport: Node, op_node: Node, r let bound = bind_operation_transport(t: eff_transport, op_node: op_node, source_indices: env.source_indices) let real_body = emit_transport_call(bound: bound, op_name: op_text, registry: registry, depth: depth + 2, service_item: service_item, op_node: op_node, source_indices: env.source_indices, shared_types: shared_types, env: env) let mock_props = op_node.properties |> filter(p => has_mock_prefix(name: field_init_node_name_at(n: p, source_indices: env.source_indices))) - let dry_run_body = emit_dry_run_branch_from_props(op_name: op_text, inferred: resolved_type(n: op_node), mock_props: mock_props, registry: registry, source_indices: env.source_indices, op_node: op_node, env: env, shared_types: shared_types) + let dry_run_body = emit_dry_run_branch_from_props(op_name: op_text, inferred: resolved_type(n: op_node), mock_props: mock_props, registry: registry, source_indices: env.source_indices, op_node: op_node, env: env, shared_types: shared_types, emit_info: emit_info) let body = concat( "if self.dry_run.is_dry_run() {\n", make_indent(level: depth + 2),dry_run_body, "\n", @@ -13974,12 +14319,12 @@ fn emit_operation_method(service_name: String, transport: Node, op_node: Node, r make_indent(level: depth + 1),body, "\n}") } -fn emit_dry_run_branch_from_props(op_name: String, inferred: Node, mock_props: List, registry: Map, source_indices: Map, op_node: Node, env: TypeEnv, shared_types: Set) -> String { +fn emit_dry_run_branch_from_props(op_name: String, inferred: Node, mock_props: List, registry: Map, source_indices: Map, op_node: Node, env: TypeEnv, shared_types: Set, emit_info: EmitGraphInfo) -> String { let log_line = concat("eprintln!(\"[dry-run] ", op_name, "\");") if mock_props |> count > 0 { let first_mock = mock_props |> first match first_mock { - Present { value: mp } => match emit_data_value_json(value: field_init_node_value(n: mp), source_indices: source_indices) { + Present { value: mp } => match emit_data_value_json(value: field_init_node_value(n: mp), source_indices: source_indices, variant_wire: emit_info.data_variant_wire_spellings) { Refused { reason: r } => concat(log_line, "\ncompile_error!(\"", escape_string_literal_body(s: r), "\");") Emitted { json: mock_json } => { let is_multi_field_conj = inferred.connective == Conj && inferred.children |> count > 1 @@ -15153,7 +15498,7 @@ fn emit_data_def_body(type_node: Node, value: Node, registry: Map true _ => false } { concat(" ", emit_typed_expr(texpr: value, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info, fuel: 1024)) } else if has_nested_records_node(n: type_node, source_indices: scope.type_env.source_indices) && !data_value_has_cross_refs(value: value) { - match emit_data_value_json(value: value, source_indices: scope.type_env.source_indices) { + match emit_data_value_json(value: value, source_indices: scope.type_env.source_indices, variant_wire: emit_info.data_variant_wire_spellings) { Refused { reason: r } => concat(" compile_error!(\"", escape_string_literal_body(s: r), "\")") Emitted { json: json_str } => concat(" serde_json::from_value(serde_json::json!(", json_str, "))\n", " .expect(\"valid data definition\")") diff --git a/src/v1/stage0/src/compiler_tests.rs b/src/v1/stage0/src/compiler_tests.rs index 1d8b334bde3..79ae3a2dfc7 100644 --- a/src/v1/stage0/src/compiler_tests.rs +++ b/src/v1/stage0/src/compiler_tests.rs @@ -3163,10 +3163,6 @@ mod compiler_tests { coerce_container_template(RenderTarget::Rust, "Map".into()), Some("HashMap<{0}, {1}>".to_string()) ); - assert_eq!( - coerce_container_template(RenderTarget::Rust, "PointwisePower".into()), - Some("BTreeSet<{0}>".to_string()) - ); assert_eq!( coerce_container_template(RenderTarget::Rust, "Set".into()), Some("BTreeSet<{0}>".to_string()) @@ -3200,10 +3196,6 @@ mod compiler_tests { coerce_container_template(RenderTarget::Python, "PartialFunction".into()), Some("dict[{0}, {1}]".to_string()) ); - assert_eq!( - coerce_container_template(RenderTarget::Python, "PointwisePower".into()), - Some("set[{0}]".to_string()) - ); assert_eq!( coerce_container_template(RenderTarget::Python, "Set".into()), Some("set[{0}]".to_string()) @@ -3237,10 +3229,6 @@ mod compiler_tests { coerce_container_template(RenderTarget::Go, "PartialFunction".into()), Some("map[{0}]{1}".to_string()) ); - assert_eq!( - coerce_container_template(RenderTarget::Go, "PointwisePower".into()), - Some("map[{0}]struct{}".to_string()) - ); assert_eq!( coerce_container_template(RenderTarget::Go, "Set".into()), Some("map[{0}]struct{}".to_string()) diff --git a/src/v1/stage0/src/extdeps_languages_go_types.rs b/src/v1/stage0/src/extdeps_languages_go_types.rs index 189a6fc56e2..28c135ffc1c 100644 --- a/src/v1/stage0/src/extdeps_languages_go_types.rs +++ b/src/v1/stage0/src/extdeps_languages_go_types.rs @@ -39,7 +39,7 @@ pub fn go_type_checkpoints() -> Rc>> { pub fn go_algebra_inhabitants() -> Rc>> { thread_local! { static CACHED: Rc>> = { - serde_json::from_value(serde_json::json!([{"algebra": "FreeMonoid", "template": "[]{0}", "arity": 1, "identity_expr": "nil", "import_path": null, "is_copy": null}, {"algebra": "FinitePowerSet", "template": "map[{0}]struct{}", "arity": 1, "identity_expr": "nil", "import_path": null, "is_copy": null}, {"algebra": "PointwisePower", "template": "map[{0}]struct{}", "arity": 1, "identity_expr": "nil", "import_path": null, "is_copy": null}, {"algebra": "PartialFunction", "template": "map[{0}]{1}", "arity": 2, "identity_expr": "nil", "import_path": null, "is_copy": null}, {"algebra": "FinitelySupportedFunction", "template": "map[{0}]{1}", "arity": 2, "identity_expr": "nil", "import_path": null, "is_copy": null}, {"algebra": "OrderedRing", "template": "int64", "arity": 0, "identity_expr": "0", "import_path": null, "is_copy": null}, {"algebra": "ApproximateField", "template": "float64", "arity": 0, "identity_expr": "0.0", "import_path": null, "is_copy": null}])) + serde_json::from_value(serde_json::json!([{"algebra": "FreeMonoid", "template": "[]{0}", "arity": 1, "identity_expr": "nil", "import_path": null, "is_copy": null}, {"algebra": "FinitePowerSet", "template": "map[{0}]struct{}", "arity": 1, "identity_expr": "nil", "import_path": null, "is_copy": null}, {"algebra": "PartialFunction", "template": "map[{0}]{1}", "arity": 2, "identity_expr": "nil", "import_path": null, "is_copy": null}, {"algebra": "FinitelySupportedFunction", "template": "map[{0}]{1}", "arity": 2, "identity_expr": "nil", "import_path": null, "is_copy": null}, {"algebra": "OrderedRing", "template": "int64", "arity": 0, "identity_expr": "0", "import_path": null, "is_copy": null}, {"algebra": "ApproximateField", "template": "float64", "arity": 0, "identity_expr": "0.0", "import_path": null, "is_copy": null}])) .expect("valid data definition") }; } diff --git a/src/v1/stage0/src/extdeps_languages_python_types.rs b/src/v1/stage0/src/extdeps_languages_python_types.rs index 427946724ea..e34a1cca65a 100644 --- a/src/v1/stage0/src/extdeps_languages_python_types.rs +++ b/src/v1/stage0/src/extdeps_languages_python_types.rs @@ -40,7 +40,7 @@ pub fn python_type_checkpoints() -> Rc>> { pub fn python_algebra_inhabitants() -> Rc>> { thread_local! { static CACHED: Rc>> = { - serde_json::from_value(serde_json::json!([{"algebra": "FreeMonoid", "template": "list[{0}]", "arity": 1, "identity_expr": "[]", "import_path": null, "is_copy": null}, {"algebra": "FinitePowerSet", "template": "set[{0}]", "arity": 1, "identity_expr": "set()", "import_path": null, "is_copy": null}, {"algebra": "PointwisePower", "template": "set[{0}]", "arity": 1, "identity_expr": "set()", "import_path": null, "is_copy": null}, {"algebra": "PartialFunction", "template": "dict[{0}, {1}]", "arity": 2, "identity_expr": "{}", "import_path": null, "is_copy": null}, {"algebra": "FinitelySupportedFunction", "template": "dict[{0}, {1}]", "arity": 2, "identity_expr": "{}", "import_path": null, "is_copy": null}, {"algebra": "OrderedRing", "template": "int", "arity": 0, "identity_expr": "0", "import_path": null, "is_copy": null}, {"algebra": "ApproximateField", "template": "float", "arity": 0, "identity_expr": "0.0", "import_path": null, "is_copy": null}])) + serde_json::from_value(serde_json::json!([{"algebra": "FreeMonoid", "template": "list[{0}]", "arity": 1, "identity_expr": "[]", "import_path": null, "is_copy": null}, {"algebra": "FinitePowerSet", "template": "set[{0}]", "arity": 1, "identity_expr": "set()", "import_path": null, "is_copy": null}, {"algebra": "PartialFunction", "template": "dict[{0}, {1}]", "arity": 2, "identity_expr": "{}", "import_path": null, "is_copy": null}, {"algebra": "FinitelySupportedFunction", "template": "dict[{0}, {1}]", "arity": 2, "identity_expr": "{}", "import_path": null, "is_copy": null}, {"algebra": "OrderedRing", "template": "int", "arity": 0, "identity_expr": "0", "import_path": null, "is_copy": null}, {"algebra": "ApproximateField", "template": "float", "arity": 0, "identity_expr": "0.0", "import_path": null, "is_copy": null}])) .expect("valid data definition") }; } diff --git a/src/v1/stage0/src/extdeps_languages_rust_types.rs b/src/v1/stage0/src/extdeps_languages_rust_types.rs index 9b540d985a0..3b45c56e5ab 100644 --- a/src/v1/stage0/src/extdeps_languages_rust_types.rs +++ b/src/v1/stage0/src/extdeps_languages_rust_types.rs @@ -59,7 +59,7 @@ pub fn rust_checkpoint_scalar_arity_note() -> String { pub fn rust_algebra_inhabitants() -> Rc>> { thread_local! { static CACHED: Rc>> = { - serde_json::from_value(serde_json::json!([{"algebra": "FreeMonoid", "template": "Vec<{0}>", "arity": 1, "identity_expr": "Vec::new()", "import_path": null, "is_copy": false}, {"algebra": "FinitePowerSet", "template": "BTreeSet<{0}>", "arity": 1, "identity_expr": "BTreeSet::new()", "import_path": null, "is_copy": false}, {"algebra": "PointwisePower", "template": "BTreeSet<{0}>", "arity": 1, "identity_expr": "BTreeSet::new()", "import_path": null, "is_copy": false}, {"algebra": "FinitelySupportedFunction", "template": "HashMap<{0}, {1}>", "arity": 2, "identity_expr": "HashMap::new()", "import_path": null, "is_copy": false}, {"algebra": "OrderedRing", "template": "i64", "arity": 0, "identity_expr": "0i64", "import_path": null, "is_copy": true}, {"algebra": "ApproximateField", "template": "f64", "arity": 0, "identity_expr": "0.0f64", "import_path": null, "is_copy": true}])) + serde_json::from_value(serde_json::json!([{"algebra": "FreeMonoid", "template": "Vec<{0}>", "arity": 1, "identity_expr": "Vec::new()", "import_path": null, "is_copy": false}, {"algebra": "FinitePowerSet", "template": "BTreeSet<{0}>", "arity": 1, "identity_expr": "BTreeSet::new()", "import_path": null, "is_copy": false}, {"algebra": "FinitelySupportedFunction", "template": "HashMap<{0}, {1}>", "arity": 2, "identity_expr": "HashMap::new()", "import_path": null, "is_copy": false}, {"algebra": "OrderedRing", "template": "i64", "arity": 0, "identity_expr": "0i64", "import_path": null, "is_copy": true}, {"algebra": "ApproximateField", "template": "f64", "arity": 0, "identity_expr": "0.0f64", "import_path": null, "is_copy": true}])) .expect("valid data definition") }; } diff --git a/src/v1/stage0/src/namespace_wave_admission.rs b/src/v1/stage0/src/namespace_wave_admission.rs index a17e9cd935c..52c72dfd788 100644 --- a/src/v1/stage0/src/namespace_wave_admission.rs +++ b/src/v1/stage0/src/namespace_wave_admission.rs @@ -1674,7 +1674,95 @@ pub struct TransitionAdmission { /// `0 unadjudicated delta(s)`. So the per-append admission row is not the honest cost of the pool /// being adjudicated — it was the cost of the baseline being wrong, and a row per class from here /// on would be a standing mitigation over a repaired defect (DESIGN §4b: construction subsumes it). -pub const NAMESPACE_TRANSITION_ADMISSIONS: &[TransitionAdmission] = &[]; +/// +/// STRUCTURAL-TEXT AND LOGIC AUTHORITY REQUALIFICATION (2026-09-09, gunbc#10692). No ordinal is +/// claimed, for the reason the entries above give. Five binding sites on the v2 self-host branch +/// stop spelling two type names into the ambient kernel set and name the authority module +/// instead, which is `TargetChanged` and is not auto-admitted: `EmitSpellingEscape.from/to`, +/// `apply_emit_spelling_escapes.spelling` and `integer_string_to_decimal_digits_step.s` requalify +/// `String` to `v2.std.text`; `py_bool_grounding` and `ts_bool_grounding` requalify their +/// `BooleanAlgebra` parameter `Bool` to `v2.std.logic`. +/// +/// WHY THE MOVE, because a relocation with no reason is the one a reader cannot check. The three +/// `String` sites carry STRUCTURAL text — `v2.std.text.String` is `FreeMonoid`, and the +/// chars(String) <- Variant cluster repair (this branch) restored structural reads end-to-end, so +/// the carriers' types name the structural authority rather than the kernel homonym; the +/// qualification is also load-bearing for native emission, whose host-String arm keys on +/// declaration provenance (this branch, `is_host_text_carrier_type`), so an unqualified spelling +/// would render the host carrier while value-position consumers render the structure. The two +/// `Bool` sites bind a value whose own declaration already carries the qualified parameter: +/// `v2.std.logic` declares `type Bool = True | False` and `bool_boolean_algebra: +/// BooleanAlgebra` resolves that spelling to its OWN module's declaration, so a grounding +/// row annotated `BooleanAlgebra` with the kernel reading stated a type its value does not +/// inhabit. This is the gunbc#9907 namespace-lane requalification reaching five sites the XL-N +/// closure repair touched. +/// +/// ONE CHANGE CLASS. Nothing is requalified at the leaf: every spelling is identical on both +/// sides, and what moved is the declaration behind it — from the ambient kernel type set +/// (``) to the explicitly named authority module. No membership edge is added or removed +/// by these five sites (the requalification is by qualified path or an import the module already +/// carried), which is why the run reports exactly five binding rows and nothing else. +/// +/// TRIGGER, AND IT IS THESE ROWS' OWN DEATH: they go when gunbc#10692 merges. Main then binds +/// each spelling to the named target, base and head agree, no run can produce these deltas, and +/// all five report CONSUMED rather than stale — coming due on this roster's next touch. +/// Adjudicate that deletion by joining each row against main's tree on its own +/// (module, in_declaration, spelling, target) tuple rather than trusting this sentence, because +/// a trigger sentence is not evidence that the trigger fired. +const V2_STRUCTURAL_TEXT_LOGIC_REQUALIFICATION_LABEL: &str = + "gunbc#10692 structural-text/logic authority requalification 2026-09-09"; +pub const NAMESPACE_TRANSITION_ADMISSIONS: &[TransitionAdmission] = &[ + TransitionAdmission { + label: V2_STRUCTURAL_TEXT_LOGIC_REQUALIFICATION_LABEL, + subject: AdmissionSubject::Binding { + module: "v2.extdeps.languages.python", + in_declaration: "py_bool_grounding", + spelling: "Bool", + target: "v2.std.logic", + }, + disposition: NamespaceDeltaDisposition::TargetChanged, + }, + TransitionAdmission { + label: V2_STRUCTURAL_TEXT_LOGIC_REQUALIFICATION_LABEL, + subject: AdmissionSubject::Binding { + module: "v2.extdeps.languages.typescript", + in_declaration: "ts_bool_grounding", + spelling: "Bool", + target: "v2.std.logic", + }, + disposition: NamespaceDeltaDisposition::TargetChanged, + }, + TransitionAdmission { + label: V2_STRUCTURAL_TEXT_LOGIC_REQUALIFICATION_LABEL, + subject: AdmissionSubject::Binding { + module: "v2.std.compilers.target_model", + in_declaration: "EmitSpellingEscape", + spelling: "String", + target: "v2.std.text", + }, + disposition: NamespaceDeltaDisposition::TargetChanged, + }, + TransitionAdmission { + label: V2_STRUCTURAL_TEXT_LOGIC_REQUALIFICATION_LABEL, + subject: AdmissionSubject::Binding { + module: "v2.std.compilers.target_model", + in_declaration: "apply_emit_spelling_escapes", + spelling: "String", + target: "v2.std.text", + }, + disposition: NamespaceDeltaDisposition::TargetChanged, + }, + TransitionAdmission { + label: V2_STRUCTURAL_TEXT_LOGIC_REQUALIFICATION_LABEL, + subject: AdmissionSubject::Binding { + module: "v2.std.integer", + in_declaration: "integer_string_to_decimal_digits_step", + spelling: "String", + target: "v2.std.text", + }, + disposition: NamespaceDeltaDisposition::TargetChanged, + }, +]; /// The denominators a green must name (DESIGN §5): a run that cannot say what it covered is an /// instrument failure wearing coverage's clothes. diff --git a/src/v1/stage0/src/required_regen_host.rs b/src/v1/stage0/src/required_regen_host.rs index f174a8d8bed..58b0700561b 100644 --- a/src/v1/stage0/src/required_regen_host.rs +++ b/src/v1/stage0/src/required_regen_host.rs @@ -4196,6 +4196,10 @@ struct PartitionRebuildActuation { package_closure: Vec, excluded_packages: Vec, decision_line: String, + /// The model's ReleaseScopeEmpty arm: every changed mirror is excluded from the release + /// build by construction (e.g. a `#[cfg(test)]`-gated module), so the correct package + /// closure IS the empty set and the build runs purely as verification of that claim. + release_scope_empty: bool, } type ModelValue = crate::v1_interpreter::Value; @@ -4277,11 +4281,21 @@ fn partition_rebuild_actuation( )) } }; + let release_scope_empty = match call("stage0_partition_rebuild_release_scope_empty_today")? { + ModelValue::Bool(value) => value, + other => { + return Err(format!( + "refusal: stage0_partition_rebuild_release_scope_empty_today returned {} instead of Bool", + other.type_label_public() + )) + } + }; Ok(PartitionRebuildActuation { actuatable, package_closure, excluded_packages, decision_line, + release_scope_empty, }) } @@ -4295,7 +4309,7 @@ fn partitioned_rebuild_from_installed( actuation.decision_line )); } - if actuation.package_closure.is_empty() { + if actuation.package_closure.is_empty() && !actuation.release_scope_empty { return Err(format!( "StageSeedBuildRefused: actuation admitted an empty package closure -- {}", actuation.decision_line @@ -5924,11 +5938,15 @@ pub fn run_regen_round_cost( .iter() .map(|stage| stage.build_compiled_crates) .sum(); + // The receipt field is named in the model's vocabulary: a "mirror" is the basename the + // partition rows and rosters key on. The stages carry projected PATHS, so project through + // the single bridge -- feeding paths to the decision model rendered a spurious + // MirrorHasNoOwningPackage line on every drifted round's receipt. let installed_mirrors = transaction_receipt .stages .iter() .flat_map(|stage| stage.surfaces.iter()) - .map(|surface| surface.projected_path.clone()) + .map(|surface| emit_path_basename(&surface.projected_path).to_string()) .collect::>() .into_iter() .collect::>(); diff --git a/src/v1/stage0/src/v1_compiler_emit.rs b/src/v1/stage0/src/v1_compiler_emit.rs index b7b01b963ab..90ddfd14eea 100644 --- a/src/v1/stage0/src/v1_compiler_emit.rs +++ b/src/v1/stage0/src/v1_compiler_emit.rs @@ -57,7 +57,11 @@ pub use crate::v1_compiler_infer::InferScope; pub use crate::v1_compiler_infer::{ build_params_scope, call_param_caller_labels, extend_scope, is_where_refinement_type, }; -pub use crate::v1_compiler_infer_emit_info::{EmitGraphInfo, TypeSummary}; +use crate::v1_compiler_infer_emit_info::DataVariantWireSpelling::{ + DataVariantBareString, DataVariantInternalTagged, DataVariantSpellingRefused, + DataVariantUntagged, +}; +pub use crate::v1_compiler_infer_emit_info::{DataVariantWireSpelling, EmitGraphInfo, TypeSummary}; use crate::v1_compiler_infer_env::GlobalBareLookupState::*; pub use crate::v1_compiler_infer_env::UnitVariantContribution; pub use crate::v1_compiler_infer_env::{authored_name, empty_symbol_index, lookup_type_for}; @@ -1001,9 +1005,33 @@ pub fn accumulate_json_field( } } +pub fn emit_data_fields_json( + value: Rc, + source_indices: Rc>>, + variant_wire: Rc>>, +) -> Rc { + value.children.clone().iter().cloned().fold( + Rc::new(JsonFragmentsAccum::FragmentsAccumulated { + pieces: Rc::new(vec![]), + }), + |acc: Rc, fld: Rc| { + accumulate_json_field( + acc, + crate::v1_std_core::field_init_node_name_at(fld.clone(), source_indices.clone()), + emit_data_value_json( + crate::v1_std_core::field_init_node_value(fld.clone()), + source_indices.clone(), + variant_wire.clone(), + ), + ) + }, + ) +} + pub fn emit_data_value_json( value: Rc, source_indices: Rc>>, + variant_wire: Rc>>, ) -> Rc { stacker::maybe_grow(512 * 1024, 2 * 1024 * 1024, || { match (*value.expr_data.clone()).clone() { @@ -1051,7 +1079,11 @@ pub fn emit_data_value_json( |acc: Rc, e: Rc| { accumulate_json_fragment( acc, - emit_data_value_json(e.clone(), source_indices.clone()), + emit_data_value_json( + e.clone(), + source_indices.clone(), + variant_wire.clone(), + ), ) }, ); @@ -1069,39 +1101,105 @@ pub fn emit_data_value_json( } } } - ExprData::ExprRecordLit { parent_enum: _, .. } => { - let accum = value.children.clone().iter().cloned().fold( - Rc::new(JsonFragmentsAccum::FragmentsAccumulated { - pieces: Rc::new(vec![]), - }), - |acc: Rc, fld: Rc| { - accumulate_json_field( - acc, - crate::v1_std_core::field_init_node_name_at( - fld.clone(), - source_indices.clone(), - ), - emit_data_value_json( - crate::v1_std_core::field_init_node_value(fld.clone()), - source_indices.clone(), + ExprData::ExprRecordLit { + parent_enum: pe, .. + } => match pe.clone() { + Some(parent) => match crate::v1_std_core::record_lit_type_name_at( + value.clone(), + source_indices.clone(), + ) { + std::option::Option::None => Rc::new(EmitterOutcome::Refused { + reason: v1_rt::concat( + v1_rt::concat( + "variant record literal with parent coproduct ".to_string(), + parent.clone(), ), - ) - }, - ); - match (*accum.clone()).clone() { - JsonFragmentsAccum::FragmentsRefused { reason: r, .. } => { - Rc::new(EmitterOutcome::Refused { reason: r.clone() }) + " carries no authored head name to key the wire-spelling index" + .to_string(), + ), + }), + Some(head) => { + let key = v1_rt::concat( + v1_rt::concat(parent.clone(), ".".to_string()), + crate::v1_std_core::qualified_last_segment(head.clone()), + ); + match v1_rt::map_get(&variant_wire, key.clone()) { + std::option::Option::None => Rc::new(EmitterOutcome::Refused { + reason: v1_rt::concat(v1_rt::concat("no wire spelling indexed for ".to_string(), key.clone()), ": the index covers every coproduct declared in the emission closure it was built from, so this parent is outside that closure".to_string()), +}), + Some(spelling) => match (*spelling.clone()).clone() { + DataVariantWireSpelling::DataVariantSpellingRefused { reason: r, .. } => Rc::new(EmitterOutcome::Refused { + reason: r.clone(), +}), + DataVariantWireSpelling::DataVariantUntagged => if ((value.children.clone().len() as i64) == 0) { + Rc::new(EmitterOutcome::Emitted { + json: "null".to_string(), +}) + } else { + match (*emit_data_fields_json(value.clone(), source_indices.clone(), variant_wire.clone())).clone() { + JsonFragmentsAccum::FragmentsRefused { reason: r, .. } => Rc::new(EmitterOutcome::Refused { + reason: r.clone(), +}), + JsonFragmentsAccum::FragmentsAccumulated { pieces: ps, .. } => Rc::new(EmitterOutcome::Emitted { + json: v1_rt::concat(v1_rt::concat("{".to_string(), ps.clone().join(&", ".to_string())), "}".to_string()), +}), +} + }, + DataVariantWireSpelling::DataVariantBareString { tag: t, .. } => if ((value.children.clone().len() as i64) == 0) { + Rc::new(EmitterOutcome::Emitted { + json: v1_rt::concat(v1_rt::concat("\"".to_string(), crate::v1_compiler_emit_core_support::escape_json_string(t.clone())), "\"".to_string()), +}) + } else { + Rc::new(EmitterOutcome::Refused { + reason: v1_rt::concat(v1_rt::concat("variant ".to_string(), key.clone()), " carries fields under a StringVariant wire policy, which is nullary-only; the type-side emission of the parent coproduct refuses it too".to_string()), +}) + }, + DataVariantWireSpelling::DataVariantInternalTagged { tag_field: tf, tag: t, .. } => { + let tag_piece = v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat("\"".to_string(), crate::v1_compiler_emit_core_support::escape_json_string(tf.clone())), "\": \"".to_string()), crate::v1_compiler_emit_core_support::escape_json_string(t.clone())), "\"".to_string()); +match (*emit_data_fields_json(value.clone(), source_indices.clone(), variant_wire.clone())).clone() { + JsonFragmentsAccum::FragmentsRefused { reason: r, .. } => Rc::new(EmitterOutcome::Refused { + reason: r.clone(), +}), + JsonFragmentsAccum::FragmentsAccumulated { pieces: ps, .. } => Rc::new(EmitterOutcome::Emitted { + json: v1_rt::concat(v1_rt::concat("{".to_string(), v1_rt::concat(Rc::new(vec![tag_piece.clone()]), ps.clone()).join(&", ".to_string())), "}".to_string()), +}), +} +}, +}, +} } - JsonFragmentsAccum::FragmentsAccumulated { pieces: ps, .. } => { + }, + std::option::Option::None => { + if ((value.children.clone().len() as i64) == 0) { Rc::new(EmitterOutcome::Emitted { - json: v1_rt::concat( - v1_rt::concat("{".to_string(), ps.clone().join(&", ".to_string())), - "}".to_string(), - ), + json: "null".to_string(), }) + } else { + match (*emit_data_fields_json( + value.clone(), + source_indices.clone(), + variant_wire.clone(), + )) + .clone() + { + JsonFragmentsAccum::FragmentsRefused { reason: r, .. } => { + Rc::new(EmitterOutcome::Refused { reason: r.clone() }) + } + JsonFragmentsAccum::FragmentsAccumulated { pieces: ps, .. } => { + Rc::new(EmitterOutcome::Emitted { + json: v1_rt::concat( + v1_rt::concat( + "{".to_string(), + ps.clone().join(&", ".to_string()), + ), + "}".to_string(), + ), + }) + } + } } } - } + }, ExprData::ExprVar { binding_kind: _, .. } => Rc::new(EmitterOutcome::Emitted { @@ -1124,6 +1222,7 @@ pub fn emit_data_value_json( } => match (*emit_data_value_json( crate::v1_std_core::unaryop_operand(value.clone()), source_indices.clone(), + variant_wire.clone(), )) .clone() { diff --git a/src/v1/stage0/src/v1_compiler_emit_rust.rs b/src/v1/stage0/src/v1_compiler_emit_rust.rs index 1a8982a48ff..503740cf544 100644 --- a/src/v1/stage0/src/v1_compiler_emit_rust.rs +++ b/src/v1/stage0/src/v1_compiler_emit_rust.rs @@ -117,9 +117,9 @@ pub use crate::v1_compiler_closure_stub_v2_std_integer_rust::closure_stub_v2_std pub use crate::v1_compiler_closure_stub_v2_std_text_rust::closure_stub_v2_std_text_source; pub use crate::v1_compiler_coercion::{ coerce_primitive_type, declaration_realization, declaration_realizes_natively_on_rust, is_copy, - realization_host_numeric_spelling, realization_is_host_numeric, realized_checkpoint, - rust_lookup_exact_binding, target_callable, type_realization_decision, - type_reference_realization, + provenance_declares_structurally, realization_host_numeric_spelling, + realization_is_host_numeric, realized_checkpoint, rust_lookup_exact_binding, target_callable, + type_realization_decision, type_reference_realization, }; pub use crate::v1_compiler_compiler_tests_rust::compiler_tests_source; pub use crate::v1_compiler_dag_collect_support::connective_name; @@ -176,6 +176,8 @@ pub use crate::v1_compiler_infer::{ expand_type_for_field_access, expr_span, extend_scope, is_where_refinement_type, resolved_type_name, }; +pub use crate::v1_compiler_infer_emit_info::DataVariantWireSpelling; +use crate::v1_compiler_infer_emit_info::DataVariantWireSpelling::*; use crate::v1_compiler_infer_emit_info::TypeRepr::{EnumRepr, StructRepr}; pub use crate::v1_compiler_infer_emit_info::{ collect_type_node_import_surface_names, collect_type_node_import_surface_occurrences, @@ -514,6 +516,17 @@ pub fn render_rust_arrow_type_in_scope( } } +pub fn rust_overlayless_alias_leaf_requires_peel(env: Rc, n: Rc) -> bool { + { + let name = crate::v1_std_core::authored_name_at(env.source_indices.clone(), n.clone()); + (((((n.connective.clone() == Connective::NoConnective) + && ((n.children.clone().len() as i64) == 0)) + && v1_rt::contains(name.clone(), ".".to_string())) + && (crate::v1_std_core::qualified_last_segment(name.clone()) == "String".to_string())) + && closed_alias_peels_zero_param(env.clone(), n.clone())) + } +} + pub fn render_rust_type_without_applied_binding( n: Rc, shared_types: Rc>, @@ -750,10 +763,37 @@ pub fn render_rust_type_without_applied_binding( render_rust_text_carrier(shared_types.clone()), ) } else { - match crate::v1_compiler_coercion::realization_host_numeric_spelling(type_reference_realization_in_env(n.clone(), crate::v1_std_core::qualified_last_segment(tn.clone()), emit_info.fn_type_env.clone(), source_indices.clone())) { + if rust_overlayless_alias_leaf_requires_peel( + emit_info.fn_type_env.clone(), + n.clone(), + ) { + match crate::v1_compiler_infer_env::lookup_type_for( + emit_info.fn_type_env.clone(), + n.clone(), + ) { + Some(binding) => render_rust_type( + crate::v1_compiler_infer_types::resolved_type( + binding.clone(), + ), + shared_types.clone(), + source_indices.clone(), + emit_info.clone(), + ), + std::option::Option::None => { + crate::v1_compiler_emit::render_node_type( + n.clone(), + RenderTarget::Rust, + shared_types.clone(), + source_indices.clone(), + ) + } + } + } else { + match crate::v1_compiler_coercion::realization_host_numeric_spelling(type_reference_realization_in_env(n.clone(), crate::v1_std_core::qualified_last_segment(tn.clone()), emit_info.fn_type_env.clone(), source_indices.clone())) { Some(host) => rust_carrier_optional_wrap(n.clone(), host.clone()), std::option::Option::None => crate::v1_compiler_emit::render_node_type(n.clone(), RenderTarget::Rust, shared_types.clone(), source_indices.clone()), } + } } } } @@ -1080,24 +1120,35 @@ pub fn type_reference_provenance_in_env( crate::v1_std_core::type_reference_provenance(n.clone()) } _ => { - let leaf = rust_fn_sig_leaf_name(source_indices.clone(), n.clone()); - if (leaf.clone() == "".to_string()) { + let node_is_own_declaration = ((n.connective.clone() == Connective::Conj) + || (n.connective.clone() == Connective::Disj)); + if node_is_own_declaration.clone() { crate::v1_std_core::type_reference_provenance(n.clone()) } else { - match crate::v1_compiler_infer_env::lookup_type_by_name(env.clone(), leaf.clone()) { - Some(decl) => { - if (crate::v1_std_core::authored_name_at( - source_indices.clone(), - decl.clone(), - ) == leaf.clone()) - { - crate::v1_std_core::declaration_provenance_of(decl.clone()) - } else { - crate::v1_std_core::type_reference_provenance(n.clone()) - } - } - std::option::Option::None => { + { + let leaf = rust_fn_sig_leaf_name(source_indices.clone(), n.clone()); + if (leaf.clone() == "".to_string()) { crate::v1_std_core::type_reference_provenance(n.clone()) + } else { + match crate::v1_compiler_infer_env::lookup_type_by_name( + env.clone(), + leaf.clone(), + ) { + Some(decl) => { + if (crate::v1_std_core::authored_name_at( + source_indices.clone(), + decl.clone(), + ) == leaf.clone()) + { + crate::v1_std_core::declaration_provenance_of(decl.clone()) + } else { + crate::v1_std_core::type_reference_provenance(n.clone()) + } + } + std::option::Option::None => { + crate::v1_std_core::type_reference_provenance(n.clone()) + } + } } } } @@ -1298,11 +1349,23 @@ pub fn is_host_text_carrier_type( } let nm = crate::v1_std_core::authored_name_at(source_indices.clone(), n.clone()); if (nm.clone() == "String".to_string()) { - true + !crate::v1_compiler_coercion::provenance_declares_structurally( + "String".to_string(), + crate::v1_std_core::type_reference_provenance(n.clone()), + ) } else { if ((nm.clone() == "FreeMonoid".to_string()) || (nm.clone() == "List".to_string())) { - (rust_host_text_carrier_elem_name(n.clone(), source_indices.clone()) - == "Char".to_string()) + if (rust_host_text_carrier_elem_name(n.clone(), source_indices.clone()) + != "Char".to_string()) + { + false + } else { + match (*crate::v1_std_core::type_reference_provenance(n.clone())).clone() { + TypeDeclarationProvenance::CorpusDeclared { decl_file: _, .. } => false, + TypeDeclarationProvenance::KernelMinted { minted_name: _, .. } => true, + TypeDeclarationProvenance::DeclarationIdentityAbsent => true, + } + } } else { false } @@ -1735,7 +1798,12 @@ pub fn closed_alias_verdict_do_not_peel() -> ClosedAliasPeelVerdict { pub fn closed_alias_peel_verdict(env: Rc, n: Rc) -> ClosedAliasPeelVerdict { { let name = crate::v1_std_core::authored_name_at(env.source_indices.clone(), n.clone()); - if (name.clone() == "String".to_string()) { + if ((name.clone() == "String".to_string()) + && !crate::v1_compiler_coercion::provenance_declares_structurally( + "String".to_string(), + crate::v1_std_core::type_reference_provenance(n.clone()), + )) + { return closed_alias_verdict_do_not_peel(); } let binding = match crate::v1_compiler_infer_env::lookup_type_for(env.clone(), n.clone()) { @@ -5003,6 +5071,294 @@ pub fn resolve_local_coproduct_wire_policy( } } +pub fn resolve_emission_coproduct_wire_policy( + coproduct_name: String, + item: Rc, + wire_items: Rc>>, + wire_imports: Rc>>, + wire_contract_item: Option>, + scoped_data_items: Rc>>>>, + source_indices: Rc>>, +) -> Rc { + { + let all_unit_variants = { + let mut __all = true; + for child in item.children.clone().iter().cloned() { + if !((child.children.clone().len() as i64) == 0) { + __all = false; + break; + } + } + __all + }; + match resolve_local_coproduct_wire_policy( + coproduct_name.clone(), + all_unit_variants.clone(), + wire_items.clone(), + wire_imports.clone(), + source_indices.clone(), + ) { + Some(local_policy) => local_policy.clone(), + std::option::Option::None => { + if all_unit_variants.clone() { + match wire_contract_item.clone() { + Some(_) => resolve_wire_serde_policy_for_coproduct( + wire_contract_item.clone(), + source_indices.clone(), + scoped_data_items.clone(), + ), + std::option::Option::None => rust_tagged_object_policy(), + } + } else { + rust_tagged_object_policy() + } + } + } + } +} + +pub fn data_path_wire_variant_tag( + authored: String, + policy: Rc, +) -> Option { + if (policy.error_message.clone() != std::option::Option::None) { + std::option::Option::None + } else { + match policy.rename_style.clone() { + Some(_) => wire_variant_tag_for_policy(authored.clone(), policy.clone()), + std::option::Option::None => { + if v1_rt::contains( + policy.enum_attr.clone(), + "rename_all = \"snake_case\"".to_string(), + ) { + Some(crate::v1_compiler_emit_core_support::to_snake( + authored.clone(), + )) + } else { + if v1_rt::contains( + policy.enum_attr.clone(), + "rename_all = \"SCREAMING_SNAKE_CASE\"".to_string(), + ) { + Some(crate::v1_compiler_emit_core_support::to_screaming_snake( + authored.clone(), + )) + } else { + Some(authored.clone()) + } + } + } + } + } +} + +pub fn project_data_variant_spelling( + authored_variant: String, + policy: Rc, +) -> Rc { + match policy.error_message.clone() { + Some(message) => Rc::new(DataVariantWireSpelling::DataVariantSpellingRefused { + reason: message.clone(), + }), + std::option::Option::None => { + if policy_is_untagged(policy.clone()) { + Rc::new(DataVariantWireSpelling::DataVariantUntagged) + } else { + match data_path_wire_variant_tag(authored_variant.clone(), policy.clone()) { + std::option::Option::None => { + Rc::new(DataVariantWireSpelling::DataVariantSpellingRefused { + reason: v1_rt::concat( + v1_rt::concat( + "no wire tag derivable for variant ".to_string(), + authored_variant.clone(), + ), + " under the parent coproduct's naming policy".to_string(), + ), + }) + } + Some(tag) => { + if policy_is_string_variant(policy.clone()) { + Rc::new(DataVariantWireSpelling::DataVariantBareString { + tag: tag.clone(), + }) + } else { + match policy_serde_tag_field(policy.clone()) { + Some(tag_field) => Rc::new(DataVariantWireSpelling::DataVariantInternalTagged { + tag_field: tag_field.clone(), + tag: tag.clone(), +}), + std::option::Option::None => Rc::new(DataVariantWireSpelling::DataVariantSpellingRefused { + reason: v1_rt::concat(v1_rt::concat("wire policy for variant ".to_string(), authored_variant.clone()), " carries no tag field and is neither untagged nor string-variant".to_string()), +}), +} + } + } + } + } + } + } +} + +pub fn data_variant_wire_spelling_eq( + a: Rc, + b: Rc, +) -> bool { + match (*a.clone()).clone() { + DataVariantWireSpelling::DataVariantInternalTagged { + tag_field: a_tf, + tag: a_t, + .. + } => match (*b.clone()).clone() { + DataVariantWireSpelling::DataVariantInternalTagged { + tag_field: b_tf, + tag: b_t, + .. + } => ((a_tf.clone() == b_tf.clone()) && (a_t.clone() == b_t.clone())), + _ => false, + }, + DataVariantWireSpelling::DataVariantBareString { tag: a_t, .. } => { + match (*b.clone()).clone() { + DataVariantWireSpelling::DataVariantBareString { tag: b_t, .. } => { + (a_t.clone() == b_t.clone()) + } + _ => false, + } + } + DataVariantWireSpelling::DataVariantUntagged => match (*b.clone()).clone() { + DataVariantWireSpelling::DataVariantUntagged => true, + _ => false, + }, + DataVariantWireSpelling::DataVariantSpellingRefused { reason: a_r, .. } => { + match (*b.clone()).clone() { + DataVariantWireSpelling::DataVariantSpellingRefused { reason: b_r, .. } => { + (a_r.clone() == b_r.clone()) + } + _ => false, + } + } + } +} + +pub fn insert_data_variant_spelling( + index: Rc>>, + key: String, + spelling: Rc, + coproduct_name: String, +) -> Rc>> { + match v1_rt::map_get(&index, key.clone()) { + std::option::Option::None => { + v1_rt::rc_map_insert(index.clone(), key.clone(), spelling.clone()) + } + Some(existing) => { + if data_variant_wire_spelling_eq(existing.clone(), spelling.clone()) { + index.clone() + } else { + v1_rt::rc_map_insert(index.clone(), key.clone(), Rc::new(DataVariantWireSpelling::DataVariantSpellingRefused { + reason: v1_rt::concat(v1_rt::concat("ambiguous wire policy for coproduct ".to_string(), coproduct_name.clone()), ": two modules in the emission closure declare it and their resolved policies differ, so a bare parent_enum cannot name one policy".to_string()), +})) + } + } + } +} + +pub fn build_data_variant_wire_spellings( + modules: Rc>>, + data_items: Rc>>, + module_index: Rc, +) -> Rc>> { + modules.clone().iter().cloned().fold( + v1_rt::rc_empty_map::>(), + |acc: Rc>>, tm: Rc| { + let si = tm.type_env.clone().source_indices.clone(); + let module_name = crate::v1_std_core::authored_name_at(si.clone(), tm.module.clone()); + let contracts_imports = contracts_imports_for_module( + module_name.clone(), + modules.clone(), + si.clone(), + module_index.clone(), + ); + let wire_items = v1_rt::concat( + tm.items.clone(), + contracts_items_for_module( + module_name.clone(), + modules.clone(), + si.clone(), + module_index.clone(), + ), + ); + let wire_imports = v1_rt::concat( + crate::v1_std_core::module_imports(tm.module.clone()), + contracts_imports.clone(), + ); + let scoped_data = augment_scoped_data_item_index_with_imports( + build_scoped_data_item_index(tm.clone(), data_items.clone()), + contracts_imports.clone(), + si.clone(), + data_items.clone(), + ); + let wire_contract_item = Rc::new({ + let mut __result = Vec::new(); + for i in wire_items.iter().cloned() { + if ((i.module_item_kind.clone() == ParsedModuleItemKind::ModuleItemDataValue) + && (crate::v1_std_core::authored_name_at(si.clone(), i.clone()) + == "wire_contract".to_string())) + { + __result.push(i); + } + } + __result + }) + .first() + .cloned(); + Rc::new({ + let mut __result = Vec::new(); + for item in tm.items.clone().iter().cloned() { + if (crate::v1_compiler_emit_core_support::is_type_def_item(item.clone()) + && crate::v1_compiler_infer_types::is_coproduct_type(item.clone())) + { + __result.push(item); + } + } + __result + }) + .iter() + .cloned() + .fold( + acc, + |item_acc: Rc>>, item: Rc| { + let coproduct_name = + crate::v1_std_core::authored_name_at(si.clone(), item.clone()); + let policy = resolve_emission_coproduct_wire_policy( + coproduct_name.clone(), + item.clone(), + wire_items.clone(), + wire_imports.clone(), + wire_contract_item.clone(), + scoped_data.clone(), + si.clone(), + ); + item.children.clone().iter().cloned().fold( + item_acc, + |variant_acc: Rc>>, + variant: Rc| { + let variant_name = + crate::v1_std_core::authored_name_at(si.clone(), variant.clone()); + insert_data_variant_spelling( + variant_acc, + v1_rt::concat( + v1_rt::concat(coproduct_name.clone(), ".".to_string()), + variant_name.clone(), + ), + project_data_variant_spelling(variant_name.clone(), policy.clone()), + coproduct_name.clone(), + ) + }, + ) + }, + ) + }, + ) +} + pub fn build_data_item_index(modules: Rc>>) -> Rc>> { modules.iter().cloned().fold( v1_rt::rc_empty_map::>(), @@ -6095,10 +6451,20 @@ pub fn build_emit_rust_context(typed: Rc) -> Rc base_info.type_decl_items.clone(), merged_module_source_indices(typed.modules.clone()), ); + let data_items = build_data_item_index(typed.modules.clone()); + let module_index = build_module_index( + typed.modules.clone(), + base_info.item_leaf_owner_modules.clone(), + ); let emit_info = Rc::new(EmitGraphInfo { item_leaf_owner_modules: base_info.item_leaf_owner_modules.clone(), type_summaries: base_info.type_summaries.clone(), type_decl_items: base_info.type_decl_items.clone(), + data_variant_wire_spellings: build_data_variant_wire_spellings( + typed.modules.clone(), + data_items.clone(), + module_index.clone(), + ), fn_decl_items: base_info.fn_decl_items.clone(), recursive_type_set: base_info.recursive_type_set.clone(), fielded_variants: base_info.fielded_variants.clone(), @@ -6119,7 +6485,6 @@ pub fn build_emit_rust_context(typed: Rc) -> Rc expected_type: std::option::Option::None, }); let registry = typed.item_registry.clone(); - let data_items = build_data_item_index(typed.modules.clone()); let workflow_funcs = collect_workflow_funcs( typed.modules.clone(), registry.clone(), @@ -6193,10 +6558,6 @@ pub fn build_emit_rust_context(typed: Rc) -> Rc let test_projections = crate::v1_compiler_emit_core_support::extract_test_projections(typed.clone()); let export_sets = build_module_export_sets(typed.modules.clone()); - let module_index = build_module_index( - typed.modules.clone(), - emit_info.item_leaf_owner_modules.clone(), - ); let unlisted_type_names_by_module = group_unlisted_type_names(typed.diagnostics.clone()); Rc::new(EmitRustContext { emit_info: emit_info.clone(), @@ -7221,10 +7582,20 @@ pub fn emit_module( base_info.type_decl_items.clone(), merged_module_source_indices(Rc::new(vec![typed_module.clone()])), ); + let data_items = build_data_item_index(Rc::new(vec![typed_module.clone()])); + let module_index = build_module_index( + Rc::new(vec![typed_module.clone()]), + base_info.item_leaf_owner_modules.clone(), + ); let emit_info = Rc::new(EmitGraphInfo { item_leaf_owner_modules: base_info.item_leaf_owner_modules.clone(), type_summaries: base_info.type_summaries.clone(), type_decl_items: base_info.type_decl_items.clone(), + data_variant_wire_spellings: build_data_variant_wire_spellings( + Rc::new(vec![typed_module.clone()]), + data_items.clone(), + module_index.clone(), + ), fn_decl_items: base_info.fn_decl_items.clone(), recursive_type_set: base_info.recursive_type_set.clone(), fielded_variants: base_info.fielded_variants.clone(), @@ -7246,17 +7617,13 @@ pub fn emit_module( }); let shared_types = emit_info.shared_types.clone(); let export_sets = build_module_export_sets(Rc::new(vec![typed_module.clone()])); - let module_index = build_module_index( - Rc::new(vec![typed_module.clone()]), - emit_info.item_leaf_owner_modules.clone(), - ); emit_module_full( typed_module.clone(), registry.clone(), emit_info.clone(), shared_types.clone(), v1_rt::rc_empty_map::(), - build_data_item_index(Rc::new(vec![typed_module.clone()])), + data_items.clone(), export_sets.clone(), Rc::new(vec![typed_module.clone()]), module_index.clone(), @@ -9904,45 +10271,46 @@ pub fn qualified_type_reference_rows( })]) } ItemLookup::ItemLeafAmbiguous { leaf: _, .. } => { - Rc::new(vec![Rc::new(ReferenceDerivedCandidateRow { - module_name: this_module_name.clone(), - name: name.clone(), - disposition: Rc::new(ReferenceDerivedCandidateDisposition::CandidateLeafAmbiguous), -})]) - } - ItemLookup::ItemFound { info: info, .. } => { - if !registry_row_is_type_declared_in( - info.clone(), - qualifier.clone(), + match crate::v1_compiler_emit::lookup_item_by_identity( + registry.clone(), + Rc::new(DeclaredCallableIdentity { + owner_module_path: qualifier.clone(), + decl_name: leaf.clone(), + }), ) { - Rc::new(vec![]) - } else { - if provider_proven_exports_symbol( + Some(info) => qualified_type_reference_row_for_info( + name.clone(), leaf.clone(), qualifier.clone(), + this_module_name.clone(), + info.clone(), export_sets.clone(), typed_modules.clone(), source_indices.clone(), module_index.clone(), - ) { - Rc::new(vec![Rc::new(ReferenceDerivedCandidateRow { - module_name: this_module_name.clone(), - name: name.clone(), - disposition: Rc::new(ReferenceDerivedCandidateDisposition::CandidateSurvived { - provider_module: qualifier.clone(), -}), -})]) - } else { + ), + std::option::Option::None => { Rc::new(vec![Rc::new(ReferenceDerivedCandidateRow { module_name: this_module_name.clone(), name: name.clone(), - disposition: Rc::new(ReferenceDerivedCandidateDisposition::CandidateExportProofFailed { - provider_module: qualifier.clone(), -}), + disposition: Rc::new(ReferenceDerivedCandidateDisposition::CandidateLeafAmbiguous), })]) } } } + ItemLookup::ItemFound { info: info, .. } => { + qualified_type_reference_row_for_info( + name.clone(), + leaf.clone(), + qualifier.clone(), + this_module_name.clone(), + info.clone(), + export_sets.clone(), + typed_modules.clone(), + source_indices.clone(), + module_index.clone(), + ) + } } } } @@ -9956,6 +10324,49 @@ pub fn qualified_type_reference_rows( } } +pub fn qualified_type_reference_row_for_info( + name: String, + leaf: String, + qualifier: String, + this_module_name: String, + info: Rc, + export_sets: Rc>>>, + typed_modules: Rc>>, + source_indices: Rc>>, + module_index: Rc, +) -> Rc>> { + if !registry_row_is_type_declared_in(info.clone(), qualifier.clone()) { + Rc::new(vec![]) + } else { + if provider_proven_exports_symbol( + leaf.clone(), + qualifier.clone(), + export_sets.clone(), + typed_modules.clone(), + source_indices.clone(), + module_index.clone(), + ) { + Rc::new(vec![Rc::new(ReferenceDerivedCandidateRow { + module_name: this_module_name.clone(), + name: name.clone(), + disposition: Rc::new(ReferenceDerivedCandidateDisposition::CandidateSurvived { + provider_module: qualifier.clone(), + }), + })]) + } else { + Rc::new(vec![Rc::new(ReferenceDerivedCandidateRow { + module_name: this_module_name.clone(), + name: name.clone(), + disposition: Rc::new( + ReferenceDerivedCandidateDisposition::CandidateExportProofFailed { + provider_module: qualifier.clone(), + }, + ), + })]) + } + } +} + pub fn registry_row_is_type_declared_in(info: Rc, module_name: String) -> bool { if (info.module_name.clone() != module_name.clone()) { false @@ -10100,6 +10511,7 @@ pub fn emit_module_full( item_leaf_owner_modules: emit_info.item_leaf_owner_modules.clone(), type_summaries: emit_info.type_summaries.clone(), type_decl_items: emit_info.type_decl_items.clone(), + data_variant_wire_spellings: emit_info.data_variant_wire_spellings.clone(), fn_decl_items: emit_info.fn_decl_items.clone(), recursive_type_set: emit_info.recursive_type_set.clone(), fielded_variants: emit_info.fielded_variants.clone(), @@ -12080,16 +12492,28 @@ pub fn alias_rhs_base_module_filename( ) } } - std::option::Option::None => alias_rhs_base_module_from_import_or_registry( - name.clone(), - imports.clone(), - source_indices.clone(), - registry.clone(), - local_mod.clone(), - export_sets.clone(), - typed_modules.clone(), - module_index.clone(), - ), + std::option::Option::None => { + if has_physical_type_def_in_module_filename( + name.clone(), + local_mod.clone(), + typed_modules.clone(), + source_indices.clone(), + module_index.clone(), + ) { + local_mod.clone() + } else { + alias_rhs_base_module_from_import_or_registry( + name.clone(), + imports.clone(), + source_indices.clone(), + registry.clone(), + local_mod.clone(), + export_sets.clone(), + typed_modules.clone(), + module_index.clone(), + ) + } + } } } } @@ -15209,6 +15633,7 @@ pub fn emit_typed_item( item_leaf_owner_modules: emit_info.item_leaf_owner_modules.clone(), type_summaries: emit_info.type_summaries.clone(), type_decl_items: emit_info.type_decl_items.clone(), + data_variant_wire_spellings: emit_info.data_variant_wire_spellings.clone(), fn_decl_items: emit_info.fn_decl_items.clone(), recursive_type_set: emit_info.recursive_type_set.clone(), fielded_variants: emit_info.fielded_variants.clone(), @@ -15310,6 +15735,7 @@ pub fn emit_typed_item( registry.clone(), shared_types.clone(), env.clone(), + emit_info.clone(), ) } } @@ -15855,41 +16281,15 @@ pub fn emit_type_def_from_connective( } } else { { - let all_unit_variants = { - let mut __all = true; - for child in item.children.clone().iter().cloned() { - if !((child.children.clone().len() as i64) == 0) { - __all = false; - break; - } - } - __all - }; - let serde_policy = match resolve_local_coproduct_wire_policy( + let serde_policy = resolve_emission_coproduct_wire_policy( item_text.clone(), - all_unit_variants.clone(), + item.clone(), module_items.clone(), imports.clone(), + wire_contract_item.clone(), + data_items.clone(), env.source_indices.clone(), - ) { - Some(local_policy) => local_policy.clone(), - std::option::Option::None => { - if all_unit_variants.clone() { - match wire_contract_item.clone() { - Some(_) => resolve_wire_serde_policy_for_coproduct( - wire_contract_item.clone(), - env.source_indices.clone(), - data_items.clone(), - ), - std::option::Option::None => { - rust_tagged_object_policy() - } - } - } else { - rust_tagged_object_policy() - } - } - }; + ); let rename_validations = variant_rename_validations_for_policy( item.children.clone(), env.clone(), @@ -18092,6 +18492,7 @@ pub fn v1_call_forwarding_clone_bound_param_names( emit_info: Rc, shared_types: Rc>, source_indices: Rc>>, + visited: Rc>, ) -> Rc> { stacker::maybe_grow(512 * 1024, 2 * 1024 * 1024, || { if ((generic_param_names.clone().len() as i64) == 0) { @@ -18105,6 +18506,7 @@ pub fn v1_call_forwarding_clone_bound_param_names( emit_info.clone(), shared_types.clone(), source_indices.clone(), + visited.clone(), ), _ => Rc::new(vec![]), }; @@ -18120,6 +18522,7 @@ pub fn v1_call_forwarding_clone_bound_param_names( emit_info.clone(), shared_types.clone(), source_indices.clone(), + visited.clone(), ), ) }, @@ -18134,10 +18537,23 @@ pub fn v1_call_site_clone_forwarded_param_names( emit_info: Rc, shared_types: Rc>, source_indices: Rc>>, + visited: Rc>, ) -> Rc> { { let si = source_indices.clone(); let callee_name = crate::v1_std_core::expr_call_func_at(call.clone(), si.clone()); + if { + let mut __found = false; + for v in visited.iter().cloned() { + if (v.clone() == callee_name.clone()) { + __found = true; + break; + } + } + __found + } { + return Rc::new(vec![]); + } match v1_rt::map_get(&fn_decl_items, callee_name.clone()) { std::option::Option::None => Rc::new(vec![]), Some(callee_item) => match callee_item.body.clone() { @@ -18155,14 +18571,26 @@ pub fn v1_call_site_clone_forwarded_param_names( } __result }); - let callee_clone_param_names = v1_fn_body_derived_clone_param_names( - callee_params.clone(), - crate::v1_compiler_infer_types::resolved_type(callee_item.clone()), - callee_body.clone(), - emit_info.clone(), - shared_types.clone(), - si.clone(), - ); + let callee_clone_param_names = + crate::v1_compiler_trait_bound_witness::v1_union_bound_param_names( + v1_fn_body_derived_clone_param_names( + callee_params.clone(), + crate::v1_compiler_infer_types::resolved_type(callee_item.clone()), + callee_body.clone(), + emit_info.clone(), + shared_types.clone(), + si.clone(), + ), + v1_call_forwarding_clone_bound_param_names( + callee_generic_param_names.clone(), + callee_body.clone(), + fn_decl_items.clone(), + emit_info.clone(), + shared_types.clone(), + si.clone(), + v1_rt::concat(visited.clone(), Rc::new(vec![callee_name.clone()])), + ), + ); v1_call_forwarding_forwarded_param_names( call.clone(), function_value_params(callee_params.clone()), @@ -18385,6 +18813,7 @@ pub fn emit_fn_def( emit_info.clone(), shared_types.clone(), si.clone(), + Rc::new(vec![name.clone()]), ); let derived_clone_param_names_with_rc_match = crate::v1_compiler_trait_bound_witness::v1_union_bound_param_names( @@ -25809,6 +26238,9 @@ pub fn emit_rust_fold_method_call( item_leaf_owner_modules: emit_info.item_leaf_owner_modules.clone(), type_summaries: emit_info.type_summaries.clone(), type_decl_items: emit_info.type_decl_items.clone(), + data_variant_wire_spellings: emit_info + .data_variant_wire_spellings + .clone(), fn_decl_items: emit_info.fn_decl_items.clone(), recursive_type_set: emit_info.recursive_type_set.clone(), fielded_variants: emit_info.fielded_variants.clone(), @@ -25855,6 +26287,9 @@ pub fn emit_rust_fold_method_call( item_leaf_owner_modules: emit_info.item_leaf_owner_modules.clone(), type_summaries: emit_info.type_summaries.clone(), type_decl_items: emit_info.type_decl_items.clone(), + data_variant_wire_spellings: emit_info + .data_variant_wire_spellings + .clone(), fn_decl_items: emit_info.fn_decl_items.clone(), recursive_type_set: emit_info.recursive_type_set.clone(), fielded_variants: emit_info.fielded_variants.clone(), @@ -33004,6 +33439,7 @@ pub fn emit_service_def( registry: Rc>>, shared_types: Rc>, env: Rc, + emit_info: Rc, ) -> String { { let safe_name = crate::v1_compiler_emit_core_support::sanitize_service_name( @@ -33026,6 +33462,7 @@ pub fn emit_service_def( shared_types.clone(), env.clone(), item.clone(), + emit_info.clone(), ); v1_rt::concat( v1_rt::concat(struct_def.clone(), "\n\n".to_string()), @@ -33120,6 +33557,7 @@ pub fn emit_service_impl( shared_types: Rc>, env: Rc, service_item: Rc, + emit_info: Rc, ) -> String { { let depth = 0; @@ -33142,6 +33580,7 @@ pub fn emit_service_impl( shared_types.clone(), env.clone(), service_item.clone(), + emit_info.clone(), )); } __result @@ -33367,6 +33806,7 @@ pub fn emit_operation_method( shared_types: Rc>, env: Rc, service_item: Rc, + emit_info: Rc, ) -> String { { let op_text = crate::v1_compiler_infer_env::authored_name(env.clone(), op_node.clone()); @@ -33452,6 +33892,7 @@ pub fn emit_operation_method( op_node.clone(), env.clone(), shared_types.clone(), + emit_info.clone(), ); let body = v1_rt::concat( v1_rt::concat( @@ -33538,6 +33979,7 @@ pub fn emit_dry_run_branch_from_props( op_node: Rc, env: Rc, shared_types: Rc>, + emit_info: Rc, ) -> String { { let log_line = v1_rt::concat( @@ -33548,7 +33990,7 @@ pub fn emit_dry_run_branch_from_props( { let first_mock = mock_props.clone().first().cloned(); match first_mock.clone() { - Some(mp) => match (*crate::v1_compiler_emit::emit_data_value_json(crate::v1_std_core::field_init_node_value(mp.clone()), source_indices.clone())).clone() { + Some(mp) => match (*crate::v1_compiler_emit::emit_data_value_json(crate::v1_std_core::field_init_node_value(mp.clone()), source_indices.clone(), emit_info.data_variant_wire_spellings.clone())).clone() { EmitterOutcome::Refused { reason: r, .. } => v1_rt::concat(v1_rt::concat(v1_rt::concat(log_line.clone(), "\ncompile_error!(\"".to_string()), crate::v1_compiler_emit_core_support::escape_string_literal_body(r.clone())), "\");".to_string()), EmitterOutcome::Emitted { json: mock_json, .. } => { let is_multi_field_conj = ((inferred.connective.clone() == Connective::Conj) && ((inferred.children.clone().len() as i64) > 1)); @@ -36497,6 +36939,7 @@ pub fn emit_data_def_body( match (*crate::v1_compiler_emit::emit_data_value_json( value.clone(), scope.type_env.clone().source_indices.clone(), + emit_info.data_variant_wire_spellings.clone(), )) .clone() { diff --git a/src/v1/stage0/src/v1_compiler_infer.rs b/src/v1/stage0/src/v1_compiler_infer.rs index 5b456fa6d58..bfedadcfdeb 100644 --- a/src/v1/stage0/src/v1_compiler_infer.rs +++ b/src/v1/stage0/src/v1_compiler_infer.rs @@ -83,6 +83,8 @@ pub use crate::v1_compiler_coercion::provenance_realizes_natively; pub use crate::v1_compiler_infer_access::AccessCheckResultNode; pub use crate::v1_compiler_infer_access::{check_index_access_node, check_slice_access_node}; pub use crate::v1_compiler_infer_cycle::detect_type_cycles_kahn; +pub use crate::v1_compiler_infer_emit_info::DataVariantWireSpelling; +use crate::v1_compiler_infer_emit_info::DataVariantWireSpelling::*; use crate::v1_compiler_infer_emit_info::TypeRepr::{EnumRepr, StructRepr}; pub use crate::v1_compiler_infer_emit_info::{ add_emit_item_summary, build_enum_field_summaries, build_struct_field_summaries, @@ -25817,6 +25819,8 @@ pub fn build_emit_graph_info( crate::v1_compiler_infer_items::leaf_owner_modules_from_registry(registry.clone()), type_summaries: built.type_summaries.clone(), type_decl_items: built.type_decl_items.clone(), + data_variant_wire_spellings: v1_rt::rc_empty_map::>( + ), fn_decl_items: built.fn_decl_items.clone(), recursive_type_set: all_recursive.clone(), fielded_variants: variant_shapes.fielded.clone(), diff --git a/src/v1/stage0/src/v1_compiler_infer_emit_info.rs b/src/v1/stage0/src/v1_compiler_infer_emit_info.rs index 9b268cd43c5..3f6f0bb301d 100644 --- a/src/v1/stage0/src/v1_compiler_infer_emit_info.rs +++ b/src/v1/stage0/src/v1_compiler_infer_emit_info.rs @@ -1,6 +1,7 @@ // Generated by v1 compiler -- do not edit. // Source module: v1.compiler.infer_emit_info +use self::DataVariantWireSpelling::*; use self::TypeRepr::*; pub use crate::std_dissolution::DissolutionCondition; use crate::std_dissolution::DissolutionCondition::*; @@ -194,10 +195,20 @@ pub fn collect_type_node_import_surface_names( })) } +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[serde(tag = "_variant")] +pub enum DataVariantWireSpelling { + DataVariantInternalTagged { tag_field: String, tag: String }, + DataVariantBareString { tag: String }, + DataVariantUntagged, + DataVariantSpellingRefused { reason: String }, +} + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct EmitGraphInfo { pub type_summaries: Rc>>, pub type_decl_items: Rc>>, + pub data_variant_wire_spellings: Rc>>, pub fn_decl_items: Rc>>, pub recursive_type_set: Rc>, pub fielded_variants: Rc>, @@ -233,6 +244,7 @@ pub fn empty_emit_graph_info() -> Rc { item_leaf_owner_modules: v1_rt::rc_empty_map::>(), type_summaries: v1_rt::rc_empty_map::>(), type_decl_items: v1_rt::rc_empty_map::>(), + data_variant_wire_spellings: v1_rt::rc_empty_map::>(), fn_decl_items: v1_rt::rc_empty_map::>(), recursive_type_set: v1_rt::rc_empty_set::(), fielded_variants: v1_rt::rc_empty_set::(), @@ -263,6 +275,7 @@ pub fn emit_info_with_fn_type_context( item_leaf_owner_modules: emit_info.item_leaf_owner_modules.clone(), type_summaries: emit_info.type_summaries.clone(), type_decl_items: emit_info.type_decl_items.clone(), + data_variant_wire_spellings: emit_info.data_variant_wire_spellings.clone(), fn_decl_items: emit_info.fn_decl_items.clone(), recursive_type_set: emit_info.recursive_type_set.clone(), fielded_variants: emit_info.fielded_variants.clone(), @@ -292,6 +305,7 @@ pub fn emit_info_with_fn_return( item_leaf_owner_modules: emit_info.item_leaf_owner_modules.clone(), type_summaries: emit_info.type_summaries.clone(), type_decl_items: emit_info.type_decl_items.clone(), + data_variant_wire_spellings: emit_info.data_variant_wire_spellings.clone(), fn_decl_items: emit_info.fn_decl_items.clone(), recursive_type_set: emit_info.recursive_type_set.clone(), fielded_variants: emit_info.fielded_variants.clone(), @@ -321,6 +335,7 @@ pub fn emit_info_with_expected_type( item_leaf_owner_modules: emit_info.item_leaf_owner_modules.clone(), type_summaries: emit_info.type_summaries.clone(), type_decl_items: emit_info.type_decl_items.clone(), + data_variant_wire_spellings: emit_info.data_variant_wire_spellings.clone(), fn_decl_items: emit_info.fn_decl_items.clone(), recursive_type_set: emit_info.recursive_type_set.clone(), fielded_variants: emit_info.fielded_variants.clone(), diff --git a/src/v2/compiler/00_compile.dag b/src/v2/compiler/00_compile.dag index 40b3478d3f7..d0bbd203a8b 100644 --- a/src/v2/compiler/00_compile.dag +++ b/src/v2/compiler/00_compile.dag @@ -57,7 +57,7 @@ import v2.std.diagnostic { } import v2.std.node { Edge, Node, NodeFold, Symbol, fold_node } import v2.std.text { String } -import v2.std.witness { Holds, Violates, Witness } +import v2.std.witness { Holds, Violates, Witness, witness_violates } // THIS MODULE IS THE COMPILER PIPELINE ENTRY. It carries the fact rather than being recognised // by name: the emitter selected its entry-point arm by matching "v1.compiler.compile", a @@ -218,7 +218,7 @@ fn apply_compile_lens_introspect( outcome_with_diagnostics(value: w, diagnostics: od) Rejected { diagnostics: d } => outcome_with_diagnostics( - value: Violates { diagnostic: d.head }, + value: witness_violates(diagnostic: d.head), diagnostics: Some { diagnostics: d } ) } diff --git a/src/v2/compiler/01_tokenize.dag b/src/v2/compiler/01_tokenize.dag index bfd51d9f45f..84bab6dee0a 100644 --- a/src/v2/compiler/01_tokenize.dag +++ b/src/v2/compiler/01_tokenize.dag @@ -522,7 +522,7 @@ fn lex_walk_step(acc: LexWalkAcc) -> LexWalkAcc { tokens: Cons { head: Token { class: class, - lexeme: lexeme, + lexeme: chars_to_string(chars: lexeme, start: 0, end: length(xs: lexeme)), file: acc.file, start: acc.pos, end: acc.pos + length(xs: lexeme) @@ -559,7 +559,7 @@ fn lex_walk_step(acc: LexWalkAcc) -> LexWalkAcc { annotations: Cons { head: UnboundSourceAnnotation { annotation_class: class, - lexeme: lexeme, + lexeme: chars_to_string(chars: lexeme, start: 0, end: length(xs: lexeme)), placement: placement_from_line_prefix(indent_only: acc.line_prefix_indent_only), file: acc.file, start: acc.pos, diff --git a/src/v2/compiler/02_parse.dag b/src/v2/compiler/02_parse.dag index 73bdc04cb71..3f1e55fa8f3 100644 --- a/src/v2/compiler/02_parse.dag +++ b/src/v2/compiler/02_parse.dag @@ -86,7 +86,6 @@ import v2.std.provenance { import v2.std.collection { List, Map, - PointwisePower, empty_map, list_at_optional, map_get, @@ -273,7 +272,7 @@ type ParseTableRealization { memo_hits: Int memo_misses: Int memo_lookup_calls: Int - nullable_set: PointwisePower + nullable_set: List first_rows: Map> productions_by_name: Map } @@ -668,7 +667,7 @@ fn parse_table_empty() -> ParseTable { memo_hits: 0, memo_misses: 0, memo_lookup_calls: 0, - nullable_set: PointwisePower { member: fn(_) { false } }, + nullable_set: std.algebra.Empty, first_rows: empty_map(), productions_by_name: empty_map() } @@ -1208,18 +1207,20 @@ fn parse_nonterminal_memoized_core( } } -fn set_symbol_insert(s: PointwisePower, item: Symbol) -> PointwisePower { - PointwisePower { - member: fn(sym) { sym == item || s.member(sym) } +fn set_symbol_insert(s: List, item: Symbol) -> List { + if symbol_list_contains(xs: s, item: item) { + s + } else { + list_snoc_item(xs: s, item: item) } } -fn expr_is_nullable_given_set(expr: GrammarExpr, nullable_set: PointwisePower) -> Bool { +fn expr_is_nullable_given_set(expr: GrammarExpr, nullable_set: List) -> Bool { fold_grammar_expr( expr: expr, algebra: GrammarExprFold { terminal: fn(_, _) { false }, - nonterminal: fn(name) { nullable_set.member(name) }, + nonterminal: fn(name) { symbol_list_contains(xs: nullable_set, item: name) }, sequence: fn(left_nullable, right_nullable) { left_nullable && right_nullable }, choice: fn(left_nullable, right_nullable) { left_nullable || right_nullable }, optional: fn(_) { true }, @@ -1228,7 +1229,7 @@ fn expr_is_nullable_given_set(expr: GrammarExpr, nullable_set: PointwisePower, p: GrammarProduction) -> PointwisePower { +fn compute_nullable_step(ns: List, p: GrammarProduction) -> List { if expr_is_nullable_given_set(expr: p.expression, nullable_set: ns) { set_symbol_insert(s: ns, item: p.name) } else { @@ -1238,7 +1239,7 @@ fn compute_nullable_step(ns: PointwisePower, p: GrammarProduction) -> Po // ONE ROUND of the nullable fixpoint: every production gets a chance to join the set given what // the set already holds. -fn compute_nullable_round(root: GrammarRoot, ns: PointwisePower) -> PointwisePower { +fn compute_nullable_round(root: GrammarRoot, ns: List) -> List { fold_list( xs: root.productions, empty: ns, @@ -1248,21 +1249,11 @@ fn compute_nullable_round(root: GrammarRoot, ns: PointwisePower) -> Poin ) } -// THE CONVERGENCE MEASURE. The carrier is a `PointwisePower` — a characteristic function — so two -// sets cannot be compared directly, and the fixpoint cannot stop by testing `next == previous`. It can -// stop on a MONOTONE MEASURE instead: `compute_nullable_step` only ever inserts, so the number of -// productions the set admits rises until the round adds nothing, and equal counts across a round -// mean equal sets. Counting over `root.productions` is exact here because that is the only -// population `compute_nullable_step` can insert from. -fn nullable_member_count(root: GrammarRoot, ns: PointwisePower) -> Int { - fold_list( - xs: root.productions, - empty: 0, - cons: fn(acc, p) { - if ns.member(p.name) { acc + 1 } else { acc } - } - ) -} +// THE CONVERGENCE MEASURE. The carrier is the enumeration itself — a `List` — so the +// monotone measure is its length directly: `compute_nullable_step` only ever inserts, so the +// number of names the set carries rises until a round adds nothing, and equal counts across a +// round mean equal sets (insertion is de-duplicated by `set_symbol_insert`, so growth is +// membership, never repetition). // ITERATE TO CONVERGENCE, BOUNDED BY THE SAME COUNT THE OLD SHAPE SPENT UNCONDITIONALLY. // @@ -1274,15 +1265,15 @@ fn nullable_member_count(root: GrammarRoot, ns: PointwisePower) -> Int { // a round adds nothing, so the RESULT is identical and only the redundant rounds are gone. fn compute_nullable_fixpoint( root: GrammarRoot, - ns: PointwisePower, + ns: List, seen_count: Int, fuel: Int -) -> PointwisePower { +) -> List { if fuel == 0 { ns } else { let next = compute_nullable_round(root: root, ns: ns) - let next_count = nullable_member_count(root: root, ns: next) + let next_count = length(xs: next) if next_count == seen_count { next } else { @@ -1291,10 +1282,10 @@ fn compute_nullable_fixpoint( } } -fn compute_nullable_set(root: GrammarRoot) -> PointwisePower { +fn compute_nullable_set(root: GrammarRoot) -> List { compute_nullable_fixpoint( root: root, - ns: PointwisePower { member: fn(_) { false } }, + ns: std.algebra.Empty, seen_count: 0, fuel: length(xs: root.productions) ) @@ -1305,8 +1296,17 @@ type GrammarExprFirstFold { nullable: Bool } +// THE NULLABLE CARRIER IS AN ENUMERATION, NOT A PREDICATE — the same repair +// `GrammarRoot.sync_tokens` already took (v2.std.grammar). A grammar's nullable set is a finite +// subset of its production names, fully known at prepare time; carrying it as a +// `PointwisePower` characteristic function made every membership read walk a nested +// closure chain, and made the whole `PreparedGrammar` NON-PORTABLE: the cross-claim pure tier's +// publication walk refuses a closure at any depth (ServeCacheValueNotPortable), so the one fill +// every parse of `.dag` source demands could never store, and every demanding claim recomputed +// it until its own CPU budget stopped the fill mid-flight. The field is the enumeration it +// always was; `first_rows` beside it was already enumeration-shaped. type GrammarFirstAnalysis { - nullable_set: PointwisePower + nullable_set: List first_rows: Map> } @@ -1314,7 +1314,7 @@ fn grammar_empty_terminal_list() -> List { std.algebra.Empty } -fn first_list_contains(xs: List, item: Symbol) -> Bool { +fn symbol_list_contains(xs: List, item: Symbol) -> Bool { contains(xs: xs, item: item, eq: fn(a, b) { a == b }) } @@ -1323,7 +1323,7 @@ fn first_list_union(left: List, right: List) -> List { xs: right, empty: left, cons: fn(acc, sym) { - if first_list_contains(xs: acc, item: sym) { + if symbol_list_contains(xs: acc, item: sym) { acc } else { list_snoc_item(xs: acc, item: sym) @@ -1335,7 +1335,7 @@ fn first_list_union(left: List, right: List) -> List { fn first_list_intersect(left: List, right: List) -> List { filter( xs: left, - predicate: fn(sym) { first_list_contains(xs: right, item: sym) } + predicate: fn(sym) { symbol_list_contains(xs: right, item: sym) } ) } @@ -1356,12 +1356,12 @@ fn expr_first_fold_terminal(token_class: Symbol) -> GrammarExprFirstFold { fn expr_first_fold_nonterminal( name: Symbol, - nullable_set: PointwisePower, + nullable_set: List, first_rows: Map> ) -> GrammarExprFirstFold { GrammarExprFirstFold { terminals: production_first_row_lookup(rows: first_rows, name: name), - nullable: nullable_set.member(name) + nullable: symbol_list_contains(xs: nullable_set, item: name) } } @@ -1392,7 +1392,7 @@ fn expr_first_fold_repeat(element: GrammarExprFirstFold) -> GrammarExprFirstFold fn expr_first_fold( expr: GrammarExpr, - nullable_set: PointwisePower, + nullable_set: List, first_rows: Map> ) -> GrammarExprFirstFold { fold_grammar_expr( @@ -1413,7 +1413,7 @@ fn expr_first_fold( fn compute_production_first_row_terminals( rows: Map>, p: GrammarProduction, - nullable_set: PointwisePower + nullable_set: List ) -> List { expr_first_fold( expr: p.expression, @@ -1424,7 +1424,7 @@ fn compute_production_first_row_terminals( fn compute_production_first_rows_round( root: GrammarRoot, - nullable_set: PointwisePower, + nullable_set: List, rows: Map> ) -> Map> { fold_list( @@ -1461,7 +1461,7 @@ fn production_first_rows_total(root: GrammarRoot, rows: Map fn compute_production_first_rows_fixpoint( root: GrammarRoot, - nullable_set: PointwisePower, + nullable_set: List, rows: Map>, seen_total: Int, fuel: Int @@ -1487,7 +1487,7 @@ fn compute_production_first_rows_fixpoint( fn compute_production_first_rows( root: GrammarRoot, - nullable_set: PointwisePower + nullable_set: List ) -> Map> { compute_production_first_rows_fixpoint( root: root, @@ -1531,7 +1531,7 @@ fn append_choice_ambiguity_row( fn collect_choice_ambiguities_in_expr( expr: GrammarExpr, production: Symbol, - nullable_set: PointwisePower, + nullable_set: List, first_rows: Map>, acc: List ) -> List { @@ -1596,7 +1596,7 @@ fn collect_choice_ambiguities_in_expr( fn compute_grammar_first_analysis_given( root: GrammarRoot, - nullable_set: PointwisePower + nullable_set: List ) -> GrammarFirstAnalysis { GrammarFirstAnalysis { nullable_set: nullable_set, @@ -1698,10 +1698,10 @@ fn expr_corner_fold_terminal(_: Symbol) -> GrammarExprCornerFold { GrammarExprCornerFold { corners: std.algebra.Empty, nullable: false } } -fn expr_corner_fold_nonterminal(name: Symbol, nullable_set: PointwisePower) -> GrammarExprCornerFold { +fn expr_corner_fold_nonterminal(name: Symbol, nullable_set: List) -> GrammarExprCornerFold { GrammarExprCornerFold { corners: Cons { head: name, tail: std.algebra.Empty }, - nullable: nullable_set.member(name) + nullable: symbol_list_contains(xs: nullable_set, item: name) } } @@ -1741,7 +1741,7 @@ fn expr_corner_fold_repeat(element_fold: GrammarExprCornerFold) -> GrammarExprCo GrammarExprCornerFold { corners: element_fold.corners, nullable: true } } -fn expr_corner_nullable_fold(expr: GrammarExpr, nullable_set: PointwisePower) -> GrammarExprCornerFold { +fn expr_corner_nullable_fold(expr: GrammarExpr, nullable_set: List) -> GrammarExprCornerFold { fold_grammar_expr( expr: expr, algebra: GrammarExprFold { @@ -1755,7 +1755,7 @@ fn expr_corner_nullable_fold(expr: GrammarExpr, nullable_set: PointwisePower) -> List { +fn expr_direct_left_corner_list(expr: GrammarExpr, nullable_set: List) -> List { expr_corner_nullable_fold(expr: expr, nullable_set: nullable_set).corners } @@ -1786,7 +1786,7 @@ fn left_corner_list_union(left: List, right: List) -> List) -> Map> { +fn left_corner_direct_rows(root: GrammarRoot, nullable_set: List) -> Map> { fold_list( xs: root.productions, empty: empty_map(), @@ -1867,7 +1867,7 @@ fn left_corner_fixpoint( } } -fn left_corner_rows(root: GrammarRoot, nullable_set: PointwisePower) -> Map> { +fn left_corner_rows(root: GrammarRoot, nullable_set: List) -> Map> { let seeded = left_corner_direct_rows(root: root, nullable_set: nullable_set) left_corner_fixpoint( root: root, @@ -1880,7 +1880,7 @@ fn left_corner_rows(root: GrammarRoot, nullable_set: PointwisePower) -> // The nullable set is a parameter here because `grammar_validate_and_analyze` already computes it // for the FIRST analysis: the previous shape recomputed it inside this check, so one preparation // paid for the same fixpoint three times over. -fn grammar_has_left_recursion_given(root: GrammarRoot, nullable_set: PointwisePower) -> Bool { +fn grammar_has_left_recursion_given(root: GrammarRoot, nullable_set: List) -> Bool { let rows = left_corner_rows(root: root, nullable_set: nullable_set) any( xs: root.productions, @@ -2229,13 +2229,13 @@ fn parse_choice_on_present_token( left_first: GrammarExprFirstFold, right_first: GrammarExprFirstFold ) -> ParseExprResult { - if first_list_contains(xs: left_first.terminals, item: tok.class) && !first_list_contains(xs: right_first.terminals, item: tok.class) { + if symbol_list_contains(xs: left_first.terminals, item: tok.class) && !symbol_list_contains(xs: right_first.terminals, item: tok.class) { parse_expr_with_first(expr: left, tokens: tokens, all_tokens: all_tokens, root: root, table: table, prov: prov) } else { - if first_list_contains(xs: right_first.terminals, item: tok.class) && !first_list_contains(xs: left_first.terminals, item: tok.class) { + if symbol_list_contains(xs: right_first.terminals, item: tok.class) && !symbol_list_contains(xs: left_first.terminals, item: tok.class) { parse_expr_with_first(expr: right, tokens: tokens, all_tokens: all_tokens, root: root, table: table, prov: prov) } else { - if left_first.nullable && first_list_contains(xs: right_first.terminals, item: tok.class) { + if left_first.nullable && symbol_list_contains(xs: right_first.terminals, item: tok.class) { parse_expr_with_first(expr: right, tokens: tokens, all_tokens: all_tokens, root: root, table: table, prov: prov) } else { parse_choice_residue_backtrack( diff --git a/src/v2/compiler/04_infer.dag b/src/v2/compiler/04_infer.dag index 2aec278ef71..ca4fda342e8 100644 --- a/src/v2/compiler/04_infer.dag +++ b/src/v2/compiler/04_infer.dag @@ -1,14 +1,15 @@ module v2.compiler.infer import std.occurrence_identity { OccurrenceSynthetic } -import v2.std.algebra { length, list_snoc_item } +import v2.std.algebra { any, Empty, length, list_snoc_item } import v2.std.bounded_lattice_completeness { infer_bounded_lattice_consumer_gate, partial_bounded_lattice_instances_in_tree } import v2.std.compilers.target_model { canonical_operation_op_add, - canonical_operation_wire_matches_operation + canonical_operation_wire_matches_operation, + target_model_canonical_operations_roster_root } import v2.compiler.resolve { ResolvedTree } import v2.compiler.inferred_tree { @@ -22,8 +23,13 @@ import v2.compiler.inferred_tree { import v2.extdeps.languages.dag { DagCanonicalBoolLiteral, DagCanonicalIntLiteral, - dag_canonical_literal_from_node + dag_binding_denotation, + dag_canonical_literal_from_node, + dag_declared_inhabitants_root, + dag_grammar_productions_roster_root } +import v2.extdeps.languages.python { python_declared_inhabitants_root } +import v2.extdeps.languages.typescript { ts_declared_inhabitants_root } import v2.std.cardinality { TerminationProof, termination_proof_witness_for_node } import v2.std.collection { List, @@ -87,6 +93,7 @@ import v2.std.node { loop_edge_contributes_to_iteration_fold, match_arm_body, match_arm_pattern, + node_subtree_nodes, well_formed, } import v2.std.node_query { find_named_child, node_positional_child_targets } @@ -97,7 +104,7 @@ type AlgebraRef { witness: Node } -data node_grounding_frontier_note: String = "GroundingNotDerived is v2's typed inference frontier, not a failure arm and not an escape hatch. v2 derives a node's type for exactly four of the closed vocabulary's twelve node kinds (Branch, Match, Loop, Transform add-shape only — 6 connectives + 6 behaviors = 12); the other eight kinds, and every other Transform shape, have no derivation rule yet. Before this carrier existed the nine were routed through solve_constraints with the singleton candidate set [root] under a predicate that reduces to root == root, and the resulting grounding carried the SOURCE NODE as its structural evidence — i.e. as the node's resolved type (inferred_facts_resolved_type reads that field), so every literal, binding and operation in v2 was typed as itself and every downstream consumer read that as an established judgment. Each frontier node now carries a typed, located, counted diagnostic (infer_grounding_not_derived) on the Accepted path, so the deficit's frequency is observable and prioritizable rather than zeroed by construction (DESIGN §5). dissolve-on: a behavior-specific derivation rule lands for a kind, at which point that kind moves to DerivedGrounding and its frontier count drops; the carrier deletes when all twelve kinds derive." +data node_grounding_frontier_note: String = "GroundingNotDerived is v2's typed inference frontier, not a failure arm and not an escape hatch. v2 derives a node's type for six of the closed vocabulary's twelve node kinds (Branch, Match, Loop, Transform add-shape only, and Conj and Arrow by product introduction — 6 connectives + 6 behaviors = 12); the other six kinds, and every other Transform shape, have no derivation rule yet. The Conj and Arrow rules are compositional: a non-roster Conj or Arrow whose every child carries DerivedGrounding derives with the same shape re-formed over the children's grounding evidence as its evidence, and one with any frontier or absent child stays frontier — the derivation discharges only on a fully-evidenced child spine. Above the kind rules sit five specimen-scope derivations, all lookups into declared authorities rather than inventions: a node declared in any ingested language authority's declared-inhabitants roster derives with that roster root as its grounding evidence (infer_node_declared_in_language_inhabitants) — deep subtree membership over the closed ingest set (dag, python, typescript), so a declared inhabitant's leaf fact atoms derive exactly as the inhabitant node itself does; an Atom whose identity is a canonical dag binding with a declared denotation derives with that denotation as its evidence (dag_binding_denotation) — the resolver already bound the surface spelling to the canonical binding, and the authority fixes what the binding denotes; a node that is a member of the target-model authority's canonical-operations roster derives with that roster root as its evidence (infer_node_declared_in_canonical_operations) — the resolver canonicalizes surface operators to those declared operations; a node that is a member of the dag grammar authority's productions roster derives with that roster root as its evidence (infer_node_declared_in_dag_grammar_productions) — the bridge projects a production's parse into (identity atom, captured content) pairs; and an Atom whose binding an enclosing arrow's domain declares derives with the declared domain type as its evidence (infer_find_arrow_domain_type_in_tree) — the declaration-site annotation, itself derived. The binding-reference lookup is scope-naive (whole-tree, first match), the same interim the branch-operand path already uses. All five delete in favor of consuming resolution output when the resolver hands infer declaration-resolved identities directly (the namespace migration's completed state); specimens no authority declares stay on the frontier unchanged. Before this carrier existed the nine were routed through solve_constraints with the singleton candidate set [root] under a predicate that reduces to root == root, and the resulting grounding carried the SOURCE NODE as its structural evidence — i.e. as the node's resolved type (inferred_facts_resolved_type reads that field), so every literal, binding and operation in v2 was typed as itself and every downstream consumer read that as an established judgment. Each frontier node now carries a typed, located, counted diagnostic (infer_grounding_not_derived) on the Accepted path, so the deficit's frequency is observable and prioritizable rather than zeroed by construction (DESIGN §5). dissolve-on: a behavior-specific derivation rule lands for a kind, at which point that kind moves to DerivedGrounding and its frontier count drops; the carrier deletes when all twelve kinds derive." fn infer_grounding_not_derived_diagnostic(at: Locus) -> Diagnostic { Diagnostic { @@ -460,7 +467,191 @@ fn concat_inferred_facts_entries( // remains the structural-solve authority named by dag/gunbc/plans/solve_higher_order_design.dag, // to be EXTENDED with real candidate generation, never forked. -fn infer_node_facts(n: Node, partials: List) -> Outcome { +// THE DECLARED-INHABITANT MEMBERSHIP DERIVATION, GENERAL OVER THE CLOSED INGEST SET. A language +// authority's declared-inhabitants roster (v2.extdeps.languages.dag dag_declared_inhabitants_root, +// and the python/typescript analogues) already fixes what its members denote — the add fn Arrow, +// the int/number inhabitant Conj, and the inhabitant, surface-spelling, representation and +// std-projection Atoms are all declared there — so a node that IS a roster member derives its +// grounding by LOOKUP, never by invention: the roster is the evidence, which is the +// intersubjective-grounding move DESIGN section 1 requires (point at the shared framework, never +// an internal taxonomy). This is the derivation the self-host candidate-generation add-slice +// stall's original trigger named, at specimen scope: the slice's Arrow, Conj and Atom nodes derive, +// while the KINDS stay frontier — a non-member Arrow, Conj or Atom carries GroundingNotDerived +// exactly as before, so the enrolled refusal witnesses pinning other shapes (the bodied arrow, the +// empty Conj, the rust grammar atom) are untouched. The membership check is DEEP +// (node_subtree_nodes): a language's inhabitant node embeds its facts subtree, so the leaf fact +// atoms of a declared inhabitant are roster members exactly as the inhabitant node itself is. +// Growth is execution-driven: the lookup widened from the dag roster to the closed ingest set +// (dag, python, typescript) when the python/typescript same-language round-trip witnesses named +// that moment — their frontier is the grammar parse-product population, whose leaves are the +// declared inhabitants' fact atoms. The set is the compiler's own closed ingest set, not a generic +// hub enumerating upstream products: infer already fixes which languages it ingests, and each +// roster stays in its own authority module (DESIGN section 3's external-upstream decomposition). +// The end-state is the resolver handing infer declaration-resolved identities directly — the +// namespace migration's completed state — at which point this lookup deletes in favor of consuming +// resolution output. + +fn infer_language_inhabitants_roster_roots() -> List { + [ + dag_declared_inhabitants_root(), + python_declared_inhabitants_root(), + ts_declared_inhabitants_root() + ] +} + +// Returns the declaring roster ROOT as the evidence node (never the member itself, which would be +// self-evidence), or Absent when no ingested language's roster declares the node. +fn infer_node_declared_in_language_inhabitants(n: Node) -> Optional { + fold( + infer_language_inhabitants_roster_roots(), + init: optional_absent(), + f: fn(acc, roster_root) { + match acc { + Present { value: _ } => acc + Absent => + if any( + xs: node_subtree_nodes(root: roster_root), + predicate: fn(member) { member == n } + ) { + optional_present(value: roster_root) + } else { + acc + } + } + } + ) +} + +fn infer_node_declared_in_language_inhabitants_bool(n: Node) -> Bool { + match infer_node_declared_in_language_inhabitants(n: n) { + Present { value: _ } => true + Absent => false + } +} + +// THE PRODUCT-INTRODUCTION DERIVATION (Conj and Arrow kinds). A Conj node denotes the product of +// its conjuncts' denotations and an Arrow node the function from its domain's denotation to its +// codomain's — the closed vocabulary's own composition semantics (v2.std.node), not an invented +// meaning. So when EVERY child of a non-roster Conj or Arrow carries DerivedGrounding, the node +// derives: its evidence is the same shape re-formed over each child's grounding evidence, a fresh +// OccurrenceSynthetic node whose evidential chain bottoms out in authority nodes at the leaves +// (never the source node — canonical_grounding_from_derived_type refuses self-evidence by +// construction). When ANY child is still on the frontier or absent from the entries, the node +// stays on the frontier with its typed diagnostic — the derivation discharges only when the whole +// child spine is evidenced, so a partially-derived product can never masquerade as grounded +// (DESIGN section 5). Roster members keep their roster evidence (the authority's declaration +// outranks composition for what the language itself declares), and a childless Conj or Arrow has +// no evidence to compose and stays frontier. This is the frontier note's dissolve-on for these +// two kinds: the kind-level rule the specimen-scope roster lookups stood in for. + +fn infer_type_node_awaits_product_row(n: Node) -> Bool { + (length(xs: n.children) > 0) && !infer_node_declared_in_language_inhabitants_bool(n: n) +} + +// THE CANONICAL-OPERATION ROSTER MEMBERSHIP DERIVATION. The resolver canonicalizes a dag surface +// operator (e.g. `+`) to the target-model authority's declared canonical operation, and the +// transform rule already validates operators by matching against that authority's wire nodes +// (canonical_operation_wire_matches_operation). The wire atoms -- the operation discriminant and +// its field references -- are declared components of those same wire nodes, gathered under the +// authority's declared roster root (target_model_canonical_operations_roster_root). So a node +// that IS a roster member derives by lookup with the roster root as evidence, never by invention +// -- the same frame as the dag declared-inhabitants roster, and general over every declared +// operation rather than add-narrow. The end-state deletes this lookup with the other +// specimen-scope rules, when the resolver hands infer declaration-resolved identities directly. + +fn infer_node_declared_in_canonical_operations(n: Node) -> Bool { + any( + xs: node_subtree_nodes(root: target_model_canonical_operations_roster_root()), + predicate: fn(member) { member == n } + ) +} + +// THE GRAMMAR-PRODUCTION ROSTER MEMBERSHIP DERIVATION. The bridge projects a grammar production's +// parse into the tree as (identity atom, captured content) pairs; the identity atom (e.g. +// dag_surface_module) is the emitted surface atom the dag grammar authority declares for that +// production, gathered under the authority's roster root (dag_grammar_productions_roster_root). +// So a node that IS a roster member derives by lookup with the roster root as evidence, never by +// invention -- the same frame as the declared-inhabitants roster, general over every declared +// production. The end-state deletes this lookup with the other specimen-scope rules, when the +// resolver hands infer declaration-resolved identities directly. + +fn infer_node_declared_in_dag_grammar_productions(n: Node) -> Bool { + any( + xs: node_subtree_nodes(root: dag_grammar_productions_roster_root()), + predicate: fn(member) { member == n } + ) +} + +fn infer_product_child_evidence_edges( + children: List, + entries: List, +) -> Optional> { + fold( + children, + init: optional_present(value: Empty), + f: fn(acc, edge) { + match acc { + Absent => acc + Present { value: evidence_edges } => + match lookup_inferred_facts_in_entries(entries: entries, key: edge.target) { + Absent => optional_absent() + Present { value: child_facts } => + match inferred_facts_resolved_type(facts: child_facts) { + Violates { diagnostic: _ } => optional_absent() + Holds { value: child_evidence } => + optional_present( + value: list_snoc_item( + xs: evidence_edges, + item: Edge { label: edge.label, target: child_evidence } + ) + ) + } + } + } + } + ) +} + +fn infer_product_facts_from_entries( + node: Node, + entries: List, + partials: List, +) -> Outcome { + match infer_bounded_lattice_consumer_gate(consumer: node, partials: partials) { + Rejected { diagnostics: r } => Rejected { diagnostics: r } + Accepted { value: _, diagnostics: cd } => + match infer_descent_witness_for_node(n: node) { + Violates { diagnostic: d } => + Rejected { diagnostics: diagnostics_singleton(d: d) } + Holds { value: descent_proof } => + match infer_product_child_evidence_edges(children: node.children, entries: entries) { + Absent => + bind_outcome_accepted( + od: cd, + inner: inferred_facts_not_derived( + node: node, + descent: Holds { value: descent_proof } + ) + ) + Present { value: evidence_edges } => + bind_outcome_accepted( + od: cd, + inner: inferred_facts_from_derived_type( + node: node, + derived_type: Node { + kind: node.kind, + children: evidence_edges, + occurrence_id: OccurrenceSynthetic + }, + descent: Holds { value: descent_proof } + ) + ) + } + } + } +} + +fn infer_node_facts(n: Node, partials: List, tree: Node) -> Outcome { match infer_bounded_lattice_consumer_gate(consumer: n, partials: partials) { Rejected { diagnostics: r } => Rejected { diagnostics: r } Accepted { value: _, diagnostics: cd } => @@ -482,10 +673,61 @@ fn infer_node_facts(n: Node, partials: List) -> Outcome { descent: Holds { value: descent_proof } ) Rejected { diagnostics: _ } => - inferred_facts_not_derived( - node: n, - descent: Holds { value: descent_proof } - ) + match infer_node_declared_in_language_inhabitants(n: n) { + Present { value: inhabitants_roster } => + inferred_facts_from_derived_type( + node: n, + derived_type: inhabitants_roster, + descent: Holds { value: descent_proof } + ) + Absent => + match infer_atom_binding_sym(node: n) { + Present { value: binding } => + match dag_binding_denotation(sym: binding) { + Present { value: denotation } => + inferred_facts_from_derived_type( + node: n, + derived_type: denotation, + descent: Holds { value: descent_proof } + ) + Absent => + if infer_node_declared_in_canonical_operations(n: n) { + inferred_facts_from_derived_type( + node: n, + derived_type: target_model_canonical_operations_roster_root(), + descent: Holds { value: descent_proof } + ) + } else { + if infer_node_declared_in_dag_grammar_productions(n: n) { + inferred_facts_from_derived_type( + node: n, + derived_type: dag_grammar_productions_roster_root(), + descent: Holds { value: descent_proof } + ) + } else { + match infer_find_arrow_domain_type_in_tree(root: tree, binding: binding) { + Present { value: domain_ty } => + inferred_facts_from_derived_type( + node: n, + derived_type: domain_ty, + descent: Holds { value: descent_proof } + ) + Absent => + inferred_facts_not_derived( + node: n, + descent: Holds { value: descent_proof } + ) + } + } + } + } + Absent => + inferred_facts_not_derived( + node: n, + descent: Holds { value: descent_proof } + ) + } + } } } } @@ -1623,8 +1865,8 @@ fn infer_gather_transform_row_on_entries( // added without deciding, at this site, whether it derives its type or joins the counted frontier // -- never defaulted into by omission (DESIGN §4 closed vocabulary, §5 unwritable-by-construction). -fn infer_gather_fold_not_derived(n: Node, partials: List) -> InferGatherFoldAcc { - match infer_node_facts(n: n, partials: partials) { +fn infer_gather_fold_not_derived(n: Node, partials: List, tree: Node) -> InferGatherFoldAcc { + match infer_node_facts(n: n, partials: partials, tree: tree) { Rejected { diagnostics: r } => infer_gather_fold_acc_failed( node: n, @@ -1662,7 +1904,7 @@ fn infer_gather_fold_not_derived(n: Node, partials: List) -> InferGatherFo } } -fn infer_gather_fold_init(n: Node, partials: List) -> InferGatherFoldAcc { +fn infer_gather_fold_init(n: Node, partials: List, tree: Node) -> InferGatherFoldAcc { match n.kind { ComputationNode { behavior: Branch } => infer_gather_fold_acc_ok( @@ -1697,7 +1939,7 @@ fn infer_gather_fold_init(n: Node, partials: List) -> InferGatherFoldAcc { await_transform_row: false, children_remaining: length(xs: n.children) ) - ComputationNode { behavior: Value } => infer_gather_fold_not_derived(n: n, partials: partials) + ComputationNode { behavior: Value } => infer_gather_fold_not_derived(n: n, partials: partials, tree: tree) ComputationNode { behavior: Transform } => if infer_transform_is_binary_infix_int_add_shape(node: n) { infer_gather_fold_acc_ok( @@ -1711,15 +1953,43 @@ fn infer_gather_fold_init(n: Node, partials: List) -> InferGatherFoldAcc { children_remaining: length(xs: n.children) ) } else { - infer_gather_fold_not_derived(n: n, partials: partials) + infer_gather_fold_not_derived(n: n, partials: partials, tree: tree) } - ComputationNode { behavior: Bind } => infer_gather_fold_not_derived(n: n, partials: partials) - TypeNode { connective: Atom { identity: _ } } => infer_gather_fold_not_derived(n: n, partials: partials) - TypeNode { connective: Conj } => infer_gather_fold_not_derived(n: n, partials: partials) - TypeNode { connective: Disj } => infer_gather_fold_not_derived(n: n, partials: partials) - TypeNode { connective: Arrow } => infer_gather_fold_not_derived(n: n, partials: partials) - TypeNode { connective: Cardinality } => infer_gather_fold_not_derived(n: n, partials: partials) - TypeNode { connective: Instantiation } => infer_gather_fold_not_derived(n: n, partials: partials) + ComputationNode { behavior: Bind } => infer_gather_fold_not_derived(n: n, partials: partials, tree: tree) + TypeNode { connective: Atom { identity: _ } } => infer_gather_fold_not_derived(n: n, partials: partials, tree: tree) + TypeNode { connective: Conj } => + if infer_type_node_awaits_product_row(n: n) { + infer_gather_fold_acc_ok( + node: n, + entries: empty_inferred_facts_entry_list(), + pending: None, + await_branch_row: false, + await_match_row: false, + await_loop_row: false, + await_transform_row: false, + children_remaining: length(xs: n.children) + ) + } else { + infer_gather_fold_not_derived(n: n, partials: partials, tree: tree) + } + TypeNode { connective: Disj } => infer_gather_fold_not_derived(n: n, partials: partials, tree: tree) + TypeNode { connective: Arrow } => + if infer_type_node_awaits_product_row(n: n) { + infer_gather_fold_acc_ok( + node: n, + entries: empty_inferred_facts_entry_list(), + pending: None, + await_branch_row: false, + await_match_row: false, + await_loop_row: false, + await_transform_row: false, + children_remaining: length(xs: n.children) + ) + } else { + infer_gather_fold_not_derived(n: n, partials: partials, tree: tree) + } + TypeNode { connective: Cardinality } => infer_gather_fold_not_derived(n: n, partials: partials, tree: tree) + TypeNode { connective: Instantiation } => infer_gather_fold_not_derived(n: n, partials: partials, tree: tree) } } @@ -1751,6 +2021,69 @@ fn infer_literal_edge_diagnostics_derived(parent: Node, edge: Edge) -> Bool { } } +fn infer_gather_acc_awaits_product_row(node: Node, entries: List) -> Bool { + match node.kind { + TypeNode { connective: Conj } => + match lookup_inferred_facts_in_entries(entries: entries, key: node) { + Absent => true + Present { value: _ } => false + } + TypeNode { connective: Arrow } => + match lookup_inferred_facts_in_entries(entries: entries, key: node) { + Absent => true + Present { value: _ } => false + } + _ => false + } +} + +fn infer_gather_product_row_on_entries( + node: Node, + entries: List, + pending: Diagnostics, + partials: List, +) -> InferGatherFoldAcc { + match infer_product_facts_from_entries(node: node, entries: entries, partials: partials) { + Rejected { diagnostics: r } => + infer_gather_fold_acc_failed( + node: node, + pending: rejected_with_pending(pending: pending, rejected: r), + await_branch_row: false, + await_match_row: false, + await_loop_row: false, + await_transform_row: false, + children_remaining: 0 + ) + Accepted { value: facts, diagnostics: d } => + match admitted_inferred_facts_entry(node: node, facts: facts) { + Rejected { diagnostics: r } => + infer_gather_fold_acc_failed( + node: node, + pending: rejected_with_pending(pending: pending, rejected: r), + await_branch_row: false, + await_match_row: false, + await_loop_row: false, + await_transform_row: false, + children_remaining: 0 + ) + Accepted { value: entry, diagnostics: ed } => + infer_gather_fold_acc_ok( + node: node, + entries: list_snoc_item(xs: entries, item: entry), + pending: diagnostics_merge( + outer: diagnostics_merge(outer: pending, inner: d), + inner: ed + ), + await_branch_row: false, + await_match_row: false, + await_loop_row: false, + await_transform_row: false, + children_remaining: 0 + ) + } + } +} + fn infer_gather_fold_step( acc: InferGatherFoldAcc, edge: Edge, @@ -1811,6 +2144,13 @@ fn infer_gather_fold_step( partials: partials, tree: tree ) + } else if children_remaining == 0 && infer_gather_acc_awaits_product_row(node: acc.node, entries: merged_entries) { + infer_gather_product_row_on_entries( + node: acc.node, + entries: merged_entries, + pending: merged_pending, + partials: partials + ) } else { infer_gather_fold_acc_ok( node: acc.node, @@ -1829,7 +2169,7 @@ fn infer_gather_fold_step( fn infer_gather_fold_algebra(partials: List, tree: Node) -> NodeFold { NodeFold { init: fn(n0) { - infer_gather_fold_init(n: n0, partials: partials) + infer_gather_fold_init(n: n0, partials: partials, tree: tree) }, step: fn(acc, e, child) { infer_gather_fold_step(acc: acc, edge: e, child: child, partials: partials, tree: tree) diff --git a/src/v2/compiler/06_translate.dag b/src/v2/compiler/06_translate.dag index 65867f35ad1..62c36ff800d 100644 --- a/src/v2/compiler/06_translate.dag +++ b/src/v2/compiler/06_translate.dag @@ -21,12 +21,14 @@ import v2.std.compilers.target_model { TargetValueExpression, TargetValueExpressionProjection, bodied_arrow_scaffold_from_grammar_relation_row, + canonical_operation_from_wire_node, target_bodied_arrow_compose_statement_tokens, target_project_arrow_body_to_value_expression, target_project_transform_primitive_apply_in_arrow_scope, target_project_transform_callable_apply_in_arrow_scope, - target_transform_surface_op_atom, + target_transform_operator_child, target_value_expr_arrow_has_value_expression_body, + target_value_expr_diagnostic, target_value_expression_projection_from_target, target_value_expression_tokens_from_target } @@ -953,14 +955,21 @@ fn serialize_concrete_syntax_tokens_to_source_string( ) } -fn translate_project_transform_in_arrow_scope( +// The transform operator position is a TWO-REPRESENTATION boundary: resolution canonicalizes a +// surface operator atom into its canonical-operation wire node (resolve_surface_operator_atom), so +// a production tree carries the wire, while fixture trees that bypass resolution still carry the +// surface token atom. The decode tries the wire first; only a wire miss falls to the surface-token +// table, and only a surface-table miss routes to callable apply. The arms are disjoint (a wire +// node's identity is a CanonicalOperation discriminant with named fields; a surface token is a +// dag_token_* atom; anything else is shape-invalid), so the dispatch adds no fallback widening. +fn translate_project_transform_surface_or_callable_in_arrow_scope( arrow: Node, body: Node, + operator_child: Node, target: TargetModel ) -> Outcome { - bind_outcome( - o: target_transform_surface_op_atom(node: body), - f: fn(op_sym) { + match operator_child.kind { + TypeNode { connective: Atom { identity: op_sym } } => match dag_surface_operator_canonicalization_member(surface_token: op_sym) { Accepted { value: canonical_op, diagnostics: _ } => target_project_transform_primitive_apply_in_arrow_scope( @@ -981,6 +990,41 @@ fn translate_project_transform_in_arrow_scope( } ) } + _ => + outcome_rejected( + d: target_value_expr_diagnostic( + reason: ^target_value_expr_reason_transform_shape_invalid, + node: operator_child + ) + ) + } +} + +fn translate_project_transform_in_arrow_scope( + arrow: Node, + body: Node, + target: TargetModel +) -> Outcome { + bind_outcome( + o: target_transform_operator_child(node: body), + f: fn(operator_child) { + match canonical_operation_from_wire_node(wire: operator_child) { + Accepted { value: canonical_op, diagnostics: _ } => + target_project_transform_primitive_apply_in_arrow_scope( + arrow: arrow, + body: body, + canonical_op: canonical_op, + target: target + ) + + Rejected { diagnostics: _ } => + translate_project_transform_surface_or_callable_in_arrow_scope( + arrow: arrow, + body: body, + operator_child: operator_child, + target: target + ) + } } ) } diff --git a/src/v2/compiler/emit_module.dag b/src/v2/compiler/emit_module.dag index 4fb2b26e5bb..4755c038bda 100644 --- a/src/v2/compiler/emit_module.dag +++ b/src/v2/compiler/emit_module.dag @@ -3,6 +3,7 @@ module v2.compiler.emit_module import v2.compiler.emit { emit } import v2.compiler.infer { InferredTree } import v2.compiler.target_carriers { target_source_medium } +import std.content_hash { ContentHash } import std.decl_ref { DeclarationRef, decl_ref } import std.emission_provenance { EmissionAuthoredFragment, @@ -192,3 +193,16 @@ fn emit_family( } ) } + +// THE PORTABLE PROJECTION of one emit_family product, keyed for the native build's cache. A +// family pipeline's member list is not shareable across claims — EmitFamilyMember carries an +// InferredTree, whose facts PartialFunction is origin-bound — so a cross-claim share of the +// pipeline (v2.workflow.floor_pure_producer_share) must publish a closure-free stage: the +// emitted source and the content key the native build is cached under. The key's DERIVATION +// stays with each family's own native_key function; this record only joins the two portable +// outputs, and it lives here — one type, not one per family (DESIGN §2/§3) — because the family +// emit authority is the one module both family pipelines already import for emit_family itself. +type EmittedFamilyCrate { + source: Medium, + native_key: ContentHash +} diff --git a/src/v2/compiler/emit_produced.dag b/src/v2/compiler/emit_produced.dag index 898f57f6533..b76f03c372e 100644 --- a/src/v2/compiler/emit_produced.dag +++ b/src/v2/compiler/emit_produced.dag @@ -13,8 +13,8 @@ import v2.std.compilers.target_model { } import extdeps.communication.medium { Medium } import v2.std.collection { Absent, List, Present, list_at_optional } -import v2.std.algebra { fold_list, list_append } -import std.algebra { Empty } +import v2.std.algebra { fold_list, FreeMonoid, list_append, list_snoc_item } +import std.algebra { Cons, Empty } import v2.std.diagnostic { Accepted, Diagnostic, @@ -28,7 +28,8 @@ import v2.std.diagnostic { node_locus, outcome_rejected } -import v2.std.node { Conj, Named, Node, Positional, Symbol, TypeNode } +import v2.std.node { Arrow, Conj, Edge, Named, Node, Positional, Symbol, TypeNode } +import v2.std.logic { Bool } import v2.std.text { String } fn emit_produced_decl_diagnostic(reason: Symbol, node: Node) -> Diagnostic { @@ -69,6 +70,66 @@ fn emit_produced_decl_inner_arrow(decl: Node) -> Outcome { } } +// WHERE PRODUCED DECLARATIONS COME FROM. A produced declaration inside a resolved tree is the +// decl-shaped node itself: a Conj whose first child is a Named edge into an Arrow — exactly the +// shape emit_produced_decl_inner_arrow and produced_decl_subject_from_decl consume, so collection +// never synthesizes a wrapper node. The fold is total and pure: it reports every decl-shaped node +// and never recurses into one (a declaration owns its subtree). How many declarations a +// composition expects, and what a zero or surplus population means, is the caller's admission +// policy — never a silent filter here. +// The children match lives behind a declared FreeMonoid parameter because the v1 seed +// stamps pattern variables from a declared parameter type, not from a field-access scrutinee: +// matching node.children directly leaves the Cons head as an unresolved T (no field 'label' on +// type 'T'), and routing the same lookup through list_at_optional fails its generic instantiation +// when the callee is typechecked lazily from inside the recursive collector cluster. +fn produced_decl_children_have_decl_head(children: FreeMonoid) -> Bool { + match children { + Empty => false + Cons { head: first, tail: _ } => + match first.label { + Named { name: _ } => + match first.target.kind { + TypeNode { connective: Arrow } => true + _ => false + } + Positional => false + } + } +} + +fn produced_decl_node_is_decl(node: Node) -> Bool { + match node.kind { + TypeNode { connective: Conj } => produced_decl_children_have_decl_head(children: node.children) + _ => false + } +} + +fn produced_decl_conjs_in_children( + children: FreeMonoid, + acc: FreeMonoid +) -> FreeMonoid { + match children { + Empty => acc + Cons { head: e, tail: rest } => + produced_decl_conjs_in_children( + children: rest, + acc: produced_decl_conjs_in_tree_into(node: e.target, acc: acc) + ) + } +} + +fn produced_decl_conjs_in_tree_into(node: Node, acc: FreeMonoid) -> FreeMonoid { + if produced_decl_node_is_decl(node: node) { + list_snoc_item(xs: acc, item: node) + } else { + produced_decl_conjs_in_children(children: node.children, acc: acc) + } +} + +fn produced_decl_conjs_in_tree(root: Node) -> FreeMonoid { + produced_decl_conjs_in_tree_into(node: root, acc: Empty) +} + fn emit_produced_decl_with_target( decl: Node, target: TargetModel diff --git a/src/v2/compiler/program_assembly.dag b/src/v2/compiler/program_assembly.dag index 3f6e2035596..ee31d9be3b9 100644 --- a/src/v2/compiler/program_assembly.dag +++ b/src/v2/compiler/program_assembly.dag @@ -1,7 +1,7 @@ module v2.compiler.program_assembly import v2.compiler.normalized_tree { NormalizedTree } -import v2.extdeps.languages.dag { qualified_name_from_module_node } +import v2.extdeps.languages.dag { dag_grammar, qualified_name_from_module_node } import v2.compiler.source_authority { DagSourceReadWitness, SourceRootIngest, @@ -139,6 +139,19 @@ fn program_assembly_fold_step( data program_assembly_prepare_once_note: String = "prepare_grammar is hoisted ABOVE fold_list here so the grammar is well-formed-checked, validated (5 checks), and FIRST/nullable-analyzed ONCE per assembly — then parse_module_prepared reuses the PreparedGrammar for every module in the ingest. A door call over a K-module import closure therefore validates once, not K times (the recompute-per-module cost this dissolves). Empty-ingest is guarded so it never pays the one-time prepare for zero modules. Fail-closed: an invalid grammar fails the whole assembly (ProgramAssemblyFoldFailed) before any module is parsed, carrying the validation diagnostic — never a per-module silent skip." +// THE ONE PREPARED `.dag` GRAMMAR, FORCED ONCE OUTSIDE THE CLAIM FOLD. Every assembly of `.dag` +// source pays `prepare_grammar(grammar: lm.grammar)` with `lm.grammar` content-identical to +// `dag_grammar()` before its fold can start, and `prepare_grammar` is the cross-claim pure tier's +// built-in admitted arm, so one fill serves every claim — if it ever lands. The fill costs more +// than one claim's CPU budget, so an in-fold first touch dies mid-flight (FillBudgetExceeded) and is +// abandoned un-stored, and the next claim starts it over. Enrolled in +// `floor_cross_claim_pure_producers_warm` (v2.workflow.floor_pure_producer_share) so the required +// floor evaluates this nullary producer during strict preparation — outside every per-claim +// budget — and the inner `prepare_grammar` fill lands in the tier before any claim runs. +fn dag_prepared_grammar() -> Outcome { + prepare_grammar(grammar: dag_grammar()) +} + fn program_assembly_fold_ingest( ingest: SourceRootIngest, lm: LanguageModel diff --git a/src/v2/compiler/self_host/candidate_generation.dag b/src/v2/compiler/self_host/candidate_generation.dag index 2b837e996e4..8daf9d1d9da 100644 --- a/src/v2/compiler/self_host/candidate_generation.dag +++ b/src/v2/compiler/self_host/candidate_generation.dag @@ -3,6 +3,7 @@ module v2.compiler.self_host.candidate_generation import extdeps.communication.medium { Medium } import extdeps.filesystem.filesystem_io { Filesystem } import v2.compiler.emit { emit } +import v2.compiler.emit_produced { emit_produced_decl, produced_decl_conjs_in_tree } import v2.compiler.infer { infer, InferredTree } import v2.compiler.name_resolve { Admission } import v2.compiler.program_assembly { assemble_program_from_ingest } @@ -30,11 +31,13 @@ import v2.std.diagnostic { Rejected, Unavailable, bind_outcome, + node_locus, outcome_accepted, outcome_rejected, invariant_port_locus } import v2.std.node { Node, Symbol } +import std.algebra { Cons, Empty } import v2.std.optional { Absent, Present, optional_absent, optional_present } import v2.std.text { String } @@ -66,10 +69,17 @@ type WrittenProvenancedRustArtifact { write: RustWorkspaceWriteReceipt } -// Lane B direct v2 Rust door: generate_rust_emission_candidate wires serialize_target composed with -// translate (v2.compiler.emit); mint_written_provenanced_rust_artifact writes Cargo.toml and -// src/main.rs through Filesystem.Write so ingest-assemble-infer-translate-serialize-write is one -// candidate_generation surface. +// Lane B direct v2 Rust door: the PRODUCTION composition is +// generate_rust_module_emission_candidate — the inferred module's produced declaration emitted +// through v2.compiler.emit_produced, because a resolved module is a module shell (grammar +// projection over named declarations), not a type record, so emitting it means emitting its +// declarations and never record-projecting the shell through translate. +// mint_written_provenanced_rust_artifact writes Cargo.toml and src/lib.rs through Filesystem.Write +// so ingest-assemble-infer-emit-write is one candidate_generation surface. +// generate_rust_emission_candidate (raw v2.compiler.emit over the whole inferred tree) remains ONLY +// as the fixture lane's composition: fixture_synthetic_emission rides it over +// direct_rust_door_inferred_tree, a target-side fixture root with no module shell, and it deletes +// with that lane's own dissolution trigger. No production caller remains on it. data direct_rust_door_scaffold_build_cause: Symbol = ^direct_rust_door_scaffold_build @@ -166,6 +176,51 @@ fn generate_rust_emission_candidate( ) } +// The production module composition. Admission is exactly one produced-decl-shaped node (the +// collector in v2.compiler.emit_produced defines the shape): the door's specimen class is the +// single-declaration module, and zero or surplus populations refuse typed and located rather than +// picking a winner. The multi-declaration join convention lands with the first multi-declaration +// consumer — emit_produced_module's trailing separator is pinned by its own goldens and is not +// this composition's fact. +fn generate_rust_module_emission_candidate( + inferred: InferredTree, + target: TargetModel +) -> Outcome { + match produced_decl_conjs_in_tree(root: inferred.root) { + Empty => + outcome_rejected( + d: Diagnostic { + reason: ^rust_module_emission_decl_absent, + at: node_locus(node: inferred.root), + correction: Unavailable { reason: ExternalContractUnknown } + } + ) + Cons { head: decl, tail: rest } => + match rest { + Empty => + bind_outcome( + o: emit_produced_decl(decl: decl, target: target), + f: fn(source) { + outcome_accepted( + value: RustEmissionCandidate { + source: source, + emitted: inferred.root + } + ) + } + ) + Cons { head: surplus, tail: _ } => + outcome_rejected( + d: Diagnostic { + reason: ^rust_module_emission_decl_ambiguous, + at: node_locus(node: surplus), + correction: Unavailable { reason: ExternalContractUnknown } + } + ) + } + } +} + fn write_rust_workspace( workspace_dir: String, source: Medium @@ -221,7 +276,7 @@ fn mint_provenanced_rust_artifact( module_path: String ) -> Outcome { bind_outcome( - o: generate_rust_emission_candidate(inferred: inferred, target: target), + o: generate_rust_module_emission_candidate(inferred: inferred, target: target), f: fn(candidate) { mint_artifact_from_rust_emission_candidate( candidate: candidate, @@ -258,9 +313,9 @@ fn mint_written_provenanced_rust_artifact( } // STAGE-LOCATED production observation for the ingest-path Rust door. Replaces the Outcome-only -// surface whose Rejected arm erased WHICH stage refused: assembly, inference, emission (translate -// composed with serialize_target, observed as one production seam because v2.compiler.emit is one -// composition and splitting it here would fork it), or producer-receipt mint. +// surface whose Rejected arm erased WHICH stage refused: assembly, inference, emission (the +// single-declaration module composition of generate_rust_module_emission_candidate, observed as +// one production seam), or producer-receipt mint. // generate_provenanced_rust_candidate_from_ingest is now a PROJECTION of this observation, not a // second traversal — the pipeline is walked once per call and every existing caller keeps its // Outcome shape. @@ -285,7 +340,7 @@ fn observe_provenanced_rust_emission_from_ingest( match infer(tree: resolved) { Rejected { diagnostics: d } => InferenceRejected { diagnostics: d } Accepted { value: inferred, diagnostics: _ } => - match generate_rust_emission_candidate(inferred: inferred, target: target) { + match generate_rust_module_emission_candidate(inferred: inferred, target: target) { Rejected { diagnostics: d } => EmissionRejected { diagnostics: d } Accepted { value: candidate, diagnostics: _ } => match mint_artifact_from_rust_emission_candidate( diff --git a/src/v2/compiler/self_host/candidate_generation_stage_verdicts.dag b/src/v2/compiler/self_host/candidate_generation_stage_verdicts.dag index a0e23454e24..5636133c191 100644 --- a/src/v2/compiler/self_host/candidate_generation_stage_verdicts.dag +++ b/src/v2/compiler/self_host/candidate_generation_stage_verdicts.dag @@ -15,35 +15,40 @@ import v2.std.diagnostic { } import v2.std.node { Node, Symbol } -// THE PER-STAGE VERDICT INSTRUMENT `v2.workflow.floor_expected_red` NAMES AS THE REPRODUCIBLE -// PRODUCER of its add-slice receipt. That receipt was measured once, at main 3a8344b5c, by a -// throwaway probe: `infer(dag_add_emitted_root)` answered `infer_accepted` while +// THE PER-STAGE VERDICT INSTRUMENT `v2.workflow.floor_expected_red` NAMED AS THE REPRODUCIBLE +// PRODUCER of its add-slice receipt, before that row greened and the note deleted itself per its +// own dissolution statement. That receipt was measured once, at main 3a8344b5c, by a throwaway +// probe: `infer(dag_add_emitted_root)` answered `infer_accepted` while // `generate_translate_self_emit_candidate` over the same root and `dag_add_target_model` refused -// headed by `infer_grounding_not_derived`. No `.dag` entry re-derived it, so a reader could not -// re-run it; this module is the standing entry, parameterized over the one root and target so -// the seam is measured, never transcribed (DESIGN section 6). +// headed by `infer_grounding_not_derived`. This module is the standing entry, parameterized over +// the one root and target so the seam is measured, never transcribed (DESIGN section 6). // // THE VERDICT VOCABULARY IS THE RECEIPT'S. `infer_verdict` is `infer_accepted` or // `infer_rejected`. `composition_verdict` is `candidate_accepted` when the infer-then-translate -// composition accepts, and otherwise the rejection's head reason -- today +// composition accepts, and otherwise the rejection's head reason -- for the add slice that was // `infer_grounding_not_derived`, the typed frontier `v2.compiler.infer` -// `node_grounding_frontier_note` declares: infer derives grounding for four of the closed -// vocabulary's twelve kinds and records GroundingNotDerived for the rest, which -// `translate_grounding_derived_gate_subtree` then refuses. +// `node_grounding_frontier_note` declares, until the declared-inhabitant membership derivation +// (`infer_node_declared_in_dag_inhabitants`) landed: the slice's ten type-spine nodes are all +// declared in the dag language authority's declared-inhabitants roster, so they derive by lookup +// and the composition accepts clean. // -// THE CARRIED-REASONS FIELDS ARE THE LOAD-BEARING HALF THE VERDICT SYMBOLS CANNOT SAY. Infer -// ACCEPTS this root while CARRYING the frontier diagnostic on its accepted path, so the enrolled -// witness's `d == None` conjunct fails even where the composition reaches acceptance -- the -// roster note's "both conjuncts fail for the one cause". `infer_carried_reasons` surfaces what -// infer carried; `composition_carried_reasons` is the composition's accepted-path diagnostics or, -// on refusal, the full rejection chain head-first (which bind_outcome prefixes with infer's -// pending diagnostics, so today it is the one frontier reason repeated). +// THE CARRIED-REASONS FIELDS ARE THE LOAD-BEARING HALF THE VERDICT SYMBOLS CANNOT SAY. Before the +// membership derivation, infer ACCEPTED this root while CARRYING the frontier diagnostic on its +// accepted path, so the enrolled witness's `d == None` conjunct failed even where the composition +// reached acceptance. `infer_carried_reasons` surfaces what infer carried; +// `composition_carried_reasons` is the composition's accepted-path diagnostics or, on refusal, +// the full rejection chain head-first (which bind_outcome prefixes with infer's pending +// diagnostics). For the add slice both are now empty, and the flipped witness pins that. // -// THIS INSTRUMENT DOES NOT RETIRE WHEN THE ADD-SLICE STALL -// (`gunbc.guarantee_stall.self_host_candidate_generation_add_slice_stall`) DISSOLVES. It is the -// standing measurement of a production seam, and its verdicts flipping to -// `candidate_accepted` with empty carried lists is the signal the stall's trigger fired. It -// retires only if the `generate_translate_self_emit_candidate` seam itself dissolves. +// THE ADD-SLICE STALL (`gunbc.guarantee_stall.self_host_candidate_generation_add_slice_stall`) +// RETIRED 2026-09-07 when the declared-inhabitant membership derivation widened to the closed +// ingest set (v2.compiler.infer infer_node_declared_in_language_inhabitants), grounding the python +// and typescript parse-product populations and greening the stall's four round-trip members — its +// next-rung trigger fired, so the row reached its ceiling and deleted per DESIGN 4b(4). This +// instrument did not retire with it: it is the standing measurement of a production seam, and its +// verdicts flipping to `candidate_accepted` with empty carried lists was the signal the add-slice +// half of the trigger had fired. It retires only if the +// `generate_translate_self_emit_candidate` seam itself dissolves. type CandidateGenerationStageVerdicts { infer_verdict: Symbol diff --git a/src/v2/compiler/self_host/direct_rust_door_fixture.dag b/src/v2/compiler/self_host/direct_rust_door_fixture.dag index 657a910076d..d79140bc5ff 100644 --- a/src/v2/compiler/self_host/direct_rust_door_fixture.dag +++ b/src/v2/compiler/self_host/direct_rust_door_fixture.dag @@ -1,7 +1,9 @@ module v2.compiler.self_host.direct_rust_door_fixture import extdeps.communication.medium { Lossless, Medium } -import v2.compiler.infer { DerivedGrounding, InferredFacts, InferredTree } +import v2.compiler.infer { DerivedGrounding, InferredFacts, InferredTree, infer } +import v2.compiler.program_assembly { assemble_program_from_ingest } +import v2.extdeps.languages.dag { dag_language_model } import v2.extdeps.languages.rust { Bits32, PanicOnOverflow, @@ -32,8 +34,11 @@ import v2.std.constraints { CanonicalGroundingWitness } import v2.std.diagnostic { + Accepted, Diagnostic, ExternalContractUnknown, + Outcome, + Rejected, Unavailable, node_locus } @@ -86,6 +91,31 @@ fn direct_rust_door_admission() -> Admission { } } +// THE ASSEMBLED DAG SPECIMEN, ONE PIPELINE WITH ONE HOME. The grounding-frontier control files +// (v2.test.execution.infer_atom_grounding_rules, v2.test.execution.infer_product_introduction, +// v2.test.execution.dag_binding_denotation) each begin every specimen claim with the same +// ingest→assemble→infer walk over the add_probe module above; the resolved stage is the deepest +// stage whose value is closure-free and therefore portable (the InferredTree one stage up carries +// the facts PartialFunction and is deliberately not the share point), so the resolved tree is the +// cross-claim share point and infer stays per claim. `direct_rust_door_specimen_resolved` is +// nullary and warm-enrolled in `v2.workflow.floor_pure_producer_share` +// (floor_cross_claim_pure_producers_warm), which carries the measured recompute/sharing/serve +// case; the required floor forces it during strict preparation, outside every per-claim budget. +fn direct_rust_door_specimen_resolved() -> Outcome { + assemble_program_from_ingest( + ingest: direct_rust_door_ingest(), + admission: direct_rust_door_admission(), + lm: dag_language_model() + ) +} + +fn direct_rust_door_specimen_inferred() -> Outcome { + match direct_rust_door_specimen_resolved() { + Rejected { diagnostics: d } => Rejected { diagnostics: d } + Accepted { value: resolved, diagnostics: _ } => infer(tree: resolved) + } +} + fn direct_rust_door_i32_facts_node() -> Node { rust_integer_facts_node(facts: rust_facts_i32) } diff --git a/src/v2/compiler/self_host/emit_coverage_frontier.dag b/src/v2/compiler/self_host/emit_coverage_frontier.dag index 54e77a875ef..8812930556b 100644 --- a/src/v2/compiler/self_host/emit_coverage_frontier.dag +++ b/src/v2/compiler/self_host/emit_coverage_frontier.dag @@ -7,7 +7,7 @@ import v2.std.algebra { length, any } import std.decl_ref { DeclarationRef, WholeDeclaration } import std.realization_schedule { ScheduleWitnessEntry } import v2.compiler.self_host.wet_receipt_enrollment { falsifier_self_host_wet_template_entries } -import std.dissolution { DissolutionCondition, unbound_dissolution } +import std.dissolution { DissolutionCondition } // Kernel-D Stage 3 (session loyal-raven-94, 2026-07-24): a typed, counted roster over the // emit_host_*/rust_*_equals_eval witness families tracking NATIVE-EVAL CONSTRUCT COVERAGE — each @@ -59,31 +59,46 @@ fn coverage_subject(module_path: String, decl_name: String) -> DeclarationRef { } } -fn retained_via_eval_agreement( - subject: DeclarationRef, - entry: String, - f: String -) -> EmitCoverageFrontierRow { - EmitCoverageFrontierRow { - subject: subject, - witness_entry: entry, - witness_function: f, - disposition: InterpreterRetained { - reason: "witness_body_calls_eval", - dissolution: unbound_dissolution(description: "a native_only-verdict-style witness (real ingest to emit to compile to run native, verdict decoded from stdout, eval() never called — the emit_host_native_only_verdict_test.dag pattern) lands for this family and is enrolled in falsifier_self_host_wet_template_entries") - } - } -} - +// ALL FIFTEEN roster rows are SelfEmittedNative as of 2026-09-08 — the InterpreterRetained +// population is zero and the retained_via_eval_agreement row constructor was deleted with the last +// flip (DESIGN section 3c: a constructor no row consumes is dead data; the InterpreterRetained +// VARIANT stays as the disposition authority's other state, matched by coverage_row_is_native). +// Each promotion landed on a native-only verdict arm (positive + wrong-octet broken control) in +// the already file-grain-enrolled native-only verdict entry, so each backing citation is enrolled +// by construction (coverage_row_backing_is_enrolled): the add family row (first element, backing +// emit_host_native_only_add_holds), the complement family row (backing +// emit_host_native_only_complement_holds, run through the logic family crate per the +// witness_family_build_grain_ruling), the field_access family row (backing +// emit_host_native_only_field_access_holds, octet 9 pinned against the family one-build cache), +// the three match_loop_fold family rows (match, loop, fold_closure — backing +// emit_host_native_only_{match,loop,fold_closure}_holds, run through the three-member family crate +// per the witness_family_build_grain_ruling, octet lists pinned against the family one-build +// cache), the meet_join family row (backing emit_host_native_only_meet_holds — the arm split +// one-member-per-claim under the floor's 500ms claim ceiling, so emit_host_native_only_join_holds +// carries the family's other half, run through the logic family crate it shares with complement, +// octets meet=1 join=1 pinned against the family one-build cache), the variant_construct family row (backing +// emit_host_native_only_variant_construct_holds, octet 9 pinned against the family one-build +// cache), the two host-effect transport rows (filesystem_read, shell_exec_run — backing +// emit_host_native_only_{filesystem_read,shell_exec_run}_holds, literal octets grounded by the +// families' own enrolled fixture pins, run through the families' fixed witness workspaces), and +// the four emit-path rows (module, produced_module, call, record_construct — backing +// emit_host_native_only_{module,produced_module,call,record_construct}_holds, octets 5/5/7/9 +// pinned against the equals_eval pairs' own oracle legs, run under per-family fixed workspace +// roots with realization-digest content-safety). Each family's equals_eval agreement pair stays +// enrolled as its program-side discrimination leg. data emit_coverage_frontier_roster: List = [ - retained_via_eval_agreement( + EmitCoverageFrontierRow { subject: coverage_subject( module_path: "v2.test.manual.rust_add_emit_translate", decl_name: "rust_add_inferred_tree" ), - entry: "src/v2/test/claim/execution/emit_host_add_equals_eval_test.dag", - f: "emit_host_add_equals_eval_holds" - ), + witness_entry: "src/v2/test/claim/execution/emit_host_add_equals_eval_test.dag", + witness_function: "emit_host_add_equals_eval_holds", + disposition: SelfEmittedNative { + backing_entry: "src/v2/test/claim/execution/long/emit_host_native_only_verdict_test.dag", + backing_function: "emit_host_native_only_add_holds" + } + }, EmitCoverageFrontierRow { subject: coverage_subject( module_path: "v2.test.long.emit_host_classical_not_ingested_equals_eval", @@ -96,110 +111,162 @@ data emit_coverage_frontier_roster: List = [ backing_function: "emit_host_native_only_classical_not_holds" } }, - retained_via_eval_agreement( + EmitCoverageFrontierRow { subject: coverage_subject( module_path: "v2.test.execution.emit_host_complement_equals_eval", decl_name: "emit_complement_eval_subject" ), - entry: "src/v2/test/claim/execution/emit_host_complement_equals_eval_test.dag", - f: "emit_host_complement_equals_eval_holds" - ), - retained_via_eval_agreement( + witness_entry: "src/v2/test/claim/execution/emit_host_complement_equals_eval_test.dag", + witness_function: "emit_host_complement_equals_eval_holds", + disposition: SelfEmittedNative { + backing_entry: "src/v2/test/claim/execution/long/emit_host_native_only_verdict_test.dag", + backing_function: "emit_host_native_only_complement_holds" + } + }, + EmitCoverageFrontierRow { subject: coverage_subject( module_path: "v2.test.execution.emit_host_field_access_equals_eval", decl_name: "emit_field_eval_subgraph_node" ), - entry: "src/v2/test/claim/execution/emit_host_field_access_equals_eval_test.dag", - f: "emit_host_field_access_equals_eval_holds" - ), - retained_via_eval_agreement( + witness_entry: "src/v2/test/claim/execution/emit_host_field_access_equals_eval_test.dag", + witness_function: "emit_host_field_access_equals_eval_holds", + disposition: SelfEmittedNative { + backing_entry: "src/v2/test/claim/execution/long/emit_host_native_only_verdict_test.dag", + backing_function: "emit_host_native_only_field_access_holds" + } + }, + EmitCoverageFrontierRow { subject: coverage_subject( module_path: "v2.extdeps.languages.rust_test", decl_name: "rust_filesystem_read_family_target_model_staging" ), - entry: "src/v2/test/claim/execution/emit_host_filesystem_read_equals_eval_test.dag", - f: "emit_host_filesystem_read_equals_eval_holds" - ), - retained_via_eval_agreement( + witness_entry: "src/v2/test/claim/execution/emit_host_filesystem_read_equals_eval_test.dag", + witness_function: "emit_host_filesystem_read_equals_eval_holds", + disposition: SelfEmittedNative { + backing_entry: "src/v2/test/claim/execution/long/emit_host_native_only_verdict_test.dag", + backing_function: "emit_host_native_only_filesystem_read_holds" + } + }, + EmitCoverageFrontierRow { subject: coverage_subject( module_path: "v2.test.emit.fold_call_closure_fixture", decl_name: "fold_fixture_param_call_body" ), - entry: "src/v2/test/claim/execution/emit_host_fold_closure_equals_eval_test.dag", - f: "emit_host_fold_closure_equals_eval_holds" - ), - retained_via_eval_agreement( + witness_entry: "src/v2/test/claim/execution/emit_host_fold_closure_equals_eval_test.dag", + witness_function: "emit_host_fold_closure_equals_eval_holds", + disposition: SelfEmittedNative { + backing_entry: "src/v2/test/claim/execution/long/emit_host_native_only_verdict_test.dag", + backing_function: "emit_host_native_only_fold_closure_holds" + } + }, + EmitCoverageFrontierRow { subject: coverage_subject( module_path: "v2.test.complexity_gate.table_fixture_loop_subject_producer", decl_name: "table_fixture_loop_arrow_with_body" ), - entry: "src/v2/test/claim/execution/emit_host_loop_equals_eval_test.dag", - f: "emit_host_loop_equals_eval_holds" - ), - retained_via_eval_agreement( + witness_entry: "src/v2/test/claim/execution/emit_host_loop_equals_eval_test.dag", + witness_function: "emit_host_loop_equals_eval_holds", + disposition: SelfEmittedNative { + backing_entry: "src/v2/test/claim/execution/long/emit_host_native_only_verdict_test.dag", + backing_function: "emit_host_native_only_loop_holds" + } + }, + EmitCoverageFrontierRow { subject: coverage_subject( module_path: "v2.test.complexity_gate.table_fixture_match_subject_producer", decl_name: "table_fixture_match_arrow_with_body" ), - entry: "src/v2/test/claim/execution/emit_host_match_equals_eval_test.dag", - f: "emit_host_match_equals_eval_holds" - ), - retained_via_eval_agreement( + witness_entry: "src/v2/test/claim/execution/emit_host_match_equals_eval_test.dag", + witness_function: "emit_host_match_equals_eval_holds", + disposition: SelfEmittedNative { + backing_entry: "src/v2/test/claim/execution/long/emit_host_native_only_verdict_test.dag", + backing_function: "emit_host_native_only_match_holds" + } + }, + EmitCoverageFrontierRow { subject: coverage_subject( module_path: "v2.test.execution.emit_host_meet_join_equals_eval", decl_name: "meet_join_eval_subject" ), - entry: "src/v2/test/claim/execution/emit_host_meet_join_equals_eval_test.dag", - f: "emit_host_meet_equals_eval_holds" - ), - retained_via_eval_agreement( + witness_entry: "src/v2/test/claim/execution/emit_host_meet_join_equals_eval_test.dag", + witness_function: "emit_host_meet_equals_eval_holds", + disposition: SelfEmittedNative { + backing_entry: "src/v2/test/claim/execution/long/emit_host_native_only_verdict_test.dag", + backing_function: "emit_host_native_only_meet_holds" + } + }, + EmitCoverageFrontierRow { subject: coverage_subject( module_path: "v2.test.execution.emit_host_module_equals_eval", decl_name: "emit_host_module_add_equals_eval_holds" ), - entry: "src/v2/test/claim/execution/emit_host_module_equals_eval_test.dag", - f: "emit_host_module_add_equals_eval_holds" - ), - retained_via_eval_agreement( + witness_entry: "src/v2/test/claim/execution/emit_host_module_equals_eval_test.dag", + witness_function: "emit_host_module_add_equals_eval_holds", + disposition: SelfEmittedNative { + backing_entry: "src/v2/test/claim/execution/long/emit_host_native_only_verdict_test.dag", + backing_function: "emit_host_native_only_module_holds" + } + }, + EmitCoverageFrontierRow { subject: coverage_subject( module_path: "v2.test.long.emit_host_produced_module_equals_eval", decl_name: "emit_host_produced_module_add_equals_eval_holds" ), - entry: "src/v2/test/claim/execution/long/emit_host_produced_module_equals_eval_test.dag", - f: "emit_host_produced_module_add_equals_eval_holds" - ), - retained_via_eval_agreement( + witness_entry: "src/v2/test/claim/execution/long/emit_host_produced_module_equals_eval_test.dag", + witness_function: "emit_host_produced_module_add_equals_eval_holds", + disposition: SelfEmittedNative { + backing_entry: "src/v2/test/claim/execution/long/emit_host_native_only_verdict_test.dag", + backing_function: "emit_host_native_only_produced_module_holds" + } + }, + EmitCoverageFrontierRow { subject: coverage_subject( module_path: "v2.extdeps.languages.rust_test", decl_name: "rust_shell_exec_run_family_target_model_staging" ), - entry: "src/v2/test/claim/execution/emit_host_shell_exec_run_equals_eval_test.dag", - f: "emit_host_shell_exec_run_equals_eval_holds" - ), - retained_via_eval_agreement( + witness_entry: "src/v2/test/claim/execution/emit_host_shell_exec_run_equals_eval_test.dag", + witness_function: "emit_host_shell_exec_run_equals_eval_holds", + disposition: SelfEmittedNative { + backing_entry: "src/v2/test/claim/execution/long/emit_host_native_only_verdict_test.dag", + backing_function: "emit_host_native_only_shell_exec_run_holds" + } + }, + EmitCoverageFrontierRow { subject: coverage_subject( module_path: "v2.test.execution.emit_host_variant_construct_equals_eval", decl_name: "emit_variant_construct_eval_subgraph_node" ), - entry: "src/v2/test/claim/execution/emit_host_variant_construct_equals_eval_test.dag", - f: "emit_host_variant_construct_equals_eval_holds" - ), - retained_via_eval_agreement( + witness_entry: "src/v2/test/claim/execution/emit_host_variant_construct_equals_eval_test.dag", + witness_function: "emit_host_variant_construct_equals_eval_holds", + disposition: SelfEmittedNative { + backing_entry: "src/v2/test/claim/execution/long/emit_host_native_only_verdict_test.dag", + backing_function: "emit_host_native_only_variant_construct_holds" + } + }, + EmitCoverageFrontierRow { subject: coverage_subject( module_path: "v2.test.execution.rust_emit_host_call_equals_eval", decl_name: "rust_emit_host_call_equals_eval_holds" ), - entry: "src/v2/test/claim/execution/rust_emit_host_call_equals_eval_test.dag", - f: "rust_emit_host_call_equals_eval_holds" - ), - retained_via_eval_agreement( + witness_entry: "src/v2/test/claim/execution/rust_emit_host_call_equals_eval_test.dag", + witness_function: "rust_emit_host_call_equals_eval_holds", + disposition: SelfEmittedNative { + backing_entry: "src/v2/test/claim/execution/long/emit_host_native_only_verdict_test.dag", + backing_function: "emit_host_native_only_call_holds" + } + }, + EmitCoverageFrontierRow { subject: coverage_subject( module_path: "v2.test.execution.rust_record_construct_emit_host_equals_eval", decl_name: "rust_record_construct_emit_host_equals_eval_holds" ), - entry: "src/v2/test/claim/execution/rust_record_construct_emit_host_equals_eval_test.dag", - f: "rust_record_construct_emit_host_equals_eval_holds" - ) + witness_entry: "src/v2/test/claim/execution/rust_record_construct_emit_host_equals_eval_test.dag", + witness_function: "rust_record_construct_emit_host_equals_eval_holds", + disposition: SelfEmittedNative { + backing_entry: "src/v2/test/claim/execution/long/emit_host_native_only_verdict_test.dag", + backing_function: "emit_host_native_only_record_construct_holds" + } + } ] fn coverage_frontier_total_holds() -> Bool { diff --git a/src/v2/compiler/self_host/emitter_producer_provenance.dag b/src/v2/compiler/self_host/emitter_producer_provenance.dag index 73dd6dcfd0b..8c9dae6afc5 100644 --- a/src/v2/compiler/self_host/emitter_producer_provenance.dag +++ b/src/v2/compiler/self_host/emitter_producer_provenance.dag @@ -168,10 +168,12 @@ data cssl_harness_realized_closure: RealizedEmitterClosure = RealizedEmitterClos ] } -// V2EmitterInterpreted closure for Lane B direct rust door: v2.compiler.emit (serialize_target -// composed with translate) plus v2.compiler.self_host.candidate_generation +// V2EmitterInterpreted closure for the GENERIC interpreted emit route: v2.compiler.emit +// (serialize_target composed with translate) plus v2.compiler.self_host.candidate_generation // (mint_provenanced_rust_artifact entry), without v1.compiler.emit_rust. Landed as first customer -// in session bright-bear-372. +// in session bright-bear-372. The direct rust door's run closure is +// realized_closure_for_v2_direct_rust_door_emit_run below — the door's production composition +// routes through v2.compiler.emit_produced, not v2.compiler.emit. data v2_interpreted_emitter_closure: RealizedEmitterClosure = RealizedEmitterClosure { emitter_module_paths: [ @@ -202,17 +204,21 @@ fn v2_interpreted_emitter_closure_seed_free_holds() -> Bool { !seed_emit_rust_reachable(closure: v2_interpreted_emitter_closure) } +// The door run's route: candidate_generation's module composition emits the inferred module's +// produced declaration through v2.compiler.emit_produced, whose bodied-arrow serialization lives in +// v2.compiler.translate. v2.compiler.emit is NOT on this route — it remains on the generic +// emit_module / fixture-lane routes carried by v2_interpreted_emitter_closure. fn realized_closure_for_v2_direct_rust_door_emit_run() -> RealizedEmitterClosure { RealizedEmitterClosure { emitter_module_paths: [ "v2.compiler.self_host.candidate_generation", - v2_emit_interpreted_qualified_module, + "v2.compiler.emit_produced", "v2.compiler.translate" ] } } -// Post-emit-run closure authority for Lane B direct rust door: mint_provenanced_rust_artifact calls this ONLY after generate_rust_emission_candidate (the serialize_target run) succeeds — never as a caller-supplied parameter. Paths name modules on the v2 interpreted emit route excluding v1.compiler.emit_rust. +// Post-emit-run closure authority for Lane B direct rust door: mint_provenanced_rust_artifact calls this ONLY after generate_rust_module_emission_candidate (the produced-decl emission run) succeeds — never as a caller-supplied parameter. Paths name modules on the v2 interpreted emit route excluding v1.compiler.emit_rust. fn v2_interpreted_emitter_closure_authority() -> RealizedEmitterClosure { realized_closure_for_v2_direct_rust_door_emit_run() } diff --git a/src/v2/compiler/self_host/v2_emitter_direct_rust_door_contract.dag b/src/v2/compiler/self_host/v2_emitter_direct_rust_door_contract.dag index 7ca3b05c493..3604336aca5 100644 --- a/src/v2/compiler/self_host/v2_emitter_direct_rust_door_contract.dag +++ b/src/v2/compiler/self_host/v2_emitter_direct_rust_door_contract.dag @@ -63,7 +63,7 @@ import v2.std.text { String } // projected from ProvenancedRustEmissionObservation, where a stop before the artifact reports // LeafNotReached with its stage instead of a refusal. -data v2_emitter_direct_rust_door_contract_dissolution_note: String = "DISSOLVES BY GREENING, never deletion. The prior condition — candidate_generation wires serialize_target, and the seed-independent written-source check greens — is OBSOLETE and was stale prose by 2026-08-05: emit IS already called, the real inferred tree rejects inside it, and the named seed-independent admission fold has been deleted. The condition in current vocabulary: the production observation reaches ArtifactProduced, all five leaf projections establish (source fidelity, producer identity, seed-emitter absence, SourceProduced qualification, missing-rule refusal), the known-red expectation row deletes in that same change, and the unchanged group root promotes to ordinary DiscoverySelection as the lane's permanent regression evidence." +data v2_emitter_direct_rust_door_contract_dissolution_note: String = "DISSOLVES BY GREENING, never deletion — GREENED 2026-09-06 by execution. Every clause of the condition in current vocabulary is met: the production observation reaches ArtifactProduced, all five leaf projections establish (source fidelity, producer identity, seed-emitter absence, SourceProduced qualification, missing-rule refusal), the known-red expectation row deleted in the same change, and the unchanged group root stands as ordinary DiscoverySelection — the lane's permanent regression evidence, which is why this contract module remains. Superseded readings, kept so the trail stays legible: the pre-2026-08-05 belief that candidate_generation never called emit, and the 2026-08-05 reading that emission rejected the real inferred tree on the grounding frontier — the frontier closed (six derivation rules in 04_infer) and emission landed as the single-produced-declaration module composition over v2.compiler.emit_produced." // HONEST CEILING on what the SCHEDULER learns, stated so no roster prose overclaims it. The group // value retains every child outcome — which leaf refused, which stage a LeafNotReached names — but diff --git a/src/v2/extdeps/languages/bash.dag b/src/v2/extdeps/languages/bash.dag index cf74e676225..6df3f4c249b 100644 --- a/src/v2/extdeps/languages/bash.dag +++ b/src/v2/extdeps/languages/bash.dag @@ -723,7 +723,7 @@ fn bash_posix_single_quote_transform(prefix: String) -> TokenClassEmitTransform open: posix_single_quote_delimiter, close: posix_single_quote_delimiter, escapes: [ - EmitSpellingEscape { from: posix_single_quote_delimiter, to: posix_single_quote_escape_encoding } + EmitSpellingEscape { from: chars(s: posix_single_quote_delimiter), to: chars(s: posix_single_quote_escape_encoding) } ] } } diff --git a/src/v2/extdeps/languages/dag.dag b/src/v2/extdeps/languages/dag.dag index 4f4caea7044..7a6a37a4c0f 100644 --- a/src/v2/extdeps/languages/dag.dag +++ b/src/v2/extdeps/languages/dag.dag @@ -150,7 +150,7 @@ import v2.std.compilers.target_model { derive_bodied_arrow_scaffold} import v2.std.logic { Bool } import std.algebra { Cons, Empty } -import v2.std.algebra { fold_list, for_all, is_empty, length, list_append, list_snoc_item } +import v2.std.algebra { fold_list, for_all, is_empty, length, list_append, list_map, list_snoc_item } import v2.std.diagnostic { Accepted, CorrectionNotModeled, @@ -414,14 +414,14 @@ fn dag_surface_operator_canonicalization_lookup( fn dag_keyword_lex_rule(token_class: Symbol, text: String) -> LexRule { TokenRule { token_class: token_class, - pattern: KeywordPattern { text: text } + pattern: KeywordPattern { text: chars(s: text) } } } fn dag_literal_lex_rule(token_class: Symbol, text: String) -> LexRule { TokenRule { token_class: token_class, - pattern: LiteralPattern { text: text } + pattern: LiteralPattern { text: chars(s: text) } } } @@ -3383,6 +3383,32 @@ fn dag_grammar_root() -> GrammarRoot { } } +// THE DECLARED GRAMMAR-PRODUCTION ROSTER, AS A NODE. Every production the grammar authority +// declares, projected to its emitted surface atom and gathered under one Conj root keyed by the +// production's name. This is the single authority an inference membership rule consults to ground +// grammar-projection identity atoms (e.g. dag_surface_module) by lookup -- the same frame as the +// declared-inhabitants roster: the roster root is the evidence, and it is never structurally equal +// to any member atom, so the self-evidence wall holds by construction. The roster derives from +// dag_grammar_root(), so a production added to the grammar joins the roster by construction. + +fn dag_grammar_production_roster_edge(production: GrammarProduction) -> Edge { + Edge { + label: Named { name: production.name }, + target: production.emitted + } +} + +fn dag_grammar_productions_roster_root() -> Node { + Node { + kind: TypeNode { connective: Conj }, + children: list_map( + xs: dag_grammar_root().productions, + f: dag_grammar_production_roster_edge + ), + occurrence_id: OccurrenceSynthetic + } +} + fn dag_g0_void_grammar() -> ParseGrammar { VoidGrammar } @@ -4109,6 +4135,23 @@ fn dag_int_type_binding() -> Symbol { ^dag_binding_type_int } +// THE BINDING→DENOTATION JOIN, declared once at the language authority. The resolver binds the +// surface spelling "Int" to the canonical binding symbol (dag_binding_spellings); what that +// binding DENOTES is the Int inhabitant declared at dag_declared_inhabitants_core. Every +// hand-rolled fixture facts lookup re-authors this join (dag_add_canonical_grounding_for, +// record_construct_canonical_grounding_for); the authority declares it once and infer consumes it +// (v2.compiler.infer infer_node_facts). Specimen-scope interim in the same frame as +// infer_node_declared_in_dag_inhabitants: deletes in favor of consuming resolution output when +// the resolver hands infer declaration-resolved identities directly (the namespace migration's +// completed state). +fn dag_binding_denotation(sym: Symbol) -> Optional { + if sym == dag_int_type_binding() { + optional_present(value: dag_int_inhabitant_node()) + } else { + optional_absent() + } +} + fn dag_binding_branded_atom_identity(node: Node) -> Optional { match node.kind { TypeNode { connective: Atom { identity: id } } => diff --git a/src/v2/extdeps/languages/python.dag b/src/v2/extdeps/languages/python.dag index 08555bcaa47..0e2d2207022 100644 --- a/src/v2/extdeps/languages/python.dag +++ b/src/v2/extdeps/languages/python.dag @@ -241,7 +241,7 @@ data python_singleton_facts_catalog: List = [ python_facts_ellipsis ] -data py_bool_grounding: BooleanAlgebra = bool_boolean_algebra +data py_bool_grounding: BooleanAlgebra = bool_boolean_algebra fn python_inhabitant_atom(id: Symbol) -> Node { Node { @@ -1215,7 +1215,7 @@ fn python_target_model() -> TargetModel { } fn python_lex_rule(token_class: Symbol, text: String) -> LexRule { - TokenRule { token_class: token_class, pattern: LiteralPattern { text: text } } + TokenRule { token_class: token_class, pattern: LiteralPattern { text: chars(s: text) } } } fn python_ident_lex_pattern() -> LexPattern { diff --git a/src/v2/extdeps/languages/typescript.dag b/src/v2/extdeps/languages/typescript.dag index 46fec44216c..aa9639a8532 100644 --- a/src/v2/extdeps/languages/typescript.dag +++ b/src/v2/extdeps/languages/typescript.dag @@ -250,7 +250,7 @@ data ts_facts_boolean: TsBooleanPrimitiveFacts = TsBooleanPrimitiveFacts { encoding: ^ts_repr_boolean_ecma_boolean, } -data ts_bool_grounding: BooleanAlgebra = bool_boolean_algebra +data ts_bool_grounding: BooleanAlgebra = bool_boolean_algebra data ts_numeric_facts_catalog: List = [ TsNumericFactsNumber { facts: ts_facts_number }, @@ -2054,7 +2054,7 @@ fn ts_fn_add_target_model() -> Node { fn ts_lex_rule(token_class: Symbol, text: String) -> LexRule { TokenRule { token_class: token_class, - pattern: LiteralPattern { text: text } + pattern: LiteralPattern { text: chars(s: text) } } } diff --git a/src/v2/std/collection.dag b/src/v2/std/collection.dag index 8fb9318dd84..fecf3113f6c 100644 --- a/src/v2/std/collection.dag +++ b/src/v2/std/collection.dag @@ -22,7 +22,7 @@ import v2.std.optional { optional_present, optional_prefer_first_present } -import v2.std.witness { Holds, Violates, Witness, witness_from_optional } +import v2.std.witness { Holds, Violates, Witness, witness_from_optional, witness_violates } fn optional_absent_unwrap_diagnostic() -> Diagnostic { Diagnostic { @@ -87,7 +87,7 @@ fn list_nth(xs: List, wanted: Int, absent: Diagnostic) -> Witness { xs: xs, empty: IndexedLookup { index: 0, - value: Violates { diagnostic: absent } + value: witness_violates(diagnostic: absent) }, cons: fn(acc, item) { IndexedLookup { diff --git a/src/v2/std/compilers/lexing.dag b/src/v2/std/compilers/lexing.dag index 5ec4896a25d..63f01f67108 100644 --- a/src/v2/std/compilers/lexing.dag +++ b/src/v2/std/compilers/lexing.dag @@ -97,7 +97,7 @@ fn phrase_to_lex_rule_set(p: LexicalPhrase) -> LexRuleSet { xs: acc, item: TokenRule { token_class: s.token_class, - pattern: LiteralPattern { text: s.fragment } + pattern: LiteralPattern { text: chars(s: s.fragment) } } ) } diff --git a/src/v2/std/compilers/target_model.dag b/src/v2/std/compilers/target_model.dag index 2f9871dcf72..6a623a411e7 100644 --- a/src/v2/std/compilers/target_model.dag +++ b/src/v2/std/compilers/target_model.dag @@ -12,7 +12,7 @@ import std.algebra { FreeMonoid, FreeSemigroup } -import v2.std.text { string_replace } +import v2.std.text { String, string_replace } import v2.std.algebra { Equal, Greater, @@ -579,8 +579,8 @@ fn produced_decl_render_from_rows( // decomposes into this four-arm spelling algebra. Escapes apply in list order (a backslash rule // must precede the rules that introduce backslashes). type EmitSpellingEscape { - from: String - to: String + from: v2.std.text.String + to: v2.std.text.String } type TokenClassEmitTransform @@ -862,9 +862,9 @@ fn target_lex_rule_literal_step( if c == token_class { match pattern { LiteralPattern { text: text } => - Accepted { value: text, diagnostics: None } + Accepted { value: chars_to_string(chars: text, start: 0, end: length(xs: text)), diagnostics: None } KeywordPattern { text: text } => - Accepted { value: text, diagnostics: None } + Accepted { value: chars_to_string(chars: text, start: 0, end: length(xs: text)), diagnostics: None } _ => Rejected { diagnostics: diagnostics_singleton( @@ -938,7 +938,7 @@ fn render_target_text(t: TargetText) -> String { } fn target_text_is_empty(t: TargetText) -> Bool { - is_empty(xs: t.source) + is_empty(xs: chars(s: t.source)) } fn bound_tokens_source_text( @@ -991,7 +991,7 @@ fn symbol_interned_spelling(binding: Symbol) -> Outcome { // shape declares `from` itself as FreeSemigroup so the empty row is unwritable; the climb // trigger is char-level literal sugar in the .dag realization, without which producers cannot // author the nonempty carrier from an escape row's quoted spelling. -fn apply_emit_spelling_escapes(spelling: String, escapes: List) -> String { +fn apply_emit_spelling_escapes(spelling: v2.std.text.String, escapes: List) -> v2.std.text.String { fold_list( xs: escapes, empty: spelling, @@ -1013,8 +1013,10 @@ fn apply_emit_spelling_transform( EmitSpellingIdentity => spelling EmitSpellingConstant { value: v } => v EmitSpellingWrap { prefix: p, suffix: s } => concat(p, concat(spelling, s)) - EmitSpellingQuote { prefix: p, open: o, close: c, escapes: es } => - concat(p, concat(o, concat(apply_emit_spelling_escapes(spelling: spelling, escapes: es), c))) + EmitSpellingQuote { prefix: p, open: o, close: c, escapes: es } => { + let escaped = apply_emit_spelling_escapes(spelling: chars(s: spelling), escapes: es) + concat(p, concat(o, concat(chars_to_string(chars: escaped, start: 0, end: length(xs: escaped)), c))) + } } } @@ -1608,6 +1610,89 @@ fn target_model_canonical_operation_wire_node(operation: CanonicalOperation) -> } } +// THE DECLARED CANONICAL-OPERATION ROSTER, AS A NODE. Every CanonicalOperation this authority +// declares, rendered by target_model_canonical_operation_wire_node and gathered under one Conj +// root. This is the single authority an inference membership rule consults to ground canonical- +// operation wire atoms by lookup (the same frame as the dag authority's declared-inhabitants +// roster): the roster root is the evidence, and it is never structurally equal to any member, so +// the self-evidence wall holds by construction. Adding an operation to this authority means adding +// its row here -- the roster is closed over exactly the operations declared above. + +fn target_model_canonical_operation_roster_edge( + name: Symbol, + operation: CanonicalOperation +) -> Edge { + Edge { + label: Named { name: name }, + target: target_model_canonical_operation_wire_node(operation: operation) + } +} + +fn target_model_canonical_operations_roster_root() -> Node { + Node { + kind: TypeNode { connective: Conj }, + children: [ + target_model_canonical_operation_roster_edge( + name: ^canonical_operation_roster_member_add, + operation: canonical_operation_op_add() + ), + target_model_canonical_operation_roster_edge( + name: ^canonical_operation_roster_member_mul, + operation: canonical_operation_op_mul() + ), + target_model_canonical_operation_roster_edge( + name: ^canonical_operation_roster_member_negate, + operation: canonical_operation_op_negate() + ), + target_model_canonical_operation_roster_edge( + name: ^canonical_operation_roster_member_and, + operation: canonical_operation_op_and() + ), + target_model_canonical_operation_roster_edge( + name: ^canonical_operation_roster_member_or, + operation: canonical_operation_op_or() + ), + target_model_canonical_operation_roster_edge( + name: ^canonical_operation_roster_member_not, + operation: canonical_operation_op_not() + ), + target_model_canonical_operation_roster_edge( + name: ^canonical_operation_roster_member_sub, + operation: canonical_operation_op_sub() + ), + target_model_canonical_operation_roster_edge( + name: ^canonical_operation_roster_member_div, + operation: canonical_operation_op_div() + ), + target_model_canonical_operation_roster_edge( + name: ^canonical_operation_roster_member_lt, + operation: canonical_operation_op_lt() + ), + target_model_canonical_operation_roster_edge( + name: ^canonical_operation_roster_member_le, + operation: canonical_operation_op_le() + ), + target_model_canonical_operation_roster_edge( + name: ^canonical_operation_roster_member_gt, + operation: canonical_operation_op_gt() + ), + target_model_canonical_operation_roster_edge( + name: ^canonical_operation_roster_member_ge, + operation: canonical_operation_op_ge() + ), + target_model_canonical_operation_roster_edge( + name: ^canonical_operation_roster_member_eq, + operation: canonical_operation_op_eq() + ), + target_model_canonical_operation_roster_edge( + name: ^canonical_operation_roster_member_ne, + operation: canonical_operation_op_ne() + ) + ], + occurrence_id: OccurrenceSynthetic + } +} + fn canonical_operation_equal( left: CanonicalOperation, right: CanonicalOperation @@ -4018,7 +4103,7 @@ fn target_value_expression_char_unicode_scalar(codepoint: Char) -> TargetValueEx kind: TypeNode { connective: Atom { identity: symbol_intern_lexeme( - lexeme: list_snoc_item(xs: Empty, item: codepoint) + lexeme: chars_to_string(chars: list_snoc_item(xs: Empty, item: codepoint), start: 0, end: 1) ) } }, @@ -12947,6 +13032,19 @@ fn target_transform_surface_op_atom(node: Node) -> Outcome { target_transform_positional_operand_binding(node: node, index: 0) } +fn target_transform_operator_child(node: Node) -> Outcome { + match list_at_optional(xs: node_positional_child_targets(node: node), index: 0) { + Present { value: child } => outcome_accepted(value: child) + Absent => + outcome_rejected( + target_value_expr_diagnostic( + reason: ^target_value_expr_reason_transform_shape_invalid, + node: node + ) + ) + } +} + fn target_value_expr_declared_inhabitants_from_target(target: TargetModel) -> Outcome { target_model_declared_inhabitants(target: target) } diff --git a/src/v2/std/integer.dag b/src/v2/std/integer.dag index a1ff477aecc..5329cb25a31 100644 --- a/src/v2/std/integer.dag +++ b/src/v2/std/integer.dag @@ -1040,7 +1040,7 @@ fn integer_decimal_digits_snoc( } fn integer_string_to_decimal_digits_step( - s: String, + s: v2.std.text.String, acc: FreeMonoid ) -> Optional> { match string_head(s: s) { @@ -1062,7 +1062,7 @@ fn integer_string_to_decimal_digits_step( } fn integer_string_to_decimal_digits_optional(s: String) -> Optional> { - integer_string_to_decimal_digits_step(s: s, acc: Empty) + integer_string_to_decimal_digits_step(s: chars(s: s), acc: Empty) } fn integer_decimal_nonzero_magnitude_to_int(m: DecimalNonZeroMagnitude) -> Int { diff --git a/src/v2/std/provenance.dag b/src/v2/std/provenance.dag index 82b8ea8af2d..784ead56b55 100644 --- a/src/v2/std/provenance.dag +++ b/src/v2/std/provenance.dag @@ -15,7 +15,7 @@ import v2.std.diagnostic { import v2.std.node { Node, Symbol } import v2.std.optional { Absent, Present } import std.occurrence_identity { OccurrenceId } -import v2.std.witness { Holds, Violates, Witness, witness_from_optional } +import v2.std.witness { Holds, Violates, Witness, witness_from_optional, witness_violates } type OriginEvent = FromSource { locus: Locus } @@ -85,7 +85,7 @@ fn span_index_resolve_textual_locus_from_ids( ) -> Witness { fold_list( xs: ids, - empty: Violates { diagnostic: span_index_origin_chain_exhausted_diagnostic() }, + empty: witness_violates(diagnostic: span_index_origin_chain_exhausted_diagnostic()), cons: fn(acc, id) { match acc { Holds { value: _ } => acc diff --git a/src/v2/std/qualified_name.dag b/src/v2/std/qualified_name.dag index 2c7c014014c..2d42e4c6837 100644 --- a/src/v2/std/qualified_name.dag +++ b/src/v2/std/qualified_name.dag @@ -28,7 +28,7 @@ import v2.std.optional { import v2.std.compilers.lexing { symbol_intern_lexeme, symbol_lexeme } import v2.std.logic { Bool } import v2.std.node { Atom, Conj, Edge, Named, Node, NodeFold, Symbol, TypeNode, fold_node } -import v2.std.text { String, string_is_empty } +import v2.std.text { String } type QualifiedName = FreeMonoid @@ -166,7 +166,7 @@ fn qualified_name_from_string_segments(segments: List) -> FreeMonoid FreeMonoid { - if string_is_empty(s: dotted) { + if string_length(s: dotted) == 0 { Empty } else { qualified_name_from_string_segments(segments: dotted.split(delimiter: ".")) diff --git a/src/v2/std/witness.dag b/src/v2/std/witness.dag index 1ea0a6a91cc..1a22af5e31c 100644 --- a/src/v2/std/witness.dag +++ b/src/v2/std/witness.dag @@ -15,6 +15,18 @@ fn witness_from_optional(opt: Optional, absent: Diagnostic) -> Witness } } +// A bare `Violates { .. }` construction nested inside a struct literal, fold seed, or call +// argument gives the v1 emitter no Witness-headed type to read its carrier from (the enclosing +// expression's type is what reaches the constructor, never the argument position's), so those +// sites emitted a located refusal. Constructing through this fn puts the variant in a +// Witness-headed position -- the same shape witness_from_optional already emits -- and lets the +// carrier unify at the call instead. Dissolves into bare constructions once inference records +// per-call type-argument substitutions (the __applied_type_args channel named as the next-rung +// trigger in src/v1/05_emit_rust.dag's rust_witness_carrier_from_witness_headed_type_node note). +fn witness_violates(diagnostic: Diagnostic) -> Witness { + Violates { diagnostic: diagnostic } +} + type StructuralPropertyWitness { property: Symbol evidence: Node diff --git a/src/v2/test/claim/execution/dag_binding_denotation_test.dag b/src/v2/test/claim/execution/dag_binding_denotation_test.dag new file mode 100644 index 00000000000..b242c4998e0 --- /dev/null +++ b/src/v2/test/claim/execution/dag_binding_denotation_test.dag @@ -0,0 +1,141 @@ +module v2.test.execution.dag_binding_denotation + +import v2.compiler.infer { + DerivedGrounding, + GroundingNotDerived, + InferredTree +} +import v2.compiler.self_host.direct_rust_door_fixture { + direct_rust_door_specimen_inferred +} +import v2.extdeps.languages.dag { + dag_binding_denotation, + dag_int_inhabitant_node, + dag_int_type_binding +} +import v2.std.algebra { fold_list } +import v2.std.diagnostic { Accepted, Rejected } +import v2.std.integer { Int } +import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } +import v2.std.logic { Bool } +import v2.std.node { Atom, Node, TypeNode, node_subtree_nodes } +import v2.std.optional { Absent, Present } + +data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly + +// THE ENROLLED CONTROLS FOR THE BINDING-DENOTATION DERIVATION (`v2.compiler.infer` +// infer_node_facts consulting `v2.extdeps.languages.dag` dag_binding_denotation). The specimen +// is the direct-rust-door fixture's ingest -- the corpus's smallest real-ingest dag module whose +// resolved tree carries canonical binding atoms (`dag_binding_type_int`, the resolver's binding +// of the surface spelling "Int"). The positive control pins both the derivation and its evidence: +// every canonical Int binding atom carries DerivedGrounding whose structural evidence is the dag +// authority's Int inhabitant, and the census count (4) pins that the witness actually saw them. +// The boundary control pins the rule's edge at the authority itself: dag_binding_denotation +// declares no denotation for the operand bindings `x`/`y` -- the table is a lookup, not an +// invention. (The operand atoms derive today, but by a different rule -- the binding-reference +// derivation in infer_atom_grounding_rules_test.dag -- not by this one.) Deleting the rule +// reddens the positive control; widening the table to operand bindings reddens the boundary +// control. + +type BindingDenotationCensus { + int_atoms: Int + int_atoms_derived: Int + operand_atoms: Int + operand_atoms_frontier: Int +} + +fn is_int_binding_atom(n: Node) -> Bool { + match n.kind { + TypeNode { connective: Atom { identity: id } } => id == dag_int_type_binding() + _ => false + } +} + +fn is_bare_operand_atom(n: Node) -> Bool { + match n.kind { + TypeNode { connective: Atom { identity: id } } => (id == ^x) || (id == ^y) + _ => false + } +} + +fn int_binding_atom_derived(inferred: InferredTree, n: Node) -> Bool { + match inferred.facts.lookup(n) { + Absent => false + Present { value: facts } => + match facts.grounding { + GroundingNotDerived { node: _ } => false + DerivedGrounding { grounding: g } => + g.witness.structural.evidence == dag_int_inhabitant_node() + } + } +} + +fn bare_operand_atom_stays_frontier(inferred: InferredTree, n: Node) -> Bool { + match inferred.facts.lookup(n) { + Absent => false + Present { value: facts } => + match facts.grounding { + GroundingNotDerived { node: _ } => true + DerivedGrounding { grounding: _ } => false + } + } +} + +fn binding_denotation_census_step( + inferred: InferredTree, + acc: BindingDenotationCensus, + n: Node +) -> BindingDenotationCensus { + if is_int_binding_atom(n: n) { + BindingDenotationCensus { + int_atoms: acc.int_atoms + 1, + int_atoms_derived: acc.int_atoms_derived + (if int_binding_atom_derived(inferred: inferred, n: n) { 1 } else { 0 }), + operand_atoms: acc.operand_atoms, + operand_atoms_frontier: acc.operand_atoms_frontier + } + } else { + if is_bare_operand_atom(n: n) { + BindingDenotationCensus { + int_atoms: acc.int_atoms, + int_atoms_derived: acc.int_atoms_derived, + operand_atoms: acc.operand_atoms + 1, + operand_atoms_frontier: acc.operand_atoms_frontier + (if bare_operand_atom_stays_frontier(inferred: inferred, n: n) { 1 } else { 0 }) + } + } else { + acc + } + } +} + +fn binding_denotation_census(inferred: InferredTree) -> BindingDenotationCensus { + fold_list( + xs: node_subtree_nodes(root: inferred.root), + empty: BindingDenotationCensus { + int_atoms: 0, + int_atoms_derived: 0, + operand_atoms: 0, + operand_atoms_frontier: 0 + }, + cons: fn(acc, n) { binding_denotation_census_step(inferred: inferred, acc: acc, n: n) } + ) +} + +test fn dag_binding_denotation_derives_int_binding_atoms_holds() -> Bool { + match direct_rust_door_specimen_inferred() { + Rejected { diagnostics: _ } => false + Accepted { value: inferred, diagnostics: _ } => + let census = binding_denotation_census(inferred: inferred) + (census.int_atoms == 4) && (census.int_atoms_derived == 4) + } +} + +test fn dag_binding_denotation_declares_no_denotation_for_operand_bindings_holds() -> Bool { + match dag_binding_denotation(sym: ^x) { + Present { value: _ } => false + Absent => + match dag_binding_denotation(sym: ^y) { + Present { value: _ } => false + Absent => true + } + } +} diff --git a/src/v2/test/claim/execution/emit_on_demand_family_crate_witness_test.dag b/src/v2/test/claim/execution/emit_on_demand_family_crate_witness_test.dag index fe93264f2a7..c6af53dd523 100644 --- a/src/v2/test/claim/execution/emit_on_demand_family_crate_witness_test.dag +++ b/src/v2/test/claim/execution/emit_on_demand_family_crate_witness_test.dag @@ -13,7 +13,7 @@ import extdeps.realization.emit_on_demand_host { native_cache_workspace_root, emit_on_demand_evict_native_cache, } import extdeps.communication.medium { Medium } -import v2.compiler.emit_module { EmitFamilyMember, emit_family } +import v2.compiler.emit_module { EmitFamilyMember, EmittedFamilyCrate, emit_family } import v2.compiler.emit_host { emit_host_octets_byte_string, run_emit_host_cached_with_run_args, @@ -167,6 +167,34 @@ fn family_crate_native_key(members: List) -> ContentHash { ) } +// THE PRIMARY FAMILY CRATE, EMITTED ONCE OUTSIDE THE CLAIM FOLD. Every native-only verdict claim +// for this family (v2.test.long.emit_host_native_only_verdict's six complement/meet/join arms) +// begins with the same pure walk — the member list, emit_family, the native key — and the +// required floor measured all six within one runner-swing of the 500ms per-claim ceiling (run +// 34315228217: 458-487ms cpu against limit_ms=500). The members are not portable (InferredTree's +// facts PartialFunction), so the share point is the deepest portable stage: the emitted crate +// source plus its native key. Nullary and warm-enrolled in +// `v2.workflow.floor_pure_producer_share` (floor_cross_claim_pure_producers_warm), which carries +// the measured recompute/sharing/serve case; the required floor forces it during strict +// preparation, outside every per-claim budget, and each claim pays only the cached native run. +fn logic_family_primary_emitted() -> Outcome { + let members = family_crate_members() + match emit_family( + members: members, + transport_target: rust_logic_family_target_model_staging() + ) { + Accepted { value: source, diagnostics: _ } => + Accepted { + value: EmittedFamilyCrate { + source: source, + native_key: family_crate_native_key(members: members) + }, + diagnostics: None + } + Rejected { diagnostics: d } => Rejected { diagnostics: d } + } +} + fn family_crate_fixture_inputs() -> Inputs { inputs_for_emit_host( claim_input_root: dag_meet_eval_subgraph_fixture, diff --git a/src/v2/test/claim/execution/emit_on_demand_match_loop_fold_family_witness_test.dag b/src/v2/test/claim/execution/emit_on_demand_match_loop_fold_family_witness_test.dag index 41336c2526e..068aee1cb71 100644 --- a/src/v2/test/claim/execution/emit_on_demand_match_loop_fold_family_witness_test.dag +++ b/src/v2/test/claim/execution/emit_on_demand_match_loop_fold_family_witness_test.dag @@ -31,7 +31,7 @@ import extdeps.realization.emit_on_demand_host { native_cache_workspace_root, emit_on_demand_evict_native_cache, } import extdeps.communication.medium { Medium } -import v2.compiler.emit_module { EmitFamilyMember, emit_family } +import v2.compiler.emit_module { EmitFamilyMember, EmittedFamilyCrate, emit_family } import v2.compiler.emit_host { emit_host_octets_byte_string, run_emit_host_cached_with_run_args, @@ -235,6 +235,46 @@ fn mlf_family_native_key(members: List) -> ContentHash { ) } +// THE PRIMARY FAMILY CRATE, EMITTED ONCE OUTSIDE THE CLAIM FOLD. Every native-only verdict claim +// for this family (v2.test.long.emit_host_native_only_verdict's six match/loop/fold arms) begins +// with the same pure walk — three primary trees, the member list, emit_family, the native key — +// and the required floor measured all six within one runner-swing of the 500ms per-claim ceiling +// (run 34315228217: 462-504ms cpu, loop_holds over). The members are not portable (InferredTree's +// facts PartialFunction), so the share point is the deepest portable stage: the emitted crate +// source plus its native key. Nullary and warm-enrolled in +// `v2.workflow.floor_pure_producer_share` (floor_cross_claim_pure_producers_warm), which carries +// the measured recompute/sharing/serve case; the required floor forces it during strict +// preparation, outside every per-claim budget, and each claim pays only the cached native run. +fn mlf_family_primary_emitted() -> Outcome { + match match_primary_tree() { + Accepted { value: match_tree, diagnostics: _ } => + match loop_primary_tree() { + Accepted { value: loop_tree, diagnostics: _ } => + let members = mlf_family_members( + match_tree: match_tree, + loop_tree: loop_tree, + fold_tree: fold_primary_tree() + ) + match emit_family( + members: members, + transport_target: rust_match_loop_fold_family_target_model_staging() + ) { + Accepted { value: source, diagnostics: _ } => + Accepted { + value: EmittedFamilyCrate { + source: source, + native_key: mlf_family_native_key(members: members) + }, + diagnostics: None + } + Rejected { diagnostics: d } => Rejected { diagnostics: d } + } + Rejected { diagnostics: d } => Rejected { diagnostics: d } + } + Rejected { diagnostics: d } => Rejected { diagnostics: d } + } +} + fn mlf_family_member_run( workspace: String, source: Medium, diff --git a/src/v2/test/claim/execution/infer_atom_grounding_rules_test.dag b/src/v2/test/claim/execution/infer_atom_grounding_rules_test.dag new file mode 100644 index 00000000000..681fe12287c --- /dev/null +++ b/src/v2/test/claim/execution/infer_atom_grounding_rules_test.dag @@ -0,0 +1,182 @@ +module v2.test.execution.infer_atom_grounding_rules + +import std.occurrence_identity { OccurrenceSynthetic } +import v2.compiler.infer { + DerivedGrounding, + GroundingNotDerived, + InferredTree, + infer +} +import v2.compiler.self_host.direct_rust_door_fixture { + direct_rust_door_specimen_inferred +} +import v2.extdeps.languages.dag { + dag_grammar_productions_roster_root +} +import v2.std.algebra { fold_list } +import v2.std.compilers.target_model { target_model_canonical_operations_roster_root } +import v2.std.diagnostic { Accepted, Rejected } +import v2.std.integer { Int } +import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } +import v2.std.logic { Bool } +import v2.std.node { + Atom, + Conj, + Named, + Node, + TypeNode, + node_subtree_nodes +} +import v2.std.optional { Absent, optional_present, Present } + +data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly + +// THE ENROLLED CONTROLS FOR THE THREE ATOM-GROUNDING DERIVATIONS (`v2.compiler.infer` +// infer_node_facts): each is a lookup into a declared authority, never an invention. +// - canonical-operations roster: the resolver canonicalizes surface operators to the +// target-model authority's declared operations; the wire atoms (the AlgebraPrimitive +// discriminant and its ring_field_add field) are members of the authority's roster, and +// derive with the roster root as evidence. +// - grammar-productions roster: the bridge projects a production's parse into (identity atom, +// captured content); the identity atom (dag_surface_module) is the production's declared +// emitted atom, and derives with the grammar roster root as evidence. +// - binding references: an operand atom (x, y) derives with the type its enclosing arrow's +// domain declares for its binding -- the declaration-site annotation, itself derived. +// The specimen is the direct-rust-door fixture's ingest. Each control pins BOTH the derivation +// and the evidence identity, and the census pins that the witness saw the population. The +// boundary control pins that a bare atom with no authority membership and no domain declaration +// stays on the frontier. The closing control pins the specimen's full closure: zero frontier +// nodes of fifteen. Deleting a rule reddens its control; widening a rule to non-members reddens +// the boundary control. + +fn atom_grounding_node_evidence(inferred: InferredTree, n: Node) -> Optional { + match inferred.facts.lookup(n) { + Absent => Absent + Present { value: facts } => + match facts.grounding { + GroundingNotDerived { node: _ } => Absent + DerivedGrounding { grounding: g } => optional_present(value: g.witness.structural.evidence) + } + } +} + +fn atom_grounding_count_evidence( + inferred: InferredTree, + wanted: Node, + acc: Int, + n: Node +) -> Int { + match atom_grounding_node_evidence(inferred: inferred, n: n) { + Absent => acc + Present { value: evidence } => + if evidence == wanted { acc + 1 } else { acc } + } +} + +fn atom_grounding_census_frontier(inferred: InferredTree) -> Int { + fold_list( + xs: node_subtree_nodes(root: inferred.root), + empty: 0, + cons: fn(acc, n) { + match inferred.facts.lookup(n) { + Absent => acc + Present { value: facts } => + match facts.grounding { + GroundingNotDerived { node: _ } => acc + 1 + DerivedGrounding { grounding: _ } => acc + } + } + } + ) +} + +test fn canonical_operations_roster_grounds_wire_atoms_holds() -> Bool { + match direct_rust_door_specimen_inferred() { + Rejected { diagnostics: _ } => false + Accepted { value: inferred, diagnostics: _ } => + let roster = target_model_canonical_operations_roster_root() + fold_list( + xs: node_subtree_nodes(root: inferred.root), + empty: 0, + cons: fn(acc, n) { atom_grounding_count_evidence(inferred: inferred, wanted: roster, acc: acc, n: n) } + ) == 2 + } +} + +test fn grammar_productions_roster_grounds_module_atom_holds() -> Bool { + match direct_rust_door_specimen_inferred() { + Rejected { diagnostics: _ } => false + Accepted { value: inferred, diagnostics: _ } => + let roster = dag_grammar_productions_roster_root() + fold_list( + xs: node_subtree_nodes(root: inferred.root), + empty: 0, + cons: fn(acc, n) { atom_grounding_count_evidence(inferred: inferred, wanted: roster, acc: acc, n: n) } + ) == 1 + } +} + +fn is_domain_int_binding_evidence(inferred: InferredTree, n: Node) -> Bool { + match atom_grounding_node_evidence(inferred: inferred, n: n) { + Absent => false + Present { value: evidence } => + match evidence.kind { + TypeNode { connective: Atom { identity: id } } => id == ^dag_binding_type_int + _ => false + } + } +} + +fn is_operand_atom(n: Node) -> Bool { + match n.kind { + TypeNode { connective: Atom { identity: id } } => + (id == ^x) || (id == ^y) + _ => false + } +} + +test fn binding_reference_derives_parameter_atoms_holds() -> Bool { + match direct_rust_door_specimen_inferred() { + Rejected { diagnostics: _ } => false + Accepted { value: inferred, diagnostics: _ } => + fold_list( + xs: node_subtree_nodes(root: inferred.root), + empty: 0, + cons: fn(acc, n) { + if is_operand_atom(n: n) && is_domain_int_binding_evidence(inferred: inferred, n: n) { + acc + 1 + } else { + acc + } + } + ) == 2 + } +} + +test fn atom_rules_leave_unbound_atom_on_the_frontier_holds() -> Bool { + let unbound = Node { + kind: TypeNode { connective: Atom { identity: ^no_authority_declares_this_symbol } }, + children: [], + occurrence_id: OccurrenceSynthetic + } + match infer(tree: unbound) { + Rejected { diagnostics: _ } => false + Accepted { value: inferred, diagnostics: _ } => + match inferred.facts.lookup(unbound) { + Absent => false + Present { value: facts } => + match facts.grounding { + GroundingNotDerived { node: _ } => true + DerivedGrounding { grounding: _ } => false + } + } + } +} + +test fn door_specimen_fully_derives_holds() -> Bool { + match direct_rust_door_specimen_inferred() { + Rejected { diagnostics: _ } => false + Accepted { value: inferred, diagnostics: _ } => + atom_grounding_census_frontier(inferred: inferred) == 0 + } +} diff --git a/src/v2/test/claim/execution/infer_product_introduction_test.dag b/src/v2/test/claim/execution/infer_product_introduction_test.dag new file mode 100644 index 00000000000..b5a9d98e6a8 --- /dev/null +++ b/src/v2/test/claim/execution/infer_product_introduction_test.dag @@ -0,0 +1,254 @@ +module v2.test.execution.infer_product_introduction + +import std.occurrence_identity { OccurrenceSynthetic } +import v2.compiler.infer { + DerivedGrounding, + GroundingNotDerived, + InferredTree, + infer +} +import v2.compiler.self_host.direct_rust_door_fixture { + direct_rust_door_specimen_inferred +} +import v2.extdeps.languages.dag { + dag_int_inhabitant_node +} +import v2.std.algebra { fold_list } +import v2.std.collection { List, list_at_optional } +import v2.std.diagnostic { Accepted, Rejected } +import v2.std.integer { Int } +import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } +import v2.std.logic { Bool } +import v2.std.node { + Arrow, + Atom, + Conj, + Edge, + Named, + Node, + TypeNode, + node_subtree_nodes +} +import v2.std.optional { Absent, Present } + +data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly + +// THE ENROLLED CONTROLS FOR THE PRODUCT-INTRODUCTION DERIVATION (`v2.compiler.infer` +// infer_gather_product_row_on_entries): a non-roster Conj or Arrow whose every child carries +// DerivedGrounding derives, its evidence the same shape re-formed over the children's grounding +// evidence. The specimen is the direct-rust-door fixture's ingest -- the corpus's smallest +// real-ingest dag module. The census pins that the witness actually saw the population: four +// Conj nodes, all derived (the grammar-projection root included -- it cascaded once the atom +// rules grounded its children), and one bodied Arrow, derived. The evidence control pins the +// composed evidence's SHAPE on the parameter conj: a Conj whose named children target the dag +// authority's Int inhabitant, never the source node (the self-evidence wall). The two boundary +// controls pin the refusal edge on hand-built trees: a conj with a frontier child stays frontier, +// and a childless conj -- no evidence to compose -- stays frontier. Deleting the rule reddens the +// census and evidence controls; widening it to partially-evidenced or childless products reddens +// the boundary controls. + +type ProductIntroductionCensus { + conjs: Int + conjs_derived: Int + arrows: Int + arrows_derived: Int +} + +fn product_introduction_node_derived(inferred: InferredTree, n: Node) -> Bool { + match inferred.facts.lookup(n) { + Absent => false + Present { value: facts } => + match facts.grounding { + GroundingNotDerived { node: _ } => false + DerivedGrounding { grounding: _ } => true + } + } +} + +fn product_introduction_census_step( + inferred: InferredTree, + acc: ProductIntroductionCensus, + n: Node +) -> ProductIntroductionCensus { + match n.kind { + TypeNode { connective: Conj } => + ProductIntroductionCensus { + conjs: acc.conjs + 1, + conjs_derived: acc.conjs_derived + (if product_introduction_node_derived(inferred: inferred, n: n) { 1 } else { 0 }), + arrows: acc.arrows, + arrows_derived: acc.arrows_derived + } + TypeNode { connective: Arrow } => + ProductIntroductionCensus { + conjs: acc.conjs, + conjs_derived: acc.conjs_derived, + arrows: acc.arrows + 1, + arrows_derived: acc.arrows_derived + (if product_introduction_node_derived(inferred: inferred, n: n) { 1 } else { 0 }) + } + _ => acc + } +} + +fn product_introduction_census(inferred: InferredTree) -> ProductIntroductionCensus { + fold_list( + xs: node_subtree_nodes(root: inferred.root), + empty: ProductIntroductionCensus { + conjs: 0, + conjs_derived: 0, + arrows: 0, + arrows_derived: 0 + }, + cons: fn(acc, n) { product_introduction_census_step(inferred: inferred, acc: acc, n: n) } + ) +} + +fn params_conj_children_match(children: List) -> Bool { + match list_at_optional(xs: children, index: 0) { + Absent => false + Present { value: first } => + match list_at_optional(xs: children, index: 1) { + Absent => false + Present { value: second } => + match list_at_optional(xs: children, index: 2) { + Absent => + match first.label { + Named { name: a } => + match second.label { + Named { name: b } => (a == ^x) && (b == ^y) + _ => false + } + _ => false + } + Present { value: _ } => false + } + } + } +} + +fn is_params_conj(n: Node) -> Bool { + match n.kind { + TypeNode { connective: Conj } => params_conj_children_match(children: n.children) + _ => false + } +} + +fn evidence_children_target_int_inhabitant(children: List) -> Bool { + match list_at_optional(xs: children, index: 0) { + Absent => false + Present { value: first } => + match list_at_optional(xs: children, index: 1) { + Absent => false + Present { value: second } => + match list_at_optional(xs: children, index: 2) { + Absent => + (first.target == dag_int_inhabitant_node()) + && (second.target == dag_int_inhabitant_node()) + Present { value: _ } => false + } + } + } +} + +fn params_conj_evidence_is_composed_over_int_inhabitant(inferred: InferredTree, n: Node) -> Bool { + match inferred.facts.lookup(n) { + Absent => false + Present { value: facts } => + match facts.grounding { + GroundingNotDerived { node: _ } => false + DerivedGrounding { grounding: g } => + let evidence = g.witness.structural.evidence + match evidence.kind { + TypeNode { connective: Conj } => + evidence_children_target_int_inhabitant(children: evidence.children) + _ => false + } + } + } +} + +fn params_conj_evidence_seen(inferred: InferredTree) -> Bool { + fold_list( + xs: node_subtree_nodes(root: inferred.root), + empty: false, + cons: fn(acc, n) { + if is_params_conj(n: n) { + params_conj_evidence_is_composed_over_int_inhabitant(inferred: inferred, n: n) + } else { + acc + } + } + ) +} + +test fn product_introduction_derives_fully_evidenced_products_holds() -> Bool { + match direct_rust_door_specimen_inferred() { + Rejected { diagnostics: _ } => false + Accepted { value: inferred, diagnostics: _ } => + let census = product_introduction_census(inferred: inferred) + (census.conjs == 4) && (census.conjs_derived == 4) + && (census.arrows == 1) && (census.arrows_derived == 1) + } +} + +test fn product_introduction_composed_evidence_carries_child_groundings_holds() -> Bool { + match direct_rust_door_specimen_inferred() { + Rejected { diagnostics: _ } => false + Accepted { value: inferred, diagnostics: _ } => + params_conj_evidence_seen(inferred: inferred) + } +} + +test fn product_introduction_leaves_childless_conj_on_the_frontier_holds() -> Bool { + let childless = Node { + kind: TypeNode { connective: Conj }, + children: [], + occurrence_id: OccurrenceSynthetic + } + match infer(tree: childless) { + Rejected { diagnostics: _ } => false + Accepted { value: inferred, diagnostics: _ } => + match inferred.facts.lookup(childless) { + Absent => false + Present { value: facts } => + match facts.grounding { + GroundingNotDerived { node: _ } => true + DerivedGrounding { grounding: _ } => false + } + } + } +} + +fn product_introduction_hand_tree_grounding(tree: Node, n: Node) -> Int { + match infer(tree: tree) { + Rejected { diagnostics: _ } => 2 + Accepted { value: inferred, diagnostics: _ } => + match inferred.facts.lookup(n) { + Absent => 2 + Present { value: facts } => + match facts.grounding { + GroundingNotDerived { node: _ } => 1 + DerivedGrounding { grounding: _ } => 0 + } + } + } +} + +test fn product_introduction_leaves_partially_evidenced_conj_on_the_frontier_holds() -> Bool { + let frontier_child = Node { + kind: TypeNode { connective: Atom { identity: ^no_authority_declares_this_symbol } }, + children: [], + occurrence_id: OccurrenceSynthetic + } + let derived_child = dag_int_inhabitant_node() + let partial = Node { + kind: TypeNode { connective: Conj }, + children: [ + Edge { label: Named { name: ^derived_field }, target: derived_child }, + Edge { label: Named { name: ^frontier_field }, target: frontier_child } + ], + occurrence_id: OccurrenceSynthetic + } + (product_introduction_hand_tree_grounding(tree: partial, n: derived_child) == 0) + && (product_introduction_hand_tree_grounding(tree: partial, n: frontier_child) == 1) + && (product_introduction_hand_tree_grounding(tree: partial, n: partial) == 1) +} diff --git a/src/v2/test/claim/execution/long/emit_host_classical_not_ingested_equals_eval_test.dag b/src/v2/test/claim/execution/long/emit_host_classical_not_ingested_equals_eval_test.dag index 307acaade0b..807e34b4de0 100644 --- a/src/v2/test/claim/execution/long/emit_host_classical_not_ingested_equals_eval_test.dag +++ b/src/v2/test/claim/execution/long/emit_host_classical_not_ingested_equals_eval_test.dag @@ -155,11 +155,11 @@ fn ingested_arrow_from_source(source: String) -> Outcome { } ) } -fn ingested_canonical_inferred_tree_from_source(source: String) -> Outcome { +fn ingested_canonical_inferred_tree_from_arrow(arrow: Outcome) -> Outcome { bind_outcome( - o: ingested_arrow_from_source(source: source), - f: fn(arrow) { - infer(tree: arrow) + o: arrow, + f: fn(a) { + infer(tree: a) } ) } @@ -176,12 +176,12 @@ fn ingested_classical_not_staging_facts_lookup( } } -fn ingested_staging_inferred_tree_from_source(source: String) -> Outcome { +fn ingested_staging_inferred_tree_from_arrow(arrow: Outcome) -> Outcome { bind_outcome( - o: ingested_arrow_from_source(source: source), - f: fn(arrow) { + o: arrow, + f: fn(a) { bind_outcome( - o: infer(tree: arrow), + o: infer(tree: a), f: fn(inferred) { outcome_accepted( value: InferredTree { @@ -199,6 +199,10 @@ fn ingested_staging_inferred_tree_from_source(source: String) -> Outcome Outcome { + ingested_staging_inferred_tree_from_arrow(arrow: ingested_arrow_from_source(source: source)) +} + fn ingested_emit_shape_frontier_tree_from_source(source: String) -> Outcome { bind_outcome( o: ingested_arrow_from_source(source: source), @@ -240,6 +244,34 @@ fn ingested_wet_emit_eval_composed_tree_from_source(source: String) -> Outcome) is the deepest pipeline stage whose value stays closure-free and +// therefore portable: the InferredTree one stage up carries the facts PartialFunction and can +// never store (ServeCacheValueNotPortable). Same nullary-per-fixture shape as +// v2.compiler.ingested_fixture_arrows' arrow producers. +fn ingested_classical_not_arrow_with_body() -> Outcome { + ingested_arrow_from_source(source: ingested_classical_not_source) +} + +fn ingested_classical_not_swapped_arrow_with_body() -> Outcome { + ingested_arrow_from_source(source: ingested_classical_not_swapped_source) +} + fn ingested_inferred_tree_from_source(source: String) -> Outcome { ingested_staging_inferred_tree_from_source(source: source) } @@ -529,13 +561,16 @@ test fn ingested_classical_not_emit_source_arm_shape_holds() -> Bool { } } -test fn ingested_classical_not_canonical_emit_refuses_underived_holds() -> Bool { - match ingested_canonical_inferred_tree_from_source(source: ingested_classical_not_source) { +test fn ingested_classical_not_canonical_emit_accepts_holds() -> Bool { + match ingested_canonical_inferred_tree_from_arrow(arrow: ingested_classical_not_arrow_with_body()) { Accepted { value: tree, diagnostics: _ } => match emit(tree: tree, target: rust_classical_not_ingested_target_model_staging()) { - Rejected { diagnostics: d } => - d.head.reason == ^infer_grounding_not_derived - Accepted { value: _, diagnostics: _ } => false + Accepted { value: Medium { carried: source, fidelity: _ }, diagnostics: _ } => + string_contains(s: source, pattern: "fn classical_not(x") + && string_contains(s: source, pattern: "!= ") + && string_contains(s: source, pattern: "true => 0") + && string_contains(s: source, pattern: "false => 1") + Rejected { diagnostics: _ } => false } Rejected { diagnostics: _ } => false } @@ -549,24 +584,25 @@ test fn ingested_classical_not_arrow_has_match_holds() -> Bool { ingested_arrow_has_match_body(source: ingested_classical_not_source) } -fn ingested_classical_not_staging_emit_refuses_underived(source: String) -> Bool { - match ingested_staging_inferred_tree_from_source(source: source) { +fn ingested_classical_not_staging_emit_accepts_arrow(arrow: Outcome) -> Bool { + match ingested_staging_inferred_tree_from_arrow(arrow: arrow) { Accepted { value: tree, diagnostics: _ } => match emit(tree: tree, target: rust_classical_not_ingested_target_model_staging()) { - Rejected { diagnostics: d } => - d.head.reason == ^infer_grounding_not_derived - Accepted { value: _, diagnostics: _ } => false + Accepted { value: Medium { carried: emitted, fidelity: _ }, diagnostics: _ } => + string_contains(s: emitted, pattern: "fn classical_not(x") + && string_contains(s: emitted, pattern: "!= ") + Rejected { diagnostics: _ } => false } Rejected { diagnostics: _ } => false } } -test fn ingested_classical_not_staging_emit_refuses_underived_holds() -> Bool { - ingested_classical_not_staging_emit_refuses_underived(source: ingested_classical_not_source) +test fn ingested_classical_not_staging_emit_accepts_holds() -> Bool { + ingested_classical_not_staging_emit_accepts_arrow(arrow: ingested_classical_not_arrow_with_body()) } -test fn ingested_classical_not_staging_swapped_emit_refuses_underived_holds() -> Bool { - ingested_classical_not_staging_emit_refuses_underived(source: ingested_classical_not_swapped_source) +test fn ingested_classical_not_staging_swapped_emit_accepts_holds() -> Bool { + ingested_classical_not_staging_emit_accepts_arrow(arrow: ingested_classical_not_swapped_arrow_with_body()) } test fn emit_host_classical_not_ingested_equals_eval_holds() -> Bool { @@ -669,10 +705,15 @@ test fn ingested_classical_not_match_only_param_scrutinee_infer_refuses_holds() // KERNEL-D TESTING RE-STEER (operator direction, 2026-07-24): per-PR layer-boundary mock proof for // the classical_not cutover's emit-to-text half — ingested_emit_shape_frontier_tree_from_source // (orch-emit-compatible empty facts map) -> assert emitted Rust TEXT (string compare, no compile). -// ingested_canonical_inferred_tree_from_source is the pure infer carrier; -// ingested_classical_not_canonical_emit_refuses_underived_holds and -// ingested_classical_not_staging_emit_refuses_underived_holds witness translate refuses recorded -// GroundingNotDerived. Wet/nightly emit-vs-eval legs use +// ingested_canonical_inferred_tree_from_arrow is the pure infer carrier. +// FRONTIER CLOSED (2026-09-06, grounding-frontier branch): the specimen's real-infer tree fully +// derives — the grammar-production roster grounds the module identity atom, the binding-reference +// rule grounds the parameter atom from the arrow domain, and the product rules compose the rest — +// so ingested_classical_not_canonical_emit_accepts_holds and +// ingested_classical_not_staging_emit_accepts_holds now witness that emit ACCEPTS the canonical +// and staging trees and pins the emitted text's shape (the same shape the frontier-path witnesses +// pin). The translate-refuses-underived behavior stays enrolled on hand-staged frontier fixtures +// in translate_underived_refusal_test.dag. Wet/nightly emit-vs-eval legs use // ingested_wet_emit_eval_composed_tree_from_source (composed facts on every lookup — emit accepts, // eval admits); shape-frontier stays emit-only. Deliberately excludes wet legs from per-PR // CommitCheckEnrollment. diff --git a/src/v2/test/claim/execution/long/emit_host_native_only_verdict_test.dag b/src/v2/test/claim/execution/long/emit_host_native_only_verdict_test.dag index 6a7bb502238..1cda4151a60 100644 --- a/src/v2/test/claim/execution/long/emit_host_native_only_verdict_test.dag +++ b/src/v2/test/claim/execution/long/emit_host_native_only_verdict_test.dag @@ -1,19 +1,88 @@ module v2.test.long.emit_host_native_only_verdict import v2.compiler.emit { emit } -import v2.compiler.emit_host { emit_host_octets_byte_string, run_emit_host_cached } +import v2.compiler.emit_host { emit_host_octets_byte_string, run_emit_host_cached, run_emit_host_cached_with_run_args } +import v2.compiler.emit_module { emit_module } import v2.compiler.eval { inputs_root_only } -import v2.extdeps.languages.rust_test { rust_classical_not_ingested_target_model_staging } +import v2.compiler.infer { InferredTree } +import v2.extdeps.languages.rust { rust_target_model_staging } +import v2.extdeps.languages.rust_test { + rust_add_target_model_staging, + rust_call_target_model_staging, + rust_classical_not_ingested_target_model_staging, + rust_family_member_run_args, + rust_field_access_target_model_staging, + rust_logic_family_target_model_staging, + rust_match_loop_fold_family_target_model_staging, + rust_rec_nine_target_model_staging, + rust_variant_construct_target_model_staging, +} import extdeps.realization.emit_on_demand_host { native_cache_workspace_root } import v2.test.long.emit_host_classical_not_ingested_equals_eval { ingested_classical_not_source, ingested_classical_not_swapped_source, ingested_wet_emit_eval_composed_tree_from_source, } +import v2.test.execution.emit_host_add_equals_eval { + emit_eval_subject_tree, +} import v2.test.execution.emit_on_demand_classical_not_ingested_family_witness { classical_not_family_cache_root_for, classical_not_family_native_key_for, } +import v2.test.execution.emit_on_demand_kernel_witness { + kernel_add_fixture_inputs, + kernel_cache_root_for, + kernel_native_key_for, +} +import v2.test.execution.emit_on_demand_family_crate_witness { + family_crate_cache_root_for, + family_crate_fixture_inputs, + logic_family_primary_emitted, +} +import v2.test.execution.emit_host_field_access_equals_eval { + emit_field_eval_producer_tree, +} +import v2.test.execution.emit_on_demand_field_access_family_witness { + field_access_family_cache_root_for, + field_access_family_fixture_inputs, + field_access_family_native_key_for, +} +import v2.test.execution.emit_on_demand_match_loop_fold_family_witness { + family_cache_root_for, + fold_alt_expected_octets, + fold_expected_octets, + loop_alt_expected_octets, + loop_expected_octets, + match_alt_expected_octets, + match_expected_octets, + match_fixture_inputs, + mlf_family_primary_emitted, +} +import v2.test.execution.emit_host_variant_construct_equals_eval { + emit_variant_construct_eval_tree, +} +import v2.test.execution.emit_on_demand_variant_construct_family_witness { + variant_construct_family_cache_root_for, + variant_construct_family_fixture_inputs, + variant_construct_family_native_key_for, +} +import v2.test.execution.emit_host_filesystem_read_equals_eval { + filesystem_read_native_run, +} +import v2.test.execution.emit_host_shell_exec_run_equals_eval { + shell_exec_run_native_run, +} +import v2.test.long.emit_host_produced_module_equals_eval { + produced_add_module_source, +} +import v2.test.execution.rust_emit_host_call_equals_eval { + emit_call_eval_producer_tree, +} +import v2.test.execution.rust_record_construct_emit_host_equals_eval { + rust_rec_nine_eval_inferred_tree, +} +import v2.std.collection { List } import v2.std.diagnostic { Accepted, Rejected, Outcome, None } import v2.std.host_run { HostLogicalRun, HostRunStdout } import v2.std.logic { Bool } @@ -104,3 +173,516 @@ test fn emit_host_native_only_verdict_stdout_mismatch_mock_holds() -> Bool { ) == false } +// ADD-FAMILY ARM (2026-09-07): the same native-only verdict shape for the emit_host_add family — +// the second construct promoted off InterpreterRetained, after classical_not. Two differences from +// the classical_not arm are family facts, not new machinery: the add family is FIXTURE-TREE +// anchored (emit_eval_subject_tree is the family's shared tree constructor — no eval leg is +// reachable from this file despite the constructor's home module's name), so the helper takes the +// tree directly with no source-compose step; and the run shares the KERNEL family's one-build +// cache key (kernel_cache_root_for + kernel_native_key_for, the same key +// emit_on_demand_kernel_native_one_build_holds colds), exactly as the classical_not arm shares its +// family's key — an uncached leg here would duplicate a full cargo build the family witness +// already performs (DESIGN S2). The expected octet is 5, the byte the kernel witness's warm leg +// pins on this same build. Program-side discrimination stays where the family already carries it: +// the equals_eval primitive-five/primitive-six pair proves the native run's actual byte +// participates in a verdict (5 agrees, 6 diverges). A no-eval verdict has no oracle leg to break, +// so this arm's broken control breaks the EXPECTATION side: octet 6 can never match this run, and +// the comparator mocks above pin the comparator itself. +fn add_native_only_matches_expected(tree: InferredTree, expected_octet: Int) -> Bool { + match emit(tree: tree, target: rust_target_model_staging()) { + Accepted { value: emitted, diagnostics: _ } => + let workspace = native_cache_workspace_root( + cache_root: kernel_cache_root_for(test_id: "native_one_build"), + key: kernel_native_key_for(tree: tree) + ) + match run_emit_host_cached( + workspace_dir: workspace, + target: rust_target_model_staging(), + source: emitted, + fixture_inputs: kernel_add_fixture_inputs() + ) { + Accepted { value: receipt, diagnostics: _ } => + match receipt.logical_run { + Accepted { value: HostLogicalRun { stdout: stdout }, diagnostics: None } => + native_only_stdout_matches(stdout: stdout, expected_octet: expected_octet) + _ => false + } + Rejected { diagnostics: _ } => false + } + Rejected { diagnostics: _ } => false + } +} + +test fn emit_host_native_only_add_holds() -> Bool { + add_native_only_matches_expected(tree: emit_eval_subject_tree(), expected_octet: 5) +} + +test fn emit_host_native_only_add_wrong_octet_mismatch_detected_holds() -> Bool { + add_native_only_matches_expected(tree: emit_eval_subject_tree(), expected_octet: 6) == false +} + +// COMPLEMENT-FAMILY ARM (2026-09-07): the same native-only verdict shape for the emit_host_complement +// family — the third construct promoted off InterpreterRetained, after classical_not and add. The +// family difference is the build grain, not the verdict shape: complement's native execution is +// FAMILY-grain by the operator-directed witness_family_build_grain_ruling (per-witness crates +// measured ~17s each and lose to the interpreter; one family crate wins), so this arm runs the +// complement member through the family dispatcher of the logic family crate (meet + join + +// complement under the argv-dispatched family main). The crate's pure pipeline — member trees, +// emit_family, the native key — is NOT paid per claim: logic_family_primary_emitted is warm- +// enrolled in v2.workflow.floor_pure_producer_share, so the floor evaluates it once during strict +// preparation and each claim serves the landed EmittedFamilyCrate, paying only the cached native +// run (run 34315228217 measured all six logic/mlf arms at 458-504ms with the pipeline inline — +// one runner-swing from the 500ms ceiling — which is what moved the pipeline out of the fold). +// It shares the family witness's one-build cache +// key (family_crate_cache_root_for + the served native key over the same member list +// family_crate_one_build_members_warm_holds colds), exactly as the add and classical_not arms share +// their families' keys — an uncached leg here would duplicate the family cargo build (DESIGN S2). +// The expected octet is 0, the byte the family witness's warm complement leg pins on this same +// build (complement(True) = False). Program-side discrimination stays where the family already +// carries it: the equals_eval pair and the family witness's all-alt leg prove the native run's +// actual byte participates in a verdict (the all-alt family flips complement 0 -> 1). A no-eval +// verdict has no oracle leg to break, so this arm's broken control breaks the EXPECTATION side: +// octet 1 can never match this run, and the comparator mocks above pin the comparator itself. +fn logic_family_native_only_matches_expected(member_id: String, expected_octet: Int) -> Bool { + match logic_family_primary_emitted() { + Accepted { value: crate, diagnostics: _ } => + match run_emit_host_cached_with_run_args( + workspace_dir: native_cache_workspace_root( + cache_root: family_crate_cache_root_for(test_id: "one_build_members_warm"), + key: crate.native_key + ), + target: rust_logic_family_target_model_staging(), + source: crate.source, + fixture_inputs: family_crate_fixture_inputs(), + run_args: rust_family_member_run_args(member_id: member_id) + ) { + Accepted { value: receipt, diagnostics: _ } => + match receipt.logical_run { + Accepted { value: HostLogicalRun { stdout: stdout }, diagnostics: None } => + native_only_stdout_matches(stdout: stdout, expected_octet: expected_octet) + _ => false + } + Rejected { diagnostics: _ } => false + } + Rejected { diagnostics: _ } => false + } +} + +fn complement_native_only_matches_expected(expected_octet: Int) -> Bool { + logic_family_native_only_matches_expected(member_id: "complement", expected_octet: expected_octet) +} + +test fn emit_host_native_only_complement_holds() -> Bool { + complement_native_only_matches_expected(expected_octet: 0) +} + +test fn emit_host_native_only_complement_wrong_octet_mismatch_detected_holds() -> Bool { + complement_native_only_matches_expected(expected_octet: 1) == false +} + +// MEET-JOIN-FAMILY ARM: the same native-only verdict shape for the emit_host_meet_join family — +// the eighth construct promoted off InterpreterRetained. The family shares the logic family crate +// with complement (meet + join + complement under the argv-dispatched family main, per the +// witness_family_build_grain_ruling), so this arm reuses the complement arm's member- +// parameterized helper — same one-build cache key, same dispatcher, eval() never called. The +// expected octets are the bytes the family witness's warm legs pin on this same build (meet 1, +// join 1). Program-side discrimination stays where the family already carries it: the equals_eval +// pairs and the family witness's all-alt leg prove the native runs' actual bytes participate in +// verdicts (the alt trees flip meet 1 -> 0 and join 1 -> 0). A no-eval verdict has no oracle leg +// to break, so this arm's broken control breaks the EXPECTATION side: octet 0 is each member's +// alt byte and can never match the primary runs, and the comparator mocks above pin the +// comparator itself. +// +// ONE MEMBER PER CLAIM (2026-09-08): the two-member shape put TWO native-run verdicts inside one +// claim's 500ms CPU budget — emit of the family crate plus the cached-run receipt walk, twice over +// — and the required floor measured both arms over the line. The ceiling is the floor's own and +// does not move to admit a claim shape; the arm splits by member instead, the grain the family's +// equals_eval pair already claims at (emit_host_meet_equals_eval_holds / +// emit_host_join_equals_eval_holds). Each claim now pays one native run; the family crate build +// stays shared through the same one-build cache key either way, and the crate's pure pipeline is +// served from the warm share per the complement arm's note above. +test fn emit_host_native_only_meet_holds() -> Bool { + logic_family_native_only_matches_expected(member_id: "meet", expected_octet: 1) +} + +test fn emit_host_native_only_join_holds() -> Bool { + logic_family_native_only_matches_expected(member_id: "join", expected_octet: 1) +} + +test fn emit_host_native_only_meet_wrong_octet_mismatch_detected_holds() -> Bool { + logic_family_native_only_matches_expected(member_id: "meet", expected_octet: 0) == false +} + +test fn emit_host_native_only_join_wrong_octet_mismatch_detected_holds() -> Bool { + logic_family_native_only_matches_expected(member_id: "join", expected_octet: 0) == false +} + +// FIELD-ACCESS-FAMILY ARM: the same native-only verdict shape for the emit_host_field_access +// family — the fourth construct promoted off InterpreterRetained, after classical_not, add, and +// complement. The family differences are facts of that family, not new machinery: the tree is +// FIXTURE-PRODUCED (emit_field_eval_producer_tree, the family's equals_eval tree constructor — no +// eval leg is reachable from this file despite the constructor's home module's name), and the run +// shares the field_access family's one-build cache key (field_access_family_cache_root_for + +// field_access_family_native_key_for, the same key emit_on_demand_field_access_native_one_build_holds +// colds) — an uncached leg here would duplicate a full cargo build the family witness already +// performs (DESIGN S2). The expected octet is 9, the byte the family witness's warm leg pins on +// this same build. Program-side discrimination stays where the family already carries it: the +// equals_eval pair and the family witness's wrong-fixture mismatch leg prove the native run's +// actual byte participates in a verdict (the wrong-field alt tree flips the octet 9 -> 1). A +// no-eval verdict has no oracle leg to break, so this arm's broken control breaks the EXPECTATION +// side: octet 1 is the alt tree's byte and can never match the primary run, and the comparator +// mocks above pin the comparator itself. +fn field_access_native_only_matches_expected(expected_octet: Int) -> Bool { + match emit_field_eval_producer_tree() { + Accepted { value: tree, diagnostics: _ } => + match emit(tree: tree, target: rust_field_access_target_model_staging()) { + Accepted { value: emitted, diagnostics: _ } => + match run_emit_host_cached( + workspace_dir: native_cache_workspace_root( + cache_root: field_access_family_cache_root_for(test_id: "native_one_build"), + key: field_access_family_native_key_for(tree: tree) + ), + target: rust_field_access_target_model_staging(), + source: emitted, + fixture_inputs: field_access_family_fixture_inputs() + ) { + Accepted { value: receipt, diagnostics: _ } => + match receipt.logical_run { + Accepted { value: HostLogicalRun { stdout: stdout }, diagnostics: None } => + native_only_stdout_matches(stdout: stdout, expected_octet: expected_octet) + _ => false + } + Rejected { diagnostics: _ } => false + } + Rejected { diagnostics: _ } => false + } + Rejected { diagnostics: _ } => false + } +} + +test fn emit_host_native_only_field_access_holds() -> Bool { + field_access_native_only_matches_expected(expected_octet: 9) +} + +test fn emit_host_native_only_field_access_wrong_octet_mismatch_detected_holds() -> Bool { + field_access_native_only_matches_expected(expected_octet: 1) == false +} + +// MATCH/LOOP/FOLD-FAMILY ARMS: the same native-only verdict shape for the three match_loop_fold +// family roster rows — the fifth, sixth, and seventh constructs promoted off InterpreterRetained. +// The family difference is the build grain, not the verdict shape, exactly as complement: the +// family's native execution is FAMILY-grain by the operator-directed witness_family_build_grain_ruling, +// so each arm runs its own member through the family dispatcher of the three-member family crate +// (match + loop + fold under the argv-dispatched family main), served from the same warm share as +// the logic arms — mlf_family_primary_emitted is warm-enrolled in +// v2.workflow.floor_pure_producer_share, so the emit is paid once at preparation, not per claim — +// sharing the family witness's +// one-build cache key (family_cache_root_for + the served native key over the same primary member +// list emit_on_demand_match_loop_fold_family_one_build_holds colds) — an uncached leg here would +// duplicate the family cargo build (DESIGN S2). The expected octet lists are the bytes the family +// witness's warm legs pin on this same build (match [0,1,0,0,0], loop [0,1,0,0,0], fold +// [0,7,0,0,0]). Program-side discrimination stays where the family already carries it: each +// member's equals_eval pair and the family witness's alt legs prove the native run's actual bytes +// participate in verdicts (the alt trees flip match 1 -> 2, loop 1 -> 2, fold 7 -> 255...). A +// no-eval verdict has no oracle leg to break, so each arm's broken control breaks the EXPECTATION +// side with the member's own alt octets, and the comparator mocks above pin the comparator itself. +fn mlf_native_only_member_matches_expected(member_id: String, expected_octets: List) -> Bool { + match mlf_family_primary_emitted() { + Accepted { value: crate, diagnostics: _ } => + match run_emit_host_cached_with_run_args( + workspace_dir: native_cache_workspace_root( + cache_root: family_cache_root_for(test_id: "family_one_build"), + key: crate.native_key + ), + target: rust_match_loop_fold_family_target_model_staging(), + source: crate.source, + fixture_inputs: match_fixture_inputs(), + run_args: rust_family_member_run_args(member_id: member_id) + ) { + Accepted { value: receipt, diagnostics: _ } => + match receipt.logical_run { + Accepted { value: HostLogicalRun { stdout: stdout }, diagnostics: None } => + stdout.bytes == emit_host_octets_byte_string(octets: expected_octets) + _ => false + } + Rejected { diagnostics: _ } => false + } + Rejected { diagnostics: _ } => false + } +} + +test fn emit_host_native_only_match_holds() -> Bool { + mlf_native_only_member_matches_expected(member_id: "match", expected_octets: match_expected_octets) +} + +test fn emit_host_native_only_match_wrong_octet_mismatch_detected_holds() -> Bool { + mlf_native_only_member_matches_expected(member_id: "match", expected_octets: match_alt_expected_octets) == false +} + +test fn emit_host_native_only_loop_holds() -> Bool { + mlf_native_only_member_matches_expected(member_id: "loop", expected_octets: loop_expected_octets) +} + +test fn emit_host_native_only_loop_wrong_octet_mismatch_detected_holds() -> Bool { + mlf_native_only_member_matches_expected(member_id: "loop", expected_octets: loop_alt_expected_octets) == false +} + +test fn emit_host_native_only_fold_closure_holds() -> Bool { + mlf_native_only_member_matches_expected(member_id: "fold", expected_octets: fold_expected_octets) +} + +test fn emit_host_native_only_fold_closure_wrong_octet_mismatch_detected_holds() -> Bool { + mlf_native_only_member_matches_expected(member_id: "fold", expected_octets: fold_alt_expected_octets) == false +} + +// VARIANT-CONSTRUCT-FAMILY ARM: the same native-only verdict shape for the +// emit_host_variant_construct family — the ninth construct promoted off InterpreterRetained. The +// family differences are facts of that family, not new machinery: the tree is the family's own +// equals_eval tree VALUE (emit_variant_construct_eval_tree — no eval leg is reachable from this +// file despite the home module's equals_eval name), and the run shares the variant_construct +// family's one-build cache key (variant_construct_family_cache_root_for + +// variant_construct_family_native_key_for, the same key +// emit_on_demand_variant_construct_native_one_build_holds colds) — an uncached leg here would +// duplicate a full cargo build the family witness already performs (DESIGN S2). The expected +// octet is 9, the byte the family witness's warm leg pins on this same build. Program-side +// discrimination stays where the family already carries it: the equals_eval pair and the family +// witness's wrong-value alt leg prove the native run's actual byte participates in a verdict (the +// wrong-value alt tree flips the octet 9 -> 1). A no-eval verdict has no oracle leg to break, so +// this arm's broken control breaks the EXPECTATION side: octet 1 is the alt tree's byte and can +// never match the primary run, and the comparator mocks above pin the comparator itself. +fn variant_construct_native_only_matches_expected(expected_octet: Int) -> Bool { + let tree = emit_variant_construct_eval_tree + match emit(tree: tree, target: rust_variant_construct_target_model_staging()) { + Accepted { value: emitted, diagnostics: _ } => + match run_emit_host_cached( + workspace_dir: native_cache_workspace_root( + cache_root: variant_construct_family_cache_root_for(test_id: "native_one_build"), + key: variant_construct_family_native_key_for(tree: tree) + ), + target: rust_variant_construct_target_model_staging(), + source: emitted, + fixture_inputs: variant_construct_family_fixture_inputs() + ) { + Accepted { value: receipt, diagnostics: _ } => + match receipt.logical_run { + Accepted { value: HostLogicalRun { stdout: stdout }, diagnostics: None } => + native_only_stdout_matches(stdout: stdout, expected_octet: expected_octet) + _ => false + } + Rejected { diagnostics: _ } => false + } + Rejected { diagnostics: _ } => false + } +} + +test fn emit_host_native_only_variant_construct_holds() -> Bool { + variant_construct_native_only_matches_expected(expected_octet: 9) +} + +test fn emit_host_native_only_variant_construct_wrong_octet_mismatch_detected_holds() -> Bool { + variant_construct_native_only_matches_expected(expected_octet: 1) == false +} + +// FILESYSTEM-READ-FAMILY ARM: the same native-only verdict shape for the emit_host_filesystem_read +// family — the tenth construct promoted off InterpreterRetained. This family has no translated +// arrow body at all (the construct under test is the host-effect primitive itself), so the arm +// reuses the family's own native leg (filesystem_read_native_run) with the expectation pinned as a +// LITERAL: octet 1 is grounded by the family's own enrolled pin +// filesystem_read_eval_reads_real_bash_argv, which asserts the controlled fixture +// dag/extdeps/shell/exec.dag contains "bash" — a versioned repo file, not a measurement copied +// from the run (DESIGN S5's oracle rule). The fixture content load inside the family's target +// staging runs the interpreted filesystem_read builtin — harness plumbing of the same standing as +// every arm's interpreted fixture construction; the VERDICT path is native stdout vs the literal, +// and no InterpretationAlgebra oracle leg exists for this family to call. The run shares the +// family's fixed witness workspace (content-safety from the realization-digest nesting in +// run_host_process_admitted: changed content colds, never serves stale). The broken control breaks +// the EXPECTATION side: octet 0 is the byte the family's wrong-oracle control already proves the +// native run never produces, and the comparator mocks above pin the comparator itself. +test fn emit_host_native_only_filesystem_read_holds() -> Bool { + filesystem_read_native_run(expected_octet: 1) +} + +test fn emit_host_native_only_filesystem_read_wrong_octet_mismatch_detected_holds() -> Bool { + filesystem_read_native_run(expected_octet: 0) == false +} + +// MODULE-FAMILY ARM: the same native-only verdict shape for the emit_host_module family — the +// eleventh construct promoted off InterpreterRetained. The family difference is the emit entry: +// the source comes from emit_module (the module-concatenation path) over the add family's shared +// fixture tree, not from emit — exactly the source the family's equals_eval pair executes. The +// expected octet is 5, the byte the add family's primitive-five/primitive-six pair pins on this +// same subject (5 agrees, 6 diverges). This family has no one-build cache witness, so the arm runs +// under its own fixed workspace root; content-safety comes from the realization-digest nesting in +// run_host_process_admitted (changed source colds, never serves stale), the same mechanism the +// filesystem_read family's fixed workspace relies on. The broken control breaks the EXPECTATION +// side: octet 6 is the byte the equals_eval pair's wrong-oracle leg proves this run never +// produces, and the comparator mocks above pin the comparator itself. +fn module_native_only_matches_expected(expected_octet: Int) -> Bool { + match emit_module(decls: [emit_eval_subject_tree()], target: rust_add_target_model_staging()) { + Accepted { value: source, diagnostics: _ } => + match run_emit_host_cached( + workspace_dir: "/tmp/gunbc_emit_on_demand_module/native_only", + target: rust_add_target_model_staging(), + source: source, + fixture_inputs: kernel_add_fixture_inputs() + ) { + Accepted { value: receipt, diagnostics: _ } => + match receipt.logical_run { + Accepted { value: HostLogicalRun { stdout: stdout }, diagnostics: None } => + native_only_stdout_matches(stdout: stdout, expected_octet: expected_octet) + _ => false + } + Rejected { diagnostics: _ } => false + } + Rejected { diagnostics: _ } => false + } +} + +test fn emit_host_native_only_module_holds() -> Bool { + module_native_only_matches_expected(expected_octet: 5) +} + +test fn emit_host_native_only_module_wrong_octet_mismatch_detected_holds() -> Bool { + module_native_only_matches_expected(expected_octet: 6) == false +} + +// PRODUCED-MODULE-FAMILY ARM: the same native-only verdict shape for the +// emit_host_produced_module family — the twelfth construct promoted off InterpreterRetained. The +// family difference is the source's provenance: produced_add_module_source() assembles a two-fn +// module (add + add2) from INGESTED decl nodes via emit_produced_module — exactly the source the +// family's equals_eval pair executes — and the family's own golden test +// (produced_module_two_distinct_fns_assemble) pins those bytes against a literal golden. The +// expected octet is 5, the byte the add family's primitive-five/primitive-six pair pins on this +// same subject. Like the module family there is no one-build cache witness, so the arm runs under +// its own fixed workspace root with the same realization-digest content-safety. The broken +// control breaks the EXPECTATION side: octet 6 can never match this run, and the comparator mocks +// above pin the comparator itself. +fn produced_module_native_only_matches_expected(expected_octet: Int) -> Bool { + match produced_add_module_source() { + Accepted { value: source, diagnostics: _ } => + match run_emit_host_cached( + workspace_dir: "/tmp/gunbc_emit_on_demand_produced_module/native_only", + target: rust_target_model_staging(), + source: source, + fixture_inputs: kernel_add_fixture_inputs() + ) { + Accepted { value: receipt, diagnostics: _ } => + match receipt.logical_run { + Accepted { value: HostLogicalRun { stdout: stdout }, diagnostics: None } => + native_only_stdout_matches(stdout: stdout, expected_octet: expected_octet) + _ => false + } + Rejected { diagnostics: _ } => false + } + Rejected { diagnostics: _ } => false + } +} + +test fn emit_host_native_only_produced_module_holds() -> Bool { + produced_module_native_only_matches_expected(expected_octet: 5) +} + +test fn emit_host_native_only_produced_module_wrong_octet_mismatch_detected_holds() -> Bool { + produced_module_native_only_matches_expected(expected_octet: 6) == false +} + +// SHELL-EXEC-RUN-FAMILY ARM: the same native-only verdict shape for the emit_host_shell_exec_run +// family — the thirteenth construct promoted off InterpreterRetained. Like filesystem_read this +// family has no translated arrow body (the construct under test is the host-effect primitive), so +// the arm reuses the family's own native leg (shell_exec_run_native_run) with the expectation +// pinned as a LITERAL: octet 1 is grounded by the family's own enrolled pin +// shell_exec_run_eval_argv_is_bash_dash_s, which asserts the argv materialized from the controlled +// fixture dag/extdeps/shell/exec.dag's shell.Exec.Run operation is exactly ["bash", "-s"]. The +// native leg itself carries no interpreted oracle at all — the argv literals are baked into the +// target model at .dag-construction time from that same materialization, so the two legs cannot +// silently drift (rust_shell_exec_run_family_note). The run shares the family's fixed witness +// workspace with realization-digest content-safety. The broken control breaks the EXPECTATION +// side: octet 0 is the byte the family's wrong-oracle control already proves the native run never +// produces, and the comparator mocks above pin the comparator itself. +test fn emit_host_native_only_shell_exec_run_holds() -> Bool { + shell_exec_run_native_run(expected_octet: 1) +} + +test fn emit_host_native_only_shell_exec_run_wrong_octet_mismatch_detected_holds() -> Bool { + shell_exec_run_native_run(expected_octet: 0) == false +} + +// CALL-FAMILY ARM: the same native-only verdict shape for the rust_emit_host_call family — the +// fourteenth construct promoted off InterpreterRetained. The tree is the family's own producer +// (emit_call_eval_producer_tree over the rust_call_helper arrow — no eval leg is reachable from +// this file despite the constructor module's equals_eval name), emitted against the family's call +// target model. The expected octet is 7, the byte the family's primitive-seven/primitive-eight +// equals_eval pair pins on this same build (7 agrees, 8 diverges). This family has no one-build +// cache witness, so the arm runs under its own fixed workspace root with realization-digest +// content-safety, as the module families above. The broken control breaks the EXPECTATION side: +// octet 8 is the byte the equals_eval pair's wrong-oracle leg proves this run never produces, and +// the comparator mocks above pin the comparator itself. +fn call_native_only_matches_expected(expected_octet: Int) -> Bool { + match emit(tree: emit_call_eval_producer_tree(), target: rust_call_target_model_staging()) { + Accepted { value: emitted, diagnostics: _ } => + match run_emit_host_cached( + workspace_dir: "/tmp/gunbc_emit_on_demand_call/native_only", + target: rust_call_target_model_staging(), + source: emitted, + fixture_inputs: inputs_root_only(root: emit_call_eval_producer_tree().root) + ) { + Accepted { value: receipt, diagnostics: _ } => + match receipt.logical_run { + Accepted { value: HostLogicalRun { stdout: stdout }, diagnostics: None } => + native_only_stdout_matches(stdout: stdout, expected_octet: expected_octet) + _ => false + } + Rejected { diagnostics: _ } => false + } + Rejected { diagnostics: _ } => false + } +} + +test fn emit_host_native_only_call_holds() -> Bool { + call_native_only_matches_expected(expected_octet: 7) +} + +test fn emit_host_native_only_call_wrong_octet_mismatch_detected_holds() -> Bool { + call_native_only_matches_expected(expected_octet: 8) == false +} + +// RECORD-CONSTRUCT-FAMILY ARM: the same native-only verdict shape for the +// rust_record_construct_emit_host family — the fifteenth construct promoted off +// InterpreterRetained. The tree is the family's own constructor (rust_rec_nine_eval_inferred_tree +// over the rec-nine arrow — no eval leg is reachable from this file despite the constructor +// module's equals_eval name), emitted against the family's rec-nine target model. The expected +// octet is 9, the byte the family's equals_eval pair pins on this same build; the family's +// wrong-field target model leg proves the native run's actual byte participates in a verdict +// (construct-then-field-access on the wrong field diverges). This family has no one-build cache +// witness, so the arm runs under its own fixed workspace root with realization-digest +// content-safety, as the module families above. The broken control breaks the EXPECTATION side: +// octet 0 can never match this run, and the comparator mocks above pin the comparator itself. +fn record_construct_native_only_matches_expected(expected_octet: Int) -> Bool { + match emit(tree: rust_rec_nine_eval_inferred_tree(), target: rust_rec_nine_target_model_staging()) { + Accepted { value: emitted, diagnostics: _ } => + match run_emit_host_cached( + workspace_dir: "/tmp/gunbc_emit_on_demand_record_construct/native_only", + target: rust_rec_nine_target_model_staging(), + source: emitted, + fixture_inputs: inputs_root_only(root: rust_rec_nine_eval_inferred_tree().root) + ) { + Accepted { value: receipt, diagnostics: _ } => + match receipt.logical_run { + Accepted { value: HostLogicalRun { stdout: stdout }, diagnostics: None } => + native_only_stdout_matches(stdout: stdout, expected_octet: expected_octet) + _ => false + } + Rejected { diagnostics: _ } => false + } + Rejected { diagnostics: _ } => false + } +} + +test fn emit_host_native_only_record_construct_holds() -> Bool { + record_construct_native_only_matches_expected(expected_octet: 9) +} + +test fn emit_host_native_only_record_construct_wrong_octet_mismatch_detected_holds() -> Bool { + record_construct_native_only_matches_expected(expected_octet: 0) == false +} + diff --git a/src/v2/test/claim/execution/self_host_candidate_generation_stage_verdicts_test.dag b/src/v2/test/claim/execution/self_host_candidate_generation_stage_verdicts_test.dag index ba33e9656d8..1aeb047982e 100644 --- a/src/v2/test/claim/execution/self_host_candidate_generation_stage_verdicts_test.dag +++ b/src/v2/test/claim/execution/self_host_candidate_generation_stage_verdicts_test.dag @@ -20,24 +20,28 @@ import v2.std.text { String, string_join } data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly // THE ADD-SLICE BINDING OF `v2.compiler.self_host.candidate_generation_stage_verdicts` -- the -// instrument the `v2.workflow.floor_expected_red` add-slice roster note names as the producer of -// its per-stage receipt. `add_slice_stage_verdicts` is the pure entry over the slice's own +// instrument the `v2.workflow.floor_expected_red` add-slice roster note named as the producer of +// its per-stage receipt, before that row greened and the note deleted itself per its own +// dissolution statement. `add_slice_stage_verdicts` is the pure entry over the slice's own // fixture (`dag_add_emitted_root`, `dag_add_target_model`); `add_slice_stage_verdicts_entry` is // the runnable `gunbc run --function` form, exiting nonzero with the verdicts rendered while the // composition refuses, and ExitSuccess only when infer accepts clean and the composition accepts // clean -- the enrolled witness's green state. // -// THE TWO WITNESSES ARE DELIBERATELY ASYMMETRIC. `add_slice_infer_accepts_holds` is a PERMANENT -// positive control: infer accepting this root is the fact the roster note's structural account -// could not establish, and it stays true after the repair lands. -// `add_slice_composition_refuses_at_the_grounding_frontier_holds` pins TODAY's frontier state -- +// THE TWO WITNESSES ARE NOW BOTH PERMANENT REGRESSION CONTROLS. `add_slice_infer_accepts_holds` +// was authored as the permanent positive control: infer accepting this root is the fact the +// roster note's structural account could not establish, and it stays true after the repair. +// `add_slice_composition_accepts_holds` was authored as +// `add_slice_composition_refuses_at_the_grounding_frontier_holds`, pinning the frontier state -- // composition refusal headed by `infer_grounding_not_derived`, with infer carrying that same -// reason and nothing else -- so the instrument's composition arm has a discriminating consumer -// (DESIGN section 5). It is expected to RED on the day -// `gunbc.guarantee_stall.self_host_candidate_generation_add_slice_stall`'s trigger lands; the -// repair rewrites it to assert `candidate_accepted` with empty carried lists -- the DESIGN 4b(4) -// flip from frontier guard to permanent regression control -- in the same change that removes the -// roster row. +// reason and nothing else -- and flipped to assert the green state in the change that landed the +// declared-inhabitant membership derivation (`v2.compiler.infer` +// `infer_node_declared_in_dag_inhabitants`): the add slice's ten type-spine nodes are all declared +// in the dag language authority's declared-inhabitants roster, so they derive by lookup. The flip +// is the DESIGN 4b(4) move from frontier guard to permanent regression control, landed in the +// same change that removed the roster row. The stall narrowed to its four python/typescript +// round-trip members rather than dissolving: the membership derivation does not reach grammar +// parse-product nodes, which is the remaining population's frontier. fn add_slice_stage_verdicts() -> CandidateGenerationStageVerdicts { candidate_generation_stage_verdicts( @@ -98,13 +102,10 @@ test fn add_slice_infer_accepts_holds() -> Bool { add_slice_stage_verdicts().infer_verdict == ^infer_accepted } -test fn add_slice_composition_refuses_at_the_grounding_frontier_holds() -> Bool { +test fn add_slice_composition_accepts_holds() -> Bool { let verdicts = add_slice_stage_verdicts() - (verdicts.composition_verdict == ^infer_grounding_not_derived) - && !is_empty(xs: verdicts.infer_carried_reasons) - && fold_list( - xs: verdicts.infer_carried_reasons, - empty: true, - cons: fn(acc, reason) { acc && (reason == ^infer_grounding_not_derived) } - ) + (verdicts.infer_verdict == ^infer_accepted) + && is_empty(xs: verdicts.infer_carried_reasons) + && (verdicts.composition_verdict == ^candidate_accepted) + && is_empty(xs: verdicts.composition_carried_reasons) } diff --git a/src/v2/test/claim/long/inhabitant_neutralization_e2e_witness_test.dag b/src/v2/test/claim/long/inhabitant_neutralization_e2e_witness_test.dag index 1fe02ba7dc5..8323a288e24 100644 --- a/src/v2/test/claim/long/inhabitant_neutralization_e2e_witness_test.dag +++ b/src/v2/test/claim/long/inhabitant_neutralization_e2e_witness_test.dag @@ -25,7 +25,16 @@ data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly // Dissolve-on: re-enroll per-PR when measured under gunbc_ci_fast_lane_witness_eval_budget, or // delete as duplicate of a faster mechanism receipt. -test fn inhabitant_neutralization_python_to_ts_cross_language_compile_refuses_underived_holds() -> Bool { +// FLIPPED 2026-09-07 per DESIGN 4b(4): this was the e2e frontier guard +// `inhabitant_neutralization_python_to_ts_cross_language_compile_refuses_underived_holds`, asserting the +// python→typescript compile refused headed by infer_grounding_not_derived while the python inhabitants +// were on the grounding frontier. The declared-inhabitant membership derivation widening to the closed +// ingest set (v2.compiler.infer infer_node_declared_in_language_inhabitants) grounded the python and +// typescript inhabitant fact atoms, so the compile now ACCEPTS and emits the canonical typescript add +// fn byte-identical to ts_source_text. It stands as the permanent regression control for that +// acceptance; the python→go member below stays a refusal guard because go is outside the closed ingest +// set and its inhabitants remain frontier. +test fn inhabitant_neutralization_python_to_ts_cross_language_compile_round_trip_holds() -> Bool { match python_grammar_parse_fixture() { Accepted { value: python_parse_tree, diagnostics: _ } => match cross_language_compile( @@ -33,9 +42,9 @@ test fn inhabitant_neutralization_python_to_ts_cross_language_compile_refuses_un source_model: python_target_model(), target_model: ts_target_model() ) { - Rejected { diagnostics: d } => - d.head.reason == ^infer_grounding_not_derived - Accepted { value: _, diagnostics: _ } => false + Accepted { value: source, diagnostics: _ } => + source.carried == ts_source_text + Rejected { diagnostics: _ } => false } Rejected { diagnostics: _ } => false } diff --git a/src/v2/test/claim/manual/cross_language_add_python_to_typescript_test.dag b/src/v2/test/claim/manual/cross_language_add_python_to_typescript_test.dag index 93c02235156..c6d4cdbaa2e 100644 --- a/src/v2/test/claim/manual/cross_language_add_python_to_typescript_test.dag +++ b/src/v2/test/claim/manual/cross_language_add_python_to_typescript_test.dag @@ -25,7 +25,7 @@ import v2.extdeps.languages.python { python_emitted_add_fn_node, python_target_model } -import v2.extdeps.languages.typescript { ts_target_model } +import v2.extdeps.languages.typescript { ts_source_text, ts_target_model } import v2.std.grammar { ParseTree } import v2.std.logic { Bool } @@ -103,7 +103,16 @@ test fn cross_language_emit_inhabitant_neutralization_fails_closed() -> Bool { } } -test fn cross_language_emit_inhabitant_neutralization_refuses_underived_holds() -> Bool { +// FLIPPED 2026-09-07 per DESIGN 4b(4): this was +// `cross_language_emit_inhabitant_neutralization_refuses_underived_holds`, asserting emit after +// python→typescript neutralization refused headed by infer_grounding_not_derived while the typescript +// inhabitants were on the grounding frontier. The declared-inhabitant membership derivation widening +// to the closed ingest set (v2.compiler.infer infer_node_declared_in_language_inhabitants) grounded +// them, so the chain now ACCEPTS and emits the canonical typescript add fn byte-identical to +// ts_source_text. It stands as the permanent regression control for that acceptance; the raw +// (un-neutralized) emit refusal stays covered by +// cross_language_emit_inhabitant_neutralization_fails_closed above. +test fn cross_language_emit_inhabitant_neutralization_round_trip_holds() -> Bool { match neutralize_core_for_target( core: python_emitted_add_fn_node(), target: ts_target_model() @@ -112,9 +121,9 @@ test fn cross_language_emit_inhabitant_neutralization_refuses_underived_holds() match infer(tree: neutralized) { Accepted { value: inferred, diagnostics: _ } => match emit(tree: inferred, target: ts_target_model()) { - Rejected { diagnostics: d } => - d.head.reason == ^infer_grounding_not_derived - Accepted { value: _, diagnostics: _ } => false + Accepted { value: source, diagnostics: _ } => + source.carried == ts_source_text + Rejected { diagnostics: _ } => false } Rejected { diagnostics: _ } => false } @@ -134,7 +143,7 @@ test fn cross_language_add_python_to_typescript_chain_status_holds() -> Bool { Accepted { value: python_core, diagnostics: _ } => python_core == python_emitted_add_fn_node() - && cross_language_compile_on_real_tree_refuses_underived_holds(parse_tree: python_parse_tree) + && cross_language_compile_on_real_tree_round_trip_holds(parse_tree: python_parse_tree) && ts_effect_io_emit_holds() Rejected { diagnostics: _ } => false } @@ -142,15 +151,19 @@ test fn cross_language_add_python_to_typescript_chain_status_holds() -> Bool { } } -fn cross_language_compile_on_real_tree_refuses_underived_holds(parse_tree: ParseTree) -> Bool { +// FLIPPED 2026-09-07 per DESIGN 4b(4): the real-tree python→typescript compile refused headed by +// infer_grounding_not_derived until the declared-inhabitant membership derivation widened to the +// closed ingest set; it now accepts byte-identical to ts_source_text, and this helper is the +// regression control for that acceptance. +fn cross_language_compile_on_real_tree_round_trip_holds(parse_tree: ParseTree) -> Bool { match cross_language_compile( parse_tree: parse_tree, source_model: python_target_model(), target_model: ts_target_model() ) { - Rejected { diagnostics: d } => - d.head.reason == ^infer_grounding_not_derived - Accepted { value: _, diagnostics: _ } => false + Accepted { value: source, diagnostics: _ } => + source.carried == ts_source_text + Rejected { diagnostics: _ } => false } } @@ -165,6 +178,10 @@ type CrossLanguageEmitMatrixRow { status: CrossLanguageEmitCellStatus } +// The python→typescript row of this matrix moved ChainFailsClosedAtCheck { check: +// ^infer_grounding_not_derived } → ChainProven on 2026-09-07 when the declared-inhabitant +// membership derivation widened to the closed ingest set; the round-trip witnesses above are the +// executing evidence. data cross_language_emit_matrix: List = [ CrossLanguageEmitMatrixRow { @@ -176,7 +193,7 @@ data cross_language_emit_matrix: List = [ CrossLanguageEmitMatrixRow { source_language: ^python, target_language: ^typescript, - status: ChainFailsClosedAtCheck { check: ^infer_grounding_not_derived } + status: ChainProven {} }, CrossLanguageEmitMatrixRow { diff --git a/src/v2/test/claim/manual/ingest_bridge_test.dag b/src/v2/test/claim/manual/ingest_bridge_test.dag index c1983f54335..975af24c368 100644 --- a/src/v2/test/claim/manual/ingest_bridge_test.dag +++ b/src/v2/test/claim/manual/ingest_bridge_test.dag @@ -10,7 +10,7 @@ import v2.compiler.ingest { parse_tree_to_target_model_bridge } import v2.compiler.infer { canonical_grounding_for_node, infer } -import v2.compiler.translate { coerce_grounded_node } +import v2.compiler.translate { coerce_grounded_node, target_serialize_source_from_model } import v2.compiler.parse { grammar_validate_for_parse, parse_production, @@ -207,23 +207,33 @@ test fn ingest_bridge_to_canonical_holds() -> Bool { } } -test fn ingest_identity_coercion_needs_roster_membership_not_derived_grounding() -> Bool { +// FLIPPED 2026-09-06, in the change that landed the declared-inhabitant membership derivation +// (v2.compiler.infer infer_node_declared_in_dag_inhabitants). Authored as +// ingest_identity_coercion_needs_roster_membership_not_derived_grounding, it pinned that identity +// coercion succeeds via the authored roster while canonical_grounding_for_node REJECTED the +// roster member with infer_grounding_not_derived -- the isolation proving coercion does not read +// derived grounding. That premise is gone: dag_fixture_emitted_add_fn is declared in the dag +// authority's inhabitants roster, so its grounding now derives and canonical_grounding_for_node +// ACCEPTS it. The coercion half is unchanged and remains this witness's claim; the +// grounding-independence isolation now lives in the companion refusal witness below (a +// roster-ABSENT source still refuses), not here. + +test fn ingest_identity_coercion_accepts_source_present_in_authored_roster() -> Bool { let source = dag_fixture_emitted_add_fn match infer(tree: source) { Rejected { diagnostics: _ } => false Accepted { value: inferred, diagnostics: _ } => match canonical_grounding_for_node(tree: inferred, node: source) { - Accepted { value: _, diagnostics: _ } => false - Rejected { diagnostics: grounding_r } => - (grounding_r.head.reason == ^infer_grounding_not_derived) - && match coerce_grounded_node( - source: source, - tree: inferred, - target: ingest_fixture_model( - emitted: source, - spine_class: ^ingest_fixture_tok - ) - ) { + Rejected { diagnostics: _ } => false + Accepted { value: _, diagnostics: _ } => + match coerce_grounded_node( + source: source, + tree: inferred, + target: ingest_fixture_model( + emitted: source, + spine_class: ^ingest_fixture_tok + ) + ) { Rejected { diagnostics: _ } => false Accepted { value: coerced, diagnostics: _ } => (coerced.quality == Identity) && (coerced.target == source) @@ -297,7 +307,17 @@ test fn ingest_fails_closed_on_unstamped_holds() -> Bool { } } -test fn ingest_cross_language_compile_refuses_underived_holds() -> Bool { +// FLIPPED 2026-09-06, in the change that landed the declared-inhabitant membership derivation +// (v2.compiler.infer infer_node_declared_in_dag_inhabitants). Authored as +// ingest_cross_language_compile_refuses_underived_holds, it pinned the frontier refusal +// (infer_grounding_not_derived) on the dag same-language ingest path. The bridge recovers +// dag_fixture_emitted_add_fn -- a declared inhabitant -- so infer now grounds the whole tree by +// roster lookup and the compile ACCEPTS with no carried diagnostics. The oracle is the target +// model's own serializer: the compiled source is byte-equal to the authority's serialization of +// the same node, so the round trip is faithful by the authority's own measure, not by a +// transcribed literal. + +test fn ingest_cross_language_compile_accepts_holds() -> Bool { match ingest_parse() { Accepted { value: tree, diagnostics: _ } => match cross_language_compile( @@ -305,9 +325,16 @@ test fn ingest_cross_language_compile_refuses_underived_holds() -> Bool { source_model: ingest_fixture_model(emitted: dag_fixture_emitted_add_fn, spine_class: ^ingest_fixture_tok), target_model: dag_target_model() ) { - Rejected { diagnostics: d } => - d.head.reason == ^infer_grounding_not_derived - Accepted { value: _, diagnostics: _ } => false + Rejected { diagnostics: _ } => false + Accepted { value: source, diagnostics: d } => + match target_serialize_source_from_model( + target: dag_target_model(), + emitted: dag_fixture_emitted_add_fn + ) { + Accepted { value: authority, diagnostics: ad } => + (source.carried == authority.carried) && (d == None) && (ad == None) + Rejected { diagnostics: _ } => false + } } Rejected { diagnostics: _ } => false } @@ -319,6 +346,6 @@ test fn ingest_bridge_realized() -> Bool { && ingest_fails_closed_off_frontier_holds() && ingest_fails_closed_ambiguous_holds() && ingest_fails_closed_on_unstamped_holds() - && ingest_cross_language_compile_refuses_underived_holds() + && ingest_cross_language_compile_accepts_holds() } diff --git a/src/v2/test/claim/manual/inhabitant_neutralization_test.dag b/src/v2/test/claim/manual/inhabitant_neutralization_test.dag index 6474712c03d..435d7a29dbb 100644 --- a/src/v2/test/claim/manual/inhabitant_neutralization_test.dag +++ b/src/v2/test/claim/manual/inhabitant_neutralization_test.dag @@ -18,9 +18,10 @@ import v2.extdeps.languages.go { import v2.extdeps.languages.python { python_int_inhabitant_node, python_emitted_add_fn_node, + python_source_text, python_target_model } -import v2.test.manual.ingest_bridge { ingest_cross_language_compile_refuses_underived_holds } +import v2.test.manual.ingest_bridge { ingest_cross_language_compile_accepts_holds } import v2.test.manual.python_grammar_claim { python_grammar_parse_fixture } import v2.extdeps.languages.typescript { ts_source_text, ts_target_model } import v2.std.inhabitant_neutralization { @@ -134,7 +135,13 @@ test fn inhabitant_neutralization_go_unqualified_std_projection_lookup_rejects_h } } -fn inhabitant_neutralization_go_int64_to_ts_emit_refuses_underived_holds() -> Bool { +// FLIPPED 2026-09-07 per DESIGN 4b(4): was `..._emit_refuses_underived_holds`, asserting emit +// refused headed by infer_grounding_not_derived while the typescript number inhabitant sat on the +// grounding frontier. The language-general declared-inhabitant membership derivation grounded it, +// so the neutralized go-int64 tree now emits the canonical typescript add fn byte-identical to +// ts_source_text. Stands as the permanent regression control for that neutralize-then-emit round +// trip; the grounding-gate refusal class stays covered by the synthetic translate-level controls. +fn inhabitant_neutralization_go_int64_to_ts_emit_round_trip_holds() -> Bool { match neutralize_core_for_target( core: go_int64_falsifier_emitted_add_fn_node(), target: ts_target_model() @@ -143,9 +150,8 @@ fn inhabitant_neutralization_go_int64_to_ts_emit_refuses_underived_holds() -> Bo match infer(tree: neutralized) { Accepted { value: inferred, diagnostics: _ } => match emit(tree: inferred, target: ts_target_model()) { - Rejected { diagnostics: d } => - d.head.reason == ^infer_grounding_not_derived - Accepted { value: _, diagnostics: _ } => false + Accepted { value: source, diagnostics: _ } => source.carried == ts_source_text + Rejected { diagnostics: _ } => false } Rejected { diagnostics: _ } => false } @@ -171,10 +177,16 @@ test fn inhabitant_neutralization_third_inhabitant_falsifier_holds() -> Bool { && inhabitant_neutralization_go_int_additive_numeric_target_lookup_holds() && inhabitant_neutralization_go_additive_numeric_lookup_tiebreak_selects_platform_int_holds() && inhabitant_neutralization_neutralize_python_to_go_holds() - && inhabitant_neutralization_go_int64_to_ts_emit_refuses_underived_holds() + && inhabitant_neutralization_go_int64_to_ts_emit_round_trip_holds() } -test fn inhabitant_neutralization_emit_after_neutralize_refuses_underived_holds() -> Bool { +// FLIPPED 2026-09-07 per DESIGN 4b(4): was `..._emit_after_neutralize_refuses_underived_holds`, +// asserting emit refused headed by infer_grounding_not_derived while the typescript number +// inhabitant sat on the grounding frontier. The language-general declared-inhabitant membership +// derivation grounded it, so the neutralized python tree now emits the canonical typescript add fn +// byte-identical to ts_source_text. Permanent regression control for the neutralize-then-emit +// round trip; the grounding-gate refusal class stays covered by the synthetic translate controls. +test fn inhabitant_neutralization_emit_after_neutralize_round_trip_holds() -> Bool { match neutralize_core_for_target( core: python_emitted_add_fn_node(), target: ts_target_model() @@ -183,9 +195,8 @@ test fn inhabitant_neutralization_emit_after_neutralize_refuses_underived_holds( match infer(tree: neutralized) { Accepted { value: inferred, diagnostics: _ } => match emit(tree: inferred, target: ts_target_model()) { - Rejected { diagnostics: d } => - d.head.reason == ^infer_grounding_not_derived - Accepted { value: _, diagnostics: _ } => false + Accepted { value: source, diagnostics: _ } => source.carried == ts_source_text + Rejected { diagnostics: _ } => false } Rejected { diagnostics: _ } => false } @@ -204,11 +215,24 @@ fn inhabitant_neutralization_raw_emit_still_fails_closed() -> Bool { } } -fn inhabitant_neutralization_same_language_cross_language_compile_unchanged_holds() -> Bool { - ingest_cross_language_compile_refuses_underived_holds() +// The same-language ingest compile this guard watches flipped from frontier refusal +// (infer_grounding_not_derived) to clean acceptance on 2026-09-06, under the declared-inhabitant +// membership derivation (v2.compiler.infer infer_node_declared_in_language_inhabitants, widened to +// the closed ingest set on 2026-09-07) -- an infer-lane change, not a neutralization one, which is +// exactly the distinction this guard exists to draw. +fn inhabitant_neutralization_same_language_cross_language_compile_accepts_holds() -> Bool { + ingest_cross_language_compile_accepts_holds() } -fn inhabitant_neutralization_same_flavor_python_emit_refuses_underived_holds() -> Bool { +// FLIPPED 2026-09-07 per DESIGN 4b(4): was `..._same_flavor_python_emit_refuses_underived_holds`, +// asserting emit refused headed by infer_grounding_not_derived while the python int inhabitant sat +// on the grounding frontier. The language-general declared-inhabitant membership derivation +// grounded it, so the same-language (python→python) neutralize-then-emit now round-trips to the +// canonical python source byte-identical to python_source_text. Permanent regression control for +// that acceptance; the fail-closed half of this guard is carried by +// inhabitant_neutralization_raw_emit_still_fails_closed (which still refuses, now at +// NoTargetCandidate on the un-neutralized cross-target emit). +fn inhabitant_neutralization_same_flavor_python_emit_round_trip_holds() -> Bool { match neutralize_core_for_target( core: python_emitted_add_fn_node(), target: python_target_model() @@ -217,9 +241,8 @@ fn inhabitant_neutralization_same_flavor_python_emit_refuses_underived_holds() - match infer(tree: neutralized) { Accepted { value: inferred, diagnostics: _ } => match emit(tree: inferred, target: python_target_model()) { - Rejected { diagnostics: d } => - d.head.reason == ^infer_grounding_not_derived - Accepted { value: _, diagnostics: _ } => false + Accepted { value: source, diagnostics: _ } => source.carried == python_source_text + Rejected { diagnostics: _ } => false } Rejected { diagnostics: _ } => false } @@ -228,8 +251,8 @@ fn inhabitant_neutralization_same_flavor_python_emit_refuses_underived_holds() - } test fn inhabitant_neutralization_03_ingest_regression_guard_holds() -> Bool { - inhabitant_neutralization_same_language_cross_language_compile_unchanged_holds() - && inhabitant_neutralization_same_flavor_python_emit_refuses_underived_holds() + inhabitant_neutralization_same_language_cross_language_compile_accepts_holds() + && inhabitant_neutralization_same_flavor_python_emit_round_trip_holds() && inhabitant_neutralization_raw_emit_still_fails_closed() } diff --git a/src/v2/test/claim/parse/parse_table_claims_test.dag b/src/v2/test/claim/parse/parse_table_claims_test.dag index 353540a2eaa..79119df9078 100644 --- a/src/v2/test/claim/parse/parse_table_claims_test.dag +++ b/src/v2/test/claim/parse/parse_table_claims_test.dag @@ -20,8 +20,8 @@ test fn parse_table_token_position_indices_no_extra_append_holds() -> Bool { && ((length(xs: token_position_indices(tokens: parse_table_two_tokens)) == 4) == false) } -fn nullable_set_singleton(name: Symbol) -> PointwisePower { - PointwisePower { member: fn(sym) { sym == name } } +fn nullable_set_singleton(name: Symbol) -> List { + Cons { head: name, tail: std.algebra.Empty } } test fn parse_table_nullable_morphism_holds() -> Bool { diff --git a/src/v2/test/claim/self_host/emit_coverage_frontier_test.dag b/src/v2/test/claim/self_host/emit_coverage_frontier_test.dag index b72ecb12425..76c0dd5444b 100644 --- a/src/v2/test/claim/self_host/emit_coverage_frontier_test.dag +++ b/src/v2/test/claim/self_host/emit_coverage_frontier_test.dag @@ -43,17 +43,49 @@ test fn witness_unlisted_subject_has_no_row() -> Bool { } test fn witness_split_matches_expected() -> Bool { - (length(xs: self_emitted_native_rows()) == 1) - && (length(xs: interpreter_retained_rows()) == 14) + (length(xs: self_emitted_native_rows()) == 15) + && (length(xs: interpreter_retained_rows()) == 0) } -test fn witness_only_classical_not_ingested_is_native() -> Bool { +// Identity-grain census of the native rows (DESIGN section 5: completeness is an identity join, +// not a count equality — the count leg above is the budget cross-check, this leg names the +// members). 2026-09-07: the add family joined classical_not when its native-only verdict arm +// landed, and the complement family followed on the same arm shape run through the logic family +// crate. 2026-09-08: the field_access family followed against its own family one-build cache +// (octet 9 pinned), the three match_loop_fold family rows (match, loop, fold_closure) +// followed on the same arm shape run through the three-member family crate, the meet_join +// family followed on the complement arm's member-parameterized helper against the same logic +// family crate (octets meet=1 join=1 pinned), the variant_construct family followed against +// its own family one-build cache (octet 9 pinned), and the final six rows closed the frontier: +// filesystem_read and shell_exec_run on literal octets grounded by their enrolled fixture pins, +// and module, produced_module, call, record_construct on per-family fixed workspace roots +// (octets 5/5/7/9 pinned against the equals_eval pairs' oracle legs). The fifteen decl names +// are the closed membership; the InterpreterRetained population is zero. +test fn witness_native_rows_closed_membership_holds() -> Bool { let natives = self_emitted_native_rows() - (length(xs: natives) == 1) + (length(xs: natives) == 15) && fold( natives, init: true, - f: fn(acc, row) { acc && (row.subject.decl_name == "ingested_classical_not_source") } + f: fn(acc, row) { + acc && ( + (row.subject.decl_name == "ingested_classical_not_source") + || (row.subject.decl_name == "rust_add_inferred_tree") + || (row.subject.decl_name == "emit_complement_eval_subject") + || (row.subject.decl_name == "emit_field_eval_subgraph_node") + || (row.subject.decl_name == "fold_fixture_param_call_body") + || (row.subject.decl_name == "table_fixture_loop_arrow_with_body") + || (row.subject.decl_name == "table_fixture_match_arrow_with_body") + || (row.subject.decl_name == "meet_join_eval_subject") + || (row.subject.decl_name == "emit_variant_construct_eval_subgraph_node") + || (row.subject.decl_name == "rust_filesystem_read_family_target_model_staging") + || (row.subject.decl_name == "emit_host_module_add_equals_eval_holds") + || (row.subject.decl_name == "emit_host_produced_module_add_equals_eval_holds") + || (row.subject.decl_name == "rust_shell_exec_run_family_target_model_staging") + || (row.subject.decl_name == "rust_emit_host_call_equals_eval_holds") + || (row.subject.decl_name == "rust_record_construct_emit_host_equals_eval_holds") + ) + } ) } diff --git a/src/v2/test/claim/stage0_partition_rebuild_scope_witness_test.dag b/src/v2/test/claim/stage0_partition_rebuild_scope_witness_test.dag index 76bbd9948b1..3574768c461 100644 --- a/src/v2/test/claim/stage0_partition_rebuild_scope_witness_test.dag +++ b/src/v2/test/claim/stage0_partition_rebuild_scope_witness_test.dag @@ -19,12 +19,16 @@ import gunbc.stage0_partition_rebuild_scope { PartitionRebuildDecision, PartitionRebuildScopeDerived, RebuildScopeRefused, + ReleaseScopeEmpty, SharedCompilerBuildInputChanged, WholeCompilerRebuildRequired, rebuild_scope_excluded_packages, stage0_partition_rebuild_is_actuatable, stage0_partition_rebuild_decision, stage0_partition_rebuild_decision_today, + stage0_partition_rebuild_excluded_today, + stage0_partition_rebuild_packages_today, + stage0_partition_rebuild_release_scope_empty_today, stage0_next_pass_executable_assembly } @@ -72,6 +76,7 @@ fn witness_closure_of(changed: List) -> List { PartitionRebuildScopeDerived { changed_mirrors: _, owning_packages: _, package_closure: c, executable_assembly: _ } => c WholeCompilerRebuildRequired { cause: _ } => [] RebuildScopeRefused { cause: _ } => [] + ReleaseScopeEmpty { changed_mirrors: _ } => [] } } @@ -97,6 +102,7 @@ test fn witness_leaf_edit_compiles_owner_and_downstream_holds() -> Bool { }) WholeCompilerRebuildRequired { cause: _ } => false RebuildScopeRefused { cause: _ } => false + ReleaseScopeEmpty { changed_mirrors: _ } => false } } @@ -135,6 +141,7 @@ test fn witness_partition_generation_edit_requires_whole_rebuild_holds() -> Bool WholeCompilerRebuildRequired { cause: SharedCompilerBuildInputChanged { mirrors: _ } } => false PartitionRebuildScopeDerived { changed_mirrors: _, owning_packages: _, package_closure: _, executable_assembly: _ } => false RebuildScopeRefused { cause: _ } => false + ReleaseScopeEmpty { changed_mirrors: _ } => false } } @@ -144,6 +151,7 @@ test fn witness_shared_build_input_edit_requires_whole_rebuild_holds() -> Bool { WholeCompilerRebuildRequired { cause: PartitionGenerationAuthorityChanged { mirrors: _ } } => false PartitionRebuildScopeDerived { changed_mirrors: _, owning_packages: _, package_closure: _, executable_assembly: _ } => false RebuildScopeRefused { cause: _ } => false + ReleaseScopeEmpty { changed_mirrors: _ } => false } } @@ -179,6 +187,7 @@ test fn witness_unowned_mirror_refuses_holds() -> Bool { RebuildScopeRefused { cause: NoChangedMirrorsToRebuild } => false WholeCompilerRebuildRequired { cause: _ } => false PartitionRebuildScopeDerived { changed_mirrors: _, owning_packages: _, package_closure: _, executable_assembly: _ } => false + ReleaseScopeEmpty { changed_mirrors: _ } => false } } @@ -200,6 +209,7 @@ test fn witness_compiler_tests_rust_rebuild_owner_is_v1_infer_holds() -> Bool { && witness_has(items: closure, item: "v1-compiler") WholeCompilerRebuildRequired { cause: _ } => false RebuildScopeRefused { cause: _ } => false + ReleaseScopeEmpty { changed_mirrors: _ } => false } } @@ -243,6 +253,7 @@ test fn witness_duplicate_owner_refuses_holds() -> Bool { RebuildScopeRefused { cause: NoChangedMirrorsToRebuild } => false WholeCompilerRebuildRequired { cause: _ } => false PartitionRebuildScopeDerived { changed_mirrors: _, owning_packages: _, package_closure: _, executable_assembly: _ } => false + ReleaseScopeEmpty { changed_mirrors: _ } => false } } @@ -263,6 +274,7 @@ test fn witness_unlocatable_changed_path_refuses_holds() -> Bool { RebuildScopeRefused { cause: NoChangedMirrorsToRebuild } => false WholeCompilerRebuildRequired { cause: _ } => false PartitionRebuildScopeDerived { changed_mirrors: _, owning_packages: _, package_closure: _, executable_assembly: _ } => false + ReleaseScopeEmpty { changed_mirrors: _ } => false } } @@ -286,6 +298,7 @@ test fn witness_empty_changed_set_is_nothing_to_rebuild_holds() -> Bool { RebuildScopeRefused { cause: PackageDependencyClosureUnderivable { package_name: _, detail: _ } } => false WholeCompilerRebuildRequired { cause: _ } => false PartitionRebuildScopeDerived { changed_mirrors: _, owning_packages: _, package_closure: _, executable_assembly: _ } => false + ReleaseScopeEmpty { changed_mirrors: _ } => false } } @@ -306,6 +319,7 @@ test fn witness_unreached_executable_package_refuses_holds() -> Bool { RebuildScopeRefused { cause: PackageDependencyClosureUnderivable { package_name: _, detail: _ } } => false WholeCompilerRebuildRequired { cause: _ } => false PartitionRebuildScopeDerived { changed_mirrors: _, owning_packages: _, package_closure: _, executable_assembly: _ } => false + ReleaseScopeEmpty { changed_mirrors: _ } => false } } @@ -331,6 +345,7 @@ test fn witness_today_derives_scope_with_assembled_executable_holds() -> Bool { }) WholeCompilerRebuildRequired { cause: _ } => false RebuildScopeRefused { cause: _ } => false + ReleaseScopeEmpty { changed_mirrors: _ } => false } } @@ -370,3 +385,59 @@ test fn witness_next_pass_executable_assembly_is_assembled_holds() -> Bool { ExecutableAssemblyUnavailable { trigger: PartitionedClaimExecutorAssembly { capability: _ }, detail: _ } => false } } + +// CONTROL 7 -- THE RELEASE-EXCLUDED CLASS, AND ITS BOUNDARY. compiler_tests.rs is emitted by +// v1.compiler.emit_rust emit_compiler_tests_module with every item behind #[cfg(test)], so its +// bytes contribute nothing to the release executable: its precise rebuild scope is the empty +// set, answered as ReleaseScopeEmpty rather than refused. (The outer `mod compiler_tests;` IS +// ungated, so the shell crate still recompiles on the fingerprint -- the claim is about the +// executable bytes, and the 2026-09-08 convergence stage-2 receipt shows input seed digest == +// output seed digest with this file as the only installed surface.) THE BOUNDARY IS CONTROL 4 +// ABOVE: cli_run.rs is unowned and NOT on the release-excluded roster, and it still refuses +// MirrorHasNoOwningPackage -- the roster names its members, so "unowned" and "excluded by +// construction" can never collapse into one arm. Measured live 2026-09-08: a regen round whose +// generation-2 drift was compiler_tests.rs alone (the PointwisePower inhabitant-row removal +// rewrote the derived coercion assertions) refused its stage-2 rebuild MirrorHasNoOwningPackage +// before this arm existed. +test fn witness_release_excluded_mirror_scope_is_empty_holds() -> Bool { + match stage0_partition_rebuild_decision_today(changed_mirrors: ["compiler_tests.rs"], unlocatable: []) { + ReleaseScopeEmpty { changed_mirrors: ms } => (count(ms) == 1) && witness_has(items: ms, item: "compiler_tests.rs") + PartitionRebuildScopeDerived { changed_mirrors: _, owning_packages: _, package_closure: _, executable_assembly: _ } => false + WholeCompilerRebuildRequired { cause: _ } => false + RebuildScopeRefused { cause: _ } => false + } +} + +// The actuation shape of the empty scope: actuatable (the verification build still runs), no +// package may compile, and EVERY package is excluded -- so one compiled crate is a located +// refusal, not an unnoticed widening. +test fn witness_release_excluded_scope_actuation_shape_holds() -> Bool { + let decision = stage0_partition_rebuild_decision_today(changed_mirrors: ["compiler_tests.rs"], unlocatable: []) + stage0_partition_rebuild_is_actuatable(decision: decision) + && (count(stage0_partition_rebuild_packages_today(changed_mirrors: ["compiler_tests.rs"], unlocatable: [])) == 0) + && (count(stage0_partition_rebuild_excluded_today(changed_mirrors: ["compiler_tests.rs"], unlocatable: [])) == count(generated_partition_crate_rows)) +} + +// A mixed change set scopes on its release-visible members alone: the excluded mirror rides the +// install without adding a package and without refusing. +test fn witness_mixed_change_set_scopes_on_release_visible_mirrors_holds() -> Bool { + match stage0_partition_rebuild_decision_today(changed_mirrors: ["compiler_tests.rs", "std_effects.rs"], unlocatable: []) { + PartitionRebuildScopeDerived { changed_mirrors: _, owning_packages: o, package_closure: c, executable_assembly: _ } => + (count(o) == 1) + && witness_has(items: o, item: "v1-stage0-std-surface") + && witness_has(items: c, item: "v1-stage0-std-surface") + ReleaseScopeEmpty { changed_mirrors: _ } => false + WholeCompilerRebuildRequired { cause: _ } => false + RebuildScopeRefused { cause: _ } => false + } +} + +// THE HOST-FACING PROJECTION, pinned by name because required_regen_host.rs calls exactly +// stage0_partition_rebuild_release_scope_empty_today to admit an empty package closure as the +// verification-build answer rather than refuse it. Both arms are asserted: the excluded-only +// set answers true, and a release-visible change answers false -- a constant-true projection +// would silently discharge the host's empty-closure guard for every decision. +test fn witness_release_scope_empty_query_answers_both_arms_hold() -> Bool { + stage0_partition_rebuild_release_scope_empty_today(changed_mirrors: ["compiler_tests.rs"], unlocatable: []) + && !stage0_partition_rebuild_release_scope_empty_today(changed_mirrors: ["std_effects.rs"], unlocatable: []) +} diff --git a/src/v2/test/claim/translate_underived_refusal_test.dag b/src/v2/test/claim/translate_underived_refusal_test.dag index 61d5537434d..af9449d624a 100644 --- a/src/v2/test/claim/translate_underived_refusal_test.dag +++ b/src/v2/test/claim/translate_underived_refusal_test.dag @@ -10,7 +10,7 @@ import v2.extdeps.languages.python { python_emitted_add_fn_node, python_target_model } -import v2.extdeps.languages.typescript { ts_target_model } +import v2.extdeps.languages.typescript { ts_source_text, ts_target_model } import v2.std.inhabitant_neutralization { neutralize_core_for_target } import v2.test.manual.python_grammar_claim { python_grammar_parse_fixture } import v2.extdeps.languages.rust { @@ -393,7 +393,17 @@ fn translate_accepts_synthetic_empty_facts_tree() -> Bool { } } -test fn cross_language_compile_refuses_canonical_underived_holds() -> Bool { +// FLIPPED 2026-09-07 per DESIGN 4b(4). This was the pipeline-level frontier guard +// `cross_language_compile_refuses_canonical_underived_holds`, asserting the python→typescript +// compile refused headed by `infer_grounding_not_derived` while the python inhabitants sat on the +// grounding frontier — the control the add-slice stall cited as confirming the refusal persisted. +// The declared-inhabitant membership derivation widening to the closed ingest set +// (v2.compiler.infer infer_node_declared_in_language_inhabitants) grounded the python and +// typescript inhabitant fact atoms, so the compile now ACCEPTS and emits the canonical typescript +// add fn byte-identical to `ts_source_text` — verified by execution. It stands as the permanent +// regression control for that acceptance; the translate-level grounding-gate refusal class stays +// covered by the synthetic underived controls in this file. +test fn cross_language_compile_python_to_typescript_round_trip_holds() -> Bool { match python_grammar_parse_fixture() { Accepted { value: parse_tree, diagnostics: _ } => match cross_language_compile( @@ -401,9 +411,9 @@ test fn cross_language_compile_refuses_canonical_underived_holds() -> Bool { source_model: python_target_model(), target_model: ts_target_model() ) { - Rejected { diagnostics: d } => - translate_refused_canonical_grounding(d: d) - Accepted { value: _, diagnostics: _ } => false + Accepted { value: source, diagnostics: _ } => + source.carried == ts_source_text + Rejected { diagnostics: _ } => false } Rejected { diagnostics: _ } => false } diff --git a/src/v2/workflow/floor_cost_debt.dag b/src/v2/workflow/floor_cost_debt.dag index bc3a88a892d..db469b9a341 100644 --- a/src/v2/workflow/floor_cost_debt.dag +++ b/src/v2/workflow/floor_cost_debt.dag @@ -931,7 +931,7 @@ fn floor_cost_debt_proven_chunk_12() -> List { } fn floor_cost_debt_proven_chunk_14() -> List { - Cons { head: "v2.test.claim.dag_acceptance.acceptance_accounts_for_a_stage_the_contract_never_asked_for", tail: Cons { head: "v2.test.claim.emit.trait_derive_supplemental_generic_bound_contract.rust_btree_set_debug_contract_requires_ord_from_im_ord_set_authority", tail: Cons { head: "v2.test.claim.emit.trait_derive_supplemental_generic_bound_contract.rust_btree_set_partial_eq_contract_requires_ord_from_im_ord_set_authority", tail: Cons { head: "v2.test.claim.emit.trait_derive_supplemental_generic_bound_contract.rust_btree_set_serialize_contract_requires_ord_and_clone_from_im_ord_set_authority", tail: Cons { head: "v2.test.claim.emit.trait_derive_supplemental_generic_bound_contract.rust_hash_set_unrepresentable_hash_contracts_refuse_with_typed_blockers", tail: Cons { head: "v2.test.claim.emit.trait_derive_supplemental_generic_bound_contract.rust_set_target_bundle_round_trip_preserves_both_production_policies", tail: Cons { head: "v2.test.claim.emit.trait_derive_supplemental_generic_bound_contract.rust_vec_serialize_contract_requires_clone_from_im_vector_authority", tail: Cons { head: "v2.test.claim.enforcement.lens_module_gate_witness.lens_closure_question_zero_holds_live", tail: Cons { head: "v2.test.claim.enforcement.lens_module_gate_witness.lens_closure_question_zero_live_matches_gate", tail: Cons { head: "v2.test.claim.enforcement.lens_module_gate_witness.lens_module_gate_holds_live", tail: Cons { head: "v2.test.claim.rust_crate_partition_witness.witness_partition_perturbation_member_coverage_holds", tail: Cons { head: "v2.test.claim.self_host.direct_rust_door_fixture_containment_witness_test.fixture_cargo_green_advance_is_not_behaviorally_equivalent", tail: Cons { head: "v2.test.claim.self_host.direct_rust_door_fixture_containment_witness_test.fixture_synthetic_mint_is_fixture_source_produced_only", tail: Cons { head: "v2.test.claim.translate_underived_refusal.cross_language_compile_refuses_canonical_underived_holds", tail: Cons { head: "v2.test.claim.translate_underived_refusal.translate_derived_emit_byte_identical_to_before_holds", tail: Cons { head: "v2.test.compiler.pipeline.lower_stage.pipeline_lower_add_fixture_relationship_green", tail: Cons { head: "v2.test.compiler.pipeline.tokenize_stage.pipeline_tokenize_pick_relationship_green", tail: Cons { head: "v2.test.emit.produced_decl_two_target.produced_decl_unwired_target_still_refuses", tail: Empty {} } } } } } } } } } } } } } } } } } } + Cons { head: "v2.test.claim.dag_acceptance.acceptance_accounts_for_a_stage_the_contract_never_asked_for", tail: Cons { head: "v2.test.claim.emit.trait_derive_supplemental_generic_bound_contract.rust_btree_set_debug_contract_requires_ord_from_im_ord_set_authority", tail: Cons { head: "v2.test.claim.emit.trait_derive_supplemental_generic_bound_contract.rust_btree_set_partial_eq_contract_requires_ord_from_im_ord_set_authority", tail: Cons { head: "v2.test.claim.emit.trait_derive_supplemental_generic_bound_contract.rust_btree_set_serialize_contract_requires_ord_and_clone_from_im_ord_set_authority", tail: Cons { head: "v2.test.claim.emit.trait_derive_supplemental_generic_bound_contract.rust_hash_set_unrepresentable_hash_contracts_refuse_with_typed_blockers", tail: Cons { head: "v2.test.claim.emit.trait_derive_supplemental_generic_bound_contract.rust_set_target_bundle_round_trip_preserves_both_production_policies", tail: Cons { head: "v2.test.claim.emit.trait_derive_supplemental_generic_bound_contract.rust_vec_serialize_contract_requires_clone_from_im_vector_authority", tail: Cons { head: "v2.test.claim.enforcement.lens_module_gate_witness.lens_closure_question_zero_holds_live", tail: Cons { head: "v2.test.claim.enforcement.lens_module_gate_witness.lens_closure_question_zero_live_matches_gate", tail: Cons { head: "v2.test.claim.enforcement.lens_module_gate_witness.lens_module_gate_holds_live", tail: Cons { head: "v2.test.claim.rust_crate_partition_witness.witness_partition_perturbation_member_coverage_holds", tail: Cons { head: "v2.test.claim.self_host.direct_rust_door_fixture_containment_witness_test.fixture_cargo_green_advance_is_not_behaviorally_equivalent", tail: Cons { head: "v2.test.claim.self_host.direct_rust_door_fixture_containment_witness_test.fixture_synthetic_mint_is_fixture_source_produced_only", tail: Cons { head: "v2.test.claim.translate_underived_refusal.cross_language_compile_python_to_typescript_round_trip_holds", tail: Cons { head: "v2.test.claim.translate_underived_refusal.translate_derived_emit_byte_identical_to_before_holds", tail: Cons { head: "v2.test.compiler.pipeline.lower_stage.pipeline_lower_add_fixture_relationship_green", tail: Cons { head: "v2.test.compiler.pipeline.tokenize_stage.pipeline_tokenize_pick_relationship_green", tail: Cons { head: "v2.test.emit.produced_decl_two_target.produced_decl_unwired_target_still_refuses", tail: Empty {} } } } } } } } } } } } } } } } } } } } fn floor_cost_debt_proven_chunk_15() -> List { diff --git a/src/v2/workflow/floor_expected_red.dag b/src/v2/workflow/floor_expected_red.dag index e51ae8c77bf..f89eff59cce 100644 --- a/src/v2/workflow/floor_expected_red.dag +++ b/src/v2/workflow/floor_expected_red.dag @@ -991,64 +991,6 @@ fn floor_expected_red_chunk_interpreter_first_optional_divergence() -> List List { } fn floor_expected_red_chunk_live_tree_admission() -> List { - Cons { head: "test.claim.argument_shape_at_a_declared_position_probe_test.an_unapplied_function_at_a_concrete_position_is_refused", tail: Cons { head: "test.claim.argument_shape_at_a_declared_position_probe_test.a_string_at_a_generic_position_is_refused", tail: Cons { head: "test.claim.ci_budget_tree_witness.witness_live_is_fail_closed", tail: Cons { head: "test.claim.ci_budget_tree_witness.witness_srv2_symmetric_to_srv1", tail: Cons { head: "test.claim.ci_budget_tree_witness.witness_srv3_outbudgets_srv1_by_exactly_the_overhead_gap", tail: Cons { head: "test.claim.doc_reachability_witness.doc_graph_has_no_dangling_links", tail: Cons { head: "test.claim.doc_reachability_witness.doc_graph_has_no_orphan_docs", tail: Cons { head: "test.claim.doc_reachability_witness.doc_graph_registered_plan_roots_all_admitted", tail: Cons { head: "test.claim.external_model_scope_live_cover_witness.frontier_cover_of_live_extdeps_tree_holds", tail: Cons { head: "test.claim.external_model_scope_witness.carriers_declare_scope_on_disk", tail: Cons { head: "test.claim.guarantee_floor_class_probe_witness.floor_generic_instantiation_hole_does_not_satisfy_the_refusing_control", tail: Cons { head: "test.claim.guarantee_floor_class_probe_witness.floor_generic_instantiation_is_still_silently_accepted", tail: Cons { head: "test.claim.guarantee_floor_class_probe_witness.floor_hole_receipt_records_acceptance_as_the_observation", tail: Cons { head: "test.claim.guarantee_probe_corpus_witness_test.bare_none_generic_parameter_field_is_outside_the_wall_boundary", tail: Cons { head: "test.claim.guarantee_probe_corpus_witness_test.dark_suite_dispositions_cover_migrated_v1_probes", tail: Cons { head: "test.claim.legacy_test_behavior_disposition_acceptance_test.legacy_test_behavior_unclassified_frontier_is_zero", tail: Cons { head: "test.claim.operation_argv_corpus_witness.operation_argv_corpus_executable_position_wall_fires_on_the_fixture_only", tail: Cons { head: "test.claim.operation_argv_corpus_witness.operation_argv_corpus_expression_residue_is_pinned", tail: Cons { head: "test.claim.sole_constructor_completeness_audit_probe_test.f10_direct_selected_open_type_is_clean", tail: Cons { head: "test.claim.sole_constructor_completeness_audit_probe_test.f10_direct_selected_sealed_type_refuses", tail: Cons { head: "test.claim.sole_constructor_completeness_audit_probe_test.f10_neither_direct_ab_at_least_one_violation", tail: Cons { head: "test.claim.sole_constructor_completeness_audit_probe_test.f10_neither_direct_order_independent", tail: Cons { head: "test.claim.sole_constructor_completeness_audit_probe_test.f13_variant_construction_refuses", tail: Cons { head: "test.claim.sole_constructor_completeness_audit_probe_test.f19_record_lit_default_value_position_refuses", tail: Cons { head: "v2.lens.mandatory_tag.corpus_scan_witness_test.corpus_live_clean_tree_wall_holds", tail: Cons { head: "v2.test.claim.enforcement.cost_coverage_witness.cost_coverage_dag_logic_has_three_fn_bodies", tail: Cons { head: "v2.test.claim.enforcement.cost_coverage_witness.cost_coverage_file_row_totality_holds_on_smoke", tail: Cons { head: "v2.test.claim.enforcement.cost_coverage_witness.cost_coverage_v2_ingested_body_is_not_decl_facts_skeleton", tail: Cons { head: "v2.test.execution.emit_ingest_grammar_relation_round_trip.same_grammar_parse_ingest_bridge_holds", tail: Cons { head: "v2.test.execution.self_host_candidate_generation.candidate_generation_translate_self_emit_dag_add_slice_holds", tail: Cons { head: "v2.test.lens_doc_reachability.doc_reachability_test.doc_graph_has_no_dangling_links", tail: Cons { head: "v2.test.lens_doc_reachability.doc_reachability_test.doc_graph_has_no_orphan_docs", tail: Cons { head: "v2.test.lens_doc_reachability.doc_reachability_test.doc_graph_is_clean", tail: Cons { head: "v2.test.lens_test_migration_debt.test_migration_debt_test.retained_rust_kernel_wall_holds_against_live_tree", tail: Cons { head: "v2.test.program_assembly.real_ingest.program_assembly_real_ingest_host_manifest_receipt_holds", tail: Cons { head: "v2.test.program_assembly.real_ingest.program_assembly_real_ingest_module_roots_parse_holds", tail: Cons { head: "v2.test.realization_vocabulary_containment.cli_vocabulary.rest_path_reaches_no_cli.withdrawing_the_consumer_admissions_surfaces_exactly_three", tail: Cons { head: "v2.test.self_host.compiler_closure_emit_from_ingest.compiler_closure_ingest_overflow_refuses_never_empty_holds", tail: Cons { head: "v2.test.self_host.compiler_closure_emit_from_ingest.compiler_closure_ingest_receipt_describes_carrier_holds", tail: Cons { head: "v2.test.self_host.compiler_closure_emit_from_ingest.compiler_closure_scoped_ingest_module_count_ok_holds", tail: Cons { head: "v2.test.self_host.compiler_closure_ingest_boundary_witness.compiler_closure_ingest_boundary_exact_cap_transport_holds", tail: Empty {} } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } + Cons { head: "test.claim.argument_shape_at_a_declared_position_probe_test.an_unapplied_function_at_a_concrete_position_is_refused", tail: Cons { head: "test.claim.argument_shape_at_a_declared_position_probe_test.a_string_at_a_generic_position_is_refused", tail: Cons { head: "test.claim.ci_budget_tree_witness.witness_live_is_fail_closed", tail: Cons { head: "test.claim.ci_budget_tree_witness.witness_srv2_symmetric_to_srv1", tail: Cons { head: "test.claim.ci_budget_tree_witness.witness_srv3_outbudgets_srv1_by_exactly_the_overhead_gap", tail: Cons { head: "test.claim.doc_reachability_witness.doc_graph_has_no_dangling_links", tail: Cons { head: "test.claim.doc_reachability_witness.doc_graph_has_no_orphan_docs", tail: Cons { head: "test.claim.doc_reachability_witness.doc_graph_registered_plan_roots_all_admitted", tail: Cons { head: "test.claim.external_model_scope_live_cover_witness.frontier_cover_of_live_extdeps_tree_holds", tail: Cons { head: "test.claim.external_model_scope_witness.carriers_declare_scope_on_disk", tail: Cons { head: "test.claim.guarantee_floor_class_probe_witness.floor_generic_instantiation_hole_does_not_satisfy_the_refusing_control", tail: Cons { head: "test.claim.guarantee_floor_class_probe_witness.floor_generic_instantiation_is_still_silently_accepted", tail: Cons { head: "test.claim.guarantee_floor_class_probe_witness.floor_hole_receipt_records_acceptance_as_the_observation", tail: Cons { head: "test.claim.guarantee_probe_corpus_witness_test.bare_none_generic_parameter_field_is_outside_the_wall_boundary", tail: Cons { head: "test.claim.guarantee_probe_corpus_witness_test.dark_suite_dispositions_cover_migrated_v1_probes", tail: Cons { head: "test.claim.legacy_test_behavior_disposition_acceptance_test.legacy_test_behavior_unclassified_frontier_is_zero", tail: Cons { head: "test.claim.operation_argv_corpus_witness.operation_argv_corpus_executable_position_wall_fires_on_the_fixture_only", tail: Cons { head: "test.claim.operation_argv_corpus_witness.operation_argv_corpus_expression_residue_is_pinned", tail: Cons { head: "test.claim.sole_constructor_completeness_audit_probe_test.f10_direct_selected_open_type_is_clean", tail: Cons { head: "test.claim.sole_constructor_completeness_audit_probe_test.f10_direct_selected_sealed_type_refuses", tail: Cons { head: "test.claim.sole_constructor_completeness_audit_probe_test.f10_neither_direct_ab_at_least_one_violation", tail: Cons { head: "test.claim.sole_constructor_completeness_audit_probe_test.f10_neither_direct_order_independent", tail: Cons { head: "test.claim.sole_constructor_completeness_audit_probe_test.f13_variant_construction_refuses", tail: Cons { head: "test.claim.sole_constructor_completeness_audit_probe_test.f19_record_lit_default_value_position_refuses", tail: Cons { head: "v2.lens.mandatory_tag.corpus_scan_witness_test.corpus_live_clean_tree_wall_holds", tail: Cons { head: "v2.test.claim.enforcement.cost_coverage_witness.cost_coverage_dag_logic_has_three_fn_bodies", tail: Cons { head: "v2.test.claim.enforcement.cost_coverage_witness.cost_coverage_file_row_totality_holds_on_smoke", tail: Cons { head: "v2.test.claim.enforcement.cost_coverage_witness.cost_coverage_v2_ingested_body_is_not_decl_facts_skeleton", tail: Cons { head: "v2.test.execution.emit_ingest_grammar_relation_round_trip.same_grammar_parse_ingest_bridge_holds", tail: Cons { head: "v2.test.lens_doc_reachability.doc_reachability_test.doc_graph_has_no_dangling_links", tail: Cons { head: "v2.test.lens_doc_reachability.doc_reachability_test.doc_graph_has_no_orphan_docs", tail: Cons { head: "v2.test.lens_doc_reachability.doc_reachability_test.doc_graph_is_clean", tail: Cons { head: "v2.test.lens_test_migration_debt.test_migration_debt_test.retained_rust_kernel_wall_holds_against_live_tree", tail: Cons { head: "v2.test.program_assembly.real_ingest.program_assembly_real_ingest_host_manifest_receipt_holds", tail: Cons { head: "v2.test.program_assembly.real_ingest.program_assembly_real_ingest_module_roots_parse_holds", tail: Cons { head: "v2.test.realization_vocabulary_containment.cli_vocabulary.rest_path_reaches_no_cli.withdrawing_the_consumer_admissions_surfaces_exactly_three", tail: Cons { head: "v2.test.self_host.compiler_closure_emit_from_ingest.compiler_closure_ingest_overflow_refuses_never_empty_holds", tail: Cons { head: "v2.test.self_host.compiler_closure_emit_from_ingest.compiler_closure_ingest_receipt_describes_carrier_holds", tail: Cons { head: "v2.test.self_host.compiler_closure_emit_from_ingest.compiler_closure_scoped_ingest_module_count_ok_holds", tail: Cons { head: "v2.test.self_host.compiler_closure_ingest_boundary_witness.compiler_closure_ingest_boundary_exact_cap_transport_holds", tail: Empty {} } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } + } fn floor_expected_red_chunks() -> List> { diff --git a/src/v2/workflow/floor_pure_producer_share.dag b/src/v2/workflow/floor_pure_producer_share.dag index f1e295aa349..252569a7cb5 100644 --- a/src/v2/workflow/floor_pure_producer_share.dag +++ b/src/v2/workflow/floor_pure_producer_share.dag @@ -212,9 +212,124 @@ import v2.std.collection { List } // the roster, and withdrawn anyway. A row earns its place by measured recompute AND measured // sharing AND a serve below that recompute, and it is the third conjunct that refused all // three. +// dag_prepared_grammar EARNS ITS ROW ON THE FILL THAT COULD NEVER LAND. `prepare_grammar` is the +// tier's built-in admitted arm, so admission was never its question; the v2-self-host branch's +// required-witnesses-floor lane measured the failure shape directly — twelve claims refused with +// cause=FillBudgetExceeded, every one dying mid-flight inside the SAME in-flight shared fill, and +// the run's cross-claim demand census names `prepare_grammar` at evals=12 with no store ever +// landing. Two independent defects composed into that cascade, and both are repaired at source, +// not budgeted around: (1) the fill's VALUE was not portable — `GrammarFirstAnalysis.nullable_set` +// was a `PointwisePower` closure tower, which publication refuses totally +// (ServeCacheValueNotPortable), so no completed fill could ever store; the carrier is now the +// enumeration it always was (`List`, the `sync_tokens` repair's own precedent); (2) the +// fill costs more than one claim's CPU budget on the lane's runner, so an in-fold first touch can +// never complete — the nullary producer moves that first touch to strict preparation, where this +// roster's warm path already forces the bash rows, and every claim then serves the landed fill. +// The instruments, re-derived rather than transcribed: the required floor's own +// required_floor_claim_cost.tsv outcome column (the twelve refusals) and its cross-claim demand +// census (the demand concentration), plus this run's `[floor-shared-fill]` ledger, which must now +// show the fill landing at preparation with disposition=Stored and the twelve former fillers +// serving hits. +// THE THREE INGESTED-FIXTURE PIPELINE ROWS BELOW EARN THEIR PLACE ON THE v2-self-host BRANCH'S +// OWN FLOOR RECEIPT, not on inference. That run refused five changed witnesses over the per-claim +// ceiling at one head: v2.test.long.emit_host_classical_not_ingested_equals_eval's +// canonical/staging/staging-swapped emit claims (FAIL rows naming marginal_cpu_ns 488209894 and +// 473955219 against limit_ms=500 — each dying when a 13-29ms in-flight fill of the rostered +// target_project_arrow_body_to_value_expression crossed the line — plus the canonical claim +// INTERRUPTED-BEFORE-VERDICT at cpu_at_least=501ms) and +// v2.test.long.emit_host_native_only_verdict's two produced_module claims (505ms +// COMPLETED-OVER-COST-REQUIREMENT, 542ms INTERRUPTED). The recompute instrument is claim_batch's +// gross [witness] receipt over the two entry files: every emit claim in the classical_not family +// re-runs tokenize→parse→normalize→resolve→find-arrow over one of two module-constant sources +// (~370-420ms of each ~475-510ms claim, with every pipeline claim in that file funneling through +// one of the two constants), and produced_add_module_source is ~382ms of each ~476-505ms +// produced_module claim. The sharing census is the call-site graph itself: the two classical_not +// arrows feed every tree producer in their file, and produced_add_module_source feeds the +// equals_eval pair and the native_only pair. Serve-below-recompute is the header's zero-walk +// capability rather than a new measurement: the values are a resolved-module Node tree and a +// Medium, both small and fully portable (no Closure/OriginBoundNode at any depth — the +// InferredTree one stage up carries the facts PartialFunction and is deliberately NOT the share +// point), so a serve is an Rc hand-over against a ~370-382ms recompute. Re-derive rather than +// trust: claim_batch --entry [witness] lines for the recompute side, and this +// run's [floor-shared-fill] ledger, which must show the three fills landing at preparation with +// disposition=Stored and the five former over-budget claims serving hits. +// +// WARM, NEVER CLAIM-FORCED, BY THE CEILING ARITHMETIC. A claim-forced fill happens inside the +// first touching claim's fold; a ~370-382ms fill leaves under 130ms of headroom on this lane's +// own measurement, and the lane's runner crossed the ceiling on the meet/join arms at 545/550ms +// for work this host performs in ~400ms (run 34290525720's COMPLETED-OVER-COST rows) — so an +// in-fold fill of this size dies mid-flight on the lane exactly as prepare_grammar's did (the +// dag_prepared_grammar row above). All three producers are nullary, so preparation forces them +// outside every per-claim budget, and every claiming claim serves the landed fill. +// +// THE DIRECT-RUST-DOOR SPECIMEN'S RESOLVED TREE EARNS ITS ROW ON THE NEXT TIER THE +// prepare_grammar WARM STORE UNMASKED. Required-floor run 34311472357 (PR #10692 head +// abf0908222) refused seven changed witnesses as INTERRUPTED-BEFORE-VERDICT at +// cpu_at_least=501-510ms against limit_ms=500 — the four specimen claims of +// v2.test.execution.infer_atom_grounding_rules, the two of +// v2.test.execution.infer_product_introduction, and the one of +// v2.test.execution.dag_binding_denotation. The prior run 34290525720 names the same seven in +// its prepare_grammar FAIL rows at marginal_cpu_ns ~10-12M: they were inside the shared-fill +// cluster then, and the grammar warm store let them run to their own cost, which is over the +// line on the lane's runner. The recompute instrument is claim_batch's gross [witness] receipt +// over the three entry files (~304-333ms per claim locally, ~390k eval_steps each) plus the +// assemble-only/assemble-then-infer probe pair that splits the pipeline: the +// ingest→assemble walk over the fixture's add_probe module is ~245ms of each claim and infer +// is ~72ms, with the census folds inside the remainder. The sharing census is the call-site +// graph itself: seven claims across three modules funnel through one specimen pipeline, whose +// three per-file specimen_inferred spellings are dissolved into the fixture's home with this +// row (DESIGN §2/§3 — one pipeline, one authority). Serve-below-recompute is the header's +// zero-walk capability rather than a new measurement: the value is the resolved-module +// Outcome tree — small, fully portable (no Closure/OriginBoundNode at any depth; the +// InferredTree one stage up carries the facts PartialFunction and is deliberately NOT the share +// point, so infer re-derives per claim from the served tree) — so a serve is an Rc hand-over +// against a ~245ms recompute. WARM, NEVER CLAIM-FORCED, by the same ceiling arithmetic as the +// rows above: a ~245ms fill on this host is a ~410-440ms fill on the lane's runner (the +// 501-510ms interrupts for ~304-333ms of local work), leaving under ~90ms of in-fold headroom, +// and seven demanding claims means a mid-fill death is abandoned un-stored and restarted per +// claim — the prepare_grammar failure shape exactly. Re-derive rather than trust: claim_batch +// --entry [witness] lines for the recompute side, and this run's +// [floor-shared-fill] ledger, which must show the fill landing at preparation with +// disposition=Stored and the seven former interrupted claims serving hits. +// THE TWO FAMILY-CRATE EMITTED PAIRS EARN THEIR ROWS ON THE RUN THE DOOR-SPECIMEN STORE +// UNMASKED. Required-floor run 34315228217 (PR #10692 head da5c00c301) refused +// v2.test.long.emit_host_native_only_verdict's emit_host_native_only_loop_holds as +// COMPLETED-OVER-COST-REQUIREMENT at cost=504ms CEILING against limit_ms=500 — and the run's own +// cost receipt names the whole exposed population: all twelve native-only family arms (the six +// match/loop/fold claims at 462-503ms cpu, the six complement/meet/join claims at 458-487ms) +// sitting within one runner-swing of the line, while the file's next-nearest claim stands at +// 388ms. The recompute instrument is claim_batch's gross [witness] receipt over the entry file +// plus one probe per producer: each claim re-ran its family's full pure pipeline inline — +// primary trees, member assembly, emit_family, the native key — measured at 186ms +// (mlf_family_primary_emitted) and 142ms (logic_family_primary_emitted) of each ~174-247ms local +// claim on this host, against the lane's 458-503ms for the same work. The sharing census is the +// call-site graph itself: six claims per family funnel through one member-independent pipeline, +// and the two per-arm helper spellings in the verdict module are dissolved into the families' +// own homes with these rows (DESIGN §2/§3 — one pipeline per family, one authority). The share +// point is the deepest PORTABLE stage: the member list carries InferredTrees (the facts +// PartialFunction is origin-bound), so the served value is the emitted crate source plus its +// native key — EmittedFamilyCrate { source: Medium, native_key: ContentHash }, declared +// once in v2.compiler.emit_module rather than once per family — small, fully portable (no +// Closure/OriginBoundNode at any depth), so a serve is an Rc hand-over against a ~142-186ms +// recompute. WARM, NEVER CLAIM-FORCED, by the same ceiling arithmetic as the rows above: a +// 142-186ms fill on this host is a ~370-485ms fill at the lane's observed ratio (458-503ms of +// lane cpu for 174-247ms of local work), and the claim still owes its cached native run after +// the fill — an in-fold fill of this size dies mid-flight on the lane exactly as +// prepare_grammar's did. Re-derive rather than trust: claim_batch --entry +// src/v2/test/claim/execution/long/emit_host_native_only_verdict_test.dag [witness] lines for +// the claim side, one-claim probe entries over the two producers for the pipeline side, and +// this run's [floor-shared-fill] ledger, which must show both fills landing at preparation with +// disposition=Stored and the twelve former ceiling-band claims serving hits. data floor_cross_claim_pure_producers_warm: List = [ "v2.extdeps.languages.bash_command_fold.bash_fold_formal_productions", - "v2.extdeps.languages.bash_command_fold.bash_fold_lex" + "v2.extdeps.languages.bash_command_fold.bash_fold_lex", + "v2.compiler.program_assembly.dag_prepared_grammar", + "v2.test.long.emit_host_produced_module_equals_eval.produced_add_module_source", + "v2.test.long.emit_host_classical_not_ingested_equals_eval.ingested_classical_not_arrow_with_body", + "v2.test.long.emit_host_classical_not_ingested_equals_eval.ingested_classical_not_swapped_arrow_with_body", + "v2.compiler.self_host.direct_rust_door_fixture.direct_rust_door_specimen_resolved", + "v2.test.execution.emit_on_demand_match_loop_fold_family_witness.mlf_family_primary_emitted", + "v2.test.execution.emit_on_demand_family_crate_witness.logic_family_primary_emitted" ] // grammar_relation_row_for_emitted HAS NOW BEEN MEASURED ON THE THIRD CONJUNCT, AND IT PASSES.