@@ -951,6 +951,7 @@ static String fetchUpstreamSource(String className) {
951951 }
952952
953953 /// Generates a SHA256 fingerprint of the differences (first 7 chars)
954+ /// Uses only essential, stable information: class names and change types (sorted)
954955 /// Used for deduplicating GitHub issues
955956 /// @param report the full comparison report
956957 /// @return 7-character fingerprint or "0000000" if no differences
@@ -962,30 +963,46 @@ static String generateFingerprint(JsonObject report) {
962963 return "0000000" ;
963964 }
964965
965- // Extract just the differences array for fingerprinting
966+ // Build a stable, sorted representation of just the essential diff info
966967 final var differences = (JsonArray ) report .members ().get ("differences" );
967- final var diffsOnly = differences .values ().stream ()
968- .filter (v -> {
969- final var obj = (JsonObject ) v ;
970- final var status = ((JsonString ) obj .members ().get ("status" )).value ();
971- return "DIFFERENT" .equals (status );
972- })
973- .toList ();
968+ final var stableLines = new ArrayList <String >();
974969
975- // Serialize to stable JSON string for hashing
976- final var jsonString = JsonArray .of (diffsOnly ).toString ();
970+ for (final var diff : differences .values ()) {
971+ final var diffObj = (JsonObject ) diff ;
972+ final var status = ((JsonString ) diffObj .members ().get ("status" )).value ();
973+
974+ if (!"DIFFERENT" .equals (status )) continue ;
975+
976+ final var className = ((JsonString ) diffObj .members ().get ("className" )).value ();
977+ final var classDiffs = (JsonArray ) diffObj .members ().get ("differences" );
978+
979+ if (classDiffs != null ) {
980+ for (final var change : classDiffs .values ()) {
981+ final var changeObj = (JsonObject ) change ;
982+ final var type = ((JsonString ) changeObj .members ().get ("type" )).value ();
983+ final var methodValue = changeObj .members ().get ("method" );
984+ final var method = methodValue instanceof JsonString js ? js .value () : "" ;
985+ // Create stable line: "ClassName:changeType:methodName"
986+ stableLines .add (className + ":" + type + ":" + method );
987+ }
988+ }
989+ }
990+
991+ // Sort for deterministic ordering
992+ Collections .sort (stableLines );
993+ final var stableString = String .join ("\n " , stableLines );
977994
978995 try {
979996 final var digest = MessageDigest .getInstance ("SHA-256" );
980- final var hash = digest .digest (jsonString .getBytes (StandardCharsets .UTF_8 ));
997+ final var hash = digest .digest (stableString .getBytes (StandardCharsets .UTF_8 ));
981998 final var hexString = new StringBuilder ();
982999 for (final var b : hash ) {
9831000 hexString .append (String .format ("%02x" , b ));
9841001 }
9851002 return hexString .substring (0 , 7 );
9861003 } catch (NoSuchAlgorithmException e ) {
9871004 LOGGER .warning ("SHA-256 not available, using fallback fingerprint" );
988- return String .format ("%07d" , jsonString .hashCode () & 0xFFFFFFF );
1005+ return String .format ("%07d" , stableString .hashCode () & 0xFFFFFFF );
9891006 }
9901007 }
9911008
0 commit comments