Skip to content

Reserve the numbered tuple element bindings, and pin the string-history refusal rows against the pinned reference commit - #357

Merged
luisleo526 merged 4 commits into
mainfrom
fix/tuple-element-bindings-followup
Nov 9, 2026
Merged

luisleo526 merged 4 commits into
mainfrom
fix/tuple-element-bindings-followup

Conversation

@luisleo526

Copy link
Copy Markdown
Contributor

Follow-up to the fixes for #350 and #351 (already on main). It adds the pieces that the reviews of those two changes left open. It is related to both issues and closes neither.

What changes

Three commits, tests first.

  1. Census row (tests only). The census of generated names holds one representative for each numbered family, and its test requires the emitter's reserved spellings to cover every one. The element bindings _tuple_result_<n>_elem_<m> had no representative; the row adds _tuple_result_0_elem_0. The commit also renames a stale list name in the helper-series-words test (the list is derived from the reference tokens, so it is no longer a list of introduced words); the test ids do not change. The census test fails on this commit, as intended.
  2. Fix. The generator declares the per-element bindings _tuple_result_<n>_elem_<m> when the lowering decomposes the result of a tuple assignment (a call of a user-defined function or of a typed method). The temporary pattern reserved _tuple_result_<n> but not that second spelling, so a script variable spelled like one of the bindings kept its name and could redeclare the generated binding. The pattern now accepts the optional _elem_<m> suffix. It matches only the complete spelling with both numbers, so names that merely start with _tuple_result_ keep their spelling. One changelog line.
  3. Tests and one shared constant, no behaviour change. Rows now pin the keying of the price-name refusal (a string local spelled like a price field and read with history in a requested helper) against the pinned C++ reference commit that the existing helper-local tests already use ("the reference" below): a string global, a string parameter and a library local give the same C++ as the reference where it accepts the script, and the same diagnostic (severity, code, message, line, column) where it refuses it; one more row pins a positional read directly in the helper. The annotation key authored_local_names had a private constant and two copies of the literal; the constant is now public and both places import it (the defining module loads only the syntax tree, the limits, the errors and the analyzer tables, so there is no import cycle). The refusal pairs for close in the compile rows move into their own test, so they run on a host without a compiler; every compile comparison keeps its skip, its id and its assertions.

Evidence

  • Red then green: on the first commit exactly one of 2,877 tests fails, the census row, missing exactly _tuple_result_0_elem_0; on the second commit none fails.
  • Whole branch: the 25 touched or related test files, 5,273 tests, 0 failures, 0 errors, 113 skips (the rows that need the engine library or private fixtures are skipped in that run).
  • Translate-only comparison of the pinned corpus (1,507 contexts), base against this branch: identical C++, or refused on both sides, in all 1,507; no context changed and none is newly refused.
  • The host member census is unchanged (718 host members, 172 derived, 172 committed, on both trees).
  • Mutation check of the new rows: the positional row fails when the rule's first call site is removed; the parameter row fails when the declaration-kind test is loosened. The global and library rows pin "same outcome as the reference" and fail for none of the mutants tried (a global never reaches the rule: both of its call sites skip global bindings).

Not shown

  • No compile of a script whose variable is spelled _tuple_result_0_elem_0 was run; that the generated C++ collided rests on reading the emitter.
  • Hosted CI on this branch, the engine-library rows and an x86 run are not part of the evidence above.

🤖 Generated with Claude Code

luisleo526 and others added 3 commits November 9, 2026 14:18
…ename a stale list name

