Skip to content

Keep timestamps 64-bit through function and method parameters, integer array fields and array.from, and in a time input that is changed, copied or kept in a var - #322

Merged
luisleo526 merged 9 commits into
mainfrom
fix-timestamp-width-64bit
Nov 3, 2026
Merged

luisleo526 merged 9 commits into
mainfrom
fix-timestamp-width-64bit

Conversation

@luisleo526

Copy link
Copy Markdown
Contributor

Fixes #285
Fixes #321

A Pine int holds millisecond timestamps (time, input.time(), timestamp()), but the generated C++ narrowed it to a 32-bit int in several places, so the value wrapped silently (gcc) or the build stopped (clang). This change keeps every int that carries a timestamp 64-bit.

//@version=6
strategy("changed time input")
int x = input.time(timestamp("01 Apr 2025 00:00 +0000"), "d")
if bar_index % 2 == 0
    x += 1000
plot(x)

On the released version this declares int x, so the selected timestamp wraps to a negative number. The same wrap happened for a time input kept in a var variable, for a copy of a time input (observed = x), for the integer parameters of a user function fed from input.time() (inWindow(s, e) => time >= s and time <= e), for a typed int parameter of a method that stores it in a field, for an array<int> field of a user-defined type (b.stamps.unshift(time)), and for array.from(input.time()).

What changes

Cause. The generated type of an integer was chosen from the Pine type alone (int), and only a few paths asked whether the value that reaches a slot can be a timestamp: an integer parameter, a method parameter, an array field, an array built by array.from, a copy of a time input and a time input kept in a variable that is later written each took the 32-bit default.

Change (pineforge_codegen/codegen/: types.py, array_field_width.py, base.py, visit_call.py, visit_expr.py, visit_stmt.py, emit_top.py, collection_history.py). A value is wide when a timestamp can reach it, decided from the declaration and the writes of that variable (by declaration identity, never by the spelling of a name): a user-function parameter or a typed method parameter that receives one, an array<int> field of a record (one 64-bit storage plan for the field, its constructors, aliases, copies, tuples and history), an array built by array.from, a copy of one, and a variable that is written from one (changed, copied or kept in var). Missing values (na) are translated when a value crosses an integer width, and arithmetic on a value that crossed a boundary this change widened uses binary64, because TradingView's int is a binary64 value above 2^53 (a subtraction at 2^53 + 17 gives 16, not 17): the target is "no 32-bit narrowing", not exact 64-bit integers above 2^53. Named arguments of array.push(value = ..., id = ...) now write the array. Scripts without a timestamp keep the C++ they had (see Verification). The walk that decides this visits each expression once per query, so its work is linear in the script (call-count tests pin it).

A missing value that reaches one of these 64-bit slots stays missing: a floating-point na or a 32-bit integer na passed to a 64-bit integer parameter, stored into a 64-bit integer array element, or given to an int field of a user type in .new() becomes the 64-bit missing value (the previous version could store 0 for an int field given na).

No script that the previous version translated is now refused, and nothing was added to any refusal list.

Known limits (separate defects, not changed here)

  • A non-var variable declared from input.time(...) and changed later is not restarted from its input on each bar (the generated code keeps accumulating the change), so only the first bar of the two native rows for that shape is compared; they are marked in the test file. This change fixes the width; the restart is a separate defect.
  • Two script shapes that stopped with a C++ build failure before now build and run, but print a value that differs from TradingView, because of two separate defects that this change does not touch (and that the previous version already gets wrong, silently, on sibling scripts that did build): (1) an array alias that is kept past its source array lifetime: the script prints 2 where TradingView prints 3 (0 of 26 bars equal); (2) a copy of a slice of an array that still shares elements with the original: the script prints 2 where TradingView prints 1 (0 of 26 bars equal). The same change fixes three other scripts that stopped with a build failure and now equal TradingView on all 26 bars. No refusal was added for either shape, because TradingView accepts both scripts.
  • TradingView's int above 2^53 is a floating-point value; exact integers there are not promised.

Verification

  • Full suite on a host without the engine checkout, base main and this head, same command: base 13,944 passed, 0 failed; head 13,961 passed, 0 failed; nothing fails only on the head (the head skips more rows because the new native rows need the engine and skip on that host on both trees).
  • Engine-paired run on the cloud runner (compile environment present), this head: 20,615 passed, 235 skipped, 0 failed (20,850 tests); compile corpus 312 passed; the parity gate step passes over 268 fixtures.
  • Red first. The native behaviour rows run the generated strategy and compare the value of the variable with a reference computed in the same script (changed typed and untyped inputs, copies, var, copy-then-changed, a late assignment, a var input compared with the bar time, function and method parameters, array fields, array.from, tuples, history). On main plus the earlier revision of these tests (7 changed test files), 93 tests fail and none outside those files; on this head all pass. The control rows for the usual date-range shape (time >= startTime with a time input kept in an ordinary variable) pass on both. Eleven translation rows pin the text main emits for scripts without a timestamp (a bool or count function-local history, an integer record field read into an integer slot, arithmetic on values that were already 64-bit): they fail on the revision of this change that altered them and pass on main (run on main plus that one test file: all eleven pass).
  • Translated before and after over 188 reference sources: nothing changes, nothing is newly refused. Over a population of 1,384 public scripts in 1,507 environments, both versions translate all 1,507; 1,504 are byte-identical and 3 change, all by the same cause: a function whose value is a stored timestamp field now returns a 64-bit integer.
  • Against recorded TradingView results for 110 scripts (the timestamp cases of this change): 65 that differed on main now equal TradingView on every entry, 20 were equal on both, 12 differ on both (the separate defects above), 13 are scripts TradingView refuses and both versions accept, none is worse than on main.
  • Parity gate on the measured baseline, engine 9fa1ac5f570fb56ce440546a4d2d941cd2ebc957 and this head: PASS (no improvement, no regression): 7,989 comparable measurements, 0 unmeasured; hard gate 1,009 compared, 0 moves; target gate 6,980 evaluated, 0 up, 0 down (receipt be3b5c08aa455184d83c859ceeda8e324a264992b1b8e2beed0ccd66fb7df652).
  • No documentation is changed; the diff contains only Python under pineforge_codegen/ and tests/.

luisleo526 and others added 9 commits November 3, 2026 15:09
…d array fields

Pine's int is 64-bit. Callable parameters, method parameters, UDT array
fields and array literals built from time inputs were lowered to 32-bit C++
int, truncating epoch-millisecond values. Their storage and arguments now
keep the wide integer type when the value can be an epoch time.

Adds emission rows, boundary rows and engine-run rows for each shape, with
pins for the shapes that must stay narrow.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The method form `arr.push(value = x)` is refused with "wrong number of
arguments" because the keyword argument of an array method call is dropped
before lowering; that is a separate gap from timestamp field width. Remove
that value from the timestamp storage rows and pin today's behaviour in a
plainly named known-gap row, to be updated when the keyword binding is fixed.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
… array fields instead of refusing them

A Pine int holds millisecond timestamps, but the generated C++ narrowed it to a 32-bit int in user-function parameters, typed int method parameters, array<int> fields of a user-defined type and array.from(input.time()). The value wrapped silently (gcc) or the build failed (clang).

Timestamp-connected array<int> storage now follows one shared width plan: record fields, constructors, aliases, copies, typed parameters, results, tuple elements and history all use 64-bit elements together, where the earlier approach refused such fields with a static_assert in the generated C++ and so stopped scripts that TradingView accepts. Missing-value sentinels are translated when a value crosses an integer width, arithmetic after a wide boundary uses binary64 operands (TradingView's int is a binary64 value above 2**53, so exactness above that is not promised), and the native behaviour rows in tests/test_timestamp_int_width_native.py compare each case with a Pine reference computed in the same script.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…y, and fix the paths it missed

The first version of the width plan widened slots that carry no timestamp (a same-spelled local of another function, a plain 32-bit method argument, a chart-builtin subtraction) and still narrowed a typed method parameter stored into a record field, the history of a field alias, and named-argument array writes. A loop-local array built from a timestamp also got a double element type next to an int64 initializer, which the C++ compiler refused where the old output built.

The plan now resolves array element families and integer expressions in their lexical owner, declaration emission consumes that same plan, a field's provenance is recomputed when the scalar analysis grows, and the binary64 conversion is used only where a value crosses a widened boundary. Namespace array.push binds its named id and value arguments. Behaviour rows compare each result with a Pine reference computed without the path under test; rows for shapes that fail the same way on the old output (a destructured array tuple, a delayed persistent array) are not part of this change.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…visited along every path

The reachability walks that decide whether a value comes from a widened source copied their visited set on every call. A value reached again through a sibling path was explored again, so a name reassigned many times, or a chain of values each built from the previous one twice, made the translator's work grow factorially or exponentially with the script and some scripts never finished.

Each query now shares one visited set across all its paths. A repeated node has the same successors, so expanding it once gives the same yes-or-no answer; the set stays local to one query because the surrounding provenance scan can grow between queries. Declaration write lists and callable returns are keyed by their identity and specialization so another owner or call site stays a separate question.

New rows translate and run the graphs that cannot finish on the old walk, and count walk calls at two sizes to show growth is linear, not factorial.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…iteral parameters, and keep a missing integer missing

The wider timestamp handling resolved a function-local variable by its declaration but still read the old spelling-based name set for its series type, so an untyped local holding time, or a typed int local holding it, lost its 64-bit series; a block-local int that shadows a wide global no longer widened the destination of a product above int32, so the stored value became missing; a wide literal passed into an integer parameter was not recognised as a widened parameter, so the binary64 subtraction above 2**53 was exact int64 arithmetic instead; and a missing integer kept as a double NaN reached a 32-bit method parameter as zero.

A callable-local series now follows its own declaration and writes, a shadow's operand bound follows its own writes before the existing magnitude bound is applied, a scalar parameter's emitted width is the boundary, and a stored double read is translated to the integer missing value when it crosses into a narrower integer parameter. Two tests that pinned a refusal of a script whose helper shares a spelling with another function's epoch local now assert the accepted, separately typed declarations, because that refusal went away with the spelling leak. One behaviour row read the first element of an array that is appended to on every bar; it now reads the last. New rows cover the function-local epoch history, the shadowed product and the same-named counter beside an epoch local.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…alue from its emitted storage, and cover a changed time input

A plain local initialised from a read of a history parameter (previous = value[1]) borrowed the analyzer's integer type from the series inventory, which also lists parameters that have no local declaration, so a 64-bit history read was narrowed to 32 bits and the integer missing value was lost. The callable-local series type now requires an actual declaration, which keeps the widening of a local that holds an epoch and restores the na-aware fallback for the history read.

A top-level value computed by a callable with a widened parameter was folded to an exact integer from its actual argument before the width was known, so a difference above 2**53 was exact instead of the binary64 value TradingView gives. Such folds are now dropped once the width inventories exist, following aliases through their declarations.

New behaviour rows cover an input.time variable that is later changed (typed, untyped, var, copied, assigned late), and controls for the unchanged date-range shape; one row that hit a separate, older defect with a local named like a function now asks only about the epoch history.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…e rows independent of unrelated defects

The behaviour rows for time-input variables now include a copy of an unchanged time input (typed, untyped and var), which also wrapped on the previous version, and a var time input compared with the bar time. The two rows for a non-var global that is changed compare the first bar only, because later bars also depend on a separate defect (a changed input-backed global is not restarted from its input on each bar), and the tuple row no longer shares a spelling with a user function, an older and separate defect.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…ger function locals, integer field reads, and arithmetic on values that were already 64-bit

A translate-only comparison against a population of public scripts showed the wider timestamp handling also changed code that carries no timestamp: a bool function-local series was typed bool and an integer count local was typed int where the previous version used the double series, a read of an integer record field into an integer slot gained a conversion wrapper, and arithmetic on parameters, array elements and variables that were already 64-bit gained the binary64 form.

The callable-local series lookup now decides only whether an integer local holds an epoch, the field-read conversion follows the actual argument bindings instead of any non-literal write, and the binary64 arithmetic is used only when a value crosses a boundary this change widened. New rows pin the previous text for each unrelated shape and fail on the version that changed it.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>

@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.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 4 advisory finding(s) below merit a look before merge.

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

Formal verification. PR-changed functions: 13/35 verified (0 proven, 13 may-equivalent, 0 distinguished) · 22 not verified (12 unsupported, 10 over cap).

Not verified on this run: \_\_init\_\_ (unsupported), \_generate\_cpp (unsupported), \_emit\_func\_def\_body (unsupported), \_callable\_series\_local\_symbol (unsupported), \_expr\_returns\_wide\_int (unsupported), \_func\_int\_return\_uses\_wide\_history (unsupported), \_int\_arith\_bound (unsupported), \_is\_int64\_builtin\_init (unsupported), \_narrow\_int\_bound (unsupported), \_narrow\_int\_name\_info (unsupported), and 2 more; 10 changed function(s) beyond the cap and 0 skipped when the time budget ran out.


Graphify review — findings

Adds a whole-program pass, prepare_array_widths, that finds connected integer arrays (UDT fields, locals, parameters, return values, tuples, history) and promotes each connected group to int64 together, while unconnected small-int arrays keep their original C++. Declaration, constructor, parameter, result, tuple and history type emission now read this shared plan, so widths agree across callable boundaries without inserting conversions. The risk sits in the plan's inputs and consumers: spec_of types int64 builtin calls as plain int and a single for-in variable as the index rather than the element, and UDT struct emission may not apply the widened field slots.

Worth a look

  • UDT struct emission ignores widened field slots from prepare_array_widths — pineforge_codegen/codegen/base.py:5323 · Escalate · medium
    • NOT verified (no proof, no reproducing execution) — needs human review
  • int64 builtin calls are typed as plain int — pineforge_codegen/codegen/array_field_width.py:187 · Escalate · medium
    • NOT verified (no proof, no reproducing execution) — needs human review
  • for-in over an array: a single loop variable is typed as the int index instead of the element — pineforge_codegen/codegen/array_field_width.py:153 · Escalate · medium
    • NOT verified (no proof, no reproducing execution) — needs human review
  • Imported helper timestamp_array_fields may not exist in array_field_width — pineforge_codegen/codegen/base.py:5312 · Escalate · medium
    • NOT verified (no proof, no reproducing execution) — needs human review

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 — 4082 functions depend on the 999 functions this change touches.

Health — this change adds coupling hotspots:

  • new: prepare_array_widths() — 2 callers, 7 callees
  • new: spec_of() — 2 callers, 6 callees

Verification — 4082 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: 3325 function(s) in the blast radius were not formally verified this run

Cross-repo impact

Cross-repo impact

No cross-repo impact detected among the indexed peer repo(s).

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.

Docs that may be stale (advisory)

…and 5 more.

Formal verification

Could not verify: Could not verify \_\_init\_\_.

The verifier did not have enough to check \_\_init\_\_, 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)

