diff --git a/src/v2/compiler/04_infer.dag b/src/v2/compiler/04_infer.dag index 194bd74a700..b4a07fc6d24 100644 --- a/src/v2/compiler/04_infer.dag +++ b/src/v2/compiler/04_infer.dag @@ -7,7 +7,11 @@ import v2.std.bounded_lattice_completeness { partial_bounded_lattice_instances_in_tree } import v2.compiler.resolve { ResolvedTree } -import v2.extdeps.languages.dag { dag_node_is_int_literal_atom } +import v2.extdeps.languages.dag { + DagCanonicalBoolLiteral, + DagCanonicalIntLiteral, + dag_canonical_literal_from_node +} import v2.std.cardinality { TerminationProof, termination_proof_witness_for_node } import v2.std.collection { List, @@ -397,7 +401,7 @@ fn concat_inferred_facts_entries( }) } -data infer_node_facts_no_derivation_note: String = "infer_node_facts used to reach solve_constraints with the singleton candidate set [root] and the constraint-satisfaction predicate, whose preservation rule is (algebra == source_facts && candidate == source_facts) with algebra bound to the root itself — so the witness search reduced to root == root and always succeeded. The grounding it minted carried the source node as its own structural evidence, which inferred_facts_resolved_type reads as the node's resolved type. That is source identity standing where a derivation belongs, so this path no longer mints a grounding at all: the descent proof is still derived (it is a real judgment), and the grounding is the typed GroundingNotDerived frontier. solve_constraints itself is untouched — it remains the structural-solve authority named by dag/gunbc/plans/solve_higher_order_design.dag, to be EXTENDED with real candidate generation, never forked." +data infer_node_facts_no_derivation_note: String = "infer_node_facts used to reach solve_constraints with the singleton candidate set [root] and the constraint-satisfaction predicate, whose preservation rule is (algebra == source_facts && candidate == source_facts) with algebra bound to the root itself — so the witness search reduced to root == root and always succeeded. The grounding it minted carried the source node as its own structural evidence, which inferred_facts_resolved_type reads as the node's resolved type. That is source identity standing where a derivation belongs. The Bool and Int arms now consume the typed canonical-literal query and raise those derivations to leaf facts; every other node on this path carries the typed GroundingNotDerived frontier. solve_constraints itself is untouched — it 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 { match infer_bounded_lattice_consumer_gate(consumer: n, partials: partials) { @@ -407,10 +411,25 @@ fn infer_node_facts(n: Node, partials: List) -> Outcome { Violates { diagnostic: d } => Rejected { diagnostics: diagnostics_singleton(d: d) } Holds { value: descent_proof } => - inferred_facts_not_derived( - node: n, - descent: Holds { value: descent_proof } - ) + match dag_canonical_literal_from_node(node: n) { + Accepted { value: DagCanonicalBoolLiteral { value: _ }, diagnostics: _ } => + inferred_facts_from_derived_type( + node: n, + derived_type: bool_node(), + descent: Holds { value: descent_proof } + ) + Accepted { value: DagCanonicalIntLiteral { magnitude: _ }, diagnostics: _ } => + inferred_facts_from_derived_type( + node: n, + derived_type: infer_branch_int_binding_type_node(), + descent: Holds { value: descent_proof } + ) + Rejected { diagnostics: _ } => + inferred_facts_not_derived( + node: n, + descent: Holds { value: descent_proof } + ) + } } } } @@ -520,18 +539,6 @@ fn infer_branch_int_binding_type_node() -> Node { } } -fn infer_branch_is_bool_literal_atom(node: Node) -> Bool { - match node.kind { - TypeNode { connective: Atom { identity: id } } => - (id == ^dag_token_kw_true) || (id == ^dag_token_kw_false) - _ => false - } -} - -fn infer_branch_is_int_literal_atom(node: Node) -> Bool { - dag_node_is_int_literal_atom(node: node) -} - fn infer_atom_binding_sym(node: Node) -> Optional { match node.kind { TypeNode { connective: Atom { identity: sym } } => Present { value: sym } @@ -598,19 +605,20 @@ fn infer_branch_operand_resolved_type_in_tree( facts: InferredFacts, tree: Node, ) -> Witness { - if infer_branch_is_bool_literal_atom(node: node) { - Holds { value: bool_node() } - } else if infer_branch_is_int_literal_atom(node: node) { - Holds { value: infer_branch_int_binding_type_node() } - } else { - match infer_atom_binding_sym(node: node) { - Present { value: binding } => - match infer_find_arrow_domain_type_in_tree(root: tree, binding: binding) { - Present { value: domain_ty } => Holds { value: domain_ty } - Absent => inferred_facts_resolved_type(facts: facts) - } - Absent => inferred_facts_resolved_type(facts: facts) - } + match dag_canonical_literal_from_node(node: node) { + Accepted { value: DagCanonicalBoolLiteral { value: _ }, diagnostics: _ } => + Holds { value: bool_node() } + Accepted { value: DagCanonicalIntLiteral { magnitude: _ }, diagnostics: _ } => + Holds { value: infer_branch_int_binding_type_node() } + Rejected { diagnostics: _ } => + match infer_atom_binding_sym(node: node) { + Present { value: binding } => + match infer_find_arrow_domain_type_in_tree(root: tree, binding: binding) { + Present { value: domain_ty } => Holds { value: domain_ty } + Absent => inferred_facts_resolved_type(facts: facts) + } + Absent => inferred_facts_resolved_type(facts: facts) + } } } @@ -689,11 +697,17 @@ fn infer_match_bool_arm_body_type( body_facts: InferredFacts, scrutinee_type: Node ) -> Witness { - if infer_branch_type_is_int(type_node: scrutinee_type) - && infer_branch_is_bool_literal_atom(node: body_target) { - Holds { value: infer_branch_int_binding_type_node() } - } else { - infer_branch_operand_resolved_type(node: body_target, facts: body_facts) + match dag_canonical_literal_from_node(node: body_target) { + Accepted { value: DagCanonicalBoolLiteral { value: _ }, diagnostics: _ } => + if infer_branch_type_is_int(type_node: scrutinee_type) { + Holds { value: infer_branch_int_binding_type_node() } + } else { + infer_branch_operand_resolved_type(node: body_target, facts: body_facts) + } + Accepted { value: DagCanonicalIntLiteral { magnitude: _ }, diagnostics: _ } => + infer_branch_operand_resolved_type(node: body_target, facts: body_facts) + Rejected { diagnostics: _ } => + infer_branch_operand_resolved_type(node: body_target, facts: body_facts) } } @@ -879,27 +893,15 @@ type InferMatchArmRow { body_type: Node } -fn infer_branch_is_bool_true_literal_atom(node: Node) -> Bool { - match node.kind { - TypeNode { connective: Atom { identity: id } } => id == ^dag_token_kw_true - _ => false - } -} - -fn infer_branch_is_bool_false_literal_atom(node: Node) -> Bool { - match node.kind { - TypeNode { connective: Atom { identity: id } } => id == ^dag_token_kw_false - _ => false - } -} - fn infer_bool_literal_pattern_classify(pattern: Node) -> InferBoolLiteralPattern { - if infer_branch_is_bool_true_literal_atom(node: pattern) { - InferBoolLiteralPatternTrue - } else if infer_branch_is_bool_false_literal_atom(node: pattern) { - InferBoolLiteralPatternFalse - } else { - InferBoolLiteralPatternOther + match dag_canonical_literal_from_node(node: pattern) { + Accepted { value: DagCanonicalBoolLiteral { value: true }, diagnostics: _ } => + InferBoolLiteralPatternTrue + Accepted { value: DagCanonicalBoolLiteral { value: false }, diagnostics: _ } => + InferBoolLiteralPatternFalse + Accepted { value: DagCanonicalIntLiteral { magnitude: _ }, diagnostics: _ } => + InferBoolLiteralPatternOther + Rejected { diagnostics: _ } => InferBoolLiteralPatternOther } } @@ -1430,6 +1432,20 @@ fn infer_gather_failed_non_empty(acc: InferGatherFoldAcc) -> NonEmptyDiagnostics } } +data infer_literal_subtree_diagnostic_discharge_note: String = "An integer literal's modeled type derives from its canonical representation, including its sole named magnitude subtree. Its derivation discharges only that edge's local type frontier; propagating it past the literal would report GroundingNotDerived after Int was derived. Boolean literals are canonical only with no children, so no boolean payload can be discharged. Malformed literals remain GroundingNotDerived and retain every child diagnostic." + +fn infer_literal_edge_diagnostics_derived(parent: Node, edge: Edge) -> Bool { + match dag_canonical_literal_from_node(node: parent) { + Accepted { value: DagCanonicalIntLiteral { magnitude: _ }, diagnostics: _ } => + match edge.label { + Named { name: field } => field == ^integer_literal_magnitude_field + Positional => false + } + Accepted { value: DagCanonicalBoolLiteral { value: _ }, diagnostics: _ } => false + Rejected { diagnostics: _ } => false + } +} + fn infer_gather_fold_step( acc: InferGatherFoldAcc, edge: Edge, @@ -1453,7 +1469,11 @@ fn infer_gather_fold_step( ) } else { let merged_entries = concat_inferred_facts_entries(a: acc.entries, b: child.entries) - let merged_pending = diagnostics_merge(outer: acc.pending, inner: child.pending) + let merged_pending = if infer_literal_edge_diagnostics_derived(parent: acc.node, edge: edge) { + acc.pending + } else { + diagnostics_merge(outer: acc.pending, inner: child.pending) + } let children_remaining = acc.children_remaining - 1 if acc.await_branch_row && children_remaining == 0 { infer_gather_branch_row_on_entries( diff --git a/src/v2/compiler/06_translate.dag b/src/v2/compiler/06_translate.dag index 5db05ee4fdf..1a3f8e54b5e 100644 --- a/src/v2/compiler/06_translate.dag +++ b/src/v2/compiler/06_translate.dag @@ -38,6 +38,7 @@ import v2.std.coercion { CoercionCandidateSet, CoercionResult, coercion_candidate_set_from_declared_inhabitants, + coercion_identity_from_target_model, coercion_fold_with_declared_priority } import v2.std.diagnostic { @@ -1245,28 +1246,31 @@ fn coerce_grounded_node( tree: InferredTree, target: TargetModel ) -> Outcome { - bind_outcome( - o: canonical_grounding_for_node(tree: tree, node: source), - f: fn(grounding) { + match coercion_identity_from_target_model(source: source, target: target) { + Accepted { value: identity, diagnostics: d } => + Accepted { value: identity, diagnostics: d } + Rejected { diagnostics: _ } => bind_outcome( o: target_selection_priority_from_model(target: target), f: fn(priority) { bind_outcome( - o: coercion_candidates_from_target_model( - target: target - ), + o: coercion_candidates_from_target_model(target: target), f: fn(candidates) { - coercion_fold_with_declared_priority( - source: grounding, - candidates: candidates, - priority: priority + bind_outcome( + o: canonical_grounding_for_node(tree: tree, node: source), + f: fn(grounding) { + coercion_fold_with_declared_priority( + source: grounding, + candidates: candidates, + priority: priority + ) + } ) } ) } ) - } - ) + } } fn translate_type_expression_shape_missing_diagnostic(node: Node) -> Diagnostic { Diagnostic { diff --git a/src/v2/extdeps/languages/dag.dag b/src/v2/extdeps/languages/dag.dag index 587e0339753..525ffbfcb5a 100644 --- a/src/v2/extdeps/languages/dag.dag +++ b/src/v2/extdeps/languages/dag.dag @@ -144,7 +144,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, list_append, list_snoc_item } +import v2.std.algebra { fold_list, for_all, is_empty, length, list_append, list_snoc_item } import v2.std.diagnostic { Accepted, Diagnostic, @@ -4190,6 +4190,40 @@ fn dag_int_literal_magnitude_int_from_node(node: Node) -> Outcome { } } +type DagCanonicalLiteral + = DagCanonicalBoolLiteral { value: Bool } + | DagCanonicalIntLiteral { magnitude: Int } + +fn dag_canonical_literal_shape_diagnostic(node: Node) -> Diagnostic { + Diagnostic { + reason: ^dag_literal_shape_invalid, + at: node_locus(node: node), + correction: None + } +} + +data dag_canonical_literal_query_note: String = "dag_canonical_literal_from_node is the single typed query for inference-facing DAG literal shape. It preserves the Bool-vs-Int distinction and the decoded integer magnitude in a coproduct instead of publishing sibling is_* predicates over Node storage. Bool literals admit exactly the childless true/false atoms. Int literals admit exactly one named, decodable magnitude child. Every other shape is a typed refusal." + +fn dag_canonical_literal_from_node(node: Node) -> Outcome { + if dag_node_is_kw_true_atom(node: node) && (length(xs: node.children) == 0) { + outcome_accepted(value: DagCanonicalBoolLiteral { value: true }) + } else if dag_node_is_kw_false_atom(node: node) && (length(xs: node.children) == 0) { + outcome_accepted(value: DagCanonicalBoolLiteral { value: false }) + } else if dag_node_is_int_literal_atom(node: node) && (length(xs: node.children) == 1) { + match dag_int_literal_magnitude_int_from_node(node: node) { + Accepted { value: magnitude, diagnostics: d } => + Accepted { + value: DagCanonicalIntLiteral { magnitude: magnitude }, + diagnostics: d + } + Rejected { diagnostics: _ } => + outcome_rejected(dag_canonical_literal_shape_diagnostic(node: node)) + } + } else { + outcome_rejected(dag_canonical_literal_shape_diagnostic(node: node)) + } +} + fn dag_type_atom_node(identity: Symbol) -> Node { Node { kind: TypeNode { connective: Atom { identity: identity } }, diff --git a/src/v2/std/coercion.dag b/src/v2/std/coercion.dag index fbe6928439c..b04ef10d258 100644 --- a/src/v2/std/coercion.dag +++ b/src/v2/std/coercion.dag @@ -6,6 +6,7 @@ import v2.std.collection { List } import v2.std.constraints { CanonicalGrounding, CanonicalGroundingWitness } import v2.std.diagnostic { Accepted, + bind_outcome, Diagnostic, ExternalContractUnknown, None, @@ -18,6 +19,11 @@ import v2.std.diagnostic { Unavailable, outcome_rejected } +import v2.std.compilers.target_model { + TargetModel, + target_model_declared_inhabitants, + target_model_selection_priority +} import v2.std.find_witness { CandidateSet, FindWitnessResult, @@ -125,6 +131,79 @@ fn coercion_candidate_set_from_declared_inhabitants( ) } } +data identity_coercion_grounding_ruling: String = "Identity admission and semantic coercion are distinct judgments. coercion_identity_from_target_model accepts the TargetModel itself — never a caller-supplied CoercionCandidateSet — and reads its unique target_model_edge_declared_inhabitants and target_model_edge_selection_policy children inside this authority. Exact selection from that authored roster grounds CoercionQuality.Identity without claiming a resolved type for source. A candidate roster derived from source cannot enter this API; generic candidate sets remain available only to the semantic coercion path whose source is a compiler-derived CanonicalGrounding. This is the API-level wall between target-model membership and self-certification." + +fn coercion_homomorphism_evidence_for_node( + source: Node, + fw: FindWitnessResult, +) -> Node { + Node { + kind: TypeNode { connective: Conj }, + children: [ + Edge { label: Named { name: ^coercion_homomorphism_evidence_source }, target: source }, + Edge { label: Named { name: ^coercion_homomorphism_evidence_candidate }, target: fw.candidate }, + Edge { + label: Named { name: ^coercion_homomorphism_evidence_find_witness }, + target: fw.witness.evidence + } + ], + occurrence_id: SyntheticOccurrence + } +} + +fn coercion_identity_from_target_model( + source: Node, + target: TargetModel, +) -> Outcome { + bind_outcome( + o: target_model_declared_inhabitants(target: target), + f: fn(inhabitants_root) { + bind_outcome( + o: target_model_selection_priority(target: target), + f: fn(priority) { + bind_outcome( + o: coercion_candidate_set_from_declared_inhabitants( + inhabitants_root: inhabitants_root, + closedness_property: ^target_model_edge_declared_inhabitants + ), + f: fn(candidates) { + match find_witness( + source_facts: source, + candidates: candidates.set, + predicate: PreservationPredicate { + algebra: source, + preservation_rule: ^preservation_rule_exact_structural_equality_zip_fold + }, + multiplicity_policy: TargetSelection { + policy: TargetDeclaredPriority { priority: priority } + } + ) { + Accepted { value: fw, diagnostics: fw_d } => + Accepted { + value: CoercionResult { + target: fw.candidate, + quality: Identity, + witness: CoercionWitness { + homomorphism: HomomorphismWitness { + rule: ^homomorphism_rule_exact_structural_equality, + evidence: coercion_homomorphism_evidence_for_node( + source: source, + fw: fw + ) + } + } + }, + diagnostics: fw_d + } + Rejected { diagnostics: r } => Rejected { diagnostics: r } + } + } + ) + } + ) + } + ) +} fn coercion_quality_for_witness( source: CanonicalGrounding, fw: FindWitnessResult, @@ -224,15 +303,7 @@ fn coercion_homomorphism_evidence( source: CanonicalGrounding, fw: FindWitnessResult, ) -> Node { - Node { - kind: TypeNode { connective: Conj }, - children: [ - Edge { label: Named { name: ^coercion_homomorphism_evidence_source }, target: source.node }, - Edge { label: Named { name: ^coercion_homomorphism_evidence_candidate }, target: fw.candidate }, - Edge { label: Named { name: ^coercion_homomorphism_evidence_find_witness }, target: fw.witness.evidence } - ], - occurrence_id: SyntheticOccurrence -} + coercion_homomorphism_evidence_for_node(source: source.node, fw: fw) } fn coercion_fold_exact_structural( source: CanonicalGrounding, diff --git a/src/v2/std/compilers/target_model.dag b/src/v2/std/compilers/target_model.dag index 4f205b9cd2c..dabad52b7cd 100644 --- a/src/v2/std/compilers/target_model.dag +++ b/src/v2/std/compilers/target_model.dag @@ -129,6 +129,20 @@ type TargetModel { runtime_row: TargetEmitHostRuntimeRow } +fn target_model_declared_inhabitants(target: TargetModel) -> Outcome { + projection_bundle_child( + bundle: target.bundle, + name: ^target_model_edge_declared_inhabitants + ) +} + +fn target_model_selection_priority(target: TargetModel) -> Outcome { + projection_bundle_child( + bundle: target.bundle, + name: ^target_model_edge_selection_policy + ) +} + data target_model_emit_transforms_empty: Map String> = empty_map() fn target_model_make_with_emit_transforms( @@ -11698,7 +11712,7 @@ fn target_transform_surface_op_atom(node: Node) -> Outcome { } fn target_value_expr_declared_inhabitants_from_target(target: TargetModel) -> Outcome { - projection_bundle_child(bundle: target.bundle, name: ^target_model_edge_declared_inhabitants) + target_model_declared_inhabitants(target: target) } fn target_value_expr_add_signature_matches_for_target( diff --git a/src/v2/test/claim/infer_self_grounding_wall_test.dag b/src/v2/test/claim/infer_self_grounding_wall_test.dag index b5daad7fea8..0e2c75f0d32 100644 --- a/src/v2/test/claim/infer_self_grounding_wall_test.dag +++ b/src/v2/test/claim/infer_self_grounding_wall_test.dag @@ -36,6 +36,34 @@ fn wall_value_node() -> Node { } } +fn wall_malformed_bool_with_payload() -> Node { + Node { + kind: TypeNode { connective: Atom { identity: ^dag_token_kw_true } }, + children: [ + Edge { + label: Named { name: ^wall_malformed_bool_payload_field }, + target: wall_value_node() + } + ], + occurrence_id: SyntheticOccurrence + } +} + +fn wall_malformed_int_with_extra_payload() -> Node { + let literal = dag_int_literal_fixture_one() + Node { + kind: literal.kind, + children: list_snoc_item( + xs: literal.children, + item: Edge { + label: Named { name: ^wall_malformed_int_payload_field }, + target: wall_value_node() + } + ), + occurrence_id: literal.occurrence_id + } +} + fn wall_derived_type_node() -> Node { wall_atom(s: ^wall_derived_type_marker) } @@ -156,3 +184,13 @@ test fn wall_frontier_node_has_no_resolved_type() -> Bool { test fn wall_frontier_is_counted_on_the_accepted_path() -> Bool { wall_infer_counts_frontier(root: wall_value_node()) } + +test fn wall_malformed_bool_payload_is_not_grounded_or_discharged() -> Bool { + wall_grounding_for_node_refused(root: wall_malformed_bool_with_payload()) + && wall_infer_counts_frontier(root: wall_malformed_bool_with_payload()) +} + +test fn wall_malformed_int_payload_is_not_grounded_or_discharged() -> Bool { + wall_grounding_for_node_refused(root: wall_malformed_int_with_extra_payload()) + && wall_infer_counts_frontier(root: wall_malformed_int_with_extra_payload()) +} diff --git a/src/v2/test/claim/manual/branch_infer_if_then_else_test.dag b/src/v2/test/claim/manual/branch_infer_if_then_else_test.dag index b24733f4ab2..0e2f4cbc898 100644 --- a/src/v2/test/claim/manual/branch_infer_if_then_else_test.dag +++ b/src/v2/test/claim/manual/branch_infer_if_then_else_test.dag @@ -41,10 +41,11 @@ test fn branch_infer_bool_cond_accepts_holds() -> Bool { } } -test fn branch_infer_arm_mismatch_red_holds() -> Bool { +data branch_infer_underived_arm_ruling_note: String = "The else arm is the bare dag_token_ident token-class atom, not a literal with a modeled type. After the self-grounding wall it cannot participate in an arm-type comparison: inference must refuse infer_grounding_not_derived before claiming a mismatch. The discriminating mismatch RED remains covered by branch_infer_test using two genuinely derived operand types." + +test fn branch_infer_underived_arm_refuses_before_mismatch_holds() -> Bool { match infer(tree: branch_infer_arm_mismatch_body()) { Accepted { value: _, diagnostics: _ } => false - Rejected { diagnostics: d } => d.head.reason == ^infer_branch_arm_type_mismatch + Rejected { diagnostics: d } => d.head.reason == ^infer_grounding_not_derived } } - diff --git a/src/v2/test/claim/manual/ingest_bridge_test.dag b/src/v2/test/claim/manual/ingest_bridge_test.dag index c74846beefb..f4fc6a213f2 100644 --- a/src/v2/test/claim/manual/ingest_bridge_test.dag +++ b/src/v2/test/claim/manual/ingest_bridge_test.dag @@ -8,6 +8,8 @@ import v2.compiler.ingest { parse_tree_to_emitted_node, 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.parse { grammar_validate_for_parse, parse_production, @@ -37,6 +39,7 @@ import v2.std.compilers.lexing { Token } import v2.std.compilers.target_model { TargetModel, target_model_emit_transforms_empty } +import v2.std.coercion { Identity } import v2.std.diagnostic { Accepted, None, Outcome, Rejected, port_locus } import v2.std.logic { Bool } import v2.std.node { @@ -202,6 +205,50 @@ test fn ingest_bridge_to_canonical_holds() -> Bool { } } +test fn ingest_identity_coercion_needs_roster_membership_not_derived_grounding() -> 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: coerced, diagnostics: _ } => + (coerced.quality == Identity) && (coerced.target == source) + } + } + } +} + +test fn ingest_identity_coercion_refuses_source_absent_from_authored_roster() -> Bool { + let source = ingest_unstamped_node() + match infer(tree: source) { + Rejected { diagnostics: _ } => false + Accepted { value: inferred, diagnostics: _ } => + match coerce_grounded_node( + source: source, + tree: inferred, + target: ingest_fixture_model( + emitted: dag_fixture_emitted_add_fn, + spine_class: ^ingest_fixture_tok + ) + ) { + Accepted { value: _, diagnostics: _ } => false + Rejected { diagnostics: d } => d.head.reason == ^infer_grounding_not_derived + } + } +} + test fn ingest_fails_closed_off_frontier_holds() -> Bool { match ingest_parse() { Accepted { value: tree, diagnostics: _ } => @@ -273,4 +320,3 @@ test fn ingest_bridge_realized() -> Bool { && ingest_cross_language_compile_holds() } -