The census of generated names (#350) holds one representative for each numbered family, and its test requires the emitter's reserved spellings to cover every one. The element bindings that a tuple result is destructured into, _tuple_result_<n>_elem_<m>, are declared by the same lowering as _tuple_result_<n> but had no representative. The row adds _tuple_result_0_elem_0.

The list of reference words in the helper series words test is derived from the reference tokens, so it is no longer a list of introduced words. It is renamed to say so. Nothing else in that file changes, and the test ids stay the same.

Tests only. The census test fails on this commit, because _tuple_result_0_elem_0 is not reserved yet.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A tuple assignment that the generator materializes declares a temporary, _tuple_result_<n>, and then destructures it into the bindings _tuple_result_<n>_elem_<m>. The temporary pattern reserved the first spelling but not the second, so a script variable spelled like _tuple_result_0_elem_0 kept its name and could redeclare the generated binding (#350).

The pattern now also accepts the optional _elem_<m> suffix of that family. It matches only the complete spelling with both numbers, so ordinary names that merely start with _tuple_result_ keep their spelling. The census row added in the previous commit covers the new spelling. The changelog lists the change.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…, and run the refusal pairs of the compile rows without a compiler (#351)

The refusal of a string local spelled like a price field and read with history in a requested helper is keyed on a non-var local declaration whose authored spelling is one of the nine price names. Rows now pin that keying against the reference commit: a string global, a string parameter and a library local of those names, read with history in a requested helper, give the same C++ as the reference where it accepts the script and the same diagnostics (severity, code, message, line, column) where it refuses it. One more row pins a positional read, str.length(close[1]) directly in the helper, which keeps the reference's PF-E3011 at the same position.

The annotation key "authored_local_names" had a private constant in the analyzer and two copies of the literal, one in the refusal rule and one in the analyzer's declaration visitor. The constant is now public, keeps its one definition and its value, and both places import it. The module that defines it imports only the syntax tree, the limits, the errors and the analyzer tables at load time, so the new imports add no cycle, and the behaviour is unchanged.

In the compile rows, the pairs that pin the base's refusal for close in the shapes typed, nested and two-sites move into their own test, so that they run on a host without a compiler. Every compile comparison keeps its skip, its id and its assertions. The module docstring now says that these rows pin the refusal instead of a compile outcome.

Tests, and one constant made public and imported in two places.

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

@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 5 changed function(s) (5 not verified).

Formal verification. PR-changed functions: 0/5 verified (0 proven, 0 may-equivalent, 0 distinguished) · 5 not verified (3 vacuous, 2 unsupported).

Not verified on this run: \_visit\_VarDecl (unsupported), authored\_callable\_text (vacuous: never exercised), restore\_callable\_diagnostics (vacuous: never exercised), separate\_builtin\_callable\_locals (vacuous: never exercised), \_security\_check\_price\_named\_local\_history (unsupported).


Graphify review — findings

Fixes a name collision where a script variable spelled like a generated tuple element binding (_tuple_result_0_elem_0) redeclared that binding; the C++ name allocator now reserves _tuple_result_N_elem_M spellings, so such a variable gets a private name. Also exports the authored_local_names annotation key as the public AUTHORED_LOCAL_NAMES constant, so the analyzer and security emitter share it instead of repeating the string literal. Test changes move the price-field string-history refusal checks into their own transpile-only test that needs no C++ compiler, and a changelog line records the fix.

No blocking issues surfaced.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 4803 functions depend on the 975 functions this change touches.

Health — grade A; 10 existing hotspot(s) in the area this change touches (pre-existing, not introduced here):

  • Analyzer — 105 callers, 15 callees (high)
  • _generate() — 131 callers, 9 callees (high)
  • _generate_raw() — 29 callers, 9 callees (high)
  • _generate() — 9 callers, 25 callees (high)
  • _analyze() — 30 callers, 6 callees (high)
  • _check() — 31 callers, 3 callees (high)
  • resolve() — 25 callers, 3 callees (high)
  • _codegen() — 10 callers, 7 callees (high)
  • …and 2 more

Verification — 4803 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: 3936 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

508 of 508 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_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_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, 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 — 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
  • tests/test_codegen_ascii.py — impact, full-run-safety
  • tests/test_codegen_audit_fixes.py — impact, full-run-safety
  • tests/test_codegen_determinism.py — impact, full-run-safety
  • tests/test_codegen_drawing_data.py — impact, full-run-safety
  • … and 458 more

non-code file(s) changed (CHANGELOG.md) → 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) — 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.

Formal verification

Could not verify: Could not verify \_visit\_VarDecl.

The verifier did not have enough to check \_visit\_VarDecl, 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 authored\_callable\_text.

The verifier did not have enough to check authored\_callable\_text, 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 262 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 restore\_callable\_diagnostics.

The verifier did not have enough to check restore\_callable\_diagnostics, 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 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 \_security\_check\_price\_named\_local\_history.

The verifier did not have enough to check \_security\_check\_price\_named\_local\_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)

@luisleo526 luisleo526 closed this Nov 9, 2026
@luisleo526 luisleo526 reopened this Nov 9, 2026
@luisleo526
luisleo526 merged commit a156a7d into main Nov 9, 2026
12 checks passed
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