Repository navigation
v2: refinement-obligation carrier + discharge after infer; root-first cut of every infer caller - #12375
Merged
Conversation
…y infer caller over root-first infer returns ObligatedInferredTree; v2.compiler.refinement_discharge is the one route to an InferredTree, evaluating each obligation through the existing evaluator. No producer yet. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e claim that is red on main Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…); fields are read directly Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Sep 27, 2026
…old keeps main's carried let annotation and reads the let value through body_lower_value_read (#12210 deleted body_lower_bound_value); RFM row keeps both CLIMB receipts, drops the stale body_lower_bound_value citation; expected-red keeps main's new emitted_add chunk, #12210's lambda-argument retirement and this PR's fold-seam row
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Sep 27, 2026
… MQ-1: a let value reads through body_lower_value_read beside main's carried annotation; the retired chunks stay retired and main's emitted_add chunk is kept; ledger receipts kept
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Sep 27, 2026
resolution): keep this PR's fold-seam row; take #12210's comment wording
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Sep 27, 2026
…_discharge to holds/violated; one runtime encoding of true - infer_arrow_elimination eval control: after the merge of main (#12375) infer returns ObligatedInferredTree, so the control reaches eval through discharge_refinement_obligations, the only route to an InferredTree. - refinement_discharge's frontier row flips as it said it would: a true predicate admits, a false one refuses refinement_predicate_violated. The undischargeable arm is kept over a genuinely unevaluable application (an undenoted return). - The flip exposed two runtime encodings of true: v2_eval_bool_true_primitive was a one-bit byte while every evaluated Bool is built by v2_eval_bool_runtime_value (eight bits), so discharge read an evaluated true as violated. The primitive is now that constructor's value. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
This PR adds a refinement-obligation carrier and a discharge step between infer and everything downstream. It then cuts every
infercaller over to the new return type, root-first. This is the carrier-plus-discharge PR ruled by neat-boar-16 for node adhoc-032c89dc-138. It is the first of three:"abc" as NonEmptyStr). That flipsbcn_cast_into_a_refinement_refuses_at_inferand clears the 44NonEmptyStrcensus modules.v2.compiler.inferred_treeRefinementObligation { site, application, target }.applicationis the closedpredicate(operand)node, which infer itself infers, because the evaluator needs an inferred fact for every node it evaluates and discharge may not mint one.ObligatedInferredTree { root, facts, obligations }. It has noInferredTreefield.InferredTreegains no obligations field.v2.compiler.infer:infernow returnsOutcome<ObligatedInferredTree>.v2.compiler.refinement_discharge:discharge_refinement_obligationsevaluates each obligation through the existing evaluator (v2.compiler.eval, viaeval); there is no second evaluator. It maps the result to one of three outcomes and never widens:truevalue admits the crossing;refinement_predicate_violated, located at the cast site;refinement_obligation_undischargeable, located at the site, with the evaluator's diagnostics carried behind it.infer_and_dischargeis the pipeline composition that production callers take.00_compile,03_ingestand the discharge module.No producer yet (a declared frontier, not dangling code)
No producer records an obligation yet, so every tree currently discharges with nothing to evaluate. The carrier is still consumed now: infer's signature produces it, and
compile,03_ingest, the self-host emitters and the workflow stages discharge it. Its named next consumer is the literal-cast producer (PR 3 above).Rung, stated honestly
The property is that an obligation infer recorded cannot reach emission undischarged. The wall holds only where a consumer's parameter is typed
InferredTree:gunbc compilerefusesfn f(t: ObligatedInferredTree) -> InferredTree { t }with a blockingTypeMismatch. That is the compile-clean gate's path, and it is how the root-first cut surfaced its refusals: 4 in00_compile, 15 test files and the production sites.compile_dag_diagnostic_census, which every guarantee-probe fixture runs through, reports 0TypeMismatchfor this bypass and for its green control alike. A fixture there cannot go red, so none is added. The path disagreement has been reported to neat-boar-16.inhabitance_undecidable_formal_unresolved.Outcome/Optional: silent hole, even on the compile path.Outcome<ObligatedInferredTree>passes asOutcome<InferredTree>with 0 blocking errors. This is the rostered generic-instantiation hole (gunbc.guarantee_probe_corpusfloor_generic_instantiation_hole_probe)..root: not covered. A consumer taking a plainNodecan read.rootfrom the obligated form and emit a subtree. The type wall cannot see this. Two test sites did exactly that and now discharge (see dispositions below).InferredTreefixtures stay legitimate supplied inputs under the DESIGN §3 witness rule. They record no obligation.So the wall is mitigated by the site-by-site dispositions below, not guaranteed by construction.
Dispositions: all 42
infercall sites, read one by oneThe compile refusals were not a complete census. At least 11 sites handed infer's
Outcomeon through anOutcome<InferredTree>signature and compiled silently, and 2 more read.rootand emitted from it. So every site was read.Discharge (hands the tree on to emit, translate, eval or a gate, so it takes
infer_and_discharge):v2.compiler.compile(×3)v2.compiler.ingest(×2: the source-model bridge andcross_language_compile)self_host.candidate_generation(×4),self_host.compiler_closure_emit,self_host.closure_emission,self_host.direct_rust_door_fixtureworkflow.realization_attempt,workflow.dag_acceptanceinfer_self_grounding_wall(2; its other 3 sites are verdict-only and stay oninfer),translate_underived_refusal(3),stage0_production_targetinhabitant_neutralization(4),inhabitant_neutralization_e2e_witness(2)ingest_bridge(2),cross_language_add_python_to_typescript(2)infer_atom_grounding_rules(2),infer_product_introduction(2)emit_host_classical_not_ingested_equals_eval(3; its other 2 sites are verdict-only and stay oninfer),accumulator_copy_compile_gate,self_host_module_emit_deriskinfer_transform_binary_infix_witnessand its helpers,pipeline.stage_bridge(3)Outcome<InferredTree>:loop_infer_iteration,branch_infer,infer_ground_add,infer_emit_compile_anchor,infer_bounded_lattice_completeness_anchor.root, then emit:rust_module_emission_population,int_literal_form_unwired_locatedPre-discharge read (reads infer's own verdict or facts, never hands the tree on):
self_host.candidate_generation_stage_verdicts. It records infer's own stage verdict, and its emission path goes throughcandidate_generation, which discharges.body_cast_node,claim_pipeline.infer_test,parser_completeness_frontierbranch_infer_if_then_else,branch_infer_fail_open_audit,match_infer_fail_open_auditdata_decl_lowering_grounding,infer_application_argument_inhabitance_witnessinfer_list_introduction,type_param_binder_frameSource of truth:
v2.compiler.inferitself.Three test files carry no imports and resolve from the ambient pool:
loop_infer_iteration,branch_inferandinfer_emit_compile_anchor. They resolveinfer_and_dischargethe same way they already resolvedinfer, so adding an import was not needed.Floor fixes after the first run
infer. My first cut converted whole files mechanically. A site whoseAcceptedarm binds_reads only infer's verdict and must not discharge; that is the pre-discharge-read disposition. Affected:infer_self_grounding_wall(3 sites) andemit_host_classical_not_ingested_equals_eval(2 sites, includingingested_classical_not_real_infer_holds, which the first floor run failed on cost).v2.test.manual.ingest_bridgeingest_identity_coercion_accepts_source_present_in_authored_rosterenrolled expected-red.falseat merge base 21f4d0c and atorigin/mainb1b7aea, same seed,infer_algebra_ref_ungroundedfromcanonical_grounding_for_node. It is also red with v2 infer: arrow elimination + body/declared-return check; one Int, one Bool value type #12379 applied.v2.workflow.floor_expected_redfloor_expected_red_chunk_emitted_add_algebra_ref_ungrounded.Evidence
v2.test.claim.refinement_discharge:rdt_an_unevaluable_application_refuses_undischargeable_whatever_its_body. The application is real and inferred by infer.GroundingNotDerived, so the evaluator refuses it. Discharge reportsrefinement_obligation_undischargeableat the site for both atruebody and afalsebody.rdt_zero_obligations_discharge_to_the_same_tree. The identity, and today's only cost path.Frontier. Row (1) flips when infer has an arrow-elimination rule: an application derives its callee's declared return type, with the body checked against it (node adhoc-3fbf72e5-2b4, dispatched). Only then does a where-predicate application ground so that
v2.compiler.evalcan evaluate it. (Corrected: an earlier statement that infer already derives Int applications was wrong. It derives no application, Int included. Only Int add and casts derive.) At that point atruebody admits and afalsebody refusesrefinement_predicate_violated. That is the holds/violated pair the ruling asked for.The effectful-predicate case is covered by the same arm. With no effect handler bound, the evaluator refuses any effect operation. There is no separate effect analysis here.
Floor cost
Floor cost, before and after. Measured by required-floor run 36279207012 at
eefd917(job 108507699514):rdt_zero_obligations_discharge_to_the_same_treeobserved 60 ms CPU against its 302 ms budget, forinferplus discharge of a one-application tree.rdt_an_unevaluable_application_refuses_undischargeable_whatever_its_bodyobserved 91 ms.infer_and_dischargestep count for one identity on main and here. The floor logs per-claim steps only for shared-fill claims, and no main run selected the converted claims.Emptymatch arm. It performs no evaluation.🤖 Generated with Claude Code