Repository navigation
candor-java v0.40.16
[0.40.16] — 2026-10-24
-
⚠ A chained
toString/hashCode/equals/compareToon a dependency class that INHERITS the member takes the
override walk too (SOUNDNESS R1378). A dependency class that does not declaretoStringinherits Object's, so
javac spellsp.toString()asinvokevirtual java/lang/Object.toString, and R1360's walk — which runs for an owner a
dependency declares — never saw the site:go(dep.P p) { return p.toString(); }over thirteen overrides, one
reading the environment, read ABSENT,deny Env Unknown0 where the one-tree scan reads 1 (v0.40.14, v0.40.15;
executed). The receiver's DECLARED type is the dependency's class, which §4's MAY does not cover, so the walk now
runs from it, under R1360's bound and rule:[Unknown]{dispatch:dep.P.toString}= one-tree. Kill switch:
CANDOR_R1378=0. -
R1336's count only resolves a site when it can be answered; where it cannot, the dependency's own union entry
decides (SPEC PART 30 P6: degrading a sidecar may only widen a disclosure). The first cut fell back to the override
ROWS when the paired call-graph sidecar was missing, unpaired, unreadable or incomplete, so a broad site unioned
instead of hedging: deleting an optional file made the chained verdict MORE resolved. Now the sidecar counts only
when it is the report's stem sibling and its key set reproduces the report's ⟨0.21⟩analyzedmanifest (count and
digest — true of all 304 corpus dependencies); the count is an interval (decided subtypes .. plus undecided ones,
the hierarchy bounding it), and only an interval straddling the bound is unanswerable. There the verdict is the
owner's union entry when it has one (a broad union's hedge, a narrow union's effects, a pure narrow union stays
pure — PART 92 c3_pure_only), and otherwise the rows unioned beside adispatch:hedge. An overloaded owner's bare
qual is undecided, not "declares nothing". An unreadable sidecar no longer fails the run; it is unanswerable, with
a note. Executed fixture (fxP6;Sub0.parsewrites the file it is handed), cellsdeny Fs/deny Unknown/
deny Fs Unknown, sidecar present / deleted / half its keys dropped / unparseable / unpaired: concrete owner, 13
subtypes, 1 charged — 8cb94c8–aa5c880011 / 101 / 101 / exit 2 / 011, this build011 / 111 / 111 / 111 / 111;
abstract owner, 13 subtypes —011in every arm (the union entry's hedge); concrete owner, 13 pure —011in
every arm (8cb94c8:000degraded); narrow pure union (abstract, 3 pure) —000in every arm; 6 subtypes —
101in every arm. A sixth arm drops ONLY pure units' keys (the case a dependency-row test cannot see): this build
111/011on the two 13-subtype concrete shapes, aa5c880101/000. An owner's own-keydispatch:hedge is
taken as the producer's verdict only when the owner has one member of that name (from the sidecar, or from the
row's own disambiguated qual):dispatch:carries no descriptor, and log4j's realLogEventPatternConverter .format(Object, StringBuilder)body hedges its call to the other overload. Corpus (v0.40.15 → this build,
chained 304 entries, every sidecar paired): the change is visible only through overloaded owners — 2,401 walked
sites have undecided subtypes, 218 are unanswerable (184 rows unioned beside a hedge, 34 decided by a synthetic union
entry). Against aa5c880: 18 rows change, 16 in reasons only; 2 lose concrete effects (commons-math3
AbstractUnivariateStatistic.evaluateRand; h2-mvstoreBasicDataType.writeClock, Fs, Rand), each following
the dependency's own union entry for that descriptor, each keepingUnknown, each absent from the published
one-tree scan; h2-mvstoreBasicDataTypealso loses 2incompletevalues with them. Standalone: byte-identical.
Two boundary shapes found in review (Fable REVIEW3) are closed: (F1) an own-keydispatch:hedge stands in for
the union only where a paired, complete sidecar covers the owner and every dependency ancestor and shows no overload
on any of them — an overload declared on an ANCESTOR left the owner's qual bare, and with the sidecar deleted W's
Fswas dropped (deny Fs0 against 1 present, 1 one-tree); (F2) the bound counts the owner's own body only when it
has one (in-scandeclaresConcrete), read from the producer's synthetic union entry or pure-union key — an
abstract member with exactly 12 declaring subtypes hedged with the sidecar present where the one-tree scan and the
producer's NARROW union do not (tomcat-embed-elELResolver.getCommonPropertyType, a pure union, hedged). Executed
fixturesovl2,x12-0,x12-1: present = deleted = keys-dropped = pure-dropped = unparseable = unpaired =
one-tree in every one. A report with ⟨0.21⟩unanalyzedunits grants no sidecar coverage (its unanalysed types
cannot show their declarations; SOUNDNESS R1371, census reach 0).CANDOR_R1336=0is the UNANSWERABLE arm, not
v0.40.15's row count. The three overload/coverage decisions are now ONE rule (depLineState: every type on the
owner's line — chained, project or JDK — must be read, none may declare the name under another descriptor, and at
most one dependency declaration's descriptor may be unknown, then only with no other dependency declaration beside
it). It decides both whether a subtype's bare qual counts and whether an own-key hedge stands in for the union. It
closes Fable REVIEW4 F3 (the ancestor in its own report, its hierarchy or call-graph sidecar deleted:deny Fs1 →
0) and two shapes the family fixture found beyond it: an overload SPLIT across classes (A.format(String)a pure
body,P.format(Object)), and a producer's foreign union entry keyed under an ancestor for a member that ancestor
never declared (only real rows count as evidence). Executed family fixture: overload on the owner / the ancestor /
split / nowhere × ancestor in the same or its own report × each call-graph sidecar deleted or unpaired and the
ancestor's whole report deleted — present equals one-tree in all 8 layouts, every degraded arm charges everything
present does. NOT closed, pre-existing on v0.40.15 identically: deleting the OWNER's hierarchy sidecar leaves no walk
at all ([Unknown]where present reads[Fs, Unknown]; an absent row where present hedges) — shipping knowingly as
SOUNDNESS R1377: the state is detectable (a report with rows for types no hierarchy sidecar lists), the union-entry
fallback cannot close it for a pure concrete owner (nothing is published under the key), and the INCOMPLETE /
invisible: [pkg]path is the mechanism to evaluate. A bare qual is now undecided beside ANY other declaration of
the name on the line, a PLATFORM one included (Fable REVIEW5 CARVE-A, the family's 9th layout:P extends Thread { run(int) }, thirteenrun(int)subtypes and W'srun()writing a file — the barerunread asrun(), every
subtype counted, and W'sFswas dropped with the sidecar PRESENT, against one-tree and v0.40.15; now[Fs, Unknown], wider than one-tree, never narrower). A wrongly CLEAN line counts EVERY bare subtype, not one, which is
why the case is closed rather than carried. SOUNDNESS R1372 (boundary residuals, present = degraded, reasoned):
a pure concrete owner body whose nearest published entry is an ancestor's synthetic union counts no owner body, and
a pure-only default method with a body likewise — each under-counts by one at exactly 12. -
⚠ A chained
toString/hashCode/equals/compareToon a dependency's own class is bounded like every other
member (SOUNDNESS R1360, SPEC §4 ⟨0.40⟩). The consumer's walk down a dependency class's subclasses returned
NOTHING past the 12-implementor bound for the §4 Object protocol, sogo(dep.Node n) { return n.toString(); }
over 13 overrides that read the environment read ABSENT:deny Env,deny Unknownanddeny Env Unknownall
exited 0 (v0.40.14 and v0.40.15), where the one-tree scan of the same code hedges. Executed:go(new N0())prints
$HOME. §4's MAY covers a receiver typed by the root type itself; this walk only runs for a receiver typed by a
class the dependency declares. Now: under the bound the overrides are unioned as before; past it the site reads
Unknown[dispatch:<owner>.<member>]where some override is charged, and stays pure where every override is
(the in-scan floor's rule). The count is R1336's (overriding subtypes). Kill switch:CANDOR_R1360=0.
Measured against v0.40.15, chained census (304 entries): the protocol hedges at 48 sites in 36 consumer units
of 20 jars; the absent-row shape itself does not occur in the census (every reached unit already carried
Unknown), so nodeny Unknowncell moves. It changes 23 rows: every newdispatch:reason is on a site the walk
hedged (23/23). The trade: 23 concretedeny Ecells go 1 → 0 in 5 rows — bouncycastleASN1Primitive.hashCode
×4 (bc-fips, bc-fips-debug, bcprov, debugger-app) and jgitTreeFilter.toString— each where v0.40.15 unioned
≤12 effectful override rows over more than 12 overriding subtypes; each row keepsUnknownwith the hedge on the
lost site, and every lost cell is absent from both one-tree scans. Plus 11 class cells (indirect5,reflect4,
native2) on those rows and on guava/curator-shadedCharMatcher.toString, and the dropped overrides' surfaces:
incomplete10 values (bouncycastleASN1Primitive×3 jars, 3 each; jgitTreeFilter1),netClass3
(ASN1Primitive×3) andpaths1 (debugger-appASN1Primitive), each unit keepingUnknownon the lost site.
Standalone: byte-identical reports.
Shipping knowingly (pre-existing on v0.40.15): an override whose row is[]with onlyinvisiblereads as pure
— past the bound because the in-scan floor does the same (SOUNDNESS R1361), and under it because a chained consumer
does not inherit a dependency row'sinvisible(SOUNDNESS R1362). -
⚠ PROVISIONAL — rides on a SPEC §3.1 clause under revision (a write-time merged union entry's
unknownWhyis not
direct-only; the code implements its rules 1–2: contributors' classes on the merged row, scan route seeded before
propagation). A write-time merged union entry carries its contributors' reasons, and both gate routes read
them (D1; SOUNDNESS R1330, R1338). The producer merges an interface's implementers into a real row claiming the
same hash (a default method, a kept abstract member). The merge carried the implementers' EFFECTS,Unknown
included, but only a BROAD merge's owndispatch:hedge as a reason; a narrow merge'sUnknownarrived with no
account. Every reader then made one up: a chained consumer'sdep:catch-all read it asunresolved(R1330 — 38
consumer cells on v0.40.15), and the producer's own scan route read the merged unit asunresolvedwhile
gate --reportrefused the narrowed rule. Separately, the broad hedge the row did carry reached an exact
I.super.m()caller ongate --reportonly (R1338:deny Unknown[dispatch] p.Ex.callscan 0 / report 1). The
merge now carries each contributor's transitive classes (R1239's projection, as for a union entry), and the scan
route's class fixpoint is seeded with the written reasons before propagation. Executed fixture (r1330:
RPlug.initreads $HOME by reflection, the default writes the file): the merged row now readsreflect, the
chained consumer reads{reflect}where v0.40.15 read{reflect, unresolved}, and every narrowed policy is
byte-equal on both routes (v0.40.15: 6 of 7 policies differed on that producer). The cost, the same on both
routes: an exact caller of a merged default that already carriesUnknowngains the merge's CLASS (never its
effect) —PShape.plainreadsreflect,Ex.callreadsdispatch. Kill switch:CANDOR_D1=0.
Measured against v0.40.15 (JDK 21). Standalone, 452 jars:inferredCHANGED 0, ADDED 0, REMOVED 0; 6,837
merged rows in 165 jars changeunknownWhyonly, no field loses a value. Producer gate over the 161 jars that
merge, one policy of five narroweddeny Unknown[…]rules:scan --policyandgate --reportdiffered on 152
jars, now on 2 (guava and curator's shaded guava — 211 units, the same 211 on v0.40.15, a separate pre-existing
mechanism);gate --reportrefused 760 rule evaluations over reasonless merged rows, now 0; of 2,713 merged units
the scan route read as implicitlyunresolved, 2,238 now carry their contributors' classes and 475 keep
unresolved(a contributor's frameworkdep:hedge — see below). The cost, scan route: 238 non-merged units in 9 jars
(0.014% of 1,669,927 analysed) gain a class through an exact edge into a merged row —reflect232,
indirect88,unresolved24 cells; scala-library ×2 is 184 of them. Chained, 304 entries: 2,038 consumer cells
loseunresolved— the REMOVING direction for a reason class, accounted for removal by removal: thedep:
catch-all the consumer synthesised for a merged row's unaccountedUnknownis gone because the row now carries
its contributors' classes (stripping them from the report brings it back); no consumer violation is left
classless (16,170 of 16,170 carry a class in both arms); ~2,020 of the units gain nothing because the true classes
were already there. 0 effects move from D1. The 475 merged units that keepunresolvedkeep it TRULY: their
contributors' own reason is a frameworkdep:hedge on an off-jar call (e.g. amqp-clientAMQChannel's
dep:com.fasterxml.jackson.databind.ObjectMapper.readValue), for whichunresolvedis the right class. -
⚠ A chained call on a dependency class member hedges by the number of overriding SUBTYPES, not override ROWS
(SOUNDNESS R1336, SPEC §4 bounded CHA). The consumer's walk down a dependency's subclasses unions their overrides
up to the 12-implementor bound and past it readsUnknown[dispatch:…]. It counted the overrides that published
a ROW, and a pure override publishes none (§2 rule 3), so the verdict followed the producer's precision rather
than the hierarchy: zookeeper'sCliCommand.parsehas 23 overriding subtypes; v0.40.14 published 1 effectful
row (the consumer unionedFs, Log, Net) and v0.40.15 published 21 (it hedged), for one unchanged jar. The
count now comes from the dependency's call-graph sidecar, which keys every analysed unit, pure ones included;
that is what the in-scan bound counts, and the chained answer now matches the one-tree scan of the same code.
Executed fixture: 13 overriding subtypes, one writes the file — v0.40.15 readsdeny Fs1 or 0 depending on
whether the producer names its pure overrides (CANDOR_R1304); this build reads 0 withdeny Fs Unknown1 in
both. Under the bound (6 subtypes) the union is kept in every arm. zookeeperC_CliCommand:Unknown
{dispatch} whether chained on the v0.40.14 or v0.40.15 report, so the trade v0.40.15 named is now the rule,
not a side effect. Cost, chained census, against v0.40.15: 39 concretedeny <E> <unit>cells go 1 → 0 in
20 consumer units of 19 jars (groovyMetaProperty×2, jacksonJsonSerializer×2, lettuceCommandOutput
×2, log4jFilter/AbstractFilter, bouncycastleECFieldElement×4, …); every unit keepsUnknown
{dispatch}. The licence is the per-row site match: every lost effect came from a v0.40.15 union at a site
that this build hedges, and the row'sdispatch:reason names exactly that owner and member (20/20 rows). That
every lost cell is also absent from the published one-tree scan is a convergence control (the published engine
already hedges this shape in-scan), not the licence. The same trade also moves, on the same rows: 26 reason-class
cells (reflect14,indirect11,native1 — the dropped overrides' classes; each unit keepsUnknown
{dispatch}), and the dropped overrides' surfaces —incomplete9 values,netClass5 (unknown-host),paths
2 (curator shadedOrdering, groovyMetaProperty×2, lettuceCommandOutput×2, jgitRevFilter, scala
Function0). This supersedes R869's rule that kept the row-priced union beside the hedge (jackson
StdScalarSerializer.serialize), in every arm. Where the count cannot be answered, see the next entry. The bound
still adds one for the owner's own body, so an ABSTRACT owner member hedges at 12 declaring subtypes where in-scan
unions (pre-existing).CANDOR_R1336=0reads no sidecar, which is the unanswerable state below — it cannot make a
verdict more resolved than the sidecar does.