Skip to content

codegen 1.11.1: merge the release train into main - #371

Merged
luisleo526 merged 27 commits into
mainfrom
train/codegen-1.11.1
Nov 10, 2026
Merged

luisleo526 merged 27 commits into
mainfrom
train/codegen-1.11.1

Conversation

@luisleo526

Copy link
Copy Markdown
Contributor

Merges the codegen 1.11.1 train branch train/codegen-1.11.1 (head 8e88b43, tree 94129fb5) into main.

The branch is main (a156a7d) plus eight reviewed member pull requests, each merged into the train branch:

Nothing else is on the branch: every line it changes over main comes from one of these members.

Merge method: one squash commit.

Before merge, this pull request carries:

  • the full test suite on this exact head, read by test id;
  • the parity sweep (codegen paired with engine main);
  • the codegen gates.

🤖 Generated with Claude Code

luisleo526 and others added 25 commits November 9, 2026 15:46
Tests only. A function-local string that is read with history and joined with
its own current value by + (issue #354) must keep its string type: the current
slot is a std::string, so the emitted C++ must not wrap it in std::to_string.
tests/test_string_local_history_concat.py compiles, runs and compares the
result of the neutral source from the issue, the same shape under the built-in
spellings second and hour, a string-annotated local, a reassigned local, two
call sites with different arguments, a chart-level pair and swapped operands.
It also keeps the translation-time refusal of the nine price-field names read
with history in a requested helper, the emission of an explicit string
conversion and of a numeric na operand, and the analyzer refusal of a string
plus an integer. The expected values are literals computed by hand from closes
10, 20, 30 and opens 1, 2, 3. tests/test_builtin_named_locals_compile.py now
requires the neutral control of each string-history row to compile; before, it
accepted a compile failure that matched the renamed row's failure.

Two older modules compare a complete emission with the emission of an older
codegen: tests/test_generated_member_names.py and
tests/test_helper_builtin_named_locals.py. For their string rows the older
emission holds the join that did not compile, so they now repair that one site
in the older text with a helper of the new module (it requires exactly one
such site) and compare everything else unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…a concatenation

A function-local string that is read with history (aa[1]) is a string series,
but the scalar local-type map has no entry for it, so the type query for the
bare name returned the double fallback. In a + whose other operand was a string
the bare name was then wrapped in std::to_string, which does not accept a
std::string, and the generated C++ did not compile (issue #354). The binary +
lowering now asks for the series element type when the query returns double for
a bare function-local that reads its own series slot and whose series holds
strings; every other operand is typed as before. The general type query and the
translation-time refusal of price-field names are unchanged. The changelog drops
the exception that named this shape and adds a bullet for the fix.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Tests only. A record that a script keeps in a top-level var and builds from a
stable var array, and a plain var-to-var array alias, hold the source as it
stands at the end of the bar in which they were bound: live during that bar,
a separate array after it, and every holder keeps its own array.
tests/test_array_retained_alias.py compiles, runs and compares nine scripts
with the sizes observed on TradingView as literals: a record built on the first
bar from an empty source that is pushed after the construction, the same with
two pushes, a record built on a later bar, the same with one more push after
the construction, a record built on every bar (a guard that passes without the
change), pushes made through the field after the first bar, a plain var alias,
two records built from one array, and one record with two array fields given
the same array. The driver reuses one strategy for two runs, which covers the
run reset.

It also keeps the neighbours that must not change (a record whose source is
not pushed again, a record built from an array literal), the usual array reads
on a plain alias (size, get, a loop, last), the compile error for a copy of
such a record made in the creation bar and in a later bar, the checkpoint round
trip of the binding in the generated trait and in a generated strategy, and
emission guards for the shapes the change leaves alone (array copy, array
history, a nested record, varip, intrabar recalculation, a rebound source, an
alias chain).

tests/test_e2e_udt_array_fields.py: the row for a record held directly by a
var now expects the new binding instead of the copy taken at construction; its
other two rows keep the vector storage.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A record that a script keeps in a top-level var and builds from a stable var
array took a copy of the array at construction, and so did a plain var-to-var
alias. TradingView shows the array as it stands at the end of the bar in which
the binding was made: live during that bar, a separate array after it, whatever
the source does later. Each holder has its own array: two records built from
one source, and two fields of one record given the same array, do not share it.

pineforge_codegen/codegen/retained_alias.py selects the bindings whose lifetime
is proved (a direct top-level var holder, stable primitive var arrays, a
constructor outside callable bodies, no rebinding of the field) and emits a
binding handle for them and one manager per source array. Each creation takes a
binding of its own from the manager, which reads the live source; the manager
closes every binding opened in the bar after the last authored statement of the
bar, giving each its own copy of the source. A creation on a later bar makes a
new binding and leaves the earlier ones as they are. The handle has a
checkpoint specialization that keeps the binding, its pending or owned state
and the owned contents.

A copy() of a record with such a field keeps its existing compile error:
TradingView does not share the array between a record and its copy, and no
observation shows what the copy holds. Scripts with requested evaluations,
collection history, array copy or slice, an array or object subscript, nested
records, varip holders or intrabar recalculation keep their previous lowering,
and so does every script that selects nothing: their C++ does not change. The
changelog describes the change.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Brings in the pull request that landed on main after this branch was cut (the
reservation of the numbered tuple element bindings, and the string-history
refusal rows pinned against the pinned reference). Two files conflict.

CHANGELOG.md: the bullet about a local spelled like a built-in variable keeps
main's text without its clause saying that a string local read with history and
used in a concatenation still does not compile (this branch fixes that), and the
new bullet for the fix stays after it.

tests/test_builtin_named_locals_compile.py: the docstring keeps main's
price-field exception and no longer says that the string-with-history shapes
do not compile; the loop of test_string_history_compile_outcome_matches_neutral_control
is main's loop over the census of names, with this branch's assertion that the
neutral control compiles kept before it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…e emissions

The nine rows of test_price_named_string_library_local_history_keeps_the_base_outcome
compare the whole C++ of a script whose library function joins a string local
with its previous value against the older reference commit. The reference
wrapped the current value in std::to_string (issue #354), which does not
compile; with the fix merged, the new emission joins the strings. The
reference text gets the same one-site repair that the two other modules of the
fix use (it requires exactly one such site), and everything else is still
compared unchanged. Rows that compare a refusal are not touched.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Tests only. The rows that compare the translation of scripts the retained
array change must not touch with the translation of the base took their
reference from a commit that main does not contain (that pull request was
merged as a squash), so on this branch and on main the comparison never ran and
the rows fell back to pinned tokens. One token named the unescaped identifier
source where the emitter writes pf_safe_source, and another asserted the
absence of a field the script never had, so neither could be relied on.

The reference is now main's commit a156a7d (the base this change sits on), and
it is required: a commit that is missing or is not an ancestor fails the row
with the "required byte oracle commit ... is missing" text that the other
byte-oracle tests of the repository use. Every neighbour row and every row for
a shape the change leaves alone now compares the whole translation unit with
the base, and keeps the check that no retained helper appears.

New rows, test_unresolved_callable_types_keep_base_emission in
tests/test_array_retained_alias.py: a record whose array field is assigned
through the receiver of a method (self.stamps := ...), and an array copy and
an array slice made through an array parameter of a method, each in a script
that also builds a record from a stable var array. The expectation of each is
the base translation with no retained helper.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ved holder

The plan that selects a record type for the retained lowering excludes a type
whose array field is assigned (holder.field := value). It resolved the holder
with the types known at planning time, before the parameters of methods and
functions are known, so a holder that is a parameter (self in a method, for
example) came back unresolved and the exclusion was skipped. The type was then
selected, and the emitter later reached the assignment with a value that is not
a proven source and stopped with a bare AssertionError, which transpile does
not turn into a diagnostic, on a script that the base translator compiled.

An assignment to a member that is an array field of a candidate type, through a
holder whose type is not resolved, now removes that type from the selection:
it keeps the base lowering. The same holds for the method form of copy() and
slice() whose receiver is unresolved and is a name bound by a parameter, a loop
or a tuple assignment, as it already did for a receiver known to be an array.
The emitter's invariant for a retained field without a proven source now
raises the existing CodegenInvariantError, which transpile reports as the
located internal-error diagnostic, instead of a bare AssertionError.

Scripts that select nothing, and the shapes that were selected before and have
a resolved holder and receiver, are unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…variable in v6 (#361)

Pine v6 has no implicit conversion of a number to bool, so `if ta.change(t)`
with a float t, or `if ta.change(strategy.position_size)`, does not compile on
TradingView. The generator translated both and ran them with the Pine v5
meaning (a non-zero number is true). The condition refusal of if and while
now also proves the numeric type of ta.* calls and of built-in member
variables, following the overload that the argument types select: ta.change of
a bool stays a bool condition, and an argument of unknown type never causes a
refusal. Pine v5 library definitions keep their behavior.

Tests: both reported forms, while conditions, arithmetic over such a call,
numeric built-in members, overloaded calls with numeric and bool sources,
controls that must stay accepted, a v5 library control, and complete-C++ comparisons
for scripts that are accepted.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…same name plainly (#359)

A var or varip declared inside one block and a plain variable of the same
name declared in a sibling block were emitted as one class member. The plain
declaration of the other block overwrote the member, so the var read whatever
that block wrote last and lost its first value, with no diagnostic. The code
generator now selects such a persistent declaration for a member name of its
own, through the renamer that already separates declarations whose types
differ. Two var declarations (which the analyzer already separates), two plain
declarations of one type, a name declared directly at the top level, and
collisions inside a single block keep their output.

Tests: the reported script against its control, the blocks in either order,
varip, three blocks, for, while and switch siblings, history reads, shadowing
binders, and complete-C++ comparisons for scripts without such a pair.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Remove five unused members from 48 public metrics fixtures while preserving
the remaining bytes and parsed content.
…able (#360)

A function or method parameter named like a built-in series (close, high,
hl2, ...) lost its [n] history operator: f(close) => close[1] was emitted as
the current value, and the script ran with no diagnostic and gave different
trades from TradingView. The pass that gives callable locals spelled like
built-in variables a private name now covers parameters too, so such a
parameter takes the same path as a parameter with any other name. Keyword
arguments keep binding to the authored spelling, including for overloads and
methods, and diagnostics show the authored spelling. A string parameter named
like a price field that is read with history inside a requested helper keeps
its earlier refusal. A method call with an unknown keyword argument keeps its
earlier error. A function call that binds no argument to such a history
parameter (an unknown or misspelled keyword) is now refused like any other
history parameter, with the existing error about the per-callsite type; before,
it compiled and read the chart's series.

Tests: the smallest form, the trades form with its control and the swapped
argument form; neighbouring forms (method, keyword call, forwarding, hl2, two
call sites); controls that were already right; complete-C++ comparisons for
scripts without such a parameter.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Describe recorded fixture semantics and dated evidence without operational
attribution. Preserve the documented technical rules and examples.
…#362)

A float setting of a built-in indicator that is an expression on an input
(the multiplier of ta.bb, ta.bbw, ta.kc, ta.kcw and the ta.vwap bands, the
factor of ta.supertrend, the arguments of ta.sar, the offset and sigma of
ta.alma) was converted to an integer before it reached the indicator: a
multiplier of 1.5 became 1 and a SAR maximum of 0.2 became 0. The code
generator rebuilds the indicator from the input and cast every runtime
constructor argument whose lowered expression is float-typed (a math call, a
division, a timeframe helper) to an integer, whatever the slot was.

That cast now follows the declared type of the constructor slot: a float slot
receives the value as a double (an integer na stays missing). Every other
slot, and every argument the cast never applied to, is rendered exactly as
before.

The ta.vwap bands needed one more step. The slot lookup goes by the Pine
function name, and the two band classes (the anchored form and the form with
the anchor omitted and stdev_mult given by keyword) have no entry under it,
so their multiplier slot was never placed and kept the integer cast. Both
classes are now placed from the anchored-band table, which names stdev_mult
as the multiplier.

Tests: the reported scripts against their controls with input overrides, the
other float settings of the same indicators, the ta.vwap multiplier in both
forms on the chart and in a requested context, the forms that keep their
refusals, integer lengths, and complete-C++ comparisons against the previous
commit for scripts whose float settings are plain inputs or literals.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Member of the 1.11.1 codegen train (#361 group A), reviewed and tested on its exact head.
Member of the 1.11.1 codegen train (#359), reviewed and tested on its exact head.
Member of the 1.11.1 codegen train (#360), reviewed and tested on its exact head.
Member of the 1.11.1 codegen train (fixture metadata), reviewed on its exact head.
Describe the declared symbol fact as per-run metadata and name the measured
instrument types directly.
Member of the 1.11.1 codegen train (#362), reviewed and tested on its exact head.
Member of the 1.11.1 codegen train (public guidance wording), reviewed on its exact head.
… change

The only conflict is CHANGELOG.md: this change and the numeric condition entry
both add their bullet at the top of the unreleased list. Both bullets are kept,
the condition entry first. No other file needed a resolution.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The only conflict is CHANGELOG.md. This change edits the first bullet of the
built-in-named locals entry (it drops the clause saying that a string local
read with history and used in a concatenation still does not compile) and adds
its own bullet after it; the train branch adds the built-in-named parameter
bullet after the original entry. The result keeps this change's edited bullet
and its new bullet, then the train's parameter bullet. No other file needed a
resolution.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Member of the 1.11.1 codegen train (retained array alias), reviewed and tested on its exact head.
Member of the 1.11.1 codegen train (#354 string local history), reviewed and lab-tested on its exact merge head.

@graphify-labs graphify-labs Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Graphify reviewed this change.

No issues found by static checks — no coupling regressions or blocking issues in the code graph. That is not the same as safe to merge: see what was not checked.

Not checked: tests were not run; formal verification proved 0 of 35 changed function(s) (1 sampled, not proven, 34 not verified).

Formal verification. PR-changed functions: 1/35 verified (0 proven, 1 may-equivalent, 0 distinguished) · 34 not verified (3 vacuous, 21 unsupported, 10 over cap).

Not verified on this run: \_rewrite\_function\_syntax\_method\_call (unsupported), \_visit\_FuncCall (unsupported), \_visit\_IfStmt (unsupported), \_visit\_WhileStmt (unsupported), separate\_builtin\_callable\_locals (vacuous: never exercised), \_declared\_names (unsupported), rename\_declarations (unsupported), \_declarations (vacuous: never exercised), block\_declarations (vacuous: never exercised), \_block\_locals\_needing\_names (unsupported), and 14 more; 10 changed function(s) beyond the cap and 0 skipped when the time budget ran out.


Graphify review — findings

Fixes a batch of Pine compile/codegen issues, mostly around built-in-named locals and parameters, var storage, and float indicator settings (#354, #359, #360, #361, #362). The diff is truncated, so this covers the changelog and docs, not the full code changes. - Refusals: Pine v6 now rejects an if/while condition whose numeric value comes from a ta.* call or a built-in variable like strategy.position_size, as TradingView does. Bool overloads and Pine v5 libraries are unaffected. - Built-in-named locals: a parameter spelled like a built-in (close, high) keeps its [n] history. A function-local string read with history keeps its string type in concatenations, so the emitted C++ no longer calls std::to_string on a string. - var storage: a var in one block no longer shares storage with a same-named plain variable in a sibling block. Retained record fields and plain var array aliases built from stable var arrays now keep their own copy of the array as it stood at the end of the creation bar. - Float settings: float settings computed from an input keep their fraction in ta.bb/bbw/kc/kcw, ta.vwap bands, ta.supertrend, ta.sar and ta.alma. Integer slots keep their narrowing and preserve na. Generated C++ changes, so affected strategies need to be regenerated and relinked.

Review partial — this diff was larger than one review pass covers, so later files were not reviewed; some findings may be missing.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 6212 functions depend on the 2154 functions this change touches.

Health — this change adds coupling hotspots:

  • worse: Analyzer — 107 callers, 15 callees
  • worse: CodeGen — 68 callers, 14 callees
  • worse: parse_library_module() — 15 callers, 11 callees
  • worse: bind_method_call() — 6 callers, 4 callees
  • worse: separate_builtin_callable_locals() — 2 callers, 6 callees
  • worse: _assert_keeps_the_base_outcome() — 3 callers, 4 callees
  • new: test_builtin_parameter_inventory_registers_scoped_history() — 0 callers, 6 callees

Verification — 6212 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 5334 function(s) in the blast radius were not formally verified this run

Cross-repo impact

Cross-repo impact

This change impacts 2 symbol(s) across 1 peer repo(s) — blast radius over derived cross-repo edges:

  • pineforge-4pass/pineforge-hpo — python/pineforge_hpo/transpile.py (via import_moniker, heuristic), tests/python/test_transpile.py (via import_moniker, heuristic)

1 peer repo(s) in this group are not yet indexed — cross-repo impact may be incomplete.

Cross-repo linkage is derived (SCIP-moniker matches are sound; name/contract matches are heuristic) and ADVISORY — it never affects this check's conclusion.

Test selection

Test selection

515 of 515 test file(s) selected (100%) via static blast radius.

Escalated to a full run for safety (noncode-change, uncovered-code) — the selection is not trustworthy on its own (see below). CI should run the whole suite.

  • tests/test_action_pins.py — full-run-safety
  • tests/test_allow_entry_in_qualifier.py — impact, full-run-safety
  • tests/test_allow_entry_in_request_const.py — impact, full-run-safety
  • tests/test_analyzer.py — impact, full-run-safety
  • tests/test_analyzer_matrix_inference.py — impact, full-run-safety
  • tests/test_analyzer_ta_return_types.py — impact, full-run-safety
  • tests/test_anchor_tf_change.py — impact, full-run-safety
  • tests/test_anchor_time.py — impact, full-run-safety
  • tests/test_anchor_vwap.py — impact, full-run-safety
  • tests/test_array_bounds_insert_fill_slice.py — impact, full-run-safety
  • tests/test_array_checked_access.py — impact, full-run-safety
  • tests/test_array_empty_aggregates.py — impact, full-run-safety
  • tests/test_array_history.py — impact, full-run-safety
  • tests/test_array_missing_integer_index.py — impact, full-run-safety
  • tests/test_array_new_color_fill_and_na_size.py — impact, full-run-safety
  • tests/test_array_new_defaults_and_order.py — impact, full-run-safety
  • tests/test_array_reference_semantics.py — impact, full-run-safety
  • tests/test_array_retained_alias.py — impact, changed-test, full-run-safety
  • tests/test_array_slice_view.py — impact, full-run-safety
  • tests/test_array_terminal_returns.py — impact, full-run-safety
  • tests/test_arrseg_coded_run_stops.py — impact, full-run-safety
  • tests/test_arrseg_review_fixes.py — impact, full-run-safety
  • tests/test_binop_operand_order.py — impact, full-run-safety
  • tests/test_block_shadow_primitive_types.py — impact, full-run-safety
  • tests/test_block_var_plain_siblings.py — impact, changed-test, full-run-safety
  • tests/test_bool_na_refusal.py — impact, full-run-safety
  • tests/test_bool_na_refusals_2.py — impact, full-run-safety
  • tests/test_bool_na_values.py — impact, full-run-safety
  • tests/test_bool_string_inference.py — impact, full-run-safety
  • tests/test_builtin_name_shadow.py — impact, full-run-safety
  • tests/test_builtin_named_locals_compile.py — impact, changed-test, full-run-safety
  • tests/test_builtin_named_locals_price_refusal.py — impact, changed-test, full-run-safety
  • tests/test_builtin_named_locals_receipt.py — impact, full-run-safety
  • tests/test_builtin_named_locals_scope.py — impact, changed-test, full-run-safety
  • tests/test_builtin_named_parameters.py — impact, changed-test, full-run-safety
  • tests/test_builtin_named_parameters_reference.py — impact, changed-test, full-run-safety
  • tests/test_calc_on_order_fills_codegen.py — impact, full-run-safety
  • tests/test_calendar_lambda_locals.py — impact, full-run-safety
  • tests/test_call_argument_order.py — impact, full-run-safety
  • tests/test_callable_local_histories.py — impact, full-run-safety
  • tests/test_callable_series_param_types.py — impact, full-run-safety
  • tests/test_callable_string_series_type.py — impact, full-run-safety
  • tests/test_callable_ta_forwarded_length.py — impact, full-run-safety
  • tests/test_callable_var_first_reach.py — impact, full-run-safety
  • tests/test_checked_settings.py — impact, full-run-safety
  • tests/test_ci_engine_main_touched.py — full-run-safety
  • tests/test_closed_trade_fold.py — impact, full-run-safety
  • tests/test_closed_trade_fold_emission.py — impact, full-run-safety
  • tests/test_closed_trade_text_equals.py — impact, full-run-safety
  • tests/test_coded_run_stops.py — impact, full-run-safety
  • … and 465 more

non-code file(s) changed (CHANGELOG.md, docs/reference/known-codegen-quirks.md, docs/reference/support-contracts.md, tests/fixtures/bar_magnifier/mi-fx-eth-15/metrics.json, tests/fixtures/bar_magnifier/w9mag-fx-eth-15-off/metrics.json …) → running the full suite for safety (a code graph can't see config/fixture/data deps)

changed code file(s) with no mapped test (CHANGELOG.md, docs/reference/known-codegen-quirks.md, docs/reference/support-contracts.md, pineforge_codegen/codegen/emit_top.py, pineforge_codegen/codegen/retained_alias.py) — a coverage gap or a missing link — running the full suite rather than only the selected tests

Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.

Docs that may be stale (advisory)

…and 2 more.

Formal verification

Could not verify: Could not verify \_rewrite\_function\_syntax\_method\_call.

The verifier did not have enough to check \_rewrite\_function\_syntax\_method\_call, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)

Could not verify: Could not verify \_visit\_FuncCall.

The verifier did not have enough to check \_visit\_FuncCall, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)

Could not verify: Could not verify \_visit\_IfStmt.

The verifier did not have enough to check \_visit\_IfStmt, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)

Could not verify: Could not verify \_visit\_WhileStmt.

The verifier did not have enough to check \_visit\_WhileStmt, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)

No difference found (not proven): No behavior difference found in \_builtin\_callable\_local\_names (not a proof).

The verifier ran both versions of \_builtin\_callable\_local\_names on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

Could not verify: Could not verify separate\_builtin\_callable\_locals.

The verifier did not have enough to check separate\_builtin\_callable\_locals, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 80 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly AttributeError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify \_declared\_names.

The verifier did not have enough to check \_declared\_names, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `node` is annotated `ASTNode` — outside the synthesizable primitive/collection set

Could not verify: Could not verify rename\_declarations.

The verifier did not have enough to check rename\_declarations, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)

Could not verify: Could not verify \_declarations.

The verifier did not have enough to check \_declarations, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 250 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous

Could not verify: Could not verify block\_declarations.

The verifier did not have enough to check block\_declarations, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 17 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous

Could not verify: Could not verify \_block\_locals\_needing\_names.

The verifier did not have enough to check \_block\_locals\_needing\_names, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)

Could not verify: Could not verify \_collect\_ta\_runtime\_resets.

The verifier did not have enough to check \_collect\_ta\_runtime\_resets, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)

Could not verify: Could not verify \_generate\_cpp.

The verifier did not have enough to check \_generate\_cpp, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)

Could not verify: Could not verify \_lower\_reset\_expr\_via\_visitor.

The verifier did not have enough to check \_lower\_reset\_expr\_via\_visitor, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)

Could not verify: Could not verify \_runtime\_ctor\_arg\_for\_reset.

The verifier did not have enough to check \_runtime\_ctor\_arg\_for\_reset, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)

Could not verify: Could not verify \_ta\_run\_ctor\_args.

The verifier did not have enough to check \_ta\_run\_ctor\_args, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)

Could not verify: Could not verify \_emit\_handle\_checkpoint\_traits.

The verifier did not have enough to check \_emit\_handle\_checkpoint\_traits, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)

Could not verify: Could not verify \_emit\_includes.

The verifier did not have enough to check \_emit\_includes, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)

