Skip to content

MQ-1: a positional fold step lowers like the named form (position from std.algebra's fold signature) - #12272

Merged
gunbai-bot[bot] merged 160 commits into
mainfrom
session/merry-hawk-532
Sep 28, 2026
Merged

gunbai-bot[bot] merged 160 commits into
mainfrom
session/merry-hawk-532

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

MQ-1: a positional fold step lowers like the named form

fold(xs, 0, fn(acc, y) { .. }) never lowered. v2.compiler.fold_lowering fold_call_step_form found the step only by NAME (^f), so the positional spelling became StepByName. fold stayed an ordinary call, and resolve refused it with resolve_reason_unbound_symbol. Real sites that use this spelling: dag/extdeps/container/oci/linux.dag contains_namespace_type and four calls in dag/gunbc/instruments/rust_stage0_gates.dag.

The step's position comes from the primitive's declared signature

  • std.algebra collection_fold_shape. The two identical fold rows (free_monoid_collection and pointwise_power_collection) are now one template, like collection_filter_shape. It gains the ReceiverSelf slot that every sibling row (map, filter, any, ...) already carries, so param_types is the full free-call signature, receiver first.
  • template_step_position reads the first callable parameter's index off a template. fold_lowering fold_step_argument_position takes the position from that function. No literal index is written anywhere.
  • v2.extdeps.languages.dag dag_call_positional_arg_value_optional reads the direct argument at a position. It uses the same stop-at-every-arg rule as the named lookup, and a named argument at that position answers Absent.
  • fold_call_step_form tries the name first, then the declared position. Both spellings reach the same step node and lower through the one seam. No second path was added.

Parameter names. The row carries no parameter NAMES, so the names (^f, ^cons, ^snoc, ^algebra) stay authored per head in fold_step_argument_name. That is stated at the site.

Census of fold-family members

head name position
fold ^f from collection_fold_shape
fold_list, fold_list_right, fold_node ^cons / ^snoc / ^algebra frontier: name-only

Their signatures are ordinary v2 fn declarations with no data projection this stage can read, and no positional call site of any of them exists (§3c). A positional one stays the counted FoldCallStepFormUnresolved state until those signatures become readable here.

v1 consumers of the changed template, with behaviour unchanged

Adding ReceiverSelf to the fold row is admitted under v1_seed_standing because it serves v2 and corrects the row. The v1 readers of param_types are:

  • v1.compiler.types build_type_substitution strips a leading ReceiverSelf.
  • v1.compiler.lookup declared_arg_types_for_method strips a leading ReceiverSelf.
  • v1.compiler.types instantiate_algebra_field builds the same Self-first field shape map already has.
  • v1.compiler.method algebra_param_templates: fold has no derived_signature registry row, so nothing pairs names against it.
  • callback_element_position indexes inside the callable, so it is unchanged.

The stage0 mirror std_algebra.rs was regenerated with --required-regen and the candidate installed. On the rebuilt binary:

  • dag/test/claim/primitive_signature_grounding_witness_test.dag: 11/11 pass.
  • method_arg_declared_contract_witness_test: red. It is already enrolled expected-red (floor_expected_red, quarantined in gunbc.explicit_witness_admission), and its subject is get, not fold.
  • cargo test --release -p v1-compiler --lib (local only, since rust_unit_tests_off_the_merge_path takes it off CI): 10 failures on this head, all 10 also failing on the base commit (MQ-1: function values in value position (lambdas and fn literals) lower to the generic fn Arrow; unwritten types are fresh type parameters #12210's head, same host). The baseline run reported 950 passed / 30 failed. That count is inflated by the base worktree being removed mid-run, which can only add failures, never hide one. No test newly fails. None of the 10 reads the algebra fold template.
  • The regen fixed point was not re-run after the install; merge_group's build lane runs it.

Controls

  • v2.test.claim.fold_lowering positional_fold_step_lowers_to_the_named_forms_loop: the positional form's lowered Loop has the same content_hash as the named form's.
  • Mutation: reverting fold_call_step_form to name-only (Absent => StepByName) turns that claim red (FAIL), while well_formed_fold_still_lowers_through_the_split stays green.
  • positional_named_step_fold_is_step_form_unresolved: fold(xs, 0, step) stays unresolved. The positional reading does not widen into accepting whatever sits in the slot.
  • All 18 claims in the module pass locally.

The expected-red row does not retire (split ruled by gentle-koi-724)

wave1_gate1_b1_builtin_call_resolves_end_to_end_witness_holds no longer hits the unbound-fold refusal. Measured with gunbc run over a probe of conserved_normalize_of_text → resolve on the fixture:

positional fold(x, 0, fn(acc, _) { acc })          -> normalize: conservation_reason_dropped_reference
named      fold(x, init: 0, f: fn(acc, _) { acc }) -> normalize: conservation_reason_dropped_reference
bare-call control                                   -> ok

The two forms now fail identically, at the next boundary: fold_call_seam_loop builds Loop(body, bound, carrier) and carries neither the collection nor init. The row (renamed floor_expected_red_chunk_fold_seam_loop_drops_operands) restates that cause.

  • Owner: MQ-5 (gentle-koi-724 lane).
  • Trigger: the fold seam Loop carries collection and init, conserved.

Consumers of the Loop contract that the fix must migrate:

  • v2.std.node loop_behavior_edges_conform / loop_body_edges_conform close the Loop's named edges to exactly ^loop_bound_edge and ^loop_carrier_edge. New operand edges must be admitted there.
  • v2.std.node loop_carrier_binder_target
  • v2.std.cardinality loop_multiplicity, which reads the bound
  • v2.lens.complexity_accumulator_copy.analyze, which reads the carrier off fold_call_to_loop and gates on fold_family_head
  • v2.compiler.body_lowering_fold body_lower_try_fold_loop

Frontier, not this slice: option (b)

Resolve binding primitive callees from std.primitive_identity. After this PR, fold in any spelling with an inline step no longer reaches resolve as a callee. Primitive callees that still reach resolve outside fold_lowering:

  • fold with a step passed by name (fold(xs, 0, step))
  • every other roster primitive called as a free function (map, filter, flat_map, any, all, count, length, contains, ...), since fold_lowering handles only the fold family

Based on session/fierce-gull-556 (#12210), which owns the row and the shared step-literal decoder.

🤖 Generated with Claude Code

Brian Searls and others added 30 commits September 23, 2026 20:10
…d arguments no longer refuse the whole module

gunbc#12145 stopped the operand reader narrowing a sequence to its left element,
which had read h(q: a) as h and [a] as [. body_lower_call_arg_value read every
argument with that reader alone, so a nested call, record, caret symbol or
parenthesised group became call_argument_unread and its whole module refused
normalize. An argument's value is now lowered by body_lower_value_lowered (the
field-initializer / if-condition reader), with the operand reader as fallback.
A parenthesised group lowers to its inner expression instead of its first atom.
A list literal anywhere in an argument value still refuses, at the list
(body_lowering_reason_list_literal_unlowered): body lowering has no lowered form
for a list literal yet, so reading it would trade a refusal for a silent drop.
Witness: v2.test.claim.namespace_xl0.call_argument_value_resolve_refusal.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e no longer refuses the whole match

body_lower_match_scrutinee_optional read the scrutinee with the operand reader
alone: before gunbc#12145 match t(p: x) {..} narrowed to t, after it the match
refused as match_arm_navigation_refused (64 of the 133 still-refusing sample
modules). The scrutinee now goes through body_lower_value_lowered, operand reader
as fallback, refusal propagated. Witness claim: an undeclared name inside a call
scrutinee refuses at resolve at its atom.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ments read through the value reader

Stacked on #12173 (call arguments and match scrutinees via body_lower_value_lowered).
body_lower_operator_operand reads a non-operator operand whole via body_lower_value_lowered
before refusing operator_operand_unread; a function value in argument position is carried as
its preserved shell under value_carried_unlowered (the field-initializer disposition) instead of
refusing its module as call_argument_unread (weather.dag, gen-one's first fatal). Claim
v2.test.claim.namespace_xl0.value_position_whole_read with production-route shape assertions;
RFM row fold_rewrite_regression_visible_only_at_whole_route_identity_diff.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… outcome, not dropped (review 70746)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rom the #12198 srv1 diff)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e_query and node_subtree_nodes, no untyped edge field access (entry resolve refused '.target' on T)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…RFM receipts folded into lowering_accessor_collapses_a_sequence_operand as an exposure/coverage gap, not a new regression row (side-chat review)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ough the value reader

[] -> v2.std.algebra.freemonoid_empty(); [e] -> freemonoid_singleton(item: e);
[e1..en] -> right-nested list_append(left: singleton(e1), right: ..). One producer
(body_lower_list_literal) reached from body_lower_primary_expr, so every value
position gets it; each element lowers whole via body_lower_value_lowered and an
element that cannot lower refuses located at it. The call-argument
list_literal_unlowered refusal is deleted. Declares freemonoid_singleton (the
free monoid's generator embedding) beside freemonoid_empty.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… shape of the operator_operand_unread files; the bare call lowered before the fallback (srv1: M1/M2 stayed green)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…eclared body name refused at its atom; operator-operand fallback and its non-discriminating claims dropped (owned by #12194)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…tionally so the rostered named-label drop does not red it

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…; lambda claims enrolled expected-red as the MQ frontier; shape claims read an ingest without the lambda fixtures; RFM receipt updated

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
 landed) into MQ-1

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s to; its binder is scoped at resolve

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…'s landed versions

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…branch hunk the squash did not land is not this PR's)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…-resolve under #12194's match-scrutinee controls; list_literal_has_no_lowered_form records the climb and cites the renamed and new claims

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…clared; move freemonoid_empty, freemonoid_singleton, list_append, list_snoc_item there (delete-first, no re-export) and repoint every consumer

v2.std.algebra imports v2.std.node, so v2.std.node's own list literals lowered to
v2.std.algebra calls would close a node -> algebra -> node cycle; v2 collapses onto
dag/std. Adds the self-reference claim: a module std.algebra's own list literals
resolve to its own qualified names, and its twin without list_append refuses at resolve.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ue Arrow producer for fn literals to reuse

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…value Arrow producer; body through the named-fn body lowering

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…bridge std_algebra.rs (FreeMonoid + list_snoc_item) replaces the v2_std_algebra.rs bridge and normalize's FreeMonoid-only stub

The emitted 03_normalize, 03_body_producer and use_site_verdict now import
list_snoc_item from std.algebra; their transport rows, shim libs and normalize's
declared source refs point at the one bridge (review 70892).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…h arm) must normalize and resolve

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…searches (a let-bodied fn literal under a non-fold callee no longer answers the enclosing body)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ue producer and fold_lowering unfolded (single-atom bodies read; record-construct bodies well-formed)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ebra FreeMonoid reference, elements positional in order -- with one infer introduction case

Reverses the right-nested list_append lowering: std.literal_elaboration
UnicodeScalarSequenceUnfold names the flat list-literal introduction as the
language's FreeMonoid introduction and rejects cons^n on cost and emitter fuel, so
the nested form forked that authority (ruling: gentle-koi-724 / neat-boar-16).
04_infer types the introduction: every element unifies to one T (compared with
provenance stripped), a differing element refuses located at it, [] stays on the
GroundingNotDerived frontier. freemonoid_singleton is deleted (no consumer left).
Claims: flat shape reader, 200-element depth control, infer introduction controls;
the std.algebra self-reference twin now omits FreeMonoid itself.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Sep 27, 2026

Copy link
Copy Markdown
Contributor Author

Re review 71734 (src/v2/std/anonymous_binder.dag is_fresh_type_variable has no caller): confirmed. git grep finds only its definition. It is not part of this PR's change: it arrives with #12210 (commit 0d3c75e, the structural-path fresh type variables), which this branch is stacked on, and this branch's copy of that file is identical to #12210's. The fix belongs on #12210, so I've sent the finding to its author (fierce-gull-556) and will pick up the deletion or the wired-in reader on the next merge from #12210. This PR's own seven files are dag/std/algebra.dag, src/v1/stage0/src/std_algebra.rs, src/v2/compiler/fold_lowering.dag, src/v2/extdeps/languages/dag.dag, src/v2/test/claim/fold_lowering_test.dag, src/v2/workflow/floor_expected_red.dag and src/v2/workflow/floor_pure_producer_share.dag.

— sent from merry-hawk-532

@gunbai-bot

gunbai-bot Bot commented Sep 27, 2026

Copy link
Copy Markdown
Contributor Author

Re review 71746 (fresh_type_variable packs its path and label into a Symbol spelling, and is_unpositioned_fresh_type_variable parses them back by prefix): this code is not part of this PR's change. src/v2/std/anonymous_binder.dag and the body_lowering_fold.dag call sites arrive with #12210 (its structural-path fresh type variables), and this branch's copies are identical to #12210's head. The fix belongs on #12210. I've sent the finding to its author (fierce-gull-556) with your suggested fix: a typed carrier, or naming this encoding in type_variable_identified_by_spelling_in_v2 with a capability trigger. I'll pick it up on the next merge from #12210.

— sent from merry-hawk-532

Brian Searls and others added 3 commits September 27, 2026 09:46
… names built from its own domain labels), not by parsing a spelling; the prefix predicate is deleted; the RFM row names the encoding as its population (review 71746 on #12272)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e fold, positioning edges/path and rename folds were O(n^2) (review 71751, DESIGN §6 bare minimum cost)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Sep 27, 2026
@gunbai-bot
gunbai-bot Bot removed this pull request from the merge queue due to a manual request Sep 27, 2026
Brian Searls and others added 10 commits September 27, 2026 17:37
# Conflicts:
#	src/v2/workflow/floor_pure_producer_share.dag
…its seam; value_read_refusal re-pointed to a function-valued body

The base-vs-head consumer run found body_lower_fold_family_call_first returning
fold_lowering's seam Loop without lowering the call's other arguments, so
fold(map(s, <unlowerable>), ..) Accepted with the refusal lost
(value_read_refusal a_fold_refusal_inside_a_let_value_propagates..). The call is
now lowered once through the ordinary dispatch and its refusal propagates before
the seam stands. The seam dropping collection/init stays MQ-5's (#12272).

The other value_read_refusal specimens used a plain fn literal, which #12210
lowers; they are re-pointed to fn(y) { fn(z) { y } } / l => m => leaf(l), still
refused (arrow_body_is_function_value_unmodeled) until #12283.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
# Conflicts:
#	src/v2/compiler/body_lowering_fold.dag
…-site content-hash claim still sees exactly its two vpw_site sites

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
# Conflicts:
#	src/v2/compiler/fold_lowering.dag
#	src/v2/workflow/floor_expected_red.dag
@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Sep 28, 2026

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE e2f97bb

Independent exact-head review, not a proxy acceptance of dashboard review 72020. No blocking finding in MQ-1. collection_fold_shape replaces both duplicate collection templates; its ReceiverSelf-first param_types supplies the step position through template_step_position rather than a literal offset. I checked declared_arg_types_for_method and build_type_substitution at this head: both remove a leading ReceiverSelf before pairing ordinary method arguments, so adding that slot does not shift those consumers' init/step contract. The Rust delta is the generated form of the .dag template/reader change, not an undeclared handwritten rule.

The positional reader counts each direct arg once and stops at that arg rather than searching inside its value; a named actual in that source position is not reclassified as positional. Named and positional lookup converge on the same fold_step_form_at and existing function-value reader. A positional step that is a reference remains FoldCallStepFormUnresolved. The named-only frontier for fold_list/fold_list_right/fold_node is explicit rather than inferred to work from fold's row.

The current diff does not restore chunk_15 or the retired #12210 rows. Its one expected-red rename describes the still-observed reference-conservation failure, with MQ-5 owning the capability to carry collection and init in the Loop. Reaching fold lowering is not an end-to-end passing fold; this approval does not call it one.

I credit the reported 18 passing fold claims, named-only mutation failing positional parity while the named positive stays green, and the 11 primitive-signature claims. I do not credit a complete Rust-suite base/head equivalence measurement: the body says the base worktree was removed during that run, so the absence of newly listed failures is weaker evidence than a controlled completed comparison. The source consumer trace and the targeted controls are the evidence used here. Exact-head workflow 36368968596 succeeded; fixed-point enforcement remains on the merge-group route. No local rerun performed by this reviewer.

GitHub currently reports this branch non-mergeable. Conflict repair or a main merge changes the head and requires a delta review before enqueueing.

Copy link
Copy Markdown
Contributor

Correction to review 5338362885's mergeability note: I found that the normalized PR summary can turn GitHub's unknown mergeability result into false. I have not established a conflict on this branch and withdraw the statement that conflict repair is required. Do not re-merge solely because of my note; confirm GitHub's completed mergeability result. The independent APPROVE at e2f97bb and its stated evidence/operand-conservation limits remain unchanged.

Merged via the queue into main with commit f6b8c5e Sep 28, 2026
6 checks passed
@gunbai-bot
gunbai-bot Bot deleted the session/merry-hawk-532 branch September 28, 2026 18:22
gunbai-bot Bot pushed a commit that referenced this pull request Sep 28, 2026
…already here, identical)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant