| 12 |
#30 (on main) |
src/recast/verify/bitexact.py:892-905 |
any exception from the candidate was candidate raised: ..., the subprogram failed |
SystemExit or IndexError declines the draw: redrawn with a fresh seed, up to draws_per_trial = 24; the reference is never called on the declined draw; only when all 24 are declined is it an error |
a mistranslated guard (an ERROR STOP taken on inputs the Fortran accepts) or a data-dependent wrong subscript that fires on most valid draws passes once one draw survives; redrawn is written to metrics["subprograms"][name] only, with no threshold and no mention in the verdict detail. The reshaped * 2 > len(rounds) rule at :1053 covers IndexError alone |
| 13 |
#30 (on main) |
src/recast/verify/bitexact.py:1002-1034 |
a NaN on both sides at the same position was ULP 0; every other element and every other output label of the trial was compared |
a both-sided NaN in any one output label discards the whole trial, every label and every non-NaN element already staged, and redraws |
mismatches in the other elements and the other labels of that trial are never counted; with _f_sqrt returning NaN on negative input and the default draw range, a bug correlated with the NaN-producing region (a branch on a negative argument) is never compared. Counted only as redrawn |
| 14 |
#30 (on main) |
src/recast/fortran/interface.py:1452-1531 (_written_or_escaping, _infer_read_only_intents) |
an undeclared intent stayed UNKNOWN and bitexact refused the subprogram ("this verifier cannot...") |
inferred IN when the pass sees no write; the pass walks Part_Ref and Structure_Constructor only (:1490), not Function_Reference (which extract itself walks at :723), not ASSOCIATE aliases, and a Part_Ref whose base is not one of this file's subprograms is taken for a subscript (:1494) |
a dummy modified through a use-associated or external function, or an internal-unit WRITE, is spelled intent(in) in the wrapper, not returned by the candidate, and compared on neither side. test_fortran_analysis.py:1262 flipped UNKNOWN to IN; test_fortran_frontend.py:329 weakened == "UNKNOWN" to "no override key" |
| 15 |
#27 (widens row 8) |
src/recast/fortran/flatten.py:1075-1098 with interface.py:1252-1264 |
every wanted module variable became a StateVar, an adapter argument the run's value is written into |
record["public"] is built from public :: statements only; integer, public, save :: nzmax = 10 in a default-private module is classed private and goes to left_to_module (verified with interface.extract) |
the variable is reachable by use and the recorded run may have set it; both sides run at the declared default. left_to_module lives only in facts.extra["flat_plans"]; no verdict, handle or ungated entry names it |
| 16 |
#27 |
src/recast/fortran/flatten.py:961-966 |
a touched component whose dtype is not in FORTRAN_TYPES (character) reached flat._declare, which raised ValueError(... is not flat): no adapter, unit fails loudly |
dropped from the plan into left_to_module with "the physics reads it only to print"; the written flag is dropped with it |
nothing checks how the component is used: a select case (trim(err_info%err_header)) steering physics runs at the type default on both sides while the recorded outputs came from the run's value; surfaced in no verdict |
| 17 |
#27 |
src/recast/transform/jax/tree.py:1709-1740 (_guard_after_returns, from _single_exit) |
an early return in a function with several outputs was NotFlat: kernel delegated, recorded |
when every early return is the final tuple and one sits in a loop, return becomes _ret = True and only the statements after the returning statement in each block are wrapped in if not _ret |
statements that precede the returning if inside the loop body are unguarded and run on every remaining iteration: do i; y(i,:) = y(i,:) + 1; if (err(i) /= 0) return; end do gives [3, 3] for err = [1, 0] where Fortran gives [3, 2]. The test's loop puts the if first, so it never exercises this |
| 18 |
#27 |
src/recast/transform/numpy/subprograms.py:1033-1050 |
a constructor inside a larger initializer went to the parse, which raised NoRule and deferred the subprogram |
the text around each (/ ... /) goes through the token pass (strip_kind and case), never through Expressions |
the token pass has no integer division: integer, parameter :: h(3) = (/1, 2, 3/) / 2 renders np.array([1, 2, 3], dtype=np.int32) / 2, a float64 [0.5, 1.0, 1.5], where the parse path spells _f_int_div. Nothing refuses |
| 19 |
#27 |
src/recast/transform/jax/backend.py:406-416, 761-764 (_static_test) |
if mod.f(2): went to the cond lowering; a body with no carried names raised JaxQueue("IF with no carried effects"), delegated and in the evidence |
any module.func(constants) test is "static" and lowered to a plain Python if; in the statics branch a JaxQueue from _cond_form is swallowed |
when the callee is a ported companion whose closure is traced state, bool(tracer) is a ConcretizationTypeError at trace time: a run failure outside delegated. Not silently wrong; the refusal moved from emission to trace |
| 20 |
#27 |
src/recast/transform/jax/tree.py:906-918, 1197-1218 (host_calls) |
a companion procedure its port did not lower was NotFlat |
the anchor's host call is kept inside the kernel and named in notes.host_calls, assuming its guard is trace-time static |
when the guard lowers to lax.cond, both arms trace the host call; a host function that survives tracers runs once at trace and its value is baked into the jit cache. host_calls records the call, not whether its guard was static (plausible, not exercised) |
| 21 |
#27 |
src/recast/fortran/frontend.py:668-676, rwset.py:252-288, verify/rwset.py:182 |
(no refusal before; a mis-record) |
when a stubbed module is absent from the tree, every name in its only: list, constants and types included, is recorded as a stub procedure |
a read of such a constant is dropped from the Fortran-side block reads and from the Python side alike, so the rwset gate cannot see a disagreement there, and stub_procedures in the evidence misnames constants as procedures (small) |
Review of #22, #25 and #27 (2026-09-04) found rules that turned a refusal on main into a silent fallback on the branch. Each raises the count of units that reach the gate, but none of them translates anything new: what main reported as a refusal is now either a value the engine assumed, or a failure the evidence no longer names. The bit-exact gate is the backstop, and it only sees the paths the sampled trials exercise; a tolerance constant folded at the wrong kind, or an array logical spelled as a scalar
notover a length-1 array, can pass every trial and still be wrong.These are recorded here, apart from the review comments, so they are ruled on one at a time and none is merged by omission. A row closes when the rule refuses again, records what it assumed in the evidence, or takes the assumption from configuration the extension supplies.
src/recast/fortran/constants.py:277-280None, the constant is skippednp.finfo(np.float64).epsreal(4) :: xthenreal(8), parameter :: e = epsilon(x)folds to 2.2e-16 (gfortran 1.19e-7); a tolerance rarely changes a sampled outputsrc/recast/fortran/expr.py:88-91, 288-290anduse.py:86epsilon/huge/tiny/real/dblefold at 64 bits whatever the declared kind; the self-reference is replaced by a 64-bit literalreal(4), parameter :: tol = max(1e-10, epsilon(tol))folds to the 64-bit epsilonsrc/recast/fortran/expr.py:126-135real(x)refusedkind=sprefused, barereal(x)(also single precision) passes asnp.float64src/recast/fortran/expr.py:50KINDS_64listscore_rknd,shr_kind_r8as 64-bitcore_rkndis a preprocessor choice and can be 4; the engine haskind_assumptionsfor exactly thissrc/recast/transform/numpy/expressions.py:424-427notfor every non-WHERE operand)~/&/|when rank > 0, scalar spelling when rank cannot be settled or the name is undeclared (semantics.rankreturns 0 without raising)not np.array([True])isFalse, silently; only len > 1 raisessrc/recast/fortran/interface.py:588-603-nd:ndfolds the upper bound only and leaves- ndin the record, nothing refuses itsrc/recast/transform/numpy/statements.py:1752-1760NoRule: block deferred, counted, in the evidenceraise NotImplementedError(...); block statusmechanical, absent fromdeferred, the gate judges the subprogramdeferredcount falls with no translation done; another input that takes the path is a runtime failure with no note in the verdictsrc/recast/fortran/flatten.py:1076-1099left_to_module, plan continues, "both sides run with the module's own default"set_clubb_debug_levelwriteserror_code%clubb_debug_level)src/recast/oracle/record.py:305-312Two more belong on the same ledger though they are not new on these branches:
src/recast/transform/numpy/statements.py:142-184(#27 adds thefor...elserule)src/recast/transform/numpy/constants.py(pre-#22)realand the kind inquiries have spellednp.float64since before these PRsFound reviewing the tier-0/tier-1 CLUBB PRs; none of these is visible in the corpus numbers (31/67 mechanical, 433 deferred on main and on tier1-grid alike), which is the point: a silent fallback moves nothing a report can see.
Second sweep (2026-09-04, later): every open PR again for this pattern alone, plus #29 and #30 as merged
#29 holds its claim: its three
continuepaths became raises with tests. #22 and #25 gave nothing beyond rows 1-6. The rest:src/recast/verify/bitexact.py:892-905candidate raised: ..., the subprogram failedSystemExitorIndexErrordeclines the draw: redrawn with a fresh seed, up todraws_per_trial = 24; the reference is never called on the declined draw; only when all 24 are declined is it an errorredrawnis written tometrics["subprograms"][name]only, with no threshold and no mention in the verdict detail. Thereshaped * 2 > len(rounds)rule at:1053coversIndexErroralonesrc/recast/verify/bitexact.py:1002-1034_f_sqrtreturning NaN on negative input and the default draw range, a bug correlated with the NaN-producing region (a branch on a negative argument) is never compared. Counted only asredrawnsrc/recast/fortran/interface.py:1452-1531(_written_or_escaping,_infer_read_only_intents)UNKNOWNand bitexact refused the subprogram ("this verifier cannot...")INwhen the pass sees no write; the pass walksPart_RefandStructure_Constructoronly (:1490), notFunction_Reference(whichextractitself walks at:723), not ASSOCIATE aliases, and aPart_Refwhose base is not one of this file's subprograms is taken for a subscript (:1494)intent(in)in the wrapper, not returned by the candidate, and compared on neither side.test_fortran_analysis.py:1262flipped UNKNOWN to IN;test_fortran_frontend.py:329weakened== "UNKNOWN"to "no override key"src/recast/fortran/flatten.py:1075-1098withinterface.py:1252-1264StateVar, an adapter argument the run's value is written intorecord["public"]is built frompublic ::statements only;integer, public, save :: nzmax = 10in a default-private module is classed private and goes toleft_to_module(verified withinterface.extract)useand the recorded run may have set it; both sides run at the declared default.left_to_modulelives only infacts.extra["flat_plans"]; no verdict, handle orungatedentry names itsrc/recast/fortran/flatten.py:961-966FORTRAN_TYPES(character) reachedflat._declare, which raisedValueError(... is not flat): no adapter, unit fails loudlyleft_to_modulewith "the physics reads it only to print"; thewrittenflag is dropped with itselect case (trim(err_info%err_header))steering physics runs at the type default on both sides while the recorded outputs came from the run's value; surfaced in no verdictsrc/recast/transform/jax/tree.py:1709-1740(_guard_after_returns, from_single_exit)returnin a function with several outputs wasNotFlat: kernel delegated, recordedreturnbecomes_ret = Trueand only the statements after the returning statement in each block are wrapped inif not _retifinside the loop body are unguarded and run on every remaining iteration:do i; y(i,:) = y(i,:) + 1; if (err(i) /= 0) return; end dogives[3, 3]forerr = [1, 0]where Fortran gives[3, 2]. The test's loop puts theiffirst, so it never exercises thissrc/recast/transform/numpy/subprograms.py:1033-1050NoRuleand deferred the subprogram(/ ... /)goes through the token pass (strip_kindand case), never throughExpressionsinteger, parameter :: h(3) = (/1, 2, 3/) / 2rendersnp.array([1, 2, 3], dtype=np.int32) / 2, a float64[0.5, 1.0, 1.5], where the parse path spells_f_int_div. Nothing refusessrc/recast/transform/jax/backend.py:406-416, 761-764(_static_test)if mod.f(2):went to the cond lowering; a body with no carried names raisedJaxQueue("IF with no carried effects"), delegated and in the evidencemodule.func(constants)test is "static" and lowered to a plain Pythonif; in the statics branch aJaxQueuefrom_cond_formis swallowedbool(tracer)is aConcretizationTypeErrorat trace time: a run failure outsidedelegated. Not silently wrong; the refusal moved from emission to tracesrc/recast/transform/jax/tree.py:906-918, 1197-1218(host_calls)NotFlatnotes.host_calls, assuming its guard is trace-time staticlax.cond, both arms trace the host call; a host function that survives tracers runs once at trace and its value is baked into the jit cache.host_callsrecords the call, not whether its guard was static (plausible, not exercised)src/recast/fortran/frontend.py:668-676,rwset.py:252-288,verify/rwset.py:182only:list, constants and types included, is recorded as a stub procedurestub_proceduresin the evidence misnames constants as procedures (small)Rows 12-14 are on main now (#30 merged 2026-09-04); the others follow #27. A claim the second sweep made and withdrew after checking: the numpy constants module reads
entry.get("dtype")and use.py does set it, so a declared type is honoured there.