Could not verify: Could not verify \_emit\_on\_bar.

The verifier did not have enough to check \_emit\_on\_bar, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)

Could not verify: Could not verify \_emit\_script\_state\_hooks.

The verifier did not have enough to check \_emit\_script\_state\_hooks, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)

Could not verify: Could not verify \_security\_derived\_argument\_history.

The verifier did not have enough to check \_security\_derived\_argument\_history, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)

Could not verify: Could not verify \_lazy\_edge\_hoist\_block\_reason.

The verifier did not have enough to check \_lazy\_edge\_hoist\_block\_reason, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)

Could not verify: Could not verify \_ta\_arg\_takes\_plan.

The verifier did not have enough to check \_ta\_arg\_takes\_plan, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)

Could not verify: Could not verify \_ta\_ctor\_param.

The verifier did not have enough to check \_ta\_ctor\_param, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)

Could not verify: Could not verify \_ta\_dynamic\_ctor\_arg.

The verifier did not have enough to check \_ta\_dynamic\_ctor\_arg, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)

· 3 grounded finding(s) anchored inline below; 4 more finding(s) on lines outside this diff (see the check run).

)


def separate_builtin_callable_locals(program, bindings) -> bool:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regression — separate_builtin_callable_locals()

fans out to 6 callees (efferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.



def _assert_keeps_the_base_outcome(source, reference, tmp_path, libraries=None):
def _assert_keeps_the_base_outcome(source, reference, tmp_path, libraries=None,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regression — _assert_keeps_the_base_outcome()

high coupling complexity (Ca·Ce = 12).

Grounded coupling-delta finding (deterministic), not an LLM guess.


@pytest.mark.parametrize("name", _LOCAL_NAMES)
@pytest.mark.parametrize("method", [False, True], ids=["function", "method"])
def test_builtin_parameter_inventory_registers_scoped_history(name, method):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regression — test_builtin_parameter_inventory_registers_scoped_history()

fans out to 6 callees (efferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

luisleo526 and others added 2 commits November 10, 2026 21:23
…able

tests/test_numeric_condition_calls.py carried an autouse fixture that set
PINEFORGE_REQUIRE_NATIVE=1 for every test of the file. That overrides the
rule of tests/_compile.py: without an engine checkout the native tests
skip, and only an environment that sets PINEFORGE_REQUIRE_NATIVE=1 itself
fails on missing inputs. In an environment without the engine (the hosted
test job) the native tests of the file therefore failed with
"PINEFORGE_ENGINE_INCLUDE not set or invalid" instead of skipping.

The fixture and its comment are removed; nothing else changes. With the
engine set and PINEFORGE_REQUIRE_NATIVE=1 in the environment every test of
the file runs as before.

Refs #361.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Let the numeric condition tests skip when no engine checkout is available
@luisleo526
luisleo526 merged commit b482a22 into main Nov 10, 2026
11 checks passed
@luisleo526
luisleo526 deleted the train/codegen-1.11.1 branch November 10, 2026 14:20
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