You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
⚠ A JDK Throwable constructor's cause that is the body's own JDK-typed parameter is decided by what each caller
passes (R1337). CliException(Throwable c) { super(c); } was charged the toString/getMessage of EVERY project
throwable (CHA over java/lang/Throwable at the R1305 site), and every caller inherited those charges whatever it
passed: zookeeper's new CliParseException(parseEx), whose commons-cli cause no project override can run on,
charged 20 CliCommand.parse overrides Fs, Log, Net. Executed: a java.text.ParseException cause wrote nothing
and read Fs. Each caller is now resolved by its own argument (one project body charged; a WIDE type disclosed; a
foreign type nothing). The body itself is disclosed Unknown[dispatch:java.lang.Object.toString] where some
override carries an effect or an Unknown it lacks. A project-typed cause keeps its precise charge, through every
hop. Measured against v0.40.14 on the 452-jar census: the lost (unit, effect) set is unchanged (414, the same
pairs). Against the held stack: 517 concrete charges in 209 rows of 18 jars become Unknown, and none of those
charges existed in v0.40.14. Chained (304 entries): 5 consumer rows lose a concrete effect only the stack had, and
each keeps Unknown.
Shipping knowingly in this release — named so a pin bump reads them first:
A resolution traded for a hedge, chained (SOUNDNESS R1331/R1336): zookeeper C_CliCommand.dispatch. deny Fs|Log|Net on that unit goes 1 → 0 against v0.40.14; deny Net Unknown holds 1. 3 cells. CliCommand.parse now has 21 overrides that are not pure, over the 12-target bound, so the consumer hedges
(dispatch:…CliCommand.parse). v0.40.14 counted only the ONE override row that had an effect, and unioned it.
The consumer's bound counts override ROWS rather than overriding subtypes (R1336), so it flips with producer
precision. R1337 removes the fabricated concrete charges but not the count: the overrides keep Unknown.
A resolution traded for a hedge, standalone: 14 netty-common rows (*ObjectHashMap$KeySet.retainAll ×5, DefaultPromise.toStringBuilder, SingleThreadEventExecutor.removeTask, DefaultPriorityQueue/ PriorityQueuepriorityChanged/remove/removeTyped ×7) lose Env/Fs (and Log on retainAll) while
keeping Unknown with a dispatch reason.
A false reason class, chained (R1330): 38 consumer cells gain unresolved, so deny Unknown[unresolved]
goes 0 → 1, where the consumer reaches a merged default-method row whose Unknown came only from the merge. An
over-report.
A route inequality, present in v0.40.14 too (R1338): an exact I.super.m() caller of a BROAD-merged default
method reads dispatch on gate --report and not on scan --policy. Fixture: deny Unknown[dispatch] p.Ex.call, scan 0 / report 1 on both jars. An over-report on the report route.
R1337's residue: a caller handing a foreign cause through a JDK-typed parameter now reads Unknown, the
helper's disclosure, where it is really pure (e.g. zookeeper LsCommand.parse). An over-report, in place of the
fabricated concrete charge.
A chained call through an interface member whose dependency row is a merged DEFAULT method carries the
overrides' reasons (R1327, SPEC §6.2). The producer merges an interface's implementers into its default
method's row, so the row carries the overrides' effects, but its unknownWhy and calls are the default
body's own. A consumer dispatching on that member took the effects and only the default body's reason classes:
scala-compiler's TypeClassConstraint.toString gave its chained callers dispatch where an override reads by
reflection, and deny Unknown[reflect,unresolved] read 1 against v0.40.13 and 0 here. The consumer now walks
down to the overrides' own rows for that shape, as it already did for invokevirtual. The row itself is left
unchanged: adding the reasons there gave an exact Shape.super.name() caller the overrides' class on the
producer's report route only.
A chained consumer joining two overloads of one member carries each union entry's own reasons (R1326,
SPEC §3.1/§6.2). The union entries of an overloaded member share one fn, and the consumer's reason memo was
keyed by fn but seeded with whichever entry it was asked about first, so every sibling got THAT entry's
reasons in place of its own. Since R1239 gave union entries their contributors' classes, assertj-core's EnumerableAssert.hasSameSizeAs(Iterable) then (Object) kept only the first union's dispatch and dropped the
second's reflect/callback: deny Unknown[reflect,unresolved] read 1 against v0.40.13 and 0 here. The same
memo charged a caller of ONE overload with its sibling's class. An entry's own reasons are now added on every
join; only the closure over calls, a function of fn alone, is memoised.
⚠ A toString/hashCode/equals call on a receiver typed by the package's own abstraction is named in dispatchesOn (R1304, SPEC §4 ⟨0.40⟩). This resolves an ambiguity AGAINST the reading this engine shipped: the
README documented the Object protocol as a pure-exempt dispatch set and dispatchesOn never named it, so a
library formatting a Converter field ran the consumer's Converter and a chained consumer read pure — deny Fs
0 chained while the same code scanned as one tree read 1. The member is now named under the receiver's declared
public, non-final project interface or class (both javac spellings: invokevirtual Object.toString from javac ≤17, invokeinterface Converter.toString from 18+), and through a JDK string or hashing sink over such a value. A
producer report grows (scoped measurement: +0.20% rows, +1.0% bytes); a chained consumer gains its own
implementors' effects. compareTo is unchanged.
⚠ printStackTrace charges its cause chain's toString, and a JDK re-entry held back at the CHA bound is
disclosed instead of dropped (R1306). printStackTrace prints every throwable in the cause and suppressed chain;
the chain's contents are unknown, so the call is a Throwable.toString dispatch over an unknown throwable. And where
more than twelve project classes inherit the JDK toString (fifteen exceptions overriding only getMessage), the
re-entry bodies were held out of the CHA answer and "x" + t, t.toString() and t.printStackTrace() read pure;
the Object floor now weighs them. Executed: deny Fs Unknown 0 → 1 for both shapes; pure controls stay 0.
The self-gate's forbid io.poly.candor.model -> io.poly.candor.Policy rule is green again (R1307). R1239's lazy
reason-class accumulator was an anonymous AbstractMap whose get ran Policy.scanReasonClassAcc(); the engine's
CHA over Map.get reached it from model.Effect.fromSpecName (whose receiver is a private static final HashMap), a
crossing no execution performs. The accumulator is now a private holder that is not a Map. No report changes.
⚠ new RuntimeException(cause) charges the cause's toString (R1305). Throwable(Throwable) sets detailMessage = cause.toString(), which for a project exception runs its getMessage (R1235), but nothing in the
caller's bytecode names toString, so a wrap over an exception whose getMessage wrote a file read absent. The JDK
constructors that stringify an argument are now a toString sink at exactly that argument — 82 of them, DERIVED from
the pinned Temurin 17/21/25 images (including AssertionError(Object), i.e. assert c : detail); the (String, Throwable) forms do not stringify and stay pure. Executed: five shapes (a parameter, a local allocation, a
project super(cause), UncheckedIOException, a caught exception) deny Fs 0 → 1.
⚠ The Object floor carries an override's Unknown, not only its concrete effects (R1303). Past the CHA bound an equals/hashCode/toString dispatch is decided by the Object floor, which disclosed only where some override it
could run carried a CONCRETE effect the caller lacked — so an override that is itself Unknown (a reflective call, an
Object-typed concat) never reached the caller. Hidden while the site CHA'd over java/lang/Object, where some
unrelated class nearly always carried a concrete effect; R1299 bounded it by the receiver's declared type and 15 units
read pure over candidates that still carry Unknown (ant MethodHandleCPInfo.toString, commons-io NotFileFilter.toString, guice, assertj-db, scala-reflect ×10). The floor is re-decided until nothing new is
disclosed. Executed: deny Fs Unknown p.H.toString 0 → 1 over a field whose reflective subclass wrote a file.
⚠ e.printStackTrace() charges the exception's own toString/getMessage and getCause (R1300). The JDK body
prints the receiver (println(this) → toString → getLocalizedMessage → getMessage) and calls the virtual getCause(); R1235's table named only toString/getLocalizedMessage. Executed: four shapes (no-arg, PrintStream, PrintWriter, an overridden getCause) wrote a file and read absent; deny Fs 0 → 1. A pure toString keeps it pure.
⚠ An Object-owned call (x.toString() where x's type does not declare it) is bounded by x's declared project
type (R1299). javac names java/lang/Object as the owner in that case, so the dispatch fanned to every project
class, and since R1235 to every unrelated class's hashCode: commons-beanutils ConverterFacade.toString / ArrayConverter.toString were charged Clock/Db/Log/Unknown from classes that are not Converters. An ABSTRACT class
is also no longer a re-entry target of its own (spring MethodOverrideCallbackFilter.accept lost a false Unknown).
Where the declared type has no candidate at all, the old Object-wide answer and its hedge are kept.
⚠ An interfaceUnion entry carrying Unknown now carries its contributors' reasons (R1239, SPEC §3.1 ⟨0.40⟩).
A union entry published a reason only on a BROAD hedge (dispatch:), so a chained consumer read every other union's Unknown as dep: → unresolved. It now carries one raw reason per class of its contributors' TRANSITIVE class sets,
as this engine's own gate resolves them, plus its own hedge; R869 bridge rows (also interfaceUnion) carry their
contributor's the same way. Executed: a consumer whose go(lib.Sink s) reaches an implementer that reads the
environment by reflection went deny Unknown[reflect] app.Main.go 0 → 1 and deny Unknown[unresolved] 1 → 0,
on both routes. Spot check (gson, commons-compress, jackson-databind): union rows carrying Unknown with a reason
53 of 286 → 286 of 286; no unit row and no union inferred changed.
⚠ gate --report: an interfaceUnion entry is never a unit of the verdict, and always a contributor to the join
(R1223, R1234, SPEC §3.1 ⟨0.40⟩). Three changes, one clause. (1) A union entry is no longer the subject of the
narrowed-filter answerability test: a reasonless union refused deny Unknown[native] at exit 2 where no unit could
fail it (now exit 0); a REAL caller whose only reason source is that union is still refused. (2) A rule whose only
matching entries are union entries now rides zeroMatch (it was neither evaluated nor disclosed). (3) A union
published BESIDE a real entry under the same hash (a candor-swift/ts-shaped report) is no longer merged into that
unit: deny Env app.B over a body with no Env goes 1 → 0, while every caller of the key still inherits the union's
reason classes. The same change stops the two entries' unknownWhy overwriting each other (last-wins): a real app.B.m carrying reflect: beside a union carrying native: read deny Unknown[reflect] app.B and … app.C
(its caller) exit 0; both now exit 1.
⚠ A JDK body that dispatches back into project code now charges the body it runs (R1235). Executed on JDK 21,
each of these wrote a file and read absent (deny Fs exit 0): Object.toString() runs this.hashCode(), so a class
overriding an effectful hashCode but not toString was pure under h.toString(), "" + h, String.valueOf(h), append(h) and Objects.toString(h); Throwable.toString() runs getLocalizedMessage() → getMessage(); Objects.hashCode(p)/Objects.equals(p, q) on a parameter charged only the callers; and a JDK collection or map
stringified (explicitly, by concat, or through a string sink) runs every element's and value's toString. Each is
now a resolution — the override's own effects, never an Unknown — and its controls stay pure (a pure toString
over an effectful hashCode, an array stringified). Measured on commons-lang3, joda-time and gson (9,964 analysed
units): 0 rows gain a concrete effect, 0 lose one, 19 newly carry Unknown (the overrides reached are themselves Unknown, e.g. commons-lang3's ContextedException.getMessage), and one interface-union row widens. Not covered:
the JDK collection SKELETONS (AbstractList.contains → get, AbstractCollection → iterator, AbstractMap → entrySet), which run project overrides the same way and still read absent. Known over-charges in this
release (SOUNDNESS R1299): where the bytecode names java/lang/Object as the owner of a call on a receiver
typed by a project abstraction, the edge reaches unrelated classes' hashCode/toString. On the corpus that is
7 fabricated charges, e.g. commons-beanutils ArrayConverter.toString gaining Clock/Db/Log. These are
over-reports, not silences, and are fixed in this release (R1299, below).