No difference found (not proven): No behavior difference found in \_lower\_collection\_history (not a proof).

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

Guarantee: Empirical: the synth_receiver tier. 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 \_emit\_func\_def\_body.

The verifier did not have enough to check \_emit\_func\_def\_body, 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 \_array\_from\_element\_spec (not a proof).

The verifier ran both versions of \_array\_from\_element\_spec on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: the synth_receiver tier. 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 \_callable\_series\_local\_symbol.

The verifier did not have enough to check \_callable\_series\_local\_symbol, 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 \_coerce\_int\_slot (not a proof).

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

Guarantee: Empirical: the synth_receiver tier. 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.

No difference found (not proven): No behavior difference found in \_collection\_lvalue\_spec (not a proof).

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

Guarantee: Empirical: the synth_receiver tier. 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.

No difference found (not proven): No behavior difference found in \_emitted\_value\_is\_double (not a proof).

The verifier ran both versions of \_emitted\_value\_is\_double on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: the synth_receiver tier. 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.

No difference found (not proven): No behavior difference found in \_expr\_is\_int64\_builtin (not a proof).

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

Guarantee: Empirical: the synth_receiver tier. 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 \_expr\_returns\_wide\_int.

The verifier did not have enough to check \_expr\_returns\_wide\_int, 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: synthesized inputs (no suite coverage): POSSIBLE DIFFERENCE on 4 constructed input(s) — both versions returned but disagreed on the args (e.g. old=["list", [["int", "0"], ["int", "1"], ["set", [["tuple", []]]], ["none"]]], new=["list", [["int", "0"], ["int", "1"], ["set", [["tuple", [["str", "expr"], ["int", "140297618243432"], ["str", ""], ["none…]]], ["none"]]]); ADVISORY, unverified — a synthesized input may violate an unstated precondition, so this is NOT a sound refutation and does NOT gate the merge; confirm with a real test, or let carve/property refute on real inputs; property tier: method — first parameter is `self`, which needs a constructed instance (not synthesizable)

Could not verify: Could not verify \_func\_int\_return\_uses\_wide\_history.

The verifier did not have enough to check \_func\_int\_return\_uses\_wide\_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)

No difference found (not proven): No behavior difference found in \_func\_param\_int\_cpp\_type (not a proof).

The verifier ran both versions of \_func\_param\_int\_cpp\_type on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: the synth_receiver tier. 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.

No difference found (not proven): No behavior difference found in \_infer\_type (not a proof).

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

Guarantee: Empirical: the synth_receiver tier. 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 \_int\_arith\_bound.

The verifier did not have enough to check \_int\_arith\_bound, 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 \_is\_int64\_builtin\_init.

The verifier did not have enough to check \_is\_int64\_builtin\_init, 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 \_na\_aware\_wide\_is\_double (not a proof).

The verifier ran both versions of \_na\_aware\_wide\_is\_double on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: the synth_receiver tier. 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 \_narrow\_int\_bound.

The verifier did not have enough to check \_narrow\_int\_bound, 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 \_narrow\_int\_name\_info.

The verifier did not have enough to check \_narrow\_int\_name\_info, 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 \_param\_is\_integer\_scalar (not a proof).

The verifier ran both versions of \_param\_is\_integer\_scalar on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: the synth_receiver tier. 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 \_series\_type\_for.

The verifier did not have enough to check \_series\_type\_for, 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 \_type\_for\_decl.

The verifier did not have enough to check \_type\_for\_decl, 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 \_type\_spec\_from\_expr (not a proof).

The verifier ran both versions of \_type\_spec\_from\_expr on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: the synth_receiver tier. 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.

No difference found (not proven): No behavior difference found in \_udt\_array\_field\_target (not a proof).

The verifier ran both versions of \_udt\_array\_field\_target on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: the synth_receiver tier. 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.

No difference found (not proven): No behavior difference found in \_udt\_receiver\_type\_name (not a proof).

The verifier ran both versions of \_udt\_receiver\_type\_name on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: the synth_receiver tier. 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.

· 2 grounded finding(s) anchored inline below.

return spec


def prepare_array_widths(gen):

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 — prepare_array_widths()

fans out to 7 callees (efferent coupling).

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

return declarations[(scope, expr.name)], owner
return declarations.get(("", expr.name)), None

def spec_of(expr, owner, seen=frozenset()):

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 — spec_of()

fans out to 6 callees (efferent coupling).

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant